diff --git a/CHANGES.txt b/CHANGES.txt index c4a33fd..9cb4763 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,7 @@ -0.2.1 (February 24, 2025): +1.0.0 (February 25, 2025): - Fixed the SDK language version to correctly reflect the package version when it's installed from hex.pm. + - BREAKING CHANGES: + - Renamed the `:socket_path` option to `:address` in `Split.Supervisor.start_link/1`. 0.2.0 (February 14, 2025): - Added new variations of the get treatment functions to support evaluating flags in given flag set/s: `Split.get_treatments_by_flag_set/3`, `Split.get_treatments_by_flag_sets/3`, `Split.get_treatments_with_config_by_flag_set/3`, and `Split.get_treatments_with_config_by_flag_sets/3`. diff --git a/README.md b/README.md index d0a9e81..98636c9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Split SDK for Elixir -[![hex.pm version](https://img.shields.io/hexpm/v/split_thin_sdk)](https://img.shields.io/hexpm/v/split_thin_sdk) [![Build Status](https://github.com/splitio/elixir-thin-client/actions/workflows/ci-cd.yml/badge.svg)](https://github.com/splitio/elixir-thin-client/actions/workflows/ci-cd.yml) [![Greenkeeper badge](https://badges.greenkeeper.io/splitio/elixir-thin-client.svg)](https://greenkeeper.io/) +[![hex.pm version](https://img.shields.io/hexpm/v/split_thin_sdk)](https://img.shields.io/hexpm/v/split_thin_sdk) [![Build Status](https://github.com/splitio/elixir-thin-client/actions/workflows/ci.yml/badge.svg)](https://github.com/splitio/elixir-thin-client/actions/workflows/ci-cd.yml) [![Greenkeeper badge](https://badges.greenkeeper.io/splitio/elixir-thin-client.svg)](https://greenkeeper.io/) ## Overview This SDK is designed to work with Split, the platform for controlled rollouts, which serves features to your users via feature flags to manage your complete customer experience. @@ -36,7 +36,7 @@ Below is a simple example that describes the instantiation and most basic usage ```elixir # Start the SDK supervisor -Split.Supervisor.start_link(socket_path: "/var/run/splitd.sock") +Split.Supervisor.start_link(address: "/var/run/splitd.sock") # Get treatment for a user case Split.get_treatment(user_id, feature_flag_name) do diff --git a/lib/split.ex b/lib/split.ex index 94c2569..903a55e 100644 --- a/lib/split.ex +++ b/lib/split.ex @@ -16,7 +16,7 @@ defmodule Split do def start(_type, _args) do children = [ # ... other children ... - {Split, [socket_path: "/var/run/split.sock"]} + {Split, [address: "/var/run/split.sock"]} ] opts = [strategy: :one_for_one, name: MyApp.Supervisor] @@ -32,7 +32,7 @@ defmodule Split do `Split` takes a number of keyword arguments as options when starting. The following options are available: - - `:socket_path`: **OPTIONAL** The path to the splitd socket file. Default is `"/var/run/splitd.sock"`. + - `:address`: **OPTIONAL** The path to the splitd socket file. Default is `"/var/run/splitd.sock"`. - `:pool_size`: **OPTIONAL** The size of the pool of connections to the splitd daemon. Default is the number of online schedulers in the Erlang VM (See: https://www.erlang.org/doc/apps/erts/erl_cmd.html). - `:connect_timeout`: **OPTIONAL** The timeout in milliseconds to connect to the splitd daemon. Default is `1000`. @@ -52,7 +52,7 @@ defmodule Split do @typedoc "An option that can be provided when starting `Split`. See [options](#module-options) for more information." @type option :: - {:socket_path, String.t()} + {:address, String.t()} | {:pool_size, non_neg_integer()} | {:connect_timeout, non_neg_integer()} diff --git a/lib/split/rpc/message.ex b/lib/split/rpc/message.ex index 4dbd5cc..f898833 100644 --- a/lib/split/rpc/message.ex +++ b/lib/split/rpc/message.ex @@ -5,7 +5,7 @@ defmodule Split.RPC.Message do use Split.RPC.Opcodes @protocol_version 0x01 - @client_id "Splitd_Elixir-0.2.1-rc.0" + @client_id "Splitd_Elixir-1.0.0" @type opcode :: unquote(Enum.reduce(@opcodes, &{:|, [], [&1, &2]})) @type protocol_version :: unquote(@protocol_version) @@ -37,7 +37,7 @@ defmodule Split.RPC.Message do ## Examples iex> Message.register() - %Message{v: 1, o: 0, a: ["123", "Splitd_Elixir-0.2.1-rc.0", 1]} + %Message{v: 1, o: 0, a: ["123", "Splitd_Elixir-1.0.0", 1]} """ @spec register() :: t() def register, do: %__MODULE__{o: @register_opcode, a: ["123", @client_id, 1]} diff --git a/lib/split/sockets/conn.ex b/lib/split/sockets/conn.ex index 0d47da5..2a6cfd2 100644 --- a/lib/split/sockets/conn.ex +++ b/lib/split/sockets/conn.ex @@ -9,13 +9,13 @@ defmodule Split.Sockets.Conn do @type t :: %__MODULE__{ socket: port() | nil, - socket_path: String.t(), + address: String.t(), opts: keyword() } defstruct [ :socket, - :socket_path, + :address, :opts ] @@ -31,20 +31,20 @@ defmodule Split.Sockets.Conn do @default_rcv_timeout 1000 @spec new(String.t(), keyword()) :: t - def new(socket_path, opts \\ []) do + def new(address, opts \\ []) do %__MODULE__{ socket: nil, - socket_path: socket_path, + address: address, opts: opts } end @spec connect(t) :: {:ok, t()} | {:error, t(), term()} - def connect(%__MODULE__{socket: nil, socket_path: socket_path, opts: opts} = conn) do + def connect(%__MODULE__{socket: nil, address: address, opts: opts} = conn) do connect_timeout = Keyword.get(opts, :connect_timeout, @default_connect_timeout) - Telemetry.span(:connect, %{socket_path: socket_path, pool_name: opts[:pool_name]}, fn -> - case :gen_tcp.connect({:local, socket_path}, 0, @connect_opts, connect_timeout) do + Telemetry.span(:connect, %{address: address, pool_name: opts[:pool_name]}, fn -> + case :gen_tcp.connect({:local, address}, 0, @connect_opts, connect_timeout) do {:ok, socket} -> conn = %{conn | socket: socket} diff --git a/lib/split/sockets/pool.ex b/lib/split/sockets/pool.ex index 328b812..add2881 100644 --- a/lib/split/sockets/pool.ex +++ b/lib/split/sockets/pool.ex @@ -17,13 +17,13 @@ defmodule Split.Sockets.Pool do end def start_link(opts) do - socket_path = Keyword.get(opts, :socket_path, "/var/run/splitd.sock") + address = Keyword.get(opts, :address, "/var/run/splitd.sock") pool_name = Keyword.get(opts, :pool_name, __MODULE__) pool_size = Keyword.get(opts, :pool_size, System.schedulers_online()) opts = opts - |> Keyword.put_new(:socket_path, socket_path) + |> Keyword.put_new(:address, address) |> Keyword.put_new(:pool_size, pool_size) |> Keyword.put_new(:pool_name, pool_name) @@ -100,11 +100,11 @@ defmodule Split.Sockets.Pool do @impl NimblePool def init_pool(opts) do - socket_path = Keyword.get(opts, :socket_path) + address = Keyword.get(opts, :address) - unless File.exists?(socket_path) do + unless File.exists?(address) do Logger.error(""" - The Split Daemon (splitd) socket was not found at #{socket_path}. + The Split Daemon (splitd) socket was not found at address #{address}. This is likely because the Splitd daemon is not running. """) @@ -117,7 +117,7 @@ defmodule Split.Sockets.Pool do @impl NimblePool def init_worker({opts, _metrics_ref} = pool_state) do - {:ok, Conn.new(Keyword.get(opts, :socket_path), opts), pool_state} + {:ok, Conn.new(Keyword.get(opts, :address), opts), pool_state} end @impl NimblePool diff --git a/lib/split/telemetry.ex b/lib/split/telemetry.ex index b4b7a1f..070d810 100644 --- a/lib/split/telemetry.ex +++ b/lib/split/telemetry.ex @@ -100,7 +100,7 @@ defmodule Split.Telemetry do #### Metadata - * `socket_path` - The path to the socket file. + * `address` - The path to the socket file. * `pool_name` - The name of the pool being used. ### Connect Stop @@ -114,7 +114,7 @@ defmodule Split.Telemetry do #### Metadata - * `socket_path` - The path to the socket file. + * `address` - The path to the socket file. * `pool_name` - The name of the pool being used. * `error` - The error message if the connection fails. diff --git a/mix.exs b/mix.exs index 409c057..f3e9801 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule SplitThinElixir.MixProject do def project do [ app: :split, - version: "0.2.1-rc.0", + version: "1.0.0", elixir: "~> 1.14", elixirc_paths: elixirc_paths(Mix.env()), start_permanent: Mix.env() == :prod, diff --git a/test/sockets/conn_test.exs b/test/sockets/conn_test.exs index 9546132..37b63d0 100644 --- a/test/sockets/conn_test.exs +++ b/test/sockets/conn_test.exs @@ -9,34 +9,34 @@ defmodule Split.Sockets.ConnTest do describe "telemetry events" do setup context do test_id = :erlang.phash2(context.test) - socket_path = "/tmp/test-splitd-#{test_id}.sock" + address = "/tmp/test-splitd-#{test_id}.sock" process_name = :"test-#{test_id}" start_supervised!( - {Split.Test.MockSplitdServer, socket_path: socket_path, name: process_name}, + {Split.Test.MockSplitdServer, address: address, name: process_name}, id: process_name, restart: :transient ) - Split.Test.MockSplitdServer.wait_until_listening(socket_path) + Split.Test.MockSplitdServer.wait_until_listening(address) - {:ok, socket_path: socket_path, splitd_name: process_name} + {:ok, address: address, splitd_name: process_name} end - test "emits telemetry events for successful connection", %{socket_path: socket_path} do + test "emits telemetry events for successful connection", %{address: address} do ref = :telemetry_test.attach_event_handlers(self(), [ [:split, :connect, :start], [:split, :connect, :stop] ]) - Conn.new(socket_path) |> Conn.connect() + Conn.new(address) |> Conn.connect() - assert_received {[:split, :connect, :start], ^ref, _, %{socket_path: ^socket_path}} + assert_received {[:split, :connect, :start], ^ref, _, %{address: ^address}} assert_received {[:split, :connect, :stop], ^ref, _, %{}} end - test "emits telemetry events for registration message on connect", %{socket_path: socket_path} do + test "emits telemetry events for registration message on connect", %{address: address} do ref = :telemetry_test.attach_event_handlers(self(), [ [:split, :send, :start], @@ -45,7 +45,7 @@ defmodule Split.Sockets.ConnTest do [:split, :receive, :stop] ]) - Conn.new(socket_path) |> Conn.connect() + Conn.new(address) |> Conn.connect() assert_received {[:split, :send, :start], ^ref, _, %{request: %Message{v: 1, o: @register_opcode}}} @@ -59,7 +59,7 @@ defmodule Split.Sockets.ConnTest do end test "emits telemetry events for failed connection", %{ - socket_path: socket_path, + address: address, splitd_name: splitd_name } do ref = @@ -71,21 +71,21 @@ defmodule Split.Sockets.ConnTest do # Stop the mocked splitd socket to receive connection errors :ok = stop_supervised(splitd_name) - assert {:error, _conn, reason} = Conn.new(socket_path) |> Conn.connect() + assert {:error, _conn, reason} = Conn.new(address) |> Conn.connect() - assert_received {[:split, :connect, :start], ^ref, _, %{socket_path: ^socket_path}} + assert_received {[:split, :connect, :start], ^ref, _, %{address: ^address}} assert_received {[:split, :connect, :stop], ^ref, _, %{error: ^reason}} end - test "emits telemetry events for successful message sending", %{socket_path: socket_path} do + test "emits telemetry events for successful message sending", %{address: address} do ref = :telemetry_test.attach_event_handlers(self(), [ [:split, :send, :start], [:split, :send, :stop] ]) - {:ok, conn} = Conn.new(socket_path) |> Conn.connect() + {:ok, conn} = Conn.new(address) |> Conn.connect() message = Message.get_treatment(key: "user-id", feature_name: "feature") @@ -97,7 +97,7 @@ defmodule Split.Sockets.ConnTest do end test "emits telemetry events for failed message sending", %{ - socket_path: socket_path, + address: address, splitd_name: splitd_name } do ref = @@ -106,7 +106,7 @@ defmodule Split.Sockets.ConnTest do [:split, :send, :stop] ]) - {:ok, conn} = Conn.new(socket_path) |> Conn.connect() + {:ok, conn} = Conn.new(address) |> Conn.connect() message = Message.get_treatment(key: "user-id", feature_name: "feature") @@ -120,14 +120,14 @@ defmodule Split.Sockets.ConnTest do assert_received {[:split, :send, :stop], ^ref, _, %{error: ^reason}} end - test "emits telemetry events for successful message receiving", %{socket_path: socket_path} do + test "emits telemetry events for successful message receiving", %{address: address} do ref = :telemetry_test.attach_event_handlers(self(), [ [:split, :receive, :start], [:split, :receive, :stop] ]) - {:ok, conn} = Conn.new(socket_path) |> Conn.connect() + {:ok, conn} = Conn.new(address) |> Conn.connect() message = Message.get_treatment(key: "user-id", feature_name: "feature") @@ -138,14 +138,14 @@ defmodule Split.Sockets.ConnTest do assert_received {[:split, :receive, :stop], ^ref, _, %{response: ^response}} end - test "emits telemetry events for failed message receiving", %{socket_path: socket_path} do + test "emits telemetry events for failed message receiving", %{address: address} do ref = :telemetry_test.attach_event_handlers(self(), [ [:split, :receive, :start], [:split, :receive, :stop] ]) - {:ok, conn} = Conn.new(socket_path) |> Conn.connect() + {:ok, conn} = Conn.new(address) |> Conn.connect() # receive the registration messages assert_received {[:split, :receive, :start], ^ref, _, %{}} diff --git a/test/sockets/pool_test.exs b/test/sockets/pool_test.exs index 66821bd..0ea2dfd 100644 --- a/test/sockets/pool_test.exs +++ b/test/sockets/pool_test.exs @@ -9,15 +9,13 @@ defmodule Split.Sockets.PoolTest do setup_all context do test_id = :erlang.phash2(context.module) - socket_path = "/tmp/test-splitd-#{test_id}.sock" + address = "/tmp/test-splitd-#{test_id}.sock" - start_supervised!( - {Split.Test.MockSplitdServer, socket_path: socket_path, name: :"test-#{test_id}"} - ) + start_supervised!({Split.Test.MockSplitdServer, address: address, name: :"test-#{test_id}"}) - Split.Test.MockSplitdServer.wait_until_listening(socket_path) + Split.Test.MockSplitdServer.wait_until_listening(address) - start_supervised!({Split, socket_path: socket_path, pool_name: __MODULE__, pool_size: 10}) + start_supervised!({Split, address: address, pool_name: __MODULE__, pool_size: 10}) :ok end diff --git a/test/split_test.exs b/test/split_test.exs index 4c45d67..9fcb873 100644 --- a/test/split_test.exs +++ b/test/split_test.exs @@ -7,15 +7,13 @@ defmodule SplitThinElixirTest do setup_all context do test_id = :erlang.phash2(context.module) - socket_path = "/tmp/test-splitd-#{test_id}.sock" + address = "/tmp/test-splitd-#{test_id}.sock" - start_supervised!( - {Split.Test.MockSplitdServer, socket_path: socket_path, name: :"test-#{test_id}"} - ) + start_supervised!({Split.Test.MockSplitdServer, address: address, name: :"test-#{test_id}"}) - Split.Test.MockSplitdServer.wait_until_listening(socket_path) + Split.Test.MockSplitdServer.wait_until_listening(address) - start_supervised!({Split, socket_path: socket_path}) + start_supervised!({Split, address: address}) :ok end diff --git a/test/support/mock_splitd_server.ex b/test/support/mock_splitd_server.ex index 91190c0..7c3c61c 100644 --- a/test/support/mock_splitd_server.ex +++ b/test/support/mock_splitd_server.ex @@ -11,11 +11,11 @@ defmodule Split.Test.MockSplitdServer do @impl Supervisor def init(opts \\ []) do - socket_path = Keyword.get(opts, :socket_path) + address = Keyword.get(opts, :address) name = Keyword.get(opts, :name, __MODULE__) opts = Keyword.put_new(opts, :name, name) - File.rm(socket_path) + File.rm(address) children = [ {Task.Supervisor, strategy: :one_for_one, name: :"#{name}-task-supervisor"}, @@ -26,25 +26,25 @@ defmodule Split.Test.MockSplitdServer do end def accept(opts) do - socket_path = Keyword.get(opts, :socket_path) + address = Keyword.get(opts, :address) {:ok, socket} = :gen_tcp.listen(0, active: false, packet: :raw, reuseaddr: true, - ifaddr: {:local, socket_path} + ifaddr: {:local, address} ) loop_acceptor(socket, opts) end - def wait_until_listening(socket_path) do - if File.exists?(socket_path) do + def wait_until_listening(address) do + if File.exists?(address) do :ok else Process.sleep(1) - wait_until_listening(socket_path) + wait_until_listening(address) end end