diff --git a/src/server/query-tests/User.test.js b/src/server/query-tests/User.test.js index 7e54e45..28d889a 100644 --- a/src/server/query-tests/User.test.js +++ b/src/server/query-tests/User.test.js @@ -399,4 +399,60 @@ describe("User", () => { ] `); }); + + it("gets public closet lists, but not private, for other users", async () => { + const res = await query({ + query: gql` + query { + user(id: "44743") { + id + username + closetLists { + id + name + ownsOrWantsItems + isDefaultList + items { + id + name + } + } + } + } + `, + }); + + expect(res).toHaveNoErrors(); + expect(res.data).toMatchSnapshot("data"); + expect(getDbCalls()).toMatchSnapshot("db"); + }); + + it("gets public and private closet lists for current user", async () => { + await logInAsTestUser(); + + const res = await query({ + query: gql` + query { + user(id: "44743") { + id + username + closetLists { + id + name + ownsOrWantsItems + isDefaultList + items { + id + name + } + } + } + } + `, + }); + + expect(res).toHaveNoErrors(); + expect(res.data).toMatchSnapshot("data"); + expect(getDbCalls()).toMatchSnapshot("db"); + }); }); diff --git a/src/server/query-tests/__snapshots__/User.test.js.snap b/src/server/query-tests/__snapshots__/User.test.js.snap new file mode 100644 index 0000000..b5c6116 --- /dev/null +++ b/src/server/query-tests/__snapshots__/User.test.js.snap @@ -0,0 +1,207 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`User gets public and private closet lists for current user: data 1`] = ` +Object { + "user": Object { + "closetLists": Array [ + Object { + "id": "184791", + "isDefaultList": false, + "items": Array [ + Object { + "id": "39948", + "name": "Altador Cup Background - Lost Desert", + }, + ], + "name": "A Private Own List", + "ownsOrWantsItems": "OWNS", + }, + Object { + "id": "184793", + "isDefaultList": false, + "items": Array [ + Object { + "id": "39945", + "name": "Altador Cup Background - Haunted Woods", + }, + ], + "name": "A Private Want List", + "ownsOrWantsItems": "WANTS", + }, + Object { + "id": "184790", + "isDefaultList": false, + "items": Array [ + Object { + "id": "39955", + "name": "Altador Cup Background - Virtupets", + }, + ], + "name": "A Public Own List", + "ownsOrWantsItems": "OWNS", + }, + Object { + "id": "184792", + "isDefaultList": false, + "items": Array [ + Object { + "id": "39947", + "name": "Altador Cup Background - Shenkuu", + }, + ], + "name": "A Public Want List", + "ownsOrWantsItems": "WANTS", + }, + Object { + "id": "user-44743-default-list-OWNS", + "isDefaultList": true, + "items": Array [ + Object { + "id": "74967", + "name": "17th Birthday Party Hat", + }, + Object { + "id": "49026", + "name": "Abominable Snowman Hat", + }, + Object { + "id": "40319", + "name": "Blue Jelly Tiara", + }, + ], + "name": "(Not in a list)", + "ownsOrWantsItems": "OWNS", + }, + Object { + "id": "user-44743-default-list-WANTS", + "isDefaultList": true, + "items": Array [ + Object { + "id": "39956", + "name": "Altador Cup Background - Kreludor", + }, + ], + "name": "(Not in a list)", + "ownsOrWantsItems": "WANTS", + }, + ], + "id": "44743", + "username": "dti-test", + }, +} +`; + +exports[`User gets public and private closet lists for current user: db 1`] = ` +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 (?) + ORDER BY item_name", + Array [ + "44743", + ], + ], + Array [ + "SELECT * FROM closet_lists + WHERE user_id IN (?) + ORDER BY name", + Array [ + "44743", + ], + ], + Array [ + "SELECT * FROM item_translations WHERE item_id IN (?,?,?,?,?,?,?,?) AND locale = \\"en\\"", + Array [ + "39948", + "39945", + "39955", + "39947", + "74967", + "49026", + "40319", + "39956", + ], + ], +] +`; + +exports[`User gets public closet lists, but not private, for other users: data 1`] = ` +Object { + "user": Object { + "closetLists": Array [ + Object { + "id": "184790", + "isDefaultList": false, + "items": Array [ + Object { + "id": "39955", + "name": "Altador Cup Background - Virtupets", + }, + ], + "name": "A Public Own List", + "ownsOrWantsItems": "OWNS", + }, + Object { + "id": "184792", + "isDefaultList": false, + "items": Array [ + Object { + "id": "39947", + "name": "Altador Cup Background - Shenkuu", + }, + ], + "name": "A Public Want List", + "ownsOrWantsItems": "WANTS", + }, + ], + "id": "44743", + "username": "dti-test", + }, +} +`; + +exports[`User gets public closet lists, but not private, for other users: db 1`] = ` +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 (?) + ORDER BY item_name", + Array [ + "44743", + ], + ], + Array [ + "SELECT * FROM closet_lists + WHERE user_id IN (?) + ORDER BY name", + Array [ + "44743", + ], + ], + Array [ + "SELECT * FROM item_translations WHERE item_id IN (?,?) AND locale = \\"en\\"", + Array [ + "39955", + "39947", + ], + ], +] +`; diff --git a/src/server/types/User.js b/src/server/types/User.js index 147805a..bf5822e 100644 --- a/src/server/types/User.js +++ b/src/server/types/User.js @@ -1,14 +1,40 @@ const { gql } = require("apollo-server"); const typeDefs = gql` + enum OwnsOrWants { + OWNS + WANTS + } + type User { id: ID! username: String! contactNeopetsUsername: String + + closetLists: [ClosetList!]! + itemsTheyOwn: [Item!]! itemsTheyWant: [Item!]! } + type ClosetList { + id: ID! + name: String + + # Whether this is a list of items they own, or items they want. + ownsOrWantsItems: OwnsOrWants! + + # Each user has a "default list" of items they own/want. When users click + # the Own/Want button on the item page, items go here automatically. (On + # the backend, this is managed as the hangers having a null list ID.) + # + # This field is true if the list is the default list, so we can style it + # differently and change certain behaviors (e.g. can't be deleted). + isDefaultList: Boolean! + + items: [Item!]! + } + extend type Query { user(id: ID!): User currentUser: User @@ -109,6 +135,62 @@ const resolvers = { })); return items; }, + + closetLists: async ( + { id }, + _, + { + currentUserId, + userLoader, + userClosetListsLoader, + userClosetHangersLoader, + } + ) => { + const isCurrentUser = currentUserId === id; + const [allClosetHangers, closetLists, user] = await Promise.all([ + userClosetHangersLoader.load(id), + userClosetListsLoader.load(id), + userLoader.load(id), + ]); + + const closetListNodes = closetLists + .filter((closetList) => isCurrentUser || closetList.visibility >= 1) + .map((closetList) => ({ + id: closetList.id, + name: closetList.name, + ownsOrWantsItems: closetList.hangersOwned ? "OWNS" : "WANTS", + isDefaultList: false, + items: allClosetHangers + .filter((h) => h.listId === closetList.id) + .map((h) => ({ id: h.itemId })), + })); + + if (isCurrentUser || user.ownedClosetHangersVisibility >= 1) { + closetListNodes.push({ + id: `user-${id}-default-list-OWNS`, + name: "(Not in a list)", + ownsOrWantsItems: "OWNS", + isDefaultList: true, + items: allClosetHangers + .filter((h) => h.listId == null && h.owned) + .map((h) => ({ id: h.itemId })), + }); + } + + if (isCurrentUser || user.wantedClosetHangersVisibility >= 1) { + closetListNodes.push({ + id: `user-${id}-default-list-WANTS`, + name: "(Not in a list)", + ownsOrWantsItems: "WANTS", + isDefaultList: true, + items: allClosetHangers + .filter((h) => h.listId == null && !h.owned) + .map((h) => ({ id: h.itemId })), + }); + } + + return closetListNodes; + }, }, Query: {