Packages
phoenix_gen_api
2.17.1
2.22.0
2.21.1
2.21.0
2.20.1
2.20.0
2.19.0
2.18.1
2.18.0
2.17.1
2.17.0
2.16.0
2.15.0
2.14.0
2.13.0
2.12.0
2.11.0
2.10.0
2.9.1
2.9.0
2.8.0
2.7.0
2.6.1
2.6.0
2.5.0
2.4.0
2.3.0
2.2.0
2.1.1
2.1.0
2.0.1
2.0.0
1.2.0
1.1.3
1.1.2
1.1.1
1.1.0
1.0.1
1.0.0
0.2.1
0.2.0
0.1.1
0.1.0
0.0.16
0.0.15
0.0.14
0.0.13
0.0.12
0.0.11
0.0.10
0.0.9
0.0.8
0.0.7
0.0.6
0.0.5
0.0.4
0.0.3
0.0.2
0.0.1
A library for fast develop APIs in Elixir cluster, using Phoenix Channels for transport data, auto pull api configs from service nodes.
Current section
Files
Jump to
Current section
Files
lib/phoenix_gen_api/json_config.ex
defmodule PhoenixGenApi.JsonConfig do
@moduledoc """
Utility module for exporting %FunConfig{} to JSON format.
This module provides functions to generate JSON configuration lists from
PhoenixGenApi function configurations, supporting multiple output formats
and customization options.
## JSON Config List Format
The map format produces a structure like:
%{
"send_direct_message - Send direct message to other user" => %{
"event" => "phoenix_gen_api",
"data" => %{
"user_id" => "user_1",
"device_id" => "device_1",
"request_type" => "send_direct_message",
"request_id" => "request_1",
"service" => "chat",
"version" => "0.0.1",
"args" => %{
"to_user_id" => "",
"content" => "",
"reply_to_id" => ""
}
}
}
}
## Usage
# Default: returns FunConfig structs (Ash Resource type)
PhoenixGenApi.JsonConfig.generate(MyApp.Chat)
#=> [%PhoenixGenApi.Structs.FunConfig{...}, ...]
# As Elixir map (JSON config list format)
PhoenixGenApi.JsonConfig.generate(MyApp.Chat, format: :map)
#=> %{"send_direct_message - ..." => %{...}, ...}
# As JSON string
PhoenixGenApi.JsonConfig.generate(MyApp.Chat, format: :json)
#=> "{\\"send_direct_message - ...\\": {...}, ...}"
# Custom encoder MFA
PhoenixGenApi.JsonConfig.generate(MyApp.Chat, format: {MyEncoder, :encode, []})
# With custom descriptions
PhoenixGenApi.JsonConfig.generate(MyApp.Chat,
format: :map,
descriptions: %{"send_direct_message" => "Send direct message to other user"}
)
# With description function
PhoenixGenApi.JsonConfig.generate(MyApp.Chat,
format: :map,
descriptions: fn fun_config ->
String.replace(fun_config.request_type, "_", " ")
end
)
# With custom arg values
PhoenixGenApi.JsonConfig.generate(MyApp.Chat,
format: :map,
arg_values: %{
"send_direct_message" => %{
"to_user_id" => "user_2",
"content" => "Hello, how are you?",
"reply_to_id" => ""
}
}
)
# With arg values function
PhoenixGenApi.JsonConfig.generate(MyApp.Chat,
format: :map,
arg_values: fn fun_config ->
# Generate example values based on arg types
fun_config.arg_types
|> Enum.map(fn {name, type} -> {name, example_value(type)} end)
|> Map.new()
end
)
"""
alias PhoenixGenApi.ConfigDb
alias PhoenixGenApi.Structs.FunConfig
@type format :: :structs | :map | :json | {module(), atom(), list()}
@type description_source :: %{String.t() => String.t()} | (FunConfig.t() -> String.t()) | nil
@type arg_values_source :: %{String.t() => map()} | (FunConfig.t() -> map()) | nil
@doc """
Generates JSON configuration from function configurations.
## Parameters
- `service_or_services`: A service name (string/atom) or list of service names
- `opts`: Options keyword list
## Options
- `:format` - Output format (`:structs`, `:map`, `:json`, or `{module, function, args}`)
- `:descriptions` - Custom descriptions (map or function)
- `:arg_values` - Custom arg values (map or function)
- `:user_id` - User ID for the request (default: "user_1")
- `:device_id` - Device ID for the request (default: "device_1")
- `:request_id` - Request ID for the request (default: "request_1")
- `:event` - Event name (default: "phoenix_gen_api")
## Returns
Depends on the `:format` option:
- `:structs` - List of `%FunConfig{}` structs
- `:map` - Map in JSON config list format
- `:json` - JSON string
- `{module, function, args}` - Result of calling the custom encoder
"""
@spec generate(String.t() | atom() | [String.t() | atom()], keyword()) ::
[FunConfig.t()] | map() | String.t() | any()
def generate(service_or_services, opts \\ []) when is_list(opts) do
fun_configs = fetch_fun_configs(service_or_services)
format = Keyword.get(opts, :format, :structs)
result =
case format do
:structs ->
fun_configs
:map ->
build_json_config_map(fun_configs, opts)
:json ->
fun_configs
|> build_json_config_map(opts)
|> JSON.encode!()
{module, function, args} when is_atom(module) and is_atom(function) and is_list(args) ->
config_map = build_json_config_map(fun_configs, opts)
apply(module, function, [config_map | args])
other ->
raise ArgumentError, "invalid format: #{inspect(other)}"
end
result
end
@doc """
Exports a single %FunConfig{} to JSON config map format.
## Parameters
- `fun_config`: A `%FunConfig{}` struct
- `opts`: Options keyword list (same as `generate/2`)
## Returns
A map in the JSON config format for a single function.
"""
@spec export_single(FunConfig.t(), keyword()) :: {String.t(), map()}
def export_single(fun_config = %FunConfig{}, opts \\ []) do
user_id = Keyword.get(opts, :user_id, "user_1")
device_id = Keyword.get(opts, :device_id, "device_1")
request_id = Keyword.get(opts, :request_id, "request_1")
event = Keyword.get(opts, :event, "phoenix_gen_api")
key = build_key(fun_config, opts)
value = build_config_value(fun_config, user_id, device_id, request_id, event, opts)
{key, value}
end
@doc """
Exports all functions from all services.
## Parameters
- `opts`: Options keyword list (same as `generate/2`)
## Returns
Same as `generate/2` but for all services.
"""
@spec export_all(keyword()) :: [FunConfig.t()] | map() | String.t() | any()
def export_all(opts \\ []) do
generate(ConfigDb.get_all_services(), opts)
end
@doc """
Exports functions from a specific service.
## Parameters
- `service`: Service name (string or atom)
- `opts`: Options keyword list (same as `generate/2`)
## Returns
Same as `generate/2` for a single service.
"""
@spec export_service(String.t() | atom(), keyword()) ::
[FunConfig.t()] | map() | String.t() | any()
def export_service(service, opts \\ []) when is_binary(service) or is_atom(service) do
generate(service, opts)
end
# Private Functions
defp fetch_fun_configs(service_or_services) do
services =
case service_or_services do
service when is_binary(service) or is_atom(service) ->
[service]
services when is_list(services) ->
services
end
ConfigDb.get_functions_from_services(services)
|> Enum.flat_map(fn {service, request_types} ->
Enum.flat_map(request_types, fn {request_type, versions} ->
# Get the latest version for each request type
latest_version = versions |> Enum.sort() |> List.last()
case ConfigDb.get(service, request_type, latest_version) do
{:ok, config = %FunConfig{}} -> [config]
_ -> []
end
end)
end)
end
defp build_json_config_map(fun_configs, opts) do
fun_configs
|> Enum.map(fn fun_config ->
export_single(fun_config, opts)
end)
|> Map.new()
end
defp build_key(fun_config = %FunConfig{}, opts) do
description = get_description(fun_config, opts)
"#{fun_config.request_type} - #{description}"
end
defp build_config_value(fun_config = %FunConfig{}, user_id, device_id, request_id, event, opts) do
%{
"event" => event,
"data" => %{
"user_id" => user_id,
"device_id" => device_id,
"request_type" => fun_config.request_type,
"request_id" => request_id,
"service" => to_string(fun_config.service),
"version" => fun_config.version,
"args" => get_arg_values(fun_config, opts)
}
}
end
defp get_description(fun_config = %FunConfig{}, opts) do
case Keyword.get(opts, :descriptions) do
nil ->
fun_config.request_type
descriptions when is_map(descriptions) ->
Map.get(descriptions, fun_config.request_type, fun_config.request_type)
description_fn when is_function(description_fn, 1) ->
description_fn.(fun_config)
other ->
raise ArgumentError, "invalid descriptions option: #{inspect(other)}"
end
end
defp get_arg_values(fun_config = %FunConfig{}, opts) do
case Keyword.get(opts, :arg_values) do
nil ->
build_default_args(fun_config)
arg_values when is_map(arg_values) ->
Map.get(arg_values, fun_config.request_type, build_default_args(fun_config))
arg_values_fn when is_function(arg_values_fn, 1) ->
arg_values_fn.(fun_config)
other ->
raise ArgumentError, "invalid arg_values option: #{inspect(other)}"
end
end
defp build_default_args(%FunConfig{arg_types: nil}), do: %{}
defp build_default_args(%FunConfig{arg_types: arg_types, arg_orders: arg_orders}) do
case arg_orders do
:map ->
arg_types
|> Enum.map(fn {name, arg_config} ->
default_value = get_default_value_from_arg_config(arg_config)
{name, default_value || ""}
end)
|> Map.new()
orders when is_list(orders) ->
orders
|> Enum.map(fn name ->
arg_config = Map.get(arg_types, name)
default_value = get_default_value_from_arg_config(arg_config)
{name, default_value || ""}
end)
|> Map.new()
_ ->
%{}
end
end
# Helper function to extract default_value from arg_config
defp get_default_value_from_arg_config(arg_config) when is_list(arg_config) do
Keyword.get(arg_config, :default_value, nil)
end
defp get_default_value_from_arg_config(_), do: nil
end