View Source Erlang SDK For Harness Feature Flags

Harness is a feature management platform that helps teams to build better software and to test features quicker.

This repository contains our Feature Flags SDK for Erlang and other BEAM languages such as Elixir.

table-of-contents

Table of Contents

Intro<br> Requirements<br> Quickstart<br> Further Reading<br> Build Instructions<br>

intro

Intro

This sample doesn’t include configuration options. For in depth steps and configuring the SDK, e.g. disabling streaming or using our Relay Proxy, see the Erlang SDK Reference.

For a sample FF Erlang SDK project, see our test Erlang project.

For a sample FF Erlang SDK Project for Elixir, see our test Elixir Project.

FeatureFlags

requirements

Requirements

Erlang OTP 22 or newer.

quickstart

Quickstart

To follow along with our test code sample, make sure you have:

install-the-sdk-erlang

Install the SDK (Erlang)

Add this library as a dependency to your rebar.config.

{deps, [
  {cfclient, {git, "https://github.com/harness/ff-erlang-server-sdk", {tag, "0.5.0-beta.1"}}}
]}.

Add the dependency to your project's app.src.

{applications,
  [kernel, stdlib, cfclient]
},

install-the-sdk-elixir

Install the SDK (Elixir)

Add the library to mix.exs deps():

  {:cfclient, github: "harness/ff-erlang-server-sdk", tag: "0.5.0-beta.1"}

configuration

Configuration

erlang

Erlang

Configure the application environment in sys.config:

[
  {cfclient, [
      {api_key, "YOUR_API_KEY"},
  ]}
].

elixir

Elixir

Configure the application environment in config/prod.exs:

config :cfclient,
  api_key: "YOUR_API_KEY"

multiple-projects

Multiple Projects

Normally there is a single project per application. If different parts of your application need their own key, you can start up additional client instances, passing in a name and api_key for each. When you call client API functions, pass the name as the first parameter.

erlang-1

Erlang

In sys.config, define the project config:

[
    {myapp, [
                {cfclient, [
                    {api_key, "YOUR_API_KEY"}
                }
            ] 
        ]
    }
].

In your application supervisor, e.g. src/myapp_sup.erl, start up a cfclient_instance for each project:

init(Args) ->
  HarnessArgs = application:get_env(myapp, cfclient, []),

  ChildSpecs = [#{id => cfclient_instance, start => {cfclient_instance, start_link, [HarnessArgs]}}],
  SupFlags = #{strategy => one_for_one, intensity => 1, period => 5},
  {ok, {SupFlags, ChildSpecs}}.

elixir-1

Elixir

Define the api_key:

config :myapp, :cfclient, api_key: "YOUR_API_KEY"

In your application supervisor, e.g. lib/myapp/supervisor.ex, start up cfclient_instance:

def start(_type, _args) do
  harness_args = Application.get_env(:myapp, :cfclient, [])

  children = [
    %{id => :myapp_cfclient_instance, start => {:cfclient_instance, :start_link, [harness_args]}}
  ]

  opts = [strategy: :one_for_one, name: MyApp.Supervisor]
  Supervisor.start_link(children, opts)
end

code-sample

Code Sample

erlang-2

Erlang

Call the API to get the value of the harnessappdemodarkmode flag you created via https://www.harness.io/.

get_flag_loop() ->
  Target = #{identifier => "Harness_Target_1",
    name => "HT_1",
    %% Attribute keys must be atoms. 
    %% Values must be either bitstrings, atoms, or a list of bitstrings/atoms - see Targets with custom attributes section below.
    attributes => #{email => <<"demo@harness.io">>}
  },
  FlagIdentifier = "harnessappdemodarkmode",
  Result = cfclient:bool_variation(FlagIdentifier, Target, false),
  logger:info("Varaion for Flag ~p witih Target ~p is: ~p~n", [FlagIdentifier, maps:get(identifier, Target), Result]),
  timer:sleep(10000),
  get_flag_loop().

elixir-2

Elixir

Call the API to get the value of the harnessappdemodarkmode flag you created via https://www.harness.io/.

def getFlagLoop() do
  target = %{
    identifier: "Harness_Target_1",
    name: "HT_1"
  
    # Attribute keys must be atoms. 
    # Values must be either binaries, atoms, or a list of binaries/atoms.
    # See "targets with custom attributes" below.
    attributes: %{email: "demo@harness.io"}
  }
  
  flag_identifier = "harnessappdemodarkmode"
  
  result = :cfclient.bool_variation(flag_identifier, target, false)
  Logger.info("Varaion for Flag #{flag_identifier} with Target #{inspect(target)} is: #{result)")
  Process.sleep(10000)
  getFlagLoop()

targets-with-custom-attributes

Targets with custom attributes

You can use the attributes map to provide custom attributes. If the target isn't anonymous, the attributes will shortly appear in the Harness UI after an evaluation using the target.

You can create Group Rules based on these attributes.

Note: attribute keys must be atoms and the values must either be binaries or atoms or a list of binaries or atoms.

erlang-3

Erlang:

  TargetBetaGroup = #{'identifier' => <<"my_target">>,
    name => <<"my_target_name">>,
    anonymous => <<"">>,
    attributes => #{beta => <<"beta_group_1">>}
    },
  TargetBetaGroups = #{'identifier' => <<"my_other_target">>,
    name => <<"my_other_target_name">>,
    anonymous => <<"">>,
    attributes => #{beta => [<<"beta_group_1">>, 'beta_group_2']}}
    },
  TargetAlphaGroup = #{'identifier' => <<"my_alpha_target">>,
    name => <<"my_alpha_target_name">>,
    anonymous => <<"">>,
    attributes => #{alpha => 'alpha_group_1'}
    },

elixir-3

Elixir

target_beta_group = %{
  identifier: "my_target",
  name: "my_target_name",
  anonymous: "",
  attributes: %{beta: "beta_group_1"}
}

target_beta_groups = %{
  identifier: "my_other_target",
  name: "my_other_target_name",
  anonymous: "",
  attributes: %{
    beta: ["beta_group_1", :beta_group_2]
  }
}

target_alpha_group = %{
  identifier: "my_alpha_target",
  name: "my_alpha_target_name",
  anonymous: "",
  attributes: %{alpha: :alpha_group_1}
}

additional-reading

Additional Reading

For further examples and config options, see the Erlang SDK Further Reading.

For more information about Feature Flags, see our Feature Flags documentation.

contributing

Contributing

In order to run the tests, pull the submodules:

git submodule update --init