diff --git a/docs/_assets/readme-custom-code-block.jpg b/_assets/readme-custom-code-block.jpg similarity index 100% rename from docs/_assets/readme-custom-code-block.jpg rename to _assets/readme-custom-code-block.jpg diff --git a/docs/_assets/readme-custom-note-block.jpg b/_assets/readme-custom-note-block.jpg similarity index 100% rename from docs/_assets/readme-custom-note-block.jpg rename to _assets/readme-custom-note-block.jpg diff --git a/docs/_assets/readme-vanilla-block-quote.jpg b/_assets/readme-vanilla-block-quote.jpg similarity index 100% rename from docs/_assets/readme-vanilla-block-quote.jpg rename to _assets/readme-vanilla-block-quote.jpg diff --git a/docs/_assets/readme-vanilla-code-block.jpg b/_assets/readme-vanilla-code-block.jpg similarity index 100% rename from docs/_assets/readme-vanilla-code-block.jpg rename to _assets/readme-vanilla-code-block.jpg diff --git a/docs/environment-variables.md b/guides/deployment-guide/environment-variables.md similarity index 100% rename from docs/environment-variables.md rename to guides/deployment-guide/environment-variables.md diff --git a/guides/developers-guide/admin/creating-a-plugin.md b/guides/developers-guide/admin/creating-a-plugin.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/admin/setting-up-your-ide.md b/guides/developers-guide/admin/setting-up-your-ide.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/admin/workflows.md b/guides/developers-guide/admin/workflows.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/concepts/carts.md b/guides/developers-guide/concepts/carts.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/concepts/catalog-and-publishing.md b/guides/developers-guide/concepts/catalog-and-publishing.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/concepts/error-handling.md b/guides/developers-guide/concepts/error-handling.md new file mode 100644 index 0000000..cc21045 --- /dev/null +++ b/guides/developers-guide/concepts/error-handling.md @@ -0,0 +1,32 @@ +# Error Handling + +When throwing errors designed to communicate information back to the client from within a client-called function such as a GraphQL resolver, use the `ReactionError` class rather than just `Error`. In other types of functions, throwing a `new Error()` is best. + +Always include both a standard machine-readable error type and a human-readable error message, which must begin with a capital letter. + +```js +import ReactionError from "@reactioncommerce/reaction-error"; + +throw new ReactionError("access-denied", "Access denied"); +``` + +## Common `errors` and `reasons` + +Here is a list of standardized arguments for various error situations: + +| error | reason examples | +| --- | --- | +| `access-denied` | "Access denied" | +| `invalid-parameter` | "X is invalid", "Bad X ID" "X is required" | +| `not-configured` | "Open Exchange Rates not configured. Configure for current rates." | +| `not-found` | "User not found" | +| `not-implemented` | "Multiple shipping providers is currently not implemented" | +| `server-error` | "Error adding to cart" | + +#### Error message guidelines + +The above list is not exhaustive throughout the whole app. For other error scenarios, use the guidelines below: + +- Do not use HTTP status codes, like `404` or `500`. Use the error messages `invalid-parameter` and `server-error` instead respectively. +- Always start the reason with a capital letter. +- Do not use exclamation marks in the reason. \ No newline at end of file diff --git a/guides/developers-guide/concepts/fulfillment-groups.md b/guides/developers-guide/concepts/fulfillment-groups.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/concepts/how-core-plugins-load.md b/guides/developers-guide/concepts/how-core-plugins-load.md new file mode 100644 index 0000000..56775e2 --- /dev/null +++ b/guides/developers-guide/concepts/how-core-plugins-load.md @@ -0,0 +1,239 @@ +# Developing API Plugins + +Let's take a closer look at how the Open Commerce headless API works, by building one from scratch. + +``` +mkdir my-reaction-api +cd my-reaction-api +echo "12.14.1" >> .nvmrc +nvm use +# run nvm install if prompted +npm init -y +touch index.js +``` + +(Note: You can choose a higher version of Node.js, but 12.14.1 is the minimum.) + +This will create a simple Node.js app. Now open this directory in your favorite code editor. For example, enter `code .` to open it in Visual Studio Code. +Open Commerce API packages assume that your project is using ECMAScript modules, so first edit `package.json` and add `”type”: “module”`. +Also in `package.json`, add the following + +``` +"engines": { + "node" : ">=12.14.1" +}, +``` + +Add a start script in the scripts object: + +``` +"scripts": { + "start": "node --experimental-modules --experimental-json-modules ./index.js" +}, +``` + +Note: if you’re using Node.js 14+, the --experimental-modules flag is no longer necessary but --experimental-json-modules flag may still be needed +Then install the `@reactioncommerce/api-core` NPM package: + +``` +npm install @reactioncommerce/api-core +``` + +Edit the `index.js` file and paste in the following: + +``` +import { ReactionAPICore } from "@reactioncommerce/api-core"; +import packageJson from "./package.json"; +const api = new ReactionAPICore({ + version: packageJson.version +}); +async function run() { + await api.start(); +} +run().catch((error) => { + console.error(error); + process.exit(1); +}); +``` + +This is technically all you need to do to create a barebones Open Commerce API. Before we start it, though, you’ll need a MongoDB server running on the default port on localhost. The quickest way to do this is: + +``` +docker pull mongo:4.2.0 +docker run -p 27017:27017 mongo:4.2.0 mongod \ + --oplogSize 128 --replSet rs0 --storageEngine=wiredTiger +``` + +With the database now running, you can enter `npm start` in the `my-reaction-api` directory and you should see some startup logging ending with “GraphQL listening at http://localhost:3000/graphql (port 3000)” +If you go to that URL in any browser, you should see a GraphQL Playground UI. But view the Docs on the right side of the screen and you’ll notice that there are only 3 operations available: ping, echo, and tick. These are simple test operations included with the `api-core` package, but most of Open Commerce is missing! That’s because the stock Open Commerce API is really a combination of 37 API plugins, which need to be installed and registered. + +## Registering a Plugin + +To get an idea of what registering an API plugin entails, add this in the `run` function, above the `api.start()` call. + +``` +await api.registerPlugin({ + name: "test", + functionsByType: { + startup: [ + function testStartupFunction() { + console.log("I am startup code for the test plugin."); + }, + ], + }, +}); + +``` + +Press `ctrl+c` to stop the running API, and then `npm start` to start it again. It should now pick up our test plugin and you should see the startup logging. Hopefully this gives you an idea of how plugins work, but plugins can actually do much more than this, and we recommend that all plugins be separate packages that you can install with NPM. For more information, refer to https://github.com/reactioncommerce/api-core#plugins + +## A Better Way to Register Plugins + +We saw above how you can call `api.registerPlugin` one or more times before calling `api.start` to register plugins. You could `npm install` any plugin packages, import them into `index.js`, and then pass each one in to `api.registerPlugin`, but there is a simpler, more declarative way. +The recommended way to add plugins to a Open Commerce API project is by listing them in a JSON file: + +1. Create a file `plugins.json` alongside `index.js` +2. npm install each plugin package you need +3. List all plugins in the `plugins.json` file, which is an object where each key is any unique identifier for the plugin and each value is the plugin package name, for example, `@reactioncommerce/api-plugin-carts` +4. In `index.js`, import and use the importPluginsJSONFile function from `api-core` + +``` +async function run() { + const plugins = await importPluginsJSONFile("./plugins.json"); + await api.registerPlugins(plugins); + await api.start(); +} +``` + +Note that if you want to experiment with a plugin without yet creating a separate package for it, the keys in the `plugins.json` file can also be a relative path to a local ES module file that exports the package configuration. + +## Stock Open Commerce + +As we’ve seen, you can build a Open Commerce API from scratch with your own mix of plugins. However, if you have simple needs or just want to try it out, it’s unlikely that you’ll need to do that. There’s an easier way! + +The `reaction` GitHub repo is a Node.js project in which we’ve already installed and registered a particular “blessed” set of API plugins. This is the API configuration that Open Commerce maintainers test against, and which we believe to be the most useful for most use cases. So you can start by simply cloning or forking that repository if you know how to do so. + +In fact, this stock Open Commerce configuration is also published as a Docker image, so you can very easily install and run it on any computer or on Docker-based cloud hosting services, too. + +But what about all of the related services and UI applications? As explained in the “Current Open Commerce Architecture” section, Open Commerce is actually made up of several different services. How do you run the whole system locally? + +Well, actually all Open Commerce services are published as Docker images, so all you need to do is pull, configure, and run them, with proper connections among them. For an easy way to do this, there is the [Reaction Development Platform](https://github.com/reactioncommerce/reaction-development-platform), which is where most people, whether you are looking to try, demo, test, or develop Open Commerce, will want to start. + +## Switching to Development Mode + +Now let’s say you want to make some changes to a service and be able to see those changes reflected in the running service. By default, any changes you make do not affect the running service because all services run in standard mode. It’s done this way because it is much faster to start a service in standard mode. To develop a service, you need to switch it to development mode. +Development mode differs from standard mode in the following ways: + +- A generic Node.js development Docker image is used for the container rather than the published service image. +- Your locally checked out project files, other than those under `node_modules`, are mirrored into the container by way of a Docker volume mount. +- NPM packages are installed when the container starts and therefore reflect the project’s `package.json` file. (In other words, you can add additional NPM dependencies and then stop and restart the container.) +- In the container, the project runs within nodemon, which will restart the app whenever you change any file in the project. + +Switching a project to development mode is easy. + +1. Start the whole system in standard mode using `make` command in the Reaction Development Platform directory. +2. Run `make dev-` for one or more services to restart them in development mode. For example, `make dev-reaction` if you want to make API changes. + +But what exactly is this doing? Hopefully you don’t need to concern yourself with the details, but for those who want to know or who run into troubles, here’s the breakdown: + +- Every project repo has two Docker Compose configuration files: `docker-compose.yml` for standard mode and `docker-compose.dev.yml` for dev mode. The dev mode file is intended to extend the standard file, so it isn’t a complete configuration. +- There is a feature built in to Docker Compose that looks for a `docker-compose.override.yml` file and uses it to extend `docker-compose.yml` whenever you run `docker-compose` commands. +- The `make dev-*` commands stop the service if necessary, symlink `docker-compose.dev.yml` to `docker-compose.override.yml` in that service’s project folder, and then restart the service with the dev override now in effect. Conversely the `make` command and other non-dev-mode `make` subcommands always remove the `docker-compose.override.yml` symlink before running the service, ensuring that it will revert to standard mode. + +To put that another way, this sequence of commands: + +``` +make dev-reaction +cd reaction +``` + +is equivalent to this sequence of commands: + +``` +cd reaction +docker-compose down +ln -sf docker-compose.dev.yml docker-compose.override.yml +docker-compose pull +docker-compose up -d +``` + +## Developing API Plugins + +Everything discussed so far has been true since the 3.0.0 release or earlier. With the 3.7.0 release, though, developing the API is slightly more complicated because almost all API code actually lives in NPM plugins, each of which lives in its own GitHub repository. You may be familiar with the `npm link` command as a way of temporarily linking NPM package code into a project to test before publishing it, but unfortunately `npm link` doesn’t work easily with code running inside a Docker container. + +Initially, the solution was to use temporary Docker volume links to map NPM package code from the host machine into the `node_modules` directory in the API container. However, this had a number of rather severe down sides, including the fact that the `node_modules` folder in the linked plugin project folder on the host machine would often get completely deleted. +So Open Commerce 3.8.0 introduces two new scripts that implement a smarter way of linking in plugin packages (or any NPM packages): `bin/package-link` and `bin/package-unlink`. + +But before we discuss the linking approach, let’s talk about how to clone the built-in plugin packages in the first place. There are nearly 40 API plugins. That’s a lot of repositories to clone, and it can be helpful to clone them all so that you can run searches across the full codebase. Fortunately, there is a quick way to do that, too: + +``` +make clone-api-plugins +``` + +When you run this command in the Reaction Development Platform directory, it will clone every built-in plugin into an `api-plugins` subfolder, alongside the service subfolders. You can then modify files in these plugins as necessary and link them into the API project to test them. + +So back to the linking scripts. Let’s say you think there’s a bug in the built-in carts plugin. You cloned all the plugins and then changed a file in `api-plugins/api-plugin-carts` directory in an attempt to fix the bug. Now the first thing to do is to put the API in development mode if you haven’t already. After that, linking in your local carts plugin code is as simple as this: + +``` +cd reaction +bin/package-link @reactioncommerce/api-plugin-carts +``` + +The already-running API server will automatically restart to pull in your changes. + +If your fix wasn’t quite correct and you make more changes to files in the carts plugin, you’ll have to run the link command again: + +``` +bin/package-link @reactioncommerce/api-plugin-carts +``` + +If necessary, you can run the link command for other plugins as well. You can even run it for other NPM packages that are not API plugins, but then you’ll need an additional argument that is the relative or absolute path to the package code on your host machine. For example: + +``` +bin/package-link some-other-package ../../some-other-package +``` + +(You can also use the code path argument for API plugin linking if you have cloned your API plugins to a non-standard location.) + +When you’re done, be sure to unlink before stopping the API service or running `make stop`: + +``` +bin/package-unlink @reactioncommerce/api-plugin-carts +``` + +This linking approach works pretty well but has the potential to get the API into a state where it complains about missing dependencies and won’t start. If this happens and restarting the API service does not fix it, you will need to use the `docker volume rm` command to delete the API `node_modules` volume (usually named something like `reaction_api_node_modules`). If that doesn’t work, running `docker-compose down -v` in the `reaction` directory will work, but be careful because that command will also wipe out your local MongoDB database. + +### Link multiples packages with package configuration file + +Before running the `bin/package-link` script, create a `yalc-packages` file from the example. + +```sh +cp yalc-packages.example yalc-packages +``` + +Then run the link script without any arguments +```sh +./bin/package-link +``` + +This will link every package in the `yalc-packages` file to your api app. If you don't want every plugin to be linked, edit `yalc-packages` and set the packages you want disabled and unlinked to `false`. + +Format: `path=true|false` +``` +../api-plugins/api-core=true +../api-plugins/api-plugin-accounts=true + +``` +(You must have a blank line at the end of the file, otherwise your last plugin will be omitted) + +### Update package links on stop/start + +If you've stopped, then started your container you may notice your linked packages have been reset. To re-link previously linked packages you can run the package update script. + +``` +./bin/package-update +``` + +(This runs `yalc update` under the hood, and only re-links packages in the `yalc.lock` file, and only if those packages are still published inside the docker container.) + +Alternatively, if you've configured `yalc-packages`, then you can always run `./bin/package-link` again to re-link and update everything. \ No newline at end of file diff --git a/guides/developers-guide/concepts/image-handling.md b/guides/developers-guide/concepts/image-handling.md new file mode 100644 index 0000000..e5b64e1 --- /dev/null +++ b/guides/developers-guide/concepts/image-handling.md @@ -0,0 +1,39 @@ +# Images + +We are using [FileCollections](https://github.com/reactioncommerce/reaction-file-collections) for file uploading, downloading, and transformation. + +## The Media FileCollection + +There is just one `MeteorFileCollection` named `Media`, which is defined in `/imports/plugins/core/files`. The related server setup is in `/imports/plugins/core/files/server/fileCollections.js`. + +The `Media` FileCollection is available in both client code and server code with a similar API. + +Importing in a client code file: + +```js +import { Media } from "/imports/plugins/core/files/client"; +``` + +Importing in a server code file: + +```js +import { Media } from "/imports/plugins/core/files/server"; +``` + +The normal `Mongo.Collection` that backs the `Media` FileCollection is imported like any other collection: + +```js +import { MediaRecords } from "/lib/collections"; +``` + +Use `Media` when you want to work with `FileRecord` instances. Use `MediaRecords` directly if you just want to modify the collection data directly, either for performance or convenience. + +## Changing the Binary Data Store + +By default, GridFS is used to store the actual file data (as opposed to the information about the file, which is what is stored in the `MediaRecords` collection). GridFS stores the binary data in MongoDB collections. + +If you'd like to use a different store, such as a cloud storage service, this is relatively easy. Refer to [FileCollections](https://github.com/reactioncommerce/reaction-file-collections) documentation. Then import your storage adapter and change all of the `new GridFSStore` in `/imports/plugins/core/files/server/fileCollections.js` to `new YourCustomStore`. + +## Image Transformation + +The [Sharp](http://sharp.pixelplumbing.com/en/stable/) package handles resizing images when they are uploaded. Refer to the `imgTransforms` array and the `transformWrite` functions in `/imports/plugins/core/files/server/fileCollections.js`. For more details about `transformWrite`, refer to [FileCollections](https://github.com/reactioncommerce/reaction-file-collections) documentation. \ No newline at end of file diff --git a/guides/developers-guide/concepts/inventory.md b/guides/developers-guide/concepts/inventory.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/concepts/job-control.md b/guides/developers-guide/concepts/job-control.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/concepts/localization.md b/guides/developers-guide/concepts/localization.md new file mode 100644 index 0000000..12bb3a6 --- /dev/null +++ b/guides/developers-guide/concepts/localization.md @@ -0,0 +1,13 @@ +# Localization + +You can customize your shop's default currency, language, and other localization features, login to `reaction-admin` (on [localhost:4080](http://localhost:4080) if you're running it locally) and go to **Settings > Shop Localization**. + +Select your shop preferences from the drop-down menu. + +You may customize the following: + +- **Timezone** - A list of all available timezones +- **Base Currency** - A list of all available currencies +- **Base Unit of Length** - Inches, Centimeter, Feet +- **Base Unit of Measure (Weight)** - Ounce, Pounds, Grams, Kilograms +- **Base Language** - A list of languages \ No newline at end of file diff --git a/guides/developers-guide/concepts/logging-best-practices.md b/guides/developers-guide/concepts/logging-best-practices.md new file mode 100644 index 0000000..c66c61d --- /dev/null +++ b/guides/developers-guide/concepts/logging-best-practices.md @@ -0,0 +1,114 @@ +# Logging + +Open Commerce uses the [Bunyan](https://github.com/trentm/node-bunyan) logging library to provide a stream capable log handler that can send your logs to a variety of places. By default, the *Reaction logger* outputs to the console (stdout), but you can also stream your server side logs to services like [Loggly](https://www.loggly.com/) (see below) or even save them to your database. + +## Log Level + +Most loggers have the concept of *Log level*. That allows you to filter what is visible in your logs (see available levels and their descriptions below). The default levels in Open Commerce are `INFO` on the server and `WARN` on the client. To override the log level on the server set the `REACTION_LOG_LEVEL` environment variable to the level you want. + +When doing custom development and adding more logging to the app, we suggest following the [Bunyan recommendations on log levels](https://github.com/trentm/node-bunyan#levels) and use appropriate levels for your messages. + +The log levels in Bunyan are as follows. The level descriptions are best practice opinions. + +- **TRACE** (10): Logging from external libraries used by your app or very detailed application logging. +- **DEBUG** (20): Anything too verbose to be included in the standard "info" level. +- **INFO** (30): Detail on regular operation. +- **WARN** (40): Detail on something that should probably be looked at by an operator eventually. +- **ERROR** (50): Fatal for a particular event, but the service/app continues servicing other events. An operator should look at this soon. +- **FATAL** (60): The service/app is going to stop or become unusable now. An operator should definitely look into this soon. + +**Suggestion**: Use "DEBUG" sparingly. Information that will be useful to debug errors post mortem should usually be included in "info" messages if it's generally relevant or else with the corresponding "error" event. Don't rely on spewing mostly irrelevant debug messages all the time and sifting through them when an error occurs. + +## Usage + +### Server + +```js +import Logger from "@reactioncommerce/logger"; +``` + +### Client + +Default level in development: `INFO` (slightly more verbose for development) + +Default level in production: `WARN` (only show warnings or worse) + +```js +import Logger from "@reactioncommerce/logger"; +``` + +### Log stuff + +```js +import Logger from "@reactioncommerce/logger"; + +// a general message string +Logger.info("Something important happened!"); + +// include some event-specific data in the message string +Logger.info(`Order ID ${order._id} has been submitted by user ${order.userId}`); + +// or extend the JSON output of the logger with an object +// (note that the object should go before the message text) +Logger.info({ order }, "Order has been submitted"); + +/** + * Logging warnings + */ + +// Log a non-critical warning that should be investigated +Logger.warn("API key missing. The feature won't work."); + +/** + * Logging errors + */ + +Logger.error("Something went wrong!"); + +// Bunyan has an error object parser built in, so you can pass +// errors into the logger and it will format them in your console +// as well as extend the raw JSON log output if you are piping +// your logs to another service like Loggly. +// (note that the error object should go before the message text) +doSomething((err, result) => { + if (err) { + Logger.error(err, "Something went wrong!"); + throw err; + } + Logger.info("That thing worked!"); + // or + Logger.info({ result }, "That thing worked!"); +}); + +/** + * Logging fatal events + */ + +// If an event is considered fatal (will stop the app from running +// entirely), you should use the FATAL log level. +// Note that this will rarely be needed. Most negative events +// are just warnings or errors and don't entirely prevent the +// app from running. +Logger.fatal("The app is going to crash now! Attention needed!"); +``` + +## Outputs + +As mentioned above, Bunyan is capable of sending your logs to a variety of services or you can even build your own plugin to send the raw JSON output to any API you choose. We suggest [searching npm for Bunyan](https://npms.io/search?q=bunyan) to see what options are already available before attempting to build your own. There are already a lot to choose from. + +By default, Open Commerce sends logs to the console, but we also support sending to [Loggly](https://www.loggly.com/) and [Slack](https://slack.com/). + +### Loggly + +Default level: `DEBUG` + +#### Environment variables + +```sh +# required +LOGGLY_SUBDOMAIN="" +LOGGLY_TOKEN="" + +# optional +LOGGLY_LOG_LEVEL="" +``` \ No newline at end of file diff --git a/guides/developers-guide/concepts/metrics.md b/guides/developers-guide/concepts/metrics.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/concepts/multi-shop.md b/guides/developers-guide/concepts/multi-shop.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/concepts/payments.md b/guides/developers-guide/concepts/payments.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/concepts/pricing.md b/guides/developers-guide/concepts/pricing.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/concepts/products-variants-options.md b/guides/developers-guide/concepts/products-variants-options.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/concepts/simple-schemas.md b/guides/developers-guide/concepts/simple-schemas.md new file mode 100644 index 0000000..ba64ec7 --- /dev/null +++ b/guides/developers-guide/concepts/simple-schemas.md @@ -0,0 +1,24 @@ +# Schemas + +Open Commerce uses [MongoDB](https://docs.mongodb.com/manual/), which is a schemaless database. This allows maximum flexibility, particularly important when quickly reacting to the design challenges that uniquely different customizations require in the course of an e-commerce operation. + +However, we don't want to just get completely crazy, so we define a **schema** that is attached to the previously schemaless collection. These schemas apply basic content and structure validation, also very necessary in e-commerce. + +Schemas are implemented using the [simpl-schema](https://github.com/aldeed/simple-schema-js) NPM package. In most cases the schemas can be kept private to the plugin that creates them. Typically, you will import them into a mutation function and validate a document before inserting or updating it. + +```js +OrderSchema.validate(order); // Will throw a validation error if invalid +await Orders.insertOne(order); +``` + +An update example: + +```js +OrderSchema.validate(modifier, { modifier: true }); + +const { value: updatedOrder } = await Orders.findOneAndUpdate( + { _id: orderId }, + modifier, + { returnOriginal: false } + ); +``` \ No newline at end of file diff --git a/guides/developers-guide/concepts/tags.md b/guides/developers-guide/concepts/tags.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/concepts/understanding-common-order.md b/guides/developers-guide/concepts/understanding-common-order.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/concepts/understanding-events.md b/guides/developers-guide/concepts/understanding-events.md new file mode 100644 index 0000000..cffa236 --- /dev/null +++ b/guides/developers-guide/concepts/understanding-events.md @@ -0,0 +1,91 @@ +# App Events + +The `Hooks.Events` API and `MethodHooks` from previous versions of Open Commerce have been removed in favor of `appEvents`. `appEvents`, like hooks, allow you to attach callbacks to particular events throughout the app lifecycle. + +## Emit an app event + +Emit app events in API code using `appEvents.emit`. The `emit` function takes at least two parameters: the name of the event as a string and the `payload` of functions as an object. + +### Function parameters and options + +- *Event name*: The first argument is the event name, as a string. There is currently no limit to what event name you can emit, but generally try to follow established patterns for naming. See events table below. +- *Payload*: The second argument, the `payload`, should always be an object, and not an entity object. Rather than passing `order` in directly, pass it in an object: `{ order }`, so that more fields can be added or removed from the payload more easily. +- *Option arguments*: The last argument is an array of arguments to pass to each function from `payload`. +- *Using `await`*: Using the method with `await` will not resolve until all of the registered handler methods have resolved. + +## Emit an app event + +```js +context.appEvents.emit("eventName", payload, options); +``` + +## Listen for an app event + +See ["Run plugin code on app startup"](dev-how-do-i.md) and attach event listeners in startup code. + +```js +// In startup function: +context.appEvents.on("eventName", (payload, options) => { + // Handle the event +}); +``` + +## Avoid infinite loops + +It's possible to get stuck in an infinite loop of emitting and listening for the same event. To avoid this, pass `emittedBy` key with a string value in the third options parameter on the `emit`, and check for it on the `on` function: + +```js +const EMITTED_BY_NAME = "myPluginHandler"; + +appEvents.on("afterCartUpdate", async ({ cart }, { emittedBy } = {}) => { + if (emittedBy === EMITTED_BY_NAME) return; // short circuit infinite loops + + appEvents.emit("afterCartUpdate", { cart: updatedCart, updatedBy: userId }, { emittedBy: EMITTED_BY_NAME }); +}); +``` + +## Events + +Events that are currently defined in Core and their previous Hooks-equivalents are: + +| App Events | Hooks (removed) | +|-----------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------| +| Event Name: `afterAccountCreate` Arguments: `{ account, createdBy, isFirstOwnerAccount }` | Event Name: `afterAccountsInsert` Arguments: `userId, accountId` | +| Event Name: `afterAccountUpdate` Arguments: `{ account, updatedBy, updatedFields }` | Event Name: `afterAccountsUpdate` Arguments: `userId, { accountId, updatedFields }` | +| Event Name: `afterAccountDelete` Arguments: `{ account, deletedBy }` | Event Name: `afterAccountsRemove` Arguments: `userId, accountId` | +| Event Name: `afterAddUnverifiedEmailToUser` Arguments: `{ email, userId }` | N/A | +| Event Name: `afterVariantSoftDelete` Arguments: `{ variant, deletedBy }` | `afterRemoveCatalogProduct` was sometimes emitted for variants | +| Event Name: `afterProductSoftDelete` Arguments: `{ product, deletedBy }` | Event Name: `afterRemoveProduct` Arguments: `product` | +| Event Name: `afterVariantUpdate` Arguments: `{ _id, field, value }` | N/A | +| Event Name: `afterPublishProductToCatalog` Arguments: `{ catalogProduct, product }` | Event Name: `afterPublishProductToCatalog` Arguments: `product, catalogProduct` | +| Event Name: `afterOrderUpdate` Arguments: `{ order, updatedBy }` | N/A | +| Event Name: `jobServerStartArguments`: NONE | Event Name: `onJobServerStart` Arguments: NONE | +| Event Name: `afterCoreInit` Arguments: NONE Deprecated. Do not use in new code. Put your code directly in a plugin startup function instead. | Event Name: `afterCoreInit` Arguments: NONE | +| Event Name: `sendEmail` Arguments: `{ job, sendEmailCompleted, sendEmailFailed }` | N/A | +| Event Name: `afterShopCreate` Arguments: `{ createdBy, shop }` | N/A | +| Event Name: `afterMediaInsert` Arguments: `{ createdBy, mediaRecord }` | N/A | +| Event Name: `afterMediaUpdate` Arguments: `{ createdBy, mediaRecord }` | N/A | +| Event Name: `afterMediaRemove` Arguments: `{ createdBy, mediaRecord }` | N/A | +| Event Name: `afterOrderApprovePayment` Arguments: `{ approvedBy, order }` | N/A | +| Event Name: `afterOrderCancel` Arguments: `{ cancelledBy, order, returnToStock }` | N/A | +| Event Name: `afterOrderPaymentCapture` Arguments: `{ capturedBy, order, payment }` | N/A | +| Event Name: `afterOrderCreate` Arguments: `{ createdBy, order }` | N/A | +| Event Name: `afterNotificationCreate` Arguments: `{ createdBy, notification }` | N/A | +| No longer emitted. Instead `afterAccountCreate` is emitted and will have `isFirstOwnerAccount` set to true | Event Name: `afterCreateDefaultAdminUser` Arguments: `user ` | +| No longer emitted. | Event Name: `beforeUpdateOrderWorkflow` Arguments: `order, options` | +| No longer emitted. Use afterOrderUpdate. | Event Name: `afterUpdateOrderUpdateSearchRecord` Arguments: `order` | +| No longer emitted. Was not used, but can be added back in the future if necessary. | Event Name: `afterInsertProduct` Arguments: `product` | +| No longer emitted. Was not used, but can be added back in the future if necessary. | Event Name: `beforeUpdateCatalogProduct` Arguments: `product, { userId, modifier }` | +| No longer emitted. Was not used, but can be added back in the future if necessary. | Event Name: `afterUpdateCatalogProduct` Arguments: `productId, { modifier }` | +| No longer emitted. Was not used, but can be added back in the future if necessary. | Event Name: `beforeRemoveCatalogProduct` Arguments: `product, { userId } | +| No longer emitted. Was not used, but can be added back in the future if necessary. | Event Name: `afterRemoveCatalogProduct` Arguments: `userId, productId` | +| No longer emitted. Was not used, but can be added back in the future if necessary. | Event Name: `beforeCoreInit` Arguments: NONE | +| No longer emitted. Was not used, but can be added back in the future if necessary. | Event Name: `onCoreInit` Arguments: NONE | +| No longer emitted. Was not used, but can be added back in the future if necessary. | Event Name: `onImport${collectionName}` Arguments: `object` | +| No longer emitted. Was not used, but can be added back in the future if necessary. | Event Name: `onCreateUser` Arguments: `user, options` | +| No longer emitted. Was not used, but can be added back in the future if necessary. | Event Name: `onLogin` Arguments: `options` | +| No longer emitted. Was not used, but can be added back in the future if necessary. | Event Name: `afterSecurityInit` Arguments: `options` | +| No longer emitted. Was not used, but can be added back in the future if necessary. Try using afterOrderUpdate. | Event Name: `onOrderRefundCreated` Arguments: `orderId` | +| No longer emitted. Was not used, but can be added back in the future if necessary. Try using afterOrderUpdate. | Event Name: `onOrderShipmentDelivered` Arguments: `orderId` | +| No longer emitted. Was not used, but can be added back in the future if necessary. Try using afterOrderUpdate. | Event Name: `onOrderShipmentShipped` Arguments: `orderId` | +| Instead of using events, you can now register a function of type getPageSitemapItems to extend the sitemap. | Event Name: `onGenerateSitemap` Arguments: `urls` | \ No newline at end of file diff --git a/guides/developers-guide/concepts/understanding-the-context-attribute.md b/guides/developers-guide/concepts/understanding-the-context-attribute.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/concepts/users-and-authentification.md b/guides/developers-guide/concepts/users-and-authentification.md new file mode 100644 index 0000000..2c55439 --- /dev/null +++ b/guides/developers-guide/concepts/users-and-authentification.md @@ -0,0 +1,65 @@ +# Authentication + +## How to Get an Access Token for Development + +For development purposes, you'll want to try out GraphQL requests in GraphQL Playground or another GraphQL client. The normal way of getting OAuth access tokens is by going through an OAuth flow from a browser or native client, but this is a lot of extra work when you just want to test some requests on your local computer. + +You can get the access token on GraphQL Playground by running the below mutation +``` +mutation authenticate($serviceName: String!, $params: AuthenticateParamsInput!) { + authenticate(serviceName: $serviceName, params: $params) { + sessionID + tokens { + accessToken + } + } +} +``` +Make sure you have a running instance of Open Commerce to view GraphQL playground on http://localhost:3000/graphql + +You will need the below query variables as input for the mutation. Paste this in the *QUERY VARIABLES* tab in the bottom left of the playground. +``` +{ + "serviceName": "password", + "params": { + "user": { + "email": "" + }, + "password": "" + } +} +``` + +The password should be a SHA-256 hashed password. You can use this [online tool for hashing](https://emn178.github.io/online-tools/sha256.html). + +Once you run this mutation you can run other queries and mutation from the playground using the access token. Copy the `accessToken` from the result and paste it in the *HTTP HEADRES* tab in the playground. +``` +{ + "Authorization": "" +} +``` + +>NOTE: Open commerce uses session based authentication. When the first login request is sent, a session is created on the server. You can see the sesson ID in the result of the above mutation. + +## Express Middleware + +The `api-plugin-authentication` provides authentication middleware for the Reaction API. Utils included in this plugin help connect the Account-js library and the Reaction API via an Auth Token. + +### How it works + +This plugin registers the schema and resolvers that are required by account-js. + +Whenever a header with name `Authorization` is present in the request, this plugin uses account-js to get the user for that token. Then the user is attached to the `req` object. + +Also on each request, `accountsGraphQL.context` is called so that it can pass the `user` to the "internal context" of account-js. + +### Supported actions + +1. Signup +2. Login +3. Change Password +4. Forgot Password - A email is sent with a reset token url of the form `https:///?resetToken=` + +### Environment variables for api-plugin-authentication +- STORE_URL +- TOKEN_SECRET \ No newline at end of file diff --git a/guides/developers-guide/core/adding-collections.md b/guides/developers-guide/core/adding-collections.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/build-api-plugin.md b/guides/developers-guide/core/build-api-plugin.md similarity index 100% rename from guides/build-api-plugin.md rename to guides/developers-guide/core/build-api-plugin.md diff --git a/guides/developers-guide/core/creating-schemas.md b/guides/developers-guide/core/creating-schemas.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/core/environment-variables.md b/guides/developers-guide/core/environment-variables.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/core/plugin-architecture-fundamentals.md b/guides/developers-guide/core/plugin-architecture-fundamentals.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/plugins-environment-variables.md b/guides/developers-guide/core/plugins-environment-variables.md similarity index 100% rename from docs/plugins-environment-variables.md rename to guides/developers-guide/core/plugins-environment-variables.md diff --git a/guides/developers-guide/core/setting-up-ides.md b/guides/developers-guide/core/setting-up-ides.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/testing-requirements.md b/guides/developers-guide/core/testing-requirements.md similarity index 100% rename from docs/testing-requirements.md rename to guides/developers-guide/core/testing-requirements.md diff --git a/guides/developers-guide/core/using-a-graphql-client.md b/guides/developers-guide/core/using-a-graphql-client.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/core/workflows.md b/guides/developers-guide/core/workflows.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/API-plugin-package-migration.md b/guides/developers-guide/core/writing-and-running-migrations.md similarity index 100% rename from guides/API-plugin-package-migration.md rename to guides/developers-guide/core/writing-and-running-migrations.md diff --git a/guides/developers-guide/core/writing-integration-tests.md b/guides/developers-guide/core/writing-integration-tests.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/core/writing-unit-tests.md b/guides/developers-guide/core/writing-unit-tests.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/storefront/add-ability-to-log-in.md b/guides/developers-guide/storefront/add-ability-to-log-in.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/storefront/add-and-configure-apollo-client.md b/guides/developers-guide/storefront/add-and-configure-apollo-client.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/storefront/add-guest-checkout-functionality.md b/guides/developers-guide/storefront/add-guest-checkout-functionality.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/storefront/add-item-to-cart.md b/guides/developers-guide/storefront/add-item-to-cart.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/storefront/build-a-cart-page.md b/guides/developers-guide/storefront/build-a-cart-page.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/storefront/build-an-account-management-page.md b/guides/developers-guide/storefront/build-an-account-management-page.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/storefront/build-an-order-view-page.md b/guides/developers-guide/storefront/build-an-order-view-page.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/storefront/build-checkout-page.md b/guides/developers-guide/storefront/build-checkout-page.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/storefront/build-navigation-menus.md b/guides/developers-guide/storefront/build-navigation-menus.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/storefront/build-product-detail-page.md b/guides/developers-guide/storefront/build-product-detail-page.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/storefront/build-product-listing-page.md b/guides/developers-guide/storefront/build-product-listing-page.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/developers-guide/storefront/implement-cart-modification.md b/guides/developers-guide/storefront/implement-cart-modification.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/fundamentals.md b/guides/getting-started/fundamentals.md similarity index 100% rename from docs/fundamentals.md rename to guides/getting-started/fundamentals.md diff --git a/guides/quick-start-windows.md b/guides/getting-started/quick-start-windows.md similarity index 100% rename from guides/quick-start-windows.md rename to guides/getting-started/quick-start-windows.md diff --git a/guides/quick-start.md b/guides/getting-started/quick-start.md similarity index 100% rename from guides/quick-start.md rename to guides/getting-started/quick-start.md diff --git a/docs/_assets/operator-ui-product-list.png b/guides/shop-managers-guide/_assets/operator-ui-product-list.png similarity index 100% rename from docs/_assets/operator-ui-product-list.png rename to guides/shop-managers-guide/_assets/operator-ui-product-list.png diff --git a/docs/_assets/operator-ui-product-media.png b/guides/shop-managers-guide/_assets/operator-ui-product-media.png similarity index 100% rename from docs/_assets/operator-ui-product-media.png rename to guides/shop-managers-guide/_assets/operator-ui-product-media.png diff --git a/docs/_assets/reaction-admin-accounts.png b/guides/shop-managers-guide/_assets/reaction-admin-accounts.png similarity index 100% rename from docs/_assets/reaction-admin-accounts.png rename to guides/shop-managers-guide/_assets/reaction-admin-accounts.png diff --git a/docs/_assets/reaction-admin-product-archive-select.png b/guides/shop-managers-guide/_assets/reaction-admin-product-archive-select.png similarity index 100% rename from docs/_assets/reaction-admin-product-archive-select.png rename to guides/shop-managers-guide/_assets/reaction-admin-product-archive-select.png diff --git a/docs/_assets/reaction-admin-product-detail.png b/guides/shop-managers-guide/_assets/reaction-admin-product-detail.png similarity index 100% rename from docs/_assets/reaction-admin-product-detail.png rename to guides/shop-managers-guide/_assets/reaction-admin-product-detail.png diff --git a/docs/_assets/reaction-admin-product-dropdown.png b/guides/shop-managers-guide/_assets/reaction-admin-product-dropdown.png similarity index 100% rename from docs/_assets/reaction-admin-product-dropdown.png rename to guides/shop-managers-guide/_assets/reaction-admin-product-dropdown.png diff --git a/docs/_assets/reaction-admin-product-duplicate.png b/guides/shop-managers-guide/_assets/reaction-admin-product-duplicate.png similarity index 100% rename from docs/_assets/reaction-admin-product-duplicate.png rename to guides/shop-managers-guide/_assets/reaction-admin-product-duplicate.png diff --git a/docs/_assets/reaction-admin-product-make-hidden.png b/guides/shop-managers-guide/_assets/reaction-admin-product-make-hidden.png similarity index 100% rename from docs/_assets/reaction-admin-product-make-hidden.png rename to guides/shop-managers-guide/_assets/reaction-admin-product-make-hidden.png diff --git a/docs/_assets/reaction-admin-product-make-visible.png b/guides/shop-managers-guide/_assets/reaction-admin-product-make-visible.png similarity index 100% rename from docs/_assets/reaction-admin-product-make-visible.png rename to guides/shop-managers-guide/_assets/reaction-admin-product-make-visible.png diff --git a/docs/_assets/reaction-admin-product-option-add.png b/guides/shop-managers-guide/_assets/reaction-admin-product-option-add.png similarity index 100% rename from docs/_assets/reaction-admin-product-option-add.png rename to guides/shop-managers-guide/_assets/reaction-admin-product-option-add.png diff --git a/docs/_assets/reaction-admin-product-publish.png b/guides/shop-managers-guide/_assets/reaction-admin-product-publish.png similarity index 100% rename from docs/_assets/reaction-admin-product-publish.png rename to guides/shop-managers-guide/_assets/reaction-admin-product-publish.png diff --git a/docs/_assets/reaction-admin-product-tags.png b/guides/shop-managers-guide/_assets/reaction-admin-product-tags.png similarity index 100% rename from docs/_assets/reaction-admin-product-tags.png rename to guides/shop-managers-guide/_assets/reaction-admin-product-tags.png diff --git a/docs/_assets/reaction-admin-product-variant-add.png b/guides/shop-managers-guide/_assets/reaction-admin-product-variant-add.png similarity index 100% rename from docs/_assets/reaction-admin-product-variant-add.png rename to guides/shop-managers-guide/_assets/reaction-admin-product-variant-add.png diff --git a/guides/shop-managers-guide/create-a-shop.md b/guides/shop-managers-guide/create-a-shop.md new file mode 100644 index 0000000..e69de29 diff --git a/guides/shop-managers-guide/create-a-user.md b/guides/shop-managers-guide/create-a-user.md new file mode 100644 index 0000000..e69de29 diff --git a/docs/creating-organizing-products.md b/guides/shop-managers-guide/creating-organizing-products.md similarity index 100% rename from docs/creating-organizing-products.md rename to guides/shop-managers-guide/creating-organizing-products.md diff --git a/docs/fulfilling-orders.md b/guides/shop-managers-guide/fulfilling-orders.md similarity index 100% rename from docs/fulfilling-orders.md rename to guides/shop-managers-guide/fulfilling-orders.md diff --git a/docs/sending-emails.md b/guides/shop-managers-guide/sending-emails.md similarity index 100% rename from docs/sending-emails.md rename to guides/shop-managers-guide/sending-emails.md diff --git a/docs/tags-navigation--navigation-items.png b/guides/shop-managers-guide/tags-navigation--navigation-items.png similarity index 100% rename from docs/tags-navigation--navigation-items.png rename to guides/shop-managers-guide/tags-navigation--navigation-items.png diff --git a/docs/tags-navigation--shop-navigation.png b/guides/shop-managers-guide/tags-navigation--shop-navigation.png similarity index 100% rename from docs/tags-navigation--shop-navigation.png rename to guides/shop-managers-guide/tags-navigation--shop-navigation.png diff --git a/docs/tags-navigation.md b/guides/shop-managers-guide/tags-navigation.md similarity index 100% rename from docs/tags-navigation.md rename to guides/shop-managers-guide/tags-navigation.md diff --git a/how-tos/add-extra-data-to-a-product.md b/how-tos/add-extra-data-to-a-product.md new file mode 100644 index 0000000..e69de29 diff --git a/how-tos/add-translations.md b/how-tos/add-translations.md new file mode 100644 index 0000000..e69de29 diff --git a/how-tos/create-a-fulfillment-plugin.md b/how-tos/create-a-fulfillment-plugin.md new file mode 100644 index 0000000..e69de29 diff --git a/how-tos/create-a-payment-plugin.md b/how-tos/create-a-payment-plugin.md new file mode 100644 index 0000000..e69de29 diff --git a/how-tos/create-a-promotions-plugin.md b/how-tos/create-a-promotions-plugin.md new file mode 100644 index 0000000..e69de29 diff --git a/how-tos/create-a-shipping-plugin.md b/how-tos/create-a-shipping-plugin.md new file mode 100644 index 0000000..e69de29 diff --git a/how-tos/create-a-tax-plugin.md b/how-tos/create-a-tax-plugin.md new file mode 100644 index 0000000..e69de29 diff --git a/how-tos/create-an-address-validation-service.md b/how-tos/create-an-address-validation-service.md new file mode 100644 index 0000000..e69de29 diff --git a/how-tos/emit-and-listen-for-events.md b/how-tos/emit-and-listen-for-events.md new file mode 100644 index 0000000..e69de29 diff --git a/how-tos/extend-an-existing-schema.md b/how-tos/extend-an-existing-schema.md new file mode 100644 index 0000000..e69de29 diff --git a/package.json b/package.json index 77c1b45..ea47449 100644 --- a/package.json +++ b/package.json @@ -8,14 +8,14 @@ }, "repository": { "type": "git", - "url": "git+https://github.com/postlight/mailchimp-open-commerce-docs.git" + "url": "git+https://github.com/reactioncommerce/docs.git" }, "keywords": [], "author": "", "license": "ISC", "bugs": { - "url": "https://github.com/postlight/mailchimp-open-commerce-docs/issues" + "url": "https://github.com/reactioncommerce/docs/issues" }, - "homepage": "https://github.com/postlight/mailchimp-open-commerce-docs#readme", + "homepage": "https://github.com/reactioncommerce/docs#readme", "devDependencies": {} }