8000 refactor: Consolidate datafile-manager package into optimizely-sdk by ozayr-zaviar · Pull Request #781 · optimizely/javascript-sdk · GitHub
[go: up one dir, main page]

Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* Copyright 2022, Optimizely
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
let items: {[key: string]: string} = {}

export default class AsyncStorage {

static getItem(key: string, callback?: (error?: Error, result?: string) => void): Promise<string | null> {
return new Promise(resolve => {
setTimeout(() => resolve(items[key] || null), 1)
})
}

static setItem(key: string, value: string, callback?: (error?: Error) => void): Promise<void> {
return new Promise((resolve) => {
setTimeout(() => {
items[key] = value
resolve()
}, 1)
})
}

static removeItem(key: string, callback?: (error?: Error, result?: string) => void): Promise<string | null> {
return new Promise(resolve => {
setTimeout(() => {
items[key] && delete items[key]
// @ts-ignore
resolve()
}, 1)
})
}

static dumpItems(): {[key: string]: string} {
return items
}

static clearStore(): void {
items = {}
}
}
DCA4
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright 2022, Optimizely
* Copyright 2020, Optimizely
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -13,17 +13,28 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
let items: {[key: string]: string} = {}

let items: {[key: string]: string} = {}
export default class AsyncStorage {

static getItem(key: string, callback?: (error?: Error, result?: string) => void): Promise<string | null> {
return new Promise(resolve => {
setTimeout(() => resolve(items[key] || null), 1)
return new Promise((resolve, reject) => {
switch (key) {
case 'keyThatExists':
resolve('{ "name": "Awesome Object" }')
break
case 'keyThatDoesNotExist':
resolve(null)
break
case 'keyWithInvalidJsonObject':
resolve('bad json }')
break
default:
setTimeout(() => resolve(items[key] || null), 1)
}
})
}

static setItem(key: string, value: string, callback?: (error?: Error) => void): Promise<void> {
static setItem(key: string, value: string, callback?: (error?: Error) => void): Promise<void> {
return new Promise((resolve) => {
setTimeout(() => {
items[key] = value
Expand Down
14 changes: 13 additions & 1 deletion packages/optimizely-sdk/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,24 @@ module.exports = {
"^.+\\.(ts|tsx|js|jsx)$": "ts-jest",
},
"testRegex": "(/tests/.*|(\\.|/)(test|spec))\\.tsx?$",
moduleNameMapper: {
// 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
"uuid": require.resolve('uuid'),
},
"testPathIgnorePatterns" : [
"tests/testUtils.ts"
],
"moduleFileExtensions": [
"ts",
"tsx",
"js",
"jsx",
"json",
"node"
]
],
"resetMocks": false,
"setupFiles": [
"jest-localstorage-mock",
],
testEnvironment: "jsdom"
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { cloneDeep } from 'lodash';

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

import * as projectConfig from './index';
import { ERROR_MESSAGES, LOG_MESSAGES } from '../../utils/enums';
Expand Down
2 changes: 1 addition & 1 deletion packages/optimizely-sdk/lib/index.browser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import eventProcessorConfigValidator from './utils/event_processor_config_valida
import { createNotificationCenter } from './core/notification_center';
import { default as eventProcessor } from './plugins/event_processor';
import { OptimizelyDecideOption, Client, Config } from './shared_types';
import { createHttpPollingDatafileManager } from './plugins/datafile_manager/http_polling_datafile_manager';
import { createHttpPollingDatafileManager } from './plugins/datafile_manager/browser_http_polling_datafile_manager';
import { EXECUTION_CONTEXT_TYPE } from './utils/enums';
import { ExecutionContext } from './utils/execution_context';

Expand Down
2 changes: 1 addition & 1 deletion packages/optimizely-sdk/lib/index.react_native.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import eventProcessorConfigValidator from './utils/event_processor_config_valida
import { createNotificationCenter } from './core/notification_center';
import { createEventProcessor } from './plugins/event_processor/index.react_native';
import { OptimizelyDecideOption, Client, Config } from './shared_types';
import { createHttpPollingDatafileManager } from './plugins/datafile_manager/http_polling_datafile_manager';
import { createHttpPollingDatafileManager } from './plugins/datafile_manager/react_native_http_polling_datafile_manager';
import { EXECUTION_CONTEXT_TYPE } from './utils/enums';
import { ExecutionContext } from './utils/execution_context';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/**
* Copyright 2019-2020, Optimizely
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { BACKOFF_BASE_WAIT_SECONDS_BY_ERROR_COUNT } from './config';

function randomMilliseconds(): number {
return Math.round(Math.random() * 1000);
}

export default class BackoffController {
private errorCount = 0;

getDelay(): number {
if (this.errorCount === 0) {
return 0;
}
const baseWaitSeconds =
BACKOFF_BASE_WAIT_SECONDS_BY_ERROR_COUNT[
Math.min(BACKOFF_BASE_WAIT_SECONDS_BY_ERROR_COUNT.length - 1, this.errorCount)
];
return baseWaitSeconds * 1000 + randomMilliseconds();
}

countError(): void {
if (this.errorCount < BACKOFF_BASE_WAIT_SECONDS_BY_ERROR_COUNT.length - 1) {
this.errorCount++;
}
}

reset(): void {
this.errorCount = 0;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* Copyright 2022, Optimizely
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { makeGetRequest } from './browserRequest';
import HttpPollingDatafileManager from './httpPollingDatafileManager';
import { Headers, AbortableRequest } from './http';
import { DatafileManagerConfig } from './datafileManager';

export default class BrowserDatafileManager extends HttpPollingDatafileManager {
protected makeGetRequest(reqUrl: string, headers: Headers): AbortableRequest {
return makeGetRequest(reqUrl, headers);
}

protected getConfigDefaults(): Partial<DatafileManagerConfig> {
return {
autoUpdate: false,
};
}
}
Original file line number Diff line number Diff line change
DC9C @@ -0,0 +1,96 @@
/**
* Copyright 2022, Optimizely
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { AbortableRequest, Response, Headers } from './http';
import { REQUEST_TIMEOUT_MS } from './config';
import { getLogger } from '../logging';

const logger = getLogger('DatafileManager');

const GET_METHOD = 'GET';
const READY_STATE_DONE = 4;

function parseHeadersFromXhr(req: XMLHttpRequest): Headers {
const allHeadersString = req.getAllResponseHeaders();

if (allHeadersString === null) {
return {};
}

const headerLines = allHeadersString.split('\r\n');
const headers: Headers = {};
headerLines.forEach(headerLine => {
const separatorIndex = headerLine.indexOf(': ');
if (separatorIndex > -1) {
const headerName = headerLine.slice(0, separatorIndex);
const headerValue = headerLine.slice(separatorIndex + 2);
if (headerValue.length > 0) {
headers[headerName] = headerValue;
}
}
});
return headers;
}

function setHeadersInXhr(headers: Headers, req: XMLHttpRequest): void {
Object.keys(headers).forEach(headerName => {
const header = headers[headerName];
req.setRequestHeader(headerName, header!);
});
}

export function makeGetRequest(reqUrl: string, headers: Headers): AbortableRequest {
const req = new XMLHttpRequest();

const responsePromise: Promise<Response> = new Promise((resolve, reject) => {
req.open(GET_METHOD, reqUrl, true);

setHeadersInXhr(headers, req);

req.onreadystatechange = (): void => {
if (req.readyState === READY_STATE_DONE) {
const statusCode = req.status;
if (statusCode === 0) {
reject(new Error('Request error'));
return;
}

const headers = parseHeadersFromXhr(req);
const resp: Response = {
statusCode: req.status,
body: req.responseText,
headers,
};
resolve(resp);
}
};

req.timeout = REQUEST_TIMEOUT_MS;

req.ontimeout = (): void => {
logger.error('Request timed out');
};

req.send();
});

return {
responsePromise,
abort(): void {
req.abort();
},
};
}
27 changes: 27 additions & 0 deletions packages/optimizely-sdk/lib/modules/datafile-manager/config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Copyright 2022, Optimizely
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

export const DEFAULT_UPDATE_INTERVAL = 5 * 60 * 1000; // 5 minutes

export const MIN_UPDATE_INTERVAL = 1000;

export const DEFAULT_URL_TEMPLATE = `https://cdn.optimizely.com/datafiles/%s.json`;

export const DEFAULT_AUTHENTICATED_URL_TEMPLATE = `https://config.optimizely.com/datafiles/auth/%s.json`;

export const BACKOFF_BASE_WAIT_SECONDS_BY_ERROR_COUNT = [0, 8, 16, 32, 64, 128, 256, 512];

export const REQUEST_TIMEOUT_MS = 60 * 1000; // 1 minute
Loading
0