Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@lye

lye/gw2-api.js Secret

Created June 6, 2016 17:18
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lye/42c77faef2071d56720ce59c657cfa54 to your computer and use it in GitHub Desktop.
Save lye/42c77faef2071d56720ce59c657cfa54 to your computer and use it in GitHub Desktop.
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