Remove Waka values
The motivation is that I want VERCEL_URL and local net requests outta here :p and we were doing some cutesiness with leveraging the CDN cache to back the GQL fields. No more of that, folks! lol
This commit is contained in:
parent
991defffa1
commit
0a81f07849
3 changed files with 13 additions and 160 deletions
|
@ -8,127 +8,15 @@ const beeline = require("honeycomb-beeline")({
|
||||||
disableInstrumentationOnLoad: true,
|
disableInstrumentationOnLoad: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
import fetch from "node-fetch";
|
|
||||||
|
|
||||||
import connectToDb from "../../src/server/db";
|
|
||||||
|
|
||||||
async function handle(req, res) {
|
async function handle(req, res) {
|
||||||
const allNcItemNamesAndIdsPromise = loadAllNcItemNamesAndIds();
|
res.setHeader("Content-Type", "text/plain; charset=utf8");
|
||||||
|
res
|
||||||
let itemValuesByIdOrName;
|
.status(410)
|
||||||
try {
|
.send(
|
||||||
itemValuesByIdOrName = await loadWakaValuesByIdOrName();
|
"WakaGuide.com is no longer updating its values, so we no longer " +
|
||||||
} catch (e) {
|
"serve them from this endpoint. The most recent set of values is " +
|
||||||
console.error(e);
|
"archived here: https://docs.google.com/spreadsheets/d/1DRMrniTSZP0sgZK6OAFFYqpmbT6xY_Ve_i480zghOX0"
|
||||||
res.setHeader("Content-Type", "text/plain; charset=utf8");
|
);
|
||||||
res.status(500).send("Error loading Waka data from Google Sheets API");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restructure the value data to use IDs as keys, instead of names.
|
|
||||||
const allNcItemNamesAndIds = await allNcItemNamesAndIdsPromise;
|
|
||||||
const itemValues = {};
|
|
||||||
for (const { name, id } of allNcItemNamesAndIds) {
|
|
||||||
if (id in itemValuesByIdOrName) {
|
|
||||||
itemValues[id] = itemValuesByIdOrName[id];
|
|
||||||
} else if (name in itemValuesByIdOrName) {
|
|
||||||
itemValues[id] = itemValuesByIdOrName[name];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cache for 1 minute, and immediately serve stale data for a day after.
|
|
||||||
// This should keep it fast and responsive, and stay well within our API key
|
|
||||||
// limits. (This will cause the client to send more requests than necessary,
|
|
||||||
// but the CDN cache should generally respond quickly with a small 304 Not
|
|
||||||
// Modified, unless the data really did change.)
|
|
||||||
res.setHeader(
|
|
||||||
"Cache-Control",
|
|
||||||
"public, max-age=3600, stale-while-revalidate=86400"
|
|
||||||
);
|
|
||||||
return res.send(itemValues);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadAllNcItemNamesAndIds() {
|
|
||||||
const db = await connectToDb();
|
|
||||||
|
|
||||||
const [rows] = await db.query(`
|
|
||||||
SELECT items.id, item_translations.name FROM items
|
|
||||||
INNER JOIN item_translations ON item_translations.item_id = items.id
|
|
||||||
WHERE
|
|
||||||
(items.rarity_index IN (0, 500) OR is_manually_nc = 1)
|
|
||||||
AND item_translations.locale = "en"
|
|
||||||
`);
|
|
||||||
|
|
||||||
return rows.map(({ id, name }) => ({ id, name: normalizeItemName(name) }));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load all Waka values from the spreadsheet. Returns an object keyed by ID or
|
|
||||||
* name - that is, if the item ID is provided in the sheet, we use that as the
|
|
||||||
* key; or if not, we use the name as the key.
|
|
||||||
*/
|
|
||||||
async function loadWakaValuesByIdOrName() {
|
|
||||||
if (!process.env["GOOGLE_API_KEY"]) {
|
|
||||||
throw new Error(`GOOGLE_API_KEY environment variable must be provided`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const res = await fetch(
|
|
||||||
`https://sheets.googleapis.com/v4/spreadsheets/` +
|
|
||||||
`1DRMrniTSZP0sgZK6OAFFYqpmbT6xY_Ve_i480zghOX0/values/NC%20Values` +
|
|
||||||
`?fields=values&key=${encodeURIComponent(process.env["GOOGLE_API_KEY"])}`
|
|
||||||
);
|
|
||||||
const json = await res.json();
|
|
||||||
|
|
||||||
if (!res.ok) {
|
|
||||||
if (json.error) {
|
|
||||||
const { code, status, message } = json.error;
|
|
||||||
throw new Error(
|
|
||||||
`Google Sheets API returned error ${code} ${status}: ${message}`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
throw new Error(
|
|
||||||
`Google Sheets API returned unexpected error: ${res.status} ${res.statusText}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the rows from the JSON response - skipping the first-row headers.
|
|
||||||
const rows = json.values.slice(1);
|
|
||||||
|
|
||||||
// Reformat the rows as a map from item name to value. We offer the item data
|
|
||||||
// as an object with a single field `value` for extensibility, but we omit
|
|
||||||
// the spreadsheet columns that we don't use on DTI, like Notes.
|
|
||||||
//
|
|
||||||
// NOTE: The Sheets API only returns the first non-empty cells of the row.
|
|
||||||
// That's why we set `""` as the defaults, in case the value/notes/etc
|
|
||||||
// aren't provided.
|
|
||||||
const itemValuesByIdOrName = {};
|
|
||||||
for (const [
|
|
||||||
itemName,
|
|
||||||
value = "",
|
|
||||||
unusedNotes = "",
|
|
||||||
unusedMarks = "",
|
|
||||||
itemId = "",
|
|
||||||
] of rows) {
|
|
||||||
const normalizedItemName = normalizeItemName(itemName);
|
|
||||||
itemValuesByIdOrName[itemId || normalizedItemName] = { value };
|
|
||||||
}
|
|
||||||
|
|
||||||
return itemValuesByIdOrName;
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeItemName(name) {
|
|
||||||
return (
|
|
||||||
name
|
|
||||||
// Remove all spaces, they're a common source of inconsistency
|
|
||||||
.replace(/\s+/g, "")
|
|
||||||
// Lower case, because capitalization is another common source
|
|
||||||
.toLowerCase()
|
|
||||||
// Remove diacritics: https://stackoverflow.com/a/37511463/107415
|
|
||||||
// Waka has some stray ones in item names, not sure why!
|
|
||||||
.normalize("NFD")
|
|
||||||
.replace(/[\u0300-\u036f]/g, "")
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function handleWithBeeline(req, res) {
|
async function handleWithBeeline(req, res) {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import DataLoader from "dataloader";
|
import DataLoader from "dataloader";
|
||||||
import fetch from "node-fetch";
|
|
||||||
import { normalizeRow } from "./util";
|
import { normalizeRow } from "./util";
|
||||||
|
|
||||||
const buildClosetListLoader = (db) =>
|
const buildClosetListLoader = (db) =>
|
||||||
|
@ -795,30 +794,6 @@ const buildItemTradesLoader = (db, loaders) =>
|
||||||
{ cacheKeyFn: ({ itemId, isOwned }) => `${itemId}-${isOwned}` }
|
{ cacheKeyFn: ({ itemId, isOwned }) => `${itemId}-${isOwned}` }
|
||||||
);
|
);
|
||||||
|
|
||||||
const buildItemWakaValueLoader = () =>
|
|
||||||
new DataLoader(async (itemIds) => {
|
|
||||||
// This loader calls our /api/allWakaValues endpoint, to take advantage of
|
|
||||||
// the CDN caching. This helps us respond a bit faster than Google Sheets
|
|
||||||
// API would, and avoid putting pressure on our Google Sheets API quotas.
|
|
||||||
// (Some kind of internal memcache or process-level cache would be a more
|
|
||||||
// idiomatic solution in a monolith server environment!)
|
|
||||||
const url = process.env.VERCEL_URL
|
|
||||||
? `https://${process.env.VERCEL_URL}/api/allWakaValues`
|
|
||||||
: process.env.NODE_ENV === "production"
|
|
||||||
? "https://impress-2020.openneo.net/api/allWakaValues"
|
|
||||||
: "http://localhost:3000/api/allWakaValues";
|
|
||||||
const res = await fetch(url);
|
|
||||||
if (!res.ok) {
|
|
||||||
throw new Error(
|
|
||||||
`Error loading /api/allWakaValues: ${res.status} ${res.statusText}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const allWakaValues = await res.json();
|
|
||||||
|
|
||||||
return itemIds.map((itemId) => allWakaValues[itemId]);
|
|
||||||
});
|
|
||||||
|
|
||||||
const buildPetTypeLoader = (db, loaders) =>
|
const buildPetTypeLoader = (db, loaders) =>
|
||||||
new DataLoader(async (petTypeIds) => {
|
new DataLoader(async (petTypeIds) => {
|
||||||
const qs = petTypeIds.map((_) => "?").join(",");
|
const qs = petTypeIds.map((_) => "?").join(",");
|
||||||
|
@ -1470,7 +1445,6 @@ function buildLoaders(db) {
|
||||||
db
|
db
|
||||||
);
|
);
|
||||||
loaders.itemTradesLoader = buildItemTradesLoader(db, loaders);
|
loaders.itemTradesLoader = buildItemTradesLoader(db, loaders);
|
||||||
loaders.itemWakaValueLoader = buildItemWakaValueLoader();
|
|
||||||
loaders.petTypeLoader = buildPetTypeLoader(db, loaders);
|
loaders.petTypeLoader = buildPetTypeLoader(db, loaders);
|
||||||
loaders.petTypeBySpeciesAndColorLoader = buildPetTypeBySpeciesAndColorLoader(
|
loaders.petTypeBySpeciesAndColorLoader = buildPetTypeBySpeciesAndColorLoader(
|
||||||
db,
|
db,
|
||||||
|
|
|
@ -28,9 +28,8 @@ const typeDefs = gql`
|
||||||
createdAt: String
|
createdAt: String
|
||||||
|
|
||||||
"""
|
"""
|
||||||
This item's capsule trade value as text, according to wakaguide.com, as a
|
Deprecated: This item's capsule trade value as text, according to
|
||||||
human-readable string. Will be null if the value is not known, or if
|
wakaguide.com, as a human-readable string. **This now always returns null.**
|
||||||
there's an error connecting to the data source.
|
|
||||||
"""
|
"""
|
||||||
wakaValueText: String @cacheControl(maxAge: ${oneHour})
|
wakaValueText: String @cacheControl(maxAge: ${oneHour})
|
||||||
|
|
||||||
|
@ -315,17 +314,9 @@ const resolvers = {
|
||||||
const item = await itemLoader.load(id);
|
const item = await itemLoader.load(id);
|
||||||
return item.createdAt && item.createdAt.toISOString();
|
return item.createdAt && item.createdAt.toISOString();
|
||||||
},
|
},
|
||||||
wakaValueText: async ({ id }, _, { itemWakaValueLoader }) => {
|
wakaValueText: () => {
|
||||||
let wakaValue;
|
// This feature is deprecated, so now we just always return unknown value.
|
||||||
try {
|
return null;
|
||||||
wakaValue = await itemWakaValueLoader.load(id);
|
|
||||||
} catch (e) {
|
|
||||||
console.error(`Error loading wakaValueText for item ${id}, skipping:`);
|
|
||||||
console.error(e);
|
|
||||||
wakaValue = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return wakaValue ? wakaValue.value : null;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
currentUserOwnsThis: async (
|
currentUserOwnsThis: async (
|
||||||
|
|
Loading…
Reference in a new issue