From ade563ddcddc50195676546b675261163902fe99 Mon Sep 17 00:00:00 2001 From: Matt Dunn-Rankin Date: Wed, 24 Jun 2020 19:05:07 -0700 Subject: [PATCH] server can read outfit data --- setup-mysql-user.sql | 5 + .../getValidPetPoses.test.js.snap | 12 +- src/server/index.js | 126 ++++++++++++------ src/server/loaders.js | 113 +++++++++++++--- src/server/query-tests/Outfit.test.js | 104 +++++++++++++++ src/server/query-tests/PetAppearance.test.js | 54 +++++++- .../__snapshots__/Outfit.test.js.snap | 70 ++++++++++ .../__snapshots__/PetAppearance.test.js.snap | 97 ++++++++++---- .../SpeciesColorPair.test.js.snap | 40 ++++++ 9 files changed, 531 insertions(+), 90 deletions(-) create mode 100644 src/server/query-tests/Outfit.test.js create mode 100644 src/server/query-tests/__snapshots__/Outfit.test.js.snap diff --git a/setup-mysql-user.sql b/setup-mysql-user.sql index 04d0e68..48205bc 100644 --- a/setup-mysql-user.sql +++ b/setup-mysql-user.sql @@ -1,3 +1,4 @@ +-- Public data tables GRANT SELECT ON openneo_impress.colors TO impress2020; GRANT SELECT ON openneo_impress.color_translations TO impress2020; GRANT SELECT ON openneo_impress.items TO impress2020; @@ -10,3 +11,7 @@ GRANT SELECT ON openneo_impress.species_translations TO impress2020; GRANT SELECT ON openneo_impress.swf_assets TO impress2020; GRANT SELECT ON openneo_impress.zones TO impress2020; GRANT SELECT ON openneo_impress.zone_translations TO impress2020; + +-- User data tables +GRANT SELECT ON openneo_impress.item_outfit_relationships TO impress2020; +GRANT SELECT ON openneo_impress.outfits TO impress2020; diff --git a/src/server/__snapshots__/getValidPetPoses.test.js.snap b/src/server/__snapshots__/getValidPetPoses.test.js.snap index 4efa998..8b2326b 100644 --- a/src/server/__snapshots__/getValidPetPoses.test.js.snap +++ b/src/server/__snapshots__/getValidPetPoses.test.js.snap @@ -2341,7 +2341,7 @@ exports[`getValidPetPoses gets them and writes them to a buffer 1`] = ` 10000000 00000000 00000000 -00000000 +10000000 10011011 00000000 10000000 @@ -2555,7 +2555,7 @@ exports[`getValidPetPoses gets them and writes them to a buffer 1`] = ` 00000000 10111111 00000000 -00000000 +10000000 10000000 00111111 00000000 @@ -2797,7 +2797,7 @@ exports[`getValidPetPoses gets them and writes them to a buffer 1`] = ` 10000000 10000000 10000000 -00000000 +10000000 00000000 00000000 10000000 @@ -3936,7 +3936,7 @@ exports[`getValidPetPoses gets them and writes them to a buffer 1`] = ` 10011011 00000000 10011011 -00000000 +10000000 00000000 00111111 00000000 @@ -4575,7 +4575,7 @@ exports[`getValidPetPoses gets them and writes them to a buffer 1`] = ` 00000000 00000000 00111111 -00000000 +10000000 00111111 10000000 10011011 @@ -5937,7 +5937,7 @@ exports[`getValidPetPoses gets them and writes them to a buffer 1`] = ` 10000000 00000000 00000000 -00000000 +10000000 00000000 00000000 00000000 diff --git a/src/server/index.js b/src/server/index.js index f4e440a..228371e 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -70,13 +70,13 @@ const typeDefs = gql` type PetAppearance { id: ID! - petStateId: ID! - bodyId: ID! + species: Species! + color: Color! pose: Pose! - genderPresentation: GenderPresentation # deprecated - emotion: Emotion # deprecated - approximateThumbnailUrl: String! # deprecated + bodyId: ID! + layers: [AppearanceLayer!]! + petStateId: ID! # Convenience field for developers } type ItemAppearance { @@ -125,10 +125,16 @@ const typeDefs = gql` } type Outfit { - species: Species! - color: Color! - pose: Pose! - items: [Item!]! + id: ID! + name: String! + petAppearance: PetAppearance! + wornItems: [Item!]! + closetedItems: [Item!]! + + species: Species! # to be deprecated? can use petAppearance? 🤔 + color: Color! # to be deprecated? can use petAppearance? 🤔 + pose: Pose! # to be deprecated? can use petAppearance? 🤔 + items: [Item!]! # deprecated alias for wornItems } type Query { @@ -147,6 +153,8 @@ const typeDefs = gql` petAppearance(speciesId: ID!, colorId: ID!, pose: Pose!): PetAppearance petAppearances(speciesId: ID!, colorId: ID!): [PetAppearance!]! + outfit(id: ID!): Outfit + petOnNeopetsDotCom(petName: String!): Outfit } `; @@ -168,9 +176,9 @@ const resolvers = { appearanceOn: async ( item, { speciesId, colorId }, - { petTypeLoader, itemSwfAssetLoader } + { petTypeBySpeciesAndColorLoader, itemSwfAssetLoader } ) => { - const petType = await petTypeLoader.load({ + const petType = await petTypeBySpeciesAndColorLoader.load({ speciesId: speciesId, colorId: colorId, }); @@ -199,22 +207,33 @@ const resolvers = { }, }, PetAppearance: { - id: ({ petType, petState }) => { - const { speciesId, colorId } = petType; + id: async ({ petStateId }, _, { petStateLoader, petTypeLoader }) => { + const petState = await petStateLoader.load(petStateId); + const petType = await petTypeLoader.load(petState.petTypeId); const pose = getPoseFromPetState(petState); - return `${speciesId}-${colorId}-${pose}`; + return `${petType.speciesId}-${petType.colorId}-${pose}`; }, - petStateId: ({ petState }) => petState.id, - bodyId: ({ petType }) => petType.bodyId, - pose: ({ petState }) => getPoseFromPetState(petState), - genderPresentation: ({ petState }) => - getGenderPresentation(getPoseFromPetState(petState)), - emotion: ({ petState }) => getEmotion(getPoseFromPetState(petState)), - approximateThumbnailUrl: ({ petType, petState }) => { - return `http://pets.neopets.com/cp/${petType.basicImageHash}/${petState.moodId}/1.png`; + color: async ({ petStateId }, _, { petStateLoader, petTypeLoader }) => { + const petState = await petStateLoader.load(petStateId); + const petType = await petTypeLoader.load(petState.petTypeId); + return { id: petType.colorId }; }, - layers: async ({ petState }, _, { petSwfAssetLoader }) => { - const swfAssets = await petSwfAssetLoader.load(petState.id); + species: async ({ petStateId }, _, { petStateLoader, petTypeLoader }) => { + const petState = await petStateLoader.load(petStateId); + const petType = await petTypeLoader.load(petState.petTypeId); + return { id: petType.speciesId }; + }, + bodyId: async ({ petStateId }, _, { petStateLoader, petTypeLoader }) => { + const petState = await petStateLoader.load(petStateId); + const petType = await petTypeLoader.load(petState.petTypeId); + return petType.bodyId; + }, + pose: async ({ petStateId }, _, { petStateLoader }) => { + const petState = await petStateLoader.load(petStateId); + return getPoseFromPetState(petState); + }, + layers: async ({ petStateId }, _, { petSwfAssetLoader }) => { + const swfAssets = await petSwfAssetLoader.load(petStateId); return swfAssets; }, }, @@ -285,19 +304,39 @@ const resolvers = { }, }, Color: { - name: async (color, _, { colorTranslationLoader }) => { - const colorTranslation = await colorTranslationLoader.load(color.id); + name: async ({ id }, _, { colorTranslationLoader }) => { + const colorTranslation = await colorTranslationLoader.load(id); return capitalize(colorTranslation.name); }, }, Species: { - name: async (species, _, { speciesTranslationLoader }) => { - const speciesTranslation = await speciesTranslationLoader.load( - species.id - ); + name: async ({ id }, _, { speciesTranslationLoader }) => { + const speciesTranslation = await speciesTranslationLoader.load(id); return capitalize(speciesTranslation.name); }, }, + Outfit: { + name: async ({ id }, _, { outfitLoader }) => { + const outfit = await outfitLoader.load(id); + return outfit.name; + }, + petAppearance: async ({ id }, _, { outfitLoader }) => { + const outfit = await outfitLoader.load(id); + return { petStateId: outfit.petStateId }; + }, + wornItems: async ({ id }, _, { itemOutfitRelationshipsLoader }) => { + const relationships = await itemOutfitRelationshipsLoader.load(id); + return relationships + .filter((oir) => oir.isWorn) + .map((oir) => ({ id: oir.itemId })); + }, + closetedItems: async ({ id }, _, { itemOutfitRelationshipsLoader }) => { + const relationships = await itemOutfitRelationshipsLoader.load(id); + return relationships + .filter((oir) => !oir.isWorn) + .map((oir) => ({ id: oir.itemId })); + }, + }, Query: { allColors: async (_, { ids }, { loadAllColors }) => { const allColors = await loadAllColors(); @@ -326,9 +365,12 @@ const resolvers = { itemSearchToFit: async ( _, { query, speciesId, colorId, offset, limit }, - { petTypeLoader, itemSearchToFitLoader } + { petTypeBySpeciesAndColorLoader, itemSearchToFitLoader } ) => { - const petType = await petTypeLoader.load({ speciesId, colorId }); + const petType = await petTypeBySpeciesAndColorLoader.load({ + speciesId, + colorId, + }); const { bodyId } = petType; const items = await itemSearchToFitLoader.load({ query: query.trim(), @@ -341,40 +383,44 @@ const resolvers = { petAppearance: async ( _, { speciesId, colorId, pose }, - { petTypeLoader, petStateLoader } + { petTypeBySpeciesAndColorLoader, petStatesForPetTypeLoader } ) => { - const petType = await petTypeLoader.load({ + const petType = await petTypeBySpeciesAndColorLoader.load({ speciesId, colorId, }); - const petStates = await petStateLoader.load(petType.id); + const petStates = await petStatesForPetTypeLoader.load(petType.id); // TODO: This could be optimized into the query condition 🤔 const petState = petStates.find((ps) => getPoseFromPetState(ps) === pose); if (!petState) { return null; } - return { petType, petState }; + return { petStateId: petState.id }; }, petAppearances: async ( _, { speciesId, colorId }, - { petTypeLoader, petStateLoader } + { petTypeBySpeciesAndColorLoader, petStatesForPetTypeLoader } ) => { - const petType = await petTypeLoader.load({ + const petType = await petTypeBySpeciesAndColorLoader.load({ speciesId, colorId, }); - const petStates = await petStateLoader.load(petType.id); - return petStates.map((petState) => ({ petType, petState })); + const petStates = await petStatesForPetTypeLoader.load(petType.id); + return petStates.map((petState) => ({ petStateId: petState.id })); }, + outfit: (_, { id }) => ({ id }), petOnNeopetsDotCom: async (_, { petName }) => { const [petMetaData, customPetData] = await Promise.all([ neopets.loadPetMetaData(petName), neopets.loadCustomPetData(petName), ]); const outfit = { + // TODO: This isn't a fully-working Outfit object. It works for the + // client as currently implemented, but we'll probably want to + // move the client and this onto our more generic fields! species: { id: customPetData.custom_pet.species_id }, color: { id: customPetData.custom_pet.color_id }, pose: getPoseFromPetData(petMetaData, customPetData), diff --git a/src/server/loaders.js b/src/server/loaders.js index b452bf5..037c3a2 100644 --- a/src/server/loaders.js +++ b/src/server/loaders.js @@ -154,6 +154,21 @@ const buildItemSearchToFitLoader = (db) => }); const buildPetTypeLoader = (db) => + new DataLoader(async (petTypeIds) => { + const qs = petTypeIds.map((_) => "?").join(","); + const [rows, _] = await db.execute( + `SELECT * FROM pet_types WHERE id IN (${qs})`, + petTypeIds + ); + + const entities = rows.map(normalizeRow); + + return petTypeIds.map((petTypeId) => + entities.find((e) => e.id === petTypeId) + ); + }); + +const buildPetTypeBySpeciesAndColorLoader = (db, loaders) => new DataLoader(async (speciesAndColorPairs) => { const conditions = []; const values = []; @@ -172,6 +187,10 @@ const buildPetTypeLoader = (db) => entities.map((e) => [`${e.speciesId},${e.colorId}`, e]) ); + for (const petType of entities) { + loaders.petTypeLoader.prime(petType.id, petType); + } + return speciesAndColorPairs.map(({ speciesId, colorId }) => entitiesBySpeciesAndColorPair.get(`${speciesId},${colorId}`) ); @@ -226,7 +245,50 @@ const buildPetSwfAssetLoader = (db) => ); }); +const buildOutfitLoader = (db) => + new DataLoader(async (outfitIds) => { + const qs = outfitIds.map((_) => "?").join(","); + const [rows, _] = await db.execute( + `SELECT * FROM outfits WHERE id IN (${qs})`, + outfitIds + ); + + const entities = rows.map(normalizeRow); + + return outfitIds.map((outfitId) => entities.find((e) => e.id === outfitId)); + }); + +const buildItemOutfitRelationshipsLoader = (db) => + new DataLoader(async (outfitIds) => { + const qs = outfitIds.map((_) => "?").join(","); + const [rows, _] = await db.execute( + `SELECT * FROM item_outfit_relationships WHERE outfit_id IN (${qs})`, + outfitIds + ); + + const entities = rows.map(normalizeRow); + + return outfitIds.map((outfitId) => + entities.filter((e) => e.outfitId === outfitId) + ); + }); + const buildPetStateLoader = (db) => + new DataLoader(async (petStateIds) => { + const qs = petStateIds.map((_) => "?").join(","); + const [rows, _] = await db.execute( + `SELECT * FROM pet_states WHERE id IN (${qs})`, + petStateIds + ); + + const entities = rows.map(normalizeRow); + + return petStateIds.map((petStateId) => + entities.find((e) => e.id === petStateId) + ); + }); + +const buildPetStatesForPetTypeLoader = (db, loaders) => new DataLoader(async (petTypeIds) => { const qs = petTypeIds.map((_) => "?").join(","); const [rows, _] = await db.execute( @@ -238,6 +300,10 @@ const buildPetStateLoader = (db) => const entities = rows.map(normalizeRow); + for (const petState of entities) { + loaders.petStateLoader.prime(petState.id, petState); + } + return petTypeIds.map((petTypeId) => entities.filter((e) => e.petTypeId === petTypeId) ); @@ -280,24 +346,37 @@ const buildZoneTranslationLoader = (db) => }); function buildLoaders(db) { - return { - loadAllColors: loadAllColors(db), - loadAllSpecies: loadAllSpecies(db), - loadAllPetTypes: loadAllPetTypes(db), + const loaders = {}; + loaders.loadAllColors = loadAllColors(db); + loaders.loadAllSpecies = loadAllSpecies(db); + loaders.loadAllPetTypes = loadAllPetTypes(db); - colorTranslationLoader: buildColorTranslationLoader(db), - itemLoader: buildItemsLoader(db), - itemTranslationLoader: buildItemTranslationLoader(db), - itemSearchLoader: buildItemSearchLoader(db), - itemSearchToFitLoader: buildItemSearchToFitLoader(db), - petTypeLoader: buildPetTypeLoader(db), - itemSwfAssetLoader: buildItemSwfAssetLoader(db), - petSwfAssetLoader: buildPetSwfAssetLoader(db), - petStateLoader: buildPetStateLoader(db), - speciesTranslationLoader: buildSpeciesTranslationLoader(db), - zoneLoader: buildZoneLoader(db), - zoneTranslationLoader: buildZoneTranslationLoader(db), - }; + loaders.colorTranslationLoader = buildColorTranslationLoader(db); + loaders.itemLoader = buildItemsLoader(db); + loaders.itemTranslationLoader = buildItemTranslationLoader(db); + loaders.itemSearchLoader = buildItemSearchLoader(db); + loaders.itemSearchToFitLoader = buildItemSearchToFitLoader(db); + loaders.petTypeLoader = buildPetTypeLoader(db); + loaders.petTypeBySpeciesAndColorLoader = buildPetTypeBySpeciesAndColorLoader( + db, + loaders + ); + loaders.itemSwfAssetLoader = buildItemSwfAssetLoader(db); + loaders.petSwfAssetLoader = buildPetSwfAssetLoader(db); + loaders.outfitLoader = buildOutfitLoader(db); + loaders.itemOutfitRelationshipsLoader = buildItemOutfitRelationshipsLoader( + db + ); + loaders.petStateLoader = buildPetStateLoader(db); + loaders.petStatesForPetTypeLoader = buildPetStatesForPetTypeLoader( + db, + loaders + ); + loaders.speciesTranslationLoader = buildSpeciesTranslationLoader(db); + loaders.zoneLoader = buildZoneLoader(db); + loaders.zoneTranslationLoader = buildZoneTranslationLoader(db); + + return loaders; } module.exports = buildLoaders; diff --git a/src/server/query-tests/Outfit.test.js b/src/server/query-tests/Outfit.test.js new file mode 100644 index 0000000..75ce97d --- /dev/null +++ b/src/server/query-tests/Outfit.test.js @@ -0,0 +1,104 @@ +const gql = require("graphql-tag"); +const { query, getDbCalls } = require("./setup.js"); + +describe("Outfit", () => { + it("loads an outfit by ID", async () => { + const res = await query({ + query: gql` + query { + outfit(id: "31856") { + id + name + + petAppearance { + id + + color { + id + name + } + + species { + id + name + } + + pose + } + + wornItems { + id + name + } + + closetedItems { + id + name + } + } + } + `, + }); + + expect(res).toHaveNoErrors(); + expect(res.data).toMatchSnapshot(); + expect(getDbCalls()).toMatchInlineSnapshot(` + Array [ + Array [ + "SELECT * FROM outfits WHERE id IN (?)", + Array [ + "31856", + ], + ], + Array [ + "SELECT * FROM item_outfit_relationships WHERE outfit_id IN (?)", + Array [ + "31856", + ], + ], + Array [ + "SELECT * FROM item_translations WHERE item_id IN (?,?,?,?,?,?,?,?,?,?,?) AND locale = \\"en\\"", + Array [ + "38916", + "51054", + "38914", + "36125", + "36467", + "47075", + "47056", + "39662", + "56706", + "38915", + "56398", + ], + ], + Array [ + "SELECT * FROM pet_states WHERE id IN (?)", + Array [ + "3951", + ], + ], + Array [ + "SELECT * FROM pet_types WHERE id IN (?)", + Array [ + "33", + ], + ], + Array [ + "SELECT * FROM color_translations + WHERE color_id IN (?) AND locale = \\"en\\"", + Array [ + "34", + ], + ], + Array [ + "SELECT * FROM species_translations + WHERE species_id IN (?) AND locale = \\"en\\"", + Array [ + "54", + ], + ], + ] + `); + }); +}); diff --git a/src/server/query-tests/PetAppearance.test.js b/src/server/query-tests/PetAppearance.test.js index 71a15ce..3163d30 100644 --- a/src/server/query-tests/PetAppearance.test.js +++ b/src/server/query-tests/PetAppearance.test.js @@ -7,6 +7,18 @@ describe("PetAppearance", () => { query: gql` query { petAppearance(speciesId: "54", colorId: "75", pose: HAPPY_FEM) { + id + + species { + id + name + } + + color { + id + name + } + layers { id imageUrl(size: SIZE_600) @@ -49,6 +61,20 @@ describe("PetAppearance", () => { "17723", ], ], + Array [ + "SELECT * FROM species_translations + WHERE species_id IN (?) AND locale = \\"en\\"", + Array [ + "54", + ], + ], + Array [ + "SELECT * FROM color_translations + WHERE color_id IN (?) AND locale = \\"en\\"", + Array [ + "75", + ], + ], Array [ "SELECT * FROM zones WHERE id IN (?,?,?,?,?,?)", Array [ @@ -70,12 +96,20 @@ describe("PetAppearance", () => { query { petAppearances(speciesId: "54", colorId: "75") { id + + species { + id + name + } + + color { + id + name + } + bodyId petStateId pose - genderPresentation - emotion - approximateThumbnailUrl layers { id imageUrl(size: SIZE_600) @@ -124,6 +158,20 @@ describe("PetAppearance", () => { "4751", ], ], + Array [ + "SELECT * FROM species_translations + WHERE species_id IN (?) AND locale = \\"en\\"", + Array [ + "54", + ], + ], + Array [ + "SELECT * FROM color_translations + WHERE color_id IN (?) AND locale = \\"en\\"", + Array [ + "75", + ], + ], Array [ "SELECT * FROM zones WHERE id IN (?,?,?,?,?,?)", Array [ diff --git a/src/server/query-tests/__snapshots__/Outfit.test.js.snap b/src/server/query-tests/__snapshots__/Outfit.test.js.snap new file mode 100644 index 0000000..2d31af9 --- /dev/null +++ b/src/server/query-tests/__snapshots__/Outfit.test.js.snap @@ -0,0 +1,70 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Outfit loads an outfit by ID 1`] = ` +Object { + "outfit": Object { + "closetedItems": Array [ + Object { + "id": "36125", + "name": "Blue Newsboy Hat", + }, + Object { + "id": "36467", + "name": "Daring Adventurer Hat", + }, + Object { + "id": "47075", + "name": "Jordies Adventure Hat", + }, + Object { + "id": "47056", + "name": "Moltara Inventor Hat and Goggles", + }, + Object { + "id": "39662", + "name": "Simple Sun Hat", + }, + Object { + "id": "56706", + "name": "Super Sleuth Hat and Wig", + }, + Object { + "id": "38915", + "name": "Zafara Tourist Shirt", + }, + Object { + "id": "56398", + "name": "Altador Cup Kreludor Frame", + }, + ], + "id": "31856", + "name": "Zafara Tourist", + "petAppearance": Object { + "color": Object { + "id": "34", + "name": "Green", + }, + "id": "54-34-UNKNOWN", + "pose": "UNKNOWN", + "species": Object { + "id": "54", + "name": "Zafara", + }, + }, + "wornItems": Array [ + Object { + "id": "38916", + "name": "Zafara Tourist Camera", + }, + Object { + "id": "51054", + "name": "Summer Fun Beach Background", + }, + Object { + "id": "38914", + "name": "Zafara Tourist Hat", + }, + ], + }, +} +`; diff --git a/src/server/query-tests/__snapshots__/PetAppearance.test.js.snap b/src/server/query-tests/__snapshots__/PetAppearance.test.js.snap index 58ca2eb..c23b337 100644 --- a/src/server/query-tests/__snapshots__/PetAppearance.test.js.snap +++ b/src/server/query-tests/__snapshots__/PetAppearance.test.js.snap @@ -3,6 +3,11 @@ exports[`PetAppearance loads for species and color 1`] = ` Object { "petAppearance": Object { + "color": Object { + "id": "75", + "name": "Starry", + }, + "id": "54-75-HAPPY_FEM", "layers": Array [ Object { "id": "5995", @@ -53,6 +58,10 @@ Object { }, }, ], + "species": Object { + "id": "54", + "name": "Zafara", + }, }, } `; @@ -61,10 +70,11 @@ exports[`PetAppearance loads multiple for species and color 1`] = ` Object { "petAppearances": Array [ Object { - "approximateThumbnailUrl": "http://pets.neopets.com/cp/vghhzlgf/1/1.png", "bodyId": "180", - "emotion": "HAPPY", - "genderPresentation": "MASCULINE", + "color": Object { + "id": "75", + "name": "Starry", + }, "id": "54-75-HAPPY_FEM", "layers": Array [ Object { @@ -112,12 +122,17 @@ Object { ], "petStateId": "17723", "pose": "HAPPY_FEM", + "species": Object { + "id": "54", + "name": "Zafara", + }, }, Object { - "approximateThumbnailUrl": "http://pets.neopets.com/cp/vghhzlgf/1/1.png", "bodyId": "180", - "emotion": "HAPPY", - "genderPresentation": "MASCULINE", + "color": Object { + "id": "75", + "name": "Starry", + }, "id": "54-75-HAPPY_MASC", "layers": Array [ Object { @@ -165,12 +180,17 @@ Object { ], "petStateId": "17742", "pose": "HAPPY_MASC", + "species": Object { + "id": "54", + "name": "Zafara", + }, }, Object { - "approximateThumbnailUrl": "http://pets.neopets.com/cp/vghhzlgf/4/1.png", "bodyId": "180", - "emotion": "SICK", - "genderPresentation": "MASCULINE", + "color": Object { + "id": "75", + "name": "Starry", + }, "id": "54-75-SICK_FEM", "layers": Array [ Object { @@ -218,12 +238,17 @@ Object { ], "petStateId": "10014", "pose": "SICK_FEM", + "species": Object { + "id": "54", + "name": "Zafara", + }, }, Object { - "approximateThumbnailUrl": "http://pets.neopets.com/cp/vghhzlgf/4/1.png", "bodyId": "180", - "emotion": "SICK", - "genderPresentation": "MASCULINE", + "color": Object { + "id": "75", + "name": "Starry", + }, "id": "54-75-SICK_MASC", "layers": Array [ Object { @@ -271,12 +296,17 @@ Object { ], "petStateId": "11089", "pose": "SICK_MASC", + "species": Object { + "id": "54", + "name": "Zafara", + }, }, Object { - "approximateThumbnailUrl": "http://pets.neopets.com/cp/vghhzlgf/2/1.png", "bodyId": "180", - "emotion": "SAD", - "genderPresentation": "MASCULINE", + "color": Object { + "id": "75", + "name": "Starry", + }, "id": "54-75-SAD_FEM", "layers": Array [ Object { @@ -324,12 +354,17 @@ Object { ], "petStateId": "5991", "pose": "SAD_FEM", + "species": Object { + "id": "54", + "name": "Zafara", + }, }, Object { - "approximateThumbnailUrl": "http://pets.neopets.com/cp/vghhzlgf/2/1.png", "bodyId": "180", - "emotion": "SAD", - "genderPresentation": "MASCULINE", + "color": Object { + "id": "75", + "name": "Starry", + }, "id": "54-75-SAD_MASC", "layers": Array [ Object { @@ -377,12 +412,17 @@ Object { ], "petStateId": "436", "pose": "SAD_MASC", + "species": Object { + "id": "54", + "name": "Zafara", + }, }, Object { - "approximateThumbnailUrl": "http://pets.neopets.com/cp/vghhzlgf/null/1.png", "bodyId": "180", - "emotion": null, - "genderPresentation": null, + "color": Object { + "id": "75", + "name": "Starry", + }, "id": "54-75-UNKNOWN", "layers": Array [ Object { @@ -437,12 +477,17 @@ Object { ], "petStateId": "2", "pose": "UNKNOWN", + "species": Object { + "id": "54", + "name": "Zafara", + }, }, Object { - "approximateThumbnailUrl": "http://pets.neopets.com/cp/vghhzlgf/null/1.png", "bodyId": "180", - "emotion": null, - "genderPresentation": null, + "color": Object { + "id": "75", + "name": "Starry", + }, "id": "54-75-UNKNOWN", "layers": Array [ Object { @@ -497,6 +542,10 @@ Object { ], "petStateId": "4751", "pose": "UNKNOWN", + "species": Object { + "id": "54", + "name": "Zafara", + }, }, ], } diff --git a/src/server/query-tests/__snapshots__/SpeciesColorPair.test.js.snap b/src/server/query-tests/__snapshots__/SpeciesColorPair.test.js.snap index 4a360f8..5d01528 100644 --- a/src/server/query-tests/__snapshots__/SpeciesColorPair.test.js.snap +++ b/src/server/query-tests/__snapshots__/SpeciesColorPair.test.js.snap @@ -11235,6 +11235,14 @@ Object { "id": "21", }, }, + Object { + "color": Object { + "id": "99", + }, + "species": Object { + "id": "21", + }, + }, Object { "color": Object { "id": "100", @@ -12243,6 +12251,14 @@ Object { "id": "23", }, }, + Object { + "color": Object { + "id": "89", + }, + "species": Object { + "id": "23", + }, + }, Object { "color": Object { "id": "90", @@ -18635,6 +18651,14 @@ Object { "id": "36", }, }, + Object { + "color": Object { + "id": "14", + }, + "species": Object { + "id": "36", + }, + }, Object { "color": Object { "id": "16", @@ -21523,6 +21547,14 @@ Object { "id": "41", }, }, + Object { + "color": Object { + "id": "93", + }, + "species": Object { + "id": "41", + }, + }, Object { "color": Object { "id": "94", @@ -27859,6 +27891,14 @@ Object { "id": "53", }, }, + Object { + "color": Object { + "id": "111", + }, + "species": Object { + "id": "53", + }, + }, Object { "color": Object { "id": "6",