## 1. Project Overview & Quickstart (reactioncommerce/LICENSE)

# LICENSE

Open-source repository reactioncommerce/LICENSE

### Repository Details
- **Repository:** [reactioncommerce/LICENSE](https://github.com/reactioncommerce/LICENSE)
- **Primary Language:** Code

*Note: High-volume repository documentation is actively indexed and synchronized by YakaAI.*

## 2. Official Technical Reference & Guides (reactioncommerce/docs)

## File: README.md

# Mailchimp Open Commerce docs

This repo hosts the open source documentation for [Mailchimp Open Commerce](https://mailchimp.com/developer/open-commerce/) (formerly Reaction Commerce). Previous versions of the Reaction Commerce docs are archived in [a separate repo](https://github.com/reactioncommerce/reaction-docs).

## Markdown and custom syntax

The files in this repo are the source content for the [Open Commerce docs](https://mailchimp.com/developer/open-commerce/docs/) on the [Mailchimp Developer](https://mailchimp.com/developer/) site. 

To render properly on the developer site, all docs should be written in Markdown. Some standard Markdown elements are not supported: **avoid** H3, H4, H5, and H6 headers, as well as nested lists. 

The developer site does support some Markdown-compatible custom components:

### Code snippets

Delimit code blocks with triple backticks as in standard Markdown. You can optionally add a [GitHub-flavored Markdown language](https://docs.github.com/en/github/writing-on-github/creating-and-highlighting-code-blocks#syntax-highlighting) and a `title` parameter:


```js title=Example
console.log('hello world');
```




The title parameter [cannot contain whitespace](https://github.com/mdx-js/mdx/issues/702), so we recommend only using it for filenames, indicating the location of the referenced code.

Code blocks without a language and title are rendered in a plain module:


```
testing 1 2 3
```




### Note blocks

Create custom note blocks by starting a blockquote with the string `**Note**: `:


> **Note**: hello there




Other blockquotes will be rendered normally:


> Hello there
 



## Contributing

Contributions to this repo are welcome. Please submit an issue or pull request if you feel any information is incomplete or inaccurate.

Approved changes will also be published on the Mailchimp Developer site, although they will not appear there immediately.

---

## File: docs/creating-organizing-products.md

## The basics
Every Mailchimp Open Commerce store is built around products: the items in your store. Every product must have at least one variant—a descriptive aspect of the product, such as size, color, or model—and depending on how you organize your store, products can also have second-level variants, which are called options.

You can create products, variants, and options in the Open Commerce admin dashboard without making them live on your site, though for products to be added to your catalog, you first need to publish the products and then make them visible to customers. If you no longer want to offer a product for sale, you can hide it from customers or archive it to remove it from your store permanently. 

This documentation outlines the structure of products, how to set up new products from scratch, and how to manage existing products within your store.

### Products and variants
A product in Open Commerce represents a single type of item that you want to list in your store’s catalog; variants are properties of that item. Every product must have at least one variant, because some information is set at the product level and some is set at the variant level. Depending on what you are selling—and how you want to organize and manage your inventory—each product might have a single variant or multiple variants. 

For example, you could have a particular design of t-shirt (the product) that has multiple sizes (its variants). In that case, the design would appear as a single entry on catalog pages, and the variants would be visible on the product page. 

In another example, a bookseller could carry a particular book title in hardcover, paperback, and e-book editions. Different businesses might make different choices: you might want all three as variants of a single product, you might group the physical books in one product and the e-book in another, or you might set up all three as separate products. Pricing is set at the variant level, so all three book editions could be sold at different prices in any of these product/variant configurations.

### Options
Open Commerce provides yet another level of organizational flexibility with options. If you include options in a product, the customer must make a choice at each variant level. These choices uniquely identify an exact product configuration, called a terminal variant—and only terminal variants can be added to carts. If a variant does not contain options, then it is also a terminal variant.

Expanding our t-shirt example, a particular design (product) could be printed on two different colors of shirt (variants), each of which comes in three sizes (options). To order one of these shirts a customer has make two choices to specify their desired terminal variant. Ordering just a red shirt without specifying a size, or just a large shirt without specifying a color wouldn’t make any more sense in an Open Commerce store than it would in a bricks-and-mortar store!

## Create a product
To create a product, log in to the Open Commerce admin dashboard, click **Products** in the left sidebar, and then click **Create product**. You’ll add details about the product on various cards: 

- Enter the product information in the **Details** card.
- To add images, click **Drag image or click to upload** in the **Media Gallery** card. Once uploaded, you can use the Order fields to set the order that the images appear in.
- Customize the product’s social sharing messages under the **Social** card.
- Add tags by clicking on the **+** button in the **Tags** card. Select tags from the dropdown menu in the dialog, and then click **Add tags to products**. (See the [Tags and Navigation documentation](/open-commerce/docs/tags-navigation) for further information on creating new tags.)

Make sure to click **Save changes** in each card where you have entered new information.

Once you’re finished, make your product visible by clicking the **⋯** button next to its name and then clicking **Make Visible**. This creates the product, but it can’t be published yet—to be publishable, it will need to have at least one variant.

## Configure a variant
Variants allow you to create different versions of the same base product. Because it’s mandatory to have at least one variant to publish a product, every product comes filled with one default, required variant. 

To create additional variants, click the **⋯** button next to the product's name, click **Create variant**, and fill out the following fields:

| Field | Description |
| ----- | ----------- |
| Attribute label | Required. The attribute label describes the category of variant, for example, “Color” or “Size”. |
| Short title | The short title describes this particular variant within the category set by the attribute label. For example, a variant with the attribute label “Color” might have the short title “Red”. This is usually shown in a drop-down list or button on the product detail page in your shop. |
| Title | The full title is usually shown on carts, checkout pages, order summary pages, and invoices. It should fully describe the configured variant. For example, if this is an option with the short title “Large”, its parent variant has a short title “Red”, and the product title is “Fancy T-Shirt”, then a good title would be “Fancy T-Shirt – Red – Large”. |
| Origin country | Optional. |
| Width, Length, Height, Weight | Optional. Units for these measurements can be changed under **Settings > Shop Localization**. |
| Price | Required. Enter a number without currency symbols. |
| Compare at price | Optional. Use this to display a price other than what you are currently selling a product for, such as a suggested retail price or the regular price of an item that is on sale. |
| Manage inventory | Optional. If you’d like to track the item in your inventory, check this box. <li>**Quantity** – Optional. Enter your current inventory quantity.</li><li>**Warn at** – Optional. When the item’s quantity is lower than this number, “Limited Supply” notifications will be displayed on product pages.</li><li>**Allow backorder** – Optional. When enabled, customers can order the product even if the inventory quantity is 0.</li>|
| Taxable | Optional. Check this box to automatically add tax to this item when purchased. When checked, you can optionally add a variant-specific tax code or leave the Tax Code field blank to use the code set in **Settings > Taxes > Default tax code for products**. |

Once you’ve added the variant’s information, make the variant visible by clicking the **⋯** button next to the variant’s name and then clicking “Make Visible.” As a reminder, you can’t publish a new product unless the product itself and all of its variants are made visible.

## Add options to a variant
Options provide a second layer of customization within each variant. For instance, in addition to carrying shirts in multiple colors, you may also want to carry multiple size options for each color. 

To create an option:

1. Hover over the variant you’d like to create an option for. Click the **⋯** button to the right of its name and then click **Create variant**.

2. Add the option’s information as you would when [creating a top-level variant](#configure-a-variant). 

3. To make the option visible, hover over the variant name, click the **⋯** button, and then click **Make Visible**. 


## Publishing, hiding, and showing
Configuring products and variants do not make them immediately visible to customers. Products can be marked as visible or hidden, which determines whether customers can see them in your shop. When you click **Publish**, all your saved changes will become visible to customers. 

Products published to the catalog cannot be unpublished, though they may be set as hidden, so customers can’t see them in the storefront. To change change the visibility of a product, variant, or option, click the **⋯** button next to the product title in the left sidebar and select **Make Hidden** or **Make Visible**.

Visibility settings are available for the top-level product, each variant, and each option. Hiding the product will also hide the product’s variants and options from the storefront. Hiding a variant will hide that variant and all of its options, while hiding an individual option will affect only that option.

> **Note**: In some cases, logged-in administrators may be able to see hidden products on public store pages—even though they are hidden from customers.

## Duplicating products
Duplicating a product, variant, or option will create a new copy of that item, including its children. This can be a useful tool for speeding up creation of similar products, variants, or options.

To duplicate a product, variant, or option, click the **⋯** button next to the product title and select **Duplicate**. Click the **Publish** button at the top right of the window to make your changes appear on the shop.

## Archiving products
You can archive a product to remove it from both the storefront and the admin dashboard. Archiving a product is immediate upon confirmation and is not reversible from the admin dashboard. If you think you may want to use the product again in the future, it may be better to make the product hidden instead. 

To remove a product, variant, or option from your shop, click on the **⋯** button next to the product, variant, or option title, and select **Archive**.

## Bulk actions and filtering
You can perform bulk actions on multiple products at once, either by selecting the products manually by clicking the checkbox next to them, or using the filtering feature. To filter by name, type in the field above the product list. 

You can also filter by product ID:

1. Create a CSV file with a comma-separated list of product IDs you’d like to filter. 
2. Click the **Actions** menu and choose **Filter by file**.
3. Click **Import** and choose the CSV file you’d like to use.
4. Click **Filter products**.

Once you’ve filtered the products, you can use the **Actions** menu to perform bulk actions on them, including adding or removing tags, publishing, hiding or showing, duplicating, or archiving them.

---

## File: docs/environment-variables.md

## The basics

The system is made up of various services and web servers, each of which is distributed as a Docker image, or can be customized and then built into your own Docker image. Some environment variables are required because they tell a container service how to connect to another service or a database, others are optional with sensible defaults, but allow you to customize the system to meet your needs.

This article aims to be a full listing of the required and optional environment variables used by all components of the Mailchimp Open Commerce system.

## How to modify

You can set variables in the `.env` file within each plugin/project, which is used by Docker Compose when starting the system and loading plugins. Each Open Commerce repo contains a `.env.example` file that lists the environment variables and default values for that project. You can run the bin/setup script within the project to automatically copy the contents of `.env.example` to `.env` (although it will not overwrite existing values). Your `.env` files will contain personalized and potentially sensitive information, so you should never commit them to a public GitHub repo.

In some cases, projects may look for additional environment variables that are not listed in `.env.example` because they are optional and have default values set in the code. You can find these optional variables by searching for `cleanEnv` within a Node project.

**Note** For the quick start guide, the default values should be good enough.

Below is the list of all environment variable categorized by the services

### API

| Variable Name                                    | Description                                                                                                                                                                          | Default                            |
| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------- |
| `GRAPHQL_INTROSPECTION_ENABLED`                  | Allow introspection of the GraphQL API.                                                                                                                                              | `true`                             |
| `GRAPHQL_PLAYGROUND_ENABLED`                     | Serve the GraphQL Playground UI from `/graphql`.                                                                                                                                     | `true`                             |
| `MONGO_URL`                                      | The MongoDB connection string URL, including the name of the database you want to use.                                                                                               | mongodb://localhost:27017/reaction |
| `MONGO_USE_UNIFIED_TOPOLOGY`                     | MongoDB’s [`useUnifiedTopology` option](https://mongodb.github.io/node-mongodb-native/3.3/reference/unified-topology/)                                                               | `true`                             |
| `PORT`                                           | Port to serve the API on.                                                                                                                                                            | `3000`                             |
| `REACTION_LOG_LEVEL`                             | The log level, which controls how much is printed in the logs. [See available options](https://github.com/reactioncommerce/logger#log-levels)                                        | `DEBUG`                            |
| `REACTION_APOLLO_FEDERATION_ENABLED`             | Enables [Apollo Federation](https://www.apollographql.com/docs/federation/) support                                                                                                  | `false`                            |
| `REACTION_ADMIN_PUBLIC_ACCOUNT_REGISTRATION_URL` | The registration URL to be used in new account invitation emails                                                                                                                     | `http://localhost:4080`.           |
| `REACTION_GRAPHQL_SUBSCRIPTIONS_ENABLED`         | Enable [GraphQL subscriptions](https://www.apollographql.com/docs/apollo-server/data/subscriptions/) over WebSockets. Docs:                                                          | `true`                             |
| `REACTION_SHOULD_INIT_REPLICA_SET`               | Whether to auto-initialize a MongoDB replica set on startup if one isn’t found                                                                                                       | `true`                             |
| `REACTION_WORKERS_ENABLED`                       | Set to `false` to disable background job workers. Be careful because at least one instance must be working background jobs or features such as emailing and file uploads won't work. | `true`                             |
| `VERBOSE_JOBS`                                   | Set `true` to enable logs of jobs                                                                                                                                                    | `false`                            |
| `ROOT_URL`                                       | The root URL (including protocol) for the API                                                                                                                                        | http://localhost:3000              |
| `STRIPE_API_KEY`                                 | The Stripe secret key from your Stripe account dashboard. Required if you want Stripe payments to work.                                                                              | YOUR_PRIVATE_STRIPE_API_KEY        |
| `REACTION_ADMIN_PUBLIC_ACCOUNT_REGISTRATION_URL` | The URL for the admin login/signup page                                                                                                                                              | http://localhost:4080              |
| `MAIL_URL`                                       | An SMTP mail url, e.g. `smtp://user:pass@example.com:465`, that is used to send all transactional emails from the `email-smtp` plugin.                                               | No default                         |
| `EMAIL_DEBUG`                                    | Enable email plugin logging.                                                                                                                                                         | `false`                            |
| `REACTION_SHOULD_ENCODE_IDS`                     | Set to `false` to disable encoding of ids.                                                                                                                                           | `true`                             |
| `STORE_URL`                                      | The URL where storefront is available                                                                                                                                                | http://localhost:4000              |

### Example Storefront

| Variable Name           | Description                                                                                                  | Default             |
| ----------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------- |
| `CANONICAL_URL`         | The canonical root public URL for your storefront                                                            |                     |
| `BUILD_GRAPHQL_URL`     | Is used to create a build from outside of CI/CD                                                              |                     |
| `EXTERNAL_GRAPHQL_URL`  | The public GraphQL API URL. Used by Next.js on client.                                                       |                     |
| `INTERNAL_GRAPHQL_URL`  | Is used on the frontend server to reach GraphQL for the build process                                        |                     |
| `NODE_ENV`              | Standard Node environment designation                                                                        |                     |
| `PORT`                  | Port on which to run the storefront server in the container                                                  | 4000                |
| `SESSION_MAX_AGE_MS`    | The maximum age in milliseconds for the storefront session cookie, which is used for authentication          | 86400000 (24 hours) |
| `SESSION_SECRET`        | A unique key for [cookie session](https://www.npmjs.com/package/cookie-session#secret) verification          |                     |
| `STRIPE_PUBLIC_API_KEY` | This is the public/client key from your Stripe dashboard.                                                    |                     |
| `SITEMAP_MAX_AGE`       | The `max-age` value(in seconds) for the `Cache-Control` header included when serving the `sitemap.xml` file. | 43200 (12 hours)    |
| `IS_BUILDING_NEXTJS`    | If `true` uses `BUILD_GRAPHQL_URL` else uses `INTERNAL_GRAPHQL_URL` to serve the API                         | `false`             |

### Admin

| Variable Name                 | Description                                                                                                                                                                                                                                         | Default |
| ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| `PUBLIC_GRAPHQL_API_URL_HTTP` | A Reaction API URL that is accessible from browsers and accepts GraphQL POST requests over HTTP                                                                                                                                                     |         |
| `PUBLIC_GRAPHQL_API_URL_WS`   | A Reaction API URL that is accessible from browsers and accepts GraphQL WebSocket connections. Usually the same as `PUBLIC_GRAPHQL_API_URL_HTTP` but replace `http` with `ws`. If this is set, GraphQL subscriptions will be enabled on the client. | ""      |
| `PUBLIC_FILES_BASE_URL`       | A full public URL that has /assets/files and /assets/uploads endpoints for uploading and downloading files. Typically this is the API service root URL.                                                                                             |         |
| `PUBLIC_I18N_BASE_URL`        | Required. A full public URL that has /locales/namespaces.json and /locales/resources.json endpoints for loading translations. Typically this is the API service root URL.                                                                           |         |
| `PUBLIC_STOREFRONT_HOME_URL`  | Required. The URL for your storefront home page. This is only used as fallback if a URL isn’t set for the shop from the admin UI.                                                                                                                   |         |
| `ROOT_URL`                    | The canonical root public URL for the Admin server                                                                                                                                                                                                  |         |
| `PORT`                        | The port on which to run the Node server in the container. Recommend `4080`.                                                                                                                                                                        |         |
| `MONGO_ALLOW_DISK_USE`        | Allows MongoDB to use [temporary files on disk](https://docs.mongodb.com/manual/reference/method/cursor.allowDiskUse/)                                                                                                                              | `false` |

---

## File: docs/fulfilling-orders.md

## The basics

The central function of an Open Commerce store is to handle orders. The first part of this process is done by the shopper: adding items to a shopping cart and checking out. The second part is done by a store manager: accepting payment and fulfilling items by delivering them to the shopper. 

When a shopper chooses an item for purchase, a cart is created; as they continue shopping and eventually check out, Open Commerce collects information from the shopper and saves it to the cart. At the end of the checkout process, an order is created and the cart is deleted. 

An order consists of all the items that a shopper has chosen to purchase; the methods for fulfilling the items and completing the associated payments; and information on returns, taxes, and invoicing. Orders are always organized into one or more fulfillment groups, each of which contains items that can be delivered in the same way by a single shop. The delivery choices that shoppers make at checkout determine what items go into which fulfillment group. 

This documentation outlines what information is tracked in fulfillment groups, how to process orders, and how to accept and refund payments.

## Fulfillment

Every product variant has a list of available fulfillment types. The built-in fulfillment types are “shipping,” “pickup,” and “digital,” but you can customize them or create additional types with plugins. If a variant can be fulfilled in more than one way, then the shopper can choose among the available fulfillment types during checkout, and that choice determines what other fields appear on the checkout form—for example, a shipping address form for items with the “shipping” type, or a pickup time and location form for items with the “pickup” type.

### Fulfillment methods

Each fulfillment type has at least one fulfillment method, which describes how to deliver an item in greater detail. For example, you might have two methods for the “shipping” type, “Free Shipping” and “Flat Rate USPS,” which a shopper can choose between at checkout. Some fulfillment types may only have a single fulfillment method, in which case the shopper would see the method already selected during checkout. 

### Shipping restrictions

Shipping options can be restricted based on nearly any aspect of a cart, including item attribute (e.g., products with the tag “Hazardous” are for pickup only), destination (e.g., no international shipping), a price limit (e.g., shipping only for orders above $100), or a combination of these (e.g., no live plants can be sent to California). By default, shipping methods can be allowed or restricted based on the following item data: 

* `attributes`
* `weight`, `height`, `width`, `length`
* `price`
* `productVendor`
* `quantity`
* `subtotal`
* `tags`
* `title`, `variantTitle`
* `postal` (ZIP or postal code), `region`, `country`

Based on these criteria, the restriction can be set to either `allow` or `deny`. Shipping restrictions can be applied universally to all shipping methods or only to select methods.
### Fulfillment quotes

A fulfillment quote represents the current price of a single fulfillment method for a specific cart. The price may be a fixed amount or may be calculated based on the “from” and “to” addresses, the parcel dimensions, discounts, or other factors. As the items in a fulfillment group change, the fulfillment quotes for that group are updated and can also change.

> **Note**: The total price of a fulfillment group may be comprised of multiple prices, such as item prices and shipping fees. These prices can be separated for tax or accounting purposes, but may or may not be shown separately to the shopper.
    
## Processing an order

To view and process orders, go to the **Orders** page, which shows a sortable table of all the orders your shop has received or that you’ve imported from an external system. Clicking on an order will show you its details, including the order summary, fulfillment groups, payments, and refunds. 

From here, you can perform various actions on a single fulfillment group:
 
 - **Update group status** lets you change a fulfillment group’s status to any other available status. 
- **Tracking number** lets you add an optional tracking number to a fulfillment group at any time. Once a tracking number has been added, click on the number to edit it.
- **Cancel group** will cancel the fulfillment group.

Clicking **Cancel order** at the top right of the screen will cancel all fulfillment groups in the order, which in turn cancels the order itself. When you cancel an entire order, the full amount is refunded (for every payment method that supports refunds). 

## Payments

Orders have one or more payment methods, which represent a type of payment or the API used to process a payment. Open Commerce does not have any built-in payment methods—all payment methods have to be added via plugins. We include an example plugin that supports processing payments using Stripe.

Each payment method specifies what kinds of payment input it requires. For example, the `stripe_card` payment method requires a billing address and a Stripe card token as input. 

During checkout, the shopper chooses a payment method (if more than one is available) and enters any required information. Some payment methods allow the shopper to pay a portion of the amount due; the shopper can then add additional payments until they have covered the full amount.

Each payment must be authorized by the payment method handler in order to create an order. The payment plugin will then create one or more payments (also referred to as “charges”) for the order. After that, a shop operator can individually accept, refund, or partially refund each payment.

### Capturing payments

The process of accepting authorized payments is called “capturing” the payment. The practical effect of capturing a payment differs based on the payment method—it may mean charging a credit card, sending an invoice, or nothing at all (like if a shopper “purchases” a free item).

To capture payments, navigate to the **Payments** page. You can capture all payments at once by clicking **Capture all payments**, or individually by clicking **Capture payment** next to each payment line. Payment providers can also flag payments as risky—you will see a notification in the payments information area. 

### Refunds

Payment methods may or may not support refunding.  If the payment method allows refunds, you will be able to refund any amount of an already-captured payment up to the full amount charged. 

To refund a payment, go to the **Refunds** tab on the order details page and enter the amount you’d like to refund. If you’re refunding more than one payment, you’ll see the total amount reflected in the **Refund** button. You can optionally enter a reason for giving the refund for your own records.

If a payment has been fully refunded, that will be reflected on the order page; if a payment has been partially refunded, you will see the refunded amount and the amount that is still available to refund under each payment. All previous refunds are listed in a card at the bottom of the order details page.

---

## File: docs/fundamentals.md

## The basics

Mailchimp Open Commerce is an API-first, modular commerce stack. The API implements all features in GraphQL and is fully [extensible via plugins](/open-commerce/guides/build-api-plugin/).

Open Commerce is open-source-licensed, freely available software. You can explore the platform’s [repos on GitHub](https://github.com/reactioncommerce), or follow the [Quick Start guide](/open-commerce/guides/quick-start/) to install the development platform on your computer. If you’re interested in contributing to Open Commerce, be sure to read and follow the [code of conduct](https://github.com/reactioncommerce/reaction-docs/blob/trunk/public-docs/code-of-conduct.md/) and check out the [community resources](#community).

Like the code, the documentation is also [open source](https://github.com/reactioncommerce/docs), and previous versions of the Reaction Commerce docs are [archived on GitHub](https://github.com/reactioncommerce/reaction-docs).

### Using Open Commerce

Whether you’re a business owner running a shop or a developer setting up a custom implementation, it’s easy to get a shop up and running with the Open Commerce development platform and build from there.

Every shop is built around a catalog of products. You can [set up your products](/open-commerce/docs/creating-organizing-products/) in the admin dashboard and then [organize them with tags](/open-commerce/docs/tags-navigation/) to help shoppers navigate your catalog. Once shoppers have found what they want and make a purchase, you’ll [fulfill their orders](/open-commerce/docs/fulfilling-orders/) by accepting payments and delivering their items.

Developers can customize any part of the administrator or shopper experience by [writing plugins](/open-commerce/guides/build-api-plugin/). Because Open Commerce is fully modular, you’ll often want to [share code between plugins](/open-commerce/docs/sharing-code-between-plugins/) so features are available across the entire platform. If you’ve created a new plugin or modified an existing one and you want to share it with other developers, you can contribute to the [Open Commerce community](#community). When committing to existing Open Commerce repos, you should follow the [testing requirements](/open-commerce/docs/testing-requirements/)

### Open Commerce vs Reaction Commerce

Open Commerce was formerly known as Reaction Commerce. You may see references to Reaction throughout the guides and docs, especially in code—all Open Commerce repos are under the [`reactioncommerce`](https://github.com/reactioncommerce) GitHub organization.

As we continue the renaming process, we’ll announce any future breaking changes in the [release notes](/release-notes/?filter=open-commerce).

## Development platform

The Open Commerce development platform is the simplest way to set up the API and its supporting services on your local machine. The development platform is containerized and uses public Docker images by default, though you can also choose to run the platform in development mode, which uses local images that can be updated on the fly as you make edits to a running plugin.

The development platform has several `make` scripts that automatically pull the latest version of Open Commerce and start all of the required services using Docker Compose. The development platform runs on `localhost`; see the [Quick Start guide](/open-commerce/guides/quick-start/#clone-and-start-the-platform) for a list of ports where you can access its various services. For details on specific `make` commands for controlling and customizing the development platform, see the [development platform project documentation](https://github.com/reactioncommerce/reaction-development-platform#project-commands) on GitHub.

## Plugins

All of Open Commerce’s features, including the [API core](https://github.com/reactioncommerce/api-core), are provided by first-party plugins—the [main project](https://github.com/reactioncommerce/reaction) is a shell that that imports all of the required NPM packages. This structure gives you complete control over what plugins and features are active in your installation. For more information on customizing these plugins or creating your own, check out the [Build an API Plugin guide](/open-commerce/guides/build-api-plugin/).

A wide range of first-party plugins covers all the basic platform features, including handling backend services, shop configuration, and the shopper experience. You can view a list of the plugins active in your instance in the admin dashboard under **Settings > System Information**. The plugins included with the development platform are:

| Plugin                                                                                                         | Description                                                                                                               |
| -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| [`api-plugin-accounts`](https://github.com/reactioncommerce/api-plugin-accounts)                               | Manages internal accounts for shop operators and shoppers.                                                                |
| [`api-plugin-address-validation`](https://github.com/reactioncommerce/api-plugin-address-validation)           | Validates shopper addresses during checkout.                                                                              |
| [`api-plugin-address-validation-test`](https://github.com/reactioncommerce/api-plugin-address-validation-test) | A template for creating a new address validation plugin.                                                                  |
| [`api-plugin-authentication`](https://github.com/reactioncommerce/api-plugin-authentication)                   | Connects the Identity service to the Open Commerce API.                                                                   |
| [`api-plugin-authorization-simple`](https://github.com/reactioncommerce/api-plugin-authorization-simple)       | Checks account permissions to allow or deny actions.                                                                      |
| [`api-plugin-carts`](https://github.com/reactioncommerce/api-plugin-carts)                                     | Handles shopping cart objects.                                                                                            |
| [`api-plugin-catalogs`](https://github.com/reactioncommerce/api-plugin-catalogs)                               | Handles product catalogs.                                                                                                 |
| [`api-plugin-discounts`](https://github.com/reactioncommerce/api-plugin-discounts)                             | Handles discounting products.                                                                                             |
| [`api-plugin-discounts-codes`](https://github.com/reactioncommerce/api-plugin-discounts-codes)                 | Enables discount codes that shoppers can enter at checkout.                                                               |
| [`api-plugin-email`](https://github.com/reactioncommerce/api-plugin-email)                                     | Sends transactional emails.                                                                                               |
| [`api-plugin-email-smtp`](https://github.com/reactioncommerce/api-plugin-email-smtp)                           | Manages the SMTP connection for outgoing transactional emails.                                                            |
| [`api-plugin-email-templates`](https://github.com/reactioncommerce/api-plugin-email-templates)                 | Provides transactional email templates that shop operators can customize.                                                 |
| [`api-plugin-files`](https://github.com/reactioncommerce/api-plugin-files)                                     | Manages file uploads for a shop.                                                                                          |
| [`api-plugin-i18n`](https://github.com/reactioncommerce/api-plugin-i18n)                                       | Handles internationalization options for a shop.                                                                          |
| [`api-plugin-inventory`](https://github.com/reactioncommerce/api-plugin-inventory)                             | Tracks inventory counts for product variants.                                                                             |
| [`api-plugin-inventory-simple`](https://github.com/reactioncommerce/api-plugin-inventory-simple)               | Determines product availability status based on inventory counts.                                                         |
| [`api-plugin-job-queue`](https://github.com/reactioncommerce/api-plugin-job-queue)                             | Manages job queuing and execution.                                                                                        |
| [`api-plugin-navigation`](https://github.com/reactioncommerce/api-plugin-navigation)                           | Allows for the creation of hierarchical navigation in a shop.                                                             |
| [`api-plugin-notifications`](https://github.com/reactioncommerce/api-plugin-notifications)                     | Sends messages about order statuses.                                                                                      |
| [`api-plugin-orders`](https://github.com/reactioncommerce/api-plugin-orders)                                   | Handles orders, including fulfillment groups.                                                                             |
| [`api-plugin-payments`](https://github.com/reactioncommerce/api-plugin-payments)                               | Manages all payment methods for a shop.                                                                                   |
| [`api-plugin-payments-example`](https://github.com/reactioncommerce/api-plugin-payments-example)               | A template for creating a new payment handler.                                                                            |
| [`api-plugin-payments-stripe`](https://github.com/reactioncommerce/api-plugin-payments-stripe)                 | Processes payments with Stripe.                                                                                           |
| [`api-plugin-pricing-simple`](https://github.com/reactioncommerce/api-plugin-pricing-simple)                   | Enables setting prices on product variants.                                                                               |
| [`api-plugin-products`](https://github.com/reactioncommerce/api-plugin-products)                               | Creates products and manages their information.                                                                           |
| [`api-plugin-settings`](https://github.com/reactioncommerce/api-plugin-settings)                               | Manages shop settings like those available in the admin dashboard.                                                        |
| [`api-plugin-shipments`](https://github.com/reactioncommerce/api-plugin-shipments)                             | Handles shipment types, including allowing or denying shipment methods for products or orders that meet certain criteria. |
| [`api-plugin-shipments-flat-rate`](https://github.com/reactioncommerce/api-plugin-shipments-flat-rate)         | Handles shipment types that always cost the same amount.                                                                  |
| [`api-plugin-shops`](https://github.com/reactioncommerce/api-plugin-shops)                                     | Allows creation and management of shops within an Open Commerce instance.                                                 |
| [`api-plugin-simple-schema`](https://github.com/reactioncommerce/api-plugin-simple-schema)                     | Implements [SimpleSchema](https://github.com/longshotlabs/simpl-schema) to validate data for MongoDB documents.           |
| [`api-plugin-sitemap-generator`](https://github.com/reactioncommerce/api-plugin-sitemap-generator)             | Creates sitemap files for search engines at a specified time interval.                                                    |
| [`api-plugin-surcharges`](https://github.com/reactioncommerce/api-plugin-surcharges)                           | Allows applying surcharges to a cart.                                                                                     |
| [`api-plugin-system-information`](https://github.com/reactioncommerce/api-plugin-system-information)           | Provides information about the running instance of Open Commerce, including registered plugins.                           |
| [`api-plugin-tags`](https://github.com/reactioncommerce/api-plugin-tags)                                       | Handles tag creation and organization.                                                                                    |
| [`api-plugin-taxes`](https://github.com/reactioncommerce/api-plugin-taxes)                                     | Manages tax rates based on location, product type, and more.                                                              |
| [`api-plugin-taxes-flat-rate`](https://github.com/reactioncommerce/api-plugin-taxes-flat-rate)                 | Allows for setting a single tax rate.                                                                                     |
| [`api-plugin-translations`](https://github.com/reactioncommerce/api-plugin-translations)                       | Supplies translations of terms used in the API in over 20 languages.                                                      |

## Environment variables

Much of the platform-wide configuration of Open Commerce is set via environment variables—either to pass information from one service to another or simply to customize the platform to meet your needs.

You can set variables in the `.env` file within each plugin, which is used by Docker Compose when starting the system and loading plugins. Each Open Commerce repo contains a `.env.example` file that lists the environment variables and default values for that project. You can run the `bin/setup` script within the project to automatically copy the contents of `.env.example` to `.env` (although it will not overwrite existing values). Your `.env` files will contain personalized and potentially sensitive information, so you should never commit them to a public GitHub repo.

> **Note**: When running the development platform, its `make` scripts automatically set up the required `.env` files for you.

In some cases, projects may look for additional environment variables that are not listed in `.env.example` because they are optional and have default values set in the code. You can find these optional variables by searching for `cleanEnv` within a Node project.

## Community

Open Commerce has a global team of contributors to its [projects on GitHub](https://github.com/reactioncommerce). New contributors are always welcome, and they should read and abide by the [code of conduct](https://github.com/reactioncommerce/reaction-docs/blob/trunk/public-docs/code-of-conduct.md/).

If you have questions about implementing Open Commerce, customizing code for your own purposes, or contributing to a project, check out the [developer forum](https://forums.reactioncommerce.com) and [Discord server](https://discord.gg/Bwm63tBcQY). For bug reports and feature requests, you can create a new issue directly in the appropriate repo.

For more information on getting your code ready to commit, check out the [ESLint configuration](https://github.com/reactioncommerce/reaction-eslint-config) project and the [Testing Requirements](/open-commerce/docs/testing-requirements/) doc.

---

## File: docs/sending-emails.md

## The basics

Open Commerce lets you send transactional emails for a variety of reasons, such as new user signups, password resets, and shipping notifications. The development platform comes preloaded with templates that specify the subject and body content of these emails.

In this documentation, we’ll cover configuring your email server and the types of templates that are available.

>**Note**: If you need to connect to a service that sends transactional messages at scale, check out [Mailchimp Transactional](https://mailchimp.com/developer/transactional/docs/fundamentals/). 

## Email configuration and logs

To send transactional emails from Open Commerce, you need to configure an SMTP email provider. Open Commerce will send emails from the address that you set in the `MAIL_URL` [environment variable](/open-commerce/docs/fundamentals/#environment-variables):

`MAIL_URL="smtp://username:password@example.com:465"`

You can use any SMTP server, including a self-hosted server.

All emails sent by Open Commerce are logged: to view them, go to **Settings > Email** in the admin dashboard. Emails are scheduled with a job queue, so failed emails will automatically attempt to resend up to five times, with three minutes between each retry. You’ll also see a button next to failed emails that will allow you to manually attempt a resend. 

## Editing email templates

A range of email templates are preloaded into the `Templates` collection when Open Commerce starts up.

Accounts:
- Invite Shop Member – New User Account
- Reset Password
- Verify Account (via LaunchDock)
- Verify Updated Email Address
- Welcome Email

Orders:
- New Order Placed
- Order Item Refunded
- Order Refunded
- Order Shipped

To customize a template, go to **Settings > Email Templates** in the admin dashboard. There are several fields that define each template: 

| Field | Description |
|-------|-------------|
|Title|A user-friendly name for the type of email.|
|Subject|The email subject line. [Handlebars expressions](https://handlebarsjs.com/guide/) are allowed, as long as they are passed to the server-side rendering function.|
|HTML|The body of the email. [Handlebars expressions](https://handlebarsjs.com/guide/) are allowed, as long as they are passed to the server-side rendering function.|
|Name|Not editable. The name of the function that is used to trigger the email.|
|Language|Not editable. The language that is set for the shop under **Settings > Shop Localization**. The language can’t be changed on a per-template basis.|

---

## File: docs/sharing-code-between-plugins.md

## The basics

Most of Open Commerce’s features are implemented via plugins, so it’s common for one plugin to use functions provided by another. There are three ways for plugins to share functions:

* [Plugin handlers](#plugin-handlers) are the most flexible way to share code or any other information. They can provide dependency and sorting information, track which plugin registered a function, or include additional metadata.
* [Functions by type](#functions-by-type) allows sharing multiple related functions. This approach does not provide the additional information and tracking that plugin handlers can.
* [Queries and mutations](#queries-and-mutations) can register a single function with a particular name.

You will find all three approaches throughout the Open Commerce codebase, but due to their flexibility, plugin handlers are the recommended way to write new code.

In this documentation, we’ll describe all three approaches with example code. For more information on creating plugins, see the [Build an API Plugin](/open-commerce/guides/build-api-plugin/) guide.

## Plugin handlers

When you start up Open Commerce, `registerPluginHandler` is called multiple times—once for every plugin that supplies it. The handler is passed `registerPlugin` options from every plugin, providing information about the presence of other plugins and their configurations. 

The handler is also expected to examine the options passed in from each plugin and save any required data. Any plugin can extend the `registerPlugin` options by documenting data it expects to receive from each subsequent one by using `registerPluginHandler`. 

For example, a tax service might expect a `pluginTaxService`, and ask any custom tax widget to provide `pluginTaxService` as part of its registration: 

```js
export function registerPluginHandlerForTaxes({ name: pluginName, taxServices: pluginTaxServices }) {
  if (Array.isArray(pluginTaxServices)) {
    for (const pluginTaxService of pluginTaxServices) {
      taxServices[pluginTaxService.name] = { ...pluginTaxService, pluginName };
    }
  }
}
```

> **Note**: A `registerPluginHandler` function may not return a Promise. If you need to  process the data asynchronously, save it temporarily. Then create and register a `startup`-type function that imports the temporary data, does the async tasks, and returns a Promise. Startup functions are called immediately after `registerPluginHandler` functions in the Open Commerce startup process.

To use a handler in your plugin, include a file named `registration.js` that defines and exports not only `registerPluginHandler` but also any related data required by other files in the plugin. For example, the `reaction-catalog` plugin's `registration.js` file looks like this:

```js title=registration.js
export const customPublishedProductFields = [];
export const customPublishedProductVariantFields = [];

/**
 * @summary Will be called for every plugin
 * @param {Object} options The options object that the plugin passed to registerPlugin
 * @returns {undefined}
 */
export function registerPluginHandler({ catalog }) {
  if (catalog) {
    const { publishedProductFields, publishedProductVariantFields } = catalog;
    if (Array.isArray(publishedProductFields)) {
      customPublishedProductFields.push(...publishedProductFields);
    }
    if (Array.isArray(publishedProductVariantFields)) {
      customPublishedProductVariantFields.push(...publishedProductVariantFields);
    }
  }
}
```

The `registerPluginHandler` function looks for the `catalog` key and pushes its data into exported arrays, so any files in `reaction-catalog` that import `customPublishedProductFields` or `customPublishedProductVariantFields` will have the full list of fields as defined by all registered plugins.



To view all fields registered by a particular plugin, look for the `name` key of the object. You can then access and use any data fields that you need.

The `registerPluginHandler` function itself is registered using `functionsByType`:

```js
import { registerPluginHandler } from "./registration";

await app.registerPlugin({
  label: "Catalog",
  name: "reaction-catalog",
  functionsByType: {
    registerPluginHandler: [registerPluginHandler]
  }
});
```


## Functions by type

To provide a function to another plugin for a specific purpose, pass it to `registerPlugin` in the `functionsByType` list. The plugin that calls the functions must document the arguments it will provide and the expected return value and/or side effects. 

For example, this plugin registers functions of type “funky”:

```js
import funkyFn from "./funkyFn";

await app.registerPlugin({
  label: "Cart",
  name: "reaction-cart",
  functionsByType: {
    funky: [funkyFn]
  }
});
```

Another plugin can then loop through and call all “funky” functions that were registered by this plugin (or any other):

```js
for (const funkyFn of context.getFunctionsOfType("funky")) {
  // eslint-disable-next-line no-await-in-loop
  await funkyFn(...args);
}
```

## Queries and mutations

If you need to register just one function, you can use `context.queries` or `context.mutations` instead of `functionsByType`. When you use this approach, the plugin only expects to find a function with a particular name. For example, for a function named `expireCarts` on `context.mutations`, call `context.mutations.expireCarts`. 

>**Note**: We don’t recommend adding functions this way, because it’s the most limited approach. Additionally, if more than one plugin registers a query or mutation function, the one listed last in `plugins.json` will “win” and override prior registrations, and no error or warning will be thrown.

Just as with `functionsByType`, the plugin that calls the function must document the arguments it will provide and the expected return value and/or side effects. You should also add your query or mutation function to your GraphQL schema or create a GraphQL resolver for it, unless you intend it to be called only by _other_ plugins.

---

## File: docs/tags-navigation.md

## The basics

Open Commerce gives you a flexible way to organize your products with tags. Tags are shopper-facing and applied at the product level, and multiple tags can be assigned to any product. (Variants cannot have their own tags, so you should consider that when [organizing your products and variants](/open-commerce/docs/creating-organizing-products/).) 

Tags are also the basis for creating navigation items, which appear when browsing the shop. Each navigation item is linked to a particular tag, and navigation items can be nested within one another to create a hierarchy. The navigation hierarchy is also reflected in the breadcrumbs shown on each product detail page.

In this documentation, we’ll cover how to create tags, organize them, and use them to build hierarchical navigation. 

## Creating and assigning tags

To create a tag, log in to the Open Commerce admin dashboard, click **Tags** in the left sidebar, and click **Create a tag**. Enter the tag name, display title, and slug. If you want the tag to appear immediately, check **Tag is enabled in storefront**.

Tag names and slugs must be unique within a shop, which is important to remember when creating tags designed for a multi-level hierarchy. The process for creating a tag is the same regardless of what level of the navigation hierarchy it will occupy, so when creating child tags, it’s a good idea to include the parent tag in the name and slug to prevent conflicts. 

For example, a store selling language textbooks could have tags with the slugs `french`, `french-beginner`, and `french-advanced`, as well as `mandarin`, `mandarin-beginner`, and `mandarin-advanced`, but it could not have different tags with the slug `beginner` for each language. Despite this limitation, different tags _can_ have the same shopper-facing display title—`french-beginner` and `mandarin-beginner` could both have the display title “Beginner”.

After creating tags, you can use them to indicate what categories a product belongs to. If you’ve created a hierarchical tag system and you want a product to appear at all levels of the hierarchy, you will need to assign it multiple tags, such as tagging a beginner French textbook with both `french-beginner` and `french`. For more information on assigning tags to products, see [Creating and Organizing Products](/open-commerce/docs/creating-organizing-products/).

## Creating navigation items

Tags themselves are not organized hierarchically. To create a hierarchical organization and have it display in your shop, you must create navigation items, each of which corresponds to a single tag. 

To create a navigation item, click **Navigation** in the left sidebar and then click **Add navigation item**. In the dialog, fill out the following fields:

| Field | Description |
|-------|-------------|
|Display Name|The name that will appear in the shop navigation.|
|URL|Enter `/tag/YOUR_TAG_SLUG`. The leading slash is required.|
|This URL is relative|Check this box.|
	
Once you have created your navigation items, drag and drop them from the left-hand list of all items into the right-hand list of active items. You can organize the active navigation items as you want by dragging an item vertically to change its position in the list or horizontally to change its level of nesting.



When added to the hierarchy, navigation items are hidden by default. To show a navigation item, click the pencil icon next to its name in the the right-hand list and check **Show in storefront**.

---

## File: docs/testing-requirements.md

## The basics

When changing code in an Open Commerce repository, you should test your code before submitting a pull request for review. Open Commerce testing breaks down into three broad categories:

- Unit tests
- Integration tests
- Acceptance tests

All unit and integration tests should use the [Jest framework](https://jestjs.io). When submitting a pull request, [CircleCI](https://circleci.com) automatically runs unit and integration tests and will not allow merging until they all pass. Acceptance tests verify app functionality from a user’s perspective and can be done with a script or manually.

In this documentation, we’ll cover how to create and run unit and integration tests so your code can be accepted into the [Open Commerce codebase](https://github.com/reactioncommerce).

### Unit vs integration tests

Because many Open Commerce services integrate with others, there’s no sharp divide between unit and integration tests. Think of the terms “unit” and “integration” as two ends of a spectrum, where the “unit” end mocks everything and tests in complete isolation, while the “integration” end mocks nothing and is essentially like running the Open Commerce app itself.

In practice, the primary distinction is: 

- Unit tests have no mock database and are written in files throughout the codebase, with names similar to the name of the file they test.
- Integration tests are all written in the `/tests` folder and can make use of a fake, in-memory version of MongoDB.

For example, say you’ve written GraphQL resolvers for `shop` and `tags`, and you want the two resolvers to work together to produce a `shop.tags` field. You’ll need to write unit tests for each of them to determine whether they’re returning the correct information—the shop or tags list, respectively. But each individual unit test will ignore or mock the output of the other, so you’ll need an integration test to ensure that the output of `shop.tags` is correct.


## Writing Jest unit tests

Every component and utility function in Open Commerce must have a corresponding file containing unit tests written using the Jest testing framework. If you aren’t familiar with Jest, you should check out [the Jest documentation](https://jestjs.io/docs/getting-started) before writing your tests.

Some older files may not have existing unit tests. In order to update them, you must create a unit test file and add all necessary tests to achieve full coverage of that file—including testing all existing code as well as your new changes. It’s often easiest to begin your development by writing all of the missing tests before changing any code.

### Filenames and code style

Jest unit test files must be named after the component or utility function that they are testing and end in `.test.js`. The test file can be anywhere in the Open Commerce codebase, but ideally it should be in the same folder and have the same base filename as the code being tested. 

When writing your test:

- Do not add extra `describe` blocks. Jest tests are automatically file-scoped, so a file that performs a single test does not need a `describe` block in it. You may add multiple `describe` blocks to group related tests within one file, but you should not have a file with only a single `describe` block in it.
- Always use `test()` instead of `it()` to define test functions.
- Do not import `describe`, `test`, `jest`, `jasmine`, or `expect`. They are automatic globals in all test files.
- Use arrow functions for all `describe` and `test` functions.
- Use Jest’s [built-in `expect` function](https://jestjs.io/docs/en/expect.html) for assertions.

You might need to [test asynchronous code](https://jestjs.io/docs/en/asynchronous.html): functions that either return a Promise or take a callback argument. You should use Promises unless you need to use a callback, such as when the API of another package requires it. When using callbacks, make sure to add a `done` argument to your test function and call `done` when all testing is complete.

### Mocking data

To write unit tests for modules that depend on other modules, you need to supply mock data. 

There are several ways to mock data in Open Commerce:

- Use [Jest’s mocking capabilities](https://jestjs.io/docs/en/mock-functions.html). 
- Use the [rewire-exports](https://www.npmjs.com/package/babel-plugin-rewire-exports) Babel plugin, which can temporarily replace anything that another file exports. This is useful for mocking functions that are imported by the function that you’re testing.
- Use the built-in [`Factory` test utility](https://github.com/reactioncommerce/data-factory), which uses `@reactioncommerce/data-factory` to attach all core schemas to the `Factory` object. You need to add the schema to the `Factory` prior to use, or it will evaluate to an empty object.

> **Note**: `Factory` should primarily be used for backend-specific testing, such as integration testing at the API server level or unit testing at the plugin level.

To set up the initial schema for `Factory`, use `SimpleSchema` and `createFactoryForSchema`, like this:

``` js
import SimpleSchema from 'simpl-schema';
import { createFactoryForSchema } from "@reactioncommerce/data-factory";

const Example = new SimpleSchema({
  strProp: String,
  boolProp: Boolean,
});
createFactoryForSchema("Example", Example);
```

To mock a single object, use the `makeOne()` method with the name of the collection, such as:

``` js
import { Factory } from "/imports/test-utils/helpers/factory";
const mockTag = Factory.Tag.makeOne();
```

The output of `Factory` will be something like:

```js
{
  _id: "e02993ea96d7",
  name: "mockName",
  slug: "mockSlug",
  type: "mockType",
  metafields: ["item"],
  position: 3ff4e0634ecc,
  relatedTagIds: ["mockRelatedTagIds.$"],
  isDeleted: false,
  isTopLevel: true,
  isVisible: true,
  groups: ["mockGroups.$"],
  shopId: "a05276973251",
  createdAt: "1970-01-02T02:28:37.000Z",
  updatedAt: "2020-03-01T19:16:58.117Z",
  heroMediaUrl: "mockHeroMediaUrl"
}
```

To mock multiple instances of a single type of object, use the `makeMany` method with an integer argument:

``` js
import { Factory } from "/imports/test-utils/helpers/factory";
const mockTags = Factory.Tag.makeMany(2);
```

`makeMany` will output an array of objects:

```js
[
 {
   _id: "e02993ea96d7",
   name: "mockName",
   slug: "mockSlug",
   type: "mockType",
   metafields: ["item"],
   position: "3ff4e0634ecc",
   relatedTagIds: ["mockRelatedTagIds.$"],
   isDeleted: false,
   isTopLevel: true,
   isVisible: true,
   groups: ["mockGroups.$"],
   shopId: "a05276973251",
   createdAt: "1970-01-02T02:28:37.000Z",
   updatedAt: "2018-06-04T19:16:58.117Z",
   heroMediaUrl: "mockHeroMediaUrl"
 },
 {
   _id: "bdc84075a8eb",
   name: "mockName",
   slug: "mockSlug",
   type: "mockType",
   metafields: ["item"],
   position: "5034c879b7c2",
   relatedTagIds: ["mockRelatedTagIds.$"],
   isDeleted: false,
   isTopLevel: true,
   isVisible: true,
   groups: ["mockGroups.$"],
   shopId: "28d65013adc8",
   createdAt: "1970-01-02T02:28:37.000Z",
   updatedAt: "2018-06-04T19:16:58.117Z",
   heroMediaUrl: "mockHeroMediaUrl"
 }
]
```

You might need to specify a value for a property within your mock data, like making all mocked objects have the same `shopId`. Provide a properties object as an argument to either `makeOne` or `makeMany`:

``` js
import { Factory } from "/imports/test-utils/helpers/factory";
const mockTag = Factory.Tag.makeOne({ shopId: "1234" });
```

`makeMany` also supports custom values that are not constant, e.g., a series of mock objects that have sequential `_id` values. To use an arrow function to output two mocked tags with `_id` values of `"100"` and `"101"`:

``` js
import { Factory } from "/imports/test-utils/helpers/factory";
const mockTags = Factory.Tag.makeMany(2, { shopId: "1234", _id: (index) => (index + 100).toString() });
```

Finally, indexes can be passed from a `makeMany` method into another method. Here, passing `30` into `makeMany` will also pass `30` into `makeMockProductWithSpecificId`:

```js
function makeMockProductWithSpecificId(index) {
  const productId = (index + 100).toString();

  return Factory.CatalogProduct.makeOne({
    _id: productId,
    isDeleted: false,
    isVisible: true,
    tagIds: [mockTagWithFeatured._id],
    shopId: internalShopId
  });
}

const mockCatalogItemsWithFeaturedProducts = Factory.Catalog.makeMany(30, {
  product: makeMockProductWithSpecificId,
  shopId: internalShopId
});
```


### React component tests

Many Open Commerce user interface components are written in React. To write Jest unit tests for these components, you need to use a tool that renders them for testing purposes. We recommend the [React Testing Library](https://testing-library.com/docs/react-testing-library/intro/), which allows writing tests that closely resemble how interface components are used, rather than their inner workings.

## Writing Jest integration tests

Jest integration test files always end in `.test.js` and should be located in the `/tests` folder. Integration tests can write some initial test data to an in-memory MongoDB store, allowing them to test database queries without mocking them. The MongoDB collections are simulated in-memory collections, implemented using [`mongodb-memory-server`](https://github.com/nodkz/mongodb-memory-server).

### GraphQL integration tests

Integration tests can send actual GraphQL requests to a temporary test server that behaves like the real server. This makes them useful for testing things such as:

- Queries involving multiple resolvers
- Response pagination
- Whether mutations properly affect the MongoDB collections
- The effect of complex permission rules on query results

> **Note**: The `query`, `mutation`, and `subscription` properties of the test server are wrappers around the methods of the same name provided by the [graphql.js](https://github.com/f/graphql.js) package.

When creating GraphQL tests, the folder and file structure in `/tests` should match the plugin folder structure as much as possible.

Prior to running the tests in each file, initialize a server, in-memory database, and any collection data you need; then stop the server, like so:

```js
import { importPluginsJSONFile, ReactionTestAPICore } from "@reactioncommerce/api-core";

let testApp
beforeAll(async () => {
  // init the reaction test API server
  testApp = new ReactionTestAPICore();
  // create a list of plugins
  const plugins = await importPluginsJSONFile("../../../../../plugins.json", (pluginList) => {
    // Remove the `files` plugin when testing. Avoids lots of errors.
    delete pluginList.files;
    return pluginList;
  });
  
  // register plugins and start test server
  await testApp.reactionNodeApp.registerPlugins(plugins);
  await testApp.start();
})

// stoping the test server will drop all test data
afterAll(() => testApp.stop());

test("primaryShop query returns a shop", async () => {
  // (1) use testApp.collections to write to MongoDB collections to set up initial data state
  await testApp.collections.Shops.insertOne({ shopType: "primary", name: "Test Shop", createdAt: new Date(), _id: "123456" });
  // (2) execute a GraphQL query or mutation using testApp.query()() or testApp.mutation()()
  const results = await testApp.query(`query primaryShop { name _id }`);
  // (3) verify the response is as expected and/or verify that the collection data has been changed
  expect(results.name).toBe("Test Shop");
  // (4) optionally reset the database if there is a chance it will conflict with the next test in this file
  await testApp.collections.Shops.remove({ _id: "123456" });
});
```

The following code snippets are examples of common tasks to include in GraphQL integration tests. `mockAccount` may either be an account document that you’ve already inserted or one that the test server will insert for you. Either way, it must have an `_id` property, which will be used to set the `user` and `account` properties of the GraphQL context for all test queries.

To insert a primary shop:

```js
const shopId = await testApp.insertPrimaryShop();
```

To create a mock user:

```js
mockAccount = Factory.Accounts.makeOne({
  // ...any specific properties you need on the account
});
await testApp.createUserAndAccount(mockAccount);
```

To create a mock admin user:

```js
mockAdminAccount = Factory.Accounts.makeOne({
  // ...any specific properties you need on the account
});
await testApp.createUserAndAccount(mockAdminAccount, ["owner"]);
```

To set and clear the mock user:

```js
beforeAll(async () => {
  await testApp.setLoggedInUser(mockAccount);
});

afterAll(async () => {
  await testApp.clearLoggedInUser();
});
```

## Running Jest tests

When running tests, first make sure they are located in the correct directory (for unit tests, the same directory as the code being tested; for integration tests, the `/tests` folder). 

To run tests, use `npm` and specify the type of test being run—either `npm run test:unit` or `npm run test:integration`. Most repos will also accept `npm run test` for running unit tests. To  have tests rerun as you make changes to test files, add `:watch`—either `npm run test:unit:watch` or `npm run test:integration:watch`.

> **Note**: To use watch mode on macOS, you must install `watchman`. This can be done via the [Homebrew package manager](https://brew.sh) by running `brew install watchman`.

Jest has a built-in caching feature, which can sometimes give you bad cached results, even if the test has since been fixed. To force a test to ignore the cache, add the `--no-cache` flag.

You can also use Docker Compose to run tests within a local development container. This gives a more accurate picture of how production code running in a container will behave. Run `docker-compose run --rm reaction npm run test:unit` or `docker-compose run --rm reaction npm run test:integration` These tests can also be run in watch mode by suffixing `:watch`.

---

## File: guides/build-api-plugin.md

## At a glance

Mailchimp Open Commerce has a plugin system that allows you to extend or replace any of the features offered by the core platform. You can add plugins written by the Open Commerce community, or you can create your own. The plugins that you register will determine the exact functionality of your API.

You can use plugins to:

  * Add new types of information to products

  * Accept payments or ship orders with your preferred provider

  * Validate customers’ addresses when they check out

For the purposes of this guide, we manage the Open Commerce store for Leader of the Pack, an outdoor supplier that sells backpacks, among other things. We currently list fairly standard product dimensions—length, width, height—but recently, our customers have been asking just how much stuff they can fit in our backpacks. To answer their questions, we’ve decided to add a `volume` attribute to those products in our store.

In this guide, we’ll create a new plugin from the template provided by Open Commerce and register it with the platform running in development mode. Then we’ll set up the volume field and add it to the schemas for our products. Finally, we’ll extend the GraphQL API so our new field is available across our store.

## What you’ll need

  * An [Open Commerce installation](/developer/open-commerce/guides/quick-start/)

  * [Docker](https://www.docker.com/)

  * [npm](https://www.npmjs.com/get-npm)

  * A [GitHub](https://github.com/) account

## Clone the example plugin template

We’ll be creating a plugin that extends our products schema to include a `volume` attribute for our backpacks. To build our custom plugin, we’ll first create our own repo from the [Example Plugin template on GitHub](https://github.com/reactioncommerce/api-plugin-example/) and then clone the new plugin to our working directory. (Cloning to `reaction-development-platform/api-plugins` is recommended but not required.)

Next, we’ll edit the `package.json` and `src/index.js` files to replace the template defaults with information about our volume attribute plugin; the information in these files will be used when linking, installing, and registering the plugin. We’ll name our plugin Product Volume, with an npm name of `@leaderpack/api-plugin-products-volume`.

> **Note**: To keep things organized, it’s a good idea to name plugins after their “parent” plugins. In our case, we’re customizing aspects of `api-plugin-products`, so we named our plugin `api-plugin-products-volume`.

## Link the plugin

Next we have to load our new plugin by linking it inside the Open Commerce Docker container. While you can do this in production mode, it’s better to do it in development mode, which allows you to add and remove plugins on the fly. 

To enable development mode, navigate to your `reaction-development-platform` directory and run `make dev-reaction`.

> **Note**: Starting development mode takes a couple minutes—much longer than production mode. Development mode builds an editable Docker image on demand, while production mode uses an uneditable, pre-built image. 

From here, we’ll change directories to `reaction-development-platform/reaction` and add our plugin to the list in `plugins.json`: 

### plugins.json

```json
{
  ...
  "productsVolume": "@leaderpack/api-plugin-products-volume",
  ...
}
```
    
    
To link our plugin to the Open Commerce API Docker container, we’ll run `bin/package-link api-plugin-products-volume`. If you need to link a plugin that is not in the `api-plugins` folder, add the path to your plugin as the second argument of `bin/package-link`.

The API will restart automatically with the new plugin loaded and ready to use. When you make edits to a plugin in development mode, the Open Commerce API service will automatically refresh to reflect those edits. 

You can verify that the plugin has loaded in the logs by running `docker-compose logs -f api`, or in the admin dashboard under **Settings > System information**.

## Add functions

So we know that our new plugin is running, but we haven’t programmed it to do anything yet. Now we can start to add functionality—and since Open Commerce is in development mode, our plugin will reload every time we modify it.

Open Commerce offers several ways to [extend its functionality and API](/developer/open-commerce/docs/sharing-code-between-plugins/). Our plugin will use two approaches: `functionsByType`, which overrides or extends existing functions, and `graphQL`, which adds queries and mutations to the GraphQL API. 

Since our goal is to add a `volume` attribute to product variants, we’ll need to extend the existing product and catalog SimpleSchema to include this new field. First, we’ll add a startup function to extend the two SimpleSchemas, allowing them to be validated and saved to MongoDB:

```node title="startup.js"
function myStartup(context) {
  context.simpleSchemas.ProductVariant.extend({
	volume: {
	  type: Number,
	  min: 0,
	  optional: true
	}
  });

  context.simpleSchemas.CatalogProductVariant.extend({
	volume: {
	  type: Number,
	  min: 0,
	  optional: true
	}
  })
}
```

We’ll also extend the function that publishes products to the catalog, adding extra steps to `publishProductToCatalog`. The new `myPublishProductToCatalog` function parses our products, gets the new `volume` attribute, and adds it to the corresponding catalog variant in preparation for publishing it to the catalog:

```node title="index.js"
function myPublishProductToCatalog(catalogProduct, { context, product, shop, variants }) {
 catalogProduct.variants && catalogProduct.variants.map((catalogVariant) => {
   const productVariant = variants.find((variant) => variant._id === catalogVariant.variantId);
   catalogVariant.volume = productVariant.volume || null;
 });
}
```

Then we’ll instruct the `catalogs` plugin to looks for the `myPublishProductToCatalog` function by type, making it the key to communicating data from the new field to the catalog:

```node title="index.js"
export default async function register(app) {
  await app.registerPlugin({
	label: "Product Dimensions",
	name: "products-dimensions",
	version: pkg.version,
	functionsByType: {
	  startup: [myStartup]
	  publishProductToCatalog: [myPublishProductToCatalog]
	},
  });
}
```

## Extend the GraphQL API

Our custom plugin is linked, registered, and has a startup function, so now it’s time to extend the GraphQL API. We’ll do that by creating a `schema.graphql` file in the plugin’s `src/` directory and adding type extensions:

```graphql title="schema.graphql"
extend type ProductVariant {
  volume: Float
}

extend input ProductVariantInput {
  volume: Float
}

extend type CatalogProductOrVariant {
  volume: Float
}
```

Back in our plugin’s `src/index.js` file, we can import the new schema using `importAsString` and add it to the `app.registerPlugin` options:

```node title="src/index.js"

import importAsString from “@reactioncommerce/api-utils/importAsString.js”;

const mySchema = importAsString(“./schema.graphql”);

export default async function register(app) {
  await app.registerPlugin({
	label: "Product Dimensions",
	name: "products-dimensions",
	version: pkg.version,
	functionsByType: {
	  startup: [myStartup]
	  publishProductToCatalog: [myPublishProductToCatalog]
	},
	graphQL: {
	  schemas: [mySchema]
	}
  });
}
```

That’s it! We can see the plugin in action by using a GraphQL mutation to update a product variant with a `volume` attribute:

```graphql title="Mutation"
mutation {
  updateProductVariant(input: {
	variantId: "{encoded-variant-id}",
	shopId: "{encoded-shop-id}",
	variant: {
	  volume: 200
	}
  }) {
	variant {
	  volume
	}
  }
}

```

```graphql title="Response"
{
  "data": {
	"updateProductVariant": {
	  "variant": {
		"volume": 200
	  }
	}
  }
}

```

And once that variant is published, the new `volume` field will be available on the catalog product, thanks to the `publishProductToCatalog` function:

```graphql title="Query"
query {
  catalogItemProduct(
	shopId: "{encoded-shop-id}",
	slugOrId: "{encoded-slug-or-id}") {
	product {
	  variants {
		volume
	  }
	}
  }
}
 
```


```graphql title="Response"
{
  "data": {
	"catalogItemProduct": {
	  "product": {
		"variants": [
		  {
			"volume": 200
		  }
		]
	  }
	}
  }
}

```

We’ve done everything we need to add volume data to our backpacks and query that information across our store. That’s useful to Leader of the Pack’s store administrators, but we also need to get that information to customers. This will require some design work on our product pages, which are dependent on the storefront plugin that we’re using. Generally, this will involve making sure that our product query includes the new `volume` field and that the information is passed to the `render()` function in the file that describes our product detail page. For more information, see the [Creating and Organizing Products doc](/developer/open-commerce/docs/creating-organizing-products/).

## More resources

  * [Sharing Code Between Plugins](/developer/open-commerce/docs/sharing-code-between-plugins/)

  * [GraphQL Playground](/developer/open-commerce/playground/)

  * [Open Commerce Fundamentals](/developer/open-commerce/docs/fundamentals/)