Commit initially
This commit is contained in:
commit
73dd9cc152
15 changed files with 260 additions and 0 deletions
4
.formatter.exs
Normal file
4
.formatter.exs
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
# Used by "mix format"
|
||||||
|
[
|
||||||
|
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||||
|
]
|
26
.gitignore
vendored
Normal file
26
.gitignore
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
# The directory Mix will write compiled artifacts to.
|
||||||
|
/_build/
|
||||||
|
|
||||||
|
# If you run "mix test --cover", coverage assets end up here.
|
||||||
|
/cover/
|
||||||
|
|
||||||
|
# The directory Mix downloads your dependencies sources to.
|
||||||
|
/deps/
|
||||||
|
|
||||||
|
# Where third-party dependencies like ExDoc output generated docs.
|
||||||
|
/doc/
|
||||||
|
|
||||||
|
# Ignore .fetch files in case you like to edit your project deps locally.
|
||||||
|
/.fetch
|
||||||
|
|
||||||
|
# If the VM crashes, it generates a dump, let's ignore it too.
|
||||||
|
erl_crash.dump
|
||||||
|
|
||||||
|
# Also ignore archive artifacts (built via "mix archive.build").
|
||||||
|
*.ez
|
||||||
|
|
||||||
|
# Ignore package tarball (built via "mix hex.build").
|
||||||
|
kindling-*.tar
|
||||||
|
|
||||||
|
# Temporary files, for example, from tests.
|
||||||
|
/tmp/
|
21
README.md
Normal file
21
README.md
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
# Kindling
|
||||||
|
|
||||||
|
**TODO: Add description**
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
If [available in Hex](https://hex.pm/docs/publish), the package can be installed
|
||||||
|
by adding `kindling` to your list of dependencies in `mix.exs`:
|
||||||
|
|
||||||
|
```elixir
|
||||||
|
def deps do
|
||||||
|
[
|
||||||
|
{:kindling, "~> 0.1.0"}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
|
Documentation can be generated with [ExDoc](https://github.com/elixir-lang/ex_doc)
|
||||||
|
and published on [HexDocs](https://hexdocs.pm). Once published, the docs can
|
||||||
|
be found at <https://hexdocs.pm/kindling>.
|
||||||
|
|
18
lib/kindling.ex
Normal file
18
lib/kindling.ex
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
defmodule Kindling do
|
||||||
|
@moduledoc """
|
||||||
|
Documentation for `Kindling`.
|
||||||
|
"""
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Hello world.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
iex> Kindling.hello()
|
||||||
|
:world
|
||||||
|
|
||||||
|
"""
|
||||||
|
def hello do
|
||||||
|
:world
|
||||||
|
end
|
||||||
|
end
|
11
lib/kindling/schema.ex
Normal file
11
lib/kindling/schema.ex
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
defmodule Kindling.Schema do
|
||||||
|
alias Kindling.Version
|
||||||
|
|
||||||
|
def schema_object(version) do
|
||||||
|
filename = Path.join(Version.version_dir(version), "fhir.schema.json")
|
||||||
|
|
||||||
|
filename
|
||||||
|
|> File.read!()
|
||||||
|
|> Jason.decode!()
|
||||||
|
end
|
||||||
|
end
|
32
lib/kindling/schema/resource.ex
Normal file
32
lib/kindling/schema/resource.ex
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
defmodule Kindling.Schema.Resource do
|
||||||
|
@empty_properties %{const: [], has_one: [], has_many: [], enum: [], value: []}
|
||||||
|
|
||||||
|
def properties(df) do
|
||||||
|
df["properties"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def grouped_properties(df) do
|
||||||
|
properties =
|
||||||
|
(df["properties"] || [])
|
||||||
|
|> Enum.map(fn {key, value} -> {key, value, property_type(value)} end)
|
||||||
|
|> Enum.group_by(fn {_key, _value, type} ->
|
||||||
|
type
|
||||||
|
end)
|
||||||
|
|
||||||
|
Map.merge(@empty_properties, properties)
|
||||||
|
end
|
||||||
|
|
||||||
|
def required(df) do
|
||||||
|
Map.take(df["properties"], df["required"])
|
||||||
|
end
|
||||||
|
|
||||||
|
def required?(df, key) do
|
||||||
|
key in df["required"]
|
||||||
|
end
|
||||||
|
|
||||||
|
def property_type(%{"const" => _}), do: :const
|
||||||
|
def property_type(%{"$ref" => _}), do: :has_one
|
||||||
|
def property_type(%{"items" => _}), do: :has_many
|
||||||
|
def property_type(%{"enum" => _}), do: :enum
|
||||||
|
def property_type(_), do: :value
|
||||||
|
end
|
19
lib/kindling/schema_downloader.ex
Normal file
19
lib/kindling/schema_downloader.ex
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
defmodule Kindling.SchemaDownloader do
|
||||||
|
def download_version(version) do
|
||||||
|
version_dir = "#{Mix.Project.build_path(Mix.Project.config())}/fhir/#{version}"
|
||||||
|
filename = "#{version_dir}/fhir.schema.json.zip"
|
||||||
|
File.mkdir_p!(version_dir)
|
||||||
|
|
||||||
|
"http://hl7.org/fhir/#{version}/fhir.schema.json.zip"
|
||||||
|
|> Req.get!()
|
||||||
|
|> then(&File.write!(filename, &1.body))
|
||||||
|
|
||||||
|
case :zip.unzip(to_charlist(filename), cwd: to_charlist(version_dir)) do
|
||||||
|
{:ok, _filelist} ->
|
||||||
|
:ok
|
||||||
|
|
||||||
|
other ->
|
||||||
|
other
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
21
lib/kindling/template.eex
Normal file
21
lib/kindling/template.eex
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
defmodule <%= @namespace %>.<%= @version %>.<%= @resource_name %> do
|
||||||
|
use Ecto.Schema
|
||||||
|
|
||||||
|
schema "<%= Recase.to_snake(@resource_name) %>" do
|
||||||
|
<%= if @properties.const != [] do %># Constants<% end %>
|
||||||
|
<%= for {name, df, _} <- @properties.const do %>field :<%= Recase.to_snake(name) %>, :string, virtual: true, default: "<%= df["const"] %>"
|
||||||
|
<% end %>
|
||||||
|
<%= if @properties.value != [] do %># Fields<% end %>
|
||||||
|
<%= for {name, df, _} <- @properties.value do %>field :<%= Recase.to_snake(name) %>, :<%= fhir_type_to_ecto(df) %>
|
||||||
|
<% end %>
|
||||||
|
<%= if @properties.enum != [] do %># Enum<% end %>
|
||||||
|
<%= for {name, df, _} <- @properties.enum do %>field :<%= Recase.to_snake(name) %>, Ecto.Enum, values: <%= inspect(df["enum"] |> Enum.map(&Recase.to_snake(&1)) |> Enum.map(&String.to_atom/1)) %>
|
||||||
|
<% end %>
|
||||||
|
<%= if @properties.has_one != [] do %># Has One<% end %>
|
||||||
|
<%= for {name, df, _} <- @properties.has_one do %>has_one :<%= Recase.to_snake(name) %>, <%= @namespace %>.<%= @version %>.<%= ref_to_class_name(df["$ref"]) %>
|
||||||
|
<% end %>
|
||||||
|
<%= if @properties.has_many != [] do %># Has Many<% end %>
|
||||||
|
<%= for {name, df, _} <- @properties.has_many do %>has_many :<%= Recase.to_snake(name) %>, <%= @namespace %>.<%= @version %>.<%= ref_to_class_name(df["items"]["$ref"]) %>
|
||||||
|
<% end %>
|
||||||
|
end
|
||||||
|
end
|
28
lib/kindling/templates.ex
Normal file
28
lib/kindling/templates.ex
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
defmodule Kindling.Templates do
|
||||||
|
require EEx
|
||||||
|
|
||||||
|
import Kindling.Templates.Functions
|
||||||
|
|
||||||
|
alias Kindling.Schema.Resource
|
||||||
|
|
||||||
|
EEx.function_from_file(:def, :render, "lib/kindling/template.eex", [:assigns])
|
||||||
|
|
||||||
|
def resource_code(namespace, version, resource_name, resource) do
|
||||||
|
assigns = %{
|
||||||
|
namespace: namespace,
|
||||||
|
version: version,
|
||||||
|
resource_name: resource_name,
|
||||||
|
properties: Resource.grouped_properties(resource)
|
||||||
|
}
|
||||||
|
|
||||||
|
assigns |> render() |> Code.format_string!(file: "#{resource_name}.ex")
|
||||||
|
end
|
||||||
|
|
||||||
|
def write_code(namespace, version, resource_name, resource) do
|
||||||
|
dir = Path.join(String.downcase(namespace), String.downcase(version))
|
||||||
|
file = Path.join(dir, "#{Recase.to_snake(resource_name)}.ex")
|
||||||
|
|
||||||
|
File.mkdir_p!(dir)
|
||||||
|
File.write!(file, resource_code(namespace, version, resource_name, resource))
|
||||||
|
end
|
||||||
|
end
|
22
lib/kindling/templates/functions.ex
Normal file
22
lib/kindling/templates/functions.ex
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
defmodule Kindling.Templates.Functions do
|
||||||
|
@int_patterns [
|
||||||
|
"^[1-9][0-9]*$",
|
||||||
|
"^[0]|([1-9][0-9]*)$",
|
||||||
|
"^-?([0]|([1-9][0-9]*))$"
|
||||||
|
]
|
||||||
|
|
||||||
|
@decimal_patterns ["^-?(0|[1-9][0-9]*)(\\.[0-9]+)?([eE][+-]?[0-9]+)?$"]
|
||||||
|
|
||||||
|
def fhir_type_to_ecto(%{"type" => "string"}), do: :string
|
||||||
|
|
||||||
|
def fhir_type_to_ecto(%{"type" => "number", "pattern" => pattern})
|
||||||
|
when pattern in @int_patterns,
|
||||||
|
do: :integer
|
||||||
|
|
||||||
|
def fhir_type_to_ecto(%{"type" => "number", "pattern" => pattern})
|
||||||
|
when pattern in @decimal_patterns,
|
||||||
|
do: :decimal
|
||||||
|
|
||||||
|
def ref_to_class_name("#/definitions/" <> name),
|
||||||
|
do: name |> String.replace("_", ".") |> Recase.to_pascal()
|
||||||
|
end
|
4
lib/kindling/version.ex
Normal file
4
lib/kindling/version.ex
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
defmodule Kindling.Version do
|
||||||
|
def version_dir(version),
|
||||||
|
do: Path.join([Mix.Project.build_path(Mix.Project.config()), "fhir", version])
|
||||||
|
end
|
31
mix.exs
Normal file
31
mix.exs
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
defmodule Kindling.MixProject do
|
||||||
|
use Mix.Project
|
||||||
|
|
||||||
|
def project do
|
||||||
|
[
|
||||||
|
app: :kindling,
|
||||||
|
version: "0.1.0",
|
||||||
|
elixir: "~> 1.15",
|
||||||
|
start_permanent: Mix.env() == :prod,
|
||||||
|
deps: deps()
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run "mix help compile.app" to learn about applications.
|
||||||
|
def application do
|
||||||
|
[
|
||||||
|
extra_applications: [:logger]
|
||||||
|
]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Run "mix help deps" to learn about dependencies.
|
||||||
|
defp deps do
|
||||||
|
[
|
||||||
|
{:jason, "~> 1.4"},
|
||||||
|
{:recase, "~> 0.7.0"},
|
||||||
|
{:req, "~> 0.4.11"}
|
||||||
|
# {:dep_from_hexpm, "~> 0.3.0"},
|
||||||
|
# {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
|
||||||
|
]
|
||||||
|
end
|
||||||
|
end
|
14
mix.lock
Normal file
14
mix.lock
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
%{
|
||||||
|
"castore": {:hex, :castore, "1.0.5", "9eeebb394cc9a0f3ae56b813459f990abb0a3dedee1be6b27fdb50301930502f", [:mix], [], "hexpm", "8d7c597c3e4a64c395980882d4bca3cebb8d74197c590dc272cfd3b6a6310578"},
|
||||||
|
"finch": {:hex, :finch, "0.18.0", "944ac7d34d0bd2ac8998f79f7a811b21d87d911e77a786bc5810adb75632ada4", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6 or ~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "69f5045b042e531e53edc2574f15e25e735b522c37e2ddb766e15b979e03aa65"},
|
||||||
|
"hpax": {:hex, :hpax, "0.1.2", "09a75600d9d8bbd064cdd741f21fc06fc1f4cf3d0fcc335e5aa19be1a7235c84", [:mix], [], "hexpm", "2c87843d5a23f5f16748ebe77969880e29809580efdaccd615cd3bed628a8c13"},
|
||||||
|
"jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"},
|
||||||
|
"mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"},
|
||||||
|
"mint": {:hex, :mint, "1.5.2", "4805e059f96028948870d23d7783613b7e6b0e2fb4e98d720383852a760067fd", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "d77d9e9ce4eb35941907f1d3df38d8f750c357865353e21d335bdcdf6d892a02"},
|
||||||
|
"nimble_options": {:hex, :nimble_options, "1.1.0", "3b31a57ede9cb1502071fade751ab0c7b8dbe75a9a4c2b5bbb0943a690b63172", [:mix], [], "hexpm", "8bbbb3941af3ca9acc7835f5655ea062111c9c27bcac53e004460dfd19008a99"},
|
||||||
|
"nimble_ownership": {:hex, :nimble_ownership, "0.2.1", "3e44c72ebe8dd213db4e13aff4090aaa331d158e72ce1891d02e0ffb05a1eb2d", [:mix], [], "hexpm", "bf38d2ef4fb990521a4ecf112843063c1f58a5c602484af4c7977324042badee"},
|
||||||
|
"nimble_pool": {:hex, :nimble_pool, "1.0.0", "5eb82705d138f4dd4423f69ceb19ac667b3b492ae570c9f5c900bb3d2f50a847", [:mix], [], "hexpm", "80be3b882d2d351882256087078e1b1952a28bf98d0a287be87e4a24a710b67a"},
|
||||||
|
"recase": {:hex, :recase, "0.7.0", "3f2f719f0886c7a3b7fe469058ec539cb7bbe0023604ae3bce920e186305e5ae", [:mix], [], "hexpm", "36f5756a9f552f4a94b54a695870e32f4e72d5fad9c25e61bc4a3151c08a4e0c"},
|
||||||
|
"req": {:hex, :req, "0.4.11", "cb19f87d5251e7de30cfc67d1899696b290711092207c6b2e8fc2294f237fcdc", [:mix], [{:aws_signature, "~> 0.3.2", [hex: :aws_signature, repo: "hexpm", optional: true]}, {:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:nimble_ownership, "~> 0.2.0", [hex: :nimble_ownership, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "bbf4f2393c649fa4146a3b8470e2a7e8c9b23e4100a16c75f5e7d1d3d33144f3"},
|
||||||
|
"telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"},
|
||||||
|
}
|
8
test/kindling_test.exs
Normal file
8
test/kindling_test.exs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
defmodule KindlingTest do
|
||||||
|
use ExUnit.Case
|
||||||
|
doctest Kindling
|
||||||
|
|
||||||
|
test "greets the world" do
|
||||||
|
assert Kindling.hello() == :world
|
||||||
|
end
|
||||||
|
end
|
1
test/test_helper.exs
Normal file
1
test/test_helper.exs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
ExUnit.start()
|
Loading…
Reference in a new issue