Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion packages/node/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"dependencies": {
"@amplitude/analytics-node": "^1.3.4",
"@amplitude/analytics-types": "^1.3.1",
"@amplitude/experiment-core": "^0.7.2",
"@amplitude/experiment-core": "^0.11.1",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to use 0.12.0 version?

"eventsource": "^2.0.2"
}
}
18 changes: 16 additions & 2 deletions packages/node/src/remote/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
FetchError,
SdkEvaluationApi,
} from '@amplitude/experiment-core';
import { GetVariantsOptions } from '@amplitude/experiment-core/dist/types/src/api/evaluation-api';

import { version as PACKAGE_VERSION } from '../../gen/version';
import { FetchHttpClient, WrapperClient } from '../transport/http';
Expand Down Expand Up @@ -113,10 +114,23 @@ export class RemoteEvaluationClient {
options?: FetchOptions,
): Promise<Record<string, Variant>> {
const userContext = this.addContext(user || {});
const results = await this.evaluationApi.getVariants(userContext, {
const getVariantsOptions: GetVariantsOptions = {
flagKeys: options?.flagKeys,
timeoutMillis: timeoutMillis,
});
};
if (options?.tracksAssignment) {
getVariantsOptions.trackingOption = options?.tracksAssignment
? 'track'
: 'no-track';
}
if (options?.tracksExposure) {
(getVariantsOptions as any).exposureTrackingOption =
options?.tracksExposure ? 'track' : 'no-track';
}
const results = await this.evaluationApi.getVariants(
userContext,
getVariantsOptions,
);
this.logger.debug('[Experiment] Fetched variants: ', results);
return evaluationVariantsToVariants(results);
}
Expand Down
10 changes: 10 additions & 0 deletions packages/node/src/types/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,14 @@ export type FetchOptions = {
* Specific flag keys to evaluate and set variants for.
*/
flagKeys?: string[];

/**
* Whether to track exposure events for the request.
*/
tracksExposure?: boolean;

/**
* Whether to track assignment events for the request.
*/
tracksAssignment?: boolean;
};
117 changes: 72 additions & 45 deletions packages/node/test/remote/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,59 +6,86 @@ const API_KEY = 'server-qz35UwzJ5akieoAdIgzM4m9MIiOLXLoz';

const testUser: ExperimentUser = { user_id: 'test_user' };

test('ExperimentClient.fetch, success', async () => {
const client = new RemoteEvaluationClient(API_KEY, {});
const variants = await client.fetch(testUser);
const variant = variants['sdk-ci-test'];
delete variant.metadata;
expect(variant).toEqual({ key: 'on', value: 'on', payload: 'payload' });
});
describe('ExperimentClient.fetch', () => {
beforeEach(() => {
jest.clearAllMocks();
jest.resetAllMocks();
jest.restoreAllMocks();
});

test('ExperimentClient.fetch, no retries, timeout failure', async () => {
const client = new RemoteEvaluationClient(API_KEY, {
fetchRetries: 0,
fetchTimeoutMillis: 0,
test('ExperimentClient.fetch, success', async () => {
const client = new RemoteEvaluationClient(API_KEY, {});
const variants = await client.fetch(testUser);
const variant = variants['sdk-ci-test'];
delete variant.metadata;
expect(variant).toEqual({ key: 'on', value: 'on', payload: 'payload' });
});
const variants = await client.fetch(testUser);
expect(variants).toEqual({});
});

test('ExperimentClient.fetch, no retries, timeout failure, retry success', async () => {
const client = new RemoteEvaluationClient(API_KEY, {
fetchRetries: 1,
fetchTimeoutMillis: 0,
test('ExperimentClient.fetch, no retries, timeout failure', async () => {
const client = new RemoteEvaluationClient(API_KEY, {
fetchRetries: 0,
fetchTimeoutMillis: 0,
});
const variants = await client.fetch(testUser);
expect(variants).toEqual({});
});
const variants = await client.fetch(testUser);
const variant = variants['sdk-ci-test'];
delete variant.metadata;
expect(variant).toEqual({ key: 'on', value: 'on', payload: 'payload' });
});

test('ExperimentClient.fetch, retry once, timeout first then succeed with 0 backoff', async () => {
const client = new RemoteEvaluationClient(API_KEY, {
fetchTimeoutMillis: 0,
fetchRetries: 1,
fetchRetryBackoffMinMillis: 0,
fetchRetryTimeoutMillis: 10_000,
test('ExperimentClient.fetch, no retries, timeout failure, retry success', async () => {
const client = new RemoteEvaluationClient(API_KEY, {
fetchRetries: 1,
fetchTimeoutMillis: 0,
});
const variants = await client.fetch(testUser);
const variant = variants['sdk-ci-test'];
delete variant.metadata;
expect(variant).toEqual({ key: 'on', value: 'on', payload: 'payload' });
});
const variants = await client.fetch(testUser);
const variant = variants['sdk-ci-test'];
delete variant.metadata;
expect(variant).toEqual({ key: 'on', value: 'on', payload: 'payload' });
});

test('ExperimentClient.fetch, v1 off returns undefined', async () => {
const client = new RemoteEvaluationClient(API_KEY, {});
const variant = (await client.fetch({}))['sdk-ci-test'];
expect(variant).toBeUndefined();
});
test('ExperimentClient.fetch, retry once, timeout first then succeed with 0 backoff', async () => {
const client = new RemoteEvaluationClient(API_KEY, {
fetchTimeoutMillis: 0,
fetchRetries: 1,
fetchRetryBackoffMinMillis: 0,
fetchRetryTimeoutMillis: 10_000,
});
const variants = await client.fetch(testUser);
const variant = variants['sdk-ci-test'];
delete variant.metadata;
expect(variant).toEqual({ key: 'on', value: 'on', payload: 'payload' });
});

test('ExperimentClient.fetch, v1 off returns undefined', async () => {
const client = new RemoteEvaluationClient(API_KEY, {});
const variant = (await client.fetch({}))['sdk-ci-test'];
expect(variant).toBeUndefined();
});

test('ExperimentClient.fetch, v2 off returns default variant', async () => {
const client = new RemoteEvaluationClient(API_KEY, {});
const variant = (await client.fetchV2({}))['sdk-ci-test'];
expect(variant.key).toEqual('off');
expect(variant.value).toBeUndefined();
expect(variant.metadata.default).toEqual(true);
test('ExperimentClient.fetch, v2 off returns default variant', async () => {
const client = new RemoteEvaluationClient(API_KEY, {});
const variant = (await client.fetchV2({}))['sdk-ci-test'];
expect(variant.key).toEqual('off');
expect(variant.value).toBeUndefined();
expect(variant.metadata.default).toEqual(true);
});

test('ExperimentClient.fetch, v2 tracksAssignment and tracksExposure', async () => {
const client = new RemoteEvaluationClient(API_KEY, {});
const getVariantsSpy = jest.spyOn(
(client as any).evaluationApi,
'getVariants',
);
const variants = await client.fetchV2(testUser, {
tracksAssignment: true,
tracksExposure: true,
});
expect(getVariantsSpy).toHaveBeenCalledWith(
expect.objectContaining(testUser),
expect.objectContaining({
trackingOption: 'track',
exposureTrackingOption: 'track',
}),
);
});
});

describe('ExperimentClient.fetch, retry with different response codes', () => {
Expand Down
Loading