diff --git a/api/public-api/README.md b/api/public-api/README.md index 462b94d..26fabf3 100644 --- a/api/public-api/README.md +++ b/api/public-api/README.md @@ -104,24 +104,39 @@ This endpoint retrieves a list of flag values evaluated for a particular user or The endpoint is a `GET` request to ensure that the request can be completed without a `CORS Preflight` request to reduce latency. {% endhint %} -The context must be flattened and provided as query parameters. For instance, given the following nested object: - -```typescript -{ - company: { - id: 42, - }, - user: { - id: 99, - }, -} +Send the context in one query parameter called `contextJson`. Use `JSON.stringify()` to turn the whole context into JSON text, then `URLSearchParams` to encode it for the URL. Arrays stay as arrays: + +```javascript +const context = { + company: { id: "42", entitlements: ["reports", "exports"] }, + user: { id: "99", roles: ["admin", "editor"] }, + other: { tags: ["beta"] }, +}; +const query = new URLSearchParams({ + publishableKey: "", + contextJson: JSON.stringify(context), +}); +const response = await fetch(`https://front.reflag.com/features/evaluated?${query}`); +const flags = await response.json(); ``` -It needs to be flattened out into the following form: `context.company.id=42&context.user.id=99` . +The context can include `user`, `company`, and `other` objects. None of these objects is required. User and company IDs are also optional; if provided, they must be strings or numbers, not arrays. -#### Example +Context attributes can hold strings, numbers, true/false values, `null`, or arrays. You can group attributes in one nested object—for example, `user.profile.roles`, targeted with the same dotted path. Deeper object nesting is rejected. + +By default, the JSON text can be up to 16,384 bytes before URL encoding. Your HTTP server or proxy may have a lower URL limit. + +The API returns a validation error if: + +* The JSON is not valid or the context does not follow the rules above. +* You send `contextJson` more than once. +* You send `contextJson` together with `context` or `context.*` parameters. -
GET https://front.reflag.com/features/enabled?context.company.id=42&context.user.id=99&publishableKey=pub_prod_Cqx4DGo1lk3Lcct5NHLjWy
+The older query format still works for single values, such as `context.company.id=42&context.user.id=99`. For arrays, use `contextJson`; do not send separate items as `context.user.roles.0=admin`.
+
+#### Example using the older query format
+
+
GET https://front.reflag.com/features/evaluated?context.company.id=42&context.user.id=99&publishableKey=pub_prod_Cqx4DGo1lk3Lcct5NHLjWy
 
{% code title="Response" %} @@ -143,9 +158,35 @@ It needs to be flattened out into the following form: `context.company.id=42&con Reflag utilizes attributes from the `company` endpoint to identify which features are enabled for specific companies. Ensure all `company` attributes referenced in the `context` are also provided through the `company` endpoint. {% endhint %} +#### Evaluation errors + +Flag results may include `evaluationErrors`: + +* `MISSING_CONTEXT_FIELD`: a rule needs a field that was not provided. +* `UNSUPPORTED_ARRAY_OPERATOR`: a rule tried to use an operator that does not work with arrays. + +The affected rule does not match, but the API still returns a normal response and other rules can match. Errors while choosing a config value are listed in `config.evaluationErrors`. + +```json +{ + "evaluationErrors": [ + { + "code": "UNSUPPORTED_ARRAY_OPERATOR", + "field": "user.roles", + "operator": "GT", + "message": "Operator GT does not support array-valued context field \"user.roles\"." + } + ] +} +``` + +Older clients can still use `missingContextFields`, which lists missing fields only. Use `evaluationErrors` for new integrations. + +See [array attribute operators](../../product-handbook/creating-segments.md#array-attributes) for supported operators and examples. + ### `GET /features/enabled` -This endpoint is similar to `features/evaluated` but only includes flags that have been evaluated as `true`. +This endpoint is similar to `features/evaluated` but only includes flags that have been evaluated as `true`. It accepts the same `contextJson` parameter. ### `POST /features/events` @@ -294,6 +335,14 @@ Content-Type: application/json ``` {% endcode %} +## Array attributes + +You can send arrays in the `attributes` of user, company, and event requests, including bulk requests. Send `"roles": ["admin", "editor"]`, not `"roles": "[\"admin\",\"editor\"]"`. + +An update replaces the whole list; sending `[]` clears it. Each array stays one attribute, rather than becoming separate fields such as `roles.0`. + +See [array attribute operators](../../product-handbook/creating-segments.md#array-attributes) for matching rules and examples. + ## Responses The API returns a `200` status code for successful calls and a `400` status code for errors, including invalid request bodies. diff --git a/api/public-api/public-api-reference.md b/api/public-api/public-api-reference.md index 0be9732..b672204 100644 --- a/api/public-api/public-api-reference.md +++ b/api/public-api/public-api-reference.md @@ -67,6 +67,17 @@ | field | string |

Refers to a field of the context object.
Example: company.tier

| | values | string\[] | Array of values which will be compared with the value of the context field. Operators SET, NOT\_SET, IS\_TRUE, IS\_FALSE require 0 values, ANY\_OF and NOT\_ANY\_OF support multiple values. All the other operators require exactly one value. | | operator | enum(`IS`,`IS_NOT`,`ANY_OF`,`NOT_ANY_OF`,`CONTAINS`,`NOT_CONTAINS`","`GT`" ,`LT`,`AFTER`,`BEFORE`,`SET`,`NOT_SET`,`IS_TRUE`,`IS_FALSE`) | Operator for comparison of the context field with provided values. | + +When the context field holds an array: + +* `IS` matches if the array has just one item equal to the value you chose. `IS_NOT` matches if it has more than one item, is empty, or its only item differs. +* `CONTAINS` matches if the array includes the value you chose. `NOT_CONTAINS` matches if it does not. +* `ANY_OF` matches if the array includes at least one of the values you chose. `NOT_ANY_OF` matches if it includes none. +* `SET` matches arrays with at least one item. `NOT_SET` matches empty arrays. + +These checks match whole values and treat uppercase and lowercase letters as different. For example, `["admin"]` does not contain `adm` or `Admin`. Numeric and date operators, plus `IS_TRUE` and `IS_FALSE`, do not work with arrays. + +See [array attribute operators](../../product-handbook/creating-segments.md#array-attributes) for examples. {% endtab %} {% tab title="Rollout Percentage" %} diff --git a/product-handbook/concepts/filter.md b/product-handbook/concepts/filter.md index 0504ef5..20453eb 100644 --- a/product-handbook/concepts/filter.md +++ b/product-handbook/concepts/filter.md @@ -19,6 +19,16 @@ Reflag supports the following filter types: This filter can be used to check company attributes against a set of predicates. The attributes include `First seen` and `Last seen`, which are maintained by Reflag. You can use any attribute name that your application sends to Reflag. +### Array attributes + +Attributes can hold lists, such as `roles: ["admin", "editor"]`. + +* Use `CONTAINS` to check for one value, such as `admin`. +* Use `ANY_OF` to check for any of several values, such as `admin` or `owner`. +* Use `IS` when the list must have just one item, equal to the value you chose. + +Array checks match whole values and treat uppercase and lowercase letters as different. See [array attribute operators](../creating-segments.md#array-attributes) for all supported operators and examples. + ### Company flag metrics This filter allows checking company-level flag metrics. These metrics include `Event count`, `First used`, `Last used`, and more. diff --git a/product-handbook/concepts/targeting-rules.md b/product-handbook/concepts/targeting-rules.md index bd8623e..e2615b7 100644 --- a/product-handbook/concepts/targeting-rules.md +++ b/product-handbook/concepts/targeting-rules.md @@ -18,7 +18,9 @@ The evaluation context refers simply to a collection of **key** — **value** pa * Any other [company attributes](company.md#attributes) that might be used by the filters in the rules, * A collection of "_**other**_" attributes that can be used by the feature access targeting rules. -The exact structure of the data will vary by the SDK in use. +The exact structure of the data will vary by the SDK in use. Custom attributes can also hold arrays, such as `user.roles: ["admin", "editor"]`. + +Use `CONTAINS` to check for a role such as `admin`, or `ANY_OF` to check for any of several roles. See [array attribute operators](../creating-segments.md#array-attributes) for examples and the full list of operators. ### Missing context fields @@ -26,6 +28,16 @@ During the evaluation of targeting rules against a context it might happen that Reflag reports these missing context fields using [feature events](feature-events.md). Reflag SDKs will also generate warnings in these cases making it easy to find these situations in your application. +### Checks that do not support arrays + +Some operators, such as `GT`, `DATE_AFTER`, and `IS_TRUE`, do not work with arrays. Arrays also cannot be used for percentage rollouts. + +If Reflag runs one of these checks on an array, the whole rule does not match. Adding `NOT` does not turn that error into a match. Other rules can still match. + +Reflag does not report errors for conditions it skips. For example, if the first condition in an `OR` group matches, Reflag does not check the remaining conditions in that group. + +The [evaluation API](../../api/public-api/README.md#evaluation-errors) reports these errors in `evaluationErrors`, with code `UNSUPPORTED_ARRAY_OPERATOR`. Missing fields use `MISSING_CONTEXT_FIELD`. The older `missingContextFields` field is still available but lists missing fields only. + ### Next steps * Learn about [filters](filter.md), diff --git a/product-handbook/creating-segments.md b/product-handbook/creating-segments.md index 4967004..bc9ffe6 100644 --- a/product-handbook/creating-segments.md +++ b/product-handbook/creating-segments.md @@ -92,6 +92,27 @@ Operators depend on the condition type: * `In segment` * `Not in segment` +These attribute operators are also available when you create flag access rules. + +#### Array attributes + +Attributes can hold arrays such as `roles: ["admin", "editor"]`. Array operators compare whole items and are case-sensitive: `admin` does not match `Admin` or part of a value such as `adm`. + +| Operator | Matches an array when | +| --- | --- | +| `Is` | It has exactly one item, equal to the selected value. | +| `Is not` | It is empty, has more than one item, or its only item differs from the selected value. | +| `Contains` | It includes the selected value. | +| `Does not contain` | It does not include the selected value. | +| `Is any of` | It includes at least one selected value. | +| `Is not any of` | It includes none of the selected values. | +| `Has any value` | It has at least one item. | +| `Has no value` | It is empty. | + +For example, `roles: ["admin", "editor"]` matches `Contains admin` and `Is any of [admin, owner]`, but it does not match `Is admin`. + +Number, date, and boolean operators do not support arrays, and arrays cannot be used for percentage rollouts. If an access rule applies an unsupported operator to an array, that rule does not match. + ## Save and reuse the segment After you save a segment, you can reuse it in flag [access rules](feature-rollouts/feature-targeting-rules.md) and rollout workflows. diff --git a/product-handbook/feature-rollouts/feature-targeting-rules.md b/product-handbook/feature-rollouts/feature-targeting-rules.md index 2b3f8c4..f358e9a 100644 --- a/product-handbook/feature-rollouts/feature-targeting-rules.md +++ b/product-handbook/feature-rollouts/feature-targeting-rules.md @@ -72,6 +72,7 @@ Here are examples of access conditions: * Companies with Company IDs 1 and 2: `Company attribute: Company ID IS ANY OF [1,2]` * Give access to newly created companies: `Company attribute: createdAt LESS THAN [30] DAYS AGO` * Give access to users with the manager role at all companies: `User attribute: role IS [manager]` +* Give access to users whose `roles` list includes `manager`: `User attribute: roles contains [manager]`. See [array attribute operators](../creating-segments.md#array-attributes) for more examples. * Give access to companies in the Pro plan segment: `Segment: In segment ['Pro']` * Give access to companies in the Beta users’ segment: `Segment: In segment ['Beta users']` * Give access to companies who already have access to the Huddle flag: `Flag access: Flag [Huddle] is enabled`