GraphQL for user's itemsTheyOwn

This commit is contained in:
Matchu 2020-09-04 05:57:21 -07:00
parent df49e08bff
commit e2b5486168
4 changed files with 158 additions and 8 deletions

View file

@ -19,6 +19,7 @@ GRANT UPDATE ON openneo_impress.pet_states TO impress2020;
GRANT UPDATE ON openneo_impress.swf_assets TO impress2020; GRANT UPDATE ON openneo_impress.swf_assets TO impress2020;
-- User data tables -- User data tables
GRANT SELECT ON openneo_impress.closet_hangers TO impress2020;
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; GRANT SELECT ON openneo_impress.users TO impress2020;

View file

@ -229,6 +229,7 @@ const typeDefs = gql`
type User { type User {
id: ID! id: ID!
username: String! username: String!
itemsTheyOwn: [Item!]!
} }
type Query { type Query {
@ -622,13 +623,38 @@ const resolvers = {
const user = await userLoader.load(id); const user = await userLoader.load(id);
return user.name; return user.name;
}, },
itemsTheyOwn: async (
{ id },
_,
{ currentUserId, userLoader, userOwnedClosetHangersLoader }
) => {
const user = await userLoader.load(id);
const hangersAreVisible =
user.ownedClosetHangersVisibility >= 2 || user.id === currentUserId;
if (!hangersAreVisible) {
return [];
}
const allClosetHangers = await userOwnedClosetHangersLoader.load(id);
const closetHangersWithNoList = allClosetHangers.filter(
(h) => h.listId == null
);
const items = closetHangersWithNoList.map((h) => ({
id: h.itemId,
// We get this for the ORDER BY clause anyway - may as well include it
// here to avoid an extra lookup!
name: h.itemName,
}));
return items;
},
}, },
Query: { Query: {
allColors: async (_, { ids }, { colorLoader }) => { allColors: async (_, __, { colorLoader }) => {
const allColors = await colorLoader.loadAll(); const allColors = await colorLoader.loadAll();
return allColors; return allColors;
}, },
allSpecies: async (_, { ids }, { speciesLoader }) => { allSpecies: async (_, __, { speciesLoader }) => {
const allSpecies = await speciesLoader.loadAll(); const allSpecies = await speciesLoader.loadAll();
return allSpecies; return allSpecies;
}, },
@ -708,7 +734,7 @@ const resolvers = {
outfit: (_, { id }) => ({ id }), outfit: (_, { id }) => ({ id }),
user: async (_, { id }, { userLoader }) => { user: async (_, { id }, { userLoader }) => {
try { try {
const user = await userLoader.load(id); await userLoader.load(id);
} catch (e) { } catch (e) {
if (e.message.includes("could not find user")) { if (e.message.includes("could not find user")) {
return null; return null;
@ -725,7 +751,7 @@ const resolvers = {
} }
try { try {
const user = await userLoader.load(currentUserId); await userLoader.load(currentUserId);
} catch (e) { } catch (e) {
if (e.message.includes("could not find user")) { if (e.message.includes("could not find user")) {
return null; return null;

View file

@ -425,8 +425,9 @@ const buildPetStatesForPetTypeLoader = (db, loaders) =>
); );
}); });
const buildUserLoader = (db) => new DataLoader(async (ids) => { const buildUserLoader = (db) =>
const qs = ids.map((_) => "?").join(","); new DataLoader(async (ids) => {
const qs = ids.map((_) => "?").join(",");
const [rows, _] = await db.execute( const [rows, _] = await db.execute(
`SELECT * FROM users WHERE id IN (${qs})`, `SELECT * FROM users WHERE id IN (${qs})`,
ids ids
@ -440,7 +441,26 @@ const buildUserLoader = (db) => new DataLoader(async (ids) => {
entitiesById.get(String(id)) || entitiesById.get(String(id)) ||
new Error(`could not find user with ID: ${id}`) new Error(`could not find user with ID: ${id}`)
); );
}); });
const buildUserOwnedClosetHangersLoader = (db) =>
new DataLoader(async (userIds) => {
const qs = userIds.map((_) => "?").join(",");
const [rows, _] = await db.execute(
`SELECT closet_hangers.*, item_translations.name as item_name FROM closet_hangers
INNER JOIN items ON items.id = closet_hangers.item_id
INNER JOIN item_translations ON
item_translations.item_id = items.id AND locale = "en"
WHERE user_id IN (${qs}) AND owned = 1
ORDER BY item_name`,
userIds
);
const entities = rows.map(normalizeRow);
return userIds.map((userId) =>
entities.filter((e) => e.userId === String(userId))
);
});
const buildZoneLoader = (db) => { const buildZoneLoader = (db) => {
const zoneLoader = new DataLoader(async (ids) => { const zoneLoader = new DataLoader(async (ids) => {
@ -522,6 +542,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.userLoader = buildUserLoader(db);
loaders.userOwnedClosetHangersLoader = buildUserOwnedClosetHangersLoader(db);
loaders.zoneLoader = buildZoneLoader(db); loaders.zoneLoader = buildZoneLoader(db);
loaders.zoneTranslationLoader = buildZoneTranslationLoader(db); loaders.zoneTranslationLoader = buildZoneTranslationLoader(db);

View file

@ -4,7 +4,7 @@ const { query, getDbCalls, logInAsTestUser } = require("./setup.js");
describe("User", () => { describe("User", () => {
it("looks up a user", async () => { it("looks up a user", async () => {
// TODO: I'm not sure why this is taking extra time, maybe the db conn? // TODO: I'm not sure why this is taking extra time, maybe the db conn?
jest.setTimeout(10000); jest.setTimeout(20000);
const res = await query({ const res = await query({
query: gql` query: gql`
@ -115,4 +115,106 @@ describe("User", () => {
expect(res.data).toEqual({ currentUser: null }); expect(res.data).toEqual({ currentUser: null });
expect(getDbCalls()).toMatchInlineSnapshot(`Array []`); expect(getDbCalls()).toMatchInlineSnapshot(`Array []`);
}); });
it("gets private items they own for current user", async () => {
await logInAsTestUser();
const res = await query({
query: gql`
query {
user(id: "44743") {
id
username
itemsTheyOwn {
id
name
}
}
}
`,
});
expect(res).toHaveNoErrors();
expect(res.data).toMatchInlineSnapshot(`
Object {
"user": Object {
"id": "44743",
"itemsTheyOwn": Array [
Object {
"id": "74967",
"name": "17th Birthday Party Hat",
},
Object {
"id": "49026",
"name": "Abominable Snowman Hat",
},
Object {
"id": "40319",
"name": "Blue Jelly Tiara",
},
],
"username": "dti-test",
},
}
`);
expect(getDbCalls()).toMatchInlineSnapshot(`
Array [
Array [
"SELECT * FROM users WHERE id IN (?)",
Array [
"44743",
],
],
Array [
"SELECT closet_hangers.*, item_translations.name as item_name FROM closet_hangers
INNER JOIN items ON items.id = closet_hangers.item_id
INNER JOIN item_translations ON
item_translations.item_id = items.id AND locale = \\"en\\"
WHERE user_id IN (44743) AND owned = 1
ORDER BY item_name",
Array [
"44743",
],
],
]
`);
});
it("hides private items they own from other users", async () => {
const res = await query({
query: gql`
query {
user(id: "44743") {
id
username
itemsTheyOwn {
id
name
}
}
}
`,
});
expect(res).toHaveNoErrors();
expect(res.data).toMatchInlineSnapshot(`
Object {
"user": Object {
"id": "44743",
"itemsTheyOwn": Array [],
"username": "dti-test",
},
}
`);
expect(getDbCalls()).toMatchInlineSnapshot(`
Array [
Array [
"SELECT * FROM users WHERE id IN (?)",
Array [
"44743",
],
],
]
`);
});
}); });