From 8b416abb3714bacea28efe89d3ad3535b5364a8e Mon Sep 17 00:00:00 2001 From: Fahad Heylaal Date: Sat, 29 Aug 2026 18:54:06 +0200 Subject: [PATCH] chore: upgrade to v1 SDK --- README.md | 58 ++++++++++------------ lib/featurevisor_example_elixir.ex | 44 ++++++++++++----- mix.exs | 2 +- mix.lock | 2 +- test/featurevisor_example_elixir_test.exs | 60 ++++++++++++++++------- 5 files changed, 102 insertions(+), 64 deletions(-) diff --git a/README.md b/README.md index e6a4563..d9fd634 100644 --- a/README.md +++ b/README.md @@ -2,58 +2,52 @@ A small Elixir application showing how to consume the published [Featurevisor Elixir SDK](https://github.com/featurevisor/featurevisor-elixir) from Hex.pm. -The application downloads a Featurevisor datafile, creates an SDK instance, and evaluates one feature as a flag, variation, and string variable. +Learn more about Featurevisor [here](https://featurevisor.com). + +It fetches a production datafile, creates a Featurevisor instance with customer context, then evaluates: + +- the `commerce_platform` feature as a flag; +- the `checkout_experience` variation; +- integer and array variables from `checkout_experience`; +- the `serviceEndpoints` global object variable; +- the `supportContact` global string variable. ## Requirements Elixir 1.15 or newer. -## Installation +## Run the example ```sh mix deps.get +mix run -e 'FeaturevisorExampleElixir.run()' ``` -## Run it +The datafile comes from the [Featurevisor Cloudflare example](https://github.com/featurevisor/featurevisor-example-cloudflare): -```sh -mix run -e 'FeaturevisorExampleElixir.run()' +```text +https://featurevisor-example-cloudflare.pages.dev/production/featurevisor-sdk-v3.json ``` -The application fetches this production datafile: +Expected output: ```text -https://featurevisor-example-cloudflare.pages.dev/production/featurevisor-mobile.json +Commerce platform enabled: true +Checkout variation: express +Maximum checkout items: 25 +Payment methods: ["card", "wallet"] +Service endpoint: https://api.eu.example.com (timeout: 1200 ms, retries: 4) +Support contact: support-nl@example.com ``` -The important SDK usage is in `lib/featurevisor_example_elixir.ex`: - -```elixir -f = Featurevisor.create_featurevisor(%{ - datafile: datafile, - context: %{ - "userId" => "mobile-user", - "country" => "nl" - } -}) +The important SDK usage is in `lib/featurevisor_example_elixir.ex`. Change its context values to see how Featurevisor selects different rules, variations, and global variable overrides. -enabled = Featurevisor.enabled?(f, "mobile_experience") -variation = Featurevisor.get_variation(f, "mobile_experience") +The `featurevisor` dependency comes from Hex.pm. It is not a local path dependency, so this project demonstrates the same setup used by a new consumer. -welcome_message = - Featurevisor.get_variable_string( - f, - "mobile_experience", - "welcome_message" - ) +## Checks -Featurevisor.close(f) +```sh +make check ``` -The `featurevisor` dependency comes from Hex.pm. It is not a local path dependency, so this project demonstrates the same setup used by a new consumer. - Learn more in the [Featurevisor Elixir SDK documentation](https://featurevisor.com/docs/sdks/elixir/). - -## Featurevisor project - -The datafile is generated by the [Featurevisor example Cloudflare project](https://github.com/featurevisor/featurevisor-example-cloudflare). diff --git a/lib/featurevisor_example_elixir.ex b/lib/featurevisor_example_elixir.ex index 70ae2ec..c8ce4b0 100644 --- a/lib/featurevisor_example_elixir.ex +++ b/lib/featurevisor_example_elixir.ex @@ -1,8 +1,13 @@ defmodule FeaturevisorExampleElixir do @moduledoc "A small application demonstrating the Featurevisor Elixir SDK." - @datafile_url "https://featurevisor-example-cloudflare.pages.dev/production/featurevisor-mobile.json" - @context %{"userId" => "mobile-user", "country" => "nl"} + @datafile_url "https://featurevisor-example-cloudflare.pages.dev/production/featurevisor-sdk-v3.json" + @context %{ + "userId" => "customer-123", + "country" => "nl", + "locale" => "nl-NL", + "accountPlan" => "pro" + } def run(datafile_url \\ @datafile_url) do response = Req.get!(datafile_url) @@ -17,15 +22,22 @@ defmodule FeaturevisorExampleElixir do end def evaluate(datafile, context \\ @context) do - f = Featurevisor.create_featurevisor(%{datafile: datafile, context: context}) + f = + Featurevisor.create_featurevisor(%{ + datafile: datafile, + context: context, + log_level: :error + }) try do %{ - revision: Featurevisor.get_revision(f), - enabled: Featurevisor.enabled?(f, "mobile_experience"), - variation: Featurevisor.get_variation(f, "mobile_experience"), - welcome_message: - Featurevisor.get_variable_string(f, "mobile_experience", "welcome_message") + commerce_enabled: Featurevisor.enabled?(f, "commerce_platform"), + checkout_variation: Featurevisor.get_variation(f, "checkout_experience"), + max_items: Featurevisor.get_variable_integer(f, "checkout_experience", "max_items"), + payment_methods: + Featurevisor.get_variable_array(f, "checkout_experience", "payment_methods"), + service_endpoints: Featurevisor.get_global_variable_object(f, "serviceEndpoints"), + support_contact: Featurevisor.get_global_variable_string(f, "supportContact") } after Featurevisor.close(f) @@ -33,11 +45,19 @@ defmodule FeaturevisorExampleElixir do end defp print_results(results) do - IO.puts("Datafile revision: #{results.revision}") - IO.puts("Feature 'mobile_experience' enabled: #{results.enabled}") - IO.puts("Variation: #{results.variation || "none"}") - IO.puts("Variable 'welcome_message': #{results.welcome_message || "none"}") + endpoints = results.service_endpoints + IO.puts("Commerce platform enabled: #{results.commerce_enabled}") + IO.puts("Checkout variation: #{results.checkout_variation || "unavailable"}") + IO.puts("Maximum checkout items: #{results.max_items || "unavailable"}") + IO.puts("Payment methods: #{inspect(results.payment_methods || [])}") + + IO.puts( + "Service endpoint: #{endpoints["baseUrl"]} " <> + "(timeout: #{endpoints["timeoutMs"]} ms, retries: #{endpoints["retries"]})" + ) + + IO.puts("Support contact: #{results.support_contact || "unavailable"}") :ok end end diff --git a/mix.exs b/mix.exs index 1b6bfb8..b978877 100644 --- a/mix.exs +++ b/mix.exs @@ -17,7 +17,7 @@ defmodule FeaturevisorExampleElixir.MixProject do defp deps do [ - {:featurevisor, "~> 0.1.0"}, + {:featurevisor, "~> 1.0.0"}, {:req, "~> 0.5"} ] end diff --git a/mix.lock b/mix.lock index b74cfe1..0b4484e 100644 --- a/mix.lock +++ b/mix.lock @@ -1,5 +1,5 @@ %{ - "featurevisor": {:hex, :featurevisor, "0.1.0", "6270e132fca891cd60f556c04869379d8671c6308df934ddebaa235ba4bd05a2", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "f058ea23939af508d148647d4bc325f8eecbdb59aa9906f918d7de68479dfe38"}, + "featurevisor": {:hex, :featurevisor, "1.0.0", "f24d4fe45a99338d518d65db1c0e8e43c535b616f97b607680d4c2c5091f8f98", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "2dcb4a5f7696c087c597e0af60e5f4d19dd999f3c3d0181d6ef640c350cb2450"}, "finch": {:hex, :finch, "0.23.0", "e3f9287ac25a8832f848b144c2b57346aac65b205e2e0629a52adfe6507fd837", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.8", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "80e58d3f936f57e3fdf404f83a3642897ae6d9fb642934e46da4d8fe761b99d5"}, "hpax": {:hex, :hpax, "1.0.4", "777de5d433b0fbdc7c418159c8055910faa8047ffdb3d6b31098d2a46cd7685c", [:mix], [], "hexpm", "afc7cb142ebcc2d01ce7816190b98ce5dd49e799111b24249f3443d730f377ca"}, "jason": {:hex, :jason, "1.4.5", "2e3a008590b0b8d7388c20293e9dcc9cf3e5d642fd2a114e4cbbb52e595d940a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0 or ~> 3.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "b0c823996102bcd0239b3c2444eb00409b72f6a140c1950bc8b457d836b30684"}, diff --git a/test/featurevisor_example_elixir_test.exs b/test/featurevisor_example_elixir_test.exs index 0d00b95..160bb39 100644 --- a/test/featurevisor_example_elixir_test.exs +++ b/test/featurevisor_example_elixir_test.exs @@ -1,45 +1,69 @@ defmodule FeaturevisorExampleElixirTest do use ExUnit.Case, async: true - test "evaluates a flag, variation, and variable" do + test "evaluates flags, variations, feature variables, and global variables" do assert FeaturevisorExampleElixir.evaluate(datafile()) == %{ - revision: "test", - enabled: true, - variation: "treatment", - welcome_message: "Welcome to Featurevisor" + commerce_enabled: true, + checkout_variation: "express", + max_items: 25, + payment_methods: ["card", "wallet"], + service_endpoints: %{ + "baseUrl" => "https://api.eu.example.com", + "timeoutMs" => 1200, + "retries" => 4 + }, + support_contact: "support-nl@example.com" } end defp datafile do %{ "schemaVersion" => "2", - "featurevisorVersion" => "3.5.0", + "featurevisorVersion" => "3.7.0", "revision" => "test", "segments" => %{}, "features" => %{ - "mobile_experience" => %{ - "key" => "mobile_experience", + "commerce_platform" => %{ "bucketBy" => "userId", - "variations" => [ - %{"value" => "control"}, - %{"value" => "treatment"} - ], + "force" => [%{"segments" => "*", "enabled" => true}], + "traffic" => [] + }, + "checkout_experience" => %{ + "bucketBy" => "userId", + "variations" => [%{"value" => "express", "weight" => 100}], "variablesSchema" => %{ - "welcome_message" => %{ - "type" => "string", - "defaultValue" => "Welcome" - } + "max_items" => %{"type" => "integer", "defaultValue" => 10}, + "payment_methods" => %{"type" => "array", "defaultValue" => ["card"]} }, "force" => [ %{ "segments" => "*", "enabled" => true, - "variation" => "treatment", - "variables" => %{"welcome_message" => "Welcome to Featurevisor"} + "variation" => "express", + "variables" => %{ + "max_items" => 25, + "payment_methods" => ["card", "wallet"] + } } ], "traffic" => [] } + }, + "variables" => %{ + "serviceEndpoints" => %{ + "type" => "object", + "defaultValue" => %{ + "baseUrl" => "https://api.eu.example.com", + "timeoutMs" => 1200, + "retries" => 4 + }, + "overrides" => [] + }, + "supportContact" => %{ + "type" => "string", + "defaultValue" => "support-nl@example.com", + "overrides" => [] + } } } end