-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathindex.ts
More file actions
207 lines (174 loc) · 8.36 KB
/
index.ts
File metadata and controls
207 lines (174 loc) · 8.36 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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
import { merge, get } from '../lang';
import { validateMode } from './mode';
import { validateSplitFilters } from './splitFilters';
import { STANDALONE_MODE, OPTIMIZED, LOCALHOST_MODE, DEBUG, FLAG_SPEC_VERSION } from '../constants';
import { validImpressionsMode } from './impressionsMode';
import { ISettingsValidationParams } from './types';
import { ISettings } from '../../types';
import { validateKey } from '../inputValidation/key';
import { ERROR_MIN_CONFIG_PARAM, LOG_PREFIX_CLIENT_INSTANTIATION } from '../../logger/constants';
// Exported for telemetry
export const base = {
// Define which kind of object you want to retrieve from SplitFactory
mode: STANDALONE_MODE,
core: {
// API token (tight to an environment)
authorizationKey: undefined,
// key used in your system (only required for browser version)
key: undefined,
// toggle impressions tracking of labels
labelsEnabled: true,
// toggle sendind (true) or not sending (false) IP and Host Name with impressions, events, and telemetries requests (only used on nodejs version)
IPAddressesEnabled: undefined
},
scheduler: {
// fetch feature updates each 60 sec
featuresRefreshRate: 60,
// fetch segments updates each 60 sec
segmentsRefreshRate: 60,
// publish telemetry stats each 3600 secs (1 hour)
telemetryRefreshRate: 3600,
// publish evaluations each 300 sec (default value for OPTIMIZED impressions mode)
impressionsRefreshRate: 300,
// fetch offline changes each 15 sec
offlineRefreshRate: 15,
// publish events every 60 seconds after the first flush
eventsPushRate: 60,
// how many events will be queued before flushing
eventsQueueSize: 500,
// how many impressions will be queued before flushing
impressionsQueueSize: 30000,
// backoff base seconds to wait before re attempting to connect to push notifications
pushRetryBackoffBase: 1,
},
urls: {
// CDN having all the information for your environment
sdk: 'https://sdk.split.io/api',
// SDK event and impression endpoints
events: 'https://events.split.io/api',
// SDK Auth Server
auth: 'https://auth.split.io/api',
// Streaming Server
streaming: 'https://streaming.split.io',
// Telemetry Server
telemetry: 'https://telemetry.split.io/api',
},
// Defines which kind of storage we should instantiate.
storage: undefined,
// Defines if the logs are enabled, SDK wide.
debug: undefined,
// Defines the impression listener.
impressionListener: undefined,
// Instance version.
version: undefined,
// List of integrations.
integrations: undefined,
// toggle using (true) or not using (false) Server-Side Events for synchronizing storage
streamingEnabled: true,
sync: {
splitFilters: undefined,
// impressions collection mode
impressionsMode: OPTIMIZED,
enabled: true,
flagSpecVersion: FLAG_SPEC_VERSION
},
// Logger
log: undefined
};
function fromSecondsToMillis(n: number) {
return Math.round(n * 1000);
}
/**
* Validates the given config and use it to build a settings object.
* NOTE: it doesn't validate the SDK Key. Call `validateApiKey` or `validateAndTrackApiKey` for that after settings validation.
*
* @param config - user defined configuration
* @param validationParams - defaults and fields validators used to validate and creates a settings object from a given config
*/
export function settingsValidation(config: unknown, validationParams: ISettingsValidationParams) {
const { defaults, runtime, storage, integrations, logger, consent, flagSpec } = validationParams;
// creates a settings object merging base, defaults and config objects.
const withDefaults = merge({}, base, defaults, config) as ISettings;
// Keeps reference to the `features` property
withDefaults.features = get(config, 'features');
// ensure a valid logger.
// First thing to validate, since other validators might use the logger.
const log = logger(withDefaults); // @ts-ignore, modify readonly prop
withDefaults.log = log;
// ensure a valid impressionsMode
const sync = withDefaults.sync;
sync.impressionsMode = validImpressionsMode(log, sync.impressionsMode);
function validateMinValue(paramName: string, actualValue: number, minValue: number) {
if (actualValue >= minValue) return actualValue;
// actualValue is not a number or is lower than minValue
log.error(ERROR_MIN_CONFIG_PARAM, [paramName, minValue]);
return minValue;
}
// Scheduler periods
const { scheduler, startup } = withDefaults;
scheduler.featuresRefreshRate = fromSecondsToMillis(scheduler.featuresRefreshRate);
scheduler.segmentsRefreshRate = fromSecondsToMillis(scheduler.segmentsRefreshRate);
scheduler.offlineRefreshRate = fromSecondsToMillis(scheduler.offlineRefreshRate);
scheduler.eventsPushRate = fromSecondsToMillis(scheduler.eventsPushRate);
scheduler.telemetryRefreshRate = fromSecondsToMillis(validateMinValue('telemetryRefreshRate', scheduler.telemetryRefreshRate, 60));
// Default impressionsRefreshRate for DEBUG mode is 60 secs
if (get(config, 'scheduler.impressionsRefreshRate') === undefined && sync.impressionsMode === DEBUG) scheduler.impressionsRefreshRate = 60;
scheduler.impressionsRefreshRate = fromSecondsToMillis(scheduler.impressionsRefreshRate);
// Log deprecation for old telemetry param
if (scheduler.metricsRefreshRate) log.warn('`metricsRefreshRate` will be deprecated soon. For configuring telemetry rates, update `telemetryRefreshRate` value in configs');
// Startup periods
startup.requestTimeoutBeforeReady = fromSecondsToMillis(startup.requestTimeoutBeforeReady);
startup.readyTimeout = fromSecondsToMillis(startup.readyTimeout);
startup.eventsFirstPushWindow = fromSecondsToMillis(startup.eventsFirstPushWindow);
// ensure a valid SDK mode
// @ts-ignore, modify readonly prop
withDefaults.mode = validateMode(withDefaults.core.authorizationKey, withDefaults.mode);
// ensure a valid Storage based on mode defined.
// @ts-ignore, modify readonly prop
if (storage) withDefaults.storage = storage(withDefaults);
// Validate key and TT (for client-side)
const maybeKey = withDefaults.core.key;
if (validationParams.acceptKey) {
// Although `key` is required in client-side, it can be omitted in LOCALHOST mode. In that case, the value `localhost_key` is used.
if (withDefaults.mode === LOCALHOST_MODE && maybeKey === undefined) {
withDefaults.core.key = 'localhost_key';
} else {
// Keeping same behavior than JS SDK: if settings key or TT are invalid,
// `false` value is used as bound key/TT of the default client, which leads to some issues.
// @ts-ignore, @TODO handle invalid keys as a non-recoverable error?
withDefaults.core.key = validateKey(log, maybeKey, LOG_PREFIX_CLIENT_INSTANTIATION);
}
} else {
// On server-side, key is undefined and used to distinguish from client-side
if (maybeKey !== undefined) log.warn('Provided `key` is ignored in server-side SDK.'); // @ts-ignore
withDefaults.core.key = undefined;
}
// Current ip/hostname information
// @ts-ignore, modify readonly prop
withDefaults.runtime = runtime(withDefaults);
// ensure a valid list of integrations.
// `integrations` returns an array of valid integration items.
// @ts-ignore, modify readonly prop
if (integrations) withDefaults.integrations = integrations(withDefaults);
// validate push options
if (withDefaults.streamingEnabled !== false) { // @ts-ignore, modify readonly prop
withDefaults.streamingEnabled = true;
// Backoff bases.
// We are not checking if bases are positive numbers. Thus, we might be re-authenticating immediately (`setTimeout` with NaN or negative number)
scheduler.pushRetryBackoffBase = fromSecondsToMillis(scheduler.pushRetryBackoffBase);
}
// validate sync enabled
if (sync.enabled !== false) {
sync.enabled = true;
}
// validate the `splitFilters` settings and parse splits query
const splitFiltersValidation = validateSplitFilters(log, sync.splitFilters, withDefaults.mode);
sync.splitFilters = splitFiltersValidation.validFilters;
sync.__splitFiltersValidation = splitFiltersValidation;
// ensure a valid flag spec version
sync.flagSpecVersion = flagSpec ? flagSpec(withDefaults) : FLAG_SPEC_VERSION;
// ensure a valid user consent value
// @ts-ignore, modify readonly prop
withDefaults.userConsent = consent ? consent(withDefaults) : undefined;
return withDefaults;
}