@splitsoftware/splitio-commons 3.0.0-rc.3 → 3.0.0-rc.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGES.txt +1 -1
- package/cjs/listeners/browser.js +2 -2
- package/cjs/logger/messages/debug.js +1 -1
- package/cjs/presets/serverSide.js +2 -2
- package/cjs/sdkClient/sdkLifecycle.js +2 -2
- package/cjs/sdkFactory/index.js +4 -4
- package/cjs/services/authProvider.js +2 -2
- package/cjs/services/decorateHeaders.js +2 -1
- package/cjs/services/secureSplitHttpClient.js +1 -1
- package/cjs/services/serviceApi.js +145 -0
- package/cjs/services/splitApi.js +13 -14
- package/cjs/services/splitHttpClient.js +3 -5
- package/cjs/sync/polling/fetchers/splitChangesFetcher.js +1 -1
- package/cjs/sync/polling/pollingManagerCS.js +2 -2
- package/cjs/sync/polling/updaters/definitionChangesUpdater.js +8 -8
- package/cjs/sync/streaming/AuthClient/index.js +1 -1
- package/cjs/sync/streaming/pushManager.js +2 -2
- package/cjs/sync/submitters/eventsSubmitter.js +1 -1
- package/cjs/sync/submitters/impressionCountsSubmitter.js +1 -1
- package/cjs/sync/submitters/impressionsSubmitter.js +1 -1
- package/cjs/sync/submitters/telemetrySubmitter.js +3 -3
- package/cjs/sync/submitters/uniqueKeysSubmitter.js +1 -1
- package/cjs/sync/syncManagerOnline.js +1 -1
- package/cjs/utils/settingsValidation/index.js +1 -1
- package/esm/listeners/browser.js +2 -2
- package/esm/logger/messages/debug.js +1 -1
- package/esm/presets/serverSide.js +2 -2
- package/esm/sdkClient/sdkLifecycle.js +2 -2
- package/esm/sdkFactory/index.js +4 -4
- package/esm/services/authProvider.js +2 -2
- package/esm/services/decorateHeaders.js +2 -1
- package/esm/services/secureSplitHttpClient.js +1 -1
- package/esm/services/serviceApi.js +141 -0
- package/esm/services/splitApi.js +13 -14
- package/esm/services/splitHttpClient.js +3 -5
- package/esm/sync/polling/fetchers/splitChangesFetcher.js +1 -1
- package/esm/sync/polling/pollingManagerCS.js +2 -2
- package/esm/sync/polling/updaters/definitionChangesUpdater.js +8 -8
- package/esm/sync/streaming/AuthClient/index.js +1 -1
- package/esm/sync/streaming/pushManager.js +2 -2
- package/esm/sync/submitters/eventsSubmitter.js +1 -1
- package/esm/sync/submitters/impressionCountsSubmitter.js +1 -1
- package/esm/sync/submitters/impressionsSubmitter.js +1 -1
- package/esm/sync/submitters/telemetrySubmitter.js +3 -3
- package/esm/sync/submitters/uniqueKeysSubmitter.js +1 -1
- package/esm/sync/syncManagerOnline.js +1 -1
- package/esm/utils/settingsValidation/index.js +1 -1
- package/package.json +1 -1
- package/src/listeners/browser.ts +4 -4
- package/src/logger/messages/debug.ts +1 -1
- package/src/presets/serverSide.ts +2 -2
- package/src/sdkClient/sdkLifecycle.ts +2 -2
- package/src/sdkFactory/index.ts +4 -4
- package/src/sdkFactory/types.ts +7 -7
- package/src/services/authProvider.ts +2 -2
- package/src/services/decorateHeaders.ts +2 -1
- package/src/services/secureSplitHttpClient.ts +1 -1
- package/src/services/serviceApi.ts +166 -0
- package/src/services/splitApi.ts +19 -18
- package/src/services/splitHttpClient.ts +7 -4
- package/src/services/types.ts +2 -2
- package/src/storages/types.ts +3 -3
- package/src/sync/polling/fetchers/splitChangesFetcher.ts +1 -1
- package/src/sync/polling/pollingManagerCS.ts +2 -2
- package/src/sync/polling/updaters/definitionChangesUpdater.ts +9 -9
- package/src/sync/streaming/AuthClient/index.ts +1 -1
- package/src/sync/streaming/pushManager.ts +2 -2
- package/src/sync/submitters/eventsSubmitter.ts +1 -1
- package/src/sync/submitters/impressionCountsSubmitter.ts +1 -1
- package/src/sync/submitters/impressionsSubmitter.ts +1 -1
- package/src/sync/submitters/telemetrySubmitter.ts +3 -3
- package/src/sync/submitters/uniqueKeysSubmitter.ts +1 -1
- package/src/sync/syncManagerOnline.ts +1 -1
- package/src/utils/settingsValidation/index.ts +1 -1
- package/types/splitio.d.ts +8 -8
|
@@ -28,8 +28,8 @@ export function authProviderFactory(settings: ISettings, splitHttpClient: ISplit
|
|
|
28
28
|
const { urls, log } = settings;
|
|
29
29
|
|
|
30
30
|
function fetchAuth() {
|
|
31
|
-
let url = `${urls.auth}/v3/auth?capabilities=config`;
|
|
32
|
-
return splitHttpClient(url, undefined, telemetryTracker.trackHttp(TOKEN));
|
|
31
|
+
let url = `${urls.auth}/api/v3/auth?capabilities=config`;
|
|
32
|
+
return splitHttpClient(url, undefined, telemetryTracker.trackHttp(TOKEN), false, true);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
const authenticate = authenticateFactory(fetchAuth);
|
|
@@ -14,7 +14,8 @@ const FORBIDDEN_HEADERS = new Set([
|
|
|
14
14
|
'content-encoding',
|
|
15
15
|
'accept',
|
|
16
16
|
'keep-alive',
|
|
17
|
-
'x-fastly-debug'
|
|
17
|
+
'x-fastly-debug',
|
|
18
|
+
'x-harness-fme-sdk-version'
|
|
18
19
|
]);
|
|
19
20
|
|
|
20
21
|
export function decorateHeaders(settings: ISettings, headers: Record<string, string>) {
|
|
@@ -19,7 +19,7 @@ export function secureSplitHttpClientFactory(settings: ISettings, platform: Pick
|
|
|
19
19
|
const authProvider = authProviderFactory(settings, splitHttpClient, telemetryTracker);
|
|
20
20
|
|
|
21
21
|
function makeRequest(url: string, options: IRequestOptions | undefined, latencyTracker: ((error?: NetworkError) => void) | undefined, logErrorsAsInfo: boolean | undefined, token: string): Promise<IResponse> {
|
|
22
|
-
return splitHttpClient(url, { ...options, headers: { ...options?.headers, Authorization: `Bearer ${token}` } }, latencyTracker, logErrorsAsInfo);
|
|
22
|
+
return splitHttpClient(url, { ...options, headers: { ...options?.headers, Authorization: `Bearer ${token}` } }, latencyTracker, logErrorsAsInfo, true);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
const httpClient = function (url: string, options?: IRequestOptions, latencyTracker?: (error?: NetworkError) => void, logErrorsAsInfo?: boolean): Promise<IResponse> {
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { IPlatform } from '../sdkFactory/types';
|
|
2
|
+
import { ISettings } from '../types';
|
|
3
|
+
import { splitHttpClientFactory } from './splitHttpClient';
|
|
4
|
+
import { ISecureSplitHttpClient, IServiceApi } from './types';
|
|
5
|
+
import { objectAssign } from '../utils/lang/objectAssign';
|
|
6
|
+
import { ITelemetryTracker } from '../trackers/types';
|
|
7
|
+
import { SPLITS, IMPRESSIONS, IMPRESSIONS_COUNT, EVENTS, TELEMETRY, TOKEN, SEGMENT, MEMBERSHIPS } from '../utils/constants';
|
|
8
|
+
import { ERROR_TOO_MANY_SETS } from '../logger/constants';
|
|
9
|
+
|
|
10
|
+
const noCacheHeaderOptions = { headers: { 'Cache-Control': 'no-cache' } };
|
|
11
|
+
|
|
12
|
+
function userKeyToQueryParam(userKey: string) {
|
|
13
|
+
return 'users=' + encodeURIComponent(userKey); // no need to check availability of `encodeURIComponent`, since it is a global highly supported.
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Factory of ServiceApi objects, which group the collection of HTTP endpoints used by the SDKs
|
|
18
|
+
*
|
|
19
|
+
* @param settings - validated settings object
|
|
20
|
+
* @param platform - object containing environment-specific dependencies
|
|
21
|
+
* @param telemetryTracker - telemetry tracker
|
|
22
|
+
* @param secureSplitHttpClientFactory - factory of SecureSplitHttpClient objects
|
|
23
|
+
*/
|
|
24
|
+
export function serviceApiFactory(
|
|
25
|
+
settings: ISettings,
|
|
26
|
+
platform: Pick<IPlatform, 'getOptions' | 'getFetch'>,
|
|
27
|
+
telemetryTracker: ITelemetryTracker,
|
|
28
|
+
secureSplitHttpClientFactory?: (settings: ISettings, platform: Pick<IPlatform, 'getOptions' | 'getFetch'>, telemetryTracker: ITelemetryTracker) => ISecureSplitHttpClient,
|
|
29
|
+
): IServiceApi {
|
|
30
|
+
|
|
31
|
+
const urls = settings.urls;
|
|
32
|
+
const filterQueryString = settings.sync.__splitFiltersValidation && settings.sync.__splitFiltersValidation.queryString;
|
|
33
|
+
const SplitSDKImpressionsMode = settings.sync.impressionsMode;
|
|
34
|
+
const splitHttpClient = splitHttpClientFactory(settings, platform);
|
|
35
|
+
const secureSplitHttpClient = secureSplitHttpClientFactory!(settings, platform, telemetryTracker);
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
// @TODO throw errors if health check requests fail, to log them in the Synchronizer
|
|
39
|
+
getSdkAPIHealthCheck() {
|
|
40
|
+
const url = `${urls.sdk}/api/version`;
|
|
41
|
+
return splitHttpClient(url).then(() => true).catch(() => false);
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
getEventsAPIHealthCheck() {
|
|
45
|
+
const url = `${urls.events}/api/version`;
|
|
46
|
+
return splitHttpClient(url).then(() => true).catch(() => false);
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
fetchAuth(userMatchingKeys?: string[]) {
|
|
50
|
+
let url = `${urls.auth}/api/v2/auth?s=${settings.sync.flagSpecVersion}`;
|
|
51
|
+
if (userMatchingKeys) { // `userMatchingKeys` is undefined in server-side
|
|
52
|
+
const queryParams = userMatchingKeys.map(userKeyToQueryParam).join('&');
|
|
53
|
+
if (queryParams) url += '&' + queryParams;
|
|
54
|
+
}
|
|
55
|
+
return splitHttpClient(url, undefined, telemetryTracker.trackHttp(TOKEN));
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
fetchSplitChanges(since: number, noCache?: boolean, till?: number, rbSince?: number) {
|
|
59
|
+
const url = `${urls.sdk}/api/splitChanges?s=${settings.sync.flagSpecVersion}&since=${since}${rbSince ? '&rbSince=' + rbSince : ''}${filterQueryString || ''}${till ? '&till=' + till : ''}`;
|
|
60
|
+
return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(SPLITS))
|
|
61
|
+
.catch((err) => {
|
|
62
|
+
if (err.statusCode === 414) settings.log.error(ERROR_TOO_MANY_SETS);
|
|
63
|
+
throw err;
|
|
64
|
+
});
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
fetchSegmentChanges(since: number, segmentName: string, noCache?: boolean, till?: number) {
|
|
68
|
+
const url = `${urls.sdk}/api/segmentChanges/${segmentName}?since=${since}${till ? '&till=' + till : ''}`;
|
|
69
|
+
return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(SEGMENT));
|
|
70
|
+
},
|
|
71
|
+
|
|
72
|
+
// @TODO support filterQueryString and handle ERROR_TOO_MANY_SETS error
|
|
73
|
+
fetchConfigs(since: number, noCache?: boolean, till?: number) {
|
|
74
|
+
const url = `${urls.configs}/api/v1/configs?since=${since}${filterQueryString || ''}${till ? '&till=' + till : ''}`;
|
|
75
|
+
return secureSplitHttpClient(url, noCache ? noCacheHeaderOptions : undefined);
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
fetchConfigsSegmentChanges(since: number, segmentName: string, noCache?: boolean, till?: number) {
|
|
79
|
+
const url = `${urls.configs}/api/v1/segmentChanges/${segmentName}?since=${since}${till ? '&till=' + till : ''}`;
|
|
80
|
+
return secureSplitHttpClient(url, noCache ? noCacheHeaderOptions : undefined);
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
fetchMemberships(userMatchingKey: string, noCache?: boolean, till?: number) {
|
|
84
|
+
/**
|
|
85
|
+
* URI encoding of user keys in order to:
|
|
86
|
+
* - avoid 400 responses (due to URI malformed). E.g.: '/api/memberships/%'
|
|
87
|
+
* - avoid 404 responses. E.g.: '/api/memberships/foo/bar'
|
|
88
|
+
* - match user keys with special characters. E.g.: 'foo%bar', 'foo/bar'
|
|
89
|
+
*/
|
|
90
|
+
const url = `${urls.sdk}/api/memberships/${encodeURIComponent(userMatchingKey)}${till ? '?till=' + till : ''}`;
|
|
91
|
+
return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(MEMBERSHIPS));
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Post events.
|
|
96
|
+
*
|
|
97
|
+
* @param body - Events bulk payload
|
|
98
|
+
* @param headers - Optionals headers to overwrite default ones. For example, it is used in producer mode to overwrite metadata headers.
|
|
99
|
+
*/
|
|
100
|
+
postEventsBulk(body: string, headers?: Record<string, string>) {
|
|
101
|
+
const url = `${urls.events}/api/events/bulk`;
|
|
102
|
+
return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(EVENTS));
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Post impressions.
|
|
107
|
+
*
|
|
108
|
+
* @param body - Impressions bulk payload
|
|
109
|
+
* @param headers - Optionals headers to overwrite default ones. For example, it is used in producer mode to overwrite metadata headers.
|
|
110
|
+
*/
|
|
111
|
+
postTestImpressionsBulk(body: string, headers?: Record<string, string>) {
|
|
112
|
+
const url = `${urls.events}/api/testImpressions/bulk`;
|
|
113
|
+
return splitHttpClient(url, {
|
|
114
|
+
// Adding extra headers to send impressions in OPTIMIZED or DEBUG modes.
|
|
115
|
+
method: 'POST', body, headers: objectAssign({ SplitSDKImpressionsMode }, headers)
|
|
116
|
+
}, telemetryTracker.trackHttp(IMPRESSIONS));
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Post impressions counts.
|
|
121
|
+
*
|
|
122
|
+
* @param body - Impressions counts payload
|
|
123
|
+
* @param headers - Optionals headers to overwrite default ones. For example, it is used in producer mode to overwrite metadata headers.
|
|
124
|
+
*/
|
|
125
|
+
postTestImpressionsCount(body: string, headers?: Record<string, string>) {
|
|
126
|
+
const url = `${urls.events}/api/testImpressions/count`;
|
|
127
|
+
return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(IMPRESSIONS_COUNT));
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Post unique keys for client side.
|
|
132
|
+
*
|
|
133
|
+
* @param body - unique keys payload
|
|
134
|
+
* @param headers - Optionals headers to overwrite default ones. For example, it is used in producer mode to overwrite metadata headers.
|
|
135
|
+
*/
|
|
136
|
+
postUniqueKeysBulkCs(body: string, headers?: Record<string, string>) {
|
|
137
|
+
const url = `${urls.telemetry}/api/v1/keys/cs`;
|
|
138
|
+
return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(TELEMETRY));
|
|
139
|
+
},
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Post unique keys for server side.
|
|
143
|
+
*
|
|
144
|
+
* @param body - unique keys payload
|
|
145
|
+
* @param headers - Optionals headers to overwrite default ones. For example, it is used in producer mode to overwrite metadata headers.
|
|
146
|
+
*/
|
|
147
|
+
postUniqueKeysBulkSs(body: string, headers?: Record<string, string>) {
|
|
148
|
+
const url = `${urls.telemetry}/api/v1/keys/ss`;
|
|
149
|
+
return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(TELEMETRY));
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
postMetricsConfig(body: string, headers?: Record<string, string>) {
|
|
153
|
+
const url = `${urls.telemetry}/api/v1/metrics/config`;
|
|
154
|
+
return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(TELEMETRY), true);
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
postMetricsUsage(body: string, headers?: Record<string, string>) {
|
|
158
|
+
const url = `${urls.telemetry}/api/v1/metrics/usage`;
|
|
159
|
+
return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(TELEMETRY), true);
|
|
160
|
+
},
|
|
161
|
+
|
|
162
|
+
stop() {
|
|
163
|
+
secureSplitHttpClient.stop();
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
}
|
package/src/services/splitApi.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { IPlatform } from '../sdkFactory/types';
|
|
2
2
|
import { ISettings } from '../types';
|
|
3
3
|
import { splitHttpClientFactory } from './splitHttpClient';
|
|
4
|
-
import {
|
|
4
|
+
import { IServiceApi } from './types';
|
|
5
5
|
import { objectAssign } from '../utils/lang/objectAssign';
|
|
6
6
|
import { ITelemetryTracker } from '../trackers/types';
|
|
7
7
|
import { SPLITS, IMPRESSIONS, IMPRESSIONS_COUNT, EVENTS, TELEMETRY, TOKEN, SEGMENT, MEMBERSHIPS } from '../utils/constants';
|
|
@@ -13,8 +13,9 @@ function userKeyToQueryParam(userKey: string) {
|
|
|
13
13
|
return 'users=' + encodeURIComponent(userKey); // no need to check availability of `encodeURIComponent`, since it is a global highly supported.
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
// @TODO remove when migrating to the new `urls` defaults.
|
|
16
17
|
/**
|
|
17
|
-
* Factory of
|
|
18
|
+
* Factory of ServiceApi objects, which group the collection of Split HTTP endpoints used by the SDK
|
|
18
19
|
*
|
|
19
20
|
* @param settings - validated settings object
|
|
20
21
|
* @param platform - object containing environment-specific dependencies
|
|
@@ -23,15 +24,13 @@ function userKeyToQueryParam(userKey: string) {
|
|
|
23
24
|
export function splitApiFactory(
|
|
24
25
|
settings: ISettings,
|
|
25
26
|
platform: Pick<IPlatform, 'getOptions' | 'getFetch'>,
|
|
26
|
-
telemetryTracker: ITelemetryTracker
|
|
27
|
-
|
|
28
|
-
): ISplitApi {
|
|
27
|
+
telemetryTracker: ITelemetryTracker
|
|
28
|
+
): IServiceApi {
|
|
29
29
|
|
|
30
30
|
const urls = settings.urls;
|
|
31
31
|
const filterQueryString = settings.sync.__splitFiltersValidation && settings.sync.__splitFiltersValidation.queryString;
|
|
32
32
|
const SplitSDKImpressionsMode = settings.sync.impressionsMode;
|
|
33
33
|
const splitHttpClient = splitHttpClientFactory(settings, platform);
|
|
34
|
-
const secureSplitHttpClient = secureSplitHttpClientFactory ? secureSplitHttpClientFactory(settings, platform, telemetryTracker) : undefined;
|
|
35
34
|
|
|
36
35
|
return {
|
|
37
36
|
// @TODO throw errors if health check requests fail, to log them in the Synchronizer
|
|
@@ -68,17 +67,6 @@ export function splitApiFactory(
|
|
|
68
67
|
return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(SEGMENT));
|
|
69
68
|
},
|
|
70
69
|
|
|
71
|
-
// @TODO support filterQueryString and handle ERROR_TOO_MANY_SETS error
|
|
72
|
-
fetchConfigs(since: number, noCache?: boolean, till?: number) {
|
|
73
|
-
const url = `${urls.configs}/v1/configs?since=${since}${filterQueryString || ''}${till ? '&till=' + till : ''}`;
|
|
74
|
-
return (secureSplitHttpClient || splitHttpClient)(url, noCache ? noCacheHeaderOptions : undefined);
|
|
75
|
-
},
|
|
76
|
-
|
|
77
|
-
fetchConfigsSegmentChanges(since: number, segmentName: string, noCache?: boolean, till?: number) {
|
|
78
|
-
const url = `${urls.configs}/v1/segmentChanges/${segmentName}?since=${since}${till ? '&till=' + till : ''}`;
|
|
79
|
-
return (secureSplitHttpClient || splitHttpClient)(url, noCache ? noCacheHeaderOptions : undefined);
|
|
80
|
-
},
|
|
81
|
-
|
|
82
70
|
fetchMemberships(userMatchingKey: string, noCache?: boolean, till?: number) {
|
|
83
71
|
/**
|
|
84
72
|
* URI encoding of user keys in order to:
|
|
@@ -158,8 +146,21 @@ export function splitApiFactory(
|
|
|
158
146
|
return splitHttpClient(url, { method: 'POST', body, headers }, telemetryTracker.trackHttp(TELEMETRY), true);
|
|
159
147
|
},
|
|
160
148
|
|
|
149
|
+
|
|
150
|
+
// Not used. Just here to satisfy the IServiceApi interface.
|
|
151
|
+
|
|
152
|
+
fetchConfigs(since: number, noCache?: boolean, till?: number) {
|
|
153
|
+
const url = `${urls.configs}/v1/configs?since=${since}${filterQueryString || ''}${till ? '&till=' + till : ''}`;
|
|
154
|
+
return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined);
|
|
155
|
+
},
|
|
156
|
+
|
|
157
|
+
fetchConfigsSegmentChanges(since: number, segmentName: string, noCache?: boolean, till?: number) {
|
|
158
|
+
const url = `${urls.configs}/v1/segmentChanges/${segmentName}?since=${since}${till ? '&till=' + till : ''}`;
|
|
159
|
+
return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined);
|
|
160
|
+
},
|
|
161
|
+
|
|
161
162
|
stop() {
|
|
162
|
-
|
|
163
|
+
// no-op
|
|
163
164
|
}
|
|
164
165
|
};
|
|
165
166
|
}
|
|
@@ -27,17 +27,20 @@ export function splitHttpClientFactory(settings: ISettings, { getOptions, getFet
|
|
|
27
27
|
const commonHeaders: Record<string, string> = {
|
|
28
28
|
'Accept': 'application/json',
|
|
29
29
|
'Content-Type': 'application/json',
|
|
30
|
-
'Authorization': `Bearer ${authorizationKey}
|
|
31
|
-
'SplitSDKVersion': version
|
|
30
|
+
'Authorization': `Bearer ${authorizationKey}`
|
|
32
31
|
};
|
|
33
32
|
|
|
34
33
|
if (ip) commonHeaders['SplitSDKMachineIP'] = ip;
|
|
35
34
|
if (hostname) commonHeaders['SplitSDKMachineName'] = removeNonISO88591(hostname);
|
|
36
35
|
|
|
37
|
-
return function httpClient(url: string, reqOpts: IRequestOptions = {}, latencyTracker: (error?: NetworkError) => void = () => { }, logErrorsAsInfo
|
|
36
|
+
return function httpClient(url: string, reqOpts: IRequestOptions = {}, latencyTracker: (error?: NetworkError) => void = () => { }, logErrorsAsInfo?: boolean, newVersionHeader?: boolean): Promise<IResponse> {
|
|
38
37
|
|
|
39
38
|
const request = objectAssign({
|
|
40
|
-
headers: decorateHeaders(settings, objectAssign(
|
|
39
|
+
headers: decorateHeaders(settings, objectAssign(
|
|
40
|
+
newVersionHeader ? { 'X-Harness-FME-SDK-Version': version } : { 'SplitSDKVersion': version },
|
|
41
|
+
commonHeaders,
|
|
42
|
+
reqOpts.headers || {}
|
|
43
|
+
)),
|
|
41
44
|
method: reqOpts.method || 'GET',
|
|
42
45
|
body: reqOpts.body
|
|
43
46
|
}, options);
|
package/src/services/types.ts
CHANGED
|
@@ -31,7 +31,7 @@ export type IFetch = (url: string, options?: IRequestOptions) => Promise<IRespon
|
|
|
31
31
|
// IFetch specialization
|
|
32
32
|
export type IHealthCheckAPI = () => Promise<boolean>
|
|
33
33
|
|
|
34
|
-
export type ISplitHttpClient = (url: string, options?: IRequestOptions, latencyTracker?: (error?: NetworkError) => void, logErrorsAsInfo?: boolean) => Promise<IResponse>
|
|
34
|
+
export type ISplitHttpClient = (url: string, options?: IRequestOptions, latencyTracker?: (error?: NetworkError) => void, logErrorsAsInfo?: boolean, newVersionHeader?: boolean) => Promise<IResponse>
|
|
35
35
|
|
|
36
36
|
export type ISecureSplitHttpClient = ISplitHttpClient & { stop(): void }
|
|
37
37
|
|
|
@@ -57,7 +57,7 @@ export type IPostMetricsConfig = (body: string, headers?: Record<string, string>
|
|
|
57
57
|
|
|
58
58
|
export type IPostMetricsUsage = (body: string, headers?: Record<string, string>) => Promise<IResponse>
|
|
59
59
|
|
|
60
|
-
export interface
|
|
60
|
+
export interface IServiceApi {
|
|
61
61
|
getSdkAPIHealthCheck: IHealthCheckAPI
|
|
62
62
|
getEventsAPIHealthCheck: IHealthCheckAPI
|
|
63
63
|
fetchAuth: IFetchAuth
|
package/src/storages/types.ts
CHANGED
|
@@ -195,7 +195,7 @@ export interface IPluggableStorageWrapper {
|
|
|
195
195
|
/** Splits cache */
|
|
196
196
|
|
|
197
197
|
export interface IDefinitionsCacheBase {
|
|
198
|
-
update(
|
|
198
|
+
update(toUpsert: IDefinition[], toRemove: string[], changeNumber: number): MaybeThenable<boolean>,
|
|
199
199
|
get(name: string): MaybeThenable<IDefinition | null>,
|
|
200
200
|
getMany(names: string[]): MaybeThenable<Record<string, IDefinition | null>>, // `fetchMany` in spec
|
|
201
201
|
// should never reject or throw an exception. Instead return -1 by default, assuming no splits are present in the storage.
|
|
@@ -212,7 +212,7 @@ export interface IDefinitionsCacheBase {
|
|
|
212
212
|
}
|
|
213
213
|
|
|
214
214
|
export interface IDefinitionsCacheSync extends IDefinitionsCacheBase {
|
|
215
|
-
update(
|
|
215
|
+
update(toUpsert: IDefinition[], toRemove: string[], changeNumber: number): boolean,
|
|
216
216
|
get(name: string): IDefinition | null,
|
|
217
217
|
getMany(names: string[]): Record<string, IDefinition | null>,
|
|
218
218
|
getChangeNumber(): number,
|
|
@@ -226,7 +226,7 @@ export interface IDefinitionsCacheSync extends IDefinitionsCacheBase {
|
|
|
226
226
|
}
|
|
227
227
|
|
|
228
228
|
export interface IDefinitionsCacheAsync extends IDefinitionsCacheBase {
|
|
229
|
-
update(
|
|
229
|
+
update(toUpsert: IDefinition[], toRemove: string[], changeNumber: number): Promise<boolean>,
|
|
230
230
|
get(name: string): Promise<IDefinition | null>,
|
|
231
231
|
getMany(names: string[]): Promise<Record<string, IDefinition | null>>,
|
|
232
232
|
getChangeNumber(): Promise<number>,
|
|
@@ -55,7 +55,7 @@ function partitionByStatus<T extends { status?: string; name: string }>(items: T
|
|
|
55
55
|
// @TODO breaking: drop support for Split Proxy below v5.10.0 and simplify the implementation
|
|
56
56
|
export function splitChangesFetcherFactory(params: ISdkFactoryContextSync): IDefinitionChangesFetcher {
|
|
57
57
|
|
|
58
|
-
const {
|
|
58
|
+
const { serviceApi: { fetchSplitChanges }, settings, storage } = params;
|
|
59
59
|
const log = settings.log;
|
|
60
60
|
const PROXY_CHECK_INTERVAL_MILLIS = checkIfServerSide(settings) ? PROXY_CHECK_INTERVAL_MILLIS_SS : PROXY_CHECK_INTERVAL_MILLIS_CS;
|
|
61
61
|
let lastProxyCheckTimestamp: number | undefined;
|
|
@@ -21,7 +21,7 @@ export function pollingManagerCSFactory(
|
|
|
21
21
|
definitionChangesFetcher: IDefinitionChangesFetcher
|
|
22
22
|
): IPollingManagerCS {
|
|
23
23
|
|
|
24
|
-
const {
|
|
24
|
+
const { serviceApi, storage, readiness, settings } = params;
|
|
25
25
|
const log = settings.log;
|
|
26
26
|
|
|
27
27
|
const definitionsSyncTask = definitionsSyncTaskFactory(definitionChangesFetcher, storage, readiness, settings, true);
|
|
@@ -59,7 +59,7 @@ export function pollingManagerCSFactory(
|
|
|
59
59
|
});
|
|
60
60
|
|
|
61
61
|
function add(matchingKey: string, readiness: IReadinessManager, storage: IStorageSync) {
|
|
62
|
-
const mySegmentsSyncTask = mySegmentsSyncTaskFactory(
|
|
62
|
+
const mySegmentsSyncTask = mySegmentsSyncTaskFactory(serviceApi.fetchMemberships, storage, readiness, settings, matchingKey);
|
|
63
63
|
|
|
64
64
|
// smart ready
|
|
65
65
|
function smartReady(metadata: SdkUpdateMetadata) {
|
|
@@ -57,7 +57,7 @@ export function parseSegments(ruleEntity: IDefinition | IRBSegment, matcherType:
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
interface IDefinitionMutations<T extends IDefinition | IRBSegment> {
|
|
60
|
-
|
|
60
|
+
updated: T[],
|
|
61
61
|
removed: string[],
|
|
62
62
|
names: string[]
|
|
63
63
|
}
|
|
@@ -91,7 +91,7 @@ function matchFilters(definition: IDefinition, filters: ISplitFiltersValidation)
|
|
|
91
91
|
export function computeMutation<T extends IDefinition | IRBSegment>(update: { updated: T[], removed: string[] }, segments: Set<string>, filters?: ISplitFiltersValidation): IDefinitionMutations<T> {
|
|
92
92
|
return update.updated.reduce((accum, ruleEntity) => {
|
|
93
93
|
if (!filters || matchFilters(ruleEntity as IDefinition, filters)) {
|
|
94
|
-
accum.
|
|
94
|
+
accum.updated.push(ruleEntity);
|
|
95
95
|
|
|
96
96
|
parseSegments(ruleEntity).forEach((segmentName: string) => {
|
|
97
97
|
segments.add(segmentName);
|
|
@@ -101,7 +101,7 @@ export function computeMutation<T extends IDefinition | IRBSegment>(update: { up
|
|
|
101
101
|
}
|
|
102
102
|
accum.names.push(ruleEntity.name);
|
|
103
103
|
return accum;
|
|
104
|
-
}, {
|
|
104
|
+
}, { updated: [], removed: update.removed, names: update.removed.slice() } as IDefinitionMutations<T>);
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
function convertInstantUpdateToDefinitionChanges(instantUpdate: InstantUpdate) {
|
|
@@ -180,17 +180,17 @@ export function definitionChangesUpdaterFactory(
|
|
|
180
180
|
let updatedDefinitions: string[] = [];
|
|
181
181
|
let ffUpdate: MaybeThenable<boolean> = false;
|
|
182
182
|
if (definitionChanges.d) {
|
|
183
|
-
const {
|
|
183
|
+
const { updated, removed, names } = computeMutation(definitionChanges.d, usedSegments, splitFiltersValidation);
|
|
184
184
|
updatedDefinitions = names;
|
|
185
|
-
log.debug(SYNC_UPDATE, [definitionChangesFetcher.type,
|
|
186
|
-
ffUpdate = definitions.update(
|
|
185
|
+
log.debug(SYNC_UPDATE, [definitionChangesFetcher.type, updated.length, removed.length]);
|
|
186
|
+
ffUpdate = definitions.update(updated, removed, definitionChanges.d.till);
|
|
187
187
|
}
|
|
188
188
|
|
|
189
189
|
let rbsUpdate: MaybeThenable<boolean> = false;
|
|
190
190
|
if (definitionChanges.rbs) {
|
|
191
|
-
const {
|
|
192
|
-
log.debug(SYNC_UPDATE, ['rule-based segments',
|
|
193
|
-
rbsUpdate = rbSegments.update(
|
|
191
|
+
const { updated, removed } = computeMutation(definitionChanges.rbs, usedSegments);
|
|
192
|
+
log.debug(SYNC_UPDATE, ['rule-based segments', updated.length, removed.length]);
|
|
193
|
+
rbsUpdate = rbSegments.update(updated, removed, definitionChanges.rbs.till);
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
return Promise.all([ffUpdate, rbsUpdate,
|
|
@@ -8,7 +8,7 @@ import { hash } from '../../../utils/murmur3/murmur3';
|
|
|
8
8
|
/**
|
|
9
9
|
* Factory of authentication function.
|
|
10
10
|
*
|
|
11
|
-
* @param fetchAuth - `
|
|
11
|
+
* @param fetchAuth - `ServiceApi.fetchAuth` endpoint
|
|
12
12
|
*/
|
|
13
13
|
export function authenticateFactory(fetchAuth: IFetchAuth): IAuthenticate {
|
|
14
14
|
|
|
@@ -30,7 +30,7 @@ export function pushManagerFactory(
|
|
|
30
30
|
pollingManager: IPollingManager,
|
|
31
31
|
): IPushManager | undefined {
|
|
32
32
|
|
|
33
|
-
const { settings, storage,
|
|
33
|
+
const { settings, storage, serviceApi, readiness, platform, telemetryTracker } = params;
|
|
34
34
|
|
|
35
35
|
// `userKey` is the matching key of main client in client-side SDK.
|
|
36
36
|
// It can be used to check if running on client-side or server-side SDK.
|
|
@@ -45,7 +45,7 @@ export function pushManagerFactory(
|
|
|
45
45
|
log.warn(STREAMING_FALLBACK, [e]);
|
|
46
46
|
return;
|
|
47
47
|
}
|
|
48
|
-
const authenticate = authenticateFactory(
|
|
48
|
+
const authenticate = authenticateFactory(serviceApi.fetchAuth);
|
|
49
49
|
|
|
50
50
|
// init feedback loop
|
|
51
51
|
const pushEmitter = new platform.EventEmitter() as IPushEventEmitter;
|
|
@@ -9,7 +9,7 @@ export function eventsSubmitterFactory(params: ISdkFactoryContextSync) {
|
|
|
9
9
|
|
|
10
10
|
const {
|
|
11
11
|
settings: { log, scheduler: { eventsPushRate }, startup: { eventsFirstPushWindow } },
|
|
12
|
-
|
|
12
|
+
serviceApi: { postEventsBulk },
|
|
13
13
|
storage: { events },
|
|
14
14
|
} = params;
|
|
15
15
|
|
|
@@ -41,7 +41,7 @@ export function impressionsSubmitterFactory(params: ISdkFactoryContextSync) {
|
|
|
41
41
|
|
|
42
42
|
const {
|
|
43
43
|
settings: { log, scheduler: { impressionsRefreshRate }, core: { labelsEnabled } },
|
|
44
|
-
|
|
44
|
+
serviceApi: { postTestImpressionsBulk },
|
|
45
45
|
storage: { impressions },
|
|
46
46
|
entityType
|
|
47
47
|
} = params;
|
|
@@ -119,12 +119,12 @@ export function telemetrySubmitterFactory(params: ISdkFactoryContextSync) {
|
|
|
119
119
|
const { storage: { telemetry }, platform: { now } } = params;
|
|
120
120
|
if (!telemetry || !now) return; // No submitter created if telemetry cache is not defined
|
|
121
121
|
|
|
122
|
-
const { settings, settings: { log, scheduler: { telemetryRefreshRate } },
|
|
122
|
+
const { settings, settings: { log, scheduler: { telemetryRefreshRate } }, serviceApi, readiness } = params;
|
|
123
123
|
const stopTimer = timer(now);
|
|
124
124
|
|
|
125
125
|
const submitter = firstPushWindowDecorator(
|
|
126
126
|
submitterFactory(
|
|
127
|
-
log,
|
|
127
|
+
log, serviceApi.postMetricsUsage,
|
|
128
128
|
telemetry,
|
|
129
129
|
telemetryRefreshRate, undefined, 0, true
|
|
130
130
|
),
|
|
@@ -140,7 +140,7 @@ export function telemetrySubmitterFactory(params: ISdkFactoryContextSync) {
|
|
|
140
140
|
|
|
141
141
|
// Post config data when the SDK is ready and if the telemetry submitter was started
|
|
142
142
|
if (submitter.isRunning()) {
|
|
143
|
-
const postMetricsConfigTask = submitterFactory(log,
|
|
143
|
+
const postMetricsConfigTask = submitterFactory(log, serviceApi.postMetricsConfig, telemetryCacheConfigAdapter(telemetry, settings), 0, undefined, 0, true);
|
|
144
144
|
postMetricsConfigTask.execute();
|
|
145
145
|
}
|
|
146
146
|
});
|
|
@@ -11,7 +11,7 @@ export function uniqueKeysSubmitterFactory(params: ISdkFactoryContextSync) {
|
|
|
11
11
|
|
|
12
12
|
const {
|
|
13
13
|
settings: { log, core: { key } },
|
|
14
|
-
|
|
14
|
+
serviceApi: { postUniqueKeysBulkCs, postUniqueKeysBulkSs },
|
|
15
15
|
storage: { uniqueKeys }
|
|
16
16
|
} = params;
|
|
17
17
|
|
|
@@ -28,7 +28,7 @@ export function syncManagerOnlineFactory(
|
|
|
28
28
|
pollingManagerFactory?: (params: ISdkFactoryContextSync, definitionChangesFetcher: IDefinitionChangesFetcher, segmentChangesFetcher: ISegmentChangesFetcher) => IPollingManager,
|
|
29
29
|
pushManagerFactory?: (params: ISdkFactoryContextSync, pollingManager: IPollingManager) => IPushManager | undefined,
|
|
30
30
|
definitionFetcherFactory = splitChangesFetcherFactory,
|
|
31
|
-
segmentFetcherFactory = (params: ISdkFactoryContextSync) => segmentChangesFetcherFactory(params.
|
|
31
|
+
segmentFetcherFactory = (params: ISdkFactoryContextSync) => segmentChangesFetcherFactory(params.serviceApi.fetchSegmentChanges)
|
|
32
32
|
): (params: ISdkFactoryContextSync) => ISyncManagerCS {
|
|
33
33
|
|
|
34
34
|
/**
|
|
@@ -59,7 +59,7 @@ export const base = {
|
|
|
59
59
|
// Telemetry Server
|
|
60
60
|
telemetry: 'https://telemetry.split.io/api',
|
|
61
61
|
// Configs Server
|
|
62
|
-
configs: 'https://configs.split.io
|
|
62
|
+
configs: 'https://configs.split.io',
|
|
63
63
|
},
|
|
64
64
|
|
|
65
65
|
// Defines which kind of storage we should instantiate.
|
package/types/splitio.d.ts
CHANGED
|
@@ -213,7 +213,7 @@ interface IServerSideSharedSettings {
|
|
|
213
213
|
*/
|
|
214
214
|
startup?: {
|
|
215
215
|
/**
|
|
216
|
-
* Time in seconds
|
|
216
|
+
* Time in seconds before emitting the `SDK_READY_TIMED_OUT` event.
|
|
217
217
|
*
|
|
218
218
|
* @defaultValue `15`
|
|
219
219
|
*/
|
|
@@ -373,7 +373,7 @@ interface IClientSideSyncSharedSettings extends IClientSideSharedSettings, ISync
|
|
|
373
373
|
*/
|
|
374
374
|
startup?: {
|
|
375
375
|
/**
|
|
376
|
-
* Time in seconds
|
|
376
|
+
* Time in seconds before emitting the `SDK_READY_TIMED_OUT` event.
|
|
377
377
|
*
|
|
378
378
|
* @defaultValue `10`
|
|
379
379
|
*/
|
|
@@ -1439,7 +1439,7 @@ declare namespace SplitIO {
|
|
|
1439
1439
|
*/
|
|
1440
1440
|
startup?: {
|
|
1441
1441
|
/**
|
|
1442
|
-
* Time in seconds
|
|
1442
|
+
* Time in seconds before emitting the `SDK_READY_TIMED_OUT` event.
|
|
1443
1443
|
*
|
|
1444
1444
|
* @defaultValue `10`
|
|
1445
1445
|
*/
|
|
@@ -2341,8 +2341,8 @@ declare namespace SplitIO {
|
|
|
2341
2341
|
*/
|
|
2342
2342
|
logLevel?: LogLevel;
|
|
2343
2343
|
/**
|
|
2344
|
-
* Time in seconds
|
|
2345
|
-
* A value of
|
|
2344
|
+
* Time in seconds before emitting the `SDK_READY_TIMED_OUT` event.
|
|
2345
|
+
* A value of `-1` disables the timeout and thus the event is never emitted.
|
|
2346
2346
|
*
|
|
2347
2347
|
* @defaultValue `10`
|
|
2348
2348
|
*/
|
|
@@ -2354,19 +2354,19 @@ declare namespace SplitIO {
|
|
|
2354
2354
|
/**
|
|
2355
2355
|
* String property to override the base URL where the SDK will get JWT authentication credentials.
|
|
2356
2356
|
*
|
|
2357
|
-
* @defaultValue `'https://auth.split.io
|
|
2357
|
+
* @defaultValue `'https://auth.split.io'`
|
|
2358
2358
|
*/
|
|
2359
2359
|
auth?: string;
|
|
2360
2360
|
/**
|
|
2361
2361
|
* String property to override the base URL where the SDK will get rollout plan related data, like configs and segments definitions.
|
|
2362
2362
|
*
|
|
2363
|
-
* @defaultValue `'https://configs.split.io
|
|
2363
|
+
* @defaultValue `'https://configs.split.io'`
|
|
2364
2364
|
*/
|
|
2365
2365
|
configs?: string;
|
|
2366
2366
|
/**
|
|
2367
2367
|
* String property to override the base URL where the SDK will post event-related information like impressions.
|
|
2368
2368
|
*
|
|
2369
|
-
* @defaultValue `'https://events.split.io
|
|
2369
|
+
* @defaultValue `'https://events.split.io'`
|
|
2370
2370
|
*/
|
|
2371
2371
|
events?: string;
|
|
2372
2372
|
};
|