From c8f03be4510fbecf55dc01b08a677511110e3d31 Mon Sep 17 00:00:00 2001 From: Mark Siebert Date: Thu, 19 Mar 2026 12:06:15 -0700 Subject: [PATCH 1/5] Add OpenFeature provider documentation for all SDKs Add documentation pages for the OpenFeature provider integration for Android, Go, Java, Node.js, Python, Ruby, and Swift SDKs. Each page covers installation, usage with local and remote evaluation, supported flag types, context/identity handling, error codes, and lifecycle behavior. Co-Authored-By: Claude Opus 4.6 --- .../tracking-methods/sdks/android/_meta.ts | 3 +- .../sdks/android/android-openfeature.mdx | 89 +++++++++++++ pages/docs/tracking-methods/sdks/go/_meta.ts | 1 + .../sdks/go/go-openfeature.mdx | 120 ++++++++++++++++++ .../docs/tracking-methods/sdks/java/_meta.ts | 1 + .../sdks/java/java-openfeature.mdx | 120 ++++++++++++++++++ .../tracking-methods/sdks/nodejs/_meta.ts | 1 + .../sdks/nodejs/nodejs-openfeature.mdx | 109 ++++++++++++++++ .../tracking-methods/sdks/python/_meta.ts | 1 + .../sdks/python/python-openfeature.mdx | 101 +++++++++++++++ .../docs/tracking-methods/sdks/ruby/_meta.ts | 1 + .../sdks/ruby/ruby-openfeature.mdx | 113 +++++++++++++++++ .../docs/tracking-methods/sdks/swift/_meta.ts | 3 +- .../sdks/swift/swift-openfeature.mdx | 100 +++++++++++++++ 14 files changed, 761 insertions(+), 2 deletions(-) create mode 100644 pages/docs/tracking-methods/sdks/android/android-openfeature.mdx create mode 100644 pages/docs/tracking-methods/sdks/go/go-openfeature.mdx create mode 100644 pages/docs/tracking-methods/sdks/java/java-openfeature.mdx create mode 100644 pages/docs/tracking-methods/sdks/nodejs/nodejs-openfeature.mdx create mode 100644 pages/docs/tracking-methods/sdks/python/python-openfeature.mdx create mode 100644 pages/docs/tracking-methods/sdks/ruby/ruby-openfeature.mdx create mode 100644 pages/docs/tracking-methods/sdks/swift/swift-openfeature.mdx diff --git a/pages/docs/tracking-methods/sdks/android/_meta.ts b/pages/docs/tracking-methods/sdks/android/_meta.ts index 5ee586c759..4c5d58100f 100644 --- a/pages/docs/tracking-methods/sdks/android/_meta.ts +++ b/pages/docs/tracking-methods/sdks/android/_meta.ts @@ -1,4 +1,5 @@ export default { "android-replay": "Session Replay (Android)", - "android-flags": "Feature Flags (Android)" + "android-flags": "Feature Flags (Android)", + "android-openfeature": "OpenFeature Provider (Android)", } diff --git a/pages/docs/tracking-methods/sdks/android/android-openfeature.mdx b/pages/docs/tracking-methods/sdks/android/android-openfeature.mdx new file mode 100644 index 0000000000..9e2d005e21 --- /dev/null +++ b/pages/docs/tracking-methods/sdks/android/android-openfeature.mdx @@ -0,0 +1,89 @@ +import { Callout } from 'nextra/components' + +# OpenFeature Provider (Android) + +## Overview + +The Mixpanel OpenFeature provider allows you to use [Mixpanel Feature Flags](/docs/featureflags) through the standardized [OpenFeature](https://openfeature.dev/) API. This provider wraps the Mixpanel Android SDK's `MixpanelAPI.Flags`, letting you use the OpenFeature Kotlin SDK with Mixpanel as the backend. + +For the core Feature Flags SDK guide, see [Feature Flags (Android)](/docs/tracking-methods/sdks/android/android-flags). + +## Prerequisites + +- You are on an Enterprise subscription plan +- You have the [Mixpanel Android SDK](/docs/tracking-methods/sdks/android) installed +- You have your Project Token from your [Mixpanel Project Settings](/docs/orgs-and-projects/managing-projects#find-your-project-tokens) + +## Installation + +Add the OpenFeature provider and SDK to your `build.gradle`: + +```groovy +dependencies { + implementation 'com.mixpanel:mixpanel-android-openfeature-provider:1.0.0' + implementation 'dev.openfeature:android-sdk:0.3.0' +} +``` + +## Usage + +The provider wraps `MixpanelAPI.Flags` from the Mixpanel Android SDK. + +```kotlin +import com.mixpanel.android.mpmetrics.MixpanelAPI +import com.mixpanel.openfeature.MixpanelProvider +import dev.openfeature.sdk.OpenFeatureAPI + +// Initialize Mixpanel +val mixpanel = MixpanelAPI.getInstance(context, "YOUR_PROJECT_TOKEN") + +// Register the OpenFeature provider +val provider = MixpanelProvider(mixpanel.flags) +OpenFeatureAPI.setProvider(provider) + +val client = OpenFeatureAPI.getClient() +val showNewUI = client.getBooleanValue("new-ui", false) +``` + +## Supported Flag Types + +| OpenFeature Method | Kotlin Type | +|---|---| +| `getBooleanValue` / `getBooleanDetails` | `Boolean` | +| `getStringValue` / `getStringDetails` | `String` | +| `getIntegerValue` / `getIntegerDetails` | `Int` | +| `getDoubleValue` / `getDoubleDetails` | `Double` | +| `getObjectValue` / `getObjectDetails` | `Value` | + +## Context and Identity + + +Context is set globally when registering the provider, not per-evaluation. Per-evaluation context passed to individual flag evaluations is ignored. + + +- **`targetingKey`** has no special meaning in this provider. It is treated as a regular context property. +- Identity should be managed through the Mixpanel SDK via `mixpanel.identify()`. + +## Error Handling + +The provider returns the default value on all errors, with an error code indicating the cause: + +| Error Code | Condition | +|---|---| +| `PROVIDER_NOT_READY` | The provider has not been initialized | +| `FLAG_NOT_FOUND` | The requested flag does not exist | +| `TYPE_MISMATCH` | The flag value does not match the requested type | + +Use `getDetails()` methods instead of `getValue()` to inspect error codes: + +```kotlin +val details = client.getBooleanDetails("my-flag", false) +if (details.errorCode != null) { + Log.d("Flags", "Flag error: ${details.errorCode} - ${details.errorMessage}") +} +``` + +## Lifecycle + +- **`shutdown()`** is a no-op. The Mixpanel SDK manages its own lifecycle. +- The reason code for successful evaluations is `STATIC`. diff --git a/pages/docs/tracking-methods/sdks/go/_meta.ts b/pages/docs/tracking-methods/sdks/go/_meta.ts index 2d7221cfd8..07d9df4915 100644 --- a/pages/docs/tracking-methods/sdks/go/_meta.ts +++ b/pages/docs/tracking-methods/sdks/go/_meta.ts @@ -1,3 +1,4 @@ export default { "go-flags": "Feature Flags (Go)", + "go-openfeature": "OpenFeature Provider (Go)", } diff --git a/pages/docs/tracking-methods/sdks/go/go-openfeature.mdx b/pages/docs/tracking-methods/sdks/go/go-openfeature.mdx new file mode 100644 index 0000000000..68aba79902 --- /dev/null +++ b/pages/docs/tracking-methods/sdks/go/go-openfeature.mdx @@ -0,0 +1,120 @@ +import { Callout } from 'nextra/components' + +# OpenFeature Provider (Go) + +## Overview + +The Mixpanel OpenFeature provider allows you to use [Mixpanel Feature Flags](/docs/featureflags) through the standardized [OpenFeature](https://openfeature.dev/) API. This provider wraps the Mixpanel Go SDK's feature flags, letting you use the OpenFeature Go SDK with Mixpanel as the backend. + +For the core Feature Flags SDK guide, see [Feature Flags (Go)](/docs/tracking-methods/sdks/go/go-flags). + +## Prerequisites + +- You are on an Enterprise subscription plan +- You have the [Mixpanel Go SDK](/docs/tracking-methods/sdks/go) installed (minimum version `v2.0.0`) +- You have your Project Token from your [Mixpanel Project Settings](/docs/orgs-and-projects/managing-projects#find-your-project-tokens) + +## Installation + +```bash +go get github.com/mixpanel/mixpanel-go/openfeature-provider +go get github.com/open-feature/go-sdk +``` + +## Usage + +The provider wraps either a `LocalFlagsProvider` or `RemoteFlagsProvider` from the Mixpanel SDK. + +### With Local Evaluation + +```go +package main + +import ( + "context" + "fmt" + + mixpanel "github.com/mixpanel/mixpanel-go" + mpof "github.com/mixpanel/mixpanel-go/openfeature-provider" + "github.com/open-feature/go-sdk/openfeature" +) + +func main() { + mp := mixpanel.NewClient("YOUR_PROJECT_TOKEN", + mixpanel.WithLocalFlagsConfig(mixpanel.LocalFlagsConfig{ + EnablePolling: true, + PollingInterval: 60 * time.Second, + }), + ) + + mp.LocalFlags().StartPollingForDefinitions() + defer mp.LocalFlags().StopPollingForDefinitions() + + // Register the provider + provider := mpof.NewMixpanelProvider(mp.LocalFlags()) + openfeature.SetProvider(provider) + + client := openfeature.NewClient("my-app") + showNewUI, _ := client.BooleanValue(context.Background(), "new-ui", false, openfeature.EvaluationContext{}) + fmt.Println("Show new UI:", showNewUI) +} +``` + +### With Remote Evaluation + +```go +mp := mixpanel.NewClient("YOUR_PROJECT_TOKEN", + mixpanel.WithRemoteFlagsConfig(mixpanel.RemoteFlagsConfig{ + APIHost: "api.mixpanel.com", + }), +) + +provider := mpof.NewMixpanelProvider(mp.RemoteFlags()) +openfeature.SetProvider(provider) + +client := openfeature.NewClient("my-app") +variant, _ := client.StringValue(context.Background(), "checkout-flow", "control", openfeature.EvaluationContext{}) +``` + +## Supported Flag Types + +| OpenFeature Method | Go Type | +|---|---| +| `BooleanValue` / `BooleanValueDetails` | `bool` | +| `StringValue` / `StringValueDetails` | `string` | +| `IntValue` / `IntValueDetails` | `int64` | +| `FloatValue` / `FloatValueDetails` | `float64` | +| `ObjectValue` / `ObjectValueDetails` | `interface{}` | + +## Context and Identity + + +Context is set globally when registering the provider, not per-evaluation. Per-evaluation context passed to individual flag evaluations is ignored. + + +- **`targetingKey`** has no special meaning in this provider. It is treated as a regular context property. +- Identity should be managed through the Mixpanel SDK (e.g., setting `distinct_id` in the context). + +## Error Handling + +The provider returns the default value on all errors, with a resolution error indicating the cause: + +| Error Code | Condition | +|---|---| +| `PROVIDER_NOT_READY` | The provider has not been initialized | +| `FLAG_NOT_FOUND` | The requested flag does not exist | +| `TYPE_MISMATCH` | The flag value does not match the requested type | + +Use `ValueDetails` methods to inspect error codes: + +```go +details, err := client.BooleanValueDetails(ctx, "my-flag", false, openfeature.EvaluationContext{}) +if details.ErrorCode != "" { + fmt.Printf("Flag error: %s - %s\n", details.ErrorCode, details.ErrorMessage) +} +``` + +## Lifecycle + +- **`Shutdown()`** is a no-op. The Mixpanel SDK manages its own lifecycle. +- The reason code for successful evaluations is `STATIC`. diff --git a/pages/docs/tracking-methods/sdks/java/_meta.ts b/pages/docs/tracking-methods/sdks/java/_meta.ts index 534176a476..de3f9c9547 100644 --- a/pages/docs/tracking-methods/sdks/java/_meta.ts +++ b/pages/docs/tracking-methods/sdks/java/_meta.ts @@ -1,4 +1,5 @@ export default { "index": "Java", "java-flags": "Feature Flags (Java)", + "java-openfeature": "OpenFeature Provider (Java)", } \ No newline at end of file diff --git a/pages/docs/tracking-methods/sdks/java/java-openfeature.mdx b/pages/docs/tracking-methods/sdks/java/java-openfeature.mdx new file mode 100644 index 0000000000..437be821cf --- /dev/null +++ b/pages/docs/tracking-methods/sdks/java/java-openfeature.mdx @@ -0,0 +1,120 @@ +import { Callout } from 'nextra/components' + +# OpenFeature Provider (Java) + +## Overview + +The Mixpanel OpenFeature provider allows you to use [Mixpanel Feature Flags](/docs/featureflags) through the standardized [OpenFeature](https://openfeature.dev/) API. This provider wraps the Mixpanel Java SDK's feature flags, letting you use the OpenFeature Java SDK with Mixpanel as the backend. + +For the core Feature Flags SDK guide, see [Feature Flags (Java)](/docs/tracking-methods/sdks/java/java-flags). + +## Prerequisites + +- You are on an Enterprise subscription plan +- You have the [Mixpanel Java SDK](/docs/tracking-methods/sdks/java) installed (minimum version `v1.7.0`) +- You have your Project Token from your [Mixpanel Project Settings](/docs/orgs-and-projects/managing-projects#find-your-project-tokens) + +## Installation + +Add the OpenFeature provider and SDK to your Maven `pom.xml`: + +```xml + + com.mixpanel + mixpanel-java-openfeature-provider + 1.7.0 + + + dev.openfeature + sdk + 1.20.1 + +``` + +Or with Gradle: + +```groovy +implementation 'com.mixpanel:mixpanel-java-openfeature-provider:1.7.0' +implementation 'dev.openfeature:sdk:1.20.1' +``` + +## Usage + +The provider wraps a `BaseFlagsProvider` (either `LocalFlagsProvider` or `RemoteFlagsProvider`) from the Mixpanel SDK. + +### With Local Evaluation + +```java +import com.mixpanel.mixpanelapi.featureflags.provider.LocalFlagsProvider; +import com.mixpanel.openfeature.MixpanelProvider; +import dev.openfeature.sdk.*; + +// Initialize Mixpanel flags provider +LocalFlagsProvider flagsProvider = new LocalFlagsProvider("YOUR_PROJECT_TOKEN", config, sdkVersion, eventSender); + +// Register with OpenFeature +OpenFeatureAPI api = OpenFeatureAPI.getInstance(); +api.setProvider(new MixpanelProvider(flagsProvider)); + +Client client = api.getClient(); +boolean showNewUI = client.getBooleanValue("new-ui", false); +``` + +### With Remote Evaluation + +```java +import com.mixpanel.mixpanelapi.featureflags.provider.RemoteFlagsProvider; + +RemoteFlagsProvider flagsProvider = new RemoteFlagsProvider("YOUR_PROJECT_TOKEN", config, sdkVersion, eventSender); + +OpenFeatureAPI api = OpenFeatureAPI.getInstance(); +api.setProvider(new MixpanelProvider(flagsProvider)); + +Client client = api.getClient(); +String variant = client.getStringValue("checkout-flow", "control"); +``` + +## Supported Flag Types + +| OpenFeature Method | Java Type | +|---|---| +| `getBooleanValue` / `getBooleanDetails` | `Boolean` | +| `getStringValue` / `getStringDetails` | `String` | +| `getIntegerValue` / `getIntegerDetails` | `Integer` (coerces from `Long`, `Double`) | +| `getDoubleValue` / `getDoubleDetails` | `Double` (coerces from `Integer`, `Long`) | +| `getObjectValue` / `getObjectDetails` | `Value` (wraps maps, lists, and primitives) | + +Numeric types are coerced automatically. For example, a flag returning a `Long` value will work with `getIntegerValue()`. + +## Context and Identity + + +Context is set globally when registering the provider, not per-evaluation. Per-evaluation context passed to individual flag evaluations is ignored. + + +- **`targetingKey`** has no special meaning in this provider. It is treated as a regular context property. +- Identity should be managed through the Mixpanel SDK (e.g., setting `distinct_id` in the context). + +## Error Handling + +The provider returns the default value on all errors, with an error code indicating the cause: + +| Error Code | Condition | +|---|---| +| `PROVIDER_NOT_READY` | The provider has not been initialized | +| `FLAG_NOT_FOUND` | The requested flag does not exist | +| `TYPE_MISMATCH` | The flag value does not match the requested type | + +Use `getDetails()` methods instead of `getValue()` to inspect error codes: + +```java +FlagEvaluationDetails details = client.getBooleanDetails("my-flag", false); +if (details.getErrorCode() != null) { + System.out.println("Flag error: " + details.getErrorCode() + " - " + details.getErrorMessage()); +} +``` + +## Lifecycle + +- **`shutdown()`** is a no-op. The Mixpanel SDK manages its own lifecycle. +- The reason code for successful evaluations is `STATIC`. diff --git a/pages/docs/tracking-methods/sdks/nodejs/_meta.ts b/pages/docs/tracking-methods/sdks/nodejs/_meta.ts index 01a0e4d9dd..1327af91c4 100644 --- a/pages/docs/tracking-methods/sdks/nodejs/_meta.ts +++ b/pages/docs/tracking-methods/sdks/nodejs/_meta.ts @@ -1,3 +1,4 @@ export default { "nodejs-flags": "Feature Flags (Node.js)", + "nodejs-openfeature": "OpenFeature Provider (Node.js)", } diff --git a/pages/docs/tracking-methods/sdks/nodejs/nodejs-openfeature.mdx b/pages/docs/tracking-methods/sdks/nodejs/nodejs-openfeature.mdx new file mode 100644 index 0000000000..fe512e6540 --- /dev/null +++ b/pages/docs/tracking-methods/sdks/nodejs/nodejs-openfeature.mdx @@ -0,0 +1,109 @@ +import { Callout } from 'nextra/components' + +# OpenFeature Provider (Node.js) + +## Overview + +The Mixpanel OpenFeature provider allows you to use [Mixpanel Feature Flags](/docs/featureflags) through the standardized [OpenFeature](https://openfeature.dev/) API. This provider wraps the Mixpanel Node.js SDK's feature flags, letting you use the OpenFeature Server SDK with Mixpanel as the backend. + +For the core Feature Flags SDK guide, see [Feature Flags (Node.js)](/docs/tracking-methods/sdks/nodejs/nodejs-flags). + +## Prerequisites + +- You are on an Enterprise subscription plan +- You have the [Mixpanel Node.js SDK](/docs/tracking-methods/sdks/nodejs) installed (minimum version `v0.20.0`) +- You have your Project Token from your [Mixpanel Project Settings](/docs/orgs-and-projects/managing-projects#find-your-project-tokens) + +## Installation + +```bash +npm install @mixpanel/openfeature-server-provider @openfeature/server-sdk +``` + +## Usage + +The provider wraps either a `LocalFeatureFlagsProvider` or `RemoteFeatureFlagsProvider` from the Mixpanel SDK. Initialize Mixpanel with feature flags first, then register the provider with OpenFeature. + +### With Local Evaluation + +```javascript +const Mixpanel = require('mixpanel'); +const { OpenFeature } = require('@openfeature/server-sdk'); +const { MixpanelProvider } = require('@mixpanel/openfeature-server-provider'); + +const mixpanel = Mixpanel.init('YOUR_PROJECT_TOKEN', { + local_flags_config: { + enable_polling: true, + polling_interval_in_seconds: 60 + } +}); + +await mixpanel.local_flags.startPollingForDefinitions(); + +// Register the provider with the user context for evaluation +await OpenFeature.setProviderAndWait(new MixpanelProvider(mixpanel.local_flags)); +OpenFeature.setContext({ distinct_id: 'user-123', plan: 'premium' }); + +const client = OpenFeature.getClient(); +const showNewUI = await client.getBooleanValue('new-ui', false); +``` + +### With Remote Evaluation + +```javascript +const mixpanel = Mixpanel.init('YOUR_PROJECT_TOKEN', { + remote_flags_config: { + api_host: 'api.mixpanel.com' + } +}); + +await OpenFeature.setProviderAndWait(new MixpanelProvider(mixpanel.remote_flags)); +OpenFeature.setContext({ distinct_id: 'user-123' }); + +const client = OpenFeature.getClient(); +const variant = await client.getStringValue('checkout-flow', 'control'); +``` + +## Supported Flag Types + +| OpenFeature Method | JavaScript Type | +|---|---| +| `getBooleanValue` / `getBooleanDetails` | `boolean` | +| `getStringValue` / `getStringDetails` | `string` | +| `getNumberValue` / `getNumberDetails` | `number` | +| `getObjectValue` / `getObjectDetails` | `object` (non-null, non-array) | + +## Context and Identity + + +Context is set globally when registering the provider, not per-evaluation. Per-evaluation context passed to individual flag evaluations is ignored. + + +- **`targetingKey`** has no special meaning in this provider. It is treated as a regular context property. +- Identity should be managed through the Mixpanel SDK (e.g., setting `distinct_id` in the context). +- To update context after initialization, set a new global context via `OpenFeature.setContext()`. + +## Error Handling + +The provider returns the default value on all errors, with an error code indicating the cause: + +| Error Code | Condition | +|---|---| +| `PROVIDER_NOT_READY` | The provider has not been initialized | +| `FLAG_NOT_FOUND` | The requested flag does not exist | +| `TYPE_MISMATCH` | The flag value does not match the requested type | + +Use `getDetails()` methods instead of `getValue()` to inspect error codes: + +```javascript +const details = await client.getBooleanDetails('my-flag', false); +if (details.errorCode) { + console.log(`Flag error: ${details.errorCode} - ${details.errorMessage}`); +} +``` + +## Lifecycle + +- **`initialize()`** stores the global context for all subsequent evaluations. +- **`onClose()`** is a no-op. The Mixpanel SDK manages its own lifecycle. +- The reason code for successful evaluations is `STATIC`. diff --git a/pages/docs/tracking-methods/sdks/python/_meta.ts b/pages/docs/tracking-methods/sdks/python/_meta.ts index 2f8a88bad9..26d9e67855 100644 --- a/pages/docs/tracking-methods/sdks/python/_meta.ts +++ b/pages/docs/tracking-methods/sdks/python/_meta.ts @@ -1,3 +1,4 @@ export default { "python-flags": "Feature Flags (Python)", + "python-openfeature": "OpenFeature Provider (Python)", } diff --git a/pages/docs/tracking-methods/sdks/python/python-openfeature.mdx b/pages/docs/tracking-methods/sdks/python/python-openfeature.mdx new file mode 100644 index 0000000000..dfb0e116dd --- /dev/null +++ b/pages/docs/tracking-methods/sdks/python/python-openfeature.mdx @@ -0,0 +1,101 @@ +import { Callout } from 'nextra/components' + +# OpenFeature Provider (Python) + +## Overview + +The Mixpanel OpenFeature provider allows you to use [Mixpanel Feature Flags](/docs/featureflags) through the standardized [OpenFeature](https://openfeature.dev/) API. This provider wraps the Mixpanel Python SDK's feature flags, letting you use the OpenFeature Python SDK with Mixpanel as the backend. + +For the core Feature Flags SDK guide, see [Feature Flags (Python)](/docs/tracking-methods/sdks/python/python-flags). + +## Prerequisites + +- You are on an Enterprise subscription plan +- You have the [Mixpanel Python SDK](/docs/tracking-methods/sdks/python) installed +- You have your Project Token from your [Mixpanel Project Settings](/docs/orgs-and-projects/managing-projects#find-your-project-tokens) + +## Installation + +```bash +pip install mixpanel-openfeature-provider openfeature-sdk +``` + +## Usage + +The provider wraps either a local or remote flags provider from the Mixpanel SDK. + +### With Local Evaluation + +```python +from mixpanel import Mixpanel +from openfeature import api +from mixpanel_openfeature import MixpanelProvider + +mp = Mixpanel("YOUR_PROJECT_TOKEN", local_flags_config={ + "enable_polling": True, + "polling_interval_in_seconds": 60 +}) + +mp.local_flags.start_polling_for_definitions() + +# Register the provider +api.set_provider(MixpanelProvider(mp.local_flags)) + +client = api.get_client() +show_new_ui = client.get_boolean_value("new-ui", False) +``` + +### With Remote Evaluation + +```python +mp = Mixpanel("YOUR_PROJECT_TOKEN", remote_flags_config={ + "api_host": "api.mixpanel.com" +}) + +api.set_provider(MixpanelProvider(mp.remote_flags)) + +client = api.get_client() +variant = client.get_string_value("checkout-flow", "control") +``` + +## Supported Flag Types + +| OpenFeature Method | Python Type | +|---|---| +| `get_boolean_value` / `get_boolean_details` | `bool` | +| `get_string_value` / `get_string_details` | `str` | +| `get_integer_value` / `get_integer_details` | `int` | +| `get_float_value` / `get_float_details` | `float` | +| `get_object_value` / `get_object_details` | `dict` | + +## Context and Identity + + +Context is set globally when registering the provider, not per-evaluation. Per-evaluation context passed to individual flag evaluations is ignored. + + +- **`targeting_key`** has no special meaning in this provider. It is treated as a regular context property. +- Identity should be managed through the Mixpanel SDK (e.g., setting `distinct_id` in the context). + +## Error Handling + +The provider returns the default value on all errors, with an error code indicating the cause: + +| Error Code | Condition | +|---|---| +| `PROVIDER_NOT_READY` | The provider has not been initialized | +| `FLAG_NOT_FOUND` | The requested flag does not exist | +| `TYPE_MISMATCH` | The flag value does not match the requested type | + +Use `get_details()` methods instead of `get_value()` to inspect error codes: + +```python +details = client.get_boolean_details("my-flag", False) +if details.error_code: + print(f"Flag error: {details.error_code} - {details.error_message}") +``` + +## Lifecycle + +- **`shutdown()`** is a no-op. The Mixpanel SDK manages its own lifecycle. +- The reason code for successful evaluations is `STATIC`. diff --git a/pages/docs/tracking-methods/sdks/ruby/_meta.ts b/pages/docs/tracking-methods/sdks/ruby/_meta.ts index f7acbe4f14..1e4e2354db 100644 --- a/pages/docs/tracking-methods/sdks/ruby/_meta.ts +++ b/pages/docs/tracking-methods/sdks/ruby/_meta.ts @@ -1,3 +1,4 @@ export default { "ruby-flags": "Feature Flags (Ruby)", + "ruby-openfeature": "OpenFeature Provider (Ruby)", } diff --git a/pages/docs/tracking-methods/sdks/ruby/ruby-openfeature.mdx b/pages/docs/tracking-methods/sdks/ruby/ruby-openfeature.mdx new file mode 100644 index 0000000000..48cb92e4af --- /dev/null +++ b/pages/docs/tracking-methods/sdks/ruby/ruby-openfeature.mdx @@ -0,0 +1,113 @@ +import { Callout } from 'nextra/components' + +# OpenFeature Provider (Ruby) + +## Overview + +The Mixpanel OpenFeature provider allows you to use [Mixpanel Feature Flags](/docs/featureflags) through the standardized [OpenFeature](https://openfeature.dev/) API. This provider wraps the Mixpanel Ruby SDK's feature flags, letting you use the OpenFeature Ruby SDK with Mixpanel as the backend. + +For the core Feature Flags SDK guide, see [Feature Flags (Ruby)](/docs/tracking-methods/sdks/ruby/ruby-flags). + +## Prerequisites + +- You are on an Enterprise subscription plan +- You have the [Mixpanel Ruby SDK](/docs/tracking-methods/sdks/ruby) installed +- You have your Project Token from your [Mixpanel Project Settings](/docs/orgs-and-projects/managing-projects#find-your-project-tokens) + +## Installation + +```bash +gem install mixpanel-openfeature-provider openfeature-sdk +``` + +Or add to your `Gemfile`: + +```ruby +gem 'mixpanel-openfeature-provider' +gem 'openfeature-sdk' +``` + +## Usage + +The provider wraps either a local or remote flags provider from the Mixpanel SDK. + +### With Local Evaluation + +```ruby +require 'mixpanel' +require 'openfeature/sdk' +require 'mixpanel/openfeature' + +tracker = Mixpanel::Tracker.new('YOUR_PROJECT_TOKEN', local_flags_config: { + enable_polling: true, + polling_interval_in_seconds: 60 +}) + +tracker.local_flags.start_polling_for_definitions + +# Register the provider +OpenFeature::SDK.configure do |config| + config.set_provider(Mixpanel::OpenFeature::Provider.new(tracker.local_flags)) +end + +client = OpenFeature::SDK.build_client +show_new_ui = client.fetch_boolean_value(flag_key: 'new-ui', default_value: false) +``` + +### With Remote Evaluation + +```ruby +tracker = Mixpanel::Tracker.new('YOUR_PROJECT_TOKEN', remote_flags_config: { + api_host: 'api.mixpanel.com' +}) + +OpenFeature::SDK.configure do |config| + config.set_provider(Mixpanel::OpenFeature::Provider.new(tracker.remote_flags)) +end + +client = OpenFeature::SDK.build_client +variant = client.fetch_string_value(flag_key: 'checkout-flow', default_value: 'control') +``` + +## Supported Flag Types + +| OpenFeature Method | Ruby Type | +|---|---| +| `fetch_boolean_value` / `fetch_boolean_details` | `TrueClass` / `FalseClass` | +| `fetch_string_value` / `fetch_string_details` | `String` | +| `fetch_integer_value` / `fetch_integer_details` | `Integer` | +| `fetch_float_value` / `fetch_float_details` | `Float` | +| `fetch_object_value` / `fetch_object_details` | `Hash` | + +## Context and Identity + + +Context is set globally when registering the provider, not per-evaluation. Per-evaluation context passed to individual flag evaluations is ignored. + + +- **`targeting_key`** has no special meaning in this provider. It is treated as a regular context property. +- Identity should be managed through the Mixpanel SDK (e.g., setting `distinct_id` in the context). + +## Error Handling + +The provider returns the default value on all errors, with an error code indicating the cause: + +| Error Code | Condition | +|---|---| +| `PROVIDER_NOT_READY` | The provider has not been initialized | +| `FLAG_NOT_FOUND` | The requested flag does not exist | +| `TYPE_MISMATCH` | The flag value does not match the requested type | + +Use `fetch_details` methods to inspect error codes: + +```ruby +details = client.fetch_boolean_details(flag_key: 'my-flag', default_value: false) +if details.error_code + puts "Flag error: #{details.error_code} - #{details.error_message}" +end +``` + +## Lifecycle + +- **`shutdown`** is a no-op. The Mixpanel SDK manages its own lifecycle. +- The reason code for successful evaluations is `STATIC`. diff --git a/pages/docs/tracking-methods/sdks/swift/_meta.ts b/pages/docs/tracking-methods/sdks/swift/_meta.ts index cae800483b..62775e5fcf 100644 --- a/pages/docs/tracking-methods/sdks/swift/_meta.ts +++ b/pages/docs/tracking-methods/sdks/swift/_meta.ts @@ -1,4 +1,5 @@ export default { "swift-replay": "Session Replay (Swift)", - "swift-flags": "Feature Flags (Swift)" + "swift-flags": "Feature Flags (Swift)", + "swift-openfeature": "OpenFeature Provider (Swift)", } diff --git a/pages/docs/tracking-methods/sdks/swift/swift-openfeature.mdx b/pages/docs/tracking-methods/sdks/swift/swift-openfeature.mdx new file mode 100644 index 0000000000..b21b6b178e --- /dev/null +++ b/pages/docs/tracking-methods/sdks/swift/swift-openfeature.mdx @@ -0,0 +1,100 @@ +import { Callout } from 'nextra/components' + +# OpenFeature Provider (Swift) + +## Overview + +The Mixpanel OpenFeature provider allows you to use [Mixpanel Feature Flags](/docs/featureflags) through the standardized [OpenFeature](https://openfeature.dev/) API. This provider wraps the Mixpanel Swift SDK's `MixpanelFlags` protocol, letting you use the OpenFeature Swift SDK with Mixpanel as the backend. + +For the core Feature Flags SDK guide, see [Feature Flags (Swift)](/docs/tracking-methods/sdks/swift/swift-flags). + +## Prerequisites + +- You are on an Enterprise subscription plan +- You have the [Mixpanel Swift SDK](/docs/tracking-methods/sdks/swift) installed +- You have your Project Token from your [Mixpanel Project Settings](/docs/orgs-and-projects/managing-projects#find-your-project-tokens) + +## Installation + +### Swift Package Manager + +Add the OpenFeature provider package to your `Package.swift`: + +```swift +dependencies: [ + .package(url: "https://github.com/mixpanel/mixpanel-swift-openfeature-provider", from: "0.1.0"), + .package(url: "https://github.com/open-feature/swift-sdk", from: "0.1.0"), +] +``` + +### CocoaPods + +```ruby +pod 'MixpanelOpenFeatureProvider' +pod 'OpenFeature' +``` + +## Usage + +The provider wraps `MixpanelFlags` from the Mixpanel Swift SDK. + +```swift +import Mixpanel +import MixpanelOpenFeatureProvider +import OpenFeature + +// Initialize Mixpanel with feature flags enabled +Mixpanel.initialize(token: "YOUR_PROJECT_TOKEN", options: MixpanelOptions( + featureFlagOptions: FeatureFlagOptions(enabled: true) +)) + +// Register the OpenFeature provider +let provider = MixpanelProvider(flags: Mixpanel.mainInstance().flags) +OpenFeatureAPI.shared.setProvider(provider: provider) + +let client = OpenFeatureAPI.shared.getClient() +let showNewUI = client.getBooleanValue(key: "new-ui", defaultValue: false) +``` + +## Supported Flag Types + +| OpenFeature Method | Swift Type | +|---|---| +| `getBooleanValue` / `getBooleanDetails` | `Bool` | +| `getStringValue` / `getStringDetails` | `String` | +| `getIntegerValue` / `getIntegerDetails` | `Int64` | +| `getDoubleValue` / `getDoubleDetails` | `Double` | +| `getObjectValue` / `getObjectDetails` | `Value` | + +## Context and Identity + + +Context is set globally when registering the provider, not per-evaluation. Per-evaluation context passed to individual flag evaluations is ignored. + + +- **`targetingKey`** has no special meaning in this provider. It is treated as a regular context property. +- Identity should be managed through the Mixpanel SDK via `Mixpanel.mainInstance().identify()`. + +## Error Handling + +The provider returns the default value on all errors, with an error code indicating the cause: + +| Error Code | Condition | +|---|---| +| `PROVIDER_NOT_READY` | The provider has not been initialized | +| `FLAG_NOT_FOUND` | The requested flag does not exist | +| `TYPE_MISMATCH` | The flag value does not match the requested type | + +Use `getDetails()` methods instead of `getValue()` to inspect error codes: + +```swift +let details = client.getBooleanDetails(key: "my-flag", defaultValue: false) +if let errorCode = details.errorCode { + print("Flag error: \(errorCode) - \(details.errorMessage ?? "")") +} +``` + +## Lifecycle + +- **`onClose()`** is a no-op. The Mixpanel SDK manages its own lifecycle. +- The reason code for successful evaluations is `STATIC`. From 927845ae341f7fdbd884742401c81b553f3747bb Mon Sep 17 00:00:00 2001 From: Mark Siebert Date: Tue, 7 Apr 2026 10:32:44 -0700 Subject: [PATCH 2/5] Update OpenFeature docs to match current SDK implementations - Go: fix import paths to v2, add convenience constructors, fix shutdown docs - Java: add convenience constructors, fix shutdown docs - Node.js: add createLocal/createRemote factory methods, fix context merging docs - Python: add from_local_config/from_remote_config factory methods - Ruby: add from_local/from_remote factory methods, fix gem name - Swift: fix package URL/name, use MixpanelOpenFeatureProvider class name - Android: add convenience constructor, fix artifact ID and SDK dependency - All: show both convenience and direct constructor patterns, fix context and lifecycle documentation to match actual behavior Co-Authored-By: Claude Opus 4.6 --- .../sdks/android/android-openfeature.mdx | 33 +++++++-- .../sdks/go/go-openfeature.mdx | 73 ++++++++++++------- .../sdks/java/java-openfeature.mdx | 34 +++++---- .../sdks/nodejs/nodejs-openfeature.mdx | 54 ++++++++------ .../sdks/python/python-openfeature.mdx | 53 +++++++++----- .../sdks/ruby/ruby-openfeature.mdx | 41 +++++++---- .../sdks/swift/swift-openfeature.mdx | 51 ++++++++----- 7 files changed, 223 insertions(+), 116 deletions(-) diff --git a/pages/docs/tracking-methods/sdks/android/android-openfeature.mdx b/pages/docs/tracking-methods/sdks/android/android-openfeature.mdx index 9e2d005e21..9a054968d0 100644 --- a/pages/docs/tracking-methods/sdks/android/android-openfeature.mdx +++ b/pages/docs/tracking-methods/sdks/android/android-openfeature.mdx @@ -20,8 +20,8 @@ Add the OpenFeature provider and SDK to your `build.gradle`: ```groovy dependencies { - implementation 'com.mixpanel:mixpanel-android-openfeature-provider:1.0.0' - implementation 'dev.openfeature:android-sdk:0.3.0' + implementation 'com.mixpanel:mixpanel-android-openfeature:1.0.0' + implementation 'dev.openfeature:kotlin-sdk-android:0.7.2' } ``` @@ -29,15 +29,32 @@ dependencies { The provider wraps `MixpanelAPI.Flags` from the Mixpanel Android SDK. +### Using the Convenience Constructor + ```kotlin -import com.mixpanel.android.mpmetrics.MixpanelAPI -import com.mixpanel.openfeature.MixpanelProvider +import com.mixpanel.android.openfeature.MixpanelProvider +import com.mixpanel.android.mpmetrics.MixpanelOptions import dev.openfeature.sdk.OpenFeatureAPI -// Initialize Mixpanel +// Create provider (internally creates a MixpanelAPI instance) +val provider = MixpanelProvider(context, "YOUR_PROJECT_TOKEN", MixpanelOptions()) +OpenFeatureAPI.setProvider(provider) + +val client = OpenFeatureAPI.getClient() +val showNewUI = client.getBooleanValue("new-ui", false) + +// Access the underlying Mixpanel instance for identify/track +provider.mixpanel?.identify("user-123") +``` + +### Using an Existing MixpanelAPI Instance + +```kotlin +import com.mixpanel.android.mpmetrics.MixpanelAPI +import com.mixpanel.android.openfeature.MixpanelProvider + val mixpanel = MixpanelAPI.getInstance(context, "YOUR_PROJECT_TOKEN") -// Register the OpenFeature provider val provider = MixpanelProvider(mixpanel.flags) OpenFeatureAPI.setProvider(provider) @@ -61,8 +78,10 @@ val showNewUI = client.getBooleanValue("new-ui", false) Context is set globally when registering the provider, not per-evaluation. Per-evaluation context passed to individual flag evaluations is ignored. +The provider forwards the global OpenFeature context to the Mixpanel SDK via `setContext()` during initialization and whenever the global context changes. + - **`targetingKey`** has no special meaning in this provider. It is treated as a regular context property. -- Identity should be managed through the Mixpanel SDK via `mixpanel.identify()`. +- Identity should be managed through the Mixpanel SDK via `mixpanel.identify()` or `provider.mixpanel?.identify()`. ## Error Handling diff --git a/pages/docs/tracking-methods/sdks/go/go-openfeature.mdx b/pages/docs/tracking-methods/sdks/go/go-openfeature.mdx index 68aba79902..ddadcc7043 100644 --- a/pages/docs/tracking-methods/sdks/go/go-openfeature.mdx +++ b/pages/docs/tracking-methods/sdks/go/go-openfeature.mdx @@ -17,13 +17,13 @@ For the core Feature Flags SDK guide, see [Feature Flags (Go)](/docs/tracking-me ## Installation ```bash -go get github.com/mixpanel/mixpanel-go/openfeature-provider +go get github.com/mixpanel/mixpanel-go/v2/openfeature go get github.com/open-feature/go-sdk ``` ## Usage -The provider wraps either a `LocalFlagsProvider` or `RemoteFlagsProvider` from the Mixpanel SDK. +The provider wraps either a `LocalFeatureFlagsProvider` or `RemoteFeatureFlagsProvider` from the Mixpanel SDK. ### With Local Evaluation @@ -33,25 +33,24 @@ package main import ( "context" "fmt" + "time" - mixpanel "github.com/mixpanel/mixpanel-go" - mpof "github.com/mixpanel/mixpanel-go/openfeature-provider" + "github.com/mixpanel/mixpanel-go/v2/flags" + mpof "github.com/mixpanel/mixpanel-go/v2/openfeature" "github.com/open-feature/go-sdk/openfeature" ) func main() { - mp := mixpanel.NewClient("YOUR_PROJECT_TOKEN", - mixpanel.WithLocalFlagsConfig(mixpanel.LocalFlagsConfig{ - EnablePolling: true, - PollingInterval: 60 * time.Second, - }), - ) - - mp.LocalFlags().StartPollingForDefinitions() - defer mp.LocalFlags().StopPollingForDefinitions() - - // Register the provider - provider := mpof.NewMixpanelProvider(mp.LocalFlags()) + config := flags.DefaultLocalFlagsConfig() + config.PollingInterval = 60 * time.Second + + // Create a provider with local evaluation (automatically starts polling) + provider, err := mpof.NewProviderWithLocalConfig("YOUR_PROJECT_TOKEN", config) + if err != nil { + panic(err) + } + defer provider.Shutdown() + openfeature.SetProvider(provider) client := openfeature.NewClient("my-app") @@ -63,19 +62,43 @@ func main() { ### With Remote Evaluation ```go -mp := mixpanel.NewClient("YOUR_PROJECT_TOKEN", - mixpanel.WithRemoteFlagsConfig(mixpanel.RemoteFlagsConfig{ - APIHost: "api.mixpanel.com", - }), -) +config := flags.DefaultRemoteFlagsConfig() + +provider, err := mpof.NewProviderWithRemoteConfig("YOUR_PROJECT_TOKEN", config) +if err != nil { + panic(err) +} -provider := mpof.NewMixpanelProvider(mp.RemoteFlags()) openfeature.SetProvider(provider) client := openfeature.NewClient("my-app") variant, _ := client.StringValue(context.Background(), "checkout-flow", "control", openfeature.EvaluationContext{}) ``` +### With an Existing Flags Provider + +If you already have a configured `LocalFeatureFlagsProvider` or `RemoteFeatureFlagsProvider`, you can wrap it directly: + +```go +import ( + mixpanel "github.com/mixpanel/mixpanel-go/v2" + mpof "github.com/mixpanel/mixpanel-go/v2/openfeature" +) + +mp := mixpanel.NewClient("YOUR_PROJECT_TOKEN", + mixpanel.WithLocalFlagsConfig(mixpanel.LocalFlagsConfig{ + EnablePolling: true, + PollingInterval: 60 * time.Second, + }), +) + +mp.LocalFlags().StartPollingForDefinitions() +defer mp.LocalFlags().StopPollingForDefinitions() + +provider := mpof.NewProvider(mp.LocalFlags()) +openfeature.SetProvider(provider) +``` + ## Supported Flag Types | OpenFeature Method | Go Type | @@ -88,9 +111,7 @@ variant, _ := client.StringValue(context.Background(), "checkout-flow", "control ## Context and Identity - -Context is set globally when registering the provider, not per-evaluation. Per-evaluation context passed to individual flag evaluations is ignored. - +The evaluation context passed to each flag evaluation is forwarded to the Mixpanel flags provider. - **`targetingKey`** has no special meaning in this provider. It is treated as a regular context property. - Identity should be managed through the Mixpanel SDK (e.g., setting `distinct_id` in the context). @@ -116,5 +137,5 @@ if details.ErrorCode != "" { ## Lifecycle -- **`Shutdown()`** is a no-op. The Mixpanel SDK manages its own lifecycle. +- **`Shutdown()`** stops background polling for local evaluation providers. Call it when your application exits. - The reason code for successful evaluations is `STATIC`. diff --git a/pages/docs/tracking-methods/sdks/java/java-openfeature.mdx b/pages/docs/tracking-methods/sdks/java/java-openfeature.mdx index 437be821cf..ed1f6d0b2a 100644 --- a/pages/docs/tracking-methods/sdks/java/java-openfeature.mdx +++ b/pages/docs/tracking-methods/sdks/java/java-openfeature.mdx @@ -45,16 +45,12 @@ The provider wraps a `BaseFlagsProvider` (either `LocalFlagsProvider` or `Remote ### With Local Evaluation ```java -import com.mixpanel.mixpanelapi.featureflags.provider.LocalFlagsProvider; import com.mixpanel.openfeature.MixpanelProvider; import dev.openfeature.sdk.*; -// Initialize Mixpanel flags provider -LocalFlagsProvider flagsProvider = new LocalFlagsProvider("YOUR_PROJECT_TOKEN", config, sdkVersion, eventSender); - -// Register with OpenFeature +// Create provider with local evaluation (automatically starts polling) OpenFeatureAPI api = OpenFeatureAPI.getInstance(); -api.setProvider(new MixpanelProvider(flagsProvider)); +api.setProvider(new MixpanelProvider("YOUR_PROJECT_TOKEN", localFlagsConfig)); Client client = api.getClient(); boolean showNewUI = client.getBooleanValue("new-ui", false); @@ -63,17 +59,29 @@ boolean showNewUI = client.getBooleanValue("new-ui", false); ### With Remote Evaluation ```java -import com.mixpanel.mixpanelapi.featureflags.provider.RemoteFlagsProvider; - -RemoteFlagsProvider flagsProvider = new RemoteFlagsProvider("YOUR_PROJECT_TOKEN", config, sdkVersion, eventSender); +import com.mixpanel.openfeature.MixpanelProvider; OpenFeatureAPI api = OpenFeatureAPI.getInstance(); -api.setProvider(new MixpanelProvider(flagsProvider)); +api.setProvider(new MixpanelProvider("YOUR_PROJECT_TOKEN", remoteFlagsConfig)); Client client = api.getClient(); String variant = client.getStringValue("checkout-flow", "control"); ``` +### With an Existing Flags Provider + +If you already have a configured `LocalFlagsProvider` or `RemoteFlagsProvider`, you can wrap it directly: + +```java +import com.mixpanel.mixpanelapi.featureflags.provider.LocalFlagsProvider; +import com.mixpanel.openfeature.MixpanelProvider; + +LocalFlagsProvider flagsProvider = new LocalFlagsProvider("YOUR_PROJECT_TOKEN", config, sdkVersion, eventSender); + +OpenFeatureAPI api = OpenFeatureAPI.getInstance(); +api.setProvider(new MixpanelProvider(flagsProvider)); +``` + ## Supported Flag Types | OpenFeature Method | Java Type | @@ -88,9 +96,7 @@ Numeric types are coerced automatically. For example, a flag returning a `Long` ## Context and Identity - -Context is set globally when registering the provider, not per-evaluation. Per-evaluation context passed to individual flag evaluations is ignored. - +The evaluation context passed to each flag evaluation is forwarded to the Mixpanel flags provider. - **`targetingKey`** has no special meaning in this provider. It is treated as a regular context property. - Identity should be managed through the Mixpanel SDK (e.g., setting `distinct_id` in the context). @@ -116,5 +122,5 @@ if (details.getErrorCode() != null) { ## Lifecycle -- **`shutdown()`** is a no-op. The Mixpanel SDK manages its own lifecycle. +- **`shutdown()`** delegates to the underlying flags provider to clean up resources (e.g., stop polling). - The reason code for successful evaluations is `STATIC`. diff --git a/pages/docs/tracking-methods/sdks/nodejs/nodejs-openfeature.mdx b/pages/docs/tracking-methods/sdks/nodejs/nodejs-openfeature.mdx index fe512e6540..5362485427 100644 --- a/pages/docs/tracking-methods/sdks/nodejs/nodejs-openfeature.mdx +++ b/pages/docs/tracking-methods/sdks/nodejs/nodejs-openfeature.mdx @@ -22,26 +22,21 @@ npm install @mixpanel/openfeature-server-provider @openfeature/server-sdk ## Usage -The provider wraps either a `LocalFeatureFlagsProvider` or `RemoteFeatureFlagsProvider` from the Mixpanel SDK. Initialize Mixpanel with feature flags first, then register the provider with OpenFeature. +The provider wraps either a `LocalFeatureFlagsProvider` or `RemoteFeatureFlagsProvider` from the Mixpanel SDK. ### With Local Evaluation ```javascript -const Mixpanel = require('mixpanel'); const { OpenFeature } = require('@openfeature/server-sdk'); const { MixpanelProvider } = require('@mixpanel/openfeature-server-provider'); -const mixpanel = Mixpanel.init('YOUR_PROJECT_TOKEN', { - local_flags_config: { - enable_polling: true, - polling_interval_in_seconds: 60 - } +// Create provider with local evaluation (automatically starts polling) +const provider = MixpanelProvider.createLocal('YOUR_PROJECT_TOKEN', { + enable_polling: true, + polling_interval_in_seconds: 60 }); -await mixpanel.local_flags.startPollingForDefinitions(); - -// Register the provider with the user context for evaluation -await OpenFeature.setProviderAndWait(new MixpanelProvider(mixpanel.local_flags)); +await OpenFeature.setProviderAndWait(provider); OpenFeature.setContext({ distinct_id: 'user-123', plan: 'premium' }); const client = OpenFeature.getClient(); @@ -51,19 +46,38 @@ const showNewUI = await client.getBooleanValue('new-ui', false); ### With Remote Evaluation ```javascript -const mixpanel = Mixpanel.init('YOUR_PROJECT_TOKEN', { - remote_flags_config: { - api_host: 'api.mixpanel.com' - } +const provider = MixpanelProvider.createRemote('YOUR_PROJECT_TOKEN', { + api_host: 'api.mixpanel.com' }); -await OpenFeature.setProviderAndWait(new MixpanelProvider(mixpanel.remote_flags)); +await OpenFeature.setProviderAndWait(provider); OpenFeature.setContext({ distinct_id: 'user-123' }); const client = OpenFeature.getClient(); const variant = await client.getStringValue('checkout-flow', 'control'); ``` +### With an Existing Flags Provider + +If you already have a configured Mixpanel instance, you can wrap its flags provider directly: + +```javascript +const Mixpanel = require('mixpanel'); +const { MixpanelProvider } = require('@mixpanel/openfeature-server-provider'); + +const mixpanel = Mixpanel.init('YOUR_PROJECT_TOKEN', { + local_flags_config: { + enable_polling: true, + polling_interval_in_seconds: 60 + } +}); + +await mixpanel.local_flags.startPollingForDefinitions(); + +const provider = new MixpanelProvider(mixpanel.local_flags); +await OpenFeature.setProviderAndWait(provider); +``` + ## Supported Flag Types | OpenFeature Method | JavaScript Type | @@ -75,13 +89,11 @@ const variant = await client.getStringValue('checkout-flow', 'control'); ## Context and Identity - -Context is set globally when registering the provider, not per-evaluation. Per-evaluation context passed to individual flag evaluations is ignored. - +The provider merges the base context (set via `OpenFeature.setContext()` during initialization) with any per-evaluation context. Per-evaluation context values override the base context for overlapping keys. - **`targetingKey`** has no special meaning in this provider. It is treated as a regular context property. - Identity should be managed through the Mixpanel SDK (e.g., setting `distinct_id` in the context). -- To update context after initialization, set a new global context via `OpenFeature.setContext()`. +- To update the base context after initialization, call `OpenFeature.setContext()`. ## Error Handling @@ -105,5 +117,5 @@ if (details.errorCode) { ## Lifecycle - **`initialize()`** stores the global context for all subsequent evaluations. -- **`onClose()`** is a no-op. The Mixpanel SDK manages its own lifecycle. +- **`onClose()`** delegates to the underlying flags provider's `shutdown()` method if available. - The reason code for successful evaluations is `STATIC`. diff --git a/pages/docs/tracking-methods/sdks/python/python-openfeature.mdx b/pages/docs/tracking-methods/sdks/python/python-openfeature.mdx index dfb0e116dd..28cc55be31 100644 --- a/pages/docs/tracking-methods/sdks/python/python-openfeature.mdx +++ b/pages/docs/tracking-methods/sdks/python/python-openfeature.mdx @@ -27,19 +27,18 @@ The provider wraps either a local or remote flags provider from the Mixpanel SDK ### With Local Evaluation ```python -from mixpanel import Mixpanel from openfeature import api from mixpanel_openfeature import MixpanelProvider +from mixpanel.flags.types import LocalFlagsConfig -mp = Mixpanel("YOUR_PROJECT_TOKEN", local_flags_config={ - "enable_polling": True, - "polling_interval_in_seconds": 60 -}) - -mp.local_flags.start_polling_for_definitions() +config = LocalFlagsConfig( + enable_polling=True, + polling_interval_in_seconds=60 +) -# Register the provider -api.set_provider(MixpanelProvider(mp.local_flags)) +# Create provider with local evaluation (automatically starts polling) +provider = MixpanelProvider.from_local_config("YOUR_PROJECT_TOKEN", config) +api.set_provider(provider) client = api.get_client() show_new_ui = client.get_boolean_value("new-ui", False) @@ -48,16 +47,38 @@ show_new_ui = client.get_boolean_value("new-ui", False) ### With Remote Evaluation ```python -mp = Mixpanel("YOUR_PROJECT_TOKEN", remote_flags_config={ - "api_host": "api.mixpanel.com" -}) +from mixpanel.flags.types import RemoteFlagsConfig + +config = RemoteFlagsConfig( + api_host="api.mixpanel.com" +) -api.set_provider(MixpanelProvider(mp.remote_flags)) +provider = MixpanelProvider.from_remote_config("YOUR_PROJECT_TOKEN", config) +api.set_provider(provider) client = api.get_client() variant = client.get_string_value("checkout-flow", "control") ``` +### With an Existing Flags Provider + +If you already have a configured Mixpanel instance, you can wrap its flags provider directly: + +```python +from mixpanel import Mixpanel +from mixpanel_openfeature import MixpanelProvider + +mp = Mixpanel("YOUR_PROJECT_TOKEN", local_flags_config={ + "enable_polling": True, + "polling_interval_in_seconds": 60 +}) + +mp.local_flags.start_polling_for_definitions() + +provider = MixpanelProvider(mp.local_flags) +api.set_provider(provider) +``` + ## Supported Flag Types | OpenFeature Method | Python Type | @@ -70,9 +91,7 @@ variant = client.get_string_value("checkout-flow", "control") ## Context and Identity - -Context is set globally when registering the provider, not per-evaluation. Per-evaluation context passed to individual flag evaluations is ignored. - +The evaluation context passed to each flag evaluation is forwarded to the Mixpanel flags provider. - **`targeting_key`** has no special meaning in this provider. It is treated as a regular context property. - Identity should be managed through the Mixpanel SDK (e.g., setting `distinct_id` in the context). @@ -97,5 +116,5 @@ if details.error_code: ## Lifecycle -- **`shutdown()`** is a no-op. The Mixpanel SDK manages its own lifecycle. +- **`shutdown()`** delegates to the underlying flags provider to clean up resources (e.g., stop polling). - The reason code for successful evaluations is `STATIC`. diff --git a/pages/docs/tracking-methods/sdks/ruby/ruby-openfeature.mdx b/pages/docs/tracking-methods/sdks/ruby/ruby-openfeature.mdx index 48cb92e4af..afe0c3462e 100644 --- a/pages/docs/tracking-methods/sdks/ruby/ruby-openfeature.mdx +++ b/pages/docs/tracking-methods/sdks/ruby/ruby-openfeature.mdx @@ -17,13 +17,13 @@ For the core Feature Flags SDK guide, see [Feature Flags (Ruby)](/docs/tracking- ## Installation ```bash -gem install mixpanel-openfeature-provider openfeature-sdk +gem install mixpanel-ruby-openfeature openfeature-sdk ``` Or add to your `Gemfile`: ```ruby -gem 'mixpanel-openfeature-provider' +gem 'mixpanel-ruby-openfeature' gem 'openfeature-sdk' ``` @@ -34,20 +34,17 @@ The provider wraps either a local or remote flags provider from the Mixpanel SDK ### With Local Evaluation ```ruby -require 'mixpanel' require 'openfeature/sdk' require 'mixpanel/openfeature' -tracker = Mixpanel::Tracker.new('YOUR_PROJECT_TOKEN', local_flags_config: { +# Create provider with local evaluation (automatically starts polling) +provider = Mixpanel::OpenFeature::Provider.from_local('YOUR_PROJECT_TOKEN', { enable_polling: true, polling_interval_in_seconds: 60 }) -tracker.local_flags.start_polling_for_definitions - -# Register the provider OpenFeature::SDK.configure do |config| - config.set_provider(Mixpanel::OpenFeature::Provider.new(tracker.local_flags)) + config.set_provider(provider) end client = OpenFeature::SDK.build_client @@ -57,18 +54,36 @@ show_new_ui = client.fetch_boolean_value(flag_key: 'new-ui', default_value: fals ### With Remote Evaluation ```ruby -tracker = Mixpanel::Tracker.new('YOUR_PROJECT_TOKEN', remote_flags_config: { +provider = Mixpanel::OpenFeature::Provider.from_remote('YOUR_PROJECT_TOKEN', { api_host: 'api.mixpanel.com' }) OpenFeature::SDK.configure do |config| - config.set_provider(Mixpanel::OpenFeature::Provider.new(tracker.remote_flags)) + config.set_provider(provider) end client = OpenFeature::SDK.build_client variant = client.fetch_string_value(flag_key: 'checkout-flow', default_value: 'control') ``` +### With an Existing Flags Provider + +If you already have a configured Tracker instance, you can wrap its flags provider directly: + +```ruby +require 'mixpanel' +require 'mixpanel/openfeature' + +tracker = Mixpanel::Tracker.new('YOUR_PROJECT_TOKEN', local_flags_config: { + enable_polling: true, + polling_interval_in_seconds: 60 +}) + +tracker.local_flags.start_polling_for_definitions + +provider = Mixpanel::OpenFeature::Provider.new(tracker.local_flags) +``` + ## Supported Flag Types | OpenFeature Method | Ruby Type | @@ -81,9 +96,7 @@ variant = client.fetch_string_value(flag_key: 'checkout-flow', default_value: 'c ## Context and Identity - -Context is set globally when registering the provider, not per-evaluation. Per-evaluation context passed to individual flag evaluations is ignored. - +The evaluation context passed to each flag evaluation is forwarded to the Mixpanel flags provider. - **`targeting_key`** has no special meaning in this provider. It is treated as a regular context property. - Identity should be managed through the Mixpanel SDK (e.g., setting `distinct_id` in the context). @@ -109,5 +122,5 @@ end ## Lifecycle -- **`shutdown`** is a no-op. The Mixpanel SDK manages its own lifecycle. +- **`shutdown`** delegates to the underlying flags provider to clean up resources if supported. - The reason code for successful evaluations is `STATIC`. diff --git a/pages/docs/tracking-methods/sdks/swift/swift-openfeature.mdx b/pages/docs/tracking-methods/sdks/swift/swift-openfeature.mdx index b21b6b178e..2d892855b2 100644 --- a/pages/docs/tracking-methods/sdks/swift/swift-openfeature.mdx +++ b/pages/docs/tracking-methods/sdks/swift/swift-openfeature.mdx @@ -11,7 +11,7 @@ For the core Feature Flags SDK guide, see [Feature Flags (Swift)](/docs/tracking ## Prerequisites - You are on an Enterprise subscription plan -- You have the [Mixpanel Swift SDK](/docs/tracking-methods/sdks/swift) installed +- You have the [Mixpanel Swift SDK](/docs/tracking-methods/sdks/swift) installed (minimum version `6.2.0`) - You have your Project Token from your [Mixpanel Project Settings](/docs/orgs-and-projects/managing-projects#find-your-project-tokens) ## Installation @@ -22,35 +22,50 @@ Add the OpenFeature provider package to your `Package.swift`: ```swift dependencies: [ - .package(url: "https://github.com/mixpanel/mixpanel-swift-openfeature-provider", from: "0.1.0"), - .package(url: "https://github.com/open-feature/swift-sdk", from: "0.1.0"), + .package(url: "https://github.com/mixpanel/mixpanel-swift-openfeature", from: "0.1.0"), + .package(url: "https://github.com/open-feature/swift-sdk", from: "0.5.0"), ] ``` -### CocoaPods - -```ruby -pod 'MixpanelOpenFeatureProvider' -pod 'OpenFeature' -``` - ## Usage The provider wraps `MixpanelFlags` from the Mixpanel Swift SDK. +### Using the Convenience Initializer + ```swift import Mixpanel -import MixpanelOpenFeatureProvider +import MixpanelOpenFeature +import OpenFeature + +// Create provider with a new Mixpanel instance +let provider = MixpanelOpenFeatureProvider(options: MixpanelOptions( + token: "YOUR_PROJECT_TOKEN", + featureFlagOptions: FeatureFlagOptions(enabled: true) +)) + +await OpenFeatureAPI.shared.setProviderAndWait(provider: provider) + +let client = OpenFeatureAPI.shared.getClient() +let showNewUI = client.getBooleanValue(key: "new-ui", defaultValue: false) + +// Access the underlying Mixpanel instance for identify/track +provider.mixpanel?.identify(distinctId: "user-123") +``` + +### Using an Existing Mixpanel Instance + +```swift +import Mixpanel +import MixpanelOpenFeature import OpenFeature -// Initialize Mixpanel with feature flags enabled Mixpanel.initialize(token: "YOUR_PROJECT_TOKEN", options: MixpanelOptions( featureFlagOptions: FeatureFlagOptions(enabled: true) )) -// Register the OpenFeature provider -let provider = MixpanelProvider(flags: Mixpanel.mainInstance().flags) -OpenFeatureAPI.shared.setProvider(provider: provider) +let provider = MixpanelOpenFeatureProvider(flags: Mixpanel.mainInstance().flags) +await OpenFeatureAPI.shared.setProviderAndWait(provider: provider) let client = OpenFeatureAPI.shared.getClient() let showNewUI = client.getBooleanValue(key: "new-ui", defaultValue: false) @@ -72,8 +87,10 @@ let showNewUI = client.getBooleanValue(key: "new-ui", defaultValue: false) Context is set globally when registering the provider, not per-evaluation. Per-evaluation context passed to individual flag evaluations is ignored. +The provider forwards the global OpenFeature context to the Mixpanel SDK via `setContext()` during initialization and whenever the global context changes. + - **`targetingKey`** has no special meaning in this provider. It is treated as a regular context property. -- Identity should be managed through the Mixpanel SDK via `Mixpanel.mainInstance().identify()`. +- Identity should be managed through the Mixpanel SDK via `Mixpanel.mainInstance().identify()` or `provider.mixpanel?.identify()`. ## Error Handling @@ -96,5 +113,5 @@ if let errorCode = details.errorCode { ## Lifecycle -- **`onClose()`** is a no-op. The Mixpanel SDK manages its own lifecycle. +- **`shutdown()`** is a no-op. The Mixpanel SDK manages its own lifecycle. - The reason code for successful evaluations is `STATIC`. From ab89cac7d36567a8feba49f8e78153c13fe1826f Mon Sep 17 00:00:00 2001 From: Mark Siebert Date: Fri, 10 Apr 2026 16:59:26 -0700 Subject: [PATCH 3/5] Update OpenFeature docs to match current SDK READMEs and add Web provider - Update all 7 existing OpenFeature provider docs with comprehensive content from the current SDK READMEs: flag type mapping tables, evaluation context examples, custom_properties/runtime properties, full resolution details, troubleshooting sections - Add new Web (JavaScript) OpenFeature provider doc and _meta.ts entry - Add OpenFeature Providers section to featureflags.mdx with card links to all 8 providers (3 client-side, 5 server-side) - Add React integration example (Web) and Rails integration example (Ruby) Co-Authored-By: Claude Opus 4.6 --- pages/docs/featureflags.mdx | 22 ++ .../sdks/android/android-openfeature.mdx | 191 +++++++++++----- .../sdks/go/go-openfeature.mdx | 189 +++++++++------- .../sdks/java/java-openfeature.mdx | 184 ++++++++++------ .../tracking-methods/sdks/javascript/_meta.ts | 1 + .../javascript/javascript-openfeature.mdx | 198 +++++++++++++++++ .../sdks/nodejs/nodejs-openfeature.mdx | 190 ++++++++++------ .../sdks/python/python-openfeature.mdx | 195 +++++++++++------ .../sdks/ruby/ruby-openfeature.mdx | 207 ++++++++++++------ .../sdks/swift/swift-openfeature.mdx | 189 +++++++++++----- 10 files changed, 1118 insertions(+), 448 deletions(-) create mode 100644 pages/docs/tracking-methods/sdks/javascript/javascript-openfeature.mdx diff --git a/pages/docs/featureflags.mdx b/pages/docs/featureflags.mdx index bf04cad403..7fba796c97 100644 --- a/pages/docs/featureflags.mdx +++ b/pages/docs/featureflags.mdx @@ -248,6 +248,28 @@ See our developer guides on implementing feature flags on these platforms below: +### OpenFeature Providers + +Mixpanel also offers [OpenFeature](https://openfeature.dev/) providers, which let you use Mixpanel's feature flags through OpenFeature's standardized, vendor-agnostic API. + +#### Client Side + + + + + + + +#### Server Side + + + + + + + + + If you'd like to see Feature Flags availability in other SDKs, please [reach out to the Support team](https://mixpanel.com/get-support). diff --git a/pages/docs/tracking-methods/sdks/android/android-openfeature.mdx b/pages/docs/tracking-methods/sdks/android/android-openfeature.mdx index 9a054968d0..02e54d77eb 100644 --- a/pages/docs/tracking-methods/sdks/android/android-openfeature.mdx +++ b/pages/docs/tracking-methods/sdks/android/android-openfeature.mdx @@ -4,105 +4,186 @@ import { Callout } from 'nextra/components' ## Overview -The Mixpanel OpenFeature provider allows you to use [Mixpanel Feature Flags](/docs/featureflags) through the standardized [OpenFeature](https://openfeature.dev/) API. This provider wraps the Mixpanel Android SDK's `MixpanelAPI.Flags`, letting you use the OpenFeature Kotlin SDK with Mixpanel as the backend. +This guide covers using Mixpanel's [Feature Flags](/docs/featureflags) through the [OpenFeature](https://openfeature.dev/) standard with the Mixpanel Android OpenFeature provider. OpenFeature provides a vendor-agnostic API for feature flag evaluation, allowing you to switch between providers without changing your application code. -For the core Feature Flags SDK guide, see [Feature Flags (Android)](/docs/tracking-methods/sdks/android/android-flags). +For the native Mixpanel SDK approach, see the [Feature Flags (Android)](/docs/tracking-methods/sdks/android/android-flags) guide. ## Prerequisites -- You are on an Enterprise subscription plan -- You have the [Mixpanel Android SDK](/docs/tracking-methods/sdks/android) installed -- You have your Project Token from your [Mixpanel Project Settings](/docs/orgs-and-projects/managing-projects#find-your-project-tokens) +- Enterprise subscription plan with Feature Flags enabled +- Android SDK 21+ +- Project Token from your [Mixpanel Project Settings](/docs/orgs-and-projects/managing-projects#find-your-project-tokens) ## Installation -Add the OpenFeature provider and SDK to your `build.gradle`: +Add to your `build.gradle.kts`: -```groovy +```kotlin dependencies { - implementation 'com.mixpanel:mixpanel-android-openfeature:1.0.0' - implementation 'dev.openfeature:kotlin-sdk-android:0.7.2' + implementation("com.mixpanel.android:mixpanel-android-openfeature:") + implementation("dev.openfeature:kotlin-sdk-android:0.7.2") } ``` -## Usage - -The provider wraps `MixpanelAPI.Flags` from the Mixpanel Android SDK. - -### Using the Convenience Constructor +## Quick Start ```kotlin import com.mixpanel.android.openfeature.MixpanelProvider import com.mixpanel.android.mpmetrics.MixpanelOptions -import dev.openfeature.sdk.OpenFeatureAPI +import dev.openfeature.kotlin.sdk.OpenFeatureAPI -// Create provider (internally creates a MixpanelAPI instance) -val provider = MixpanelProvider(context, "YOUR_PROJECT_TOKEN", MixpanelOptions()) -OpenFeatureAPI.setProvider(provider) +// 1. Create the provider +val options = MixpanelOptions.Builder() + .featureFlags() + .build() +val provider = MixpanelProvider(context, "YOUR_PROJECT_TOKEN", options) +// 2. Register the provider with OpenFeature +OpenFeatureAPI.setProviderAndWait(provider) + +// 3. Get a client and evaluate flags val client = OpenFeatureAPI.getClient() -val showNewUI = client.getBooleanValue("new-ui", false) +val showNewFeature = client.getBooleanValue("new-feature-flag", false) -// Access the underlying Mixpanel instance for identify/track -provider.mixpanel?.identify("user-123") +if (showNewFeature) { + // New feature is enabled! +} ``` ### Using an Existing MixpanelAPI Instance ```kotlin import com.mixpanel.android.mpmetrics.MixpanelAPI -import com.mixpanel.android.openfeature.MixpanelProvider -val mixpanel = MixpanelAPI.getInstance(context, "YOUR_PROJECT_TOKEN") +val mixpanel = MixpanelAPI.getInstance(context, "YOUR_PROJECT_TOKEN", false, options) +val provider = MixpanelProvider(mixpanel.getFlags()) + +OpenFeatureAPI.setProviderAndWait(provider) +``` + + +This provider does **not** call `mixpanel.identify()` or `mixpanel.track()`. If you need to update the logged-in user or use [Runtime Events](/docs/featureflags/runtime-events) for targeting, call these methods on the **same `MixpanelAPI` instance** whose `Flags` object was passed to the provider. + + +```kotlin +val provider = MixpanelProvider(context, "YOUR_PROJECT_TOKEN", options) + +// Use the same instance for identity and tracking +provider.mixpanel?.identify("user-123") +provider.mixpanel?.track("Purchase Completed") +``` + +## Usage + +### Flag Types and Evaluation Methods -val provider = MixpanelProvider(mixpanel.flags) -OpenFeatureAPI.setProvider(provider) +| Mixpanel Flag Type | Variant Values | OpenFeature Method | +|---|---|---| +| Feature Gate | `true` / `false` | `getBooleanValue()` | +| Experiment | boolean, string, number, or JSON object | `getBooleanValue()`, `getStringValue()`, `getIntegerValue()`, `getDoubleValue()`, or `getObjectValue()` | +| Dynamic Config | JSON object | `getObjectValue()` | +```kotlin val client = OpenFeatureAPI.getClient() -val showNewUI = client.getBooleanValue("new-ui", false) + +// Feature Gate +val isFeatureOn = client.getBooleanValue("new-checkout", false) + +// Experiment with string variants +val buttonColor = client.getStringValue("button-color-test", "blue") + +// Experiment with numeric variants +val maxItems = client.getIntegerValue("max-items", 10) +val threshold = client.getDoubleValue("score-threshold", 0.5) + +// Dynamic Config +val featureConfig = client.getObjectValue("homepage-layout", Value.Structure(mapOf( + "layout" to Value.String("grid"), + "itemsPerRow" to Value.Integer(3) +))) ``` -## Supported Flag Types +### Evaluation Context -| OpenFeature Method | Kotlin Type | -|---|---| -| `getBooleanValue` / `getBooleanDetails` | `Boolean` | -| `getStringValue` / `getStringDetails` | `String` | -| `getIntegerValue` / `getIntegerDetails` | `Int` | -| `getDoubleValue` / `getDoubleDetails` | `Double` | -| `getObjectValue` / `getObjectDetails` | `Value` | +Context must be set globally via `OpenFeatureAPI.setContext()`: -## Context and Identity +```kotlin +import dev.openfeature.kotlin.sdk.ImmutableContext +import dev.openfeature.kotlin.sdk.Value + +OpenFeatureAPI.setContext(ImmutableContext( + attributes = mutableMapOf( + "email" to Value.String("user@example.com"), + "plan" to Value.String("premium") + ) +)) +``` - -Context is set globally when registering the provider, not per-evaluation. Per-evaluation context passed to individual flag evaluations is ignored. + +Per-evaluation context (the optional `context` parameter on evaluation methods) is **not supported** by this provider. Context must be set globally via `OpenFeatureAPI.setContext()`, which triggers a re-fetch of flag values from Mixpanel. -The provider forwards the global OpenFeature context to the Mixpanel SDK via `setContext()` during initialization and whenever the global context changes. +### Runtime Properties -- **`targetingKey`** has no special meaning in this provider. It is treated as a regular context property. -- Identity should be managed through the Mixpanel SDK via `mixpanel.identify()` or `provider.mixpanel?.identify()`. +Pass `custom_properties` in the evaluation context for runtime targeting: -## Error Handling +```kotlin +OpenFeatureAPI.setContext(ImmutableContext( + attributes = mutableMapOf( + "custom_properties" to Value.Structure(mapOf( + "tier" to Value.String("enterprise"), + "seats" to Value.Integer(50), + )) + ) +)) +``` -The provider returns the default value on all errors, with an error code indicating the cause: + +Unlike some providers, `targetingKey` is not used as a special bucketing key. It is passed as another context property. Mixpanel's server-side configuration determines which properties are used for targeting and bucketing. + -| Error Code | Condition | -|---|---| -| `PROVIDER_NOT_READY` | The provider has not been initialized | -| `FLAG_NOT_FOUND` | The requested flag does not exist | -| `TYPE_MISMATCH` | The flag value does not match the requested type | +### Full Resolution Details + +```kotlin +val details = client.getBooleanDetails("my-feature", false) + +println(details.value) +println(details.variant) +println(details.reason) +println(details.errorCode) +``` -Use `getDetails()` methods instead of `getValue()` to inspect error codes: +### User Identity + +This provider does **not** call `mixpanel.identify()`. Manage identity through the same Mixpanel instance: ```kotlin -val details = client.getBooleanDetails("my-flag", false) -if (details.errorCode != null) { - Log.d("Flags", "Flag error: ${details.errorCode} - ${details.errorMessage}") -} +provider.mixpanel?.identify("user-123") ``` -## Lifecycle +## Error Handling + +| Error Code | When | +|---|---| +| `PROVIDER_NOT_READY` | Flags evaluated before the provider has finished initializing | +| `FLAG_NOT_FOUND` | The requested flag does not exist in Mixpanel | +| `TYPE_MISMATCH` | The flag value type does not match the requested type | + +## Troubleshooting -- **`shutdown()`** is a no-op. The Mixpanel SDK manages its own lifecycle. -- The reason code for successful evaluations is `STATIC`. +### Flags Always Return Default Values + +1. **Feature flags not enabled:** Ensure MixpanelOptions includes `.featureFlags()`. +2. **Provider not ready:** Use `setProviderAndWait` to ensure initialization. +3. **Network issues:** Check Logcat for failed requests. +4. **Flag not configured:** Verify the flag exists and is enabled. + +### Flags Not Updating After Context Change + +Update context and the provider will re-fetch flags: + +```kotlin +OpenFeatureAPI.setContext(ImmutableContext( + attributes = mutableMapOf("plan" to Value.String("premium")) +)) +``` diff --git a/pages/docs/tracking-methods/sdks/go/go-openfeature.mdx b/pages/docs/tracking-methods/sdks/go/go-openfeature.mdx index ddadcc7043..ba7da0ca38 100644 --- a/pages/docs/tracking-methods/sdks/go/go-openfeature.mdx +++ b/pages/docs/tracking-methods/sdks/go/go-openfeature.mdx @@ -4,28 +4,23 @@ import { Callout } from 'nextra/components' ## Overview -The Mixpanel OpenFeature provider allows you to use [Mixpanel Feature Flags](/docs/featureflags) through the standardized [OpenFeature](https://openfeature.dev/) API. This provider wraps the Mixpanel Go SDK's feature flags, letting you use the OpenFeature Go SDK with Mixpanel as the backend. +This guide covers using Mixpanel's [Feature Flags](/docs/featureflags) through the [OpenFeature](https://openfeature.dev/) standard with the Mixpanel Go OpenFeature provider. OpenFeature provides a vendor-agnostic API for feature flag evaluation, allowing you to switch between providers without changing your application code. -For the core Feature Flags SDK guide, see [Feature Flags (Go)](/docs/tracking-methods/sdks/go/go-flags). +For the native Mixpanel SDK approach, see the [Feature Flags (Go)](/docs/tracking-methods/sdks/go/go-flags) guide. ## Prerequisites -- You are on an Enterprise subscription plan -- You have the [Mixpanel Go SDK](/docs/tracking-methods/sdks/go) installed (minimum version `v2.0.0`) -- You have your Project Token from your [Mixpanel Project Settings](/docs/orgs-and-projects/managing-projects#find-your-project-tokens) +- Enterprise subscription plan with Feature Flags enabled +- Project Token from your [Mixpanel Project Settings](/docs/orgs-and-projects/managing-projects#find-your-project-tokens) ## Installation ```bash -go get github.com/mixpanel/mixpanel-go/v2/openfeature +go get github.com/mixpanel/mixpanel-go/openfeature go get github.com/open-feature/go-sdk ``` -## Usage - -The provider wraps either a `LocalFeatureFlagsProvider` or `RemoteFeatureFlagsProvider` from the Mixpanel SDK. - -### With Local Evaluation +## Quick Start ```go package main @@ -33,109 +28,151 @@ package main import ( "context" "fmt" - "time" + mixpanelopenfeature "github.com/mixpanel/mixpanel-go/openfeature" "github.com/mixpanel/mixpanel-go/v2/flags" - mpof "github.com/mixpanel/mixpanel-go/v2/openfeature" - "github.com/open-feature/go-sdk/openfeature" + of "github.com/open-feature/go-sdk/openfeature" ) func main() { - config := flags.DefaultLocalFlagsConfig() - config.PollingInterval = 60 * time.Second - - // Create a provider with local evaluation (automatically starts polling) - provider, err := mpof.NewProviderWithLocalConfig("YOUR_PROJECT_TOKEN", config) + // 1. Create the Mixpanel OpenFeature provider with local evaluation + provider, err := mixpanelopenfeature.NewProviderWithLocalConfig("YOUR_PROJECT_TOKEN", flags.LocalFlagsConfig{}) if err != nil { panic(err) } - defer provider.Shutdown() - openfeature.SetProvider(provider) + // 2. Register the provider with OpenFeature + of.SetProvider(provider) + client := of.NewClient("my-app") + + // 3. Evaluate flags + showNewFeature, _ := client.BooleanValue(context.Background(), "new-feature-flag", false, of.EvaluationContext{}) - client := openfeature.NewClient("my-app") - showNewUI, _ := client.BooleanValue(context.Background(), "new-ui", false, openfeature.EvaluationContext{}) - fmt.Println("Show new UI:", showNewUI) + if showNewFeature { + fmt.Println("New feature is enabled!") + } } ``` -### With Remote Evaluation +## Initialization + +### Local Evaluation (Recommended) + +Flag definitions are fetched from Mixpanel and evaluated locally. This is faster and works offline after the initial fetch. + + +Targeting by Mixpanel cohorts and sticky variants are not supported in Local Evaluation mode. + ```go -config := flags.DefaultRemoteFlagsConfig() +provider, err := mixpanelopenfeature.NewProviderWithLocalConfig("YOUR_PROJECT_TOKEN", flags.LocalFlagsConfig{}) +``` -provider, err := mpof.NewProviderWithRemoteConfig("YOUR_PROJECT_TOKEN", config) -if err != nil { - panic(err) -} +### Remote Evaluation -openfeature.SetProvider(provider) +Each flag evaluation makes a request to Mixpanel's servers. -client := openfeature.NewClient("my-app") -variant, _ := client.StringValue(context.Background(), "checkout-flow", "control", openfeature.EvaluationContext{}) +```go +provider, err := mixpanelopenfeature.NewProviderWithRemoteConfig("YOUR_PROJECT_TOKEN", flags.RemoteFlagsConfig{}) +``` + +### Using an Existing Mixpanel Instance + +```go +mp := mixpanel.NewApiClient("YOUR_TOKEN", mixpanel.WithLocalFlags(flags.LocalFlagsConfig{})) +mp.LocalFlags.StartPollingForDefinitions(context.Background()) + +provider, err := mixpanelopenfeature.NewProvider(mp.LocalFlags) ``` -### With an Existing Flags Provider +## Usage + +### Flag Types and Evaluation Methods -If you already have a configured `LocalFeatureFlagsProvider` or `RemoteFeatureFlagsProvider`, you can wrap it directly: +| Mixpanel Flag Type | Variant Values | OpenFeature Method | +|---|---|---| +| Feature Gate | `true` / `false` | `BooleanValue()` | +| Experiment | boolean, string, number, or JSON object | `BooleanValue()`, `StringValue()`, `FloatValue()`, `IntValue()`, or `ObjectValue()` | +| Dynamic Config | JSON object | `ObjectValue()` | ```go -import ( - mixpanel "github.com/mixpanel/mixpanel-go/v2" - mpof "github.com/mixpanel/mixpanel-go/v2/openfeature" -) +ctx := context.Background() +evalCtx := of.EvaluationContext{} -mp := mixpanel.NewClient("YOUR_PROJECT_TOKEN", - mixpanel.WithLocalFlagsConfig(mixpanel.LocalFlagsConfig{ - EnablePolling: true, - PollingInterval: 60 * time.Second, - }), -) +// Feature Gate +enabled, _ := client.BooleanValue(ctx, "new-checkout", false, evalCtx) + +// Experiment with string variants +buttonColor, _ := client.StringValue(ctx, "button-color-test", "blue", evalCtx) -mp.LocalFlags().StartPollingForDefinitions() -defer mp.LocalFlags().StopPollingForDefinitions() +// Numeric flags +threshold, _ := client.FloatValue(ctx, "score-threshold", 0.5, evalCtx) +maxItems, _ := client.IntValue(ctx, "max-items", 10, evalCtx) -provider := mpof.NewProvider(mp.LocalFlags()) -openfeature.SetProvider(provider) +// Dynamic Config +config, _ := client.ObjectValue(ctx, "homepage-layout", map[string]any{ + "layout": "grid", +}, evalCtx) ``` -## Supported Flag Types +### Evaluation Context -| OpenFeature Method | Go Type | -|---|---| -| `BooleanValue` / `BooleanValueDetails` | `bool` | -| `StringValue` / `StringValueDetails` | `string` | -| `IntValue` / `IntValueDetails` | `int64` | -| `FloatValue` / `FloatValueDetails` | `float64` | -| `ObjectValue` / `ObjectValueDetails` | `interface{}` | +```go +evalCtx := of.NewEvaluationContext("user-123", map[string]any{ + "email": "user@example.com", + "plan": "premium", +}) -## Context and Identity +enabled, _ := client.BooleanValue(ctx, "premium-feature", false, evalCtx) +``` -The evaluation context passed to each flag evaluation is forwarded to the Mixpanel flags provider. + +Unlike some providers, `targetingKey` is not used as a special bucketing key. It is passed as another context property. Mixpanel's server-side configuration determines which properties are used for targeting and bucketing. + -- **`targetingKey`** has no special meaning in this provider. It is treated as a regular context property. -- Identity should be managed through the Mixpanel SDK (e.g., setting `distinct_id` in the context). +### Full Resolution Details -## Error Handling +```go +details, _ := client.BooleanValueDetails(ctx, "my-feature", false, of.EvaluationContext{}) -The provider returns the default value on all errors, with a resolution error indicating the cause: +fmt.Println(details.Value) +fmt.Println(details.Variant) +fmt.Println(details.Reason) +fmt.Println(details.ErrorCode) +``` -| Error Code | Condition | -|---|---| -| `PROVIDER_NOT_READY` | The provider has not been initialized | -| `FLAG_NOT_FOUND` | The requested flag does not exist | -| `TYPE_MISMATCH` | The flag value does not match the requested type | +### Accessing the Underlying Mixpanel Client -Use `ValueDetails` methods to inspect error codes: +```go +provider, _ := mixpanelopenfeature.NewProviderWithLocalConfig("YOUR_TOKEN", flags.LocalFlagsConfig{}) + +provider.Mixpanel.Track(ctx, []*mixpanel.Event{ + {Name: "button_clicked", Properties: map[string]any{"color": "blue"}}, +}) +``` + +### Shutdown ```go -details, err := client.BooleanValueDetails(ctx, "my-flag", false, openfeature.EvaluationContext{}) -if details.ErrorCode != "" { - fmt.Printf("Flag error: %s - %s\n", details.ErrorCode, details.ErrorMessage) -} +provider.Shutdown() ``` -## Lifecycle +## Error Handling + +| Error Code | When | +|---|---| +| `PROVIDER_NOT_READY` | Flags evaluated before the local provider has finished fetching definitions | +| `FLAG_NOT_FOUND` | The requested flag does not exist in Mixpanel | +| `TYPE_MISMATCH` | The flag value type does not match the requested type | + +## Troubleshooting + +### Flags Always Return Default Values + +1. **Provider not ready:** Ensure polling has started and definitions have been received. +2. **Flag not configured:** Verify the flag exists and is enabled in your Mixpanel project. +3. **Network issues:** For remote evaluation, check that your application can reach Mixpanel's API. + +### Type Mismatch Errors -- **`Shutdown()`** stops background polling for local evaluation providers. Call it when your application exits. -- The reason code for successful evaluations is `STATIC`. +The flag's value type must match the evaluation method. `IntValue()` accepts whole-number `float64` values. `FloatValue()` accepts any numeric type. diff --git a/pages/docs/tracking-methods/sdks/java/java-openfeature.mdx b/pages/docs/tracking-methods/sdks/java/java-openfeature.mdx index ed1f6d0b2a..3e3918a355 100644 --- a/pages/docs/tracking-methods/sdks/java/java-openfeature.mdx +++ b/pages/docs/tracking-methods/sdks/java/java-openfeature.mdx @@ -1,28 +1,28 @@ -import { Callout } from 'nextra/components' +import { Callout } from "nextra/components"; # OpenFeature Provider (Java) ## Overview -The Mixpanel OpenFeature provider allows you to use [Mixpanel Feature Flags](/docs/featureflags) through the standardized [OpenFeature](https://openfeature.dev/) API. This provider wraps the Mixpanel Java SDK's feature flags, letting you use the OpenFeature Java SDK with Mixpanel as the backend. +This guide covers using Mixpanel's [Feature Flags](/docs/featureflags) through the [OpenFeature](https://openfeature.dev/) standard with the Mixpanel Java OpenFeature provider. OpenFeature provides a vendor-agnostic API for feature flag evaluation, allowing you to switch between providers without changing your application code. -For the core Feature Flags SDK guide, see [Feature Flags (Java)](/docs/tracking-methods/sdks/java/java-flags). +For the native Mixpanel SDK approach, see the [Feature Flags (Java)](/docs/tracking-methods/sdks/java/java-flags) guide. ## Prerequisites -- You are on an Enterprise subscription plan -- You have the [Mixpanel Java SDK](/docs/tracking-methods/sdks/java) installed (minimum version `v1.7.0`) -- You have your Project Token from your [Mixpanel Project Settings](/docs/orgs-and-projects/managing-projects#find-your-project-tokens) +- Enterprise subscription plan with Feature Flags enabled +- Java 8 or higher +- Project Token from your [Mixpanel Project Settings](/docs/orgs-and-projects/managing-projects#find-your-project-tokens) ## Installation -Add the OpenFeature provider and SDK to your Maven `pom.xml`: +### Maven ```xml com.mixpanel - mixpanel-java-openfeature-provider - 1.7.0 + mixpanel-java-openfeature + 0.1.0 dev.openfeature @@ -31,96 +31,156 @@ Add the OpenFeature provider and SDK to your Maven `pom.xml`: ``` -Or with Gradle: +### Gradle ```groovy -implementation 'com.mixpanel:mixpanel-java-openfeature-provider:1.7.0' +implementation 'com.mixpanel:mixpanel-java-openfeature:0.1.0' implementation 'dev.openfeature:sdk:1.20.1' ``` -## Usage - -The provider wraps a `BaseFlagsProvider` (either `LocalFlagsProvider` or `RemoteFlagsProvider`) from the Mixpanel SDK. - -### With Local Evaluation +## Quick Start ```java import com.mixpanel.openfeature.MixpanelProvider; -import dev.openfeature.sdk.*; - -// Create provider with local evaluation (automatically starts polling) +import com.mixpanel.mixpanelapi.featureflags.config.LocalFlagsConfig; +import dev.openfeature.sdk.OpenFeatureAPI; +import dev.openfeature.sdk.Client; + +// 1. Create and register the provider with local evaluation +MixpanelProvider provider = new MixpanelProvider( + "YOUR_PROJECT_TOKEN", + new LocalFlagsConfig("YOUR_PROJECT_TOKEN") +); OpenFeatureAPI api = OpenFeatureAPI.getInstance(); -api.setProvider(new MixpanelProvider("YOUR_PROJECT_TOKEN", localFlagsConfig)); +api.setProvider(provider); +// 2. Get a client and evaluate flags Client client = api.getClient(); -boolean showNewUI = client.getBooleanValue("new-ui", false); +boolean showNewFeature = client.getBooleanValue("new-feature-flag", false); + +if (showNewFeature) { + System.out.println("New feature is enabled!"); +} ``` -### With Remote Evaluation +## Initialization + +### Local Evaluation (Recommended) + + +Targeting by Mixpanel cohorts and sticky variants are not supported in Local Evaluation mode. + ```java -import com.mixpanel.openfeature.MixpanelProvider; +MixpanelProvider provider = new MixpanelProvider( + "YOUR_PROJECT_TOKEN", + new LocalFlagsConfig("YOUR_PROJECT_TOKEN") +); +``` -OpenFeatureAPI api = OpenFeatureAPI.getInstance(); -api.setProvider(new MixpanelProvider("YOUR_PROJECT_TOKEN", remoteFlagsConfig)); +### Remote Evaluation -Client client = api.getClient(); -String variant = client.getStringValue("checkout-flow", "control"); +```java +MixpanelProvider provider = new MixpanelProvider( + "YOUR_PROJECT_TOKEN", + new RemoteFlagsConfig("YOUR_PROJECT_TOKEN") +); +``` + +### Using an Existing MixpanelAPI Instance + +```java +MixpanelAPI mixpanel = new MixpanelAPI(new LocalFlagsConfig("YOUR_PROJECT_TOKEN")); +LocalFlagsProvider localFlags = mixpanel.getLocalFlags(); +localFlags.startPollingForDefinitions(); + +MixpanelProvider provider = new MixpanelProvider(localFlags); ``` -### With an Existing Flags Provider +## Usage -If you already have a configured `LocalFlagsProvider` or `RemoteFlagsProvider`, you can wrap it directly: +### Flag Types and Evaluation Methods + +| Mixpanel Flag Type | Variant Values | OpenFeature Method | +|---|---|---| +| Feature Gate | `true` / `false` | `getBooleanValue()` | +| Experiment | boolean, string, number, or JSON object | `getBooleanValue()`, `getStringValue()`, `getIntegerValue()`, `getDoubleValue()`, or `getObjectValue()` | +| Dynamic Config | JSON object | `getObjectValue()` | ```java -import com.mixpanel.mixpanelapi.featureflags.provider.LocalFlagsProvider; -import com.mixpanel.openfeature.MixpanelProvider; +Client client = api.getClient(); -LocalFlagsProvider flagsProvider = new LocalFlagsProvider("YOUR_PROJECT_TOKEN", config, sdkVersion, eventSender); +// Feature Gate +boolean isFeatureOn = client.getBooleanValue("new-checkout", false); -OpenFeatureAPI api = OpenFeatureAPI.getInstance(); -api.setProvider(new MixpanelProvider(flagsProvider)); +// Experiment with string variants +String buttonColor = client.getStringValue("button-color-test", "blue"); + +// Experiment with numeric variants +int maxItems = client.getIntegerValue("max-items", 10); +double threshold = client.getDoubleValue("score-threshold", 0.5); + +// Dynamic Config +Value featureConfig = client.getObjectValue("homepage-layout", new Value("default")); ``` -## Supported Flag Types +### Evaluation Context -| OpenFeature Method | Java Type | -|---|---| -| `getBooleanValue` / `getBooleanDetails` | `Boolean` | -| `getStringValue` / `getStringDetails` | `String` | -| `getIntegerValue` / `getIntegerDetails` | `Integer` (coerces from `Long`, `Double`) | -| `getDoubleValue` / `getDoubleDetails` | `Double` (coerces from `Integer`, `Long`) | -| `getObjectValue` / `getObjectDetails` | `Value` (wraps maps, lists, and primitives) | +```java +MutableContext context = new MutableContext(); +context.setTargetingKey("user-123"); +context.add("email", "user@example.com"); +context.add("plan", "premium"); +context.add("beta_tester", true); -Numeric types are coerced automatically. For example, a flag returning a `Long` value will work with `getIntegerValue()`. +boolean value = client.getBooleanValue("premium-feature", false, context); +``` -## Context and Identity + +Unlike some providers, `targetingKey` is not used as a special bucketing key. It is passed as another context property. Mixpanel's server-side configuration determines which properties are used for targeting and bucketing. + -The evaluation context passed to each flag evaluation is forwarded to the Mixpanel flags provider. +### Full Resolution Details -- **`targetingKey`** has no special meaning in this provider. It is treated as a regular context property. -- Identity should be managed through the Mixpanel SDK (e.g., setting `distinct_id` in the context). +```java +FlagEvaluationDetails details = client.getBooleanDetails("my-feature", false); -## Error Handling +System.out.println(details.getValue()); +System.out.println(details.getVariant()); +System.out.println(details.getReason()); +System.out.println(details.getErrorCode()); +``` -The provider returns the default value on all errors, with an error code indicating the cause: +### Accessing the Underlying MixpanelAPI -| Error Code | Condition | -|---|---| -| `PROVIDER_NOT_READY` | The provider has not been initialized | -| `FLAG_NOT_FOUND` | The requested flag does not exist | -| `TYPE_MISMATCH` | The flag value does not match the requested type | +```java +MixpanelAPI mixpanel = provider.getMixpanel(); +``` -Use `getDetails()` methods instead of `getValue()` to inspect error codes: +### Shutdown ```java -FlagEvaluationDetails details = client.getBooleanDetails("my-flag", false); -if (details.getErrorCode() != null) { - System.out.println("Flag error: " + details.getErrorCode() + " - " + details.getErrorMessage()); -} +provider.shutdown(); ``` -## Lifecycle +## Error Handling + +| Error Code | When | +|---|---| +| `PROVIDER_NOT_READY` | Flags evaluated before the local provider has finished loading definitions | +| `FLAG_NOT_FOUND` | The requested flag does not exist in Mixpanel | +| `TYPE_MISMATCH` | The flag value type does not match the requested type | + +## Troubleshooting + +### Flags Always Return Default Values + +1. **Provider not ready:** Flag definitions are polled asynchronously. Allow time for the initial fetch. +2. **Invalid project token:** Verify the token matches your Mixpanel project. +3. **Flag not configured:** Verify the flag exists and is enabled. + +### Type Mismatch Errors -- **`shutdown()`** delegates to the underlying flags provider to clean up resources (e.g., stop polling). -- The reason code for successful evaluations is `STATIC`. +1. Verify the flag's value type matches your evaluation method. +2. Use `getObjectValue()` for JSON objects. +3. Integer evaluation accepts `Long` and whole-number `Double` values within `Integer` bounds. Double evaluation accepts any numeric type. diff --git a/pages/docs/tracking-methods/sdks/javascript/_meta.ts b/pages/docs/tracking-methods/sdks/javascript/_meta.ts index e0a438591d..c79fefa139 100644 --- a/pages/docs/tracking-methods/sdks/javascript/_meta.ts +++ b/pages/docs/tracking-methods/sdks/javascript/_meta.ts @@ -1,4 +1,5 @@ export default { "javascript-replay": "Session Replay (Javascript)", "javascript-flags": "Feature Flags (Javascript)", + "javascript-openfeature": "OpenFeature Provider (Web)", } diff --git a/pages/docs/tracking-methods/sdks/javascript/javascript-openfeature.mdx b/pages/docs/tracking-methods/sdks/javascript/javascript-openfeature.mdx new file mode 100644 index 0000000000..1ace7c1db7 --- /dev/null +++ b/pages/docs/tracking-methods/sdks/javascript/javascript-openfeature.mdx @@ -0,0 +1,198 @@ +import { Callout } from 'nextra/components' + +# OpenFeature Provider (Web) + +## Overview + +This guide covers using Mixpanel's [Feature Flags](/docs/featureflags) through the [OpenFeature](https://openfeature.dev/) standard with the Mixpanel Web OpenFeature provider. OpenFeature provides a vendor-agnostic API for feature flag evaluation, allowing you to switch between providers without changing your application code. + +For the native Mixpanel SDK approach, see the [Feature Flags (Web)](/docs/tracking-methods/sdks/javascript/javascript-flags) guide. + +## Prerequisites + +- Enterprise subscription plan with Feature Flags enabled +- Project Token from your [Mixpanel Project Settings](/docs/orgs-and-projects/managing-projects#find-your-project-tokens) + +## Installation + +```bash +npm install @mixpanel/openfeature-web-provider @openfeature/web-sdk mixpanel-browser +``` + +## Quick Start + +```typescript +import mixpanel from 'mixpanel-browser'; +import { OpenFeature } from '@openfeature/web-sdk'; +import { MixpanelProvider } from '@mixpanel/openfeature-web-provider'; + +// 1. Initialize Mixpanel with feature flags +mixpanel.init('YOUR_PROJECT_TOKEN', { + flags: { + context: { plan: 'premium' } + } +}); + +// 2. Create and register the Mixpanel provider +const provider = new MixpanelProvider(mixpanel.flags); +await OpenFeature.setProviderAndWait(provider); + +// 3. Get a client and evaluate flags +const client = OpenFeature.getClient(); +const showNewFeature = client.getBooleanValue('new-feature-flag', false); + +if (showNewFeature) { + console.log('New feature is enabled!'); +} +``` + +## Usage + +### Flag Types and Evaluation Methods + +| Mixpanel Flag Type | Variant Values | OpenFeature Method | +|---|---|---| +| Feature Gate | `true` / `false` | `getBooleanValue()` | +| Experiment | boolean, string, number, or JSON object | `getBooleanValue()`, `getStringValue()`, `getNumberValue()`, or `getObjectValue()` | +| Dynamic Config | JSON object | `getObjectValue()` | + +```typescript +const client = OpenFeature.getClient(); + +// Feature Gate +const isFeatureOn = client.getBooleanValue('new-checkout', false); + +// Experiment with string variants +const buttonColor = client.getStringValue('button-color-test', 'blue'); + +// Experiment with number variants +const maxItems = client.getNumberValue('max-items', 10); + +// Dynamic Config +const featureConfig = client.getObjectValue('homepage-layout', { + layout: 'grid', + itemsPerRow: 3 +}); +``` + +### Evaluation Context + +Context must be set globally via `OpenFeature.setContext()`: + +```typescript +await OpenFeature.setContext({ + email: 'user@example.com', + plan: 'premium' +}); +``` + + +Per-evaluation context (the optional third argument to evaluation methods) is **not supported** by this provider. Context must be set globally via `OpenFeature.setContext()`, which triggers a re-fetch of flag values from Mixpanel. + + +### Runtime Properties + +Pass `custom_properties` in the evaluation context for runtime targeting: + +```typescript +await OpenFeature.setContext({ + custom_properties: { + tier: 'enterprise', + seats: 50, + industry: 'technology' + } +}); +``` + + +Unlike some providers, `targetingKey` is not used as a special bucketing key. It is passed as another context property. Mixpanel's server-side configuration determines which properties are used for targeting and bucketing. + + +### Full Resolution Details + +```typescript +const details = client.getBooleanDetails('my-feature', false); + +console.log(details.value); +console.log(details.variant); +console.log(details.reason); +console.log(details.errorCode); +``` + +### React Integration + +```tsx +import { OpenFeatureProvider, useBooleanFlagValue } from '@openfeature/react-sdk'; +import { OpenFeature } from '@openfeature/web-sdk'; +import mixpanel from 'mixpanel-browser'; +import { MixpanelProvider } from '@mixpanel/openfeature-web-provider'; + +// Initialize outside of component +mixpanel.init('YOUR_PROJECT_TOKEN', { + flags: { + context: { plan: 'premium' } + } +}); +const provider = new MixpanelProvider(mixpanel.flags); +OpenFeature.setProvider(provider); + +function App() { + return ( + + + + ); +} + +function MyComponent() { + const showBanner = useBooleanFlagValue('show-banner', false); + + return ( +
+ {showBanner && } +
+ ); +} +``` + +### User Identity + +This provider does **not** call `mixpanel.identify()`. Manage identity through Mixpanel directly: + +```typescript +mixpanel.identify('user-123'); +``` + +## Error Handling + +| Error Code | When | +|---|---| +| `PROVIDER_NOT_READY` | Flags evaluated before the provider has finished initializing | +| `FLAG_NOT_FOUND` | The requested flag does not exist in Mixpanel | +| `TYPE_MISMATCH` | The flag value type does not match the requested type | + +To avoid `PROVIDER_NOT_READY`, use `setProviderAndWait`: + +```typescript +await OpenFeature.setProviderAndWait(provider); +``` + +## Troubleshooting + +### Flags Always Return Default Values + +1. **Feature flags not enabled:** Ensure Mixpanel was initialized with `flags` enabled: + ```typescript + mixpanel.init('YOUR_TOKEN', { flags: { context: { plan: 'premium' } } }); + ``` +2. **Provider not ready:** Use `setProviderAndWait` to ensure initialization. +3. **Network issues:** Check the browser console for failed requests. +4. **Flag not configured:** Verify the flag exists and is enabled. + +### Flags Not Updating After Context Change + +Update context and the provider will re-fetch flags: + +```typescript +await OpenFeature.setContext({ plan: 'premium' }); +``` diff --git a/pages/docs/tracking-methods/sdks/nodejs/nodejs-openfeature.mdx b/pages/docs/tracking-methods/sdks/nodejs/nodejs-openfeature.mdx index 5362485427..ccf6b0e58c 100644 --- a/pages/docs/tracking-methods/sdks/nodejs/nodejs-openfeature.mdx +++ b/pages/docs/tracking-methods/sdks/nodejs/nodejs-openfeature.mdx @@ -4,118 +4,164 @@ import { Callout } from 'nextra/components' ## Overview -The Mixpanel OpenFeature provider allows you to use [Mixpanel Feature Flags](/docs/featureflags) through the standardized [OpenFeature](https://openfeature.dev/) API. This provider wraps the Mixpanel Node.js SDK's feature flags, letting you use the OpenFeature Server SDK with Mixpanel as the backend. +This guide covers using Mixpanel's [Feature Flags](/docs/featureflags) through the [OpenFeature](https://openfeature.dev/) standard with the Mixpanel Node.js OpenFeature provider. OpenFeature provides a vendor-agnostic API for feature flag evaluation, allowing you to switch between providers without changing your application code. -For the core Feature Flags SDK guide, see [Feature Flags (Node.js)](/docs/tracking-methods/sdks/nodejs/nodejs-flags). +For the native Mixpanel SDK approach, see the [Feature Flags (Node.js)](/docs/tracking-methods/sdks/nodejs/nodejs-flags) guide. ## Prerequisites -- You are on an Enterprise subscription plan -- You have the [Mixpanel Node.js SDK](/docs/tracking-methods/sdks/nodejs) installed (minimum version `v0.20.0`) -- You have your Project Token from your [Mixpanel Project Settings](/docs/orgs-and-projects/managing-projects#find-your-project-tokens) +- Enterprise subscription plan with Feature Flags enabled +- Node.js 10 or higher +- Project Token from your [Mixpanel Project Settings](/docs/orgs-and-projects/managing-projects#find-your-project-tokens) ## Installation ```bash -npm install @mixpanel/openfeature-server-provider @openfeature/server-sdk +npm install @mixpanel/openfeature-server-provider @openfeature/server-sdk mixpanel ``` -## Usage - -The provider wraps either a `LocalFeatureFlagsProvider` or `RemoteFeatureFlagsProvider` from the Mixpanel SDK. +## Quick Start -### With Local Evaluation +```typescript +import { OpenFeature } from "@openfeature/server-sdk"; +import { MixpanelProvider } from "@mixpanel/openfeature-server-provider"; -```javascript -const { OpenFeature } = require('@openfeature/server-sdk'); -const { MixpanelProvider } = require('@mixpanel/openfeature-server-provider'); +// 1. Create and register the provider with local evaluation +const provider = MixpanelProvider.createLocal("YOUR_PROJECT_TOKEN"); +await OpenFeature.setProviderAndWait(provider); -// Create provider with local evaluation (automatically starts polling) -const provider = MixpanelProvider.createLocal('YOUR_PROJECT_TOKEN', { - enable_polling: true, - polling_interval_in_seconds: 60 +// 2. Get a client and evaluate flags +const client = OpenFeature.getClient(); +const showNewFeature = await client.getBooleanValue("new-feature-flag", false, { + distinct_id: "user-123", }); -await OpenFeature.setProviderAndWait(provider); -OpenFeature.setContext({ distinct_id: 'user-123', plan: 'premium' }); +if (showNewFeature) { + console.log("New feature is enabled!"); +} +``` -const client = OpenFeature.getClient(); -const showNewUI = await client.getBooleanValue('new-ui', false); +## Initialization + +### Local Evaluation (Recommended) + + +Targeting by Mixpanel cohorts and sticky variants are not supported in Local Evaluation mode. + + +```typescript +const provider = MixpanelProvider.createLocal("YOUR_PROJECT_TOKEN"); ``` -### With Remote Evaluation +### Remote Evaluation + +```typescript +const provider = MixpanelProvider.createRemote("YOUR_PROJECT_TOKEN"); +``` + +### Using an Existing Mixpanel Instance + +```typescript +import Mixpanel from "mixpanel"; -```javascript -const provider = MixpanelProvider.createRemote('YOUR_PROJECT_TOKEN', { - api_host: 'api.mixpanel.com' +const mixpanel = Mixpanel.init("YOUR_PROJECT_TOKEN", { + local_flags_config: {}, }); +const localFlags = mixpanel.local_flags!; +localFlags.startPollingForDefinitions(); -await OpenFeature.setProviderAndWait(provider); -OpenFeature.setContext({ distinct_id: 'user-123' }); +const provider = new MixpanelProvider(localFlags); +``` + +## Usage + +### Flag Types and Evaluation Methods +| Mixpanel Flag Type | Variant Values | OpenFeature Method | +|---|---|---| +| Feature Gate | `true` / `false` | `getBooleanValue()` | +| Experiment | boolean, string, number, or JSON object | `getBooleanValue()`, `getStringValue()`, `getNumberValue()`, or `getObjectValue()` | +| Dynamic Config | JSON object | `getObjectValue()` | + +```typescript const client = OpenFeature.getClient(); -const variant = await client.getStringValue('checkout-flow', 'control'); -``` +const context = { distinct_id: "user-123" }; -### With an Existing Flags Provider +// Feature Gate +const isFeatureOn = await client.getBooleanValue("new-checkout", false, context); -If you already have a configured Mixpanel instance, you can wrap its flags provider directly: +// Experiment with string variants +const buttonColor = await client.getStringValue("button-color-test", "blue", context); -```javascript -const Mixpanel = require('mixpanel'); -const { MixpanelProvider } = require('@mixpanel/openfeature-server-provider'); +// Experiment with number variants +const maxItems = await client.getNumberValue("max-items", 10, context); -const mixpanel = Mixpanel.init('YOUR_PROJECT_TOKEN', { - local_flags_config: { - enable_polling: true, - polling_interval_in_seconds: 60 - } -}); +// Dynamic Config +const featureConfig = await client.getObjectValue("homepage-layout", {}, context); +``` -await mixpanel.local_flags.startPollingForDefinitions(); +### Evaluation Context -const provider = new MixpanelProvider(mixpanel.local_flags); -await OpenFeature.setProviderAndWait(provider); +```typescript +// Global context +OpenFeature.setContext({ environment: "production" }); + +// Per-evaluation context (merged with and overrides global context) +const value = await client.getBooleanValue("premium-feature", false, { + distinct_id: "user-123", + email: "user@example.com", + plan: "premium", +}); ``` -## Supported Flag Types + +Unlike some providers, `targetingKey` is not used as a special bucketing key. It is passed as another context property. Mixpanel's server-side configuration determines which properties are used for targeting and bucketing. + -| OpenFeature Method | JavaScript Type | -|---|---| -| `getBooleanValue` / `getBooleanDetails` | `boolean` | -| `getStringValue` / `getStringDetails` | `string` | -| `getNumberValue` / `getNumberDetails` | `number` | -| `getObjectValue` / `getObjectDetails` | `object` (non-null, non-array) | +### Full Resolution Details + +```typescript +const details = await client.getBooleanDetails("my-feature", false, { + distinct_id: "user-123", +}); -## Context and Identity +console.log(details.value); +console.log(details.variant); +console.log(details.reason); +console.log(details.errorCode); +``` -The provider merges the base context (set via `OpenFeature.setContext()` during initialization) with any per-evaluation context. Per-evaluation context values override the base context for overlapping keys. +### Accessing the Underlying Mixpanel Instance -- **`targetingKey`** has no special meaning in this provider. It is treated as a regular context property. -- Identity should be managed through the Mixpanel SDK (e.g., setting `distinct_id` in the context). -- To update the base context after initialization, call `OpenFeature.setContext()`. +```typescript +const mixpanel = provider.mixpanel; +if (mixpanel) { + mixpanel.track("button_clicked", { distinct_id: "user-123" }); +} +``` -## Error Handling +### Shutdown + +```typescript +await OpenFeature.close(); +``` -The provider returns the default value on all errors, with an error code indicating the cause: +## Error Handling -| Error Code | Condition | +| Error Code | When | |---|---| -| `PROVIDER_NOT_READY` | The provider has not been initialized | -| `FLAG_NOT_FOUND` | The requested flag does not exist | -| `TYPE_MISMATCH` | The flag value does not match the requested type | +| `PROVIDER_NOT_READY` | Flags evaluated before the local provider has finished loading definitions | +| `FLAG_NOT_FOUND` | The requested flag does not exist in Mixpanel | +| `TYPE_MISMATCH` | The flag value type does not match the requested type | -Use `getDetails()` methods instead of `getValue()` to inspect error codes: +## Troubleshooting -```javascript -const details = await client.getBooleanDetails('my-flag', false); -if (details.errorCode) { - console.log(`Flag error: ${details.errorCode} - ${details.errorMessage}`); -} -``` +### Flags Always Return Default Values + +1. **Provider not ready:** Use `setProviderAndWait()` to ensure flags are ready before evaluation. +2. **Invalid project token:** Verify the token matches your Mixpanel project. +3. **Flag not configured:** Verify the flag exists and is enabled. -## Lifecycle +### Type Mismatch Errors -- **`initialize()`** stores the global context for all subsequent evaluations. -- **`onClose()`** delegates to the underlying flags provider's `shutdown()` method if available. -- The reason code for successful evaluations is `STATIC`. +Verify the flag's value type matches your evaluation method. Use `getObjectValue()` for JSON objects. diff --git a/pages/docs/tracking-methods/sdks/python/python-openfeature.mdx b/pages/docs/tracking-methods/sdks/python/python-openfeature.mdx index 28cc55be31..ffd33bd04d 100644 --- a/pages/docs/tracking-methods/sdks/python/python-openfeature.mdx +++ b/pages/docs/tracking-methods/sdks/python/python-openfeature.mdx @@ -4,117 +4,190 @@ import { Callout } from 'nextra/components' ## Overview -The Mixpanel OpenFeature provider allows you to use [Mixpanel Feature Flags](/docs/featureflags) through the standardized [OpenFeature](https://openfeature.dev/) API. This provider wraps the Mixpanel Python SDK's feature flags, letting you use the OpenFeature Python SDK with Mixpanel as the backend. +This guide covers using Mixpanel's [Feature Flags](/docs/featureflags) through the [OpenFeature](https://openfeature.dev/) standard with the Mixpanel Python OpenFeature provider. OpenFeature provides a vendor-agnostic API for feature flag evaluation, allowing you to switch between providers without changing your application code. -For the core Feature Flags SDK guide, see [Feature Flags (Python)](/docs/tracking-methods/sdks/python/python-flags). +For the native Mixpanel SDK approach, see the [Feature Flags (Python)](/docs/tracking-methods/sdks/python/python-flags) guide. ## Prerequisites -- You are on an Enterprise subscription plan -- You have the [Mixpanel Python SDK](/docs/tracking-methods/sdks/python) installed -- You have your Project Token from your [Mixpanel Project Settings](/docs/orgs-and-projects/managing-projects#find-your-project-tokens) +- Enterprise subscription plan with Feature Flags enabled +- Python 3.9 or higher +- Project Token from your [Mixpanel Project Settings](/docs/orgs-and-projects/managing-projects#find-your-project-tokens) ## Installation ```bash -pip install mixpanel-openfeature-provider openfeature-sdk +pip install mixpanel-openfeature openfeature-sdk ``` -## Usage - -The provider wraps either a local or remote flags provider from the Mixpanel SDK. - -### With Local Evaluation +## Quick Start ```python -from openfeature import api from mixpanel_openfeature import MixpanelProvider from mixpanel.flags.types import LocalFlagsConfig +from openfeature import api -config = LocalFlagsConfig( - enable_polling=True, - polling_interval_in_seconds=60 +# 1. Create and register the provider with local evaluation +provider = MixpanelProvider.from_local_config( + "YOUR_PROJECT_TOKEN", + LocalFlagsConfig(token="YOUR_PROJECT_TOKEN"), ) - -# Create provider with local evaluation (automatically starts polling) -provider = MixpanelProvider.from_local_config("YOUR_PROJECT_TOKEN", config) api.set_provider(provider) +# 2. Get a client and evaluate flags client = api.get_client() -show_new_ui = client.get_boolean_value("new-ui", False) +show_new_feature = client.get_boolean_value("new-feature-flag", False) + +if show_new_feature: + print("New feature is enabled!") ``` -### With Remote Evaluation +## Initialization + +The provider supports three initialization methods depending on your evaluation strategy. + +### Local Evaluation (Recommended) + +Evaluates flags locally using cached flag definitions polled from Mixpanel. This minimizes latency since there is no network call at evaluation time. + + +Targeting by Mixpanel cohorts and sticky variants are not supported in Local Evaluation mode. + ```python -from mixpanel.flags.types import RemoteFlagsConfig +from mixpanel_openfeature import MixpanelProvider +from mixpanel.flags.types import LocalFlagsConfig -config = RemoteFlagsConfig( - api_host="api.mixpanel.com" +provider = MixpanelProvider.from_local_config( + "YOUR_PROJECT_TOKEN", + LocalFlagsConfig(token="YOUR_PROJECT_TOKEN"), ) +``` -provider = MixpanelProvider.from_remote_config("YOUR_PROJECT_TOKEN", config) -api.set_provider(provider) +### Remote Evaluation -client = api.get_client() -variant = client.get_string_value("checkout-flow", "control") +Evaluates flags by making a request to Mixpanel's servers for each evaluation. Use this when you need real-time flag values or cohort-based targeting. + +```python +from mixpanel_openfeature import MixpanelProvider +from mixpanel.flags.types import RemoteFlagsConfig + +provider = MixpanelProvider.from_remote_config( + "YOUR_PROJECT_TOKEN", + RemoteFlagsConfig(token="YOUR_PROJECT_TOKEN"), +) ``` -### With an Existing Flags Provider +### Using an Existing Mixpanel Instance -If you already have a configured Mixpanel instance, you can wrap its flags provider directly: +If your application already has a `Mixpanel` instance configured for tracking, you can wrap its flags provider: ```python from mixpanel import Mixpanel +from mixpanel.flags.types import LocalFlagsConfig from mixpanel_openfeature import MixpanelProvider -mp = Mixpanel("YOUR_PROJECT_TOKEN", local_flags_config={ - "enable_polling": True, - "polling_interval_in_seconds": 60 -}) - +mp = Mixpanel("YOUR_PROJECT_TOKEN", local_flags_config=LocalFlagsConfig(token="YOUR_PROJECT_TOKEN")) mp.local_flags.start_polling_for_definitions() provider = MixpanelProvider(mp.local_flags) -api.set_provider(provider) ``` -## Supported Flag Types +## Usage -| OpenFeature Method | Python Type | -|---|---| -| `get_boolean_value` / `get_boolean_details` | `bool` | -| `get_string_value` / `get_string_details` | `str` | -| `get_integer_value` / `get_integer_details` | `int` | -| `get_float_value` / `get_float_details` | `float` | -| `get_object_value` / `get_object_details` | `dict` | +### Flag Types and Evaluation Methods -## Context and Identity +| Mixpanel Flag Type | Variant Values | OpenFeature Method | +|---|---|---| +| Feature Gate | `True` / `False` | `get_boolean_value()` | +| Experiment | boolean, string, number, or JSON object | `get_boolean_value()`, `get_string_value()`, `get_integer_value()`, `get_float_value()`, or `get_object_value()` | +| Dynamic Config | JSON object | `get_object_value()` | -The evaluation context passed to each flag evaluation is forwarded to the Mixpanel flags provider. +```python +client = api.get_client() -- **`targeting_key`** has no special meaning in this provider. It is treated as a regular context property. -- Identity should be managed through the Mixpanel SDK (e.g., setting `distinct_id` in the context). +# Feature Gate +is_feature_on = client.get_boolean_value("new-checkout", False) -## Error Handling +# Experiment with string variants +button_color = client.get_string_value("button-color-test", "blue") -The provider returns the default value on all errors, with an error code indicating the cause: +# Experiment with numeric variants +max_items = client.get_integer_value("max-items", 10) +threshold = client.get_float_value("score-threshold", 0.5) -| Error Code | Condition | -|---|---| -| `PROVIDER_NOT_READY` | The provider has not been initialized | -| `FLAG_NOT_FOUND` | The requested flag does not exist | -| `TYPE_MISMATCH` | The flag value does not match the requested type | +# Dynamic Config +feature_config = client.get_object_value("homepage-layout", {"layout": "default"}) +``` + +### Evaluation Context -Use `get_details()` methods instead of `get_value()` to inspect error codes: +Pass context to provide user attributes for targeting: ```python -details = client.get_boolean_details("my-flag", False) -if details.error_code: - print(f"Flag error: {details.error_code} - {details.error_message}") +from openfeature.evaluation_context import EvaluationContext + +context = EvaluationContext( + targeting_key="user-123", + attributes={ + "email": "user@example.com", + "plan": "premium", + "beta_tester": True, + }, +) + +value = client.get_boolean_value("premium-feature", False, context) ``` -## Lifecycle + +Unlike some providers, `targetingKey` is not used as a special bucketing key. It is passed as another context property. Mixpanel's server-side configuration determines which properties are used for targeting and bucketing. + + +### Full Resolution Details + +```python +details = client.get_boolean_details("my-feature", False) + +print(details.value) # The resolved value +print(details.variant) # The variant key from Mixpanel +print(details.reason) # Why this value was returned +print(details.error_code) # Error code if evaluation failed +``` + +### Accessing the Underlying Mixpanel Instance + +When using `from_local_config` or `from_remote_config`, you can access the Mixpanel instance for tracking: + +```python +mp = provider.mixpanel +``` + +### Shutdown + +```python +provider.shutdown() +``` + +## Error Handling + +The provider uses OpenFeature's standard error codes: + +| Error Code | When | +|---|---| +| `PROVIDER_NOT_READY` | Flags evaluated before local provider has finished loading definitions | +| `FLAG_NOT_FOUND` | The requested flag does not exist in Mixpanel | +| `TYPE_MISMATCH` | The flag value type does not match the requested type | + +## Troubleshooting + +### Flags Always Return Default Values + +1. **Provider not ready (local evaluation):** Flag definitions are polled asynchronously. Allow time for the initial fetch to complete. +2. **Invalid project token:** Verify the token matches your Mixpanel project. +3. **Flag not configured:** Verify the flag exists and is enabled in your Mixpanel project. + +### Type Mismatch Errors -- **`shutdown()`** delegates to the underlying flags provider to clean up resources (e.g., stop polling). -- The reason code for successful evaluations is `STATIC`. +1. Verify the flag's value type in Mixpanel matches your evaluation method (e.g., string `"true"` requires `get_string_value()`, not `get_boolean_value()`). +2. Use `get_object_value()` for JSON objects. +3. Integer evaluation accepts whole-number `float` values. Float evaluation accepts any numeric type. diff --git a/pages/docs/tracking-methods/sdks/ruby/ruby-openfeature.mdx b/pages/docs/tracking-methods/sdks/ruby/ruby-openfeature.mdx index afe0c3462e..2022b7da0c 100644 --- a/pages/docs/tracking-methods/sdks/ruby/ruby-openfeature.mdx +++ b/pages/docs/tracking-methods/sdks/ruby/ruby-openfeature.mdx @@ -4,123 +4,200 @@ import { Callout } from 'nextra/components' ## Overview -The Mixpanel OpenFeature provider allows you to use [Mixpanel Feature Flags](/docs/featureflags) through the standardized [OpenFeature](https://openfeature.dev/) API. This provider wraps the Mixpanel Ruby SDK's feature flags, letting you use the OpenFeature Ruby SDK with Mixpanel as the backend. +This guide covers using Mixpanel's [Feature Flags](/docs/featureflags) through the [OpenFeature](https://openfeature.dev/) standard with the Mixpanel Ruby OpenFeature provider. OpenFeature provides a vendor-agnostic API for feature flag evaluation, allowing you to switch between providers without changing your application code. -For the core Feature Flags SDK guide, see [Feature Flags (Ruby)](/docs/tracking-methods/sdks/ruby/ruby-flags). +For the native Mixpanel SDK approach, see the [Feature Flags (Ruby)](/docs/tracking-methods/sdks/ruby/ruby-flags) guide. ## Prerequisites -- You are on an Enterprise subscription plan -- You have the [Mixpanel Ruby SDK](/docs/tracking-methods/sdks/ruby) installed -- You have your Project Token from your [Mixpanel Project Settings](/docs/orgs-and-projects/managing-projects#find-your-project-tokens) +- Enterprise subscription plan with Feature Flags enabled +- Ruby 3.1.0 or higher +- Project Token from your [Mixpanel Project Settings](/docs/orgs-and-projects/managing-projects#find-your-project-tokens) ## Installation -```bash -gem install mixpanel-ruby-openfeature openfeature-sdk -``` - -Or add to your `Gemfile`: +Add to your `Gemfile`: ```ruby gem 'mixpanel-ruby-openfeature' gem 'openfeature-sdk' +gem 'mixpanel-ruby' ``` -## Usage +Then run: -The provider wraps either a local or remote flags provider from the Mixpanel SDK. +```bash +bundle install +``` -### With Local Evaluation +## Quick Start ```ruby -require 'openfeature/sdk' +require 'mixpanel-ruby' require 'mixpanel/openfeature' -# Create provider with local evaluation (automatically starts polling) -provider = Mixpanel::OpenFeature::Provider.from_local('YOUR_PROJECT_TOKEN', { - enable_polling: true, - polling_interval_in_seconds: 60 -}) +# 1. Create the provider with local evaluation +provider = Mixpanel::OpenFeature::Provider.from_local( + 'YOUR_PROJECT_TOKEN', + { poll_interval: 300 } +) +# 2. Register the provider with OpenFeature OpenFeature::SDK.configure do |config| config.set_provider(provider) end +# 3. Get a client and evaluate flags client = OpenFeature::SDK.build_client -show_new_ui = client.fetch_boolean_value(flag_key: 'new-ui', default_value: false) + +show_new_feature = client.fetch_boolean_value(flag_key: 'new-feature-flag', default_value: false) + +if show_new_feature + puts 'New feature is enabled!' +end ``` -### With Remote Evaluation +## Initialization -```ruby -provider = Mixpanel::OpenFeature::Provider.from_remote('YOUR_PROJECT_TOKEN', { - api_host: 'api.mixpanel.com' -}) +### Local Evaluation (Recommended) -OpenFeature::SDK.configure do |config| - config.set_provider(provider) -end +Downloads flag definitions and evaluates them locally with no per-evaluation network requests. -client = OpenFeature::SDK.build_client -variant = client.fetch_string_value(flag_key: 'checkout-flow', default_value: 'control') + +Targeting by Mixpanel cohorts and sticky variants are not supported in Local Evaluation mode. + + +```ruby +provider = Mixpanel::OpenFeature::Provider.from_local( + 'YOUR_PROJECT_TOKEN', + { poll_interval: 300 } +) ``` -### With an Existing Flags Provider +### Remote Evaluation -If you already have a configured Tracker instance, you can wrap its flags provider directly: +Sends each flag check to Mixpanel's servers. ```ruby -require 'mixpanel' -require 'mixpanel/openfeature' +provider = Mixpanel::OpenFeature::Provider.from_remote( + 'YOUR_PROJECT_TOKEN', + {} +) +``` -tracker = Mixpanel::Tracker.new('YOUR_PROJECT_TOKEN', local_flags_config: { - enable_polling: true, - polling_interval_in_seconds: 60 -}) +### Using an Existing Tracker -tracker.local_flags.start_polling_for_definitions +```ruby +tracker = Mixpanel::Tracker.new('YOUR_PROJECT_TOKEN', nil, local_flags_config: { poll_interval: 300 }) +tracker.local_flags.start_polling_for_definitions! provider = Mixpanel::OpenFeature::Provider.new(tracker.local_flags) ``` -## Supported Flag Types +## Usage -| OpenFeature Method | Ruby Type | -|---|---| -| `fetch_boolean_value` / `fetch_boolean_details` | `TrueClass` / `FalseClass` | -| `fetch_string_value` / `fetch_string_details` | `String` | -| `fetch_integer_value` / `fetch_integer_details` | `Integer` | -| `fetch_float_value` / `fetch_float_details` | `Float` | -| `fetch_object_value` / `fetch_object_details` | `Hash` | +### Flag Types and Evaluation Methods -## Context and Identity +| Mixpanel Flag Type | Variant Values | OpenFeature Method | +|---|---|---| +| Feature Gate | `true` / `false` | `fetch_boolean_value` | +| Experiment | boolean, string, number, or JSON object | `fetch_boolean_value`, `fetch_string_value`, `fetch_number_value`, or `fetch_object_value` | +| Dynamic Config | JSON object | `fetch_object_value` | -The evaluation context passed to each flag evaluation is forwarded to the Mixpanel flags provider. +```ruby +client = OpenFeature::SDK.build_client -- **`targeting_key`** has no special meaning in this provider. It is treated as a regular context property. -- Identity should be managed through the Mixpanel SDK (e.g., setting `distinct_id` in the context). +# Feature Gate +is_feature_on = client.fetch_boolean_value(flag_key: 'new-checkout', default_value: false) -## Error Handling +# Experiment with string variants +button_color = client.fetch_string_value(flag_key: 'button-color-test', default_value: 'blue') -The provider returns the default value on all errors, with an error code indicating the cause: +# Experiment with number variants +max_items = client.fetch_number_value(flag_key: 'max-items', default_value: 10) -| Error Code | Condition | -|---|---| -| `PROVIDER_NOT_READY` | The provider has not been initialized | -| `FLAG_NOT_FOUND` | The requested flag does not exist | -| `TYPE_MISMATCH` | The flag value does not match the requested type | +# Dynamic Config +feature_config = client.fetch_object_value( + flag_key: 'homepage-layout', + default_value: { 'layout' => 'grid', 'items_per_row' => 3 } +) +``` -Use `fetch_details` methods to inspect error codes: +### Evaluation Context ```ruby -details = client.fetch_boolean_details(flag_key: 'my-flag', default_value: false) -if details.error_code - puts "Flag error: #{details.error_code} - #{details.error_message}" +context = OpenFeature::SDK::EvaluationContext.new( + targeting_key: 'user-123', + email: 'user@example.com', + plan: 'premium' +) + +value = client.fetch_boolean_value( + flag_key: 'premium-feature', + default_value: false, + evaluation_context: context +) +``` + + +Unlike some providers, `targetingKey` is not used as a special bucketing key. It is passed as another context property. Mixpanel's server-side configuration determines which properties are used for targeting and bucketing. + + +### Full Resolution Details + +```ruby +details = client.fetch_boolean_details(flag_key: 'my-feature', default_value: false) + +puts details.value +puts details.variant +puts details.reason +puts details.error_code +``` + +### Accessing the Mixpanel Tracker + +```ruby +provider.mixpanel.track('user-123', 'Page View', { 'page' => '/home' }) +``` + +### Rails Integration + +```ruby +# config/initializers/openfeature.rb +provider = Mixpanel::OpenFeature::Provider.from_local( + ENV['MIXPANEL_TOKEN'], + { poll_interval: 300 } +) + +OpenFeature::SDK.configure do |config| + config.set_provider(provider) end + +at_exit { provider.shutdown } ``` -## Lifecycle +### Shutdown + +```ruby +provider.shutdown +``` + +## Error Handling + +| Error Code | When | +|---|---| +| `PROVIDER_NOT_READY` | Flags evaluated before the provider has finished initializing | +| `FLAG_NOT_FOUND` | The requested flag does not exist in Mixpanel | +| `TYPE_MISMATCH` | The flag value type does not match the requested type | + +## Troubleshooting + +### Flags Always Return Default Values + +1. **Provider not ready:** Flag definitions are fetched asynchronously. Allow time for the initial fetch. +2. **Invalid project token:** Verify the token matches your Mixpanel project. +3. **Flag not configured:** Verify the flag exists and is enabled. + +### Type Mismatch Errors -- **`shutdown`** delegates to the underlying flags provider to clean up resources if supported. -- The reason code for successful evaluations is `STATIC`. +Verify the flag's value type matches your evaluation method. Use `fetch_object_value` for JSON objects. diff --git a/pages/docs/tracking-methods/sdks/swift/swift-openfeature.mdx b/pages/docs/tracking-methods/sdks/swift/swift-openfeature.mdx index 2d892855b2..372bd44b80 100644 --- a/pages/docs/tracking-methods/sdks/swift/swift-openfeature.mdx +++ b/pages/docs/tracking-methods/sdks/swift/swift-openfeature.mdx @@ -4,114 +4,189 @@ import { Callout } from 'nextra/components' ## Overview -The Mixpanel OpenFeature provider allows you to use [Mixpanel Feature Flags](/docs/featureflags) through the standardized [OpenFeature](https://openfeature.dev/) API. This provider wraps the Mixpanel Swift SDK's `MixpanelFlags` protocol, letting you use the OpenFeature Swift SDK with Mixpanel as the backend. +This guide covers using Mixpanel's [Feature Flags](/docs/featureflags) through the [OpenFeature](https://openfeature.dev/) standard with the Mixpanel Swift OpenFeature provider. OpenFeature provides a vendor-agnostic API for feature flag evaluation, allowing you to switch between providers without changing your application code. -For the core Feature Flags SDK guide, see [Feature Flags (Swift)](/docs/tracking-methods/sdks/swift/swift-flags). +For the native Mixpanel SDK approach, see the [Feature Flags (Swift)](/docs/tracking-methods/sdks/swift/swift-flags) guide. ## Prerequisites -- You are on an Enterprise subscription plan -- You have the [Mixpanel Swift SDK](/docs/tracking-methods/sdks/swift) installed (minimum version `6.2.0`) -- You have your Project Token from your [Mixpanel Project Settings](/docs/orgs-and-projects/managing-projects#find-your-project-tokens) +- Enterprise subscription plan with Feature Flags enabled +- iOS 14.0+ / tvOS 14.0+ / macOS 11.0+ / watchOS 7.0+ +- Swift 5.5+ +- Project Token from your [Mixpanel Project Settings](/docs/orgs-and-projects/managing-projects#find-your-project-tokens) ## Installation -### Swift Package Manager - -Add the OpenFeature provider package to your `Package.swift`: +Add the following to your `Package.swift`: ```swift dependencies: [ .package(url: "https://github.com/mixpanel/mixpanel-swift-openfeature", from: "0.1.0"), - .package(url: "https://github.com/open-feature/swift-sdk", from: "0.5.0"), ] ``` -## Usage +Then add `MixpanelOpenFeature` as a dependency of your target: -The provider wraps `MixpanelFlags` from the Mixpanel Swift SDK. +```swift +.target( + name: "YourTarget", + dependencies: [ + .product(name: "MixpanelOpenFeature", package: "mixpanel-swift-openfeature"), + ] +), +``` -### Using the Convenience Initializer +## Quick Start ```swift import Mixpanel import MixpanelOpenFeature import OpenFeature -// Create provider with a new Mixpanel instance -let provider = MixpanelOpenFeatureProvider(options: MixpanelOptions( - token: "YOUR_PROJECT_TOKEN", - featureFlagOptions: FeatureFlagOptions(enabled: true) -)) - +// 1. Create and register the provider +let options = MixpanelOptions(token: "YOUR_PROJECT_TOKEN") +let provider = MixpanelOpenFeatureProvider(options: options) await OpenFeatureAPI.shared.setProviderAndWait(provider: provider) +// 2. Get a client and evaluate flags let client = OpenFeatureAPI.shared.getClient() -let showNewUI = client.getBooleanValue(key: "new-ui", defaultValue: false) +let showNewFeature = client.getBooleanValue(key: "new-feature-flag", defaultValue: false) -// Access the underlying Mixpanel instance for identify/track -provider.mixpanel?.identify(distinctId: "user-123") +if showNewFeature { + print("New feature is enabled!") +} ``` ### Using an Existing Mixpanel Instance ```swift -import Mixpanel -import MixpanelOpenFeature -import OpenFeature +let flags = Mixpanel.mainInstance().flags +let provider = MixpanelOpenFeatureProvider(flags: flags) +await OpenFeatureAPI.shared.setProviderAndWait(provider: provider) +``` -Mixpanel.initialize(token: "YOUR_PROJECT_TOKEN", options: MixpanelOptions( - featureFlagOptions: FeatureFlagOptions(enabled: true) -)) + +This provider does **not** call `mixpanel.identify()` or `mixpanel.track()`. If you need to update the logged-in user or use [Runtime Events](/docs/featureflags/runtime-events) for targeting, call these methods on the **same Mixpanel instance** that was passed to the provider. + -let provider = MixpanelOpenFeatureProvider(flags: Mixpanel.mainInstance().flags) -await OpenFeatureAPI.shared.setProviderAndWait(provider: provider) +```swift +// When using init(options:), access via provider.mixpanel +provider.mixpanel?.identify(distinctId: "user-123") +provider.mixpanel?.track(event: "Purchase", properties: ["amount": 49.99]) + +// When using init(flags:), use the original instance +let mixpanelInstance = Mixpanel.mainInstance() +let provider = MixpanelOpenFeatureProvider(flags: mixpanelInstance.flags) +mixpanelInstance.identify(distinctId: "user-123") +``` +## Usage + +### Flag Types and Evaluation Methods + +| Mixpanel Flag Type | Variant Values | OpenFeature Method | +|---|---|---| +| Feature Gate | `true` / `false` | `getBooleanValue()` | +| Experiment | boolean, string, number, or JSON object | `getBooleanValue()`, `getStringValue()`, `getIntegerValue()`, `getDoubleValue()`, or `getObjectValue()` | +| Dynamic Config | JSON object | `getObjectValue()` | + +```swift let client = OpenFeatureAPI.shared.getClient() -let showNewUI = client.getBooleanValue(key: "new-ui", defaultValue: false) + +// Feature Gate +let isFeatureOn = client.getBooleanValue(key: "new-checkout", defaultValue: false) + +// Experiment with string variants +let buttonColor = client.getStringValue(key: "button-color-test", defaultValue: "blue") + +// Experiment with numeric variants +let maxItems = client.getIntegerValue(key: "max-items", defaultValue: 10) +let threshold = client.getDoubleValue(key: "score-threshold", defaultValue: 0.5) + +// Dynamic Config +let featureConfig = client.getObjectValue( + key: "homepage-layout", + defaultValue: Value.structure(["layout": .string("grid"), "itemsPerRow": .integer(3)]) +) ``` -## Supported Flag Types +### Evaluation Context -| OpenFeature Method | Swift Type | -|---|---| -| `getBooleanValue` / `getBooleanDetails` | `Bool` | -| `getStringValue` / `getStringDetails` | `String` | -| `getIntegerValue` / `getIntegerDetails` | `Int64` | -| `getDoubleValue` / `getDoubleDetails` | `Double` | -| `getObjectValue` / `getObjectDetails` | `Value` | +Context must be set globally via `OpenFeatureAPI.shared.setEvaluationContext()`: + +```swift +let ctx = MutableContext( + targetingKey: "user-123", + structure: MutableStructure(attributes: [ + "email": .string("user@example.com"), + "plan": .string("premium"), + ]) +) +await OpenFeatureAPI.shared.setEvaluationContext(evaluationContext: ctx) +``` + +### Runtime Properties + +Pass `custom_properties` in the evaluation context for [Runtime Properties](/docs/featureflags) targeting: -## Context and Identity +```swift +let ctx = MutableContext( + structure: MutableStructure(attributes: [ + "custom_properties": .structure([ + "tier": .string("enterprise"), + "seats": .integer(50), + ]), + ]) +) +await OpenFeatureAPI.shared.setEvaluationContext(evaluationContext: ctx) +``` -Context is set globally when registering the provider, not per-evaluation. Per-evaluation context passed to individual flag evaluations is ignored. +Unlike some providers, `targetingKey` is not used as a special bucketing key. It is passed as another context property. Mixpanel's server-side configuration determines which properties are used for targeting and bucketing. -The provider forwards the global OpenFeature context to the Mixpanel SDK via `setContext()` during initialization and whenever the global context changes. +### Full Resolution Details + +```swift +let details = client.getBooleanDetails(key: "my-feature", defaultValue: false) -- **`targetingKey`** has no special meaning in this provider. It is treated as a regular context property. -- Identity should be managed through the Mixpanel SDK via `Mixpanel.mainInstance().identify()` or `provider.mixpanel?.identify()`. +print(details.value) +print(details.variant) +print(details.reason) +print(details.errorCode) +``` ## Error Handling -The provider returns the default value on all errors, with an error code indicating the cause: - -| Error Code | Condition | +| Error Code | When | |---|---| -| `PROVIDER_NOT_READY` | The provider has not been initialized | -| `FLAG_NOT_FOUND` | The requested flag does not exist | -| `TYPE_MISMATCH` | The flag value does not match the requested type | +| `PROVIDER_NOT_READY` | Flags evaluated before the provider has finished initializing | +| `FLAG_NOT_FOUND` | The requested flag does not exist in Mixpanel | +| `TYPE_MISMATCH` | The flag value type does not match the requested type | -Use `getDetails()` methods instead of `getValue()` to inspect error codes: +To avoid `PROVIDER_NOT_READY`, use `setProviderAndWait`: ```swift -let details = client.getBooleanDetails(key: "my-flag", defaultValue: false) -if let errorCode = details.errorCode { - print("Flag error: \(errorCode) - \(details.errorMessage ?? "")") -} +await OpenFeatureAPI.shared.setProviderAndWait(provider: provider) ``` -## Lifecycle +## Troubleshooting + +### Flags Always Return Default Values + +1. **Provider not ready:** Use `setProviderAndWait` to ensure initialization completes. +2. **Network issues:** Check for failed requests to Mixpanel's flags API. +3. **Flag not configured:** Verify the flag exists and is enabled in your Mixpanel project. + +### Flags Not Updating After Context Change + +When you update the OpenFeature context, the provider fetches new flag values: + +```swift +let newCtx = MutableContext( + structure: MutableStructure(attributes: ["plan": .string("premium")]) +) +await OpenFeatureAPI.shared.setEvaluationContext(evaluationContext: newCtx) +``` -- **`shutdown()`** is a no-op. The Mixpanel SDK manages its own lifecycle. -- The reason code for successful evaluations is `STATIC`. +If flags still aren't updating, verify your targeting rules in Mixpanel use the context properties you're setting. From 7e2c3056935a548e439f7ea008a3883f09e3070c Mon Sep 17 00:00:00 2001 From: Mark Siebert Date: Mon, 13 Apr 2026 09:46:07 -0700 Subject: [PATCH 4/5] Add openfeature and Logcat to spellcheck dictionary Fix CI spellcheck failures by adding these words to the cspell.json allowed words list. Co-Authored-By: Claude Opus 4.6 --- cspell.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cspell.json b/cspell.json index b9a3542906..6bfe0c04ac 100644 --- a/cspell.json +++ b/cspell.json @@ -222,6 +222,7 @@ "Langfuse", "langfuse", "Leanplum", + "Logcat", "logit", "logotable", "Lookback", @@ -269,6 +270,7 @@ "onesignal", "OADA", "openapi", + "openfeature", "operationalize", "operationalized", "Operationalizing", From 7a9aaa2af017d8a69aea0affe6756ad8b0fa7446 Mon Sep 17 00:00:00 2001 From: Mark Siebert Date: Mon, 13 Apr 2026 10:13:59 -0700 Subject: [PATCH 5/5] Pin all GitHub Actions to full-length commit SHAs Required by repo policy. Pins actions/checkout@v4, actions/setup-node@v5, actions/stale@v10.2.0, actions/github-script@v8, and streetsidesoftware/cspell-action@v8 to their current commit SHAs across all workflow files. Co-Authored-By: Claude Opus 4.6 --- .github/workflows/cspell.yaml | 4 ++-- .github/workflows/rdme-delete-staging.yml | 4 ++-- .github/workflows/rdme-docs.yml | 2 +- .github/workflows/rdme-openapi.yml | 4 ++-- .github/workflows/rdme-staging.yml | 8 ++++---- .github/workflows/stale.yaml | 2 +- .github/workflows/tests.yml | 4 ++-- .github/workflows/vercel-preview.yaml | 2 +- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/.github/workflows/cspell.yaml b/.github/workflows/cspell.yaml index 1a8567b1aa..819af81d30 100644 --- a/.github/workflows/cspell.yaml +++ b/.github/workflows/cspell.yaml @@ -9,8 +9,8 @@ jobs: spellcheck: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: streetsidesoftware/cspell-action@v8 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: streetsidesoftware/cspell-action@de2a73e963e7443969755b648a1008f77033c5b2 # v8 with: # Define glob patterns to filter the files to be checked. Use a new line between patterns to define multiple patterns. # The default is to check ALL files that were changed in in the pull_request or push. diff --git a/.github/workflows/rdme-delete-staging.yml b/.github/workflows/rdme-delete-staging.yml index 113a778416..91aee970b5 100644 --- a/.github/workflows/rdme-delete-staging.yml +++ b/.github/workflows/rdme-delete-staging.yml @@ -15,8 +15,8 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repo 📚 - uses: actions/checkout@v4 - - uses: actions/setup-node@v5 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5 with: node-version: 20 cache: 'npm' diff --git a/.github/workflows/rdme-docs.yml b/.github/workflows/rdme-docs.yml index ff01cd363f..0a4df787c0 100644 --- a/.github/workflows/rdme-docs.yml +++ b/.github/workflows/rdme-docs.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repo 📚 - uses: actions/checkout@v4 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Run `docs` command 🚀 uses: readmeio/rdme@5d702394cb1fd1dc3fab6e0769b44b56c9d425d1 # rdme version 10.6.0 diff --git a/.github/workflows/rdme-openapi.yml b/.github/workflows/rdme-openapi.yml index 72421cbe0c..4ff7695567 100644 --- a/.github/workflows/rdme-openapi.yml +++ b/.github/workflows/rdme-openapi.yml @@ -12,8 +12,8 @@ jobs: rdme-openapi: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v5 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5 with: node-version: 20.x - run: npm ci diff --git a/.github/workflows/rdme-staging.yml b/.github/workflows/rdme-staging.yml index 4136ac7dbe..5d4cb76404 100644 --- a/.github/workflows/rdme-staging.yml +++ b/.github/workflows/rdme-staging.yml @@ -20,9 +20,9 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out repo 📚 - uses: actions/checkout@v4 + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - uses: actions/setup-node@v5 + - uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5 with: node-version: 20 cache: 'npm' @@ -76,7 +76,7 @@ jobs: issues: write pull-requests: write steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 with: sparse-checkout: | .github @@ -85,7 +85,7 @@ jobs: # Build PR Comment - name: Build PR comment id: build-pr-comment - uses: actions/github-script@v8 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 with: result-encoding: string script: | diff --git a/.github/workflows/stale.yaml b/.github/workflows/stale.yaml index 5cc19ca386..f6170d2871 100644 --- a/.github/workflows/stale.yaml +++ b/.github/workflows/stale.yaml @@ -14,7 +14,7 @@ jobs: steps: # https://github.com/actions/stale - - uses: actions/stale@v10.2.0 + - uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} days-before-stale: 180 diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e44ac43445..64b82dd049 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -16,9 +16,9 @@ jobs: node-version: [20.x] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v5 + uses: actions/setup-node@a0853c24544627f65ddf259abe73b1d18a591444 # v5 with: node-version: ${{ matrix.node-version }} - run: npm ci diff --git a/.github/workflows/vercel-preview.yaml b/.github/workflows/vercel-preview.yaml index 7645451713..6a9adfcc2f 100644 --- a/.github/workflows/vercel-preview.yaml +++ b/.github/workflows/vercel-preview.yaml @@ -10,7 +10,7 @@ jobs: Deploy-Preview: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Install Vercel CLI run: npm install --global vercel@latest - name: Pull Vercel Environment Information