-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathserviceApi.ts
More file actions
166 lines (146 loc) · 7.94 KB
/
Copy pathserviceApi.ts
File metadata and controls
166 lines (146 loc) · 7.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
import { IPlatform } from '../sdkFactory/types';
import { ISettings } from '../types';
import { splitHttpClientFactory } from './splitHttpClient';
import { ISecureSplitHttpClient, IServiceApi } from './types';
import { objectAssign } from '../utils/lang/objectAssign';
import { ITelemetryTracker } from '../trackers/types';
import { SPLITS, IMPRESSIONS, IMPRESSIONS_COUNT, EVENTS, TELEMETRY, TOKEN, SEGMENT, MEMBERSHIPS } from '../utils/constants';
import { ERROR_TOO_MANY_SETS } from '../logger/constants';
const noCacheHeaderOptions = { headers: { 'Cache-Control': 'no-cache' } };
function userKeyToQueryParam(userKey: string) {
return 'users=' + encodeURIComponent(userKey); // no need to check availability of `encodeURIComponent`, since it is a global highly supported.
}
/**
* Factory of ServiceApi objects, which group the collection of HTTP endpoints used by the SDKs
*
* @param settings - validated settings object
* @param platform - object containing environment-specific dependencies
* @param telemetryTracker - telemetry tracker
* @param secureSplitHttpClientFactory - factory of SecureSplitHttpClient objects
*/
export function serviceApiFactory(
settings: ISettings,
platform: Pick<IPlatform, 'getOptions' | 'getFetch'>,
telemetryTracker: ITelemetryTracker,
secureSplitHttpClientFactory?: (settings: ISettings, platform: Pick<IPlatform, 'getOptions' | 'getFetch'>, telemetryTracker: ITelemetryTracker) => ISecureSplitHttpClient,
): IServiceApi {
const urls = settings.urls;
const filterQueryString = settings.sync.__splitFiltersValidation && settings.sync.__splitFiltersValidation.queryString;
const SplitSDKImpressionsMode = settings.sync.impressionsMode;
const splitHttpClient = splitHttpClientFactory(settings, platform);
const secureSplitHttpClient = secureSplitHttpClientFactory!(settings, platform, telemetryTracker);
return {
// @TODO throw errors if health check requests fail, to log them in the Synchronizer
getSdkAPIHealthCheck() {
const url = `${urls.sdk}/api/version`;
return splitHttpClient(url).then(() => true).catch(() => false);
},
getEventsAPIHealthCheck() {
const url = `${urls.events}/api/version`;
return splitHttpClient(url).then(() => true).catch(() => false);
},
fetchAuth(userMatchingKeys?: string[]) {
let url = `${urls.auth}/api/v2/auth?s=${settings.sync.flagSpecVersion}`;
if (userMatchingKeys) { // `userMatchingKeys` is undefined in server-side
const queryParams = userMatchingKeys.map(userKeyToQueryParam).join('&');
if (queryParams) url += '&' + queryParams;
}
return splitHttpClient(url, undefined, telemetryTracker.trackHttp(TOKEN));
},
fetchSplitChanges(since: number, noCache?: boolean, till?: number, rbSince?: number) {
const url = `${urls.sdk}/api/splitChanges?s=${settings.sync.flagSpecVersion}&since=${since}${rbSince ? '&rbSince=' + rbSince : ''}${filterQueryString || ''}${till ? '&till=' + till : ''}`;
return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(SPLITS))
.catch((err) => {
if (err.statusCode === 414) settings.log.error(ERROR_TOO_MANY_SETS);
throw err;
});
},
fetchSegmentChanges(since: number, segmentName: string, noCache?: boolean, till?: number) {
const url = `${urls.sdk}/api/segmentChanges/${segmentName}?since=${since}${till ? '&till=' + till : ''}`;
return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(SEGMENT));
},
// @TODO support filterQueryString and handle ERROR_TOO_MANY_SETS error
fetchConfigs(since: number, noCache?: boolean, till?: number, rbSince?: number) {
const url = `${urls.configs}/api/v1/configs?since=${since}${rbSince ? '&rbSince=' + rbSince : ''}${filterQueryString || ''}${till ? '&till=' + till : ''}`;
return secureSplitHttpClient(url, noCache ? noCacheHeaderOptions : undefined);
},
fetchConfigsSegmentChanges(since: number, segmentName: string, noCache?: boolean, till?: number) {
const url = `${urls.configs}/api/v1/segmentChanges/${segmentName}?since=${since}${till ? '&till=' + till : ''}`;
return secureSplitHttpClient(url, noCache ? noCacheHeaderOptions : undefined);
},
fetchMemberships(userMatchingKey: string, noCache?: boolean, till?: number) {
/**
* URI encoding of user keys in order to:
* - avoid 400 responses (due to URI malformed). E.g.: '/api/memberships/%'
* - avoid 404 responses. E.g.: '/api/memberships/foo/bar'
* - match user keys with special characters. E.g.: 'foo%bar', 'foo/bar'
*/
const url = `${urls.sdk}/api/memberships/${encodeURIComponent(userMatchingKey)}${till ? '?till=' + till : ''}`;
return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(MEMBERSHIPS));
},
/**
* Post events.
*
* @param body - Events bulk payload
* @param headers - Optionals headers to overwrite default ones. For example, it is used in producer mode to overwrite metadata headers.
*/
postEventsBulk(body: string, headers?: Record<string, string>) {
const url = `${urls.events}/api/events/bulk`;
return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(EVENTS));
},
/**
* Post impressions.
*
* @param body - Impressions bulk payload
* @param headers - Optionals headers to overwrite default ones. For example, it is used in producer mode to overwrite metadata headers.
*/
postTestImpressionsBulk(body: string, headers?: Record<string, string>) {
const url = `${urls.events}/api/testImpressions/bulk`;
return splitHttpClient(url, {
// Adding extra headers to send impressions in OPTIMIZED or DEBUG modes.
method: 'POST', body, headers: objectAssign({ SplitSDKImpressionsMode }, headers)
}, telemetryTracker.trackHttp(IMPRESSIONS));
},
/**
* Post impressions counts.
*
* @param body - Impressions counts payload
* @param headers - Optionals headers to overwrite default ones. For example, it is used in producer mode to overwrite metadata headers.
*/
postTestImpressionsCount(body: string, headers?: Record<string, string>) {
const url = `${urls.events}/api/testImpressions/count`;
return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(IMPRESSIONS_COUNT));
},
/**
* Post unique keys for client side.
*
* @param body - unique keys payload
* @param headers - Optionals headers to overwrite default ones. For example, it is used in producer mode to overwrite metadata headers.
*/
postUniqueKeysBulkCs(body: string, headers?: Record<string, string>) {
const url = `${urls.telemetry}/api/v1/keys/cs`;
return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(TELEMETRY));
},
/**
* Post unique keys for server side.
*
* @param body - unique keys payload
* @param headers - Optionals headers to overwrite default ones. For example, it is used in producer mode to overwrite metadata headers.
*/
postUniqueKeysBulkSs(body: string, headers?: Record<string, string>) {
const url = `${urls.telemetry}/api/v1/keys/ss`;
return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(TELEMETRY));
},
postMetricsConfig(body: string, headers?: Record<string, string>) {
const url = `${urls.telemetry}/api/v1/metrics/config`;
return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(TELEMETRY), true);
},
postMetricsUsage(body: string, headers?: Record<string, string>) {
const url = `${urls.telemetry}/api/v1/metrics/usage`;
return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(TELEMETRY), true);
},
stop() {
secureSplitHttpClient.stop();
}
};
}