feat: Add metadata about polymorphic fields
This commit is contained in:
parent
4fc2b745e4
commit
dee77ae424
6 changed files with 118 additions and 2 deletions
|
@ -35,6 +35,14 @@ defmodule <%= @namespace %>.<%= @version %>.<%= class_name(@resource_name) %> do
|
||||||
<% end %>
|
<% end %>
|
||||||
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_namespace, do: <%= @namespace %>.<%= @version %>
|
||||||
def version, do: "<%= @version %>"
|
def version, do: "<%= @version %>"
|
||||||
|
|
||||||
|
|
|
@ -18,11 +18,21 @@ defmodule Kindling.Schema do
|
||||||
@spec schema_map(version_string()) :: map()
|
@spec schema_map(version_string()) :: map()
|
||||||
def schema_map(version) do
|
def schema_map(version) do
|
||||||
filename = Path.join(Version.version_dir(version), "fhir.schema.json")
|
filename = Path.join(Version.version_dir(version), "fhir.schema.json")
|
||||||
|
choices = get_choice_data(version)
|
||||||
|
|
||||||
filename
|
filename
|
||||||
|> File.read!()
|
|> File.read!()
|
||||||
|> Jason.decode!()
|
|> Jason.decode!()
|
||||||
|> build_backlinks()
|
|> 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
|
end
|
||||||
|
|
||||||
def all_resources(schema) do
|
def all_resources(schema) do
|
||||||
|
@ -57,6 +67,57 @@ defmodule Kindling.Schema do
|
||||||
Map.put(schema, "definitions", defs)
|
Map.put(schema, "definitions", defs)
|
||||||
end
|
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 """
|
@doc """
|
||||||
Given a schema map in the style returned by `&schema_map/1`, and a root resource name (e.g.
|
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
|
`"Encounter"`), return a `MapSet` of resources that are referenced (recursively) from the
|
||||||
|
|
|
@ -29,7 +29,6 @@ defmodule Kindling.SchemaDownloader do
|
||||||
:ok
|
:ok
|
||||||
|
|
||||||
other ->
|
other ->
|
||||||
dbg(other)
|
|
||||||
other
|
other
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -53,4 +52,42 @@ defmodule Kindling.SchemaDownloader do
|
||||||
raise "Could not download and unzip the FHIR schema for version #{version}."
|
raise "Could not download and unzip the FHIR schema for version #{version}."
|
||||||
end
|
end
|
||||||
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
|
end
|
||||||
|
|
|
@ -39,6 +39,14 @@ defmodule <%= @namespace %>.<%= @version %>.<%= class_name(@resource_name) %> do
|
||||||
<% end %>
|
<% end %>
|
||||||
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_namespace, do: <%= @namespace %>.<%= @version %>
|
||||||
def version, do: "<%= @version %>"
|
def version, do: "<%= @version %>"
|
||||||
def path, do: "/<%= @resource_name %>"
|
def path, do: "/<%= @resource_name %>"
|
||||||
|
|
|
@ -21,7 +21,8 @@ defmodule Kindling.Templates do
|
||||||
properties: Resource.grouped_properties(resource, roots),
|
properties: Resource.grouped_properties(resource, roots),
|
||||||
all_fields: Resource.all_fields(resource),
|
all_fields: Resource.all_fields(resource),
|
||||||
required_fields: Resource.required_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
|
if resource_name in roots do
|
||||||
|
|
|
@ -15,6 +15,7 @@ defmodule Mix.Tasks.Kindling.GenerateSchemas do
|
||||||
[namespace, version] = args
|
[namespace, version] = args
|
||||||
|
|
||||||
Kindling.SchemaDownloader.ensure_version!(version)
|
Kindling.SchemaDownloader.ensure_version!(version)
|
||||||
|
Kindling.SchemaDownloader.ensure_choices!(version)
|
||||||
|
|
||||||
schema = Kindling.Schema.schema_map(version)
|
schema = Kindling.Schema.schema_map(version)
|
||||||
roots = Kindling.Config.root_resources()
|
roots = Kindling.Config.root_resources()
|
||||||
|
|
Loading…
Reference in a new issue