Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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",
"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