Skip to content

Commit

Permalink
feat: add fetch options with flag keys option (#23)
Browse files Browse the repository at this point in the history
  • Loading branch information
bgiori authored Jul 19, 2023
1 parent 17c64e1 commit 0d9d901
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 7 deletions.
1 change: 1 addition & 0 deletions packages/node/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export {
RemoteEvaluationDefaults,
} from './types/config';
export { Experiment } from './factory';
export { FetchOptions } from './types/fetch';
export { ExperimentUser } from './types/user';
export { Variant, Variants } from './types/variant';
export { LocalEvaluationClient } from './local/client';
Expand Down
35 changes: 28 additions & 7 deletions packages/node/src/remote/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
RemoteEvaluationDefaults,
RemoteEvaluationConfig,
} from '../types/config';
import { FetchOptions } from '../types/fetch';
import { HttpClient } from '../types/transport';
import { ExperimentUser } from '../types/user';
import { Variant, Variants } from '../types/variant';
Expand Down Expand Up @@ -37,32 +38,39 @@ export class RemoteEvaluationClient {
* This method will automatically retry if configured (default).
*
* @param user The {@link ExperimentUser} context
* @param options The {@link FetchOptions} for this specific fetch request.
* @return The {@link Variants} for the user on success, empty
* {@link Variants} on error.
*/
public async fetch(user: ExperimentUser): Promise<Variants> {
public async fetch(
user: ExperimentUser,
options?: FetchOptions,
): Promise<Variants> {
if (!this.apiKey) {
throw Error('Experiment API key is empty');
}
try {
return await this.fetchInternal(user);
return await this.fetchInternal(user, options);
} catch (e) {
console.error('[Experiment] Failed to fetch variants: ', e);
return {};
}
}

private async fetchInternal(user: ExperimentUser): Promise<Variants> {
private async fetchInternal(
user: ExperimentUser,
options?: FetchOptions,
): Promise<Variants> {
if (!this.apiKey) {
throw Error('Experiment API key is empty');
}
this.debug('[Experiment] Fetching variants for user: ', user);
try {
return await this.doFetch(user, this.config.fetchTimeoutMillis);
return await this.doFetch(user, this.config.fetchTimeoutMillis, options);
} catch (e) {
console.error('[Experiment] Fetch failed: ', e);
try {
return await this.retryFetch(user);
return await this.retryFetch(user, options);
} catch (e) {
console.error(e);
}
Expand All @@ -73,6 +81,7 @@ export class RemoteEvaluationClient {
private async doFetch(
user: ExperimentUser,
timeoutMillis: number,
options?: FetchOptions,
): Promise<Variants> {
const userContext = this.addContext(user || {});
const endpoint = `${this.config.serverUrl}/sdk/vardata`;
Expand All @@ -83,6 +92,11 @@ export class RemoteEvaluationClient {
Authorization: `Api-Key ${this.apiKey}`,
'X-Amp-Exp-User': encodedUser,
};
if (options && options.flagKeys) {
headers['X-Amp-Exp-Flag-Keys'] = Buffer.from(
JSON.stringify(options.flagKeys),
).toString('base64url');
}
this.debug('[Experiment] Fetch variants for user: ', userContext);
const response = await this.httpClient.request(
endpoint,
Expand All @@ -102,7 +116,10 @@ export class RemoteEvaluationClient {
return variants;
}

private async retryFetch(user: ExperimentUser): Promise<Variants> {
private async retryFetch(
user: ExperimentUser,
options?: FetchOptions,
): Promise<Variants> {
if (this.config.fetchRetries == 0) {
return {};
}
Expand All @@ -112,7 +129,11 @@ export class RemoteEvaluationClient {
for (let i = 0; i < this.config.fetchRetries; i++) {
await sleep(delayMillis);
try {
return await this.doFetch(user, this.config.fetchRetryTimeoutMillis);
return await this.doFetch(
user,
this.config.fetchRetryTimeoutMillis,
options,
);
} catch (e) {
console.error('[Experiment] Retry falied: ', e);
err = e;
Expand Down
9 changes: 9 additions & 0 deletions packages/node/src/types/fetch.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Options to modify the behavior of a remote evaluation fetch request.
*/
export type FetchOptions = {
/**
* Specific flag keys to evaluate and set variants for.
*/
flagKeys?: string[];
};
8 changes: 8 additions & 0 deletions packages/node/test/remote/client.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,11 @@ test('ExperimentClient.fetch, retry once, timeout first then succeed with 0 back
const variant = variants['sdk-ci-test'];
expect(variant).toEqual({ value: 'on', payload: 'payload' });
});

test('ExperimentClient.fetch, with flag keys options, success', async () => {
const client = new RemoteEvaluationClient(API_KEY, {});
const variants = await client.fetch(testUser, { flagKeys: ['sdk-ci-test'] });
expect(variants).toEqual({
'sdk-ci-test': { value: 'on', payload: 'payload' },
});
});

0 comments on commit 0d9d901

Please sign in to comment.