add isCommonlyUsedByItems to Zone

This is in preparation for hiding bio zone restrictions but showing item zone restrictions!

I also refactor the build-cached-data script substantially, to run GraphQL against the server instead of a custom query.
This commit is contained in:
Emi Matchu 2020-09-01 01:13:03 -07:00
parent 8f9f1a14de
commit 3a6e3fac8e
5 changed files with 95 additions and 31 deletions

View file

@ -1,41 +1,62 @@
// We run this on build to cache some stable database tables into the JS
// bundle!
require("honeycomb-beeline")({
writeKey: process.env["HONEYCOMB_WRITE_KEY"],
dataset:
process.env["NODE_ENV"] === "production"
? "Dress to Impress (2020)"
: "Dress to Impress (2020, dev)",
serviceName: "impress-2020-build-process",
});
const fs = require("fs").promises;
const path = require("path");
const { ApolloServer } = require("apollo-server");
const { createTestClient } = require("apollo-server-testing");
const gql = require("graphql-tag");
const connectToDb = require("../src/server/db");
const { normalizeRow } = require("../src/server/util");
const { config } = require("../src/server");
const cachedDataPath = path.join(__dirname, "..", "src", "app", "cached-data");
async function buildZonesCache(db) {
const [rows] = await db.query(
`SELECT z.id, z.depth, zt.label FROM zones z ` +
`INNER JOIN zone_translations zt ON z.id = zt.zone_id ` +
`WHERE locale = "en" ORDER BY z.id;`
);
const entities = rows.map(normalizeRow);
async function main() {
await fs.mkdir(cachedDataPath, { recursive: true });
// Check out this scrappy way of making a query against server code ^_^`
const { query } = createTestClient(new ApolloServer(config));
const res = await query({
query: gql`
query BuildCachedData {
allZones {
id
label
depth
isCommonlyUsedByItems
}
}
`,
});
if (res.errors) {
for (const error of res.errors) {
console.error(error);
}
throw new Error(`GraphQL request failed`);
}
const filePath = path.join(cachedDataPath, "zones.json");
fs.writeFile(filePath, JSON.stringify(entities, null, 4), "utf8");
await fs.writeFile(
filePath,
JSON.stringify(res.data.allZones, null, 4),
"utf8"
);
console.log(`📚 Wrote zones to ${path.relative(process.cwd(), filePath)}`);
}
async function main() {
const db = await connectToDb();
await fs.mkdir(cachedDataPath, { recursive: true });
try {
await buildZonesCache(db);
} catch (e) {
db.close();
throw e;
}
db.close();
}
main().catch((e) => {
console.error(e);
process.exitCode = 1;
});
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.then(() => process.exit());

View file

@ -105,6 +105,15 @@ const typePolicies = {
const id = readField("id");
return label || cachedZonesById.get(id)?.label || `Zone #${id}`;
},
isCommonlyUsedByItems: (isCommonlyUsedByItems, { readField }) => {
const id = readField("id");
return (
isCommonlyUsedByItems ||
cachedZonesById.get(id)?.isCommonlyUsedByItems ||
false
);
},
},
},
};

View file

@ -1,5 +1,5 @@
const { gql, makeExecutableSchema } = require("apollo-server");
import { addBeelineToSchema, beelinePlugin } from "./lib/beeline-graphql";
const { addBeelineToSchema, beelinePlugin } = require("./lib/beeline-graphql");
const connectToDb = require("./db");
const buildLoaders = require("./loaders");
@ -177,6 +177,7 @@ const typeDefs = gql`
id: ID!
depth: Int!
label: String!
isCommonlyUsedByItems: Boolean!
}
type ItemSearchResult {
@ -224,6 +225,7 @@ const typeDefs = gql`
allColors: [Color!]! @cacheControl(maxAge: 10800) # Cache for 3 hours (we might add more!)
allSpecies: [Species!]! @cacheControl(maxAge: 10800) # Cache for 3 hours (we might add more!)
allValidSpeciesColorPairs: [SpeciesColorPair!]! # deprecated
allZones: [Zone!]!
item(id: ID!): Item
items(ids: [ID!]!): [Item!]!
itemSearch(query: String!): ItemSearchResult!
@ -536,6 +538,15 @@ const resolvers = {
const zoneTranslation = await zoneTranslationLoader.load(id);
return zoneTranslation.label;
},
isCommonlyUsedByItems: async ({ id }, _, { zoneLoader }) => {
// Zone metadata marks item zones with types 2, 3, and 4. But also, in
// practice, the Biology Effects zone (type 1) has been used for a few
// items too. So, that's what we return true for!
const zone = await zoneLoader.load(id);
const isMarkedForItems = ["2", "3", "4"].includes(zone.typeId);
const isBiologyEffects = zone.id === "4";
return isMarkedForItems || isBiologyEffects;
},
},
Color: {
name: async ({ id }, _, { colorTranslationLoader }) => {
@ -599,6 +610,10 @@ const resolvers = {
}));
return allPairs;
},
allZones: async (_, __, { zoneLoader }) => {
const zones = await zoneLoader.loadAll();
return zones.map(({ id }) => ({ id }));
},
item: (_, { id }) => ({ id }),
items: (_, { ids }) => {
return ids.map((id) => ({ id }));

View file

@ -3,7 +3,7 @@
const beeline = require("honeycomb-beeline");
const gql = require("graphql");
export function addBeelineToSchema(schema) {
function addBeelineToSchema(schema) {
if (!beeline) return;
forEachField(schema, (field) => {
if (!field.resolve) return;
@ -68,7 +68,7 @@ const fieldsFor = (name, path) => ({
"graphql.key": path.split(".").pop(),
});
export const beelinePlugin = {
const beelinePlugin = {
requestDidStart() {
const trace = beeline.startTrace();
return {
@ -84,3 +84,8 @@ export const beelinePlugin = {
};
},
};
module.exports = {
addBeelineToSchema,
beelinePlugin,
};

View file

@ -397,8 +397,8 @@ const buildPetStatesForPetTypeLoader = (db, loaders) =>
);
});
const buildZoneLoader = (db) =>
new DataLoader(async (ids) => {
const buildZoneLoader = (db) => {
const zoneLoader = new DataLoader(async (ids) => {
const qs = ids.map((_) => "?").join(",");
const [rows, _] = await db.execute(
`SELECT * FROM zones WHERE id IN (${qs})`,
@ -415,6 +415,20 @@ const buildZoneLoader = (db) =>
);
});
zoneLoader.loadAll = async () => {
const [rows, _] = await db.execute(`SELECT * FROM zones`);
const entities = rows.map(normalizeRow);
for (const zone of entities) {
zoneLoader.prime(zone.id, zone);
}
return entities;
};
return zoneLoader;
};
const buildZoneTranslationLoader = (db) =>
new DataLoader(async (zoneIds) => {
const qs = zoneIds.map((_) => "?").join(",");