Skip to content

Implement starts_with, ends_with, and sem_ver custom operators - #5

Merged
aepfli merged 2 commits into
mainfrom
copilot/implement-custom-operators
Dec 1, 2025
Merged

aepfli merged 2 commits into
mainfrom
copilot/implement-custom-operators

Conversation

Copilot AI commented Dec 1, 2025 •

Copy link
Copy Markdown
Contributor

Implements remaining flagd custom operators to achieve full compatibility with the flagd specification.

New Operators

  • starts_with / ends_with - Case-sensitive string prefix/suffix matching
  • sem_ver - Semantic version comparison per semver.org with operators: =, !=, <, <=, >, >=, ^ (caret), ~ (tilde)

Usage

{"starts_with": [{"var": "email"}, "admin@"]}

{"ends_with": [{"var": "filename"}, ".pdf"]}

{"sem_ver": [{"var": "app.version"}, ">=", "2.0.0"]}

{"sem_ver": [{"var": "version"}, "^", "1.2.3"]}

Implementation

  • SemVer struct with parser handling pre-release tags, build metadata, missing parts, and v prefixes
  • Caret range: ^1.2.3 matches >=1.2.3 <2.0.0 (special handling for 0.x versions per npm conventions)
  • Tilde range: ~1.2.3 matches >=1.2.3 <1.3.0
  • All operators support both literal values and {"var": "path"} references
Original prompt

This section details on the original issue you should resolve

<issue_title>Implement all flagd custom operators for feature flag evaluation</issue_title>
<issue_description>

Labels

enhancement, feature, operators, flagd-compatibility

Description

This issue tracks the implementation of all custom operators from the flagd specification to achieve full compatibility with flagd's evaluation engine.

Currently, flagd-evaluator only implements the fractional operator. To provide feature parity with flagd, we need to implement all remaining custom operators.

Background

flagd extends standard JSONLogic with custom operators specifically designed for feature flag evaluation. These operators enable advanced targeting rules including:

  • Fractional/percentage-based rollouts with consistent hashing
  • Semantic version comparisons
  • String pattern matching
  • Date/time comparisons

Custom Operators to Implement

1. ✅ fractional (Already Implemented)

Status: Complete

Purpose: Deterministic percentage-based bucket assignment using consistent hashing

Syntax:

{
  "fractional": [
    { "var": "targetingKey" },
    ["bucketA", 50, "bucketB", 30, "bucketC", 20]
  ]
}

Behavior:

  • First argument: value to hash (typically user ID or targeting key)
  • Second argument: array of alternating bucket names and percentages
  • Percentages must sum to 100
  • Uses consistent hashing (murmurhash3 or similar) to deterministically assign to buckets
  • Same input always produces same bucket assignment

Example:

// Input
{
  "fractional": [
    { "var": "user.id" },
    ["control", 50, "treatment", 50]
  ]
}

// Data
{ "user": { "id": "user-123" } }

// Output
"control" or "treatment" (deterministic based on hash of "user-123")

2. ⬜ sem_ver (Semantic Version Comparison)

Status: Not implemented

Purpose: Compare semantic versions according to semver.org specification

Syntax:

{
  "sem_ver": [
    { "var": "app.version" },
    "operator",
    "target_version"
  ]
}

Supported Operators:

  • "=" - Equal to
  • "!=" - Not equal to
  • "<" - Less than
  • "<=" - Less than or equal to
  • ">" - Greater than
  • ">=" - Greater than or equal to
  • "^" - Caret range (compatible with - allows patch and minor updates)
  • "~" - Tilde range (allows patch updates only)

Examples:

// Exact version match
{
  "sem_ver": [{ "var": "version" }, "=", "1.2.3"]
}
// Returns: true if version == "1.2.3"

// Caret range (^1.2.3 means >=1.2.3 <2.0.0)
{
  "sem_ver": [{ "var": "version" }, "^", "1.2.3"]
}
// Returns: true for "1.2.3", "1.2.4", "1.9.0", false for "2.0.0"

// Tilde range (~1.2.3 means >=1.2.3 <1.3.0)
{
  "sem_ver": [{ "var": "version" }, "~", "1.2.3"]
}
// Returns: true for "1.2.3", "1.2.9", false for "1.3.0"

Edge Cases:

  • Pre-release versions (e.g., "1.2.3-alpha.1")
  • Build metadata (e.g., "1.2.3+build.123")
  • Missing patch/minor versions (e.g., "1.2" should be treated as "1.2.0")
  • Invalid version strings should return evaluation error

Implementation Notes:

  • Consider using a semver parsing library or implement parser following semver spec
  • Caret and tilde ranges follow npm semver conventions
  • Must handle pre-release version precedence correctly

3. ⬜ starts_with (String Prefix Match)

Status: Not implemented

Purpose: Check if a string starts with a specific prefix

Syntax:

{
  "starts_with": [
    { "var": "string_value" },
    "prefix"
  ]
}

Examples:

// Basic usage
{
  "starts_with": [{ "var": "email" }, "admin@"]
}
// Data: { "email": "admin@example.com" }
// Returns: true

// Case sensitive
{
  "starts_with": [{ "var": "path" }, "/api/"]
}
// Data: { "path": "/api/users" }
// Returns: true
// Data: { "path": "/API/users" }
// Returns: false

Edge Cases:

  • Empty prefix should return true (every string starts with empty string)
  • Non-string inputs should coerce to string or return error
  • null/undefined values should return false

4. ⬜ ends_with (String Suffix Match)

Status: Not implemented

Purpose: Check if a string ends with a specific suffix

Syntax:

{
  "ends_with": [
    { "var": "string_value" },
    "suffix"
  ]
}

Examples:

// Basic usage
{
  "ends_with": [{ "var": "filename" }, ".pdf"]
}
// Data: { "filename": "document.pdf" }
// Returns: true

// Case sensitive
{
  "ends_with": [{ "var": "url" }, ".com"]
}
// Data: { "url": "https://example.com" }
// Returns: true
// Data: { "url": "https://example.COM" }
// Returns: false

Edge Cases:

  • Empty suffix should return true
  • Non-string inputs should coerce to string or return error
  • null/undefined values should return false

Implementation Requirements

General Requirements

  • Each operator must be implemented in `src/operators/c...

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Co-authored-by: aepfli <9987394+aepfli@users.noreply.github.com>
Copilot AI changed the title [WIP] Implement custom operators for flagd compatibility Implement starts_with, ends_with, and sem_ver custom operators Dec 1, 2025
Copilot AI requested a review from aepfli December 1, 2025 21:23
@aepfli
aepfli marked this pull request as ready for review December 1, 2025 21:26
@aepfli
aepfli merged commit 8175d3d into main Dec 1, 2025
10 checks passed
aepfli added a commit that referenced this pull request Mar 14, 2026
)

* feat(java): implement flagd-api Evaluator interface in FlagEvaluator

Closes #2

- Add flagd-api 0.1.0 dependency to pom.xml
- Make FlagEvaluator implement dev.openfeature.contrib.tools.flagd.api.Evaluator
- Bridge setFlags/setFlagsAndGetChangedKeys to updateState (throws FlagStoreException on failure)
- Bridge per-type resolve methods to evaluateFlag with EvaluationResult→ProviderEvaluation conversion
- Add mapErrorCode helper for error code translation
- Add EvaluatorInterfaceTest covering all interface methods

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com>

* refactor(java): extract updateStateAndHandleErrors helper

Eliminate duplicated error handling logic between setFlags and
setFlagsAndGetChangedKeys by extracting a private helper method.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com>

---------

Signed-off-by: Simon Schrottner <simon.schrottner@dynatrace.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement all flagd custom operators for feature flag evaluation

2 participants