Skip to content

feat(javascript): add abtesting client, better init usage #784

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

Merged
merged 3 commits into from
Jul 5, 2022
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"files": [
{
"path": "packages/algoliasearch/dist/algoliasearch.umd.js",
"maxSize": "7.85KB"
"maxSize": "8.40KB"
},
{
"path": "packages/algoliasearch/dist/lite/lite.umd.js",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,100 @@ describe('api', () => {
expect(client.search).not.toBeUndefined();
});

it('provides an init method for the analytics client', () => {
expect(client.initAnalytics).not.toBeUndefined();
});
describe('init clients', () => {
it('provides an init method for the analytics client', () => {
expect(client.initAnalytics).not.toBeUndefined();
});

it('provides an init method for the personalization client', () => {
expect(client.initPersonalization).not.toBeUndefined();
it('provides an init method for the abtesting client', () => {
expect(client.initAbtesting).not.toBeUndefined();
});

it('provides an init method for the personalization client', () => {
expect(client.initPersonalization).not.toBeUndefined();
});

it('default `init` clients to the root `algoliasearch` credentials', async () => {
const abtestingClient = client.initAbtesting();
const analyticsClient = client.initAnalytics();
const personalizationClient = client.initPersonalization({
region: 'eu',
});

const res1 = (await abtestingClient.get({
path: 'abtestingClient',
})) as unknown as EchoResponse;
const res2 = (await analyticsClient.get({
path: 'analyticsClient',
})) as unknown as EchoResponse;
const res3 = (await personalizationClient.get({
path: 'personalizationClient',
})) as unknown as EchoResponse;

expect(res1.headers).toEqual(
expect.objectContaining({
'x-algolia-application-id': 'APP_ID',
'x-algolia-api-key': 'API_KEY',
})
);
expect(res2.headers).toEqual(
expect.objectContaining({
'x-algolia-application-id': 'APP_ID',
'x-algolia-api-key': 'API_KEY',
})
);
expect(res3.headers).toEqual(
expect.objectContaining({
'x-algolia-application-id': 'APP_ID',
'x-algolia-api-key': 'API_KEY',
})
);
});

it('`init` clients accept different credentials', async () => {
const abtestingClient = client.initAbtesting({
appId: 'appId1',
apiKey: 'apiKey1',
});
const analyticsClient = client.initAnalytics({
appId: 'appId2',
apiKey: 'apiKey2',
});
const personalizationClient = client.initPersonalization({
appId: 'appId3',
apiKey: 'apiKey3',
region: 'eu',
});

const res1 = (await abtestingClient.get({
path: 'abtestingClient',
})) as unknown as EchoResponse;
const res2 = (await analyticsClient.get({
path: 'analyticsClient',
})) as unknown as EchoResponse;
const res3 = (await personalizationClient.get({
path: 'personalizationClient',
})) as unknown as EchoResponse;

expect(res1.headers).toEqual(
expect.objectContaining({
'x-algolia-application-id': 'appId1',
'x-algolia-api-key': 'apiKey1',
})
);
expect(res2.headers).toEqual(
expect.objectContaining({
'x-algolia-application-id': 'appId2',
'x-algolia-api-key': 'apiKey2',
})
);
expect(res3.headers).toEqual(
expect.objectContaining({
'x-algolia-application-id': 'appId3',
'x-algolia-api-key': 'apiKey3',
})
);
});
});
});

Expand Down
2 changes: 1 addition & 1 deletion clients/algoliasearch-client-javascript/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1330,7 +1330,7 @@ __metadata:
languageName: unknown
linkType: soft

"@experimental-api-clients-automation/client-abtesting@workspace:packages/client-abtesting":
"@experimental-api-clients-automation/client-abtesting@0.7.2, @experimental-api-clients-automation/client-abtesting@workspace:packages/client-abtesting":
version: 0.0.0-use.local
resolution: "@experimental-api-clients-automation/client-abtesting@workspace:packages/client-abtesting"
dependencies:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ private void setDefaultGeneratorOptions() {
if (isAlgoliasearchClient) {
// Files used to create the package.json of the algoliasearch package
additionalProperties.put("analyticsVersion", Utils.getOpenApiToolsField("javascript", "analytics", "packageVersion"));
additionalProperties.put("abtestingVersion", Utils.getOpenApiToolsField("javascript", "abtesting", "packageVersion"));
additionalProperties.put("personalizationVersion", Utils.getOpenApiToolsField("javascript", "personalization", "packageVersion"));
additionalProperties.put("searchVersion", Utils.getOpenApiToolsField("javascript", "search", "packageVersion"));

Expand Down
44 changes: 22 additions & 22 deletions playground/javascript/node/algoliasearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,8 @@ const apiKey = process.env.ALGOLIA_SEARCH_KEY || '**** SEARCH_API_KEY *****';

const searchIndex = process.env.SEARCH_INDEX || 'test_index';
const searchQuery = process.env.SEARCH_QUERY || 'test_query';

const analyticsAppId =
process.env.ALGOLIA_APPLICATION_ID || '**** APP_ID *****';
const analyticsApiKey =
process.env.ALGOLIA_ANALYTICS_KEY || '**** ANALYTICS_API_KEY *****';

const analyticsIndex = process.env.ANALYTICS_INDEX || 'test_index';

const personalizationAppId =
process.env.ALGOLIA_APPLICATION_ID || '**** APP_ID *****';
const personalizationApiKey =
process.env.ALGOLIA_RECOMMENDATION_KEY || '**** RECOMMENDATION_API_KEY *****';

// Init client with appId and apiKey
const client = algoliasearch(appId, apiKey);
const clientLite = liteClient(appId, apiKey);
Expand Down Expand Up @@ -89,17 +78,14 @@ async function testAlgoliasearch() {
}

try {
const analyticsClient = client.initAnalytics(
analyticsAppId,
analyticsApiKey
);
const analyticsClient = client.initAnalytics();

const res = await analyticsClient.getTopFilterForAttribute({
attribute: 'myAttribute1,myAttribute2',
index: analyticsIndex,
});

console.log(`[OK analytics ]`, res);
console.log(`[OK analytics]`, res);
} catch (e) {
if (e instanceof ApiError) {
return console.log(`[${e.status}] ${e.message}`, e.stackTrace);
Expand All @@ -109,13 +95,27 @@ async function testAlgoliasearch() {
}

try {
const personalizationCilent = client.initPersonalization(
personalizationAppId,
personalizationApiKey,
'eu'
);
const abtestingClient = client.initAbtesting();

const res = await abtestingClient.getABTest({
id: 42,
});

console.log(`[OK abtesting]`, res);
} catch (e) {
if (e instanceof ApiError) {
return console.log(`[${e.status}] ${e.message}`, e.stackTrace);
}

console.log('[ERROR abtesting]', e);
}

try {
const personalizationClient = client.initPersonalization({
region: 'eu',
});

const res = await personalizationCilent.getUserTokenProfile({
const res = await personalizationClient.getUserTokenProfile({
userToken: 'wouhou',
});

Expand Down
1 change: 1 addition & 0 deletions playground/javascript/node/search.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { searchClient } from '@experimental-api-clients-automation/client-search';
import { ApiError } from '@experimental-api-clients-automation/client-common';
import dotenv from 'dotenv';
import { echoRequester } from '@experimental-api-clients-automation/requester-node-http';

dotenv.config({ path: '../../.env' });

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { createXhrRequester } from '{{{npmNamespace}}}/requester-browser-xhr';
export function algoliasearch(
appId: string,
apiKey: string,
options?: { requester?: Requester; hosts?: Host[] }
options?: CommonClientOptions
) {
{{> algoliasearch/builds/checkParameters}}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,22 +1,16 @@
import type {
AnalyticsClient,
Region as AnalyticsRegion,
} from '{{{npmNamespace}}}/client-analytics/src/analyticsClient';
import { createAnalyticsClient } from '{{{npmNamespace}}}/client-analytics/src/analyticsClient';
import type {
PersonalizationClient,
Region as PersonalizationRegion,
} from '{{{npmNamespace}}}/client-personalization/src/personalizationClient';
import { createPersonalizationClient } from '{{{npmNamespace}}}/client-personalization/src/personalizationClient';
import {
createSearchClient,
apiClientVersion as searchClientVersion,
} from '{{{npmNamespace}}}/client-search/src/searchClient';
import type {
CreateClientOptions,
Host,
Requester,
} from '{{{npmNamespace}}}/client-common';
import type { Region as AnalyticsRegion, AnalyticsClient } from '{{{npmNamespace}}}/client-analytics/src/analyticsClient';
import { createAnalyticsClient, REGIONS as analyticsRegions } from '{{{npmNamespace}}}/client-analytics/src/analyticsClient';

import type { Region as AbtestingRegion, AbtestingClient } from '{{{npmNamespace}}}/client-abtesting/src/abtestingClient';
import { createAbtestingClient, REGIONS as abtestingRegions } from '{{{npmNamespace}}}/client-abtesting/src/abtestingClient';

import type { Region as PersonalizationRegion, PersonalizationClient } from '{{{npmNamespace}}}/client-personalization/src/personalizationClient';
import { createPersonalizationClient, REGIONS as personalizationRegions } from '{{{npmNamespace}}}/client-personalization/src/personalizationClient';

import { createSearchClient, apiClientVersion as searchClientVersion } from '{{{npmNamespace}}}/client-search/src/searchClient';

import type { CreateClientOptions } from '{{{npmNamespace}}}/client-common';
import { CommonInitOptions, InitRegion, CommonClientOptions } from "./models"

export * from './models';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,39 +1,70 @@
function initAnalytics(
analyticsAppId: string,
analyticsApiKey: string,
region?: AnalyticsRegion,
analyticsOptions?: { requester?: Requester; hosts?: Host[] }
): AnalyticsClient {
function initAnalytics(initOptions: CommonInitOptions & InitRegion<AnalyticsRegion> = {}): AnalyticsClient {
if (
initOptions.region &&
(typeof initOptions.region !== 'string' ||
!analyticsRegions.includes(initOptions.region))
) {
throw new Error(
`\`region\` must be one of the following: ${analyticsRegions.join(', ')}`
);
}

return createAnalyticsClient({
appId: analyticsAppId,
apiKey: analyticsApiKey,
region,
...analyticsOptions,
...initOptions.options,
...commonOptions,
appId: initOptions.appId ?? appId,
apiKey: initOptions.apiKey ?? apiKey,
region: initOptions.region,
});
}

function initAbtesting(initOptions: CommonInitOptions & InitRegion<AbtestingRegion> = {}): AbtestingClient {
Copy link
Collaborator

Choose a reason for hiding this comment

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

is there a way to make the Required<> or not correlated with the specs ? I guess it would be very hard

Copy link
Member Author

Choose a reason for hiding this comment

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

We could leverage the hasFallback logic but since each init are unique it would be the same as having it hardcoded :/

if (
initOptions.region &&
(typeof initOptions.region !== 'string' ||
!abtestingRegions.includes(initOptions.region))
) {
throw new Error(
`\`region\` must be one of the following: ${abtestingRegions.join(', ')}`
);
}

return createAbtestingClient({
...initOptions.options,
...commonOptions,
appId: initOptions.appId ?? appId,
apiKey: initOptions.apiKey ?? apiKey,
region: initOptions.region,
});
}

function initPersonalization(
personalizationAppId: string,
personalizationApiKey: string,
region: PersonalizationRegion,
personalizationOptions?: { requester?: Requester; hosts?: Host[] }
): PersonalizationClient {
if (!region) {
function initPersonalization(initOptions: CommonInitOptions & Required<InitRegion<PersonalizationRegion>>): PersonalizationClient {
if (!initOptions.region) {
throw new Error('`region` is missing.');
}

if (
initOptions.region &&
(typeof initOptions.region !== 'string' ||
!personalizationRegions.includes(initOptions.region))
) {
throw new Error(
`\`region\` must be one of the following: ${personalizationRegions.join(', ')}`
);
}

return createPersonalizationClient({
appId: personalizationAppId,
apiKey: personalizationApiKey,
region,
...personalizationOptions,
...initOptions.options,
...commonOptions,
appId: initOptions.appId ?? appId,
apiKey: initOptions.apiKey ?? apiKey,
region: initOptions.region,
});
}

return {
...createSearchClient({ appId, apiKey, ...commonOptions }),
initAnalytics,
initPersonalization,
initAbtesting,
};
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import type {
Host,
Requester,
} from '{{{npmNamespace}}}/client-common';
import {
ErrorBase,
PutProps,
Expand All @@ -9,5 +13,27 @@ import {
export * from '{{{npmNamespace}}}/client-search/model';
export * from '{{{npmNamespace}}}/client-personalization/model';
export * from '{{{npmNamespace}}}/client-analytics/model';
export * from '{{{npmNamespace}}}/client-abtesting/model';

export { ErrorBase, PutProps, PostProps, DelProps, GetProps };

export type CommonClientOptions = { requester?: Requester; hosts?: Host[] };

export type CommonInitOptions = Partial<{
/**
* App to target with the initialized client, defaults to the `algoliasearch` appId.
*/
appId: string;
/**
* API key of the targeted app ID, defaults to the `algoliasearch` apiKey.
*/
apiKey: string;
options: CommonClientOptions;
}>;

export type InitRegion<TRegion> = Partial<{
/**
* Available regions of the initialized client.
*/
region: TRegion;
}>;
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { createHttpRequester } from '{{{npmNamespace}}}/requester-node-http';
export function algoliasearch(
appId: string,
apiKey: string,
options?: { requester?: Requester; hosts?: Host[] }
options?: CommonClientOptions
) {
{{> algoliasearch/builds/checkParameters}}

Expand Down
Loading