feat: Add metadata about polymorphic fields

This commit is contained in:
Robert Prehn 2024-04-25 14:02:05 -05:00
parent 4fc2b745e4
commit dee77ae424
No known key found for this signature in database
6 changed files with 118 additions and 2 deletions

View file

@ -35,6 +35,14 @@ defmodule <%= @namespace %>.<%= @version %>.<%= class_name(@resource_name) %> do
<% end %>
end
<%= for {name, choices} <- @choices do %>
def choices(<%= inspect(name) %>) do
[<%= Enum.map_join(choices, ", ", &":#{&1}") %>]
end
<% end %>
def choices(_), do: nil
def version_namespace, do: <%= @namespace %>.<%= @version %>
def version, do: "<%= @version %>"

View file

@ -18,11 +18,21 @@ defmodule Kindling.Schema do
@spec schema_map(version_string()) :: map()
def schema_map(version) do
filename = Path.join(Version.version_dir(version), "fhir.schema.json")
choices = get_choice_data(version)
filename
|> File.read!()
|> Jason.decode!()
|> build_backlinks()
|> add_choice_data(choices)
end
def get_choice_data(version) do
filename = Path.join(Version.version_dir(version), "choice-elements.json")
filename
|> File.read!()
|> Jason.decode!()
end
def all_resources(schema) do
@ -57,6 +67,57 @@ defmodule Kindling.Schema do
Map.put(schema, "definitions", defs)
end
def add_choice_data(schema, %{"elements" => choices}) do
Enum.reduce(choices, schema, &apply_choice/2)
end
defp apply_choice({path, types_list}, schema) do
[type_name | rest] = String.split(path, ".")
{type_name, field_name} = follow_refs(schema, type_name, rest)
field_name = String.replace(field_name, "[x]", "")
type = schema["definitions"][type_name]
types_list =
case types_list do
["*"] ->
type["properties"]
|> Map.keys()
|> Enum.filter(&String.starts_with?(&1, field_name))
|> Enum.map(&String.replace(&1, field_name, ""))
other ->
other
end
field_names =
Enum.map(types_list, fn type ->
"#{field_name}_#{Recase.to_snake(type)}"
end)
existing_choices = Map.get(type, "__choices", %{})
new_choices = Map.put(existing_choices, field_name, field_names)
put_in(schema, ["definitions", type_name, "__choices"], new_choices)
end
defp follow_refs(_schema, type_name, [field_name]), do: {type_name, field_name}
defp follow_refs(schema, type_name, [hd | rest]) do
ref =
case schema["definitions"][type_name]["properties"][hd] do
%{"items" => %{"$ref" => ref}} ->
ref
%{"$ref" => ref} ->
ref
end
"#/definitions/" <> next_type = ref
follow_refs(schema, next_type, rest)
end
@doc """
Given a schema map in the style returned by `&schema_map/1`, and a root resource name (e.g.
`"Encounter"`), return a `MapSet` of resources that are referenced (recursively) from the

View file

@ -29,7 +29,6 @@ defmodule Kindling.SchemaDownloader do
:ok
other ->
dbg(other)
other
end
end
@ -53,4 +52,42 @@ defmodule Kindling.SchemaDownloader do
raise "Could not download and unzip the FHIR schema for version #{version}."
end
end
def download_choices(version) do
{:ok, _} = Application.ensure_all_started(:req)
version_alias = Map.get(@version_aliases, version, version)
version_dir = "#{Mix.Project.build_path(Mix.Project.config())}/fhir/#{version}"
filename = "#{version_dir}/choice-elements.json"
File.mkdir_p!(version_dir)
Req.Request.new(
method: :get,
url: "http://www.hl7.org/fhir/#{version_alias}/choice-elements.json"
)
|> Req.Request.prepend_response_steps(handle_utf16: &handle_utf16/1)
|> Req.Request.append_response_steps(decompress_body: &Req.Steps.decompress_body/1)
|> Req.get!()
|> then(&File.write!(filename, &1.body))
end
def handle_utf16({request, %{body: "\uFEFF" <> body} = response}) do
{request, %{response | body: body}}
end
def ensure_choices!(version) do
version_dir = "#{Mix.Project.build_path(Mix.Project.config())}/fhir/#{version}"
filename = "#{version_dir}/choice-elements.json"
cond do
File.exists?(filename) ->
:ok
download_choices(version) == :ok ->
:ok
true ->
raise "Could not download choice element data for version #{version}."
end
end
end

View file

@ -39,6 +39,14 @@ defmodule <%= @namespace %>.<%= @version %>.<%= class_name(@resource_name) %> do
<% end %>
end
<%= for {name, choices} <- @choices do %>
def choices(<%= inspect(name) %>) do
[<%= Enum.map_join(choices, ", ", &":#{&1}") %>]
end
<% end %>
def choices(_), do: nil
def version_namespace, do: <%= @namespace %>.<%= @version %>
def version, do: "<%= @version %>"
def path, do: "/<%= @resource_name %>"

View file

@ -21,7 +21,8 @@ defmodule Kindling.Templates do
properties: Resource.grouped_properties(resource, roots),
all_fields: Resource.all_fields(resource),
required_fields: Resource.required_fields(resource),
backlinks: Enum.filter(resource["__backlinks"], &(&1 in roots))
backlinks: Enum.filter(resource["__backlinks"], &(&1 in roots)),
choices: resource["__choices"] || %{}
}
if resource_name in roots do

View file

@ -15,6 +15,7 @@ defmodule Mix.Tasks.Kindling.GenerateSchemas do
[namespace, version] = args
Kindling.SchemaDownloader.ensure_version!(version)
Kindling.SchemaDownloader.ensure_choices!(version)
schema = Kindling.Schema.schema_map(version)
roots = Kindling.Config.root_resources()