From 868081042bf2b82055790aef1d2fe4f0da579740 Mon Sep 17 00:00:00 2001 From: Devon Blandin Date: Tue, 17 Dec 2024 15:06:18 +0100 Subject: [PATCH] feat: add (record|remove)ArtworkView mutations - add recordArtworkView mutation to replace stitched version from Gravity - new removeArtworkView mutation --- _schemaV2.graphql | 44 +++++++++++++------ .../loaders_with_authentication/gravity.ts | 10 +++++ src/lib/stitching/gravity/schema.ts | 3 ++ .../recordArtworkViewMutation.test.ts | 25 +++++++++++ .../removeArtworkViewMutation.test.ts | 25 +++++++++++ .../__tests__/recentlyViewedArtworks.test.ts | 8 +--- src/schema/v2/recordArtworkView.ts | 43 ++++++++++++++++++ src/schema/v2/removeArtworkView.ts | 43 ++++++++++++++++++ src/schema/v2/schema.ts | 4 ++ 9 files changed, 184 insertions(+), 21 deletions(-) create mode 100644 src/schema/v2/__tests__/recordArtworkViewMutation.test.ts create mode 100644 src/schema/v2/__tests__/removeArtworkViewMutation.test.ts create mode 100644 src/schema/v2/recordArtworkView.ts create mode 100644 src/schema/v2/removeArtworkView.ts diff --git a/_schemaV2.graphql b/_schemaV2.graphql index f475dc99bc..016a68585b 100644 --- a/_schemaV2.graphql +++ b/_schemaV2.graphql @@ -14057,10 +14057,16 @@ type Mutation { input: MyCollectionUpdateArtworkInput! ): MyCollectionUpdateArtworkPayload publishViewingRoom(input: PublishViewingRoomInput!): PublishViewingRoomPayload + + # Record an artwork view. recordArtworkView( - # Parameters for RecordArtworkView - input: RecordArtworkViewInput! - ): RecordArtworkViewPayload + input: RecordArtworkViewMutationInput! + ): RecordArtworkViewMutationPayload + + # Remove an artwork view. + removeArtworkView( + input: RemoveArtworkViewMutationInput! + ): RemoveArtworkViewMutationPayload removeAssetFromConsignmentSubmission( # Parameters for RemoveAssetFromConsignmentSubmission input: RemoveAssetFromConsignmentSubmissionInput! @@ -17717,23 +17723,18 @@ type RecentlySoldArtworkTypeEdge { node: RecentlySoldArtworkType } -# Autogenerated input type of RecordArtworkView -input RecordArtworkViewInput { +input RecordArtworkViewMutationInput { + # ID of artwork. artwork_id: String! - - # A unique identifier for the client performing the mutation. clientMutationId: String } -# Autogenerated return type of RecordArtworkView -type RecordArtworkViewPayload { - # Id of viewed artwork +type RecordArtworkViewMutationPayload { + # ID of viewed artwork. artworkId: String! - # Id of viewed artwork - artwork_id: String! @deprecated(reason: "Use artworkId") - - # A unique identifier for the client performing the mutation. + # ID of viewed artwork. + artwork_id: String! @deprecated(reason: "Use artworkId.") clientMutationId: String } @@ -17761,6 +17762,21 @@ type RelatedArtworkGrid implements ArtworkContextGrid { title: String } +input RemoveArtworkViewMutationInput { + # ID of artwork. + artwork_id: String! + clientMutationId: String +} + +type RemoveArtworkViewMutationPayload { + # ID of viewed artwork. + artworkId: String! + + # ID of viewed artwork. + artwork_id: String! @deprecated(reason: "Use artworkId.") + clientMutationId: String +} + # Autogenerated input type of RemoveAssetFromConsignmentSubmission input RemoveAssetFromConsignmentSubmissionInput { assetID: String diff --git a/src/lib/loaders/loaders_with_authentication/gravity.ts b/src/lib/loaders/loaders_with_authentication/gravity.ts index add2f30087..53668dd4bb 100644 --- a/src/lib/loaders/loaders_with_authentication/gravity.ts +++ b/src/lib/loaders/loaders_with_authentication/gravity.ts @@ -72,6 +72,11 @@ export default (accessToken, userID, opts) => { {}, { method: "POST" } ), + createArtworkViewLoader: gravityLoader( + (artworkID) => `artwork/${artworkID}/view`, + {}, + { method: "POST" } + ), createCommerceOptInEligibleArtworksReportLoader: gravityLoader( (id) => `partner/${id}/commerce_opt_in_eligible_artworks_report`, {}, @@ -215,6 +220,11 @@ export default (accessToken, userID, opts) => { {}, { method: "DELETE" } ), + deleteArtworkViewLoader: gravityLoader( + (artworkID) => `artwork/${artworkID}/view`, + {}, + { method: "DELETE" } + ), deleteBankAccountLoader: gravityLoader( (id) => `me/bank_account/${id}`, {}, diff --git a/src/lib/stitching/gravity/schema.ts b/src/lib/stitching/gravity/schema.ts index bf067708c1..6a91f1f599 100644 --- a/src/lib/stitching/gravity/schema.ts +++ b/src/lib/stitching/gravity/schema.ts @@ -87,6 +87,7 @@ export const executableGravitySchema = () => { excludedMutations.push("updateViewingRoomArtworks") excludedMutations.push("updateViewingRoomSubsections") } + excludedMutations.push("recordArtworkView") // Types which come from Gravity that are not (yet) needed in MP. // In the future, these can be removed from this list as they are needed. @@ -96,6 +97,8 @@ export const executableGravitySchema = () => { "LotEvent", "RefundCommissionExemptionInput", "RefundCommissionExemptionPayload", + "RecordArtworkViewInput", + "RecordArtworkViewPayload", ] // Return the new modified schema diff --git a/src/schema/v2/__tests__/recordArtworkViewMutation.test.ts b/src/schema/v2/__tests__/recordArtworkViewMutation.test.ts new file mode 100644 index 0000000000..30e80f1494 --- /dev/null +++ b/src/schema/v2/__tests__/recordArtworkViewMutation.test.ts @@ -0,0 +1,25 @@ +/* eslint-disable promise/always-return */ +import { runAuthenticatedQuery } from "schema/v2/test/utils" + +describe("recording an artwork view", () => { + const query = ` + mutation { + recordArtworkView(input: { artwork_id: "artwork-id" }) { + artworkId + } + } + ` + + const context = { + createArtworkViewLoader: (id) => Promise.resolve({ artwork_id: id }), + } + + it("records an artwork view", async () => { + const data = await runAuthenticatedQuery(query, context) + expect(data).toEqual({ + recordArtworkView: { + artworkId: "artwork-id", + }, + }) + }) +}) diff --git a/src/schema/v2/__tests__/removeArtworkViewMutation.test.ts b/src/schema/v2/__tests__/removeArtworkViewMutation.test.ts new file mode 100644 index 0000000000..a2512bc590 --- /dev/null +++ b/src/schema/v2/__tests__/removeArtworkViewMutation.test.ts @@ -0,0 +1,25 @@ +/* eslint-disable promise/always-return */ +import { runAuthenticatedQuery } from "schema/v2/test/utils" + +describe("removing an artwork view", () => { + const query = ` + mutation { + removeArtworkView(input: { artwork_id: "artwork-id" }) { + artworkId + } + } + ` + + const context = { + deleteArtworkViewLoader: (id) => Promise.resolve({ artwork_id: id }), + } + + it("removes an artwork view", async () => { + const data = await runAuthenticatedQuery(query, context) + expect(data).toEqual({ + removeArtworkView: { + artworkId: "artwork-id", + }, + }) + }) +}) diff --git a/src/schema/v2/me/__tests__/recentlyViewedArtworks.test.ts b/src/schema/v2/me/__tests__/recentlyViewedArtworks.test.ts index 713de94318..162a91f639 100644 --- a/src/schema/v2/me/__tests__/recentlyViewedArtworks.test.ts +++ b/src/schema/v2/me/__tests__/recentlyViewedArtworks.test.ts @@ -22,7 +22,7 @@ describe("RecentlyViewedArtworks", () => { context = { meLoader: async () => me, artworksLoader: async () => artworks, - recordArtworkViewLoader: jest.fn(async () => me), + createArtworkViewLoader: jest.fn(async (id) => ({ artwork_id: id })), } }) @@ -213,12 +213,6 @@ describe("RecentlyViewedArtworks", () => { const data = await runAuthenticatedQuery(mutation, context) - // The graphQL API - expect(mockFetch).toBeCalledWith( - "https://api.artsy.test/api/graphql", - expect.anything() - ) - const artworkID = data!.recordArtworkView.artwork_id expect(artworkID).toEqual("percy") }) diff --git a/src/schema/v2/recordArtworkView.ts b/src/schema/v2/recordArtworkView.ts new file mode 100644 index 0000000000..dd7e7a8b77 --- /dev/null +++ b/src/schema/v2/recordArtworkView.ts @@ -0,0 +1,43 @@ +import { GraphQLNonNull, GraphQLString } from "graphql" +import { mutationWithClientMutationId } from "graphql-relay" +import { ResolverContext } from "types/graphql" +import { GraphQLError } from "graphql" + +export interface RecordArtworkViewMutationInput { + artwork_id: string +} + +export const recordArtworkViewMutation = mutationWithClientMutationId< + RecordArtworkViewMutationInput, + any, + ResolverContext +>({ + name: "RecordArtworkViewMutation", + description: "Record an artwork view.", + inputFields: { + artwork_id: { + type: new GraphQLNonNull(GraphQLString), + description: "ID of artwork.", + }, + }, + outputFields: { + artwork_id: { + description: "ID of viewed artwork.", + type: new GraphQLNonNull(GraphQLString), + deprecationReason: "Use artworkId.", + }, + artworkId: { + description: "ID of viewed artwork.", + type: new GraphQLNonNull(GraphQLString), + }, + }, + mutateAndGetPayload: async ({ artwork_id }, { createArtworkViewLoader }) => { + try { + const response = await createArtworkViewLoader(artwork_id) + + return { artwork_id: response.artwork_id, artworkId: response.artwork_id } + } catch (error) { + throw new GraphQLError(`RecordArtworkViewMutation error: ${error}`) + } + }, +}) diff --git a/src/schema/v2/removeArtworkView.ts b/src/schema/v2/removeArtworkView.ts new file mode 100644 index 0000000000..5fff89025d --- /dev/null +++ b/src/schema/v2/removeArtworkView.ts @@ -0,0 +1,43 @@ +import { GraphQLNonNull, GraphQLString } from "graphql" +import { mutationWithClientMutationId } from "graphql-relay" +import { ResolverContext } from "types/graphql" +import { GraphQLError } from "graphql" + +export interface RemoveArtworkViewMutationInput { + artwork_id: string +} + +export const removeArtworkViewMutation = mutationWithClientMutationId< + RemoveArtworkViewMutationInput, + any, + ResolverContext +>({ + name: "RemoveArtworkViewMutation", + description: "Remove an artwork view.", + inputFields: { + artwork_id: { + type: new GraphQLNonNull(GraphQLString), + description: "ID of artwork.", + }, + }, + outputFields: { + artwork_id: { + description: "ID of viewed artwork.", + type: new GraphQLNonNull(GraphQLString), + deprecationReason: "Use artworkId.", + }, + artworkId: { + description: "ID of viewed artwork.", + type: new GraphQLNonNull(GraphQLString), + }, + }, + mutateAndGetPayload: async ({ artwork_id }, { deleteArtworkViewLoader }) => { + try { + const response = await deleteArtworkViewLoader(artwork_id) + + return { artworkId: response.artwork_id } + } catch (error) { + throw new GraphQLError(`RemoveArtworkViewMutation error: ${error}`) + } + }, +}) diff --git a/src/schema/v2/schema.ts b/src/schema/v2/schema.ts index f34c917cb2..c2c2a5503c 100644 --- a/src/schema/v2/schema.ts +++ b/src/schema/v2/schema.ts @@ -268,6 +268,8 @@ import { unpublishViewingRoomMutation } from "./viewingRooms/mutations/unpublish import { updateViewingRoomArtworksMutation } from "./viewingRooms/mutations/updateViewingRoomArtworks" import { updateViewingRoomSubsectionsMutation } from "./viewingRooms/mutations/updateViewingRoomSubsections" import { ViewingRoomConnection } from "./viewingRooms" +import { recordArtworkViewMutation } from "./recordArtworkView" +import { removeArtworkViewMutation } from "./removeArtworkView" const viewingRoomUnstitchedRootField = config.USE_UNSTITCHED_VIEWING_ROOM_SCHEMA ? { @@ -506,6 +508,8 @@ export default new GraphQLSchema({ myCollectionCreateArtwork: myCollectionCreateArtworkMutation, myCollectionDeleteArtwork: myCollectionDeleteArtworkMutation, myCollectionUpdateArtwork: myCollectionUpdateArtworkMutation, + recordArtworkView: recordArtworkViewMutation, + removeArtworkView: removeArtworkViewMutation, requestCredentialsForAssetUpload: CreateAssetRequestLoader, requestPriceEstimate: requestPriceEstimateMutation, saveArtwork: saveArtworkMutation,