diff --git a/README.md b/README.md index 1db9b1f..f7b4f7a 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,12 @@ ![Arke](https://github.com/arkemishub/arke/assets/81776297/7a04d11b-5cd0-4349-8621-d19cf0274585) -## Installation +## Documentation + +In depth documentation can be found at [https://hexdocs.pm/arke](https://hexdocs.pm/arke), while a more generic can be found [here](https://docs.arkehub.com/docs) -If [available in Hex](https://hex.pm/docs/publish), the package can be installed -by adding `arke` to your list of dependencies in `mix.exs`: +## Installation +The package can be installed by adding `arke` to your list of dependencies in `mix.exs`: ```elixir def deps do @@ -15,7 +17,20 @@ def deps do 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/arke](https://hexdocs.pm/arke). - +Also add in our project the configuration below based on your persistence. In this case we use `arke_postgres` +``` +config :arke, + persistence: %{ + arke_postgres: %{ + init: &ArkePostgres.init/0, + create: &ArkePostgres.create/2, + update: &ArkePostgres.update/2, + delete: &ArkePostgres.delete/2, + execute_query: &ArkePostgres.Query.execute/2, + create_project: &ArkePostgres.create_project/1, + delete_project: &ArkePostgres.delete_project/1, + repo: ArkePostgres.Repo, + } + } +``` +This configuration is used to apply all the CRUD operations in the `Arke.QueryManager` module \ No newline at end of file diff --git a/lib/arke.ex b/lib/arke.ex index fbcf7f3..2b3d399 100644 --- a/lib/arke.ex +++ b/lib/arke.ex @@ -13,6 +13,7 @@ # limitations under the License. defmodule Arke do + @moduledoc false alias Arke.Core.Unit alias Arke.Boundary.{ArkeManager, GroupManager, ParameterManager} alias Arke.System.BaseParameter diff --git a/lib/arke/boundary/arke_manager.ex b/lib/arke/boundary/arke_manager.ex index 83d30ac..3a65b14 100644 --- a/lib/arke/boundary/arke_manager.ex +++ b/lib/arke/boundary/arke_manager.ex @@ -13,9 +13,10 @@ # limitations under the License. defmodule Arke.Boundary.ArkeManager do - @moduledoc """ - This module manage the gen servers for the element specified in `Arke.Core.Arke` - """ + @moduledoc false && + """ + It extends `Arke.Boundary.UnitManager` by providing more functions specific to the Arkes. + """ alias Arke.Utils.ErrorGenerator, as: Error use Arke.Boundary.UnitManager diff --git a/lib/arke/boundary/group_manager.ex b/lib/arke/boundary/group_manager.ex index 14dc073..32af1ff 100644 --- a/lib/arke/boundary/group_manager.ex +++ b/lib/arke/boundary/group_manager.ex @@ -13,7 +13,11 @@ # limitations under the License. defmodule Arke.Boundary.GroupManager do - @moduledoc false + @moduledoc false && + """ + It extends `Arke.Boundary.UnitManager` by providing more functions specific to the Groups. + """ + use Arke.Boundary.UnitManager alias Arke.Utils.ErrorGenerator, as: Error diff --git a/lib/arke/boundary/parameter_manager.ex b/lib/arke/boundary/parameter_manager.ex index 08fcb5d..b19dc47 100644 --- a/lib/arke/boundary/parameter_manager.ex +++ b/lib/arke/boundary/parameter_manager.ex @@ -13,12 +13,13 @@ # limitations under the License. defmodule Arke.Boundary.ParameterManager do - @moduledoc false + @moduledoc false && + """ + It extends `Arke.Boundary.UnitManager` by providing more functions specific to the Parameters. + """ use Arke.Boundary.UnitManager manager_id(:parameter) - # set_registry_name(:parameter_registry) - # set_supervisor_name(:parameter_supervisor) end diff --git a/lib/arke/boundary/unit_manager.ex b/lib/arke/boundary/unit_manager.ex index 1fe19c4..159fae1 100644 --- a/lib/arke/boundary/unit_manager.ex +++ b/lib/arke/boundary/unit_manager.ex @@ -13,6 +13,10 @@ # limitations under the License. defmodule Arke.Boundary.UnitManager do + @moduledoc false &&""" + This module handle all the managers providing functions to create/delete/edit such managers. + The managers stores the struct of a Unit. Such struct is used in the validation process. + """ defmacro __using__(_) do quote do use GenServer diff --git a/lib/arke/core/arke.ex b/lib/arke/core/arke.ex index 6a2b533..dfb50cc 100644 --- a/lib/arke/core/arke.ex +++ b/lib/arke/core/arke.ex @@ -13,13 +13,12 @@ # limitations under the License. defmodule Arke.Core.Arke do - @moduledoc """ - Defines the skeleton of an Arke by defining its characteristics and the various parameters of which it is composed + @moduledoc false && """ + This module is used as entrypoint for every Arke created which does not have a module associated """ defstruct [:id, :label, :active, :type, :parameters] use Arke.System - alias Arke.Core.Parameter alias Arke.Boundary.ArkeManager arke id: :arke do diff --git a/lib/arke/core/link.ex b/lib/arke/core/link.ex index f984f6f..208adf2 100644 --- a/lib/arke/core/link.ex +++ b/lib/arke/core/link.ex @@ -61,8 +61,8 @@ defmodule Arke.Core.Link do def on_create( _, %{ - data: %{type: type, parent_id: parent_id, child_id: child_id}, - metadata: %{project: project} = metadata + data: %{type: _type, parent_id: _parent_id, child_id: _child_id}, + metadata: %{project: _project} = _metadata } = unit ) do {:ok, unit} @@ -83,7 +83,7 @@ defmodule Arke.Core.Link do _, %{ data: %{type: "parameter", parent_id: parent_id, child_id: child_id}, - metadata: %{project: project} = metadata + metadata: %{project: project} = _metadata } = unit ) do ArkeManager.remove_link( @@ -100,7 +100,7 @@ defmodule Arke.Core.Link do _, %{ data: %{type: "group", parent_id: parent_id, child_id: child_id}, - metadata: %{project: project} = metadata + metadata: %{project: project} = _metadata } = unit ) do GroupManager.remove_link( @@ -116,8 +116,8 @@ defmodule Arke.Core.Link do def on_delete( _, %{ - data: %{type: type, parent_id: parent_id, child_id: child_id}, - metadata: %{project: project} = metadata + data: %{type: _type, parent_id: _parent_id, child_id: _child_id}, + metadata: %{project: _project} = _metadata } = unit ) do {:ok, unit} diff --git a/lib/arke/core/parameter.ex b/lib/arke/core/parameter.ex index 68d2e58..2688d79 100644 --- a/lib/arke/core/parameter.ex +++ b/lib/arke/core/parameter.ex @@ -14,11 +14,9 @@ defmodule Arke.Core.Parameter do @moduledoc """ - Module to manage the defaults parameter + This module defines the parameter group and its functions. + By default, the Arke members are: - In order to create a new one simply use the .new(opts) - - ## Types - `string` - `integer` - `float` @@ -28,18 +26,9 @@ defmodule Arke.Core.Parameter do - `time` - `datetime` - ## Values - There are two possible ways to declare the values for a parameter: - - list => by giving a list of values \n - values: ["value 1", "value 2", ...."value n"] - - list of map => by giving a list of map. Each map **must** contain `label` and `value`. \n - values: [%{label:"value 1", value: 1},... %{label: "value 999", value: 999}] - - The result will always be a list of map containing `label` and `value`. Keep in mind that if the values are provided using a list the `label` will be autogenerated. - Remember also that all the values provided must be the same type as the [ParameterType](#module-types) and only `string`, `integer` and `float` support the `values` declaration - - ## Get list of attributes definable in opts during creation: - iex> Arke.Core.Parameter.'ParameterType'.get_parameters() + It is used to share common gen servers functions across the Arkes. In this way we have a single point of + access to the ParameterManager and all its CRUD operations. It also does not interfere with all the `Arke.System.__using__/1` + overridable functions defined in each module """ alias Arke.Boundary.ParameterManager @@ -51,25 +40,23 @@ defmodule Arke.Core.Parameter do | Parameter.Integer.t() | Parameter.Float.t() - @doc """ - Macro defining a shared struct of parameter used across Arkes - """ + use Arke.System.Group group id: "parameter" do end - def on_unit_create(_arke, %{id: id, metadata: %{project: project}} = unit) do + def on_unit_create(_arke, %{id: _id, metadata: %{project: _project}} = unit) do ParameterManager.create(unit) {:ok, unit} end - def on_unit_update(_, %{id: id, metadata: %{project: project}} = unit) do + def on_unit_update(_arke, %{id: id, metadata: %{project: project}} = unit) do ParameterManager.update(id, project, unit) {:ok, unit} end - def on_unit_delete(_, %{id: id, metadata: %{project: project}} = unit) do + def on_unit_delete(_arke, %{id: _id, metadata: %{project: _project}} = unit) do ParameterManager.remove(unit) {:ok, unit} end @@ -77,28 +64,7 @@ defmodule Arke.Core.Parameter do end defmodule Arke.Core.Parameter.String do - @moduledoc """ - Module that define the struct of a String by extending the Arke.Core.Parameter.base_parameters() - {arke_struct} = Parameter.String - - ## Element added - - `min_length` => :atom => define the min_length the string could have. It will check during creation - - `max_length` => :atom => define the max_length the string could have. It will check during creation - - `values` => [list] || [%{label: string, value: any}, ...] => use this to create a parameter with only certain values assignable. (Values must be the same type as the parameter we want to create) - - `multiple` => boolean => relevant only if values are set. It makes possible to assign more than a values defined in values - - `unique` => boolean => check if there is an existing record in the database with the same value before creating one - - `default` => String => default value - - ## Example - iex> params = [id: :string_test, min_length: 1, values: ["value1", "value2"], multiple: true] - ...> Arke.Core.Parameter.new(%{type: :string, opts: params}) - - ## Return - %Arke.Core.Parameter.String{} - """ - alias Arke.Core.Parameter - alias Arke.Boundary.ParameterManager - + @moduledoc false use Arke.System arke id: "string" do @@ -112,27 +78,7 @@ defmodule Arke.Core.Parameter.String do end defmodule Arke.Core.Parameter.Integer do - @moduledoc """ - Module that define the struct of a Integer by extending the Arke.Core.Parameter.base_parameters() - {arke_struct} = Parameter.Integer - - ## Element added - - `min` => :atom => define the mix value the parammeter could have - - `max` => :atom => define the max the parammeter could have - - `values` => [list] || [%{label: string, value: any}, ...] => use this to create a parameter with only certain values assignable. (Values must be the same type as the parameter we want to create) - - `multiple` => boolean => relevant only if values are set. It makes possible to assign more than a values defined in values - - `default` => Integer => default value - - ## Example - iex> params = [id: :integer_test, min: 3, max: 7.5, values: [%{label: "option 1", value: 1}, %{label: "option 2", value: 2}]] - ...> Arke.Core.Parameter.new(%{type: :integer, opts: params}) - - ## Return - %Arke.Core.Parameter.Float{} - """ - alias Arke.Core.Parameter - alias Arke.Boundary.ParameterManager - + @moduledoc false use Arke.System arke id: "integer" do @@ -146,27 +92,7 @@ defmodule Arke.Core.Parameter.Integer do end defmodule Arke.Core.Parameter.Float do - @moduledoc """ - Module that define the struct of a Float by extending the Arke.Core.Parameter.base_parameters() - {arke_struct} = Parameter.Float - - ## Element added - - `min` => :atom => define the mix value the parammeter could have - - `max` => :atom => define the max the parammeter could have - - `values` => [list] || [%{label: string, value: any}, ...] => use this to create a parameter with only certain values assignable. (Values must be the same type as the parameter we want to create) - - `multiple` => boolean => relevant only if values are set. It makes possible to assign more than a values defined in values - - `default` => Float => default value - - ## Example - iex> params = [id: :float_test, min: 3, max: 7.5] - ...> Arke.Core.Parameter.new(%{type: :float, opts: params}) - - ## Return - %Arke.Core.Parameter.Float{} - """ - alias Arke.Core.Parameter - alias Arke.Boundary.ParameterManager - + @moduledoc false use Arke.System arke id: "float" do @@ -180,23 +106,7 @@ defmodule Arke.Core.Parameter.Float do end defmodule Arke.Core.Parameter.Boolean do - @moduledoc """ - Module that define the struct of a Boolean by extending the Arke.Core.Parameter.base_parameters() - {arke_struct} = Parameter.Boolean - - ## Element added - - `default` => Boolean => default value - - ## Example - iex> params = [id: :boolean_test, default: false] - ...> Arke.Core.Parameter.Boolean(%{type: :boolean, opts: params}) - - ## Return - %Arke.Core.Parameter.Boolean{} - """ - alias Arke.Core.Parameter - alias Arke.Boundary.ParameterManager - + @moduledoc false use Arke.System arke id: "boolean" do @@ -204,23 +114,7 @@ defmodule Arke.Core.Parameter.Boolean do end defmodule Arke.Core.Parameter.Dict do - @moduledoc """ - Module that define the struct of a Dict by extending the Arke.Core.Parameter.base_parameters() - {arke_struct} = Parameter.Dict - - ## Element added - - `default` => Dict => default value - - ## Example - iex> params = [id: :dict_test, default: %{default_key: default_value}] - ...> Arke.Core.Parameter.Dict(%{type: :dict, opts: params}) - - ## Return - %Arke.Core.Parameter.Dict{} - """ - alias Arke.Core.Parameter - alias Arke.Boundary.ParameterManager - + @moduledoc false use Arke.System arke id: "dict" do @@ -228,23 +122,7 @@ defmodule Arke.Core.Parameter.Dict do end defmodule Arke.Core.Parameter.List do - @moduledoc """ - Module that define the struct of a Dict by extending the Arke.Core.Parameter.base_parameters() - {arke_struct} = Parameter.Dict - - ## Element added - - `default` => Dict => default value - - ## Example - iex> params = [id: :dict_test, default: %{default_key: default_value}] - ...> Arke.Core.Parameter.Dict(%{type: :dict, opts: params}) - - ## Return - %Arke.Core.Parameter.Dict{} - """ - alias Arke.Core.Parameter - alias Arke.Boundary.ParameterManager - + @moduledoc false use Arke.System arke id: "list" do @@ -252,31 +130,7 @@ defmodule Arke.Core.Parameter.List do end defmodule Arke.Core.Parameter.Date do - @moduledoc """ - Module that define the struct of a Date by extending the Arke.Core.Parameter.base_parameters(). - - ## Accepted values - Date accepts the following format as values: - - string => "YYYY-MM-DD" (separator must be - hyphen) - - sigil => ~D[YYYY-MM-DD] (separator must be - hyphen) - - struct => %Date{} - - {arke_struct} = Parameter.Date - - ## Element added - - `default` => [values](#module-accepted-values) => default value - - ## Example - iex> params = [id: :date_test, default: "1999-09-03"] - ...> Arke.Core.Parameter.new(%{type: :date, opts: params}) - - ## Return - %Arke.Core.Parameter.Date{} - """ - - alias Arke.Core.Parameter - alias Arke.Boundary.ParameterManager - + @moduledoc false use Arke.System arke id: "date" do @@ -284,30 +138,7 @@ defmodule Arke.Core.Parameter.Date do end defmodule Arke.Core.Parameter.Time do - @moduledoc """ - Module that define the struct of a Time by extending the Arke.Core.Parameter.base_parameters(). - - ## Accepted values - Date accepts the following format as values: - - string => "HH:MM:SS" (separator must be - hyphen) - - sigil => ~T[HH:MM:SS] (separator must be - hyphen) - - struct => %Time{} - - {arke_struct} = Parameter.Date - - ## Element added - - `default` => [values](#module-accepted-values) => default value - - ## Example - iex> params = [id: :time_test, default: "23:14:15"] - ...> Arke.Core.Parameter.new(%{type: :time, opts: params}) - - ## Return - %Arke.Core.Parameter.Time{} - """ - alias Arke.Core.Parameter - alias Arke.Boundary.ParameterManager - + @moduledoc false use Arke.System arke id: "time" do @@ -316,33 +147,7 @@ defmodule Arke.Core.Parameter.Time do end defmodule Arke.Core.Parameter.DateTime do - @moduledoc """ - Module that define the struct of a DateTime by extending the Arke.Core.Parameter.base_parameters(). - - ## Accepted values - Date accepts the following format as values: - - string => "YYYY-MM-DDTHH:MM:SSZ" | "YYYY-MM-DD HH:MM:SSZ" | "YYYY-MM-DD HH:MM:SS" (separator must be - hyphen for Date and colon : for Time) - - sigil => ~U[YYYY-MM-DDTHH:MM:SSZ] | ~U[YYYY-MM-DD HH:MM:SSZ] | ~N[YYYY-MM-DDTHH:MM:SSZ] | ~N[YYYY-MM-DD HH:MM:SSZ] | ~N[YYYY-MM-DD HH:MM:SS] (separator must be - hyphen for Date and colon : for Time) - - struct => %DateTime{} | %NaiveDateTime{} - - The T separator is optional. If no offset is provided (Z will be added at the end) - - {arke_struct} = Parameter.Date - - ## Element added - - `default` => [values](#module-accepted-values) => default value - - ## Example - iex> params = [id: :time_test, default: "1999-12-12 09:08:07"] - ...> Arke.Core.Parameter.new(%{type: :datetime, opts: params}) - - ## Return - %Arke.Core.Parameter.DateTime{} - """ - - alias Arke.Core.Parameter - alias Arke.Boundary.ParameterManager - + @moduledoc false use Arke.System arke id: "datetime" do @@ -350,23 +155,7 @@ defmodule Arke.Core.Parameter.DateTime do end defmodule Arke.Core.Parameter.Link do - @moduledoc """ - Module that define the struct of a Link by extending the Arke.Core.Parameter.base_parameters() - {arke_struct} = Parameter.Link - - ## Element added - - `link` => Link => link handler - - ## Example - iex> params = [id: :dict_test, default: %{default_key: default_value}] - ...> Arke.Core.Parameter.Dict(%{type: :dict, opts: params}) - - ## Return - %Arke.Core.Parameter.Link{} - """ - alias Arke.Core.Parameter - alias Arke.Boundary.ParameterManager - + @moduledoc false use Arke.System arke id: "link" do @@ -375,16 +164,7 @@ defmodule Arke.Core.Parameter.Link do end defmodule Arke.Core.Parameter.Dynamic do - @moduledoc """ - Module that define the struct of a Dynamic by extending the Arke.Core.Parameter.base_parameters() - {arke_struct} = Parameter.Dynamic - - ## Return - %Arke.Core.Parameter.Dynamic{} - """ - alias Arke.Core.Parameter - alias Arke.Boundary.ParameterManager - + @moduledoc false use Arke.System arke id: "dynamic" do @@ -392,15 +172,7 @@ defmodule Arke.Core.Parameter.Dynamic do end defmodule Arke.Core.Parameter.Binary do - @moduledoc """ - Module that define the struct of a Binary by extending the Arke.Core.Parameter.base_parameters() - {arke_struct} = Parameter.Binary - - ## Return - %Arke.Core.Parameter.Binary{} - """ - alias Arke.Core.Parameter - alias Arke.Boundary.ParameterManager + @moduledoc false use Arke.System arke id: "binary" do diff --git a/lib/arke/core/query.ex b/lib/arke/core/query.ex index 5d8949d..1e8239b 100644 --- a/lib/arke/core/query.ex +++ b/lib/arke/core/query.ex @@ -23,9 +23,9 @@ defmodule Arke.Core.Query do defmodule LinkFilter do @moduledoc """ Base struct of a LinkFilter: - - unit => %Arke.Core.`{arke_struct}`{} => the `arke_struct` of the unit which we want to filter on. See `Arke.Struct` - - depth => integer => how many results we want to have at max - - direction => "child" | "parent" => the direction the query will use to search, + - unit => the starting unit for the query + - depth => how many lookups or lookdowns the recursive query must do + - direction => the direction the query will use to search, - type => the name of the connection we want to look at \n It is used to define a common filter struct which will be applied on an arke_link Query """ @@ -62,21 +62,13 @@ defmodule Arke.Core.Query do Create a new BaseParameter ## Parameters - - parameter => %Arke.Core.Parameter.`ParameterType` => refer to `Arke.Core.Parameter` + - parameter => Parameter on which the base filter will be applied - operator => refer to [operators](#module-operators) - - value => any => the value that the query will search for - - negate => boolean => used to figure out whether the condition is to be denied \n - - ## Example - iex> filter = Arke.Core.Query.BaseFilter.new(parameter: "name", operator: "eq", value: "John", negate: false) - ...> Arke.Core.Query.BaseFilter.new(person, :default) - - ## Return - %Arke.Core.Query.BaseFilter{} - + - value => the value that the query will search for + - negate => used to figure out whether the condition is to be denied \n """ @spec new( - parameter :: Arke.Core.Parameter.ParameterType, + parameter :: %Arke.Core.Unit{}, operator :: atom(), value :: any, negate :: boolean @@ -110,8 +102,8 @@ defmodule Arke.Core.Query do defmodule Order do @moduledoc """ Base struct Order: - - parameter => %Arke.Core.Parameter.`ParameterType` => refer to `Arke.Core.Parameter` - - direction => "child" | "parent" => the direction the query will use to search \n + - parameter => Parameter on which the order will be applied + - direction => the direction the query will use to search \n It is used to define the return order of a Query """ defstruct ~w[parameter direction]a @@ -124,14 +116,6 @@ defmodule Arke.Core.Query do ## Parameters - arke => %Arke.Core.`{arke_struct}`{} => the `arke_struct` of the unit which we want to filter on. See `Arke.Struct` - project => :atom => identify the `Arke.Core.Project` - - ## Example - iex> person = Arke.Core.Arke.new(id: "person", label: "Person") - ...> Arke.Core.Query.new(person, :default) - - ## Return - %Arke.Core.Query{} - """ @spec new(arke :: %Arke.Core.Arke{}, project :: atom()) :: Arke.Core.Query.t() def new(arke, project), @@ -146,28 +130,19 @@ defmodule Arke.Core.Query do } @doc """ - Add a new link filter + Add a new link filter in order to make a recursive query in the topology table. ## Parameters - query => refer to `new/1` - - unit => %Arke.Core.`{arke_struct}`{} => the `arke_struct` of the unit which we want to filter on. See `Arke.Struct` - - depth => integer => how many results we want to have at max - - direction => "child" | "parent" => the direction the query will use to search + - unit => the starting unit for the query + - depth => how many lookups or lookdowns the recursive query must do + - direction => the direction the query will use to search - type => the name of the link we want to look at - - ## Example - - iex> person = Arke.Core.Arke.new(id: :person, label: "Person") - ...> query = Arke.Core.Query.new(person, :arke_system) - ...> Arke.Core.Query.add_link_filter(query, person, 0, "child", "link") - - ## Return - %Arke.Core.Query{... link: %Arke.Core.Query.LinkFilter{} ... } """ @spec add_link_filter( query :: Arke.Core.Query.t(), - unit :: Arke.Core.Unit.t(), + unit :: %Arke.Core.Unit{}, depth :: integer(), - direction :: atom(), + direction :: :child | :parent, connection_type :: String.t() ) :: Arke.Core.Query.t() def add_link_filter(query, unit, depth, direction, type) do @@ -180,16 +155,6 @@ defmodule Arke.Core.Query do ## Parameters - query => refer to `new/1` - filter => refer to `Arke.Core.Query.BaseFilter` - - ## Example - iex> person = Arke.Core.Arke.new(id: :person, label: "Person") - ...> query = Arke.Core.Query.new(person, :arke_system) - ...> parameter = Arke.Boundary.ParameterManager.get(:id,:arke_system) - ...> filter = Arke.Core.Query.new_filter(parameter,:eq, "name", false) - ...> Arke.Core.Query.add_filter(query, filter) - - ## Return - %Arke.Core.Query{... filters: [ %Arke.Core.Query.Filter{} ] ... } """ def add_filter(query, filter) do %{query | filters: [filter | query.filters]} @@ -204,16 +169,6 @@ defmodule Arke.Core.Query do - operator - value - negate - - ## Example - iex> person = Arke.Core.Arke.new(id: :person, label: "Person") - ...> query = Arke.Core.Query.new(person, :arke_system) - ...> parameter = Arke.Boundary.ParameterManager.get(:id,:arke_system) - ...> base_filter = Arke.Core.Query.new_base_filter(parameter, :eq, "name", false) - ...> Arke.Core.Query.add_filter(query, :and, false, base_filter) - - ## Return - %Arke.Core.Query{... filters: [ %Arke.Core.Query.Filter{} ] ... } """ def add_filter(query, parameter, operator, value, negate) do %{query | filters: [new_filter(parameter, operator, value, negate) | query.filters]} @@ -224,19 +179,16 @@ defmodule Arke.Core.Query do ## Parameters - query => refer to `new/1` - - logic => :and | :or => the logic of the filter - - negate => boolean => used to figure out whether the condition is to be denied - - base_filters - - ## Example - iex> person = Arke.new(id: :person, label: "Person") - ...> query = Arke.Core.Query.new(person, :arke_system) - ...> parameter = Arke.Core.ParameterManager.get(:id,:arke_system) - ...> Arke.Core.Query.add_filter(query, parameter, :eq, "name", false) - - ## Return - %Arke.Core.Query{... filters: [ %Arke.Core.Query.Filter{} ] ... } + - logic => the logic of the filter + - negate => used to figure out whether the condition is to be denied + - base_filters => One or a list of `Arke.Core.Query.BaseFilter` """ + @spec add_filter( + query :: Arke.Core.Query.t(), + logic :: :and | :or, + negate :: boolean, + base_filters :: Arke.Core.Query.BaseFilter.t() | [Arke.Core.Query.BaseFilter.t()] + ) :: Arke.Core.Query.t() def add_filter(query, logic, negate, base_filters) do %{query | filters: [new_filter(logic, negate, base_filters) | query.filters]} end @@ -244,18 +196,17 @@ defmodule Arke.Core.Query do @doc """ Create a new filter ## Parameters - - parameter => %Arke.Core.Parameter.`ParameterType` => refer to `Arke.Core.Parameter` + - parameter => Parameter to filter - operator => refer to [operators](#module-operators) - - value => any => the value that the query will search for - - negate => boolean => used to figure out whether the condition is to be denied - - ## Example - iex> parameter = Arke.Boundary.ParameterManager.get(:id,:arke_system) - ...> Arke.Core.Query.new_filter(parameter,:eq, "name", false) - - ## Return - %Arke.Core.Query.Filter{base_filters: [ %Arke.Core.Query.BaseFilter{} ]} + - value => the value that the query will search for + - negate => used to figure out whether the condition is to be denied """ + @spec new_filter( + parameter :: %Arke.Core.Unit{}, + operator :: Arke.Core.QueryManager.operator(), + value :: any, + negate :: boolean() + ):: Arke.Core.Query.Filter.t() def new_filter(parameter, operator, value, negate) do %Filter{ logic: :and, @@ -267,17 +218,15 @@ defmodule Arke.Core.Query do @doc """ Create a new filter ## Parameters - - logic => :and | :or => the logic of the filter - - negate => boolean => used to figure out whether the condition is to be denied + - logic => the logic of the filter + - negate => used to figure out whether the condition is to be denied - base_filters => refer to `Arke.Core.Query.BaseFilter` - - ## Example - iex> base_filter = Arke.Core.Query.new_base_filter(parameter, :eq, "name", false) - ...> Arke.Core.Query.new_filter(:and, false, base_filter) - - ## Return - %Arke.Core.Query.Filter{base_filters: [ %Arke.Core.Query.BaseFilter{} ]} """ + @spec new_filter( + logic :: :and | :or, + negate :: boolean(), + base_filters :: Arke.Core.Query.BaseFilter.t() | [Arke.Core.Query.BaseFilter.t()] + ):: Arke.Core.Query.Filter.t() def new_filter(logic, negate, base_filters) do %Filter{base_filters: parse_base_filters(base_filters), logic: logic, negate: negate} end @@ -286,22 +235,18 @@ defmodule Arke.Core.Query do Create a new base filter ## Parameters - - parameter => %Arke.Core.Parameter.`ParameterType` => refer to `Arke.Core.Parameter` + - parameter => Parameter to filter - operator => refer to [operators](#module-operators) - - value => any => the value that the query will search for - - negate => boolean => used to figure out whether the condition is to be denied - - ## Example - iex> parameter = Arke.Boundary.ParameterManager.get(:id,:arke_system) - ...> Arke.Core.Query.new_base_filter(parameter, :eq, "name", false) - - ## Return - %Arke.Core.Query.BaseFilter{} + - value => the value that the query will search for + - negate => used to figure out whether the condition is to be denied """ - # TODO: standardize parameter - # if it is a string convert it to existing atom and get it from paramater manager - # if it is an atom get it from paramater manaager + @spec new_base_filter( + parameter :: %Arke.Core.Unit{}, + operator :: Arke.Core.QueryManager.operator(), + value :: any, + negate :: boolean() + ) :: Arke.Core.Query.BaseFilter.t() def new_base_filter(parameter, operator, value, negate) do BaseFilter.new(parameter, operator, value, negate) end @@ -313,19 +258,15 @@ defmodule Arke.Core.Query do Get the query result ordered by specific criteria ## Parameters - - query => refer to refer to `new/1` - - parameter => %Arke.Core.Parameter.`ParameterType` => refer to `Arke.Core.Parameter` - - direction => "child" | "parent" => the direction the query will use to search - - ## Example - iex> person = Arke.Core.Arke.new(id: "person", label: "Person") - ...> query = Arke.Core.Query.new(person, :arke_system) - ...> parameter = Arke.Boundary.ParameterManager.get(:id,:arke_system) - ...> Arke.Core.Query.add_order(query, parameter, :asc) - - ## Return - %Arke.Core.Query{ ... orders: [ %Arke.Core.Query.Order{} ] ... } + - query => refer to `new/1` + - parameter => parameter used to order the query results + - direction => the direction the query will use to search """ + @spec add_order( + query :: Arke.Core.Query.t(), + parameter :: %Arke.Core.Unit{}, + direction :: :asc | :desc + ):: Arke.Core.Query.t() def add_order(query, parameter, direction) do %{ query @@ -338,18 +279,9 @@ defmodule Arke.Core.Query do ## Parameters - query => refer to `new/1` - - offset => integer => define the offset of the query - - ## Example - iex> person = Arke.Core.Arke.new(id: :person, label: "Person") - ...> query = Arke.Core.Query.new(person, :arke_system) - ...> Arke.Core.Query.set_offset(query, 5) - - ## Return - %Arke.Core.Query{... offset: value ...} - + - offset => define the offset of the query """ - + @spec set_offset(query :: Arke.Core.Query.t(), offset :: nil | number() | String.t()):: Arke.Core.Query.t() def set_offset(query, nil), do: query def set_offset(query, offset) when is_binary(offset), @@ -365,15 +297,8 @@ defmodule Arke.Core.Query do ## Parameters - query => refer to `new/1` - limit => integer => set the results limit of the query - - ## Example - iex> person = Arke.Core.Arke.new(id: :person, label: "Person") - ...> query = Arke.Core.Query.new(person, :arke_system) - ...> Arke.Core.Query.set_limit(query, 100) - - ## Return - %Arke.Core.Query{... limit: value ...} """ + @spec set_limit(query :: Arke.Core.Query.t(), offset :: nil | number() | String.t()):: Arke.Core.Query.t() def set_limit(query, nil), do: query def set_limit(query, offset) when is_binary(offset), diff --git a/lib/arke/group.ex b/lib/arke/group.ex index b7dd991..ef3217f 100644 --- a/lib/arke/group.ex +++ b/lib/arke/group.ex @@ -13,9 +13,18 @@ # limitations under the License. defmodule Arke.System.Group do + @moduledoc """ + Core module which handle all the management functions for the Unit of a Group. + See `Arke.Example.System.MacroGroup` to get a list of all the available functions. + """ + + @doc """ + This macro is used whenever we want to edit the default behaviour + + use Arke.System.Group + """ defmacro __using__(_) do quote do - # @after_compile __MODULE__ Module.register_attribute(__MODULE__, :group, accumulate: false, persist: true) Module.register_attribute(__MODULE__, :parameters, accumulate: true, persist: false) Module.register_attribute(__MODULE__, :system_group, accumulate: false, persist: true) @@ -24,21 +33,44 @@ defmodule Arke.System.Group do import unquote(__MODULE__), only: [group: 1, group: 2, parameter: 3, parameter: 2] - # @before_compile unquote(__MODULE__) - + # Return the Group struct for the given module + @doc false def group_from_attr(), do: Keyword.get(__MODULE__.__info__(:attributes), :group, []) |> List.first() + + @doc false def is_group?(), do: Keyword.get(__MODULE__.__info__(:attributes), :system_group, []) |> List.first() + @doc false def on_unit_load(arke, data, _persistence_fn), do: {:ok, data} + + @doc false def before_unit_load(_arke, data, _persistence_fn), do: {:ok, data} + + @doc false def on_unit_validate(_arke, unit), do: {:ok, unit} + + @doc false def before_unit_validate(_arke, unit), do: {:ok, unit} + + @doc false def on_unit_create(_arke, unit), do: {:ok, unit} + + @doc false def before_unit_create(_arke, unit), do: {:ok, unit} + + @doc false def on_unit_struct_encode(unit, _), do: {:ok, unit} + + @doc false def on_unit_update(_arke, unit), do: {:ok, unit} + + @doc false def before_unit_update(_arke, unit), do: {:ok, unit} + + @doc false def on_unit_delete(_arke, unit), do: {:ok, unit} + + @doc false def before_unit_delete(_arke, unit), do: {:ok, unit} defoverridable on_unit_load: 3, @@ -55,39 +87,25 @@ defmodule Arke.System.Group do end end - # defmacro __before_compile__(env) do - # end - # - # def compile(translations) do - # - # end ###################################################################################################################### # Group MACRO ######################################################################################################### ###################################################################################################################### @doc """ - Macro to create an arke struct with the given parameters. - Usable only via `code` and not `iex`. - + Macro to manager a group and its related functions ## Example - group do - parameter :custom_parameter - parameter :custom_parameter2 - parameter :custom_parameter3 - parameter :custom_parameter4 + group id: :some_id do end - ## Return - %Arke.Core.Unit{} - + From now on all the overridable functions can be edited and all the public functions will be used as API custom function """ + @spec group(args :: list(), Macro.t()) :: %{} defmacro group(opts \\ [], do: block) do id = Keyword.get(opts, :id) - # metadata = Keyword.get(opts, :metadata, %{}) - # base_parameters = get_base_arke_parameters(type) + quote do id = unquote(id) @@ -109,11 +127,7 @@ defmodule Arke.System.Group do # PARAMETER MACRO #################################################################################################### ###################################################################################################################### - @doc """ - Macro used to define parameter in an arke. - See example above `arke/2` - - """ + @doc false @spec parameter(id :: atom(), type:: atom(), opts :: list()) :: Macro.t() defmacro parameter(id, type, opts \\ []) do # parameter_dict = Arke.System.BaseParameter.parameter_options(opts, id, type) @@ -129,5 +143,8 @@ defmodule Arke.System.Group do end defmodule Arke.System.BaseGroup do + @moduledoc false && """ + This module is used as entrypoint for every Group created which does not have a module associated + """ use Arke.System.Group end diff --git a/lib/arke/query_manager.ex b/lib/arke/query_manager.ex index a48f0a4..73b0b74 100644 --- a/lib/arke/query_manager.ex +++ b/lib/arke/query_manager.ex @@ -14,27 +14,28 @@ defmodule Arke.QueryManager do @moduledoc """ - Module to manage the CRUD operations to create the below elements and also manage the query to get the elements from db. - - ## `arke` - - Parameter => `Arke.Core.Parameter` - - Arke => `Arke.Core.Arke` - - Unit => `Arke.Core.Unit` - - Group => `Arke.Core.Group` - - ## `operators` - - eq => equal => `=` - - contains => contains a value (Case sensitive) => `LIKE %word%` - - icontains => contains a value (Not case sensitive) => `LIKE %word%` - - startswith => starts with the given value (Case sensitive) => `LIKE %word` - - istartswith => starts with the given value (Not case sensitive) => `LIKE %word` - - endswith => ends with the given value (Case sensitive) => `LIKE word%` - - iendswith => ends with the given value (Not case sensitive) => `LIKE word%` - - lte => less than or equal => `<=` - - lt => less than => `<` - - gt => greater than => `>` - - gte => greater than or equal => `>=` - - in => value is in a collection => `IN` + Module to manage the CRUD operations to create the below `Elements` and also manage the query to get the elements from db. + + ## `Elements` + - Parameter -> `Arke.Core.Parameter` + - Arke -> `Arke.Core.Arke` + - Unit -> `Arke.Core.Unit` + - Group -> `Arke.Core.Group` + - Link -> `Arke.Core.Link` + ## `Operators` + - `eq` -> equal -> `=` + - `contains` -> contains a value (Case sensitive) -> `LIKE %word%` + - `icontains` -> contains a value (Not case sensitive) -> `ILIKE %word%` + - `startswith` -> starts with the given value (Case sensitive) -> `LIKE %word` + - `istartswith` -> starts with the given value (Not case sensitive) -> `ILIKE %word` + - `endswith` -> ends with the given value (Case sensitive) -> `LIKE word%` + - `iendswith` -> ends with the given value (Not case sensitive) -> `ILIKE word%` + - `lte` -> less than or equal -> `<=` + - `lt` -> less than -> `<` + - `gt` -> greater than -> `>` + - `gte` -> greater than or equal -> `>=` + - `in` -> value is in a collection -> `IN` + - `isnull` -> value is_nil -> `IS NULL` """ alias Arke.Boundary.{ArkeManager, ParameterManager, GroupManager} @@ -48,7 +49,7 @@ defmodule Arke.QueryManager do @persistence Application.get_env(:arke, :persistence) @record_fields [:id, :data, :metadata, :inserted_at, :updated_at] - @type func_return() :: {:ok, Unit.t()} | Error.t() + @type func_return() :: {:ok, %Unit{}} | Error.t() @type operator() :: :eq | :contains @@ -66,14 +67,9 @@ defmodule Arke.QueryManager do @doc """ Create a new query - - ## Parameter - - opts => %{map} || [keyword: value] || key1: value1, key2: value2 => map containing the project and the arke where to apply the query - - ## Example - iex> Arke.QueryManager.query(project: :public) """ - @spec query(list()) :: Query.t() + @spec query(opts :: [project: String.t() | atom()]) :: + Query.t() def query(opts) do project = Keyword.get(opts, :project) arke = get_arke(Keyword.get(opts, :arke), project) @@ -81,27 +77,22 @@ defmodule Arke.QueryManager do end @doc """ - Create a new topology query + Create a new topology query. It will get all the units related to the first one ## Parameter - - query => refer to `query/1` - - unit => %{arke_struct} => struct of the unit used as reference for the query - - opts => [keyword: value] => KeywordList containing the link conditions - - depth => int => max depth of the topoplogy - - direction => :atom => :child/:parent => the direction of the link. From parent to child or viceversa - - connection_type => string => name of the connection where to search - - ## Example - - iex> Arke.QueryManager.query(project: :public) - ...> unit = QueryManager.get_by([project: :arke_system, id: "test"]) - ...> QueryManager.link(query, unit) - - ## Return - %Arke.Core.Query{} + - `query` -> refer to `query/1` + - `unit` -> struct of the unit used as reference for the query + - `opts` -> KeywordList containing the link conditions + - `depth` max depth of the recursion. + - `direction` --> the direction of the link. One of `child` or `parent` + - `connection_type` -> name of the connection to search """ - @spec link(Query.t(), unit :: Unit.t(), opts :: list()) :: Query.t() + @spec link( + Query.t(), + unit :: %Unit{}, + opts :: [direction: :child | :parent, depth: integer(), type: String.t()] + ) :: Query.t() def link(query, unit, opts \\ []) do direction = Keyword.get(opts, :direction, :child) depth = Keyword.get(opts, :depth, 500) @@ -123,23 +114,13 @@ defmodule Arke.QueryManager do defp parse_link_direction(direction), do: direction @doc """ - Function to create an element - + Function to create a Unit. It will load a Unit based on the given arke, which is used as a model, and the given data. ## Parameters - - project => :atom => identify the `Arke.Core.Project` - - arke => {arke_struct} => identify the struct of the element we want to create - - args => [list] => list of key: value we want to assign to the {arke_struct} above - - ## Example - iex> string = ArkeManager.get(:string, :default) - ...> Arke.QueryManager.create(:default, string, [id: "name", label: "Nome"]) - - ## Returns - {:ok, %Arke.Core.Unit{}} - - + - `project` -> identify the `Arke.Core.Project` + - `arke` -> identify the struct of the element we want to create + - `args` -> the data of the new element we want to create """ - @spec create(project :: atom(), arke :: Arke.t(), args :: list()) :: func_return() + @spec create(project :: atom(), arke :: %Arke{}, args :: [...]) :: func_return() def create(project, arke, args) do persistence_fn = @persistence[:arke_postgres][:create] with %Unit{} = unit <- Unit.load(arke, args, :create), @@ -236,23 +217,15 @@ defmodule Arke.QueryManager do def check_group_manager_functions_errors(unit), do: {:ok, unit} @doc """ - Function to update an element + Function to update a Unit. ## Parameters - - project => :atom => identify the `Arke.Core.Project` - - unit => %{arke_struct} => unit to update - - args => [list] => list of key: value to update - - ## Example - iex> name = QueryManager.get_by(id: "name") - ...> QueryManager.update(:default, name, [max_length: 20]) - - ## Returns - {:ok, %Arke.Core.Unit{} } - {:error, [msg]} + - `project` -> identify the `Arke.Core.Project` + - `unit` -> unit to update + - `args` -> list of key: value to update """ - @spec update(Unit.t(), args :: list()) :: func_return() + @spec update(%Unit{}, args :: list()) :: func_return() def update(%{arke_id: arke_id, metadata: %{project: project}, data: data} = current_unit, args) do persistence_fn = @persistence[:arke_postgres][:update] arke = ArkeManager.get(arke_id, project) @@ -274,19 +247,12 @@ defmodule Arke.QueryManager do {:ok, Unit.update(unit, updated_at: updated_at)} end @doc """ - Function to delete a given unit + Function to delete a given unit. It will delete the manager, if any, and the db record ## Parameters - - project => :atom => identify the `Arke.Core.Project` - - unit => %{arke_struct} => the unit to delete - ## Example - iex> element = Arke.QueryManager.get_by(id: "name") - ...> Arke.QueryManager.delete(element) - - ## Returns - {:ok, _} - + - `project` -> identify the `Arke.Core.Project` + - `unit` -> the unit to delete """ - @spec delete(project :: atom(), Unit.t()) :: {:ok, any()} + @spec delete(project :: atom(), %Unit{}) :: {:ok, any()} def delete(project, %{arke_id: arke_id} = unit) do arke = ArkeManager.get(arke_id, project) persistence_fn = @persistence[:arke_postgres][:delete] @@ -301,29 +267,16 @@ defmodule Arke.QueryManager do end @doc """ - Function to get a single element identified by the opts. Use `Arke.QueryManager.filter_by` if more than one element is returned - ## Parameters - - opts => %{map} || [keyword: value] || key1: value1, key2: value2 => identify the element to get - - ## Example - iex> Arke.QueryManager.get_by(id: "name") + Create a query which is used to get a single element which match the given criteria. + If more are returned then an exception will be raised """ - @spec get_by(opts :: list()) :: Unit.t() | nil + @spec get_by(opts :: [{:project,atom} | {atom,any}]) :: %Unit{} | nil def get_by(opts \\ []), do: basic_query(opts) |> one @doc """ - Function to get all the element which match the given criteria - ## Parameters - - opts => %{map} || [keyword: value] || key1: value1, key2: value2 => identify the element to get - - operator => :atom => refer to [operators](#module-operators) - - ## Example - iex> Arke.QueryManager.filter_by(id: "name") - - ## Return - [ Arke.Core.Unit{}, ...] + Create a query which is used to get all the element which match the given criteria """ - @spec filter_by(opts :: list()) :: [Unit.t()] | [] + @spec filter_by(opts :: [{:project,atom} | {atom,any}]) :: [%Unit{}] | [] def filter_by(opts \\ []), do: basic_query(opts) |> all defp basic_query(opts) when is_map(opts), do: Map.to_list(opts) |> basic_query @@ -354,16 +307,13 @@ defmodule Arke.QueryManager do Add an `:and` logic to a query ## Parameter - - query => refer to `query/1` - - negate => boolean => used to figure out whether the condition is to be denied - - filters => refer to `condition/3 | conditions/1` + - query -> refer to `query/1` + - negate -> boolean -> used to figure out whether the condition is to be denied + - filters -> refer to `condition/3 | conditions/1` ## Example iex> query = QueryManager.query(arke: nil, project: :arke_system) ...> query = QueryManager.and_(query, false, QueryManager.conditions(parameter__eq: "value")) - - ## Return - %Arke.Core.Query{} """ @spec and_(query :: Query.t(), negate :: boolean(), filters :: list()) :: Query.t() def and_(query, negate, filters) when is_list(filters), @@ -375,16 +325,14 @@ defmodule Arke.QueryManager do Add an `:or` logic to a query ## Parameter - - query => refer to `query/1` - - negate => boolean => used to figure out whether the condition is to be denied - - filters => refer to `condition/3 | conditions/1` + - query -> refer to `query/1` + - negate -> boolean -> used to figure out whether the condition is to be denied + - filters -> refer to `condition/3 | conditions/1` ## Example iex> query = QueryManager.query(arke: nil, project: :arke_system) ...> query = QueryManager.or_(query, false, QueryManager.conditions(parameter__eq: "value")) - ## Return - %Arke.Core.Query{} """ @spec or_(query :: Query.t(), negate :: boolean(), filters :: list()) :: Query.t() def or_(query, negate, filters) when is_list(filters), @@ -403,21 +351,18 @@ defmodule Arke.QueryManager do Create a `Arke.Core.Query.BaseFilter` ## Parameters - - parameter => :atom | %Arke.Core.Arke{} => the parameter where to check the condition - - operator => :atom => refer to [operators](#module-operators) - - value => string | boolean | nil => the value the parameter and operator must check - - negate => boolean => used to figure out whether the condition is to be denied + - `parameter` -> the parameter where to check the condition + - `operator` -> refer to [operators](#module-operators) + - `value` -> it will be parsed against the parameter type else it will return an error. + - `negate` -> used to figure out whether the condition is to be denied ## Example iex> QueryManager.condition(:string, :eq, "test") - - ## Return - %Arke.Core.Query.BaseFilter{} """ @spec condition( - parameter :: Arke.t() | atom(), + parameter :: %Unit{}, negate :: boolean(), - value :: String.t() | boolean() | nil, + value :: String.t() | boolean() | number() | nil, negate :: boolean() ) :: Query.BaseFilter.t() def condition(parameter, operator, value, negate \\ false), @@ -427,13 +372,11 @@ defmodule Arke.QueryManager do Create a list of `Arke.Core.Query.BaseFilter` ## Parameter - - opts => %{map} || [keyword: value] || key1: value1, key2: value2 => the condtions used to create the BaseFilters + - `opts` -> the condtions used to create the BaseFilters. + The key of the opts must be written as: parameter__operator ## Example - iex> QueryManager.conditions(parameter__eq: "test", string__contains: "t") - - ## Return - [ %Arke.Core.Query.BaseFilter{}, ...] + iex> QueryManager.conditions(name__eq: "test", string__contains: "t") """ @spec conditions(opts :: list()) :: [Query.BaseFilter.t()] def conditions(opts \\ []) do @@ -444,19 +387,15 @@ defmodule Arke.QueryManager do end @doc """ - Create query with specific options + Create query with specific filter. For the `opts` refer to `conditions/1` ## Parameters - - query => refer to `query/1` - - opts => %{map} || [keyword: value] || key1: value1, key2: value2 => keyword list containing the filter to apply + - `query` -> refer to `query/1` + - `opts` -> keyword list containing the filter to apply ## Example iex> query = Arke.QueryManager.query() ...> QueryManager.where(query, [id__contains: "me", id__contains: "n"]) - - ## Return - %Arke.Core.Query{ %Arke.Core.Query.Filter{ ... base_filters: %Arke.Core.Query.BaseFilter{ ... }}} - """ @spec where(query :: Query.t(), opts :: list()) :: Query.t() def where(query, opts \\ []) do @@ -469,45 +408,23 @@ defmodule Arke.QueryManager do end @doc """ - Filter of the query + It adds a filter for the given query ## Parameters - - query => refer to `query/1` - - filter => refer to `Arke.Core.Query.Filter` - - ## Example - iex> query = Arke.QueryManager.Query.t - ...> parameter = Arke.Boundary.ParameterManager.get(:id,:arke_system) - ...> Arke.Core.Query.new_filter(parameter,:equal,"name",false) - ...> Arke.Core.Query.filter(query, filter - + - `query` -> refer to `query/1` + - `filter` -> refer to `Arke.Core.Query.Filter` """ @spec filter(query :: Query.t(), filter :: Query.Filter.t()) :: Query.t() def filter(query, filter), do: Query.add_filter(query, filter) @doc """ - Filter of the query - - ## Parameters - - query => refer to `query/1` - - parameter => %{arke_struct} => arke struct of the parameter - - operator => :atom => refer to [operators](#module-operators) - - value => string | boolean | nil => the value the parameter and operator must check - - negate => boolean => used to figure out whether the condition is to be denied - - ## Example - iex> query = Arke.QueryManager.query() - ...> QueryManager.filter(query, Arke.Core.Query.new_filter(Arke.Boundary.ParameterManager.get(:id,:default),:equal,"name",false)) - - ## Return - %Arke.Core.Query{...} - + It adds a filter for the given query """ @spec filter( query :: Query.t(), - parameter :: Arke.t() | String.t() | atom(), + parameter :: %Arke{} | String.t() | atom(), operator :: operator(), - value :: String.t() | boolean() | number(), + value :: any, negate :: boolean() ) :: Query.t() def filter(query, parameter, operator, value, negate \\ false), @@ -544,19 +461,14 @@ defmodule Arke.QueryManager do Define a criteria to order the element returned from a query ## Parameter - - query => refer to `query/1` - - order => int => number of element to return - - ## Example - iex> query = QueryManager.query() - ...> parameter = Arke.Boundary.ParameterManager.get(:id,:default) - ...> QueryManager.order(query, parameter, :asc) - + - `query` => refer to `query/1` + - `parameter` => used to order the query + - `direction` => way of sorting the results (ascending or descending) """ @spec order( query :: Query.t(), - parameter :: Arke.t() | String.t() | atom(), - direction :: atom() + parameter :: %Arke{} | String.t() | atom(), + direction :: :asc | :desc ) :: Query.t() def order(query, parameter, direction), do: Query.add_order(query, get_parameter(query, parameter), direction) @@ -565,32 +477,32 @@ defmodule Arke.QueryManager do Set the offset of the query ## Parameter - - query => refer to `query/1` - - offset => int => offset of the query - - ## Example - iex> query = QueryManager.query() - ...> QueryManager.where(query, id: "name") |> QueryManager.offset(5) - + - `query` -> refer to `query/1` + - `offset` -> offset of the query """ @spec offset(query :: Query.t(), offset :: integer()) :: Query.t() def offset(query, offset), do: Query.set_offset(query, offset) @doc """ - Set the limit of the element to be returned from a query + Set the limit of the results of a query ## Parameter - - query => refer to `query/1` - - limit => int => number of element to return - - ## Example - iex> query = QueryManager..query() - ...> QueryManager.where(query, id: "name") |> QueryManager.limit(1) - + - `query` -> refer to `query/1` + - `limit` -> number of element to return """ @spec limit(query :: Query.t(), limit :: integer()) :: Query.t() def limit(query, limit), do: Query.set_limit(query, limit) + @doc """ + Get both the total count of the elements and the elements returned from the query + + ## Parameter + - `query` -> refer to `query/1` + - `offset` -> offset of the query + - `limit` -> number of element to return + """ + @spec pagination(query :: Query.t(), offset :: integer(), limit :: integer()) :: + {count :: integer(), elements :: [] | [%Unit{}]} def pagination(query, offset, limit) do tmp_query = %{query | orders: []} count = count(tmp_query) @@ -599,53 +511,42 @@ defmodule Arke.QueryManager do end @doc """ - Return all the results from a query - + Run the given query and return all the results ## Parameter - - query => refer to `query/1` + - query -> refer to `query/1` """ - @spec all(query :: Query.t()) :: [Unit.t()] | [] + @spec all(query :: Query.t()) :: [%Unit{}] | [] def all(query), do: execute_query(query, :all) @doc """ - Return the first result of a query - + Run the given query and return only the first result ## Parameter - - query => refer to `query/1` + - `query` -> refer to `query/1` + """ - @spec one(query :: Query.t()) :: Unit.t() | nil + @spec one(query :: Query.t()) :: %Unit{} | nil def one(query), do: execute_query(query, :one) @doc """ - Return the query as a string - + Return the given query as a string ## Parameter - - query => refer to `query/1` + - `query` -> refer to `query/1` """ @spec raw(query :: Query.t()) :: String.t() def raw(query), do: execute_query(query, :raw) @doc """ - Return the count of the element returned from a query - + Run the given query and return only the number of the records that have been found ## Parameter - - query => refer to `query/1` + - `query` -> refer to `query/1` """ @spec count(query :: Query.t()) :: integer() def count(query), do: execute_query(query, :count) @doc """ - Return a string which represent the query itself - + Return the given query as Ecto pseudo query ## Parameter - - query => refer to `query/1` - - ## Example - iex> query = QueryManager.query() - ...> QueryManager.where(query, id: "name") |> QueryManager.pseudo_query - - ## Return - #Ecto.Query<> + - `query` -> refer to `query/1` """ @spec pseudo_query(query :: Query.t()) :: Ecto.Query.t() def pseudo_query(query), do: execute_query(query, :pseudo_query) diff --git a/lib/arke/struct_manager.ex b/lib/arke/struct_manager.ex index 64015db..3c17eca 100644 --- a/lib/arke/struct_manager.ex +++ b/lib/arke/struct_manager.ex @@ -25,30 +25,23 @@ defmodule Arke.StructManager do alias Arke.Core.{Unit, Arke} @type parameter :: %{ - default: String.t() | boolean() | atom() | map() | list() | nil, + default: any(), helper_text: String.t() | nil, id: String.t(), label: String.t(), required: boolean() | nil, type: String.t(), - key: String.t() | boolean() | atom() | map() | list() | nil + key: any() } @doc """ - Function that encodes a Unit or list of Unit + Function thate encodes the given Unit/Units to json ## Parameters - - unit => [%{arke_struct}] | %{arke_struct} => unit or list of units that we want to encode - - type => :json => desired encode type - - ## Example - iex> units = QueryManager.filter_by(arke_id: id) - ...> StructManager.encode(units, type: :json) - + - `unit` -> unit or list of units that we want to encode + - `type` -> desired encode type """ - @spec encode(unit :: Unit.t(), format :: atom()) :: %{ - key: String.t() | number() | boolean() | atom() - } + @spec encode(unit :: [%Unit{}, ...], format :: :json) :: %{atom() => any} | [...] def encode(unit, opts \\ []) def encode(unit, opts) do @@ -57,7 +50,6 @@ defmodule Arke.StructManager do handle_encode(unit, type, load_links, opts) end - def encode(unit, opts) defp handle_encode(u, type, load_links, opts \\ []) defp handle_encode([], _, _, _), do: [] @@ -172,9 +164,8 @@ defmodule Arke.StructManager do end def encode(_unit, _format), do: raise("Must pass a valid unit") - def validate_data(id, value, arke, opts \\ []) - def validate_data(id, value, arke, opts) do + defp validate_data(id, value, arke, opts \\ []) do param = ArkeManager.get_parameter(arke, id) new_value = parse_value(value, param, Enum.into(opts, %{})) %{id => new_value} @@ -234,38 +225,30 @@ defmodule Arke.StructManager do end @doc """ - Function that decodes data into a Unit or list of Unit + Function that convert the given json data to a valid Unit struct ## Parameters - - project => :atom => identify the `Arke.Core.Project` - - arke_id => atom | string => arke id - - json => %{key: value} => json data that we want to decode - - type => :json => data input type + - `project` -> identify the `Arke.Core.Project` + - `arke_id` -> the model to use to load the data + - `data` -> json data that we want to decode + - `type` -> data input type - ## Example - iex> StructManager.decode(:arke, my_json_data, :json) """ @spec decode( project :: atom(), arke_id :: atom(), - json :: %{key: String.t() | number() | boolean() | atom()}, + data :: %{key: any()}, format :: atom() - ) :: Unit.t() - def decode(project, arke_id, json, :json) when is_atom(arke_id) do + ) :: %Unit{} + def decode(project, arke_id, data, :json) when is_atom(arke_id) do ArkeManager.get(arke_id, project) - |> Unit.load(json) + |> Unit.load(data) end - def decode(project, arke_id, json, :json) when is_binary(arke_id) do - ArkeManager.get(String.to_existing_atom(arke_id), project) - |> Unit.load(json) - end + def decode(project, arke_id, data, :json) when is_binary(arke_id), do: decode(project, String.to_existing_atom(arke_id), data, :json) def decode(_project, _arke_id, _json, _format), do: raise("Must pass valid data") - def load(arke_id, data) do - end - defp handle_default_value( %{arke_id: :string, data: %{default_string: default_string}} = _, value @@ -317,21 +300,29 @@ defmodule Arke.StructManager do defp handle_default_value(_, value), do: value @doc """ - Function that returns a Struct that describes an Arke or a Unit + Get an Arke's parameters struct ## Parameters - - unit => %Unit{} | %Arke{} => unit or arke struct - - ## Example - iex> arke = ArkeManager.get(:test, :default) - ...> StructManager.get_struct(arke) - """ - @spec get_struct(arke :: Unit.t()) :: %{parameters: [parameter()], label: String.t()} + - `arke` -> Arke struct + """ + @spec get_struct(arke :: %Unit{}) :: %{parameters: [parameter()], label: String.t()} def get_struct(%{arke_id: :arke, data: data} = arke) do struct = %{parameters: get_struct_parameters(arke, %{}), label: data.label} ArkeManager.call_func(arke, :after_get_struct, [arke, struct]) end + @doc """ + Get a Unit Struct with only the data defined by the `opts` + + ## Parameters + - `arke` -> Arke model to load the data + - `unit` -> Unit struct + - `opts` -> Data we want to include or exclude + """ + @spec get_struct(arke :: %Unit{}, unit :: %Unit{}, opts :: [{:include, [atom]} | {:exclude, [atom]}] | [...] | [] ) :: %{ + parameters: [parameter()], + label: String.t() + } def get_struct(arke, %{data: data} = unit, opts) do struct = %{ parameters: get_struct_parameters(arke, unit, opts), @@ -341,6 +332,13 @@ defmodule Arke.StructManager do ArkeManager.call_func(arke, :after_get_struct, [arke, unit, struct]) end + @doc """ + Get a Unit Struct + """ + @spec get_struct(arke :: %Unit{}, unit :: %Unit{}) :: %{ + parameters: [parameter()], + label: String.t() + } def get_struct(arke, %{data: data} = unit) do struct = %{ parameters: get_struct_parameters(arke, unit, %{}), @@ -350,10 +348,6 @@ defmodule Arke.StructManager do ArkeManager.call_func(arke, :after_get_struct, [arke, unit, struct]) end - @spec get_struct(arke :: Unit.t(), opts :: list()) :: %{ - parameters: [parameter()], - label: String.t() - } def get_struct(%{arke_id: :arke, data: data} = arke, opts) do struct = %{ parameters: get_struct_parameters(arke, opts), diff --git a/lib/arke/system.ex b/lib/arke/system.ex index 3123740..b076c4f 100644 --- a/lib/arke/system.ex +++ b/lib/arke/system.ex @@ -13,9 +13,19 @@ # limitations under the License. defmodule Arke.System do + @moduledoc """ + Core module which handle all the management functions for an Arke. + See `Arke.Example.System.MacroArke` to get a list of all the available functions. + """ + + @doc """ + This macro is used whenever we want to edit the default behaviour + + use Arke.System + """ defmacro __using__(_) do quote do - # @after_compile __MODULE__ + Module.register_attribute(__MODULE__, :arke, accumulate: false, persist: true) Module.register_attribute(__MODULE__, :groups, accumulate: true, persist: true) Module.register_attribute(__MODULE__, :parameters, accumulate: true, persist: false) @@ -25,34 +35,57 @@ defmodule Arke.System do import unquote(__MODULE__), only: [arke: 1, arke: 2, parameter: 3, parameter: 2, group: 1, group: 2] - # @before_compile unquote(__MODULE__) - + # Return the Arke struct for the given module + @doc false def arke_from_attr(), do: Keyword.get(__MODULE__.__info__(:attributes), :arke, []) |> List.first() + @doc false def groups_from_attr(), do: Keyword.get(__MODULE__.__info__(:attributes), :groups, []) + @doc false def base_parameters() do unit = arke_from_attr() unit.data.parameters end + @doc false def on_load(data, _persistence_fn), do: {:ok, data} + + @doc false def before_load(data, _persistence_fn), do: {:ok, data} + + @doc false def on_validate(arke, unit), do: {:ok, unit} + + @doc false def before_validate(arke, unit), do: {:ok, unit} + + @doc false def on_create(arke, unit), do: {:ok, unit} + + @doc false def before_create(arke, unit), do: {:ok, unit} + @doc false def on_struct_encode(_, _, data, opts), do: {:ok, data} + @doc false def before_struct_encode(_, unit), do: {:ok, unit} + @doc false def on_update(arke, old_unit, unit), do: {:ok, unit} + @doc false def before_update(arke, unit), do: {:ok, unit} + @doc false def on_delete(arke, unit), do: {:ok, unit} + @doc false def before_delete(arke, unit), do: {:ok, unit} + @doc false def after_get_struct(arke, unit, struct), do: struct + + @doc false def after_get_struct(arke, struct), do: struct + @doc false def import(%{runtime_data: %{conn: %{method: "POST"}=conn}, metadata: %{project: project}} = arke) do member = ArkeAuth.Guardian.Plug.current_resource(conn) mode = Map.get(conn.body_params, "mode", "default") @@ -63,6 +96,7 @@ defmodule Arke.System do end end + @doc false defp import_units(arke, project, member, file, mode) do {:ok, ref} = Enum.at(Xlsxir.multi_extract(file.path), 0) all_units = get_all_units_for_import(project) @@ -72,7 +106,7 @@ defmodule Arke.System do header_file = Enum.at(file_as_list, 0) rows = file_as_list |> List.delete_at(0) - header = get_header_for_import(project, arke, header_file) |> parse_haeder_for_import(header_file) + header = get_header_for_import(project, arke, header_file) |> parse_header_for_import(header_file) {correct_units, error_units} = Enum.with_index(rows) |> Enum.reduce({[], []}, fn {row, index}, {correct_units, error_units} -> case Enum.filter(row, & !is_nil(&1)) do @@ -114,6 +148,7 @@ defmodule Arke.System do defp parse_cell(value) when is_tuple(value), do: Kernel.inspect(value) defp parse_cell(value), do: value + @doc false defp get_header_for_import(project, arke, header_file) do Enum.reduce(Enum.with_index(header_file), [], fn {cell, index}, acc -> case Arke.Boundary.ArkeManager.get_parameter(arke, project, cell) do @@ -122,7 +157,7 @@ defmodule Arke.System do end end) end - defp parse_haeder_for_import(header, header_file) do + defp parse_header_for_import(header, header_file) do Enum.reduce(Enum.with_index(header_file), [], fn {cell, index}, acc -> case cell do nil -> acc @@ -136,8 +171,10 @@ defmodule Arke.System do end) end + @doc false defp get_all_units_for_import(project), do: [] + @doc false defp load_units(project, arke, header, row, _, "default") do args = Enum.reduce(header, [], fn {parameter_id, index}, acc -> acc = Keyword.put(acc, String.to_existing_atom(parameter_id), Enum.at(row, index)) @@ -148,7 +185,9 @@ defmodule Arke.System do do: {:ok, args}, else: ({:error, errors} -> {:error, args, errors}) end + @doc false defp get_existing_units_for_import(project, arke, header, units_args), do: [] + @doc false defp check_existing_units_for_import(project, arke, header, units_args, existing_units), do: true defp get_import_value(header, row, column) do index = Enum.find(header, fn {k, v} -> k == column end) |> elem(1) @@ -181,33 +220,18 @@ defmodule Arke.System do end end - # defmacro __before_compile__(env) do - # end - # - # def compile(translations) do - # - # end - ###################################################################################################################### # ARKE MACRO ######################################################################################################### ###################################################################################################################### @doc """ - Macro to create an arke struct with the given parameters. - Usable only via `code` and not `iex`. - + Macro to manager an arke and its related functions ## Example - arke do - parameter :custom_parameter, :string, required: true, unique: true - parameter :custom_parameter2, :string, required: true, values: ["value1", "value2"] - parameter :custom_parameter3, :integer, required: true, values: [%{label: "option 1", value: 1},%{label: "option 2", value: 2}] - parameter :custom_parameter4, :dict, required: true, default: %{"default_dict_key": "default_dict_value"} + arke id: :some_id do end - ## Return - %Arke.Core.'{arke_struct}'{} - + From now on all the overridable functions can be edited and all the public functions will be used as API custom function """ @spec arke(args :: list(), Macro.t()) :: %{} defmacro arke(opts \\ [], do: block) do @@ -276,14 +300,13 @@ defmodule Arke.System do # PARAMETER MACRO #################################################################################################### ###################################################################################################################### - @doc """ + @doc false && """ Macro used to define parameter in an arke. See example above `arke/2` """ @spec parameter(id :: atom(), type :: atom(), opts :: list()) :: Macro.t() defmacro parameter(id, type, opts \\ []) do - # parameter_dict = Arke.System.BaseParameter.parameter_options(opts, id, type) quote bind_quoted: [id: id, type: type, opts: opts] do opts = Arke.System.BaseParameter.check_enum(type, opts) @parameters %{id: id, arke: type, metadata: opts} @@ -298,10 +321,9 @@ defmodule Arke.System do # GROUP MACRO #################################################################################################### ###################################################################################################################### - @doc """ - Macro used to define parameter in an arke. + @doc false && """ + Assign an arke to a given group. See example above `arke/2` - """ @spec group(id :: atom(), opts :: list()) :: Macro.t() defmacro group(id, opts \\ []) do @@ -316,40 +338,38 @@ defmodule Arke.System do end defmodule Arke.System.Arke do + @moduledoc false use Arke.System end defmodule Arke.System.BaseArke do + @moduledoc false defstruct [:id, :label, :active, :type, :parameters, :metadata] end defmodule Arke.System.BaseParameter do + @moduledoc false && """ + This module is used as entrypoint for every Parameter created which does not have a module associated + """ defstruct [:id, :label, :active, :metadata, :type, :parameters] - @doc """ + @doc false && """ Used in the parameter macro to create the map for every parameter which have the `values` option. It check if the given value are the same type as the parameter type and then creates a list of map as follows: [%{label "given label", value: given_value}, %{label "given label two ", value: given_value_two}] Keep in mind that if the values are declared as list instead of map the label will be generated from the value itself. - ... omitted code parameter :custom_parameter2, :integer, required: true, values: [1, 2, 3] - ... omitted code - The code above will results in an `{arke_struct}` with the values as follows - - ... omitted code + The code above will be modified to be as follows values: [%{label "1", value: 1}, %{label "2", value: 2}, %{label "3", value: 3}] - - ... omitted code - """ - @spec parameter_options(opts :: list(), id :: atom(), type :: atom()) :: %{ + @spec parameter_options(opts :: [...], id :: atom(), type :: atom()) :: %{ type: atom(), - opts: list() + opts: [...] } def parameter_options(opts, id, type) do opts = @@ -360,8 +380,14 @@ defmodule Arke.System.BaseParameter do %{type: type, opts: opts} end + @doc """ + Checks if the given parameter type has enum values and if these values are formatted correctly + """ + @spec check_enum(type :: :atom, opts :: [...] | []) :: + [...] | [] | [%{label: String.t(), value: float() | integer() | String.t()}, ...] def check_enum(type, opts) when is_binary(type), do: check_enum(String.to_atom(type),opts) def check_enum(type, opts) do + #todo: move check_enum in another module because the BaseParameter struct is unused enum_parameters = [:string, :integer, :float] case type in enum_parameters do true -> @@ -423,8 +449,8 @@ defmodule Arke.System.BaseParameter do Keyword.put_new(opts, key, default) end - def __enum_parameter__(opts, type) when is_map(opts), do: __enum_parameter__(Map.to_list(opts),type) - def __enum_parameter__(opts, type) do + defp __enum_parameter__(opts, type) when is_map(opts), do: __enum_parameter__(Map.to_list(opts),type) + defp __enum_parameter__(opts, type) do case Keyword.has_key?(opts, :values) do true -> __validate_values__(opts, opts[:values], type) false -> @@ -458,7 +484,7 @@ defmodule Arke.System.BaseParameter do true -> __create_map_values__(__check_map__(values), opts, type, condition) - # FARE RAISE ECCEZIONE DA GESTIRE. CHIAVI DEVONO ESSERE TUTTE UGUALI + # RAISE EXCEPTION TO HANDLE. KEYS MUST ALL BE THE SAME _ -> Keyword.update(opts, :values, nil, fn _current_value -> nil end) end @@ -487,7 +513,7 @@ defmodule Arke.System.BaseParameter do defp __check_map__(values), do: values defp __create_map_values__(values, opts, type, condition) do - # FARE RAISE ECCEZIONE DA GESTIRE. CHIAVI DEVONO ESSERE TUTTE UGUALI + # RAISE EXCEPTION TO HANDLE. KEYS MUST ALL BE THE SAME with true <- Enum.all?(values, fn %{label: l, value: v} -> condition.(l, v) end) do new_values = Enum.map(values, fn k -> @@ -504,7 +530,7 @@ defmodule Arke.System.BaseParameter do defp __get_map_value__(value, _), do: value defp __values_from_list__(values, opts, condition) do - # FARE RAISE ECCEZIONE DA GESTIRE. CHIAVI DEVONO ESSERE TUTTE UGUALI + # RAISE EXCEPTION TO HANDLE. KEYS MUST ALL BE THE SAME with true <- Enum.all?(values, &condition.(&1)) do new_values = Enum.map(values, fn k -> %{label: String.capitalize(to_string(k)), value: k} end) diff --git a/lib/arke/utils/datetime_handler.ex b/lib/arke/utils/datetime_handler.ex index da6663d..e8d0e13 100644 --- a/lib/arke/utils/datetime_handler.ex +++ b/lib/arke/utils/datetime_handler.ex @@ -13,6 +13,9 @@ # limitations under the License. defmodule Arke.Utils.DatetimeHandler do + @moduledoc """ + This module is responsible for managing datetime, date and time values + """ use Timex @datetime_msg "must be %DateTime{} | %NaiveDatetime{} | ~N[YYYY-MM-DDTHH:MM:SS] | ~N[YYYY-MM-DD HH:MM:SS] | ~U[YYYY-MM-DD HH:MM:SS] format" @@ -66,7 +69,11 @@ defmodule Arke.Utils.DatetimeHandler do {:error, @time_msg} end end + @doc """ + Returns a %DateTime{} | %Date{}| %Time{} representing the current moment in time. + """ + @spec now(:datetime | :date | :time) :: DateTime.t() | Date.t() | Time.t() def now(:datetime), do: Timex.set(Timex.now(), microsecond: 0) def now(:date), do: Timex.now() |> Timex.to_date() def now(:time), do: Time.utc_now() |> Time.truncate(:second) @@ -75,6 +82,16 @@ defmodule Arke.Utils.DatetimeHandler do # ----- DATETIME ----- def from_unix(s, unit \\ :second), do: Timex.from_unix(s, unit) + + @doc """ + Parse the given value to a %DateTime{}. + Usually returns `{:ok, value}` except if the `only_value` parameter is set to `true`. In this case it will returns only the value but it still returns `{:error, msg}` if an error occured + Returns `nil` if `nil` is given + """ + @spec parse_datetime( + value :: DateTime.t() | NaiveDateTime.t() | nil | String.t(), + only_value :: boolean() + ) :: nil | {:ok, DateTime.t()} | DateTime.t() | {:error, term()} def parse_datetime(value, only_value \\ false) def parse_datetime(value, true) when is_nil(value), do: value def parse_datetime(value, _only_value) when is_nil(value), do: {:ok, value} @@ -102,6 +119,15 @@ defmodule Arke.Utils.DatetimeHandler do def shift_datetime(opts), do: Timex.shift(now(:datetime), opts) # ----- DATE ----- + @doc """ + Parse the given value to a %Date{}. + Usually returns `{:ok, value}` except if the `only_value` parameter is set to `true`. In this case it will returns only the value but it still returns `{:error, msg}` if an error occured + Returns `nil` if `nil` is given + """ + @spec parse_date( + value :: Date.t() | nil | String.t(), + only_value :: boolean() + ) :: nil | {:ok, Date.t()} | Date.t() | {:error, term()} def parse_date(value, only_value \\ false) def parse_date(value, true) when is_nil(value), do: nil def parse_date(value, _only_value) when is_nil(value), do: {:ok, nil} @@ -126,6 +152,15 @@ defmodule Arke.Utils.DatetimeHandler do # ----- TIME ----- + @doc """ + Parse the given value to a %Time{}. + Usually returns `{:ok, value}` except if the `only_value` parameter is set to `true`. In this case it will returns only the value but it still returns `{:error, msg}` if an error occured + Returns `nil` if `nil` is given + """ + @spec parse_time( + value :: Time.t() | nil | String.t(), + only_value :: boolean() + ) :: nil | {:ok, Date.t()} | Date.t() | {:error, term()} def parse_time(value, only_value \\ false) def parse_time(value, true) when is_nil(value), do: nil def parse_time(value, _only_value) when is_nil(value), do: {:ok, nil} diff --git a/lib/arke/utils/error_generator.ex b/lib/arke/utils/error_generator.ex index d3ebdbf..f68fd60 100644 --- a/lib/arke/utils/error_generator.ex +++ b/lib/arke/utils/error_generator.ex @@ -14,16 +14,7 @@ defmodule Arke.Utils.ErrorGenerator do @moduledoc """ - Documentation for `Arke.Utils.ErrorGenerator` - """ - - @doc """ - Create standardized errors - - ## Parameters - - context => string => the context where the error has been generated - - errors => list | string => the error itself - + Used to standardize all the arke package errors ## Example iex> Arke.Utils.ErrorGenerator.create(:auth, "login error") @@ -31,9 +22,19 @@ defmodule Arke.Utils.ErrorGenerator do {:error , [%{context: "context_value", message: "message_value"}, ...]} """ - @type t() :: {:error, [%{context: String.t(), message: String.t()}]} + @doc """ + Create standardized errors. + ## Note + - If a list of error is provided they will be created with the same context + - If a string is provided the above example will be returned + + ## Parameters + - `context` => the context where the error has been generated + - `errors` => the error itself + + """ - @spec create(context :: String.t(), errors :: list() | String.t()) :: + @spec create(context :: String.t() | atom(), errors :: [String.t()] | String.t()) :: {:error, [%{context: String.t(), message: String.t()}]} def create(context, errors) when is_list(errors) do {:error, create_map(context, errors)} diff --git a/lib/arke/validator.ex b/lib/arke/validator.ex index e6d7df6..6994bad 100644 --- a/lib/arke/validator.ex +++ b/lib/arke/validator.ex @@ -14,7 +14,7 @@ defmodule Arke.Validator do @moduledoc """ - This module provide validation before assign a certain value to an `{arke_struct}` + This module provide the data validation of a unit before its creation """ alias Arke.Boundary.{ArkeManager, ParameterManager} alias Arke.QueryManager, as: QueryManager @@ -22,29 +22,17 @@ defmodule Arke.Validator do alias Arke.Utils.DatetimeHandler, as: DatetimeHandler alias Arke.Core.{Arke, Unit, Parameter} - @type func_return() :: {:ok, Unit.t()} | Error.t() @doc """ - Function to check the given data based on the fields in the reference schema. - + Check if the given unit has valid data. + In order to do so, it uses the arke_id to get the arke which has been used to load the parameters. + Then check whether the value of each unit parameter meets the type and requirements of those associated with the arke ## Parameters - - unit => => %Arke.Core.Unit{} => unit to add - - persistence_fn => fun.() => function containng the action that will be performed on the Repo - - project => :atom => identify the `Arke.Core.Project` - - ## Example - iex> schema = Arke.Core.Arke.new - ...> param = Arke.Core.Parameter.new(%{type: :string,opts: [id: :name]}) - ...> schema = Arke.Core.Arke.add_parameter(schema, param) - ...> Arke.Validator.validate(%{arke: schema, data: %{name: "test"}}) - - ## Return - %{:ok,_} - %{:error, [message]} - + - `unit` -> unit to validate + - `persistence_fn` -> operation identifiers + - `project` -> `Arke.Core.Project` """ - @spec validate(unit :: Unit.t(), peristence_fn :: (() -> any()), project :: atom()) :: - func_return() + @spec validate(unit :: %Unit{}, peristence_fn :: :create | :update, project :: atom()) :: {:ok, %Unit{}} | {:error, [Error.t()]} def validate(%{arke_id: arke_id} = unit, persistence_fn, project \\ :arke_system) do with {:ok, unit} <- check_duplicate_unit(unit, project, persistence_fn) do {%{data: data} = unit, errors} = before_validate(unit, project) @@ -110,27 +98,21 @@ defmodule Arke.Validator do defp get_result({unit, _errors} = _res), do: {:ok, unit} @doc """ - Check if the value can be assigned to a given parameter in a specific schema struct. + Check if the value can be assigned to a given parameter for the given Arke. + It always returns a tuple with the given value and a list of errors, if any, otherwise it is empty. ## Parameters - - schema_struct => %{arke_struct} => the element where to find and check the field - - field => :atom => the id of the paramater - - value => any => the value we want to assign to the above field - - project => :atom => identify the `Arke.Core.Project` - - ## Example - iex> Arke.Boundary.ArkeValidator.validate_field(schema_struct, :field_id, value_to_check) - - ## Returns - {value,[]} if success - {value,["parameter label", message ]} in case of error + - `arke` -> used as a model to get all the requirements to validate the value of parameter + - `parameter` -> the id of the parameter + - `value` -> the value we want to assign to the above field + - `project` -> identify the `Arke.Core.Project` """ @spec validate_parameter( - arke :: Arke.t(), + arke :: %Arke{}, parameter :: Sring.t() | atom() | Parameter.parameter_struct(), - value :: String.t() | number() | atom() | boolean() | map() | list(), + value :: any(), project :: atom() - ) :: func_return() + ) :: {any(),[Error.t()]} | {any(),[]} def validate_parameter(arke, parameter, value, project \\ :arke_system) def validate_parameter(arke, parameter, value, project) when is_atom(parameter) do @@ -162,8 +144,8 @@ defmodule Arke.Validator do {value, errors} end - def get_default_value(parameter, value) when is_nil(value), do: handle_default_value(parameter) - def get_default_value(parameter, value), do: value + defp get_default_value(parameter, value) when is_nil(value), do: handle_default_value(parameter) + defp get_default_value(parameter, value), do: value defp parse_value(%{arke_id: :integer, data: %{multiple: false} = data} = _, value) when not is_integer(value) and not is_nil(value) do diff --git a/lib/examples/starter.ex b/lib/examples/starter.ex deleted file mode 100644 index b29671c..0000000 --- a/lib/examples/starter.ex +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright 2023 Arkemis S.r.l. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -defmodule Arke.Examples.Starter do - @moduledoc """ - Module to start all the defaults gen server - """ - alias Arke.Boundary.{ArkeManager} - - @doc """ - It starts all the defaults gen server which will contain all the {arke_struct} - """ - def init() do - Enum.each([:arke], fn app -> - {:ok, modules} = :application.get_key(app, :modules) - - Enum.each(modules, fn mod -> - is_arke = Keyword.get(mod.__info__(:attributes), :is_arke, false) - init_arke_by_struct(mod, is_arke) - end) - end) - - Arke.Boundary.ParameterManager.get_from_persistence() - init_arke_by_persistence() - init_arke_parameters_by_persistence() - end - - defp init_arke_by_struct(struct, [true]) do - args = struct.get_info() - - parameters = - Enum.reduce(struct.get_parameters(), [], fn [p], parameters -> - parameter = Arke.Core.Parameter.new(p) - Arke.Boundary.ParameterManager.create(parameter, :arke_system) - [parameter | parameters] - end) - - args = Keyword.put_new(args, :parameters, parameters) - ArkeManager.set_manager(Arke.Core.Arke.new(args), :arke_system) - end - - defp init_arke_by_struct(_, _), do: nil - - defp init_arke_by_persistence() do - arke = ArkeManager.get(:arke, :arke_system) - arkes = Arke.QueryManager.filter_by(project: :arke_system, arke: "arke") - - Enum.map(arkes, fn %{data: data} -> - ArkeManager.create( - Arke.Core.Arke.new(Map.merge(data, %{parameters: arke.parameters})), - :arke_system - ) - end) - end - - defp init_arke_parameters_by_persistence() do - arke_link = ArkeManager.get(:arke_link, :arke_system) - - arke_parameters = - Arke.QueryManager.query(arke: arke_link) - |> Arke.QueryManager.where(type: "parameter") - |> Arke.QueryManager.all() - - Enum.map(arke_parameters, fn %{data: data} -> - parent_id = String.to_existing_atom(data.parent_id) - child_id = String.to_existing_atom(data.child_id) - ArkeManager.add_parameter(parent_id, :arke_system, child_id, data.metadata) - end) - end - -end diff --git a/lib/examples/system/macro_arke.ex b/lib/examples/system/macro_arke.ex new file mode 100644 index 0000000..b8b85ea --- /dev/null +++ b/lib/examples/system/macro_arke.ex @@ -0,0 +1,178 @@ +defmodule Arke.Example.System.MacroArke do + @moduledoc""" + List of all the available overridable functions in the `Arke.System.__using__/1` macro. \n + All the functions named `before_*` are executed before the overridable function of + `Arke.System.Group.__using__/1` while the ones with `on_*` are executed before. + """ + use Arke.System + + + @doc """ + Overridable function in order to be able to manage the data before the load. + ## Parameters + - `data` => All the data of the Unit we are loading + - `persistence_fn` => Used to identify on which CRUD operations we are loading the unit, so we can pattern match it + """ + @spec before_load(data :: %{atom() => any},persistence_fn :: :create | :update) :: {:ok, %{atom() => any}} | {:error, String.t()} + def before_load(data, _persistence_fn), do: {:ok, data} + + @doc """ + Overridable function in order to be able to manage the data during the unit load + ## Parameters + - `data` => All the data of the Unit we are loading + - `persistence_fn` => Used to identify on which CRUD operations we are loading the unit, so we can pattern match it + """ + @spec on_load(data :: %{atom => any()},persistence_fn :: :create | :update) :: {:ok, %{atom() => any}} | {:error, String.t()} + def on_load(data, _persistence_fn), do: {:ok, data} + + @doc """ + Overridable function in order to be able to manage the data before the validation + ## Parameters + - `arke` => Arke we use as model to validate the parameter + - `unit` => Unit we want to validate against the arke model + """ + @spec before_validate(arke :: %Arke.Core.Arke{},unit :: %Arke.Core.Unit{}) :: {:ok, %Arke.Core.Unit{}} | {:error, String.t()} + def before_validate(_arke, unit), do: {:ok, unit} + + @doc """ + Overridable function in order to be able to manage the data right after the validation + ## Parameters + - `arke` => Arke we used as model to validate the parameter + - `unit` => Unit we have validated against the arke model + """ + @spec on_validate(arke :: %Arke.Core.Arke{},unit :: %Arke.Core.Unit{}) :: {:ok, %Arke.Core.Unit{}} | {:error, String.t()} + def on_validate(_arke, unit), do: {:ok, unit} + + @doc """ + Overridable function in order to be able to manage the data before the creation + ## Parameters + - `arke` => Arke we use as model to create the Unit + - `unit` => Unit we want to create + """ + @spec before_create(arke :: %Arke.Core.Arke{},unit :: %Arke.Core.Unit{}) :: {:ok, %Arke.Core.Unit{}} | {:error, String.t()} + def before_create(_arke, unit), do: {:ok, unit} + + @doc """ + Overridable function in order to be able to right after the creation + ## Parameters + - `arke` => Arke we used as model to create the Unit + - `unit` => Unit we have just created + """ + @spec on_create(arke :: %Arke.Core.Arke{},unit :: %Arke.Core.Unit{}) :: {:ok, %Arke.Core.Unit{}} | {:error, String.t()} + def on_create(_arke, unit), do: {:ok, unit} + + @doc """ + Overridable function in order to be able to manage the data before the encoding + ## Parameters + - `arke` => Arke we uses as model to encode the Unit struct + - `unit` => Unit we want to encode + """ + @spec before_struct_encode(arke :: %Arke.Core.Arke{},unit :: %Arke.Core.Unit{}) :: {:ok, %Arke.Core.Unit{}} | {:error, String.t()} + def before_struct_encode(_arke, unit), do: {:ok, unit} + + @doc """ + Overridable function in order to be able to edit right after the encoding + ## Parameters + - `arke` => Arke we used as model to encode the Unit struct + - `unit` => Unit we have just encoded + - `data` => Data we want to encode + - opts => List of options + """ + @spec on_struct_encode(arke :: %Arke.Core.Arke{},unit :: %Arke.Core.Unit{},data :: %{atom() => any} | %{}, opts :: [] | [...]) :: {:ok, %Arke.Core.Unit{}} | {:error, String.t()} + def on_struct_encode(_arke, _unit, data, _opts), do: {:ok, data} + + @doc """ + Overridable function in order to be able to manage the data before the update + ## Parameters + - `arke` => Arke we use as model to update the Unit + - `unit` => Unit we want to update + """ + @spec before_update(arke :: %Arke.Core.Arke{},unit :: %Arke.Core.Unit{}) :: {:ok, %Arke.Core.Unit{}} | {:error, String.t()} + def before_update(_arke, unit), do: {:ok, unit} + + @doc """ + Overridable function in order to be able to manage the data on the update + ## Parameters + - `arke` => Arke we use as model to update the Unit + - old_`unit` => Unit before the update + - `unit` => Unit after the update + """ + @spec on_update(arke :: %Arke.Core.Arke{},old_unit :: %Arke.Core.Unit{},unit :: %Arke.Core.Unit{}) :: {:ok, %Arke.Core.Unit{}} | {:error, String.t()} + def on_update(_arke, _old_unit, unit), do: {:ok, unit} + + @doc """ + Overridable function in order to be able to manage the data before the deletion + ## Parameters + - `arke` => Arke we use as model to delete the Unit + - `unit` => Unit we want to delete + """ + @spec before_delete(arke :: %Arke.Core.Arke{},unit :: %Arke.Core.Unit{}) :: {:ok, %Arke.Core.Unit{}} | {:error, String.t()} + def before_delete(_arke, unit), do: {:ok, unit} + + @doc """ + Overridable function in order to be able to manage the data during the deletion + ## Parameters + - `arke` => Arke we used as model to delete the Unit + - `unit` => Unit we have just deleted + """ + @spec on_delete(arke :: %Arke.Core.Arke{},unit :: %Arke.Core.Unit{}) :: {:ok, %Arke.Core.Unit{}} | {:error, String.t()} + def on_delete(_arke, unit), do: {:ok, unit} + + @doc """ + Overridable function in order to be able to manage the data of the Unit after the encoding + ## Parameters + - `arke` => Arke we used as model to delete the Unit + - `unit` => Unit we have just deleted + - `struct` => New struct of the unit + """ + @spec after_get_struct(arke :: %Arke.Core.Arke{},unit :: %Arke.Core.Unit{},struct:: %Arke.Core.Unit{}) :: %Arke.Core.Unit{} + def after_get_struct(_arke, _unit, struct), do: struct + + @doc """ + Overridable function in order to be able to manage the data of the Arke after the encoding + ## Parameters + - `arke` => Arke we used as model to delete the Unit + - `struct` => New struct of the Arke + """ + @spec after_get_struct(arke :: %Arke.Core.Arke{},struct:: %Arke.Core.Unit{}) :: %Arke.Core.Arke{} + def after_get_struct(_arke, struct), do: struct + + @doc """ + Overridable function used to import arkes from Excel file + """ + def import(%{runtime_data: %{conn: %{method: "POST"}=_conn}, metadata: %{project: _project}} = _arke), do: {:ok, %{}, 201} + + @doc """ + Overridable function used to import units from excels files + """ + defp import_units(_arke, _project, _member, _file, _mode), do: {:ok, %{}, 201} + + @doc """ + Overridable function used to get all the units that will be used in the import + """ + defp get_all_units_for_import(_project), do: [] + + @doc """ + Overridable function used to create Units struct from the data in an import file + """ + defp load_units(_project, _arke, _header, _row, _, "default"), do: {:ok, []} + + @doc """ + Overridable function used to get all the units already created + """ + defp get_existing_units_for_import(_project, _arke, _header, _units_args), do: [] + + @doc """ + Overridable function used to check if all the units for the import are valid or not + """ + defp check_existing_units_for_import(_project, _arke, _header, _units_args, _existing_units), do: true + + defp get_import_value(_header, _row, _column), do: "" + + @doc """ + Return the values of `Arke.Parameter.BaseParameter` + """ + @spec base_parameters() :: [Arke.Parameter.BaseParameter.t()] | [] + def base_parameters(), do: [] + +end \ No newline at end of file diff --git a/lib/examples/system/macro_group.ex b/lib/examples/system/macro_group.ex new file mode 100644 index 0000000..cd34bfb --- /dev/null +++ b/lib/examples/system/macro_group.ex @@ -0,0 +1,110 @@ +defmodule Arke.Example.System.MacroGroup do + @moduledoc """ + List of all the available overridable functions in the `Arke.System.Group.__using__/1` macro. \n + All the functions named `before_*` are executed after the overridable function of + `Arke.System.__using__/1` while the ones with `on_*` are executed after. + """ + use Arke.System.Group + + @doc """ + Overridable function in order to be able to edit data before the unit load + ## Parameters + - `arke` => Arke of the group we are preparing the load + - `data` => All the data of the Unit we are loading + - `persistence_fn` => Used to identify on which CRUD operations we are loading the unit, so we can pattern match it + """ + @spec before_unit_load(arke :: %Arke.Core.Arke{},data :: %{atom => any()},persistence_fn :: :create | :update) :: {:ok, %{atom() => any}} | {:error, String.t()} + def before_unit_load(_arke, data, _persistence_fn), do: {:ok, data} + + @doc """ + Overridable function in order to be able to edit data during the unit load + ## Parameters + - `arke` => Arke of the group we are permorming the load + - `data` => All the data of the Unit we are loading + - `persistence_fn` => Used to identify on which CRUD operations we are loading the unit, so we can pattern match it + """ + @spec on_unit_load(arke :: %Arke.Core.Arke{},data :: %{atom => any()},persistence_fn :: :create | :update) :: {:ok, %{atom() => any}} | {:error, String.t()} + def on_unit_load(_arke, data, _persistence_fn), do: {:ok, data} + + @doc """ + Overridable function in order to be able to edit data before the validation + ## Parameters + - `arke` => Arke we use as model to validate the parameter + - `unit` => Unit we want to validate against the arke model + """ + @spec before_unit_validate(arke :: %Arke.Core.Arke{},unit :: %Arke.Core.Unit{}) :: {:ok, %Arke.Core.Unit{}} | {:error, String.t()} + def before_unit_validate(_arke, unit), do: {:ok, unit} + + @doc """ + Overridable function in order to be able to edit data during the validation + ## Parameters + - `arke` => Arke we used as model to validate the parameter + - `unit` => Unit we have validated against the arke model + """ + @spec on_unit_validate(arke :: %Arke.Core.Arke{},unit :: %Arke.Core.Unit{}) :: {:ok, %Arke.Core.Unit{}} | {:error, String.t()} + def on_unit_validate(_arke, unit), do: {:ok, unit} + + @doc """ + Overridable function in order to be able to edit data before the creation + ## Parameters + - `arke` => Arke we use as model to create the Unit + - `unit` => Unit we want to create + """ + @spec before_unit_create(arke :: %Arke.Core.Arke{},unit :: %Arke.Core.Unit{}) :: {:ok, %Arke.Core.Unit{}} | {:error, String.t()} + def before_unit_create(_arke, unit), do: {:ok, unit} + + @doc """ + Overridable function in order to be able to edit data during the creation + ## Parameters + - `arke` => Arke we used as model to create the Unit + - `unit` => Unit we have just created + """ + @spec on_unit_create(arke :: %Arke.Core.Arke{},unit :: %Arke.Core.Unit{}) :: {:ok, %Arke.Core.Unit{}} | {:error, String.t()} + def on_unit_create(_arke, unit), do: {:ok, unit} + + @doc """ + Overridable function in order to be able to edit data during the encoding + ## Parameters + - `unit` => Unit we have just encoded + - `arke` => Arke we used as model to encode the Unit struct + """ + @spec on_unit_struct_encode(arke :: %Arke.Core.Arke{},unit :: %Arke.Core.Unit{}) :: {:ok, %Arke.Core.Unit{}} | {:error, String.t()} + def on_unit_struct_encode(unit, _arke), do: {:ok, unit} + + @doc """ + Overridable function in order to be able to edit data before the update + ## Parameters + - `arke` => Arke we use as model to update the Unit + - `unit` => Unit before the update + """ + @spec before_unit_update(arke :: %Arke.Core.Arke{},unit :: %Arke.Core.Unit{}) :: {:ok, %Arke.Core.Unit{}} | {:error, String.t()} + def before_unit_update(_arke, unit), do: {:ok, unit} + + @doc """ + Overridable function in order to be able to edit data on the update + ## Parameters + - `arke` => Arke we use as model to update the Unit + - `unit` => Unit after the update + """ + @spec on_unit_update(arke :: %Arke.Core.Arke{},unit :: %Arke.Core.Unit{}) :: {:ok, %Arke.Core.Unit{}} | {:error, String.t()} + def on_unit_update(_arke, unit), do: {:ok, unit} + + @doc """ + Overridable function in order to be able to edit data before the deletion + ## Parameters + - `arke` => Arke we use as model to delete the Unit + - `unit` => Unit we want to delete + """ + @spec before_unit_delete(arke :: %Arke.Core.Arke{},unit :: %Arke.Core.Unit{}) :: {:ok, %Arke.Core.Unit{}} | {:error, String.t()} + def before_unit_delete(_arke, unit), do: {:ok, unit} + + @doc """ + Overridable function in order to be able to edit data during the deletion + ## Parameters + - `arke` => Arke we used as model to delete the Unit + - `unit` => Unit we have just deleted + """ + @spec on_unit_delete(arke :: %Arke.Core.Arke{},unit :: %Arke.Core.Unit{}) :: {:ok, %Arke.Core.Unit{}} | {:error, String.t()} + def on_unit_delete(_arke, unit), do: {:ok, unit} + +end \ No newline at end of file diff --git a/mix.exs b/mix.exs index d0a5786..0cf1b23 100644 --- a/mix.exs +++ b/mix.exs @@ -4,6 +4,7 @@ defmodule Arke.MixProject do @version "0.3.1" @scm_url "https://github.com/arkemishub/arke" @site_url "https://arkehub.com" + @logo_url "public/arke-logo.png" def project do [ @@ -24,9 +25,18 @@ defmodule Arke.MixProject do test_coverage: [tool: ExCoveralls], aliases: aliases(), deps: deps(), + source_url: @scm_url, + homepage_url: @site_url, elixirc_paths: elixirc_paths(Mix.env()), elixirc_options: [warnings_as_errors: false], - versioning: versioning() + versioning: versioning(), + docs: [ + # The main page in the docs + main: "Arke", + logo: @logo_url, + extras: ["README.md", "LICENSE"], + groups_for_modules: groups_for_modules(), + ] ] end @@ -52,7 +62,7 @@ defmodule Arke.MixProject do [ {:typed_struct, "~> 0.2.1"}, {:uuid, "~> 1.1"}, - {:ex_doc, "~> 0.28", only: :dev, runtime: false}, + {:ex_doc, "~> 0.32", only: :dev, runtime: false}, {:excoveralls, "~> 0.10", only: :test}, {:credo, "~> 1.6", only: [:dev, :test], runtime: false}, {:timex, "~> 3.7.11"}, @@ -95,4 +105,18 @@ defmodule Arke.MixProject do } ] end + + defp groups_for_modules() do + [ + System: [~r"Arke.System"], + Parameter: [~r"Arke.Core.Parameter"], + Query: [~r"Arke.Core.Query"], + Core: [~r"Arke.Core."], + Utils: [~r"Arke.Utils."], + Example: [~r"Arke.Example."], + ] + end + defp extras do + [] + end end diff --git a/mix.lock b/mix.lock index 28c8cc8..efc3cbf 100644 --- a/mix.lock +++ b/mix.lock @@ -1,16 +1,18 @@ %{ "bunt": {:hex, :bunt, "0.2.1", "e2d4792f7bc0ced7583ab54922808919518d0e57ee162901a16a1b6664ef3b14", [:mix], [], "hexpm", "a330bfb4245239787b15005e66ae6845c9cd524a288f0d141c148b02603777a5"}, - "castore": {:hex, :castore, "0.1.22", "4127549e411bedd012ca3a308dede574f43819fe9394254ca55ab4895abfa1a2", [:mix], [], "hexpm", "c17576df47eb5aa1ee40cc4134316a99f5cad3e215d5c77b8dd3cfef12a22cac"}, + "calendar": {:hex, :calendar, "1.0.0", "f52073a708528482ec33d0a171954ca610fe2bd28f1e871f247dc7f1565fa807", [:mix], [{:tzdata, "~> 0.1.201603 or ~> 0.5.20 or ~> 1.0", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "990e9581920c82912a5ee50e62ff5ef96da6b15949a2ee4734f935fdef0f0a6f"}, + "castore": {:hex, :castore, "1.0.3", "7130ba6d24c8424014194676d608cb989f62ef8039efd50ff4b3f33286d06db8", [:mix], [], "hexpm", "680ab01ef5d15b161ed6a95449fac5c6b8f60055677a8e79acf01b27baa4390b"}, "certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"}, "combine": {:hex, :combine, "0.10.0", "eff8224eeb56498a2af13011d142c5e7997a80c8f5b97c499f84c841032e429f", [:mix], [], "hexpm", "1b1dbc1790073076580d0d1d64e42eae2366583e7aecd455d1215b0d16f2451b"}, - "credo": {:hex, :credo, "1.6.7", "323f5734350fd23a456f2688b9430e7d517afb313fbd38671b8a4449798a7854", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "41e110bfb007f7eda7f897c10bf019ceab9a0b269ce79f015d54b0dcf4fc7dd3"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.29", "149d50dcb3a93d9f3d6f3ecf18c918fb5a2d3c001b5d3305c926cddfbd33355b", [:mix], [], "hexpm", "4902af1b3eb139016aed210888748db8070b8125c2342ce3dcae4f38dcc63503"}, - "ex_doc": {:hex, :ex_doc, "0.29.1", "b1c652fa5f92ee9cf15c75271168027f92039b3877094290a75abcaac82a9f77", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "b7745fa6374a36daf484e2a2012274950e084815b936b1319aeebcf7809574f6"}, - "excoveralls": {:hex, :excoveralls, "0.15.1", "83c8cf7973dd9d1d853dce37a2fb98aaf29b564bf7d01866e409abf59dac2c0e", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "f8416bd90c0082d56a2178cf46c837595a06575f70a5624f164a1ffe37de07e7"}, + "credo": {:hex, :credo, "1.7.0", "6119bee47272e85995598ee04f2ebbed3e947678dee048d10b5feca139435f75", [:mix], [{:bunt, "~> 0.2.1", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2.8", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "6839fcf63d1f0d1c0f450abc8564a57c43d644077ab96f2934563e68b8a769d7"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, + "erlsom": {:hex, :erlsom, "1.5.1", "c8fe2babd33ff0846403f6522328b8ab676f896b793634cfe7ef181c05316c03", [:rebar3], [], "hexpm", "7965485494c5844dd127656ac40f141aadfa174839ec1be1074e7edf5b4239eb"}, + "ex_doc": {:hex, :ex_doc, "0.32.2", "f60bbeb6ccbe75d005763e2a328e6f05e0624232f2393bc693611c2d3ae9fa0e", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "a4480305cdfe7fdfcbb77d1092c76161626d9a7aa4fb698aee745996e34602df"}, + "excoveralls": {:hex, :excoveralls, "0.16.1", "0bd42ed05c7d2f4d180331a20113ec537be509da31fed5c8f7047ce59ee5a7c5", [:mix], [{:hackney, "~> 1.16", [hex: :hackney, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "dae763468e2008cf7075a64cb1249c97cb4bc71e236c5c2b5e5cdf1cfa2bf138"}, "expo": {:hex, :expo, "0.4.1", "1c61d18a5df197dfda38861673d392e642649a9cef7694d2f97a587b2cfb319b", [:mix], [], "hexpm", "2ff7ba7a798c8c543c12550fa0e2cbc81b95d4974c65855d8d15ba7b37a1ce47"}, "file_system": {:hex, :file_system, "0.2.10", "fb082005a9cd1711c05b5248710f8826b02d7d1784e7c3451f9c1231d4fc162d", [:mix], [], "hexpm", "41195edbfb562a593726eda3b3e8b103a309b733ad25f3d642ba49696bf715dc"}, - "finch": {:hex, :finch, "0.14.0", "619bfdee18fc135190bf590356c4bf5d5f71f916adb12aec94caa3fa9267a4bc", [:mix], [{:castore, "~> 0.1", [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", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5459acaf18c4fdb47a8c22fb3baff5d8173106217c8e56c5ba0b93e66501a8dd"}, - "gettext": {:hex, :gettext, "0.22.1", "e7942988383c3d9eed4bdc22fc63e712b655ae94a672a27e4900e3d4a2c43581", [:mix], [{:expo, "~> 0.4.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "ad105b8dab668ee3f90c0d3d94ba75e9aead27a62495c101d94f2657a190ac5d"}, + "finch": {:hex, :finch, "0.16.0", "40733f02c89f94a112518071c0a91fe86069560f5dbdb39f9150042f44dcfb1a", [: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", "f660174c4d519e5fec629016054d60edd822cdfe2b7270836739ac2f97735ec5"}, + "gettext": {:hex, :gettext, "0.22.3", "c8273e78db4a0bb6fba7e9f0fd881112f349a3117f7f7c598fa18c66c888e524", [:mix], [{:expo, "~> 0.4.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "935f23447713954a6866f1bb28c3a878c4c011e802bcd68a726f5e558e4b64bd"}, "google_api_storage": {:hex, :google_api_storage, "0.34.0", "4811461a5065f8a5d59a7fe71efd9c491ba593d20743f0d6f4714153c1dd575f", [:mix], [{:google_gax, "~> 0.4", [hex: :google_gax, repo: "hexpm", optional: false]}], "hexpm", "b343f2f1688acd2e8a2302bed399a24d93626f8e8c809b358214bb0fb7d2669a"}, "google_gax": {:hex, :google_gax, "0.4.1", "310105070626013712c56f8007b6ff7b4ead02ecad1efe7888350c6eaba52783", [:mix], [{:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}, {:poison, ">= 3.0.0 and < 5.0.0", [hex: :poison, repo: "hexpm", optional: false]}, {:tesla, "~> 1.2", [hex: :tesla, repo: "hexpm", optional: false]}], "hexpm", "aef7dce7e04840c0e611f962475e3223d27d50ebd5e7d8e9e963c5e9e3b1ca79"}, "goth": {:hex, :goth, "1.3.1", "f3e08a7f23ea8992ab92d2e1d5c72ea1a8fbd2fe3a46ad1b08d0620f71374fdc", [:mix], [{:finch, "~> 0.9", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:jose, "~> 1.10", [hex: :jose, repo: "hexpm", optional: false]}], "hexpm", "90326c2c0a7acda7fb75fc4a4f0cba84945d8fcb22694d36c9967cec8949937c"}, @@ -20,24 +22,26 @@ "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, "jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"}, "jose": {:hex, :jose, "1.11.5", "3bc2d75ffa5e2c941ca93e5696b54978323191988eb8d225c2e663ddfefd515e", [:mix, :rebar3], [], "hexpm", "dcd3b215bafe02ea7c5b23dafd3eb8062a5cd8f2d904fd9caa323d37034ab384"}, - "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"}, - "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, + "libcluster": {:hex, :libcluster, "3.3.3", "a4f17721a19004cfc4467268e17cff8b1f951befe428975dd4f6f7b84d927fe0", [:mix], [{:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "7c0a2275a0bb83c07acd17dab3c3bfb4897b145106750eeccc62d302e3bdfee5"}, + "makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, + "makeup_erlang": {:hex, :makeup_erlang, "1.0.0", "6f0eff9c9c489f26b69b61440bf1b238d95badae49adac77973cbacae87e3c2e", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "ea7a9307de9d1548d2a72d299058d1fd2339e3d398560a0e46c27dab4891e4d2"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "mime": {:hex, :mime, "1.6.0", "dabde576a497cef4bbdd60aceee8160e02a6c89250d6c0b29e56c0dfb00db3d2", [:mix], [], "hexpm", "31a1a8613f8321143dde1dafc36006a17d28d02bdfecb9e95a880fa7aabd19a7"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, "mint": {:hex, :mint, "1.5.1", "8db5239e56738552d85af398798c80648db0e90f343c8469f6c6d8898944fb6f", [: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", "4a63e1e76a7c3956abd2c72f370a0d0aecddc3976dea5c27eccbecfa5e7d5b1e"}, - "nimble_options": {:hex, :nimble_options, "0.5.2", "42703307b924880f8c08d97719da7472673391905f528259915782bb346e0a1b", [:mix], [], "hexpm", "4da7f904b915fd71db549bcdc25f8d56f378ef7ae07dc1d372cbe72ba950dce0"}, - "nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"}, - "nimble_pool": {:hex, :nimble_pool, "0.2.6", "91f2f4c357da4c4a0a548286c84a3a28004f68f05609b4534526871a22053cde", [:mix], [], "hexpm", "1c715055095d3f2705c4e236c18b618420a35490da94149ff8b580a2144f653f"}, + "nimble_options": {:hex, :nimble_options, "1.0.2", "92098a74df0072ff37d0c12ace58574d26880e522c22801437151a159392270e", [:mix], [], "hexpm", "fd12a8db2021036ce12a309f26f564ec367373265b53e25403f0ee697380f1b8"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, + "nimble_pool": {:hex, :nimble_pool, "1.0.0", "5eb82705d138f4dd4423f69ceb19ac667b3b492ae570c9f5c900bb3d2f50a847", [:mix], [], "hexpm", "80be3b882d2d351882256087078e1b1952a28bf98d0a287be87e4a24a710b67a"}, "parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"}, "poison": {:hex, :poison, "4.0.1", "bcb755a16fac91cad79bfe9fc3585bb07b9331e50cfe3420a24bcc2d735709ae", [:mix], [], "hexpm", "ba8836feea4b394bb718a161fc59a288fe0109b5006d6bdf97b6badfcf6f0f25"}, - "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, - "tesla": {:hex, :tesla, "1.5.1", "f2ba04f5e6ace0f1954f1fb4375f55809a5f2ff491c18ccb09fbc98370d4280b", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "2815d4f6550973d1ed65692d545d079174f6a1f8cb4775f6eb606cbc0666a9de"}, + "tesla": {:hex, :tesla, "1.7.0", "a62dda2f80d4f8a925eb7b8c5b78c461e0eb996672719fe1a63b26321a5f8b4e", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:exjsx, ">= 3.0.0", [hex: :exjsx, repo: "hexpm", optional: true]}, {:finch, "~> 0.13", [hex: :finch, repo: "hexpm", optional: true]}, {:fuse, "~> 2.4", [hex: :fuse, repo: "hexpm", optional: true]}, {:gun, "~> 1.3", [hex: :gun, repo: "hexpm", optional: true]}, {:hackney, "~> 1.6", [hex: :hackney, repo: "hexpm", optional: true]}, {:ibrowse, "4.4.0", [hex: :ibrowse, repo: "hexpm", optional: true]}, {:jason, ">= 1.0.0", [hex: :jason, repo: "hexpm", optional: true]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.0", [hex: :mint, repo: "hexpm", optional: true]}, {:msgpax, "~> 2.3", [hex: :msgpax, repo: "hexpm", optional: true]}, {:poison, ">= 1.0.0", [hex: :poison, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: true]}], "hexpm", "2e64f01ebfdb026209b47bc651a0e65203fcff4ae79c11efb73c4852b00dc313"}, "timex": {:hex, :timex, "3.7.11", "bb95cb4eb1d06e27346325de506bcc6c30f9c6dea40d1ebe390b262fad1862d1", [:mix], [{:combine, "~> 0.10", [hex: :combine, repo: "hexpm", optional: false]}, {:gettext, "~> 0.20", [hex: :gettext, repo: "hexpm", optional: false]}, {:tzdata, "~> 1.1", [hex: :tzdata, repo: "hexpm", optional: false]}], "hexpm", "8b9024f7efbabaf9bd7aa04f65cf8dcd7c9818ca5737677c7b76acbc6a94d1aa"}, "typed_struct": {:hex, :typed_struct, "0.2.1", "e1993414c371f09ff25231393b6430bd89d780e2a499ae3b2d2b00852f593d97", [:mix], [], "hexpm", "8f5218c35ec38262f627b2c522542f1eae41f625f92649c0af701a6fab2e11b3"}, "tzdata": {:hex, :tzdata, "1.1.1", "20c8043476dfda8504952d00adac41c6eda23912278add38edc140ae0c5bcc46", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "a69cec8352eafcd2e198dea28a34113b60fdc6cb57eb5ad65c10292a6ba89787"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, "uuid": {:hex, :uuid, "1.1.8", "e22fc04499de0de3ed1116b770c7737779f226ceefa0badb3592e64d5cfb4eb9", [:mix], [], "hexpm", "c790593b4c3b601f5dc2378baae7efaf5b3d73c4c6456ba85759905be792f2ac"}, + "xlsxir": {:hex, :xlsxir, "1.6.4", "d1e69439cbd9edc1190950f9f883ac364e1f31641e0395ccdb27761791b169a3", [:mix], [{:erlsom, "~> 1.5", [hex: :erlsom, repo: "hexpm", optional: false]}], "hexpm", "38e91f65eb8a4c8dea07d941c8b7e21baf8c8d4938232395c9ffd19d2eb071f2"}, } diff --git a/public/arke-logo.png b/public/arke-logo.png new file mode 100644 index 0000000..d09ba60 Binary files /dev/null and b/public/arke-logo.png differ