Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: implement identify #304

Merged
merged 72 commits into from
Oct 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
b95bbaf
chore: initial commit
yusinto Jun 27, 2023
4bf4d3c
chore: added createSafeLogger
yusinto Jul 5, 2023
10fb3c2
chore: add LDInspection types. Band aid ts errors for now. Fix export…
yusinto Jul 11, 2023
d29a840
chore: added TypedEventTarget
yusinto Jul 12, 2023
cb891ab
chore: move TypedEventTarget to sdk-client. It's incomplete.
yusinto Jul 13, 2023
147c5da
chore: complete EventTarget dom implementation
yusinto Jul 13, 2023
f539c7a
chore: improved LDEmitter (#207)
yusinto Jul 18, 2023
121b872
Merge branch 'main' into yus/sc-208024/scaffold-js-common-in-js-core
yusinto Jul 18, 2023
080e5c8
Merge branch 'main' into yus/sc-208024/scaffold-js-common-in-js-core
yusinto Jul 19, 2023
4f416b3
fix: eslint errors
yusinto Jul 19, 2023
5c9226f
chore: add prettier and check commands
yusinto Jul 19, 2023
b78c95f
feat: Add migrationVariation method. (#212)
kinyoklion Jul 24, 2023
b48f847
feat: Add migration configuration and basic migration. (#213)
kinyoklion Jul 26, 2023
72c0f53
feat: Add support for payloads to read and write methods. (#215)
kinyoklion Jul 28, 2023
36fef4f
feat: Add support for exclude from summaries. (#216)
kinyoklion Jul 28, 2023
59b8b6f
Merge branch 'main' into feat/node-migrations
kinyoklion Jul 31, 2023
bee4e74
chore: Prettier.
kinyoklion Jul 31, 2023
d32dcc8
Merge branch 'main' into yus/sc-208024/scaffold-js-common-in-js-core
yusinto Aug 1, 2023
965376d
Merge branch 'main' into yus/sc-208024/scaffold-js-common-in-js-core
yusinto Aug 1, 2023
42f48b3
fix: ran prettier
yusinto Aug 1, 2023
2a1eb6f
feat: Add migration operation input event and tracker. (#214)
kinyoklion Aug 1, 2023
87095c9
feat: Implement migration op event and connect to tracking. (#218)
kinyoklion Aug 1, 2023
8655ed3
feat: configuration and options validation (#221)
yusinto Aug 2, 2023
a2154cc
feat: Adjustments to spec and enhanced event validation. (#222)
kinyoklion Aug 3, 2023
ababe74
feat: Add configuration overrides and metrics data kinds. (#220)
kinyoklion Aug 3, 2023
f750bd5
feat: Add custom event support to tracker. (#227)
kinyoklion Aug 4, 2023
7e8fe03
Merge branch 'main' into yus/sc-208024/scaffold-js-common-in-js-core
Aug 10, 2023
9a6bd82
Merge branch 'main' into feat/node-migrations
kinyoklion Aug 16, 2023
02e92a4
feat: Remove custom events. (#243)
kinyoklion Aug 16, 2023
f9b4e6e
feat: Event sampling. (#245)
kinyoklion Aug 23, 2023
ab61937
chore: Add execution order support for contract tests. (#247)
kinyoklion Aug 23, 2023
bd95697
feat: Change migration variation to support forwarding the sampling r…
kinyoklion Aug 23, 2023
ee62381
feat: New data kinds for edge SDKs. (#260)
kinyoklion Aug 28, 2023
ee4ebbf
feat: Add invoked measurement. (#258)
kinyoklion Aug 30, 2023
760567d
chore: Support migration payload contract tests. (#262)
kinyoklion Aug 30, 2023
e12068a
chore: Add initial documentation. (#263)
kinyoklion Aug 30, 2023
01fd110
Merge branch 'main' into rlamb/merge-perf-updates-migrations
kinyoklion Aug 31, 2023
f7ed7eb
fix sampling
kinyoklion Aug 31, 2023
10c4875
fix merging event sampling
kinyoklion Aug 31, 2023
7c08e82
feat: Refactor variation method and consistency tracking. (#264)
kinyoklion Aug 31, 2023
e56e18f
Merge branch 'feat/node-migrations' into rlamb/merge-perf-updates-mig…
kinyoklion Aug 31, 2023
d7b8170
Merge branch 'main' into feat/node-migrations
kinyoklion Aug 31, 2023
cadc90b
fix: Fix double call when platform support performance API. (#268)
kinyoklion Sep 5, 2023
11179ba
fix: Fix log messages for failed migration creation. (#274)
kinyoklion Sep 11, 2023
5ff85e4
chore: Use new granular categories for event sampling. (#277)
kinyoklion Sep 13, 2023
d1f7197
fix: Handle exceptions thrown in the comparison function. (#278)
kinyoklion Sep 15, 2023
5b1a4d3
feat: reuse EventProcessor for dom (#228)
yusinto Sep 19, 2023
55f8e72
Merge branch 'main' into yus/sc-208024/scaffold-js-common-in-js-core
yusinto Sep 19, 2023
37b33fb
chore: fixed conflicts
yusinto Sep 19, 2023
f97935e
chore: remove duplicate stream processor tests.
yusinto Sep 19, 2023
71ee878
chore: fixed missing mocks. prefix and add bash directive.
yusinto Sep 19, 2023
ca881de
chore: add bash directive to shell scripts
yusinto Sep 21, 2023
703f56d
Update package.json
yusinto Sep 21, 2023
fe852fb
Merge branch 'main' into yus/sc-208024/scaffold-js-common-in-js-core
yusinto Sep 21, 2023
c120948
feat: Do not generate an event if the measurements are inconsistent. …
kinyoklion Sep 22, 2023
9c5f402
feat: Add flag version to migration op event. (#281)
kinyoklion Sep 22, 2023
f3481a4
feat: Use a factory method to create migrations. (#283)
kinyoklion Sep 25, 2023
dda6b37
feat: No migration op event for empty flag key. (#287)
kinyoklion Sep 27, 2023
8e96a52
feat: Add typed variation methods. (#288)
kinyoklion Sep 27, 2023
2ada20d
fix: refactor mocks to its own project (#284)
yusinto Oct 2, 2023
837c4e1
fix: Include flag version for WRONG_TYPE migrations. (#290)
kinyoklion Oct 2, 2023
c3f2d88
feat: Remove index and custom event sampling. (#289)
kinyoklion Oct 2, 2023
b22eef4
fix: yarn topological dev build (#291)
yusinto Oct 2, 2023
4ed3c76
feat: Client refactor and Migrations.
kinyoklion Oct 3, 2023
a3f64af
fix unit tests post merge
kinyoklion Oct 3, 2023
bacb607
Merge branch 'main' into feat/merge-client-and-migrations
kinyoklion Oct 3, 2023
a0dac0d
Remove config and metric kind from contract tests.
kinyoklion Oct 3, 2023
8f34dfa
chore: implement initial flag fetch (#294)
yusinto Oct 10, 2023
fc2c212
chore: implement variation functions (#298)
yusinto Oct 13, 2023
f65707b
chore: remove hardcoded path in streamuri (#299)
yusinto Oct 13, 2023
b7702f2
chore: draft implementation of identify
yusinto Oct 18, 2023
7b55d73
Merge branch 'main' into yus/sc-220941/implement-identify
yusinto Oct 18, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 51 additions & 12 deletions packages/shared/sdk-client/src/LDClientImpl.test.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,35 @@
import { LDContext } from '@launchdarkly/js-sdk-common';
import { basicPlatform } from '@launchdarkly/private-js-mocks';
import { basicPlatform, logger } from '@launchdarkly/private-js-mocks';

import LDEmitter from './api/LDEmitter';
import fetchFlags from './evaluation/fetchFlags';
import * as mockResponseJson from './evaluation/mockResponse.json';
import LDClientImpl from './LDClientImpl';

jest.mock('./evaluation/fetchFlags', () => {
const actual = jest.requireActual('./evaluation/fetchFlags');
return {
__esModule: true,
...actual,
default: jest.fn(),
};
});
jest.mock('./api/LDEmitter');
jest.mock('./evaluation/fetchFlags');

describe('sdk-client object', () => {
const testSdkKey = 'test-sdk-key';
const context: LDContext = { kind: 'org', key: 'Testy Pizza' };
const mockFetchFlags = fetchFlags as jest.Mock;

let ldc: LDClientImpl;
let mockEmitter: LDEmitter;

beforeEach(async () => {
beforeEach(() => {
mockFetchFlags.mockResolvedValue(mockResponseJson);

ldc = new LDClientImpl(testSdkKey, context, basicPlatform, {});
await ldc.start();
ldc = new LDClientImpl(testSdkKey, context, basicPlatform, { logger });
[mockEmitter] = (LDEmitter as jest.Mock).mock.instances;
});

afterEach(() => {
jest.resetAllMocks();
});

test('instantiate with blank options', () => {
ldc = new LDClientImpl(testSdkKey, context, basicPlatform, {});
expect(ldc.config).toMatchObject({
allAttributesPrivate: false,
baseUri: 'https://sdk.launchdarkly.com',
Expand Down Expand Up @@ -61,6 +62,7 @@ describe('sdk-client object', () => {
});

test('all flags', async () => {
await ldc.start();
const all = ldc.allFlags();

expect(all).toEqual({
Expand All @@ -76,8 +78,45 @@ describe('sdk-client object', () => {
});

test('variation', async () => {
await ldc.start();
const devTestFlag = ldc.variation('dev-test-flag');

expect(devTestFlag).toBe(true);
});

test('identify success', async () => {
mockResponseJson['dev-test-flag'].value = false;
mockFetchFlags.mockResolvedValue(mockResponseJson);
const carContext: LDContext = { kind: 'car', key: 'mazda-cx7' };

await ldc.identify(carContext);
const c = ldc.getContext();
const all = ldc.allFlags();

expect(carContext).toEqual(c);
expect(all).toMatchObject({
'dev-test-flag': false,
});
});

test('identify error invalid context', async () => {
// @ts-ignore
const carContext: LDContext = { kind: 'car', key: undefined };

await expect(ldc.identify(carContext)).rejects.toThrowError(/no key/);
expect(logger.error).toBeCalledTimes(1);
expect(mockEmitter.emit).toHaveBeenNthCalledWith(1, 'error', expect.any(Error));
expect(ldc.getContext()).toEqual(context);
});

test('identify error fetch error', async () => {
// @ts-ignore
mockFetchFlags.mockRejectedValue(new Error('unknown test fetch error'));
const carContext: LDContext = { kind: 'car', key: 'mazda-3' };

await expect(ldc.identify(carContext)).rejects.toThrowError(/fetch error/);
expect(logger.error).toBeCalledTimes(1);
expect(mockEmitter.emit).toHaveBeenNthCalledWith(1, 'error', expect.any(Error));
expect(ldc.getContext()).toEqual(context);
});
});
37 changes: 21 additions & 16 deletions packages/shared/sdk-client/src/LDClientImpl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,14 @@ export default class LDClientImpl implements LDClient {
*/
constructor(
public readonly sdkKey: string,
public readonly context: LDContext,
public context: LDContext,
public readonly platform: Platform,
options: LDOptions,
) {
if (!sdkKey) {
throw new Error('You must configure the client with a client-side SDK key');
}

const checkedContext = Context.fromLDContext(context);
if (!checkedContext.valid) {
throw new Error('Context was unspecified or had no key');
}

if (!platform.encoding) {
throw new Error('Platform must implement Encoding because btoa is required.');
}
Expand All @@ -78,12 +73,11 @@ export default class LDClientImpl implements LDClient {

async start() {
try {
this.flags = await fetchFlags(this.sdkKey, this.context, this.config, this.platform);
await this.identify(this.context);
this.emitter.emit('ready');
} catch (error: any) {
this.logger.error(error);
this.emitter.emit('error', error);
this.emitter.emit('failed', error);
throw error;
}
}

Expand Down Expand Up @@ -113,13 +107,24 @@ export default class LDClientImpl implements LDClient {
return clone(this.context);
}

identify(
context: LDContext,
hash?: string,
onDone?: (err: Error | null, flags: LDFlagSet | null) => void,
): Promise<LDFlagSet> {
// TODO:
return Promise.resolve({});
// TODO: implement secure mode
async identify(context: LDContext, hash?: string): Promise<void> {
const checkedContext = Context.fromLDContext(context);
if (!checkedContext.valid) {
const error = new Error('Context was unspecified or had no key');
this.logger.error(error);
this.emitter.emit('error', error);
throw error;
}

try {
this.flags = await fetchFlags(this.sdkKey, context, this.config, this.platform);
this.context = context;
} catch (error: any) {
this.logger.error(error);
this.emitter.emit('error', error);
throw error;
}
}

off(eventName: EventName, listener?: Function): void {
Expand Down
15 changes: 2 additions & 13 deletions packages/shared/sdk-client/src/api/LDClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,21 +89,10 @@ export interface LDClient {
* The context properties. Must contain at least the `key` property.
* @param hash
* The signed context key if you are using [Secure Mode](https://docs.launchdarkly.com/sdk/features/secure-mode#configuring-secure-mode-in-the-javascript-client-side-sdk).
* @param onDone
* A function which will be called as soon as the flag values for the new context are available,
* with two parameters: an error value (if any), and an {@link LDFlagSet} containing the new values
* (which can also be obtained by calling {@link variation}). If the callback is omitted, you will
* receive a Promise instead.
* @returns
* If you provided a callback, then nothing. Otherwise, a Promise which resolve once the flag
* values for the new context are available, providing an {@link LDFlagSet} containing the new values
* (which can also be obtained by calling {@link variation}).
* A Promise which resolve once the flag values for the new context are available.
*/
identify(
context: LDContext,
hash?: string,
onDone?: (err: Error | null, flags: LDFlagSet | null) => void,
): Promise<LDFlagSet>;
identify(context: LDContext, hash?: string): Promise<void>;

/**
* Returns the client's current context.
Expand Down