|
1 | 1 | defmodule ElixirProvider do
|
2 |
| - @behaviour OpenFeature.Provider |
3 |
| - |
4 |
| - alias OpenFeature.ResolutionDetails |
5 |
| - alias ElixirProvider.GoFeatureFlagOptions |
6 |
| - alias ElixirProvider.HttpClient |
7 |
| - alias ElixirProvider.DataCollectorHook |
8 |
| - alias ElixirProvider.CacheController |
9 |
| - alias ElixirProvider.ResponseFlagEvaluation |
10 |
| - alias ElixirProvider.GoFWebSocketClient |
11 |
| - alias ElixirProvider.RequestFlagEvaluation |
12 |
| - alias ElixirProvider.ContextTransformer |
13 |
| - alias ElixirProvider.GofEvaluationContext |
14 |
| - |
15 | 2 | @moduledoc """
|
16 |
| - The GO Feature Flag provider for OpenFeature, managing HTTP requests, caching, and flag evaluation. |
17 |
| - """ |
18 |
| - |
19 |
| - defstruct [ |
20 |
| - :options, |
21 |
| - :http_client, |
22 |
| - :data_collector_hook, |
23 |
| - :ws, |
24 |
| - :domain |
25 |
| - ] |
26 |
| - |
27 |
| - @type t :: %__MODULE__{ |
28 |
| - options: GoFeatureFlagOptions.t(), |
29 |
| - http_client: HttpClient.t(), |
30 |
| - data_collector_hook: DataCollectorHook.t() | nil, |
31 |
| - ws: GoFWebSocketClient.t(), |
32 |
| - domain: String.t() |
33 |
| - } |
34 |
| - |
35 |
| - @impl true |
36 |
| - def initialize(%__MODULE__{} = provider, domain, _context) do |
37 |
| - {:ok, http_client} = HttpClient.start_http_connection(provider.options) |
38 |
| - CacheController.start_link(provider.options) |
39 |
| - {:ok, data_collector_hook} = DataCollectorHook.start_link(provider.options, http_client) |
40 |
| - {:ok, ws} = GoFWebSocketClient.start_link(provider.options.endpoint) |
41 |
| - |
42 |
| - updated_provider = %__MODULE__{ |
43 |
| - provider |
44 |
| - | domain: domain, |
45 |
| - http_client: http_client, |
46 |
| - data_collector_hook: data_collector_hook, |
47 |
| - ws: ws |
48 |
| - } |
49 |
| - |
50 |
| - {:ok, updated_provider} |
51 |
| - end |
52 |
| - |
53 |
| - @impl true |
54 |
| - def shutdown(%__MODULE__{ws: ws} = provider) do |
55 |
| - Process.exit(ws, :normal) |
56 |
| - CacheController.clear() |
57 |
| - if provider.data_collector_hook, do: DataCollectorHook.shutdown(provider.data_collector_hook) |
58 |
| - :ok |
59 |
| - end |
60 |
| - |
61 |
| - @impl true |
62 |
| - def resolve_boolean_value(provider, key, default, context) do |
63 |
| - generic_resolve(provider, :boolean, key, default, context) |
64 |
| - end |
65 |
| - |
66 |
| - @impl true |
67 |
| - def resolve_string_value(provider, key, default, context) do |
68 |
| - generic_resolve(provider, :string, key, default, context) |
69 |
| - end |
70 |
| - |
71 |
| - @impl true |
72 |
| - def resolve_number_value(provider, key, default, context) do |
73 |
| - generic_resolve(provider, :number, key, default, context) |
74 |
| - end |
75 |
| - |
76 |
| - @impl true |
77 |
| - def resolve_map_value(provider, key, default, context) do |
78 |
| - generic_resolve(provider, :map, key, default, context) |
79 |
| - end |
80 |
| - |
81 |
| - defp generic_resolve(provider, type, flag_key, default_value, context) do |
82 |
| - {:ok, goff_context} = ContextTransformer.transform_context(context) |
83 |
| - goff_request = %RequestFlagEvaluation{user: goff_context, default_value: default_value} |
84 |
| - eval_context_hash = GofEvaluationContext.hash(goff_context) |
85 |
| - |
86 |
| - response_body = |
87 |
| - case CacheController.get(flag_key, eval_context_hash) do |
88 |
| - {:ok, cached_response} -> |
89 |
| - cached_response |
90 |
| - |
91 |
| - :miss -> |
92 |
| - # Fetch from HTTP if cache miss |
93 |
| - case HttpClient.post(provider.http_client, "/v1/feature/#{flag_key}/eval", goff_request) do |
94 |
| - {:ok, response} -> handle_response(flag_key, eval_context_hash, response) |
95 |
| - {:error, reason} -> {:error, {:unexpected_error, reason}} |
96 |
| - end |
97 |
| - end |
98 |
| - |
99 |
| - handle_flag_resolution(response_body, type, flag_key, default_value) |
100 |
| - end |
101 |
| - |
102 |
| - defp handle_response(flag_key, eval_context_hash, response) do |
103 |
| - # Build the flag evaluation struct directly from the response map |
104 |
| - flag_eval = ResponseFlagEvaluation.decode(response) |
105 |
| - |
106 |
| - # Cache the response if it's marked as cacheable |
107 |
| - if flag_eval.cacheable do |
108 |
| - CacheController.set(flag_key, eval_context_hash, response) |
109 |
| - end |
110 |
| - |
111 |
| - {:ok, flag_eval} |
112 |
| - end |
113 |
| - |
114 |
| - defp handle_flag_resolution(response, type, flag_key, _default_value) do |
115 |
| - case response do |
116 |
| - {:ok, %ResponseFlagEvaluation{value: value, reason: reason}} -> |
117 |
| - case {type, value} do |
118 |
| - {:boolean, val} when is_boolean(val) -> |
119 |
| - {:ok, %ResolutionDetails{value: val, reason: reason}} |
120 |
| - |
121 |
| - {:string, val} when is_binary(val) -> |
122 |
| - {:ok, %ResolutionDetails{value: val, reason: reason}} |
123 |
| - |
124 |
| - {:number, val} when is_number(val) -> |
125 |
| - {:ok, %ResolutionDetails{value: val, reason: reason}} |
126 |
| - |
127 |
| - {:map, val} when is_map(val) -> |
128 |
| - {:ok, %ResolutionDetails{value: val, reason: reason}} |
129 |
| - |
130 |
| - _ -> |
131 |
| - {:error, {:variant_not_found, "Expected #{type} but got #{inspect(value)} for flag #{flag_key}"}} |
132 |
| - end |
133 |
| - |
134 |
| - _ -> |
135 |
| - {:error, {:flag_not_found, "Flag #{flag_key} not found"}} |
136 |
| - end |
137 |
| - end |
| 3 | + `ElixirProvider` is a feature flag manager for controlling feature availability in Go applications. |
138 | 4 |
|
| 5 | + It allows toggling features dynamically based on configurations from sources like databases and APIs, enabling flexible, real-time control over application behavior. |
| 6 | + """ |
139 | 7 | end
|
0 commit comments