add basic user data to GraphQL API

This commit is contained in:
Emi Matchu 2020-09-02 16:09:11 -07:00
parent 4796d213aa
commit f3013c2956
4 changed files with 108 additions and 0 deletions

View file

@ -21,3 +21,4 @@ GRANT UPDATE ON openneo_impress.swf_assets TO impress2020;
-- User data tables -- User data tables
GRANT SELECT ON openneo_impress.item_outfit_relationships TO impress2020; GRANT SELECT ON openneo_impress.item_outfit_relationships TO impress2020;
GRANT SELECT ON openneo_impress.outfits TO impress2020; GRANT SELECT ON openneo_impress.outfits TO impress2020;
GRANT SELECT ON openneo_impress.users TO impress2020;

View file

@ -222,6 +222,11 @@ const typeDefs = gql`
items: [Item!]! # deprecated alias for wornItems items: [Item!]! # deprecated alias for wornItems
} }
type User {
id: ID!
username: String!
}
type Query { type Query {
allColors: [Color!]! @cacheControl(maxAge: 10800) # Cache for 3 hours (we might add more!) 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!) allSpecies: [Species!]! @cacheControl(maxAge: 10800) # Cache for 3 hours (we might add more!)
@ -256,6 +261,8 @@ const typeDefs = gql`
color(id: ID!): Color color(id: ID!): Color
species(id: ID!): Species species(id: ID!): Species
user(id: ID!): User
petOnNeopetsDotCom(petName: String!): Outfit petOnNeopetsDotCom(petName: String!): Outfit
} }
@ -605,6 +612,12 @@ const resolvers = {
.map((oir) => ({ id: oir.itemId })); .map((oir) => ({ id: oir.itemId }));
}, },
}, },
User: {
username: async ({ id }, _, { userLoader }) => {
const user = await userLoader.load(id);
return user.name;
},
},
Query: { Query: {
allColors: async (_, { ids }, { colorLoader }) => { allColors: async (_, { ids }, { colorLoader }) => {
const allColors = await colorLoader.loadAll(); const allColors = await colorLoader.loadAll();
@ -688,6 +701,19 @@ const resolvers = {
return petStates.map((petState) => ({ id: petState.id })); return petStates.map((petState) => ({ id: petState.id }));
}, },
outfit: (_, { id }) => ({ id }), outfit: (_, { id }) => ({ id }),
user: async (_, { id }, { userLoader }) => {
try {
const user = await userLoader.load(id);
} catch (e) {
if (e.message.includes("could not find user")) {
return null;
} else {
throw e;
}
}
return { id };
},
petOnNeopetsDotCom: async (_, { petName }) => { petOnNeopetsDotCom: async (_, { petName }) => {
const [petMetaData, customPetData] = await Promise.all([ const [petMetaData, customPetData] = await Promise.all([
neopets.loadPetMetaData(petName), neopets.loadPetMetaData(petName),

View file

@ -425,6 +425,23 @@ const buildPetStatesForPetTypeLoader = (db, loaders) =>
); );
}); });
const buildUserLoader = (db) => new DataLoader(async (ids) => {
const qs = ids.map((_) => "?").join(",");
const [rows, _] = await db.execute(
`SELECT * FROM users WHERE id IN (${qs})`,
ids
);
const entities = rows.map(normalizeRow);
const entitiesById = new Map(entities.map((e) => [e.id, e]));
return ids.map(
(id) =>
entitiesById.get(String(id)) ||
new Error(`could not find user with ID: ${id}`)
);
});
const buildZoneLoader = (db) => { const buildZoneLoader = (db) => {
const zoneLoader = new DataLoader(async (ids) => { const zoneLoader = new DataLoader(async (ids) => {
const qs = ids.map((_) => "?").join(","); const qs = ids.map((_) => "?").join(",");
@ -504,6 +521,7 @@ function buildLoaders(db) {
); );
loaders.speciesLoader = buildSpeciesLoader(db); loaders.speciesLoader = buildSpeciesLoader(db);
loaders.speciesTranslationLoader = buildSpeciesTranslationLoader(db); loaders.speciesTranslationLoader = buildSpeciesTranslationLoader(db);
loaders.userLoader = buildUserLoader(db);
loaders.zoneLoader = buildZoneLoader(db); loaders.zoneLoader = buildZoneLoader(db);
loaders.zoneTranslationLoader = buildZoneTranslationLoader(db); loaders.zoneTranslationLoader = buildZoneTranslationLoader(db);

View file

@ -0,0 +1,63 @@
const gql = require("graphql-tag");
const { query, getDbCalls } = require("./setup.js");
describe("User", () => {
it("looks up a user", async () => {
const res = await query({
query: gql`
query {
user(id: "6") {
id
username
}
}
`,
});
expect(res).toHaveNoErrors();
expect(res.data).toMatchInlineSnapshot(`
Object {
"user": Object {
"id": "6",
"username": "matchu",
},
}
`);
expect(getDbCalls()).toMatchInlineSnapshot(`
Array [
Array [
"SELECT * FROM users WHERE id IN (?)",
Array [
"6",
],
],
]
`);
});
it("returns null when user not found", async () => {
const res = await query({
query: gql`
query {
user(id: "<invalid-user-id>") {
id
username
}
}
`,
});
expect(res).toHaveNoErrors();
expect(res.data.user).toBe(null);
expect(getDbCalls()).toMatchInlineSnapshot(`
Array [
Array [
"SELECT * FROM users WHERE id IN (?)",
Array [
"<invalid-user-id>",
],
],
]
`);
});
});