Skip to content

Commit 0cc986f

Browse files
committed
Clarify scoped and base flag override APIs
1 parent c921b04 commit 0cc986f

3 files changed

Lines changed: 88 additions & 26 deletions

File tree

packages/node-sdk/README.md

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -565,19 +565,19 @@ REFLAG_FLAGS_DISABLED=flag3,flag4
565565

566566
1. Programmatically through the client options:
567567

568-
You can use a simple `Record<string, boolean>` and pass it either in the constructor or by setting `client.flagOverrides`:
568+
You can use a simple `Record<string, boolean>` and pass it either in the constructor or by replacing the client's base overrides with `setFlagOverrides()`:
569569

570570
```typescript
571571
// pass directly in the constructor
572572
const client = new ReflagClient({ flagOverrides: { myFlag: true } });
573-
// or set on the client at a later time
574-
client.flagOverrides = { myFlag: false };
573+
// or replace the base overrides at a later time
574+
client.setFlagOverrides({ myFlag: false });
575575

576-
// clear flag overrides. Same as setting to {}.
576+
// clear only the base overrides
577577
client.clearFlagOverrides();
578578
```
579579

580-
For nested or temporary test overrides, use `pushFlagOverrides()`. It layers on top of the current overrides and returns a restore function that removes only that layer. You can wrap that in a small helper:
580+
`pushFlagOverrides()` serves a different purpose: it adds a temporary layer on top of the base overrides and returns a restore function that removes only that layer. This is useful for nested tests:
581581

582582
```typescript
583583
export const flag = function (name: string, enabled: boolean): void {
@@ -606,6 +606,13 @@ describe("foo", () => {
606606
});
607607
```
608608

609+
The precedence is:
610+
611+
1. Base overrides from the constructor or `setFlagOverrides()`
612+
2. Temporary layers added by `pushFlagOverrides()`
613+
614+
If the same flag is set in both places, the pushed override wins until its restore function is called.
615+
609616
To get dynamic overrides, use a function which takes a context and returns a boolean or an object with the shape of `{isEnabled, config}`:
610617

611618
```typescript

packages/node-sdk/src/client.ts

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -416,28 +416,35 @@ export class ReflagClient {
416416
}
417417

418418
/**
419-
* Sets the flag overrides.
419+
* Replaces the base flag overrides used by the client.
420420
*
421421
* @param overrides - The flag overrides.
422422
*
423423
* @remarks
424-
* The flag overrides are used to override the flag definitions.
425-
* This is useful for testing or development.
424+
* Base overrides are always applied before any temporary layers added through
425+
* `pushFlagOverrides()`.
426426
*
427427
* @example
428428
* ```ts
429-
* client.flagOverrides = {
429+
* client.setFlagOverrides({
430430
* "flag-1": true,
431431
* "flag-2": false,
432-
* };
432+
* });
433433
* ```
434434
**/
435-
set flagOverrides(overrides: FlagOverridesFn | FlagOverrides) {
435+
setFlagOverrides(overrides: FlagOverridesFn | FlagOverrides) {
436436
this.baseFlagOverrides = normalizeFlagOverrides(overrides);
437-
this.flagOverrideLayers = [];
438437
this.syncFlagOverrides();
439438
}
440439

440+
/**
441+
* @deprecated
442+
* Use `setFlagOverrides()` for replacing the base override set.
443+
**/
444+
set flagOverrides(overrides: FlagOverridesFn | FlagOverrides) {
445+
this.setFlagOverrides(overrides);
446+
}
447+
441448
/**
442449
* Temporarily layers flag overrides on top of the current overrides.
443450
*
@@ -493,21 +500,18 @@ export class ReflagClient {
493500
}
494501

495502
/**
496-
* Clears the flag overrides.
503+
* Clears the base flag overrides.
497504
*
498505
* @remarks
499-
* This is useful for testing or development.
506+
* This does not affect temporary layers added with `pushFlagOverrides()`.
500507
*
501508
* @example
502509
* ```ts
503-
* afterAll(() => {
504-
* client.clearFlagOverrides();
505-
* });
510+
* client.clearFlagOverrides();
506511
* ```
507512
**/
508513
clearFlagOverrides() {
509514
this.baseFlagOverrides = () => ({});
510-
this.flagOverrideLayers = [];
511515
this.syncFlagOverrides();
512516
}
513517

packages/node-sdk/test/client.test.ts

Lines changed: 59 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1748,7 +1748,7 @@ describe("ReflagClient", () => {
17481748
);
17491749
});
17501750

1751-
it("should use flag overrides", async () => {
1751+
it("should use base flag overrides", async () => {
17521752
await client.initialize();
17531753
const context = { user, company, other: otherContext };
17541754

@@ -1773,9 +1773,9 @@ describe("ReflagClient", () => {
17731773
},
17741774
});
17751775

1776-
client.flagOverrides = {
1776+
client.setFlagOverrides({
17771777
flag1: false,
1778-
};
1778+
});
17791779
const flags = client.getFlags(context);
17801780

17811781
expect(flags).toStrictEqual({
@@ -1809,7 +1809,7 @@ describe("ReflagClient", () => {
18091809
});
18101810
});
18111811

1812-
it("should use flag overrides from function", async () => {
1812+
it("should use base flag overrides from function", async () => {
18131813
await client.initialize();
18141814
const context = { user, company, other: otherContext };
18151815

@@ -1834,7 +1834,7 @@ describe("ReflagClient", () => {
18341834
},
18351835
});
18361836

1837-
client.flagOverrides = (_context: Context) => {
1837+
client.setFlagOverrides((_context: Context) => {
18381838
expect(context).toStrictEqual(context);
18391839
return {
18401840
flag1: { isEnabled: false },
@@ -1847,7 +1847,7 @@ describe("ReflagClient", () => {
18471847
},
18481848
},
18491849
};
1850-
};
1850+
});
18511851
const flags = client.getFlags(context);
18521852

18531853
expect(flags).toStrictEqual({
@@ -1911,9 +1911,9 @@ describe("ReflagClient", () => {
19111911
await client.initialize();
19121912
const context = { user, company, other: otherContext };
19131913

1914-
client.flagOverrides = () => ({
1914+
client.setFlagOverrides(() => ({
19151915
flag1: false,
1916-
});
1916+
}));
19171917

19181918
const restore = client.pushFlagOverrides((overrideContext: Context) => {
19191919
expect(overrideContext).toStrictEqual({
@@ -1936,6 +1936,57 @@ describe("ReflagClient", () => {
19361936
expect(client.getFlag(context, "flag1").isEnabled).toBe(false);
19371937
expect(client.getFlag(context, "flag2").isEnabled).toBe(false);
19381938
});
1939+
1940+
it("should keep pushed overrides layered on top when replacing base overrides", async () => {
1941+
await client.initialize();
1942+
const context = { user, company, other: otherContext };
1943+
1944+
client.setFlagOverrides({
1945+
flag1: false,
1946+
});
1947+
1948+
const restore = client.pushFlagOverrides({
1949+
flag1: true,
1950+
});
1951+
1952+
expect(client.getFlag(context, "flag1").isEnabled).toBe(true);
1953+
1954+
client.setFlagOverrides({
1955+
flag1: false,
1956+
flag2: true,
1957+
});
1958+
1959+
expect(client.getFlag(context, "flag1").isEnabled).toBe(true);
1960+
expect(client.getFlag(context, "flag2").isEnabled).toBe(true);
1961+
1962+
restore();
1963+
1964+
expect(client.getFlag(context, "flag1").isEnabled).toBe(false);
1965+
expect(client.getFlag(context, "flag2").isEnabled).toBe(true);
1966+
});
1967+
1968+
it("should only clear the base overrides", async () => {
1969+
await client.initialize();
1970+
const context = { user, company, other: otherContext };
1971+
1972+
client.setFlagOverrides({
1973+
flag1: false,
1974+
});
1975+
1976+
const restore = client.pushFlagOverrides({
1977+
flag2: true,
1978+
});
1979+
1980+
client.clearFlagOverrides();
1981+
1982+
expect(client.getFlag(context, "flag1").isEnabled).toBe(true);
1983+
expect(client.getFlag(context, "flag2").isEnabled).toBe(true);
1984+
1985+
restore();
1986+
1987+
expect(client.getFlag(context, "flag1").isEnabled).toBe(true);
1988+
expect(client.getFlag(context, "flag2").isEnabled).toBe(false);
1989+
});
19391990
});
19401991

19411992
describe("getFlagsForBootstrap", () => {

0 commit comments

Comments
 (0)