8000 refactor: Consolidate datafile-manager package into optimizely-sdk (#… · optimizely/javascript-sdk@346f2c0 · GitHub
[go: up one dir, main page]

Skip to content

Commit 346f2c0

Browse files
authored
refactor: Consolidate datafile-manager package into optimizely-sdk (#781)
## Summary Integrated Datafile Manager package with Optimizely SDK to remove external dependency. ## Test plan All existing unit tests and FSC tests pass. ## Jira [FSSDK-8205]
1 parent 1a20b08 commit 346f2c0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+19464
-16286
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* Copyright 2022, Optimizely
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
let items: {[key: string]: string} = {}
17+
18+
export default class AsyncStorage {
19+
20+
static getItem(key: string, callback?: (error?: Error, result?: string) => void): Promise<string | null> {
21+
return new Promise(resolve => {
22+
setTimeout(() => resolve(items[key] || null), 1)
23+
})
24+
}
25+
26+
static setItem(key: string, value: string, callback?: (error?: Error) => void): Promise<void> {
27+
return new Promise((resolve) => {
28+
setTimeout(() => {
29+
items[key] = value
30+
resolve()
31+
}, 1)
32+
})
33+
}
34+
35+
static removeItem(key: string, callback?: (error?: Error, result?: string) => void): Promise<string | null> {
36+
return new Promise(resolve => {
37+
setTimeout(() => {
38+
items[key] && delete items[key]
39+
// @ts-ignore
40+
resolve()
41+
}, 1)
42+
})
43+
}
44+
45+
static dumpItems(): {[key: string]: string} {
46+
return items
47+
}
48+
49+
static clearStore(): void {
50+
items = {}
51+
}
52+
}

packages/optimizely-sdk/__mocks__/@react-native-async-storage/async-storage.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/**
2-
* Copyright 2022, Optimizely
2+
* Copyright 2020, Optimizely
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -13,17 +13,28 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
let items: {[key: string]: string} = {}
1716

17+
let items: {[key: string]: string} = {}
1818
export default class AsyncStorage {
19-
2019
static getItem(key: string, callback?: (error?: Error, result?: string) => void): Promise<string | null> {
21-
return new Promise(resolve => {
22-
setTimeout(() => resolve(items[key] || null), 1)
20+
return new Promise((resolve, reject) => {
21+
switch (key) {
22+
case 'keyThatExists':
23+
resolve('{ "name": "Awesome Object" }')
24+
break
25+
case 'keyThatDoesNotExist':
26+
resolve(null)
27+
break
28+
case 'keyWithInvalidJsonObject':
29+
resolve('bad json }')
30+
break
31+
default:
32+
setTimeout(() => resolve(items[key] || null), 1)
33+
}
2334
})
2435
}
2536

26-
static setItem(key: string, value: string, callback?: (error?: Error) => void): Promise<void> {
37+
static setItem(key: string, value: string, callback?: (error?: Error) => void): Promise<void> {
2738
return new Promise((resolve) => {
2839
setTimeout(() => {
2940
items[key] = value

packages/optimizely-sdk/jest.config.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,24 @@ module.exports = {
33
"^.+\\.(ts|tsx|js|jsx)$": "ts-jest",
44
},
55
"testRegex": "(/tests/.*|(\\.|/)(test|spec))\\.tsx?$",
6+
moduleNameMapper: {
7+
// Force module uuid to resolve with the CJS entry point, because Jest does not support package.json.exports. See https://github.com/uuidjs/uuid/issues/451
8+
"uuid": require.resolve('uuid'),
9+
},
10+
"testPathIgnorePatterns" : [
11+
"tests/testUtils.ts"
12+
],
613
"moduleFileExtensions": [
714
"ts",
815
"tsx",
916
"js",
1017
"jsx",
1118
"json",
1219
"node"
13-
]
20+
],
21+
"resetMocks": false,
22+
"setupFiles": [
23+
"jest-localstorage-mock",
24+
],
25+
testEnvironment: "jsdom"
1426
}

packages/optimizely-sdk/lib/core/project_config/project_config_manager.tests.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import { cloneDeep } from 'lodash';
1919

2020
import { sprintf } from '../../utils/fns';
2121
import * as logging from '../../modules/logging';
22-
import * as datafileManager from '@optimizely/js-sdk-datafile-manager';
22+
import datafileManager from '../../modules/datafile-manager/index.node';
2323

2424
import * as projectConfig from './index';
2525
import { ERROR_MESSAGES, LOG_MESSAGES } from '../../utils/enums';

packages/optimizely-sdk/lib/index.browser.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import eventProcessorConfigValidator from './utils/event_processor_config_valida
3131
import { createNotificationCenter } from './core/notification_center';
3232
import { default as eventProcessor } from './plugins/event_processor';
3333
import { OptimizelyDecideOption, Client, Config } from './shared_types';
34-
import { createHttpPollingDatafileManager } from './plugins/datafile_manager/http_polling_datafile_manager';
34+
import { createHttpPollingDatafileManager } from './plugins/datafile_manager/browser_http_polling_datafile_manager';
3535
import { EXECUTION_CONTEXT_TYPE } from './utils/enums';
3636
import { ExecutionContext } from './utils/execution_context';
3737

packages/optimizely-sdk/lib/index.react_native.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import eventProcessorConfigValidator from './utils/event_processor_config_valida
3131
import { createNotificationCenter } from './core/notification_center';
3232
import { createEventProcessor } from './plugins/event_processor/index.react_native';
3333
import { OptimizelyDecideOption, Client, Config } from './shared_types';
34-
import { createHttpPollingDatafileManager } from './plugins/datafile_manager/http_polling_datafile_manager';
34+
import { createHttpPollingDatafileManager } from './plugins/datafile_manager/react_native_http_polling_datafile_manager';
3535
import { EXECUTION_CONTEXT_TYPE } from './utils/enums';
3636
import { ExecutionContext } from './utils/execution_context';
3737

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* Copyright 2019-2020, Optimizely
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { BACKOFF_BASE_WAIT_SECONDS_BY_ERROR_COUNT } from './config';
18+
19+
function randomMilliseconds(): number {
20+
return Math.round(Math.random() * 1000);
21+
}
22+
23+
export default class BackoffController {
24+
private errorCount = 0;
25+
26+
getDelay(): number {
27+
if (this.errorCount === 0) {
28+
return 0;
29+
}
30+
const baseWaitSeconds =
31+
BACKOFF_BASE_WAIT_SECONDS_BY_ERROR_COUNT[
32+
Math.min(BACKOFF_BASE_WAIT_SECONDS_BY_ERROR_COUNT.length - 1, this.errorCount)
33+
];
34+
return baseWaitSeconds * 1000 + randomMilliseconds();
35+
}
36+
37+
countError(): void {
38+
if (this.errorCount < BACKOFF_BASE_WAIT_SECONDS_BY_ERROR_COUNT.length - 1) {
39+
this.errorCount++;
40+
}
41+
}
42+
43+
reset(): void {
44+
this.errorCount = 0;
45+
}
46+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* Copyright 2022, Optimizely
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { makeGetRequest } from './browserRequest';
18+
import HttpPollingDatafileManager from './httpPollingDatafileManager';
19+
import { Headers, AbortableRequest } from './http';
20+
import { DatafileManagerConfig } from './datafileManager';
21+
22+
export default class BrowserDatafileManager extends HttpPollingDatafileManager {
23+
protected makeGetRequest(reqUrl: string, headers: Headers): AbortableRequest {
24+
return makeGetRequest(reqUrl, headers);
25+
}
26+
27+
protected getConfigDefaults(): Partial<DatafileManagerConfig> {
28+
return {
29+
autoUpdate: false,
30+
};
31+
}
32+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/**
2+
* Copyright 2022, Optimizely
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import { AbortableRequest, Response, Headers } from './http';
18+
import { REQUEST_TIMEOUT_MS } from './config';
19+
import { getLogger } from '../logging';
20+
21+
const logger = getLogger('DatafileManager');
22+
23+
const GET_METHOD = 'GET';
24+
const READY_STATE_DONE = 4;
25+
26+
function parseHeadersFromXhr(req: XMLHttpRequest): Headers {
27+
const allHeadersString = req.getAllResponseHeaders();
28+
29+
if (allHeadersString === null) {
30+
return {};
31+
}
32+
33+
const headerLines = allHeadersString.split('\r\n');
34+
const headers: Headers = {};
35+
headerLines.forEach(headerLine => {
36+
const separatorIndex = headerLine.indexOf(': ');
37+
if (separatorIndex > -1) {
38+
const headerName = headerLine.slice(0, separatorIndex);
39+
const headerValue = headerLine.slice(separatorIndex + 2);
40+
if (headerValue.length > 0) {
41+
headers[headerName] = headerValue;
42+
}
43+
}
44+
});
45+
return headers;
46+
}
47+
48+
function setHeadersInXhr(headers: Headers, req: XMLHttpRequest): void {
49+
Object.keys(headers).forEach(headerName => {
50+
const header = headers[headerName];
51+
req.setRequestHeader(headerName, header!);
52+
})< 10000 /span>;
53+
}
54+
55+
export function makeGetRequest(reqUrl: string, headers: Headers): AbortableRequest {
56+
const req = new XMLHttpRequest();
57+
58+
const responsePromise: Promise<Response> = new Promise((resolve, reject) => {
59+
req.open(GET_METHOD, reqUrl, true);
60+
61+
setHeadersInXhr(headers, req);
62+
63+
req.onreadystatechange = (): void => {
64+
if (req.readyState === READY_STATE_DONE) {
65+
const statusCode = req.status;
66+
if (statusCode === 0) {
67+
reject(new Error('Request error'));
68+
return;
69+
}
70+
71+
const headers = parseHeadersFromXhr(req);
72+
const resp: Response = {
73+
statusCode: req.status,
74+
body: req.responseText,
75+
headers,
76+
};
77+
resolve(resp);
78+
}
79+
};
80+
81+
req.timeout = REQUEST_TIMEOUT_MS;
82+
83+
req.ontimeout = (): void => {
84+
logger.error('Request timed out');
85+
};
86+
87+
req.send();
88+
});
89+
90+
return {
91+
responsePromise,
92+
abort(): void {
93+
req.abort();
94+
},
95+
};
96+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* Copyright 2022, Optimizely
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
export const DEFAULT_UPDATE_INTERVAL = 5 * 60 * 1000; // 5 minutes
18+
19+
export const MIN_UPDATE_INTERVAL = 1000;
20+
21+
export const DEFAULT_URL_TEMPLATE = `https://cdn.optimizely.com/datafiles/%s.json`;
22+
23+
export const DEFAULT_AUTHENTICATED_URL_TEMPLATE = `https://config.optimizely.com/datafiles/auth/%s.json`;
24+
25+
export const BACKOFF_BASE_WAIT_SECONDS_BY_ERROR_COUNT = [0, 8, 16, 32, 64, 128, 256, 512];
26+
27+
export const REQUEST_TIMEOUT_MS = 60 * 1000; // 1 minute

0 commit comments

Comments
 (0)
0