Commit 66c2554
authored
feat: Add FDv2 payload application via FlagManager.applyChanges (#299)
## What this adds
Two pieces the FDv2 data pipeline (a later PR) needs to apply payloads
to the flag store correctly. Nothing produces FDv2 payloads for the flag
manager yet, so behavior is unchanged.
### Envelope version is authoritative for flag-eval objects
The flag-eval mapper now parses objects with the `put-object` envelope
version substituted in, replacing any in-object value. The FDv2 wire
format moved versioning onto the envelope; live service objects carry
only `flagVersion`:
```json
{"key":"greekTest","kind":"flag-eval","version":420,
"object":{"flagVersion":7,"value":true,"variation":0,"trackEvents":false}}
```
The previous parse required an in-object `version`, which made every
real payload fail with "FDv2 payload contained invalid data." The
contract test harness includes an in-object version in its mock data,
which is why the suites did not catch it; verified against the live
service. This matches the JS implementation, which spreads the object
over `version: update.version`.
### `FlagManager.applyChanges`
Applies a changeset from an FDv2 payload, handling all three transfer
types in one place — matching the JS implementation's
`applyChanges(context, updates, type)` — plumbed through
`FlagPersistence`/`FlagUpdater`:
- **full** — replaces all stored flags. This delegates to the existing
init path, so it keeps init's semantics: the context becomes the active
context (as with an FDv1 put), changes are reported by diffing against
the previous state (including deletions), and the cache is always
written — replacing the stored flags with an empty set is a change.
`environmentId` is applied on this path.
- **partial** — applies the individual updates without per-item version
comparison. FDv2 orders data at the payload level, so a lower envelope
version must still apply — the existing `upsert` discards it as
out-of-order, which is correct for FDv1 patches but wrong here (the
harness's "ignores model version" test sends exactly this). Updates are
context-checked like `upsert`, a single change event covers the affected
keys, and an empty update set changes nothing and skips the cache write.
- **none** — takes no action: no store mutation, no events, no cache
write. The payload confirms the SDK is already up to date; freshness and
status handling stay with the caller.
Putting the type dispatch inside the flag manager (rather than having
the data pipeline choose between `init` and a partial-only method) keeps
the full/partial/none semantics in one self-documenting place and
prevents a caller from applying a full payload through the merge path by
mistake.
## Known pre-existing divergence, intentionally not addressed here
Change detection (`FlagUpdater._hasChanged`) compares evaluation details
by **value only** — `LDEvaluationDetail.==` ignores `variationIndex` and
`reason` — so a variation- or reason-only change applies to the store
without a `FlagsChangedEvent`, despite the event's documentation
promising reason changes. This is shared behavior across
`init`/`upsert`/`applyChanges` (the JS SDK compares the whole
descriptor) and predates this PR; fixing it here would mix a behavior
change into otherwise-inert plumbing, so it is left for a separate
change.
## Testing
Mapper tests pin the live wire shape (no in-object version parses;
envelope version wins over a differing in-object value), alongside the
existing put/delete/unknown-kind coverage. Flag updater tests cover
partial transfers (no version comparison, a single change event for the
changed keys, tombstones, rejection for an inactive context), full
transfers (replacement with change events for the differences including
deletions, making the context active, setting the environment ID), and a
transfer of none leaving the store untouched and emitting nothing.
Persistence-level tests assert a partial apply writes through to the
cache, a rejected apply does not, an empty partial and a transfer of
none skip the write, and a full transfer always writes — even an empty
one.
SDK-2186
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **Medium Risk**
> Changes core flag-store update semantics for a new FDv2 path (no
per-item version on partial applies, full replace behavior); production
behavior is unchanged until callers wire it, but mistakes when
integrating could cause stale or overwritten flags.
>
> **Overview**
> Prepares the Dart common client for FDv2 by fixing **flag-eval**
parsing and adding a single entry point to apply FDv2 changesets to the
flag store (not wired from the data pipeline yet).
>
> **Flag-eval mapper:** When deserializing `flag-eval` objects, the
put-object **envelope `version`** is merged into the JSON passed to
`LDEvaluationResultSerialization.fromJson`, overriding any in-object
`version`. That matches live FDv2 wire data where objects only carry
`flagVersion`, which previously caused parse failures.
>
> **`FlagManager.applyChanges`:** New API plumbed through
`FlagPersistence` and `FlagUpdater` for `PayloadType.full`, `partial`,
and `none`. **Full** replaces all flags via the existing `init` path
(active context, diff-based change events, optional `environmentId`,
cache always written). **Partial** merges updates **without** per-item
version checks (unlike `upsert`), rejects inactive contexts, batches
change notifications, and skips cache when the update map is empty.
**None** is a no-op (store, events, cache unchanged).
>
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
268c45b. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->1 parent 43d899e commit 66c2554
8 files changed
Lines changed: 452 additions & 4 deletions
File tree
- packages/common_client
- lib/src
- data_sources/fdv2
- flag_manager
- test
- data_sources/fdv2
Lines changed: 11 additions & 4 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
22 | 22 | | |
23 | 23 | | |
24 | 24 | | |
25 | | - | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
26 | 33 | | |
27 | 34 | | |
28 | 35 | | |
29 | 36 | | |
30 | 37 | | |
31 | | - | |
32 | | - | |
33 | | - | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
34 | 41 | | |
35 | 42 | | |
36 | 43 | | |
| |||
Lines changed: 12 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
| 3 | + | |
3 | 4 | | |
4 | 5 | | |
5 | 6 | | |
| |||
55 | 56 | | |
56 | 57 | | |
57 | 58 | | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
58 | 70 | | |
59 | 71 | | |
60 | 72 | | |
| |||
Lines changed: 18 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
| 4 | + | |
4 | 5 | | |
5 | 6 | | |
6 | 7 | | |
| |||
63 | 64 | | |
64 | 65 | | |
65 | 66 | | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
66 | 84 | | |
67 | 85 | | |
68 | 86 | | |
| |||
Lines changed: 39 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
| 4 | + | |
4 | 5 | | |
5 | 6 | | |
6 | 7 | | |
| |||
101 | 102 | | |
102 | 103 | | |
103 | 104 | | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
104 | 143 | | |
105 | 144 | | |
106 | 145 | | |
| |||
Lines changed: 47 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
33 | 80 | | |
34 | 81 | | |
35 | 82 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
| 4 | + | |
4 | 5 | | |
5 | 6 | | |
6 | 7 | | |
| |||
31 | 32 | | |
32 | 33 | | |
33 | 34 | | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
34 | 179 | | |
35 | 180 | | |
36 | 181 | | |
| |||
0 commit comments