Home

Awesome

OpenFeature Gleam SDK

Package Version Hex Docs

OpenFeature is an open specification that provides a vendor-agnostic, community-driven API for feature flagging that works with your favorite feature flag management tool.

This repository contains the unofficial SDK implementation of the specification for the Gleam programming language.

🚀 Quick start

Requirements

Install

gleam add openfeature

Usage

import gleam/bool
import gleam/dict
import gleam/dynamic
import gleam/io
import gleam/option.{None}
import openfeature
import openfeature/client
import openfeature/evaluation_context
import openfeature/providers/in_memory

pub fn main() {
  // flags defined in memory
  let v2_flag =
    in_memory.Flag(
      default_variant: "off",
      variants: dict.from_list([
        #("off", dynamic.from(False)),
        #("on", dynamic.from(True)),
      ]),
      context_evaluator: None,
    )
  let flags = dict.from_list([#("v2_enabled", v2_flag)])

  // configure a provider
  let _ = openfeature.set_provider(in_memory.provider(flags))

  // get a bool flag value
  let flag_value =
    openfeature.get_client()
    |> client.get_bool_value("v2_enabled", False, evaluation_context.empty())

  // use the returned flag value
  io.println("`v2_enabled` evaluated to: " <> bool.to_string(flag_value))
}

Further documentation can be found at https://hexdocs.pm/openfeature.

🌟 Features

StatusFeaturesDescription
ProvidersIntegrate with a commercial, open source, or in-house feature management tool.
TargetingContextually-aware flag evaluation using evaluation context.
HooksAdd functionality to various stages of the flag evaluation life-cycle.
LoggingIntegrate with popular logging packages.
DomainsLogically bind clients with providers.
EventingReact to state changes in the provider or flag management system.
ShutdownGracefully clean up a provider during application shutdown.
⚠️ExtendingExtend OpenFeature with custom providers and hooks.

<sub>Implemented: ✅ | In-progress: ⚠️ | Not implemented yet: ❌</sub>

Providers

Providers are an abstraction between a flag management system and the OpenFeature SDK. Look here for a complete list of available providers. If the provider you're looking for hasn't been created yet, see the develop a provider section to learn how to build it yourself.

Once you've added a provider as a dependency, it can be registered with OpenFeature like this:

import openfeature

openfeature.set_provider(my_provider())

In some situations, it may be beneficial to register multiple providers in the same application. This is possible using domains, which is covered in more details below.

Targeting

Sometimes, the value of a flag must consider some dynamic criteria about the application or user, such as the user's location, IP, email address, or the server's location. In OpenFeature, we refer to this as targeting. If the flag management system you're using supports targeting, you can provide the input data using the evaluation context.

import gleam/dynamic
import gleam/option.{None}
import openfeature
import openfeature/client
import openfeature/evaluation_context.{EvaluationContext}

// set the global evaluation context
openfeature.set_context(
  evaluation_context.targetless([
    #("region", dynamic.from("us-east-1-iah-1a")),
  ])
)

// set the client evaluation context
let my_app_client =
  openfeature.get_domain_client("my-app")
  |> client.set_context(
    evaluation_context.targetless([
      #("version",  dynamic.from("1.4.6")),
    ])
  )

// set the invocation context
client.get_bool_details(
  my_app_client,
  "bool-flag",
  False,
  evaluation_context.targeted(
    "userId:1234",
    [#("company", dynamic.from("Wise"))]
  ),
)

Hooks

TODO

Logging

TODO

Domains

Clients can be assigned to a domain. A domain is a logical identifier which can be used to associate clients with a particular provider. If a domain has no associated provider, the default provider is used.

import openfeature

// registering the default provider
openfeature.set_provider(local_provider())
// registering a domain provider
openfeature.set_domain_provider("cached-domain", cached_provider())

// a client bound to the default provider
openfeature.get_client()
// a client bound to the cached provider
openfeature.get_domain_client("cached-domain")

Eventing

TODO

Shutdown

The OpenFeature API provides a close function to perform a cleanup of all registered providers. This should only be called when your application is in the process of shutting down.

import openfeature

openfeature.shutdown()

Extending

Develop a provider

To develop a provider, you need to create a new project and include the OpenFeature SDK as a dependency. This can be a new repository or included in the existing contrib repository available under the OpenFeature organization. You’ll then need to write the provider by exporting a function that returns a value of type FeatureProvider, exported by the OpenFeature SDK.

import gleam/dynamic.{type Dynamic}
import openfeature/evaluation_context.{type EvaluationContext}
import openfeature/provider.{type FeatureProvider, FeatureProvider}

pub fn my_provider() -> FeatureProvider {
  FeatureProvider(
    get_metadata: fn() { provider.Metadata("My Provider") },
    initialize: fn(evaluation_context: EvaluationContext) {
      // code to initialize your provider
      todo
    },
    shutdown: fn() {
      // code to shutdown your provider
      todo
    },
    resolve_bool_evaluation: fn(
      flag: String,
      default_value: Bool,
      evaluation_context: EvaluationContext,
    ) {
      // code to evaluate a boolean value
      todo
    },
    resolve_string_evaluation: fn(
      flag: String,
      default_value: String,
      evaluation_context: EvaluationContext,
    ) {
      // code to evaluate a string value
      todo
    },
    resolve_int_evaluation: fn(
      flag: String,
      default_value: Int,
      evaluation_context: EvaluationContext,
    ) {
      // code to evaluate an integer value
      todo
    },
    resolve_float_evaluation: fn(
      flag: String,
      default_value: Float,
      evaluation_context: EvaluationContext,
    ) {
      // code to evaluate a float value
      todo
    },
    resolve_dynamic_evaluation: fn(
      flag: String,
      default_value: Dynamic,
      evaluation_context: EvaluationContext,
    ) {
      // code to evaluate a dynamic value
      todo
    },
  )
}

Develop a hook

TODO