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
	
	 Robert Prehn
						Robert Prehn