Skip to content

Instantly share code, notes, and snippets.

@sepans
Last active November 26, 2018 17:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sepans/56e13c4084dcdd38fd64812d61b89bdb to your computer and use it in GitHub Desktop.
Save sepans/56e13c4084dcdd38fd64812d61b89bdb to your computer and use it in GitHub Desktop.
How to add operations (mutations/queries) to Metaphysics to expose Exchange operations to clients

After adding an operation (mutation or query) to exchange the corresponding operation has to be added to Metaphysics. Because most of the clients don't use exchange directly since exchange doesn't know anything about galleries, users or artworks and it only keeps the ID of those entities. MP pulls that data from gravity and adds that to the order object coming from exchange.

This is a temporary solution for now. Graphql has a mechanism called sticking that can infer all these from the schema and defined types. At this point stitching is not fully implemented so it needs to be done manually. If you know that stitching is enabled for exchange stop reading and delete this document immediately.

How?

Follow this PR as a reference: artsy/metaphysics#1375

  1. Copy updated exchange schema which includes the new mutation here: src/data/exchange.graphql (from exchange/_schema.graphql)

  2. Add test in src/schema/__tests__/ecommerce similar to seller_accept_offer_mutation.test.ts

    Some annotations:

    This mocks exchange to return exchangeOrderJSON (a sample order) when the mutation is called.

    const resolvers = {
      Mutation: {
        sellerAcceptOffer: () => ({
          orderOrError: { order: exchangeOrderJSON },
        }),
      },
    }
    
    rootValue = mockxchange(resolvers)

    This calls the mutation and verifies if the mutation return value matches sampleOrder

        return runQuery(mutation, rootValue).then(data => {
          expect(data!.ecommerceSellerAcceptOffer.orderOrError.order).toEqual(
            sampleOrder()
          )
        })
      })

    Note: There is a limitation with this pattern of testing: it completely mocks the call to exchange so if there is a problem with the last 3 lines of the mutation code where it actually calls the exchange mutation, the test passes but the actual mutation doesn't work.

  3. Add the mutation file in src/schema/ecommerce/ similar to seller_accept_offer_mutation.ts

    Define the input types of the mutation. If the mutation/query you are adding has input types similar to an already existing one, reuse that otherwise define new ones.

    inputFields: OfferMutationInputType.getFields()

    Output of the mutation. Most likely you don't need to change this:

    outputFields: {
      orderOrError: {
        type: OrderOrFailureUnionType,
      },
    },

    offerId needs to match the input field you have defined above accessToken and exchangeSchema are passed to the function to be used to authenticate the call and actually pass it to exchange.

    mutateAndGetPayload: (
        { offerId },
        context,
        { rootValue: { accessToken, exchangeSchema } }

    This is the actual mutation that is passed to exchange:

    const mutation = gql`
      mutation sellerAcceptOffer($offerId: ID!) {
        ecommerceSellerAcceptOffer(input: {
          offerId: $offerId,
        }) {
          orderOrError {
            __typename
            ... on EcommerceOrderWithMutationSuccess {
              order {
                ${SellerOrderFields}
              }
            }
            ... on EcommerceOrderWithMutationFailure {
              error {
                type
                code
                data
              }
            }
          }
        }
      }
    `

    sellerAcceptOffer($offerId: ID!) is just what the mutation is called here and is arbitrary (true? does the name matter?) ecommerceSellerAcceptOffer matches the name of the exchange mutation with ecommerce prefix. (defined in step 5)

    All the fields exchange need to return are defined here. The most common fields are defined here to be reused. You most likely need the SellerOrderFields or BuyerOrderFields depending on what client (force vs volt) calls this mutation

    order {
      ${SellerOrderFields}
    }

    This is where the exchange mutation is actually being called and the result is returned back to the client. ecommerceSellerAcceptOffer should match the prefixed mutation name above.

    return graphql(exchangeSchema, mutation, null, context, {
      offerId,
    }).then(extractEcommerceResponse("ecommerceSellerAcceptOffer"))
  4. Add/Update types in src/schema/ecommerce/types/ if needed:

  5. Add your mutation to src/schema/schema.ts

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment