diff --git a/_schemaV2.graphql b/_schemaV2.graphql index c288f9d279..c3cf803a79 100644 --- a/_schemaV2.graphql +++ b/_schemaV2.graphql @@ -14504,6 +14504,14 @@ type PartialArtwork { } type Partner implements Node { + alertsConnection( + after: String + before: String + first: Int + last: Int + page: Int + size: Int + ): PartnerAlertsConnection alertsSummaryArtistsConnection( activeInventory: Boolean after: String @@ -14821,6 +14829,46 @@ type PartnerAgreement { # A partner agreement or errors object union PartnerAgreementOrErrorsUnion = Errors | PartnerAgreement +# A connection to a list of items. +type PartnerAlertsConnection { + # A list of edges. + edges: [PartnerAlertsEdge] + pageCursors: PageCursors! + + # Information to aid in pagination. + pageInfo: PageInfo! + totalCount: Int +} + +# An edge in a connection. +type PartnerAlertsEdge { + artistId: String + collectorProfilesConnection( + after: String + before: String + first: Int + last: Int + totalCount: Boolean + ): PartnerCollectorProfilesConnection + + # A cursor for use in pagination + cursor: String! + + # A globally unique ID. + id: ID! + + # A type-specific ID. + internalID: ID! + matchedAt: String + + # The item at the end of the edge + node: Alert + partnerId: String + score: String + searchCriteriaId: String + userIds: [String] +} + type PartnerArtist { artist: Artist artworksConnection( @@ -15037,6 +15085,26 @@ enum PartnerClassification { PRIVATE_DEALER } +# A connection to a list of items. +type PartnerCollectorProfilesConnection { + # A list of edges. + edges: [PartnerCollectorProfilesEdge] + pageCursors: PageCursors! + + # Information to aid in pagination. + pageInfo: PageInfo! + totalCount: Int +} + +# An edge in a connection. +type PartnerCollectorProfilesEdge { + # A cursor for use in pagination + cursor: String! + + # The item at the end of the edge + node: CollectorProfileType +} + # A connection to a list of items. type PartnerConnection { # A list of edges. diff --git a/src/lib/loaders/loaders_with_authentication/gravity.ts b/src/lib/loaders/loaders_with_authentication/gravity.ts index 9299ba4929..def2cdd235 100644 --- a/src/lib/loaders/loaders_with_authentication/gravity.ts +++ b/src/lib/loaders/loaders_with_authentication/gravity.ts @@ -597,6 +597,11 @@ export default (accessToken, userID, opts) => { ({ partnerId, userId }) => `partner_collector_profile?partner_id=${partnerId}&user_id=${userId}` ), + partnerCollectorProfilesLoader: gravityLoader( + "partner_collector_profiles", + {}, + { headers: true } + ), partnerCollectorProfileArtworkInquiryCountLoader: gravityLoader< any, { partnerID: string; collectorProfileID: string } @@ -643,6 +648,11 @@ export default (accessToken, userID, opts) => { {}, { headers: true } ), + partnerSearchCriteriaLoader: gravityLoader( + (id) => `/partner/${id}/partner_search_criterias`, + {}, + { headers: true } + ), partnerShowsLoader: gravityLoader( (partner_id) => `partner/${partner_id}/shows`, {}, diff --git a/src/schema/v2/Alerts/index.ts b/src/schema/v2/Alerts/index.ts index 73f93201ea..416e4e986d 100644 --- a/src/schema/v2/Alerts/index.ts +++ b/src/schema/v2/Alerts/index.ts @@ -12,6 +12,7 @@ import { connectionWithCursorInfo, createPageCursors, emptyConnection, + paginationResolver, } from "schema/v2/fields/pagination" import { ResolverContext } from "types/graphql" import { IDFields } from "../object_identification" @@ -26,6 +27,7 @@ import { pageable } from "relay-cursor-paging" import { convertConnectionArgsToGravityArgs } from "lib/helpers" import { connectionFromArray } from "graphql-relay" import { generateDisplayName } from "../previewSavedSearch/generateDisplayName" +import { CollectorProfileType } from "../CollectorProfile/collectorProfile" type GravityAlertSettingsJSON = { name: string @@ -35,6 +37,11 @@ type GravityAlertSettingsJSON = { frequency: string } +const PartnerCollectorProfilesConnectionType = connectionWithCursorInfo({ + name: "PartnerCollectorProfiles", + nodeType: CollectorProfileType, +}).connectionType + export const AlertSettingsFrequencyType = new GraphQLEnumType({ name: "AlertSettingsFrequency", values: { @@ -484,3 +491,84 @@ export const AlertsSummaryFields = { resolve: (resp) => resp, }, } + +export const PartnerAlertsEdgeFields = { + ...IDFields, + searchCriteriaId: { + type: GraphQLString, + resolve: ({ search_criteria_id }) => search_criteria_id, + }, + partnerId: { + type: GraphQLString, + resolve: ({ partner_id }) => partner_id, + }, + score: { type: GraphQLString }, + matchedAt: { + type: GraphQLString, + resolve: ({ matched_at }) => matched_at, + }, + userIds: { + type: new GraphQLList(GraphQLString), + resolve: ({ user_ids }) => user_ids, + }, + artistId: { + type: GraphQLString, + resolve: ({ artist_id }) => artist_id, + }, + collectorProfilesConnection: { + type: PartnerCollectorProfilesConnectionType, + args: pageable({ + totalCount: { + type: GraphQLBoolean, + }, + }), + resolve: async (parent, args, { partnerCollectorProfilesLoader }) => { + if (!partnerCollectorProfilesLoader) return null + + const { page, size, offset } = convertConnectionArgsToGravityArgs(args) + + const { partner_id, user_ids } = parent + if (!partner_id || !user_ids) { + throw new Error( + "partnerId or userIds is undefined in the parent object" + ) + } + + type GravityArgs = { + page: number + size: number + offset: number + total_count: boolean + partner_id: string + user_ids: string[] + } + + const gravityArgs: GravityArgs = { + page, + size, + offset, + total_count: true, + partner_id: parent.partner_id, + user_ids: parent.user_ids, + } + + const { body, headers } = await partnerCollectorProfilesLoader( + gravityArgs + ) + + const collectorProfiles = body.flatMap((item) => + item.collector_profile ? [item.collector_profile].flat() : [] + ) + + const totalCount = parseInt(headers["x-total-count"] || "0", 10) + return paginationResolver({ + totalCount, + offset, + page, + size, + body: collectorProfiles, + args, + }) + }, + }, +} diff --git a/src/schema/v2/partner/__tests__/partner.test.js b/src/schema/v2/partner/__tests__/partner.test.js index 3a43d4fcda..9b58537146 100644 --- a/src/schema/v2/partner/__tests__/partner.test.js +++ b/src/schema/v2/partner/__tests__/partner.test.js @@ -1421,6 +1421,77 @@ describe("Partner type", () => { }) }) + describe("#alertsConnection", () => { + it("returns partner search criteria details and associated search criteria details", async () => { + const response = + { + body: { + hits: [ + { + id: '8754ff90-b020-425b-8dae-894ce5ad9d1f', + search_criteria_id: '3f980bbe-7b9b-4fa9-beb1-69d13e94fb0c', + partner_id: '5f80bfefe8d808000ea212c1', + search_criteria: { + id: '3f980bbe-7b9b-4fa9-beb1-69d13e94fb0c', + price_range: "1-2" + } + }, + { + id: 'b4adc50d-a584-4820-9140-4d49d7b6c7dc', + search_criteria_id: '614af56c-88cb-4ea7-bec5-fbdf9b67c24a', + partner_id: '5f80bfefe8d808000ea212c1', + search_criteria: { + id: '614af56c-88cb-4ea7-bec5-fbdf9b67c24a', + price_range: "1000-5000" + } + }, + ], + }, + headers: { + "x-total-count": 2, + }, + } + + const query = gql` + { + partner(id: "catty-partner") { + alertsConnection(first: 10) { + edges { + node { + priceRange + } + } + } + } + } + ` + const partnerSearchCriteriaLoader = () => Promise.resolve(response) + + const data = await runAuthenticatedQuery(query, { + ...context, + partnerSearchCriteriaLoader, + }) + expect(data).toEqual({ + partner: { + alertsConnection: { + edges: [ + { + node: { + priceRange: "1-2", + }, + }, + { + node: { + priceRange: "1000-5000" + } + } + ], + }, + }, + }) + }) + }) + describe("#alertsSummaryArtistsConnection", () => { it("returns the summary of artists with recently enabled user search criteria", async () => { const summaryResponse = { diff --git a/src/schema/v2/partner/partner.ts b/src/schema/v2/partner/partner.ts index 591531593d..421ef9632b 100644 --- a/src/schema/v2/partner/partner.ts +++ b/src/schema/v2/partner/partner.ts @@ -46,7 +46,11 @@ import { setVersion } from "schema/v2/image/normalize" import { compact } from "lodash" import { InquiryRequestType } from "./partnerInquiryRequest" import { PartnerDocumentsConnection } from "./partnerDocumentsConnection" -import { AlertsSummaryFields } from "../Alerts" +import { + AlertType, + AlertsSummaryFields, + PartnerAlertsEdgeFields, +} from "../Alerts" import { ArtworkVisibility, ArtworkVisibilityEnumValues, @@ -154,6 +158,12 @@ export const PartnerType = new GraphQLObjectType({ nodeType: ArtistType, }).connectionType + const PartnerAlertsConnectionType = connectionWithCursorInfo({ + name: "PartnerAlerts", + edgeFields: PartnerAlertsEdgeFields, + nodeType: AlertType, + }).connectionType + return { ...SlugAndInternalIDFields, cached, @@ -222,6 +232,52 @@ export const PartnerType = new GraphQLObjectType({ }) }, }, + alertsConnection: { + type: PartnerAlertsConnectionType, + args: pageable({ + page: { + type: GraphQLInt, + }, + size: { + type: GraphQLInt, + }, + }), + resolve: async ({ _id }, args, { partnerSearchCriteriaLoader }) => { + if (!partnerSearchCriteriaLoader) return null + const { page, size, offset } = convertConnectionArgsToGravityArgs( + args + ) + + type GravityArgs = { + page: number + size: number + total_count: boolean + } + + const gravityArgs: GravityArgs = { + page, + size, + total_count: true, + } + + const { body, headers } = await partnerSearchCriteriaLoader?.( + _id, + gravityArgs + ) + + const totalCount = parseInt(headers["x-total-count"] || "0", 10) + + return paginationResolver({ + totalCount, + offset, + page, + size, + body: body.hits, + args, + resolveNode: ({ search_criteria }) => search_criteria, + }) + }, + }, articlesConnection: { description: "A connection of articles related to a partner.", type: articleConnection.connectionType,