-
-
Save lye/42c77faef2071d56720ce59c657cfa54 to your computer and use it in GitHub Desktop.
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
var buildId, | |
tmpCache = {}, | |
baseUrl = "https://api.guildwars2.com"; | |
export function refetchBuildId() { | |
buildId = fetch(`${baseUrl}/v2/build`) | |
.then((res) => res.json()) | |
.then((res) => res.id); | |
return buildId; | |
} | |
// Make sure the buildId is set. | |
refetchBuildId(); | |
export{ buildId }; | |
export function setBaseUrl(url) { | |
baseUrl = url; | |
return refetchBuildId(); | |
} | |
export function objectify(ary, field) { | |
if(!ary) { | |
return {}; | |
} | |
return ary.reduce( | |
(a, x) => { | |
a[x[field || "id"]] = x; | |
return a; | |
}, | |
{} | |
); | |
} | |
function cacheSet(k, v) { | |
try { | |
tmpCache[k] = JSON.stringify(v); | |
localStorage.setItem(k, JSON.stringify(v)); | |
} catch(ex) { | |
// XXX: it's horribly easy to smash the quota; just | |
// rely on the tmpCache for this case. | |
} | |
} | |
function cacheGet(k) { | |
// Check the tmpCache first since it'll have the freshest value. | |
var val = tmpCache[k] || localStorage.getItem(k) ; | |
if(val) { | |
val = JSON.parse(val); | |
} | |
return val; | |
} | |
export function memoize(key, fn) { | |
/* eslint no-console:[0] */ | |
return new Promise((resolve, reject) => { | |
return buildId.then((build) => { | |
var cachedTag = cacheGet(key); | |
if(cachedTag && cachedTag.build >= build) { | |
return resolve(cachedTag.data); | |
} | |
return fn().then( | |
(data) => { | |
cacheSet(key, { | |
build : build, | |
data : data | |
}); | |
return resolve(data, build); | |
}, | |
(err) => { | |
if(cachedTag) { | |
console.error(`Using stale cache entry for ${key}; got error`, err); | |
return resolve(cachedTag.data, cachedTag.build); | |
} | |
return reject(err); | |
} | |
); | |
}); | |
}); | |
} | |
export function memoizeBulk(keyPattern, fn, idList) { | |
return buildId.then((build) => { | |
return new Promise((resolve, reject) => { | |
// Figure out which aren't cached. | |
var missing = [], | |
needsFetch = [], | |
results = idList.reduce( | |
(a, id) => { | |
var key = keyPattern.replace("{id}", id), | |
cachedTag = cacheGet(key); | |
if(!cachedTag) { | |
missing.push(id); | |
needsFetch.push(id); | |
} else if(cachedTag.build < build) { | |
needsFetch.push(id); | |
} | |
a[id] = cachedTag && cachedTag.data; | |
return a; | |
}, | |
{} | |
); | |
// If nothing is missing or stale, early-out. | |
if(!needsFetch.length) { | |
return resolve(idList.map((id) => results[id])); | |
} | |
// Otherwise, need to bulk load. | |
return fn(needsFetch).then( | |
(resMapping) => { | |
Object.keys(resMapping).forEach((id) => { | |
results[id] = resMapping[id]; | |
cacheSet(keyPattern.replace("{id}", id), { | |
build : build, | |
data : resMapping[id] | |
}); | |
}); | |
return resolve(idList.map((id) => results[id])); | |
}, | |
(err) => { | |
if(!missing.length) { | |
console.error("Using stale cache data for some bulk-expanded keys; got error", err); | |
return resolve(idList.map((id) => results[id])); | |
} | |
return reject(err); | |
} | |
); | |
}); | |
}); | |
} | |
function sliceBatch(max, fn) { | |
return function(args) { | |
var calls = [], | |
promise; | |
while(args.length > 0) { | |
calls.push(args.splice(0, max)); | |
} | |
promise = Promise.all(calls.map((call) => fn(call))); | |
return promise.then( | |
(res) => objectify( | |
res.reduce( | |
(a, x) => a.concat(x), | |
[] | |
) | |
) | |
); | |
}; | |
} | |
function autoExpand(endpoint) { | |
var baseKey = endpoint; | |
return function(...args) { | |
if(!args.length) { | |
return memoize(baseKey, () => | |
fetch(`${baseUrl}${endpoint}`).then((res) => res.json()) | |
); | |
} | |
return memoizeBulk( | |
`${baseKey}/{id}`, | |
sliceBatch(200, (ids) => fetch(`${baseUrl}${endpoint}?ids=${ids.join(",")}`) | |
.then((res) => res.json()) | |
), | |
args | |
); | |
}; | |
} | |
function autoAll(endpoint) { | |
return function() { | |
return fetch(`${baseUrl}${endpoint}?ids=all`).then((res) => res.json()); | |
} | |
} | |
var achievements = autoExpand("/v2/achievements"), | |
colors = autoExpand("/v2/colors"), | |
commerce = { | |
listings : autoExpand("/v2/commerce/listings"), | |
prices : autoExpand("/v2/commerce/prices") | |
}, | |
continents = autoExpand("/v2/continents"), | |
currencies = autoExpand("/v2/currencies"), | |
files = autoExpand("/v2/files"), | |
items = autoExpand("/v2/items"), | |
itemstats = autoExpand("/v2/itemstats"), | |
legends = autoExpand("/v2/legends"), | |
maps = autoExpand("/v2/maps"), | |
materials = autoExpand("/v2/materials"), | |
minis = autoExpand("/v2/minis"), | |
pets = autoExpand("/v2/pets"), | |
professions = autoExpand("/v2/professions"), | |
pvp = { | |
amulets : autoExpand("/v2/pvp/amulets"), | |
seasons : autoExpand("/v2/pvp/seasons") | |
}, | |
quaggans = autoExpand("/v2/quaggans"), | |
recipes = autoExpand("/v2/recipes"), | |
skills = autoExpand("/v2/skills"), | |
skins = autoExpand("/v2/skins"), | |
specializations = autoExpand("/v2/specializations"), | |
traits = autoExpand("/v2/traits"), | |
worlds = autoExpand("/v2/worlds"), | |
wvw = { | |
abilities : autoExpand("/v2/wvw/abilities"), | |
matches : autoExpand("/v2/wvw/matches"), | |
objectives : autoExpand("/v2/wvw/objectives") | |
}; | |
// These are a bit weird. | |
achievements.categories = autoExpand("/v2/achievements/categories"); | |
achievements.groups = autoExpand("/v2/achievements/groups"); | |
achievements.categories.all = autoAll("/v2/achievements/categories"); | |
achievements.groups.all = autoAll("/v2/achievements/groups"); | |
// These are explicitly not memoized since they change more frequently | |
// than the buildId. | |
achievements.daily = function() { | |
return fetch(`${baseUrl}/v2/achievements/daily`).then((res) => res.json()); | |
}; | |
achievements.daily.tomorrow = function() { | |
return fetch(`${baseUrl}/v2/achievements/daily/tomorrow`).then((res) => res.json()); | |
}; | |
export{ | |
achievements, colors, commerce, continents, currencies, files, items, | |
itemstats, legends, maps, materials, minis, pets, professions, pvp, | |
quaggans, recipes, skills, skins, specializations, traits, worlds, wvw, | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment