Last active
February 2, 2020 20:45
-
-
Save kyle-ssg/ed6140ae5294b5d4dbe2c76db26dfb0a to your computer and use it in GitHub Desktop.
Trying out deno with Bullet Train
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let fetch; | |
let AsyncStorage; | |
const BULLET_TRAIN_KEY = "BULLET_TRAIN_DB"; | |
const defaultAPI = 'https://api.bullet-train.io/api/v1/'; | |
const BulletTrain = class { | |
constructor(props) { | |
if (props.fetch) { | |
fetch = props.fetch; | |
} else { | |
fetch = global.fetch; | |
} | |
AsyncStorage = props.AsyncStorage; | |
} | |
getJSON = (url, method, body) => { | |
const { environmentID } = this; | |
const options = { | |
method: method || 'GET', | |
body, | |
headers: { | |
'x-environment-key': environmentID | |
} | |
}; | |
if (method !== "GET") | |
options.headers['Content-Type'] = 'application/json; charset=utf-8' | |
return fetch(url, options) | |
.then(res => { | |
if (res.status >= 200 && res.status < 300) { | |
return res; | |
} | |
return res.text() | |
.then((res)=>{ | |
let err = res; | |
try { | |
err = JSON.parse(res); | |
} catch (e) {} | |
return Promise.reject(err); | |
}) | |
}) | |
.then(res => res.json()); | |
}; | |
getFlags = (resolve,reject) => { | |
const { onChange, onError, identity, api, disableCache } = this; | |
let resolved = false; | |
const handleResponse = ({ flags: features, traits }, segments) => { | |
// Handle server response | |
let flags = {}; | |
let userTraits = {}; | |
features = features||[]; | |
traits = traits||[]; | |
features.forEach(feature => { | |
flags[feature.feature.name.toLowerCase().replace(/ /g, '_')] = { | |
enabled: feature.enabled, | |
value: feature.feature_state_value | |
}; | |
}); | |
traits.forEach(trait => { | |
userTraits[trait.trait_key.toLowerCase().replace(/ /g, '_')] = trait.trait_value | |
}); | |
this.oldFlags = flags; | |
this.flags = flags; | |
this.traits = userTraits; | |
if (segments) { | |
let userSegments = {}; | |
segments.map((s)=>{ | |
userSegments[s.name] = s; | |
}); | |
this.segments = userSegments; | |
} | |
this.updateStorage(); | |
if (onChange) { | |
onChange(this.oldFlags, { isFromServer: true }); | |
} | |
}; | |
if (identity) { | |
return Promise.all([ | |
this.getJSON(api + 'identities/?identifier=' + encodeURIComponent(identity)), | |
]) | |
.then((res) => { | |
handleResponse(res[0],res[1]) | |
}).catch(({ message }) => { | |
onError && onError({ message }) | |
}); | |
} else { | |
return Promise.all([ | |
this.getJSON(api + "flags/") | |
]) | |
.then((res) => { | |
handleResponse({flags: res[0]},null) | |
if (resolve && !resolved) { | |
resolved = true; | |
resolve(); | |
} | |
}).catch((err) => { | |
if (reject && !resolved) { | |
resolved = true; | |
reject(err); | |
} | |
onError && onError(err) | |
}); | |
} | |
}; | |
init({ | |
environmentID, | |
api = defaultAPI, | |
onChange, | |
cacheFlags, | |
onError, | |
defaultFlags, | |
preventFetch, | |
enableLogs, | |
AsyncStorage:_AsyncStorage, | |
state | |
}) { | |
return new Promise((resolve, reject)=>{ | |
this.environmentID = environmentID; | |
this.api = api; | |
this.interval = null; | |
this.onChange = onChange; | |
this.onError = onError; | |
this.enableLogs = enableLogs; | |
this.flags = Object.assign({}, defaultFlags) || {}; | |
this.initialised = true; | |
this.timer = this.enableLogs ? new Date().valueOf() : null; | |
if (_AsyncStorage) { | |
AsyncStorage = _AsyncStorage; | |
} | |
this.cacheFlags = typeof AsyncStorage !== 'undefined' && cacheFlags; | |
this.setState(state) | |
if (!environmentID) { | |
reject('Please specify a environment id') | |
throw('Please specify a environment id'); | |
} | |
//If the user specified default flags emit a changed event immediately | |
if (cacheFlags) { | |
AsyncStorage.getItem(BULLET_TRAIN_KEY, (err, res) => { | |
if (res) { | |
try { | |
var json = JSON.parse(res); | |
this.setState(json); | |
this.log("Retrieved flags from cache", json); | |
if (this.flags) { // retrieved flags from local storage | |
if (this.onChange) { | |
this.onChange(null, { isFromServer: false }); | |
} | |
resolve(); | |
this.getFlags(Promise.resolve, Promise.reject); | |
} else { | |
this.getFlags(resolve, reject); | |
} | |
} catch(e) { | |
this.log("Exception fetching cached logs", e); | |
} | |
} | |
}); | |
} else if (!preventFetch) { | |
this.getFlags(resolve, reject); | |
} | |
}) | |
} | |
getAllFlags() { | |
return this.flags; | |
} | |
identify(userId) { | |
this.identity = userId; | |
if (this.initialised && !this.interval) | |
this.getFlags(); | |
} | |
getState() { | |
return { | |
api: this.api, | |
environmentID: this.environmentID, | |
flags: this.flags, | |
identity: this.identity, | |
segments: this.segments, | |
traits: this.traits, | |
} | |
} | |
setState(state) { | |
if (state) { | |
this.initialised = true; | |
this.api = state.api || this.api || defaultAPI; | |
this.environmentID = state.environmentID || this.environmentID ; | |
this.flags = state.flags || this.flags; | |
this.identity = state.identity || this.identity; | |
this.segments = state.segments || this.segments; | |
this.traits = state.traits || this.traits; | |
} | |
} | |
log(...args) { | |
if (this.enableLogs) { | |
console.log.apply(this, ["BULLET TRAIN:", new Date().valueOf()-this.timer,"ms", ...args]); | |
} | |
} | |
updateStorage() { | |
if (this.cacheFlags) { | |
const state = JSON.stringify(this.getState()); | |
this.log("Setting storage", state); | |
AsyncStorage.setItem(BULLET_TRAIN_KEY, state); | |
} | |
} | |
logout() { | |
this.identity = null; | |
this.segments = null; | |
this.traits = null; | |
if (this.initialised && !this.interval) { | |
this.getFlags(); | |
} | |
} | |
startListening(ticks = 1000) { | |
if (this.interval) { | |
return; | |
} | |
this.interval = setInterval(this.getFlags, ticks); | |
} | |
getSegments() { | |
// noop for now | |
// return this.segments; | |
} | |
stopListening() { | |
clearInterval(this.interval); | |
} | |
getValue = (key) => { | |
const flag = this.flags && this.flags[key]; | |
let res = null; | |
if (flag) { | |
res = flag.value; | |
} | |
//todo record check for value | |
return res; | |
} | |
getTrait = (key) => { | |
const trait = this.traits && this.traits[key]; | |
return trait; | |
} | |
setTrait = (key, trait_value) => { | |
const { getJSON, identity, api } = this; | |
const body = { | |
"identity": { | |
"identifier": identity | |
}, | |
"trait_key": key, | |
"trait_value": trait_value | |
} | |
return getJSON(`${api}traits/`, 'POST', JSON.stringify(body)) | |
.then(this.getFlags) | |
}; | |
incrementTrait = (trait_key, increment_by) => { | |
const { getJSON, identity, api } = this; | |
return getJSON(`${api}traits/increment-value/`, 'POST', JSON.stringify({ trait_key, increment_by, identifier:identity })) | |
.then(this.getFlags) | |
}; | |
hasFeature = (key) => { | |
const flag = this.flags && this.flags[key]; | |
let res = false; | |
if (flag && flag.enabled) { | |
res = true; | |
} | |
//todo record check for feature | |
return res; | |
} | |
}; | |
export default function ({ fetch, AsyncStorage }) { | |
return new BulletTrain({ fetch, AsyncStorage }); | |
}; | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment