Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@aldeed
Copy link
Contributor

@aldeed aldeed commented Feb 1, 2019

Resolves #4944
Impact: minor
Type: feature

Issue

For reporting, integrations, or other reasons, you may sometimes want to store additional data when placing an order.

Solution

  • The core code and schemas for placing orders now accept a customData object as part of orderInput from clients.
  • Custom plugins may also register a function of type transformCustomOrderData to add to, modify, or remove custom order data provided by clients.

Custom data sent from clients is not supported out of the box because it isn't in the OrderInput GraphQL type. If you want to use this feature, you must create a custom plugin to add that field and define types for it. See draft documentation below.

Additional Changes

HTTP request headers are now added to context, on context.requestHeaders. It is common to want to store some of these on orders that are placed.

Breaking changes

None

Testing

Using the draft documentation below, try out this feature and verify that it works as expected.

Draft Documentation

Storing Custom Order Data

For reporting, integrations, or other reasons, you may want to store additional fields when placing an order. This might be data from the client placing the order, data you can or must generate on the server, or some combination of the two.

The core orders plugin supports attaching additional arbitrary fields to orders, but you must do a bit of work to enable it. The first step is to decide whether the data you need can safely come from clients or if it must be built on the server. An in-between approach is to send data from the client but validate and extend it on the server.

Enabling Clients to Send Additional Order Data

The only step necessary to allow clients to send additional order fields is to define the expected schema for it in GraphQL, which you do by extending the OrderInput GraphQL input type in a custom plugin to add a customFields field:

"Additional order fields"
input CustomOrderFieldsInput {
  "The user agent string for the browser from which this order was placed"
  browserUserAgent: String!
}

extend input OrderInput {
  "Additional order fields"
  customFields: CustomOrderFieldsInput!
}

Now in your storefront code, you can add the now-required extra fields:

// `order` input object for `placeOrder` GraphQL mutation
const order = {
  customFields: {
    browserUserAgent: window.navigator.userAgent
  },
  // all other required OrderInput props
};

Transforming or Adding Custom Order Fields on the Server

Whether or not you've allowed clients to send additional order data, you can also provide a function that returns custom order fields on the server. Do this using the functionsByType option in registerPackage:

functionsByType: {
    transformCustomOrderFields: [({ context, customFields, order }) => ({
      ...customFields,
      // This is a simple example. IRL you should pick only headers you need.
      headers: context.requestHeaders
    })]
},

As you can see in the example, the function receives an object argument with context, customFields, and order properties. order is the full order that is about to be created. customFields is the current custom fields object that would be saved as order.customFields. This may be from the client placing the order, if you've allowed clients to send it, or it may be from transformCustomOrderFields functions registered by other plugins. It will be an empty object if no other transformCustomOrderFields functions have run and the client did not pass any custom fields.

The object you return will replace order.customFields, so if you want to keep the properties already in customFields, be sure to add them to the object you return, or simply return customFields if you have no changes to make. You may also further validate the object and either remove properties from it or throw an error.

transformCustomOrderFields functions may be async if necessary.

Extending the Order Schema

If you are adding custom fields either from clients or from server functions, you must also extend the SimpleSchema for Order so that it validates for storage. This is similar to the "Enabling Clients to Send Additional Order Data" task, but you must extend the SimpleSchema rather than the GraphQL schema, and it's required even if all data is being generated on the server. In your custom plugin:

import { Order } from "/imports/collections/schemas";

const customFieldsSchema = new SimpleSchema({
  browserUserAgent: String
});

Order.extend({
  customFields: customFieldsSchema
});

Exposing Custom Fields Through GraphQL

If you are collecting extra order fields only for reporting or integration purposes, it may not be necessary to have them available through GraphQL queries. But if you do need some fields added to orders retrieved by GraphQL, it is easy to do this by extending the schema and adding resolvers in your custom plugin:

extend type Order {
  "The user agent string for the browser from which this order was placed"
  browserUserAgent: String!
}
const resolvers = {
  Order: {
    browserUserAgent: (node) => node.customFields.browserUserAgent
  }
};

And then query for it:

{
  orderById(id: "123", shopId: "456", token: "abc") {
    browserUserAgent
  }
}

@aldeed aldeed self-assigned this Feb 1, 2019
@aldeed aldeed added this to the πŸ” Torreys milestone Feb 1, 2019
@aldeed
Copy link
Contributor Author

aldeed commented Feb 1, 2019

@Akarshit Does this solve #4944 for you?

Copy link
Member

@ticean ticean left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The suggested approach to adding this to the GraphQL is to extend the GraphQL schemas with the new fields. That looks good to me and I see the example shows adding strongly typed fields. πŸ‘

Can we require a typed customData in simple schema, too and extend the fields with a plugin? Blackboxes aren't portable when make the data available outside the reaction process.

An example to illustrate why it's important, we've got work in progress that exports the Catalog collection to a Kafka topic so that it can be consumed by external processes. It was working great, then we hit a problem where bad values were written to the MongoDB collection and they blocked entire data pipeline. πŸ’₯

Strong schemas create a contract and the and structural validation in the reaction code before writing to the db helps guarantee the data conforms to the contract.

@Akarshit
Copy link
Contributor

Akarshit commented Feb 5, 2019

I am testing this right now.

@Akarshit
Copy link
Contributor

Akarshit commented Feb 5, 2019

This works, can be merged.

Plugin will need to extend the schema

Signed-off-by: Eric Dobbertin <[email protected]>
@aldeed aldeed changed the title feat: support storing custom data on orders when placing feat: support storing custom fields on orders when placing Feb 6, 2019
@aldeed
Copy link
Contributor Author

aldeed commented Feb 6, 2019

@ticean I believe my changes should address your concerns. I removed customData from the built-in Order simple schema, and I added instructions in the draft docs for how to extend that schema from a custom plugin to make everything work. Note that the simple schema that validates the order input still has blackbox: true but since the custom schema validation will always happen before and after that, it should not matter.

I also changed customData to customFields everywhere based on discussion with @rosshadden

@aldeed
Copy link
Contributor Author

aldeed commented Feb 6, 2019

@Akarshit Note the field name change and that you now need to extend the simple schema, too. (See "Extending the Order Schema" in my draft docs.)

@aldeed aldeed merged commit 4ae72a7 into develop Feb 8, 2019
@aldeed aldeed deleted the feat-aldeed-4944-store-additional-order-data branch February 8, 2019 13:45
@jeffcorpuz jeffcorpuz mentioned this pull request Mar 1, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants