From c7c6568e37e4940361f7400bf9da728fe80778bb Mon Sep 17 00:00:00 2001 From: Robert Prehn <3952444+prehnRA@users.noreply.github.com> Date: Fri, 1 Mar 2024 11:39:16 -0600 Subject: [PATCH] feat: Support Bundle conversion and Search endpoints --- lib/kindling/client.ex | 19 +++++++++++++ lib/kindling/config.ex | 3 +- lib/kindling/converter.ex | 43 +++++++++++++++++++++-------- lib/kindling/templates/functions.ex | 1 + 4 files changed, 53 insertions(+), 13 deletions(-) diff --git a/lib/kindling/client.ex b/lib/kindling/client.ex index 5b69377..af974e1 100644 --- a/lib/kindling/client.ex +++ b/lib/kindling/client.ex @@ -21,6 +21,25 @@ defmodule Kindling.Client do end end + def search(client, resource_module, params \\ [], opts \\ []) do + base_uri = URI.parse(client.base_url) + query = URI.encode_query(params) + uri = base_uri |> URI.append_path(resource_module.path()) |> URI.append_query(query) + more_headers = Keyword.get(opts, :headers, []) + + headers = headers(client, more_headers) + + uri + |> Req.get(headers: headers) + |> case do + {:ok, %{status: status} = response} when status >= 200 and status < 300 -> + Converter.convert(resource_module.version(), response.body) + + other -> + other + end + end + def headers(client, more_headers) do case client.auth_mode do :open -> diff --git a/lib/kindling/config.ex b/lib/kindling/config.ex index fef6e23..f069a26 100644 --- a/lib/kindling/config.ex +++ b/lib/kindling/config.ex @@ -1,3 +1,4 @@ defmodule Kindling.Config do - def root_resources, do: Application.get_env(:kindling, :root_resources, ["Encounter"]) + def root_resources, + do: Application.get_env(:kindling, :root_resources, ["Bundle", "Patient", "Encounter"]) end diff --git a/lib/kindling/converter.ex b/lib/kindling/converter.ex index 8e140ef..611154d 100644 --- a/lib/kindling/converter.ex +++ b/lib/kindling/converter.ex @@ -3,31 +3,50 @@ defmodule Kindling.Converter do def convert(version_namespace, %{"resourceType" => resource_type} = resource_json) do resource_module = Module.concat(version_namespace, Resource.class_name(resource_type)) + resource_list_module = Module.concat(version_namespace, "ResourceList") - structify(resource_module, resource_json) + structify(resource_module, resource_list_module, resource_json) end - def structify(resource_module, resource_json) do + def structify( + resource_module, + resource_list_module, + %{"resourceType" => resource_type} = resource_json + ) + when resource_module == resource_list_module do + version_namespace = + resource_module + |> Module.split() + |> Enum.reverse() + |> tl() + |> Enum.reverse() + |> Module.concat() + + resource_module = Module.concat(version_namespace, Resource.class_name(resource_type)) + structify(resource_module, resource_list_module, resource_json) + end + + def structify(resource_module, resource_list_module, resource_json) do Code.ensure_loaded!(resource_module) atom_map = resource_json |> Enum.map(fn {key_string, value} -> key_atom = key_string |> Recase.to_snake() |> String.to_existing_atom() - {key_atom, convert_field(resource_module, key_atom, value)} + {key_atom, convert_field(resource_module, resource_list_module, key_atom, value)} end) |> Map.new() struct!(resource_module, atom_map) end - def convert_field(resource_module, field, value) do + def convert_field(resource_module, resource_list_module, field, value) do cond do field in resource_module.__schema__(:associations) -> - convert_association(resource_module, field, value) + convert_association(resource_module, resource_list_module, field, value) field in resource_module.__schema__(:embeds) -> - convert_embed(resource_module, field, value) + convert_embed(resource_module, resource_list_module, field, value) field in resource_module.__schema__(:fields) -> cast_field(resource_module, field, value) @@ -63,27 +82,27 @@ defmodule Kindling.Converter do end end - def convert_association(resource_module, field, value) do + def convert_association(resource_module, resource_list_module, field, value) do %{cardinality: cardinality, related: type} = resource_module.__schema__(:association, field) case cardinality do :many -> - Enum.map(value, &structify(type, &1)) + Enum.map(value, &structify(type, resource_list_module, &1)) :one -> - structify(type, value) + structify(type, resource_list_module, value) end end - def convert_embed(resource_module, field, value) do + def convert_embed(resource_module, resource_list_module, field, value) do %{cardinality: cardinality, related: type} = resource_module.__schema__(:embed, field) case cardinality do :many -> - Enum.map(value, &structify(type, &1)) + Enum.map(value, &structify(type, resource_list_module, &1)) :one -> - structify(type, value) + structify(type, resource_list_module, value) end end end diff --git a/lib/kindling/templates/functions.ex b/lib/kindling/templates/functions.ex index 7f34da8..5169f72 100644 --- a/lib/kindling/templates/functions.ex +++ b/lib/kindling/templates/functions.ex @@ -30,6 +30,7 @@ defmodule Kindling.Templates.Functions do def fhir_type_to_ecto(%{"$ref" => "#/definitions/xhtml"}), do: :string def fhir_type_to_ecto(%{"$ref" => "#/definitions/markdown"}), do: :string def fhir_type_to_ecto(%{"$ref" => "#/definitions/base64Binary"}), do: :string + def fhir_type_to_ecto(%{"$ref" => "#/definitions/date"}), do: :date def fhir_type_to_ecto(%{"$ref" => "#/definitions/dateTime"}), do: :utc_datetime_usec def fhir_type_to_ecto(%{"$ref" => "#/definitions/instant"}), do: :utc_datetime_usec def fhir_type_to_ecto(%{"$ref" => "#/definitions/time"}), do: :time_usec