Skip to content

feat: extend OpenFeature numeric, payload, and reason support - #216

Merged
marandaneto merged 3 commits into
posthog/openfeature-providerfrom
feat/openfeature-provider-parity
Sep 25, 2026
Merged

marandaneto merged 3 commits into
posthog/openfeature-providerfrom
feat/openfeature-provider-parity

Conversation

@marandaneto

@marandaneto marandaneto commented Sep 25, 2026 •

Copy link
Copy Markdown
Member

💡 Motivation and Context

Stacked on #214. This PR targets posthog/openfeature-provider, not main, so #214 can merge independently. After #214 merges, retarget this follow-up to main.

The cross-provider comparison in #214 identified three additions:

  • Accept unsigned hexadecimal numeric variants such as 0x10 and 0Xff. Invalid and signed hexadecimal strings remain type mismatches.
  • Return JSON array payloads, including empty and nested arrays. The upstream OpenFeature SDK still requires a map default (%{}), even when get_map_value/4 returns an array. This restriction is documented in the module docs and a code comment beside resolve_map_value/4, and tested through the real client.
  • Expose flag_metadata["posthog_reason"], using the PostHog description, code, or existing string reason. Explicit server flag_disabled codes map off results to :disabled. Other off results stay :default. Type-mismatch details retain available reason metadata while preserving the error code and caller default. This uses a stable code rather than Python's description substring check.

Error-hook behavior is unchanged and tracked separately in #215. There is no OpenFeature-specific SDK spec.

Related work: PostHog/posthog-python#980 and PostHog/posthog-js#5099.

💚 How did you test it?

  • Five additional regression tests reproduced missing metadata on type mismatches before the fix. All 75 provider tests now pass.
  • Added regression tests first. Nineteen tests failed before implementation for hexadecimal parsing, array payloads, and reason metadata.
  • mix test: 596 passed, 18 integration tests excluded.
  • mix format --check-formatted, MIX_ENV=test mix compile --warnings-as-errors, and mix credo --strict passed.
  • Isolated autoreview of the final branch against origin/posthog/openfeature-provider reported no actionable findings.

Checks ran on Elixir 1.20.4 / OTP 29. No live PostHog project was used.

📝 Checklist

  • I reviewed the submitted code.
  • I added tests to verify the changes.
  • I updated the docs if needed.
  • No breaking change or entry added to the changelog.

If releasing new changes

  • Ran sampo add to generate a changeset file

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Implemented with Pi using local file editing, shell tests, and the isolated autoreview helper. The user requested a separate follow-up so the initial provider PR can merge without waiting for these additions. The upstream map-default restriction is documented rather than changing the OpenFeature dependency. No shared session link is available.

@marandaneto marandaneto self-assigned this Sep 25, 2026
@greptile-apps

greptile-apps Bot commented Sep 25, 2026 •

Copy link
Copy Markdown

Retrigger

[Medium risk] Extends OpenFeature flag resolution to handle more data types.

The new change appears safe to merge, though the previously reported missing reason metadata on type mismatches remains.

Reviews (2) · Last reviewed commit: "docs: explain upstream OpenFeature array..."

@github-actions

github-actions Bot commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

posthog-elixir Compliance Report

Date: 2026-09-25T06:33:47.152904+00:00
Duration: 108660ms

⚠️ Some Tests Failed

40/47 tests passed, 7 failed


Capture Tests

⚠️ 29/30 tests passed, 1 failed

View Details
Test Status Duration
Format Validation.Event Has Required Fields ✅ 609ms
Format Validation.Event Has Uuid ✅ 610ms
Format Validation.Event Has Lib Properties ✅ 610ms
Format Validation.Distinct Id Is String ✅ 610ms
Format Validation.Token Is Present ✅ 610ms
Format Validation.Custom Properties Preserved ✅ 610ms
Format Validation.Event Has Timestamp ✅ 610ms
Format Validation.Non Utc Event Timestamp Is Converted To Utc ❌ 610ms
Retry Behavior.Retries On 503 ✅ 5616ms
Retry Behavior.Does Not Retry On 400 ✅ 2611ms
Retry Behavior.Does Not Retry On 401 ✅ 2611ms
Retry Behavior.Respects Retry After Header ✅ 5617ms
Retry Behavior.Implements Backoff ✅ 15624ms
Retry Behavior.Retries On 500 ✅ 5616ms
Retry Behavior.Retries On 502 ✅ 5616ms
Retry Behavior.Retries On 504 ✅ 5616ms
Retry Behavior.Max Retries Respected ✅ 15625ms
Deduplication.Generates Unique Uuids ✅ 623ms
Deduplication.Preserves Uuid On Retry ✅ 5615ms
Deduplication.Preserves Uuid And Timestamp On Retry ✅ 10611ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry ✅ 5618ms
Deduplication.No Duplicate Events In Batch ✅ 615ms
Deduplication.Different Events Have Different Uuids ✅ 612ms
Compression.Sends Gzip When Enabled ✅ 610ms
Batch Format.Uses Proper Batch Structure ✅ 610ms
Batch Format.Flush With No Events Sends Nothing ✅ 607ms
Batch Format.Multiple Events Batched Together ✅ 615ms
Error Handling.Does Not Retry On 403 ✅ 2612ms
Error Handling.Does Not Retry On 413 ✅ 2613ms
Error Handling.Retries On 408 ✅ 5616ms

Failures

format_validation.non_utc_event_timestamp_is_converted_to_utc

Event 0 field 'timestamp' instant '2026-09-25T06:32:03.085966Z' != expected '2025-01-02T03:04:05Z'

Feature_Flags Tests

⚠️ 11/17 tests passed, 6 failed

View Details
Test Status Duration
Request Payload.Request With Person Properties Device Id ✅ 8ms
Request Payload.Flags Request Uses V2 Query Param ✅ 7ms
Request Payload.Flags Request Hits Flags Path Not Decide ✅ 6ms
Request Payload.Flags Request Omits Authorization Header ✅ 7ms
Request Payload.Token In Flags Body Matches Init ✅ 7ms
Request Payload.Groups Round Trip ✅ 6ms
Request Payload.Groups Default To Empty Object ❌ 7ms
Request Payload.Disable Geoip False Propagates As Geoip Disable False ✅ 6ms
Request Payload.Disable Geoip Omitted Defaults To False ❌ 7ms
Request Payload.Flag Keys To Evaluate Contains Only Requested Key ✅ 7ms
Request Lifecycle.No Flags Request On Init Alone ✅ 3ms
Request Lifecycle.No Flags Request On Normal Capture ✅ 609ms
Request Lifecycle.Two Flag Calls Produce Two Remote Requests ✅ 12ms
Request Lifecycle.Mock Response Value Is Returned To Caller ❌ 6ms
Retry Behavior.Retries Flags On 502 ❌ 311ms
Retry Behavior.Retries Flags On 504 ❌ 311ms
Side Effect Events.Get Feature Flag Captures Feature Flag Called Event ❌ 609ms

Failures

request_payload.groups_default_to_empty_object

Field 'groups' not found in /flags request body at path 'groups'. Available keys: ['api_key', 'distinct_id', 'flag_keys_to_evaluate']

request_payload.disable_geoip_omitted_defaults_to_false

Field 'geoip_disable' not found in /flags request body at path 'geoip_disable'. Available keys: ['api_key', 'distinct_id', 'flag_keys_to_evaluate']

request_lifecycle.mock_response_value_is_returned_to_caller

Last action result missing field 'value'. Keys: ['error', 'success']

retry_behavior.retries_flags_on_502

Last action result missing field 'value'. Keys: ['error', 'success']

retry_behavior.retries_flags_on_504

Last action result missing field 'value'. Keys: ['error', 'success']

side_effect_events.get_feature_flag_captures_feature_flag_called_event

Expected 1 events with name '$feature_flag_called', got 0

Comment thread lib/posthog/open_feature.ex
Comment on lines +144 to +146
# open-feature/elixir-sdk 0.1.3 guards get_map_value/get_map_details defaults
# with is_map/1 before calling the provider, but does not restrict returned
# values. Array payloads work with a map default; list defaults need an upstream fix.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marandaneto
marandaneto marked this pull request as ready for review September 25, 2026 06:27
@marandaneto
marandaneto requested a review from a team as a code owner September 25, 2026 06:27
@marandaneto
marandaneto merged commit 829dbe2 into posthog/openfeature-provider Sep 25, 2026
27 of 28 checks passed
@marandaneto
marandaneto deleted the feat/openfeature-provider-parity branch September 25, 2026 12:48
@marandaneto marandaneto mentioned this pull request Sep 25, 2026
3 of 5 tasks
marandaneto added a commit that referenced this pull request Sep 25, 2026
* feat: add OpenFeature provider

Add PostHog.OpenFeature.Provider for the Elixir OpenFeature SDK, with
open_feature as an optional dependency. The provider mirrors the
posthog-node OpenFeature provider: targeting_key maps to distinct_id,
groups and group_properties map to groups, and other context keys map
to person properties.

Generated-By: PostHog Desktop
Task-Id: 45365cd7-19ef-46e1-8d2a-46d2f484d229

* fix: return the default for disabled flags and update public API snapshot

The string, number, and map resolvers now check `enabled: false` before inspecting the variant or payload, so a disabled flag that still carries a remote value resolves to the caller-supplied default. Adds a test for each type.

Regenerates public_api.snapshot to include PostHog.OpenFeature.Provider, which the "Compile with warnings as errors" CI job checks.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

Generated-By: PostHog Desktop
Task-Id: 8167c421-7e51-4669-be55-fc7b39e39d26

* docs: describe disabled-flag resolution after resolver reorder

Co-Authored-By: Claude Fable 5.1 <[email protected]>

Generated-By: PostHog Desktop
Task-Id: 8167c421-7e51-4669-be55-fc7b39e39d26

* fix: match Node and Python providers for disabled flags and numeric variants

A disabled flag that still carries a variant or payload now resolves to that value with reason `:default`, as the Node and Python providers do. Number resolution accepts `.5`, `-.5`, and `1.`, which JavaScript's Number() and Python's float() accept but Float.parse/1 does not.

Co-Authored-By: Claude Fable 5.1 <[email protected]>

Generated-By: PostHog Desktop
Task-Id: 8167c421-7e51-4669-be55-fc7b39e39d26

* feat: extend OpenFeature numeric, payload, and reason support (#216)

* feat: extend OpenFeature numeric, payload, and reason support

* docs: explain upstream OpenFeature array-default limitation

* fix: preserve OpenFeature reason metadata on type mismatches

* chore: remove redundant OpenFeature follow-up changeset

---------

Co-authored-by: Patricio <[email protected]>
Co-authored-by: Manoel Aranda Neto <[email protected]>
Co-authored-by: Manoel Aranda Neto <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Archived in project

Development

Successfully merging this pull request may close these issues.

2 participants