diff --git a/.env.example b/.env.example index f9d342cc77..4004c7b38e 100644 --- a/.env.example +++ b/.env.example @@ -12,6 +12,7 @@ GRAVITY_GRAPHQL_ENDPOINT=https://stagingapi.artsy.net/api GRAVITY_API_URL=https://stagingapi.artsy.net/ HMAC_SECRET=https://www.youtube.com/watch?v=DLzxrzFCyOs IMPULSE_API_BASE=https://impulse-staging.artsy.net/api +LEWITT_API_BASE=https://lewitt-api-staging.artsy.net POSITRON_API_BASE=https://writer-staging.artsy.net/api PREDICTION_ENDPOINT=https://live-staging.artsy.net REDIS_URL=redis://127.0.0.1:6379 diff --git a/.env.test b/.env.test index 7b89bb9df0..3073c1df28 100644 --- a/.env.test +++ b/.env.test @@ -13,6 +13,7 @@ GRAVITY_API_URL=https://api.artsy.test HMAC_SECRET=https://www.youtube.com/watch?v=F5bAa6gFvLs IMPULSE_API_BASE=https://impulse-staging.artsy.net/api IMPULSE_APPLICATION_ID=sdfsdf234234f +LEWITT_API_BASE=https://lewitt-api-staging.artsy.net METAPHYSICS_PRODUCTION_ENDPOINT=https://metaphysics-production.artsy.net METAPHYSICS_STAGING_ENDPOINT=https://metaphysics-staging.artsy.net POSITRON_API_BASE=https://writer.artsy.test/api diff --git a/scripts/dump-remote-schema.js b/scripts/dump-remote-schema.js index 14728ba8d1..de011efeb6 100644 --- a/scripts/dump-remote-schema.js +++ b/scripts/dump-remote-schema.js @@ -12,14 +12,26 @@ import fetch from "node-fetch" const destination = "src/data" -const httpLink = createHttpLink({ +const httpConvectionLink = createHttpLink({ fetch, uri: urljoin("https://convection-staging.artsy.net/api", "graphql"), }) -introspectSchema(httpLink).then(schema => { +introspectSchema(httpConvectionLink).then(schema => { fs.writeFileSync( path.join(destination, "convection.graphql"), printSchema(schema, { commentDescriptions: true }) ) }) + +const httpLewittLink = createHttpLink({ + fetch, + uri: urljoin("https://lewitt-api-staging.artsy.net", "graphql"), +}) + +introspectSchema(httpLewittLink).then(schema => { + fs.writeFileSync( + path.join(destination, "lewitt.graphql"), + printSchema(schema, { commentDescriptions: true }) + ) +}) diff --git a/src/config.js b/src/config.js index 117f5dd68e..b7a356f55e 100644 --- a/src/config.js +++ b/src/config.js @@ -31,6 +31,7 @@ const { HMAC_SECRET, IMPULSE_API_BASE, IMPULSE_APPLICATION_ID, + LEWITT_API_BASE, METAPHYSICS_STAGING_ENDPOINT, METAPHYSICS_PRODUCTION_ENDPOINT, NODE_ENV, @@ -62,6 +63,7 @@ const mustHave = { GRAVITY_SECRET, IMPULSE_API_BASE, IMPULSE_APPLICATION_ID, + LEWITT_API_BASE, POSITRON_API_BASE, } @@ -107,6 +109,7 @@ export default { HMAC_SECRET, IMPULSE_API_BASE, IMPULSE_APPLICATION_ID, + LEWITT_API_BASE, METAPHYSICS_STAGING_ENDPOINT, METAPHYSICS_PRODUCTION_ENDPOINT, NODE_ENV: NODE_ENV || "development", diff --git a/src/data/lewitt.graphql b/src/data/lewitt.graphql new file mode 100644 index 0000000000..fb61a2f902 --- /dev/null +++ b/src/data/lewitt.graphql @@ -0,0 +1,94 @@ +schema { + query: RootQuery + mutation: Mutation +} + +# An ArtworkGroup which has an artwork and its related line items +type ArtworkGroup { + artists: [String] + date: String + default_image_id: String + dimensions: Json + edition_set_id: String + id: ID! + images: Json + line_items: [LineItem] + price_in_cents: Int! + title: String +} + +# Autogenerated input type of CreateInvoice +input CreateInvoiceInput { + # A unique identifier for the client performing the mutation. + clientMutationId: String + id: ID + partner_id: String! + payment_method: String + currency: Currencies + impulse_conversation_id: String +} + +enum Currencies { + # US Dollar + usd +} + +# An Invoice +type Invoice { + artwork_groups: [ArtworkGroup] + artwork_groups_count: Int + created_at: String + currency: String + discount_total_cents: Int + fee_in_cents: Int + id: ID! + impulse_conversation_id: Int + invoice_transactions_count: Int + line_items: [LineItem] + line_items_count: Int + merchant_account: [MerchantAccount] + merchant_account_id: Int + paid_at: String + partner_id: String + payment_method: String + refunded_at: String + sent_at: String + shipping_total_cents: Int + state: String + tax_total_cents: Int + token: String! + total_cents: Int + updated_at: String + voided_at: String +} + +scalar Json + +# A LineItem +type LineItem { + amount_cents: Int! + description: String! + id: ID! + line_item_type: String! +} + +# A MerchantAccount +type MerchantAccount { + # Checks if account is properly configured for credit card payments + credit_card_enabled: Boolean! + id: ID! + partner_id: String! +} + +type Mutation { + create_invoice(input: CreateInvoiceInput!): Invoice +} + +# Root query +type RootQuery { + # Search Invoices + invoices(ids: [ID], partner_id: String!, token: String): [Invoice] + + # Retrieve a Merchant Account + partner_product_merchant_account(partner_id: String!): MerchantAccount +} diff --git a/src/index.js b/src/index.js index cfc9fa3a53..5280f354b0 100644 --- a/src/index.js +++ b/src/index.js @@ -17,7 +17,7 @@ import { } from "lib/loaders/api/logger" import { fetchPersistedQuery } from "./lib/fetchPersistedQuery" import { info } from "./lib/loggers" -import { mergeSchemas } from "./lib/mergeSchemas" +import { executableLewittSchema, mergeSchemas } from "./lib/mergeSchemas" import { middleware as requestIDsAdder } from "./lib/requestIDs" import { middleware as requestTracer, makeSchemaTraceable } from "./lib/tracer" @@ -42,6 +42,7 @@ async function startApp() { config.GRAVITY_XAPP_TOKEN = xapp.token let schema = localSchema + const lewittSchema = await executableLewittSchema() if (enableSchemaStitching) { try { @@ -111,6 +112,7 @@ async function startApp() { userID, defaultTimezone, span, + lewittSchema, ...createLoaders(accessToken, userID, { requestIDs, userAgent, diff --git a/src/lib/mergeSchemas.js b/src/lib/mergeSchemas.js index 55d852a3f7..1bea9326ac 100644 --- a/src/lib/mergeSchemas.js +++ b/src/lib/mergeSchemas.js @@ -15,7 +15,11 @@ import { headers as requestIDHeaders } from "./requestIDs" import config from "config" -const { CONVECTION_API_BASE, GRAVITY_GRAPHQL_ENDPOINT } = config +const { + CONVECTION_API_BASE, + GRAVITY_GRAPHQL_ENDPOINT, + LEWITT_API_BASE, +} = config export function createConvectionLink() { const httpLink = createHttpLink({ @@ -68,20 +72,54 @@ export function createGravityLink() { return middlewareLink.concat(authMiddleware).concat(httpLink) } -export async function mergeSchemas() { - // The below all relate to Convection stitching. - // TODO: Refactor when adding another service. +export async function executableConvectionSchema() { + const convectionLink = createConvectionLink() const convectionTypeDefs = fs.readFileSync( path.join("src/data/convection.graphql"), "utf8" ) - - const convectionLink = createConvectionLink() - - const convectionSchema = await makeRemoteExecutableSchema({ + return await makeRemoteExecutableSchema({ schema: convectionTypeDefs, link: convectionLink, }) +} + +export function createLewittLink() { + const httpLink = createHttpLink({ + fetch, + uri: urljoin(LEWITT_API_BASE, "graphql"), + }) + + const middlewareLink = new ApolloLink((operation, forward) => + forward(operation) + ) + + const authMiddleware = setContext((_request, context) => { + const locals = context.graphqlContext && context.graphqlContext.res.locals + const headers = { ...(locals && requestIDHeaders(locals.requestIDs)) } + // Lewitt uses no authentication for now + return { headers } + }) + + return middlewareLink.concat(authMiddleware).concat(httpLink) +} + +export async function executableLewittSchema() { + const lewittLink = createLewittLink() + const lewittTypeDefs = fs.readFileSync( + path.join("src/data/lewitt.graphql"), + "utf8" + ) + return await makeRemoteExecutableSchema({ + schema: lewittTypeDefs, + link: lewittLink, + }) +} + +export async function mergeSchemas() { + // The below all relate to Convection stitching. + // TODO: Refactor when adding another service. + const convectionSchema = await executableConvectionSchema() const gravityTypeDefs = fs.readFileSync( path.join("src/data/gravity.graphql"), diff --git a/src/schema/__tests__/partner.test.js b/src/schema/__tests__/partner.test.js index a534c23e50..8340780999 100644 --- a/src/schema/__tests__/partner.test.js +++ b/src/schema/__tests__/partner.test.js @@ -1,4 +1,9 @@ import { runQuery } from "test/utils" +import gql from "test/gql" + +import { makeExecutableSchema } from "graphql-tools" +import fs from "fs" +import path from "path" describe("Partner type", () => { let partner = null @@ -7,6 +12,7 @@ describe("Partner type", () => { beforeEach(() => { partner = { id: "catty-partner", + _id: "catty-partner", name: "Catty Partner", has_full_profile: true, profile_banner_display: true, @@ -27,7 +33,7 @@ describe("Partner type", () => { }) it("returns a partner and categories", () => { - const query = ` + const query = gql` { partner(id: "catty-partner") { name @@ -55,4 +61,71 @@ describe("Partner type", () => { }) }) }) + + describe("acceptsCardPayments", () => { + let credit_card_enabled = true + const query = gql` + { + partner(id: "catty-partner") { + acceptsCardPayments + } + } + ` + + beforeEach(() => { + partner.payments_enabled = true + + const typeDefs = fs.readFileSync( + path.resolve(__dirname, "../../data/lewitt.graphql"), + "utf8" + ) + + const resolvers = { + RootQuery: { + partner_product_merchant_account: () => { + return { credit_card_enabled } + }, + }, + } + + const lewittSchema = makeExecutableSchema({ + typeDefs, + resolvers, + }) + + rootValue.lewittSchema = lewittSchema + }) + + it("returns true if payments_enabled and partner_product_merchant_account is configured in lewitt", () => { + return runQuery(query, rootValue).then(data => { + expect(data).toEqual({ + partner: { + acceptsCardPayments: true, + }, + }) + }) + }) + + it("returns false if payments_enabled set to false on partner", () => { + partner.payments_enabled = false + return runQuery(query, rootValue).then(data => { + expect(data).toEqual({ + partner: { + acceptsCardPayments: false, + }, + }) + }) + }) + + it("returns false if partner_product_merchant_account is not configured in lewitt", () => { + credit_card_enabled = false + return runQuery(query, rootValue).then(data => { + expect(data).toEqual({ + partner: { + acceptsCardPayments: false, + }, + }) + }) + }) + }) }) diff --git a/src/schema/partner.js b/src/schema/partner.js index c5886abc51..ed5e8f3daf 100644 --- a/src/schema/partner.js +++ b/src/schema/partner.js @@ -8,6 +8,7 @@ import { GravityIDFields, NodeInterface } from "./object_identification" import Artwork from "./artwork" import numeral from "./fields/numeral" import ArtworkSorts from "./sorts/artwork_sorts" +import { graphql } from "graphql" import { GraphQLString, @@ -223,6 +224,38 @@ const PartnerType = new GraphQLObjectType({ return exceptions[type] || type }, }, + acceptsCardPayments: { + type: GraphQLBoolean, + resolve: ( + partner, + _args, + _request, + { rootValue: { lewittSchema } } + ) => { + const { _id, payments_enabled } = partner + if (!payments_enabled) { + return false + } + const query = ` + query MerchantAccountQuery($partner_id: String!) { + partner_product_merchant_account(partner_id: $partner_id) { + credit_card_enabled + } + } + ` + return graphql(lewittSchema, query, null, null, { + partner_id: _id, + }).then(response => { + if (response.errors) { + throw new Error( + `Lewitt errors: ${JSON.stringify(response.errors)}` + ) + } + const { data: { partner_product_merchant_account } } = response + return partner_product_merchant_account.credit_card_enabled + }) + }, + }, } }, })