featurehub-repository
Version:
Core package of API that exposes FeatureHub feature flags, values and configuration to client applications written in Typescript or Javascript.
194 lines • 7.71 kB
JavaScript
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
import { ObjectSerializer, SSEResultState } from './models/models';
import { fhLog } from './feature_hub_config';
class PollingBase {
constructor(url, frequency, callback) {
this.stopped = false;
this.url = url;
this.frequency = frequency;
this._callback = callback;
}
attributeHeader(header) {
return __awaiter(this, void 0, void 0, function* () {
this._header = header;
return this.poll();
});
}
stop() {
this.stopped = true;
}
delayTimer() {
return __awaiter(this, void 0, void 0, function* () {
return new Promise(((resolve, reject) => {
if (!this.stopped && this.frequency > 0) {
setTimeout(() => this.poll().then(resolve).catch(reject), this.frequency);
}
else {
resolve();
}
}));
});
}
}
class BrowserPollingService extends PollingBase {
constructor(options, url, frequency, callback) {
super(url, frequency, callback);
this._options = options;
}
poll() {
return __awaiter(this, void 0, void 0, function* () {
return new Promise((resolve, reject) => {
const req = new XMLHttpRequest();
req.open('GET', this.url);
req.setRequestHeader('Content-type', 'application/json');
if (this._header) {
req.setRequestHeader('x-featurehub', this._header);
}
req.send();
req.onreadystatechange = () => {
if (req.readyState === 4) {
if (req.status === 200) {
this._callback(ObjectSerializer.deserialize(JSON.parse(req.responseText), 'Array<Environment>'));
resolve();
}
else if (req.status >= 400 && req.status < 500) {
reject(`Failed to connect to ${this.url} with ${req.status}`);
}
else {
this.delayTimer().then(resolve).catch(reject);
}
}
};
});
});
}
}
class NodejsPollingService extends PollingBase {
constructor(options, url, frequency, _callback) {
super(url, frequency, _callback);
this._options = options;
this.uri = new URL(this.url);
}
poll() {
return __awaiter(this, void 0, void 0, function* () {
return new Promise(((resolve, reject) => {
const http = this.uri.protocol === 'http:' ? require('http') : require('https');
let data = '';
let headers = this._header === undefined ? {} : {
'x-featurehub': this._header
};
const reqOptions = {
protocol: this.uri.protocol,
host: this.uri.host,
hostname: this.uri.hostname,
port: this.uri.port,
method: 'GET',
path: this.uri.pathname + this.uri.search,
headers: headers,
timeout: this._options.timeout || 8000
};
const req = http.request(reqOptions, (res) => {
res.on('data', (chunk) => data += chunk);
res.on('end', () => {
if (res.statusCode === 200) {
this._callback(ObjectSerializer.deserialize(JSON.parse(data), 'Array<Environment>'));
resolve();
}
else if (res.statusCode >= 400 && res.statusCode < 500) {
reject(`Failed to connect to ${this.url} with ${res.statusCode}`);
}
else {
this.delayTimer().then(resolve).catch(reject);
}
});
});
req.end();
}));
});
}
}
export class FeatureHubPollingClient {
constructor(repository, config, frequency, options = {}) {
this._frequency = frequency;
this._repository = repository;
this._options = options;
this._config = config;
this._url = config.getHost() + 'features?' + config.getApiKeys().map(e => 'sdkUrl=' + encodeURIComponent(e)).join('&');
}
_initService() {
if (this._pollingService === undefined) {
if (Object.prototype.toString.call(global.process) !== '[object process]') {
this._pollingService = new BrowserPollingService(this._options, this._url, this._frequency, (e) => this.response(e));
}
else {
this._pollingService = new NodejsPollingService(this._options, this._url, this._frequency, (e) => this.response(e));
}
fhLog.log(`featurehub: initialized polling client to ${this._url}`);
}
}
contextChange(header) {
return __awaiter(this, void 0, void 0, function* () {
if (!this._config.clientEvaluated()) {
if (this._xHeader !== header) {
this._xHeader = header;
this._initService();
return this._pollingService.attributeHeader(header);
}
}
});
}
clientEvaluated() {
return this._config.clientEvaluated();
}
requiresReplacementOnHeaderChange() {
return false;
}
close() {
if (this._pollingService) {
this._pollingService.stop();
}
}
poll() {
this._initService();
return this._pollingService.poll();
}
stop() {
this._pollingService.stop();
this._pollingService = undefined;
}
_restartTimer() {
setTimeout(() => this._pollingService.poll().catch((e) => {
fhLog.error(`Failed to poll, restarting in ${this._frequency}ms: ${e}`);
this._repository.notify(SSEResultState.Failure, null);
}).finally(() => this._restartTimer()), this._frequency);
}
response(environments) {
if (environments.length === 0) {
this._startable = false;
this.stop();
this._repository.notify(SSEResultState.Failure, null);
}
else {
const features = new Array();
environments.forEach(e => {
if (e.features.length > 0) {
e.features.forEach(f => {
f.environmentId = e.id;
});
features.push(...e.features);
}
});
this._repository.notify(SSEResultState.Features, features);
this._restartTimer();
}
}
}
//# sourceMappingURL=polling_sdk.js.map