defmodule Chalk.Query do alias Chalk.Client alias Chalk.Client.Request alias Chalk.Common.ChalkError alias Chalk.Common.ChalkException defmodule FeatureMeta do @derive Jason.Encoder defstruct cache_hit: nil, chosen_resolver_fqn: nil, primitive_type: nil, version: nil @type t :: %__MODULE__{ cache_hit: boolean(), chosen_resolver_fqn: String.t(), primitive_type: String.t(), version: integer() } end defmodule FeatureResult do @derive Jason.Encoder defstruct error: nil, field: nil, meta: nil, pkey: nil, ts: nil, value: nil @type t :: %__MODULE__{ error: ChalkError.t(), field: String.t(), meta: FeatureMeta.t(), pkey: String.t(), ts: String.t(), value: any() } end defmodule QueryMeta do @derive Jason.Encoder defstruct deployment_id: nil, environment_id: nil, environment_name: nil, execution_duration_s: nil, query_hash: nil, query_id: nil, query_timestamp: nil @type t :: %__MODULE__{ deployment_id: String.t(), environment_id: String.t(), environment_name: String.t(), execution_duration_s: float(), query_hash: String.t(), query_id: String.t(), query_timestamp: String.t() } end defmodule OnlineQueryResponse do @derive Jason.Encoder defstruct data: [], errors: [], meta: nil @type t :: %__MODULE__{ data: [FeatureResult.t()], errors: [ChalkError.t()], meta: QueryMeta.t() } end @doc """ Execute an online query """ @spec online( %{ required(:inputs) => %{ String.t() => String.t() }, required(:outputs) => [String.t()], optional(:staleness) => %{ String.t() => String.t() }, optional(:context) => %{ optional(:environment) => String.t(), optional(:tags) => [String.t()] }, optional(:deployment_id) => String.t(), optional(:query_name) => String.t() }, map() ) :: {:ok, OnlineQueryResponse.t()} | {:error, any()} def online(params, config \\ %{}) do request_operation("v1/query/online", params, config) end defp request_operation(endpoint, params, config) do c = config[:client] || Chalk params = if deployment_id = Map.get(config, :deployment_id, System.get_env("DEPLOYMENT_ID")) do # prefer the deployment_id in params, if it exists Map.put_new(params, :deployment_id, deployment_id) else params end Request |> struct(method: :post, endpoint: endpoint, body: params) |> Request.add_metadata(config) |> c.send_request(Client.new(config)) |> c.handle_response(&map_query_response(&1)) end defp map_query_response(body) do Poison.Decode.decode(body, %{ as: %OnlineQueryResponse{ data: [ %FeatureResult{ error: %ChalkError{ exception: %ChalkException{} }, meta: %FeatureMeta{} } ], errors: [ %ChalkError{ exception: %ChalkException{} } ], meta: %QueryMeta{} } }) end end