Skip to content

Commit aa0715f

Browse files
authored
feat: Add Insights client specs + javascript insights client (#62)
1 parent aad17bb commit aa0715f

36 files changed

+1749
-376
lines changed

.github/actions/cache/action.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ runs:
3939
path: /home/runner/work/api-clients-automation/api-clients-automation/clients/algoliasearch-client-javascript/client-analytics/dist
4040
key: ${{ runner.os }}-js-client-analytics-${{ hashFiles('clients/algoliasearch-client-javascript/client-analytics/**') }}
4141

42+
- name: Restore built JavaScript insights client
43+
if: ${{ inputs.job == 'cts' }}
44+
uses: actions/cache@v2
45+
with:
46+
path: /home/runner/work/api-clients-automation/api-clients-automation/clients/algoliasearch-client-javascript/client-insights/dist
47+
key: ${{ runner.os }}-js-client-insights-${{ hashFiles('clients/algoliasearch-client-javascript/client-insights/**') }}
48+
4249
- name: Restore built Java client
4350
if: ${{ inputs.job == 'cts' }}
4451
uses: actions/cache@v2

.github/actions/setup/action.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ runs:
4646
echo "::set-output name=JS_RECOMMEND_CLIENT_CHANGED::$(git diff --shortstat origin/${{ github.base_ref }}..HEAD -- clients/algoliasearch-client-javascript/recommend | wc -l)"
4747
echo "::set-output name=JS_PERSO_CLIENT_CHANGED::$(git diff --shortstat origin/${{ github.base_ref }}..HEAD -- clients/algoliasearch-client-javascript/client-personalization | wc -l)"
4848
echo "::set-output name=JS_ANALYTICS_CLIENT_CHANGED::$(git diff --shortstat origin/${{ github.base_ref }}..HEAD -- clients/algoliasearch-client-javascript/client-analytics | wc -l)"
49+
echo "::set-output name=JS_INSIGHTS_CLIENT_CHANGED::$(git diff --shortstat origin/${{ github.base_ref }}..HEAD -- clients/algoliasearch-client-javascript/client-insights | wc -l)"
4950
echo "::set-output name=JS_TEMPLATE_CHANGED::$(git diff --shortstat origin/${{ github.base_ref }}..HEAD -- templates/javascript | wc -l)"
5051
5152
echo "::set-output name=JAVA_CLIENT_CHANGED::$(git diff --shortstat origin/${{ github.base_ref }}..HEAD -- clients/algoliasearch-client-java-2 | wc -l)"
@@ -88,6 +89,9 @@ outputs:
8889
RUN_JS_CLIENT_ANALYTICS:
8990
description: Determine if the `client_javascript_analytics` job should run
9091
value: ${{ github.ref == 'refs/heads/main' || steps.diff.outputs.GITHUB_ACTIONS_CHANGED > 0 || steps.diff.outputs.COMMON_SPECS_CHANGED > 0 || steps.diff.outputs.ANALYTICS_SPECS_CHANGED > 0 || steps.diff.outputs.SCRIPTS_CHANGED > 0 || steps.diff.outputs.JS_ANALYTICS_CLIENT_CHANGED > 0 || steps.diff.outputs.JS_TEMPLATE_CHANGED > 0 }}
92+
RUN_JS_CLIENT_INSIGHTS:
93+
description: Determine if the `client_javascript_insights` job should run
94+
value: ${{ github.ref == 'refs/heads/main' || steps.diff.outputs.GITHUB_ACTIONS_CHANGED > 0 || steps.diff.outputs.COMMON_SPECS_CHANGED > 0 || steps.diff.outputs.INSIGHTS_SPECS_CHANGED > 0 || steps.diff.outputs.SCRIPTS_CHANGED > 0 || steps.diff.outputs.JS_INSIGHTS_CLIENT_CHANGED > 0 || steps.diff.outputs.JS_TEMPLATE_CHANGED > 0 }}
9195

9296
# java client variables
9397
RUN_JAVA_CLIENT:

.github/workflows/check.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ jobs:
3838
RUN_JS_CLIENT_RECOMMEND: ${{ steps.setup.outputs.RUN_JS_CLIENT_RECOMMEND }}
3939
RUN_JS_CLIENT_PERSO: ${{ steps.setup.outputs.RUN_JS_CLIENT_PERSO }}
4040
RUN_JS_CLIENT_ANALYTICS: ${{ steps.setup.outputs.RUN_JS_CLIENT_ANALYTICS }}
41+
RUN_JS_CLIENT_INSIGHTS: ${{ steps.setup.outputs.RUN_JS_CLIENT_INSIGHTS }}
4142

4243
RUN_JAVA_CLIENT: ${{ steps.setup.outputs.RUN_JAVA_CLIENT }}
4344

@@ -107,6 +108,22 @@ jobs:
107108
- name: Lint analytics specs
108109
run: yarn eslint --ext=yml specs/analytics
109110

111+
specs_insights:
112+
runs-on: ubuntu-20.04
113+
needs: setup
114+
if: ${{ always() && needs.setup.outputs.RUN_SPECS_INSIGHTS == 'true' }}
115+
steps:
116+
- uses: actions/checkout@v2
117+
118+
- name: Restore cache
119+
uses: ./.github/actions/cache
120+
121+
- name: Checking insights specs
122+
run: yarn build:specs insights
123+
124+
- name: Lint insights specs
125+
run: yarn eslint --ext=yml specs/insights
126+
110127
client_javascript_search:
111128
runs-on: ubuntu-20.04
112129
needs: [specs_search]
@@ -207,6 +224,31 @@ jobs:
207224
if: steps.cache.outputs.cache-hit != 'true'
208225
run: yarn build:clients javascript analytics
209226

227+
client_javascript_insights:
228+
runs-on: ubuntu-20.04
229+
needs: [specs_insights]
230+
if: ${{ always() && needs.setup.outputs.RUN_JS_CLIENT_INSIGHTS == 'true' }}
231+
steps:
232+
- uses: actions/checkout@v2
233+
234+
- name: Restore cache
235+
uses: ./.github/actions/cache
236+
237+
- name: Cache insights client
238+
id: cache
239+
uses: actions/cache@v2
240+
with:
241+
path: /home/runner/work/api-clients-automation/api-clients-automation/clients/algoliasearch-client-javascript/client-insights/dist
242+
key: ${{ runner.os }}-js-client-insights-${{ hashFiles('clients/algoliasearch-client-javascript/client-insights/**') }}
243+
244+
- name: Generate insights client
245+
if: steps.cache.outputs.cache-hit != 'true'
246+
run: yarn generate javascript insights
247+
248+
- name: Build insights client
249+
if: steps.cache.outputs.cache-hit != 'true'
250+
run: yarn build:clients javascript insights
251+
210252
client_java_search:
211253
runs-on: ubuntu-20.04
212254
needs: [specs_search]
@@ -241,6 +283,7 @@ jobs:
241283
- client_javascript_recommend
242284
- client_javascript_perso
243285
- client_javascript_analytics
286+
- client_javascript_insights
244287
- client_java_search
245288

246289
if: ${{ always() && needs.setup.outputs.RUN_CTS == 'true' }}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
node_modules
2+
dist
3+
.openapi-generator
4+
.env
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# OpenAPI Generator Ignore
2+
# Generated by openapi-generator https://github.com/openapitools/openapi-generator
3+
4+
# Use this file to prevent files from being overwritten by the generator.
5+
# The patterns follow closely to .gitignore or .dockerignore.
6+
7+
git_push.sh
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// This is the entrypoint for the package
2+
export * from './src/apis';
3+
export * from './model/models';
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/**
2+
* Error.
3+
*/
4+
export type ErrorBase = {
5+
message?: string;
6+
};
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* Insights event.
3+
*/
4+
export type InsightEvent = {
5+
/**
6+
* An eventType can be a click, a conversion, or a view.
7+
*/
8+
eventType: InsightEvent.EventTypeEnum;
9+
/**
10+
* A user-defined string used to categorize events.
11+
*/
12+
eventName: string;
13+
/**
14+
* Name of the targeted index.
15+
*/
16+
index: string;
17+
/**
18+
* A user identifier. Depending if the user is logged-in or not, several strategies can be used from a sessionId to a technical identifier.
19+
*/
20+
userToken: string;
21+
/**
22+
* Time of the event expressed in milliseconds since the Unix epoch.
23+
*/
24+
timestamp?: number;
25+
/**
26+
* Algolia queryID. This is required when an event is tied to a search.
27+
*/
28+
queryID?: string;
29+
/**
30+
* An array of index objectID. Limited to 20 objects. An event can’t have both objectIDs and filters at the same time.
31+
*/
32+
objectIDs?: string[];
33+
/**
34+
* An array of filters. Limited to 10 filters. An event can’t have both objectIDs and filters at the same time.
35+
*/
36+
filters?: string[];
37+
/**
38+
* Position of the click in the list of Algolia search results. This field is required if a queryID is provided. One position must be provided for each objectID.
39+
*/
40+
positions?: number[];
41+
};
42+
43+
export namespace InsightEvent {
44+
export enum EventTypeEnum {
45+
Click = 'click',
46+
Conversion = 'conversion',
47+
View = 'view',
48+
}
49+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import type { InsightEvent } from './insightEvent';
2+
3+
/**
4+
* Object containing the events sent.
5+
*/
6+
export type InsightEvents = {
7+
/**
8+
* Array of events sent.
9+
*/
10+
events: InsightEvent[];
11+
};
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/* eslint-disable no-param-reassign */
2+
import type { RequestOptions } from '../utils/types';
3+
4+
export * from './errorBase';
5+
export * from './insightEvent';
6+
export * from './insightEvents';
7+
export * from './pushEventsResponse';
8+
9+
export interface Authentication {
10+
/**
11+
* Apply authentication settings to header and query params.
12+
*/
13+
applyToRequest: (requestOptions: RequestOptions) => Promise<void> | void;
14+
}
15+
16+
export class ApiKeyAuth implements Authentication {
17+
apiKey: string = '';
18+
19+
constructor(private location: string, private paramName: string) {}
20+
21+
applyToRequest(requestOptions: RequestOptions): void {
22+
if (this.location === 'query') {
23+
requestOptions.queryParameters[this.paramName] = this.apiKey;
24+
} else if (
25+
this.location === 'header' &&
26+
requestOptions &&
27+
requestOptions.headers
28+
) {
29+
requestOptions.headers[this.paramName] = this.apiKey;
30+
} else if (
31+
this.location === 'cookie' &&
32+
requestOptions &&
33+
requestOptions.headers
34+
) {
35+
if (requestOptions.headers.Cookie) {
36+
requestOptions.headers.Cookie += `; ${
37+
this.paramName
38+
}=${encodeURIComponent(this.apiKey)}`;
39+
} else {
40+
requestOptions.headers.Cookie = `${this.paramName}=${encodeURIComponent(
41+
this.apiKey
42+
)}`;
43+
}
44+
}
45+
}
46+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export type PushEventsResponse = {
2+
/**
3+
* A message confirming the event push.
4+
*/
5+
message: string;
6+
};
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "@algolia/client-insights",
3+
"version": "5.0.0",
4+
"description": "JavaScript client for @algolia/client-insights",
5+
"repository": "algolia/algoliasearch-client-javascript",
6+
"author": "Algolia",
7+
"private": true,
8+
"license": "MIT",
9+
"main": "dist/api.js",
10+
"types": "dist/api.d.ts",
11+
"scripts": {
12+
"clean": "rm -Rf node_modules/ *.js",
13+
"build": "tsc",
14+
"test": "yarn build && node dist/client.js"
15+
},
16+
"engines": {
17+
"node": "^16.0.0",
18+
"yarn": "^3.0.0"
19+
},
20+
"devDependencies": {
21+
"@types/node": "16.11.11",
22+
"typescript": "4.5.4"
23+
}
24+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { InsightsApi } from './insightsApi';
2+
3+
export * from './insightsApi';
4+
export * from '../utils/errors';
5+
export { EchoRequester } from '../utils/requester/EchoRequester';
6+
7+
export const APIS = [InsightsApi];
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
import type { InsightEvents } from '../model/insightEvents';
2+
import { ApiKeyAuth } from '../model/models';
3+
import type { PushEventsResponse } from '../model/pushEventsResponse';
4+
import { Transporter } from '../utils/Transporter';
5+
import type { Requester } from '../utils/requester/Requester';
6+
import type { Headers, Host, Request, RequestOptions } from '../utils/types';
7+
8+
export enum InsightsApiKeys {
9+
apiKey,
10+
appId,
11+
}
12+
13+
export class InsightsApi {
14+
protected authentications = {
15+
apiKey: new ApiKeyAuth('header', 'X-Algolia-API-Key'),
16+
appId: new ApiKeyAuth('header', 'X-Algolia-Application-Id'),
17+
};
18+
19+
private transporter: Transporter;
20+
21+
private sendRequest<TResponse>(
22+
request: Request,
23+
requestOptions: RequestOptions
24+
): Promise<TResponse> {
25+
if (this.authentications.apiKey.apiKey) {
26+
this.authentications.apiKey.applyToRequest(requestOptions);
27+
}
28+
29+
if (this.authentications.appId.apiKey) {
30+
this.authentications.appId.applyToRequest(requestOptions);
31+
}
32+
33+
return this.transporter.request(request, requestOptions);
34+
}
35+
36+
constructor(
37+
appId: string,
38+
apiKey: string,
39+
options?: { requester?: Requester; hosts?: Host[] }
40+
) {
41+
this.setApiKey(InsightsApiKeys.appId, appId);
42+
this.setApiKey(InsightsApiKeys.apiKey, apiKey);
43+
44+
this.transporter = new Transporter({
45+
hosts: options?.hosts ?? this.getDefaultHosts(),
46+
baseHeaders: {
47+
'content-type': 'application/x-www-form-urlencoded',
48+
},
49+
userAgent: 'Algolia for Javascript',
50+
timeouts: {
51+
connect: 2,
52+
read: 5,
53+
write: 30,
54+
},
55+
requester: options?.requester,
56+
});
57+
}
58+
59+
getDefaultHosts(): Host[] {
60+
return [
61+
{ url: `insights.algolia.io`, accept: 'readWrite', protocol: 'https' },
62+
];
63+
}
64+
65+
setRequest(requester: Requester): void {
66+
this.transporter.setRequester(requester);
67+
}
68+
69+
setHosts(hosts: Host[]): void {
70+
this.transporter.setHosts(hosts);
71+
}
72+
73+
setApiKey(key: InsightsApiKeys, value: string): void {
74+
this.authentications[InsightsApiKeys[key]].apiKey = value;
75+
}
76+
77+
/**
78+
* This command pushes an array of events.
79+
*
80+
* @summary Pushes an array of events.
81+
* @param pushEvents - The pushEvents parameters.
82+
* @param pushEvents.insightEvents - The insightEvents.
83+
*/
84+
pushEvents({ insightEvents }: PushEventsProps): Promise<PushEventsResponse> {
85+
const path = '/1/events';
86+
const headers: Headers = { Accept: 'application/json' };
87+
const queryParameters: Record<string, string> = {};
88+
89+
if (insightEvents === null || insightEvents === undefined) {
90+
throw new Error(
91+
'Required parameter insightEvents was null or undefined when calling pushEvents.'
92+
);
93+
}
94+
95+
if (insightEvents.events === null || insightEvents.events === undefined) {
96+
throw new Error(
97+
'Required parameter insightEvents.events was null or undefined when calling pushEvents.'
98+
);
99+
}
100+
101+
const request: Request = {
102+
method: 'POST',
103+
path,
104+
data: insightEvents,
105+
};
106+
107+
const requestOptions: RequestOptions = {
108+
headers,
109+
queryParameters,
110+
};
111+
112+
return this.sendRequest(request, requestOptions);
113+
}
114+
}
115+
116+
export type PushEventsProps = {
117+
insightEvents: InsightEvents;
118+
};

0 commit comments

Comments
 (0)