Browse Source

docs: Updates to docs with new APIs etc

Michael Bromley 2 years ago
parent
commit
7866dbf05d
40 changed files with 328 additions and 196 deletions
  1. 4 0
      docs/assets/styles/_markdown.scss
  2. 1 0
      docs/assets/styles/_shortcodes.scss
  3. 2 7
      docs/content/developer-guide/channels/index.md
  4. 1 1
      docs/content/developer-guide/configuration.md
  5. BIN
      docs/content/developer-guide/customizing-models/custom-fields-data-table.webp
  6. BIN
      docs/content/developer-guide/customizing-models/custom-fields-ui.webp
  7. 90 32
      docs/content/developer-guide/customizing-models/index.md
  8. BIN
      docs/content/developer-guide/customizing-the-order-process/custom-order-ui.webp
  9. BIN
      docs/content/developer-guide/customizing-the-order-process/custom_order_ui.jpg
  10. 5 3
      docs/content/developer-guide/customizing-the-order-process/index.md
  11. 68 3
      docs/content/developer-guide/logging.md
  12. BIN
      docs/content/developer-guide/multi-tenant/channel-selector.png
  13. BIN
      docs/content/developer-guide/multi-tenant/create-admin.png
  14. BIN
      docs/content/developer-guide/multi-tenant/create-channel.png
  15. BIN
      docs/content/developer-guide/multi-tenant/create-role.png
  16. 0 91
      docs/content/developer-guide/multi-tenant/index.md
  17. 1 1
      docs/content/developer-guide/overview/_index.md
  18. 50 15
      docs/content/developer-guide/payment-integrations/index.md
  19. BIN
      docs/content/developer-guide/stock-control/global-stock-control.jpg
  20. BIN
      docs/content/developer-guide/stock-control/global-stock-control.webp
  21. 1 1
      docs/content/developer-guide/stock-control/index.md
  22. 88 31
      docs/content/developer-guide/testing.md
  23. 1 1
      docs/content/migrating-from-v1/breaking-api-changes.md
  24. 3 3
      docs/content/plugins/_index.md
  25. BIN
      docs/content/user-guide/catalog/screen-facet-add.webp
  26. BIN
      docs/content/user-guide/catalog/screen-facet-list.webp
  27. BIN
      docs/content/user-guide/catalog/screen-inventory.webp
  28. BIN
      docs/content/user-guide/customers/screen-customer-group.webp
  29. BIN
      docs/content/user-guide/localization/screen-ui-language.webp
  30. BIN
      docs/content/user-guide/orders/screen-fulfillment-shipped.webp
  31. BIN
      docs/content/user-guide/orders/screen-fulfillment.webp
  32. BIN
      docs/content/user-guide/orders/screen-modification.webp
  33. BIN
      docs/content/user-guide/orders/screen-modify-button.webp
  34. BIN
      docs/content/user-guide/orders/screen-refund-button.webp
  35. BIN
      docs/content/user-guide/orders/screen-settle-payment.webp
  36. BIN
      docs/content/user-guide/settings/screen-shipping-test.webp
  37. BIN
      docs/content/user-guide/settings/screen-translations.webp
  38. 2 2
      docs/layouts/partials/top-bar.html
  39. 4 4
      docs/layouts/shortcodes/alert.html
  40. 7 1
      packages/core/src/config/order/default-order-process.ts

+ 4 - 0
docs/assets/styles/_markdown.scss

@@ -229,4 +229,8 @@ $block-border-radius: 4px;
             font-style: italic;
         }
     }
+
+    .highlight + .highlight {
+        margin-top: 16px;
+    }
 }

+ 1 - 0
docs/assets/styles/_shortcodes.scss

@@ -123,6 +123,7 @@
     margin-left: -8px;
     margin-bottom: 32px;
     .tab-controls {
+        margin-bottom: 6px;
         button.tab-control {
             color: $gray-600;
             border: none;

+ 2 - 7
docs/content/developer-guide/channels/index.md

@@ -7,10 +7,10 @@ showtoc: true
 
 Channels are a feature of Vendure which allows multiple sales channels to be represented in a single Vendure instance. A Channel allows you to:
 
-* Set a channel-specific currency, language, tax and shipping defaults
+* Set Channel-specific currency, language, tax and shipping defaults
 * Assign only specific Products to the Channel (with Channel-specific prices)
 * Create Administrator roles limited to the Channel
-* Assign only specific StockLocations, Assets, Facets, Collections, Promotions, ShippingMethods & PaymentMethods to the Channel
+* Assign specific StockLocations, Assets, Facets, Collections, Promotions, ShippingMethods & PaymentMethods to the Channel
 * Have Orders and Customers associated with specific Channels.
 
 Every Vendure server always has a **default Channel**, which contains _all_ entities. Subsequent channels can then contain a subset of the above entities.
@@ -67,8 +67,3 @@ This is the most advanced use of channels. For a detailed guide to this use-case
 
 To specify which channel to use when making an API call, set the `'vendure-token'` header to match the token of the desired Channel.
 
-## Multi-Tenant (Marketplace) Support
-
-Channels can also be used to implement a multi-tenant or marketplace application. In such a setup, each merchant would have their own dedicated Channel and would be granted permissions on that Channel only.
- 
-For a detailed guide on how this would be set up, see our [Multi-Tenant guide]({{< relref "multi-tenant" >}}).

+ 1 - 1
docs/content/developer-guide/configuration.md

@@ -110,7 +110,7 @@ export const config: VendureConfig = {
 
 ### Connecting to the database
 
-The database connection is configured with the `VendureConfig.dbConnectionOptions` object. This object is actually the [TypeORM configuration object](https://typeorm.io/#/connection-options) and is passed directly to TypeORM.
+The database connection is configured with the `VendureConfig.dbConnectionOptions` object. This object is actually the [TypeORM DataSourceOptions object](https://typeorm.io/data-source-options) and is passed directly to TypeORM.
 
 Example:
 

BIN
docs/content/developer-guide/customizing-models/custom-fields-data-table.webp


BIN
docs/content/developer-guide/customizing-models/custom-fields-ui.webp


+ 90 - 32
docs/content/developer-guide/customizing-models.md → docs/content/developer-guide/customizing-models/index.md

@@ -1,8 +1,8 @@
 ---
-title: "Customizing Models"
+title: 'Customizing Models'
 showtoc: true
 ---
- 
+
 # Customizing Models with custom fields
 
 Custom fields allow you to add your own custom data properties to many of the Vendure entities. The entities which may have custom fields defined are listed in the [CustomFields documentation]({{< relref "/typescript-api/custom-fields" >}})
@@ -29,7 +29,10 @@ With the example config above, the following will occur:
 
 1. The database schema will be altered, and a column will be added for each custom field. **Note: changes to custom fields require a database migration**. See the [Migrations guide]({{< relref "migrations" >}}).
 2. The GraphQL APIs will be modified to add the custom fields to the `Product` and `User` types respectively.
-3. If you are using the [admin-ui-plugin]({{< relref "/typescript-api/core-plugins/admin-ui-plugin" >}}), the Admin UI detail pages will now contain form inputs to allow the custom field data to be added or edited.
+3. If you are using the [admin-ui-plugin]({{< relref "/typescript-api/core-plugins/admin-ui-plugin" >}}), the Admin UI detail pages will now contain form inputs to allow the custom field data to be added or edited, and the list view data tables will allow custom field columns to be added, sorted and filtered. 
+
+
+{{< figure src="custom-fields-data-table.webp" >}}
 
 The values of the custom fields can then be set and queried via the GraphQL APIs:
 
@@ -42,7 +45,7 @@ mutation {
       downloadable: true,
     }
     translations: [
-      { languageCode: en, customFields: { shortName: "foo" } }  
+      { languageCode: en, customFields: { shortName: "foo" } }
     ]
   }) {
     id
@@ -60,7 +63,7 @@ mutation {
 The following types are available for custom fields:
 
 | Type           | Description                  | Example                                   |
-|----------------|------------------------------|-------------------------------------------|
+| -------------- | ---------------------------- | ----------------------------------------- |
 | `string`       | Short string data            | url, label                                |
 | `localeString` | Localized short strings      | localized url                             |
 | `text`         | Long text data               | extended product info, json config object |
@@ -85,9 +88,9 @@ const config = {
   // ...
   customFields: {
     ProductVariant: [
-      { 
+      {
         name: 'rrp',
-        type: 'int', 
+        type: 'int',
         ui: { component: 'currency-form-input' },
       },
     ]
@@ -97,7 +100,62 @@ const config = {
 
 The built-in form inputs are listed in the [DefaultFormConfigHash docs]({{< relref "default-form-config-hash" >}}).
 
-If you want to use a completely custom form input component which is not provided by the Admin UI, you'll need to create a plugin which [extends the Admin UI]({{< relref "extending-the-admin-ui" >}}) with [custom form inputs]({{< relref "custom-form-inputs" >}}). 
+If you want to use a completely custom form input component which is not provided by the Admin UI, you'll need to create a plugin which [extends the Admin UI]({{< relref "extending-the-admin-ui" >}}) with [custom form inputs]({{< relref "custom-form-inputs" >}}).
+
+Here's an example config demonstrating several ways to customize the UI controls for custom fields:
+
+```TypeScript
+import { LanguageCode, VendureConfig } from '@vendure/core';
+
+const config: VendureConfig = {
+  // ...
+  customFields: {
+    Product: [
+      // Rich text editor
+      {name: 'additionalInfo', type: 'text', ui: {component: 'rich-text-form-input'}},
+      // JSON editor
+      {name: 'specs', type: 'text', ui: {component: 'json-editor-form-input'}},
+      // Numeric with suffix
+      {
+        name: 'weight',
+        type: 'int',
+        ui: {component: 'number-form-input', suffix: 'g'},
+      },
+      // Currency input
+      {
+        name: 'RRP',
+        type: 'int',
+        ui: {component: 'currency-form-input'},
+      },
+      // Select with options
+      {
+        name: 'pageType',
+        type: 'string',
+        ui: {
+          component: 'select-form-input',
+          options: [
+            {value: 'static', label: [{languageCode: LanguageCode.en, value: 'Static'}]},
+            {value: 'dynamic', label: [{languageCode: LanguageCode.en, value: 'Dynamic'}]},
+          ],
+        },
+      },
+      // Text with prefix
+      {
+        name: 'link',
+        type: 'string',
+        ui: {
+          component: 'text-form-input',
+          prefix: 'https://',
+        },
+      },
+    ],
+  },
+};
+```
+
+and the resulting UI:
+
+{{< figure src="custom-fields-ui.webp" >}}
 
 ## Tabbed custom fields
 
@@ -131,7 +189,7 @@ OrderLine: [
     label: [
       {
         languageCode: LanguageCode.en,
-        value: 'The text to engrave on the product' 
+        value: 'The text to engrave on the product'
       },
     ],
   },
@@ -166,11 +224,11 @@ mutation {
 Furthermore, the values of these OrderLine custom fields can even be used to modify the price. This is done by defining a custom [OrderItemPriceCalculationStrategy]({{< relref "order-item-price-calculation-strategy" >}}):
 
 ```TypeScript
-import { RequestContext, PriceCalculationResult, 
+import { RequestContext, PriceCalculationResult,
   ProductVariant, OrderItemPriceCalculationStrategy } from '@vendure/core';
 
 export class EngravingPriceStrategy implements OrderItemPriceCalculationStrategy {
-  
+
   calculateUnitPrice(
     ctx: RequestContext,
     productVariant: ProductVariant,
@@ -189,7 +247,6 @@ export class EngravingPriceStrategy implements OrderItemPriceCalculationStrategy
 }
 ```
 
-
 ## TypeScript Typings
 
 Because custom fields are generated at run-time, TypeScript has no way of knowing about them based on your
@@ -197,20 +254,20 @@ VendureConfig. Consider the example above - let's say we have a [plugin]({{< rel
 access the custom field values on a Product entity.
 
 Attempting to access the custom field will result in a TS compiler error:
- 
+
 ```TypeScript {hl_lines=[12,13]}
 import { RequestContext, TransactionalConnection, ID, Product } from '@vendure/core';
 
 export class MyService {
-  constructor(private connection: TransactionalConnection) {} 
+  constructor(private connection: TransactionalConnection) {}
 
   async getInfoUrl(ctx: RequestContext, productId: ID) {
     const product = await this.connection
       .getRepository(ctx, Product)
       .findOne(productId);
-    
-    return product.customFields.infoUrl; 
-  }                           // ^ TS2339: Property 'infoUrl' 
+
+    return product.customFields.infoUrl;
+  }                           // ^ TS2339: Property 'infoUrl'
 }                             // does not exist on type 'CustomProductFields'.
 ```
 
@@ -219,7 +276,8 @@ The "easy" way to solve this is to assert the `customFields` object as `any`:
 ```TypeScript
 return (product.customFields as any).infoUrl;
 ```
-However, this sacrifices type safety. To make our custom fields type-safe we can take advantage of a couple of more advanced TypeScript features - [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#merging-interfaces) and  [ambient modules](https://www.typescriptlang.org/docs/handbook/modules.html#ambient-modules). This allows us to extend the built-in `CustomProductFields` interface to add our custom fields to it:
+
+However, this sacrifices type safety. To make our custom fields type-safe we can take advantage of a couple of more advanced TypeScript features - [declaration merging](https://www.typescriptlang.org/docs/handbook/declaration-merging.html#merging-interfaces) and [ambient modules](https://www.typescriptlang.org/docs/handbook/modules.html#ambient-modules). This allows us to extend the built-in `CustomProductFields` interface to add our custom fields to it:
 
 ```TypeScript
 // types.ts
@@ -279,7 +337,7 @@ Labels and descriptions can be specified to supply more information about the pu
 ```TypeScript
 Product: [
   {
-    name: 'extendedDescription', 
+    name: 'extendedDescription',
     type: 'localeString',
     label: [
       { languageCode: LanguageCode.en, value: 'Extended description' },
@@ -301,7 +359,7 @@ A custom field may hold an array of values by setting the `list` property to `tr
 ```TypeScript
 Product: [
   {
-    name: 'keywords', 
+    name: 'keywords',
     type: 'localeString',
     list: true,
   },
@@ -315,13 +373,13 @@ Certain custom field types may be configured with validation parameters:
 ```TypeScript
 Product: [
   {
-    name: 'partCode', 
+    name: 'partCode',
     type: 'string',
     pattern: '^[0-9]{4}\-[A-Z]{3-4}$'
   },
   {
-    name: 'location', 
-    type: 'string', 
+    name: 'location',
+    type: 'string',
     options: [
       { value: 'warehouse1' },
       { value: 'warehouse2' },
@@ -329,11 +387,11 @@ Product: [
     ]
   },
   {
-    name: 'weight', 
-    type: 'int', 
+    name: 'weight',
+    type: 'int',
     min: 0,
     max: 9999,
-    step: 1,  
+    step: 1,
   },
 ]
 ```
@@ -345,7 +403,7 @@ For even more control over validation, a `validate` function may be provided to
 ```TypeScript
 Product: [
   {
-    name: 'partCode', 
+    name: 'partCode',
     type: 'string',
     validate: async(value, injector) => {
       const partCodeService = injector.get(PartCodeService);
@@ -360,11 +418,11 @@ Product: [
 
 ### public, readonly & internal
 
-Some custom fields may be used internally in your business logic, or for integration with external systems. In this case the can restrict access to the information they contain. In this example, the Customer entity has an externalId relating to an external integration. 
+Some custom fields may be used internally in your business logic, or for integration with external systems. In this case the can restrict access to the information they contain. In this example, the Customer entity has an externalId relating to an external integration.
 
-* `public: false` means that it will not be exposed via the Shop API.
-* `readonly: true` means it will be exposed, but cannot be updated via the Admin API. It can only be changed programmatically in plugin code.
-* `internal: false` - means the field _will_ be exposed via the GraphQL APIs (in this case on the Admin API due to the `public: false` setting). If it was set to `internal: true`, then the field would not be exposed _at all_ in either of the GraphQL APIs, and will not be visible in the Admin UI. Internal custom fields are useful for purely internal implementation details.
+-   `public: false` means that it will not be exposed via the Shop API.
+-   `readonly: true` means it will be exposed, but cannot be updated via the Admin API. It can only be changed programmatically in plugin code.
+-   `internal: false` - means the field _will_ be exposed via the GraphQL APIs (in this case on the Admin API due to the `public: false` setting). If it was set to `internal: true`, then the field would not be exposed _at all_ in either of the GraphQL APIs, and will not be visible in the Admin UI. Internal custom fields are useful for purely internal implementation details.
 
 ```TypeScript
 Customer: [
@@ -390,7 +448,7 @@ Customer: [
     entity: Asset,
     // may be omitted if the entity name matches the GraphQL type name,
     // which is true for all built-in entities.
-    graphQLType: 'Asset', 
+    graphQLType: 'Asset',
     // Whether to "eagerly" load the relation
     // See https://typeorm.io/#/eager-and-lazy-relations
     eager: false,

BIN
docs/content/developer-guide/customizing-the-order-process/custom-order-ui.webp


BIN
docs/content/developer-guide/customizing-the-order-process/custom_order_ui.jpg


+ 5 - 3
docs/content/developer-guide/customizing-the-order-process/index.md

@@ -59,17 +59,19 @@ And then add this configuration to our main VendureConfig:
 
 ```TypeScript
 // vendure-config.ts
-import { VendureConfig } from '@vendure/core';
+import { defaultOrderProcess, VendureConfig } from '@vendure/core';
 import { customerValidationProcess } from './customer-validation-process';
 
 export const config: VendureConfig = {
   // ...
   orderOptions: {
-    process: [customerValidationProcess],
+    process: [defaultOrderProcess, customerValidationProcess],
   },
 };
 ```
 
+Note that we also include the `defaultOrderProcess` in the array, otherwise we will lose all the default states and transitions.
+
  To add multiple new States you need to extend the generic type like this:
  ```TypeScript
 import { OrderProcess } from '@vendure/core';
@@ -145,4 +147,4 @@ This technique uses advanced TypeScript features - [declaration merging](https:/
 If you have defined custom order states, the Admin UI will allow you to manually transition an 
 order from one state to another:
 
-{{< figure src="./custom_order_ui.jpg" >}}
+{{< figure src="./custom-order-ui.webp" >}}

+ 68 - 3
docs/content/developer-guide/logging.md

@@ -5,6 +5,71 @@ showtoc: true
 
 # Logging
 
-- log levels of default logger
-- To log database queries: set `dbConnectionOptions.logging: true` or eg `['query']` and the set the logLevel to Debug.
-- 
+Logging allows you to see what is happening inside the Vendure server. It is useful for debugging and for monitoring the health of the server in production.
+
+In Vendure, logging is configured using the `logger` property of the [VendureConfig]({{< relref "vendure-config" >}}) object. The logger must implement the [VendureLogger]({{< relref "vendure-logger" >}}) interface.
+
+See also: [Implementing a custom logger]({{< relref "logger" >}}#implementing-a-custom-logger)
+
+## Log levels
+
+Vendure uses 5 log levels, in order of increasing severity:
+
+| Level     | Description                                                                                              |
+|-----------|----------------------------------------------------------------------------------------------------------|
+| `Debug`   | The most verbose level, used for debugging purposes. The output can be very noisy at this level          |
+| `Verbose` | More information than the Info level, but less than `Debug`                                              |
+| `Info`    | General information about the normal running of the server                                               |
+| `Warning` | Issues which might need attention or action, but which do not prevent the server from continuing to run. |
+| `Error`   | Errors which should be investigated and handled; something has gone wrong.                               |
+
+
+## DefaultLogger
+
+Vendure ships with a [DefaultLogger]({{< relref "default-logger" >}}) which logs to the console (process.stdout). It can be configured with the desired log level:
+
+```TypeScript
+import { DefaultLogger, VendureConfig } from '@vendure/core';
+
+const config: VendureConfig = {
+    // ...
+    logger: new DefaultLogger({ level: LogLevel.Debug }),
+};
+```
+
+## Logging database queries
+
+To log database queries, set the `logging` property of the `dbConnectionOptions` as well as setting the logger to `Debug` level.
+
+```TypeScript
+import { DefaultLogger, LogLevel, VendureConfig } from '@vendure/core';
+
+const config: VendureConfig = {
+    // ...
+    logger: new DefaultLogger({ level: LogLevel.Debug }),
+    dbConnectionOptions: {
+        // ... etc
+        logging: true,
+        
+        // You can also specify which types of DB events to log:
+        // logging: ['error', 'warn', 'schema', 'query', 'info', 'log'],
+    },
+};
+```
+
+More information about the `logging` option can be found in the [TypeORM logging documentation](https://typeorm.io/logging).
+
+## Logging in your own plugins
+
+When you extend Vendure by creating your own plugins, it's a good idea to log useful information about what your plugin is doing. To do this, you need to import the [Logger]({{< relref "logger" >}}) class from `@vendure/core` and use it in your plugin:
+
+```TypeScript
+import { Logger } from '@vendure/core';
+
+// It is customary to define a logger context for your plugin
+// so that the log messages can be easily identified.
+const loggerCtx = 'MyPlugin';
+
+// somewhere in your code
+Logger.info(`My plugin is doing something!`, loggerCtx);
+```

BIN
docs/content/developer-guide/multi-tenant/channel-selector.png


BIN
docs/content/developer-guide/multi-tenant/create-admin.png


BIN
docs/content/developer-guide/multi-tenant/create-channel.png


BIN
docs/content/developer-guide/multi-tenant/create-role.png


+ 0 - 91
docs/content/developer-guide/multi-tenant/index.md

@@ -1,91 +0,0 @@
----
-title: 'Multi-Tenant'
-showtoc: true
----
-
-# Multi-tenancy Support
-
-Vendure supports multi-tenant headless commerce solutions through its powerful [Channels]({{< relref "channels" >}}) feature.
-
-Channels allow us to segment our business entities per tenant. To do this, you would create a separate Channel per tenant.
-
-## Creating Tenants
-
-Let's say we want to use a single Vendure instance to run 2 separate e-commerce businesses: **Ace Parts** and **Best Choice**. First we need to create a new Channel for each:
-
-{{< figure src="create-channel.png" title="Creating a new Channel via the Admin UI" >}}
-
-Do this for both of our tenants: `ace-parts` and `best-choice`.
-
-## Defining Roles
-
-The next requirement is being able to create Administrators who have permissions only for a given tenant. This is supported by Vendure's role-based access control system.
-
-First we will create a new Role, and grant all permissions on the `ace-parts` Channel only:
-
-{{< figure src="create-role.png" title="Creating a Channel-specific Role" >}}
-
-Next we create a new Administrator, and assign the Role that was just created.
-
-{{< figure src="create-admin.png" title="Creating a Channel-specific Administrator" >}}
-
-Repeat the steps of creating a Role and Administrator for the `best-choice` Channel.
-
-Now you've successfully set up your two tenants and you have an admin account for each.
-
-## Mechanics of Multi-Tenancy
-
-Each tenant is modelled as a Channel, and both of our new Channels can be considered "children" of the **default Channel**. That is, the default Channel contains every Product, Customer, Order, Promotion etc., no matter which Channel they were created in.
-
-{{< figure src="../channels/channels_diagram.png" >}}
-
-In this way, the default Channel can be used by the superadmin account to get an overview of the entire instance, whereas the admins of each tenant will only have access to the entities assigned to their respective Channels.
-
-The following entities are "channel-aware", i.e. they can be assigned to a specific Channel:
-
-* Asset
-* Collection
-* Customer
-* Facet/FacetValue
-* Order
-* PaymentMethod
-* Product/ProductVariant
-* Promotion
-* Role
-* ShippingMethod
-
-In the Admin UI, you can switch between active Channels using the switcher component in the top bar:
-
-{{< figure src="channel-selector.png" title="Switching between Channels" >}}
-
-For example, switching to the `ace-parts` Channel, and then creating a new Product will assign that new Product to the `ace-parts` Channel (_and_ the default Channel, since _everything_ is assigned to the default Channel).
-
-{{< alert "warning" >}}
-**Note:** Care must be taken if you log in with the superadmin account in the default Channel, especially with regard to prices and currencies.
-
-See more details see the Channels guide on [Channels, currencies & prices]({{< relref "/developer-guide/channels" >}}#channels-currencies--prices), and in particular the [multiple shops use-cases]({{< relref "/developer-guide/channels" >}}#use-case-multiple-separate-shops)
-{{< /alert >}}
-
-## The Storefront
-
-Your storefront applications will need to specify which channel they are interested in. This is done by adding a **query parameter** or **header** to each API requests, with the key being `vendure-token` and the value being the target Channel's `token` property.
-
-```text
-https://my-vendure-server.com/shop-api?vendure-token=best-choice
-```
-
-## Determining the Active Channel
-
-When developing plugins and writing custom server code in general, you'll often want to know the Channel that the current request is using. This can be determined from the [RequestContext.channel]({{< relref "/typescript-api/request/request-context" >}}#channel) property.
-
-```TypeScript
-createPayment: async (ctx, order, amount, args) => {
-  if (ctx.channel.code === 'ace-parts') {
-    // Use the Ace Parts account to process
-    // the payment  
-  } else {
-    // Use the Best Choice account to process
-    // the payment  
-  }
-}
-```

+ 1 - 1
docs/content/developer-guide/overview/_index.md

@@ -28,7 +28,7 @@ There are 2 separate GraphQL APIs: shop and admin.
 
 ## Database
 
-Vendure officially supports multiple databases: MySQL/MariaDB, PostgreSQL, SQLite and SQL.js, plus API-compatible cloud versions of these such as Amazon Aurora. Since Vendure uses [TypeORM](https://typeorm.io/#/) to manage data access, it can theoretically also work with other relational databases supported by TypeORM such as CockroachDB, Microsoft SQL Server, though these are as yet untested.
+Vendure officially supports multiple databases: MySQL/MariaDB, PostgreSQL, SQLite and SQL.js, plus API-compatible cloud versions of these such as Amazon Aurora. Since Vendure uses [TypeORM](https://typeorm.io/#/) to manage data access, it can theoretically also work with other relational databases supported by TypeORM such as CockroachDB, Microsoft SQL Server, though these are not guaranteed to work in all cases, as they are not covered in our testing.
 
 ## Custom Business Logic (Plugins)
 

+ 50 - 15
docs/content/developer-guide/payment-integrations/index.md

@@ -27,7 +27,16 @@ This two-step workflow can also be applied to other non-card forms of payment: e
 Payment integrations are created by defining a new [PaymentMethodHandler]({{< relref "payment-method-handler" >}}) and passing that handler into the [`paymentOptions.paymentMethodHandlers`]({{< relref "payment-options" >}}) array in the VendureConfig.
 
 ```TypeScript
-import { PaymentMethodHandler, VendureConfig, CreatePaymentResult, SettlePaymentResult, SettlePaymentErrorResult } from '@vendure/core';
+import {
+  CancelPaymentResult,
+  CancelPaymentErrorResult,
+  PaymentMethodHandler,
+  VendureConfig,
+  CreatePaymentResult,
+  SettlePaymentResult,
+  SettlePaymentErrorResult
+} from '@vendure/core';
+import { CancelPaymentErrorResult } from '@vendure/core/src/index';
 import { sdk } from 'payment-provider-sdk';
 
 /**
@@ -94,16 +103,32 @@ const myPaymentIntegration = new PaymentMethodHandler({
       }
     }
   },
+  
+  /** This is called when a payment is cancelled. */  
+  cancelPayment: async (ctx, order, payment, args): Promise<CancelPaymentResult | CancelPaymentErrorResult> => {
+    try {
+      const result = await sdk.charges.cancel({
+        apiKey: args.apiKey,
+        id: payment.transactionId,
+      });
+      return { success: true };
+    } catch (err) {
+      return {
+        success: false,
+        errorMessage: err.message,
+      }
+    }
+  },
 });
 
 /**
  * We now add this handler to our config
  */
 export const config: VendureConfig = {
-  // ...
-  paymentOptions: {
-    paymentMethodHandlers: [myPaymentIntegration],
-  },
+    // ...
+    paymentOptions: {
+        paymentMethodHandlers: [myPaymentIntegration],
+    },
 };
 ```
 
@@ -152,7 +177,17 @@ Here's an example which adds a new "Validating" state to the Payment state machi
 
 ```TypeScript
 // types.ts
-import { CustomOrderStates } from '@vendure/core';
+import {
+  defaultPaymentProcess,
+  defaultOrderprocess,
+  CustomOrderStates,
+  OrderProcess,
+  PaymentProcess,
+  PaymentMethodHandler,
+  LanguageCode,
+  OrderPlacedStrategy,
+  RequestContext
+} from '@vendure/core';
 
 /**
  * Declare your custom state in special interface to make it type-safe
@@ -230,15 +265,15 @@ class MyOrderPlacedStrategy implements OrderPlacedStrategy {
 
 // Combine the above in the VendureConfig
 export const config: VendureConfig = {
-  // ...
-  orderOptions: {
-    process: [customOrderProcess],
-    orderPlacedStrategy: new MyOrderPlacedStrategy(),
-  },
-  paymentOptions: {
-    process: [customPaymentProcess],
-    paymentMethodHandlers: [myPaymentHandler],
-  },
+    // ...
+    orderOptions: {
+        process: [defaultOrderProcess, customOrderProcess],
+        orderPlacedStrategy: new MyOrderPlacedStrategy(),
+    },
+    paymentOptions: {
+        process: [defaultPaymentProcess, customPaymentProcess],
+        paymentMethodHandlers: [myPaymentHandler],
+    },
 };
 ```
 

BIN
docs/content/developer-guide/stock-control/global-stock-control.jpg


BIN
docs/content/developer-guide/stock-control/global-stock-control.webp


+ 1 - 1
docs/content/developer-guide/stock-control/index.md

@@ -9,7 +9,7 @@ Vendure includes features to help manage your stock levels, stock allocations an
 
 Stock control is enabled globally via the Global Settings:
 
-{{< figure src="./global-stock-control.jpg" >}}
+{{< figure src="./global-stock-control.webp" >}}
 
 It can be disabled if, for example, you manage your stock with a separate inventory management system and synchronize stock levels into Vendure automatically. The setting can also be overridden at the individual ProductVariant level.
 

+ 88 - 31
docs/content/developer-guide/testing.md

@@ -18,10 +18,64 @@ The `@vendure/testing` package gives you some simple but powerful tooling for cr
 ### Install dependencies
 
 * [`@vendure/testing`](https://www.npmjs.com/package/@vendure/testing)
-* [`jest`](https://www.npmjs.com/package/jest) You'll need to install a testing framework. In this example, we will use [Jest](https://jestjs.io/), but any other framework such as Jasmine should work too.
+* [`vitest`](https://vitest.dev/) You'll need to install a testing framework. In this example, we will use [Vitest](https://vitest.dev/) as it has very good support for the modern JavaScript features that Vendure uses, and is very fast.
 * [`graphql-tag`](https://www.npmjs.com/package/graphql-tag) This is not strictly required but makes it much easier to create the DocumentNodes needed to query your server.
+* We also need to install some packages to allow us to compile TypeScript code that uses decorators:
+  - `@swc/core`
+  - `unplugin-swc`
 
-Please see the [Jest documentation](https://jestjs.io/docs/en/getting-started) on how to get set up. The remainder of this article will assume a working Jest setup configured to work with TypeScript.
+### Configure Vitest
+
+Create a `vitest.config.js` file in the root of your project:
+
+```TypeScript
+import path from 'path';
+import swc from 'unplugin-swc';
+import { defineConfig } from 'vitest/config';
+
+export default defineConfig({
+  test: {
+    include: '**/*.e2e-spec.ts',
+    typecheck: {
+      tsconfig: path.join(__dirname, 'tsconfig.e2e.json'),
+    },
+  },
+  plugins: [
+    // SWC required to support decorators used in test plugins
+    // See https://github.com/vitest-dev/vitest/issues/708#issuecomment-1118628479
+    // Vite plugin
+    swc.vite({
+      jsc: {
+        transform: {
+          // See https://github.com/vendure-ecommerce/vendure/issues/2099
+          useDefineForClassFields: false,
+        },
+      },
+    }),
+  ],
+});
+```
+
+and a `tsconfig.e2e.json` tsconfig file for the tests:
+
+```json
+{
+  "extends": "../tsconfig.json",
+  "compilerOptions": {
+    "types": ["node"],
+    "lib": ["es2015"],
+    "useDefineForClassFields": false,
+    "skipLibCheck": true,
+    "inlineSourceMap": false,
+    "sourceMap": true,
+    "allowSyntheticDefaultImports": true,
+    "experimentalDecorators": true,
+    "emitDecoratorMetadata": true,
+    "esModuleInterop": true
+  }
+}
+
+```
 
 ### Register database-specific initializers
 
@@ -29,10 +83,10 @@ The `@vendure/testing` package uses "initializers" to create the test databases
 
 ```TypeScript
 import {
-    MysqlInitializer,
-    PostgresInitializer,
-    SqljsInitializer,
-    registerInitializer,
+  MysqlInitializer,
+  PostgresInitializer,
+  SqljsInitializer,
+  registerInitializer,
 } from '@vendure/testing';
 
 const sqliteDataDir = path.join(__dirname, '__data__');
@@ -52,14 +106,15 @@ The `@vendure/testing` package exports a [`createTestEnvironment` function]({{<
 
 ```TypeScript
 import { createTestEnvironment, testConfig } from '@vendure/testing';
+import { describe } from 'vitest';
 import { MyPlugin } from '../my-plugin.ts';
 
 describe('my plugin', () => {
 
-    const { server, adminClient, shopClient } = createTestEnvironment({
-        ...testConfig,
-        plugins: [MyPlugin],
-    });
+  const { server, adminClient, shopClient } = createTestEnvironment({
+    ...testConfig,
+    plugins: [MyPlugin],
+  });
 
 });
 ```
@@ -75,21 +130,22 @@ Notice that we pass a [`VendureConfig`]({{< relref "vendure-config" >}}) object
 The [`TestServer`]({{< relref "test-server" >}}) needs to be initialized before it can be used. The `TestServer.init()` method takes an options object which defines how to populate the server:
 
 ```TypeScript
+import { beforeAll, afterAll } from 'vitest';
 import { myInitialData } from './fixtures/my-initial-data.ts';
 
 // ...
 
 beforeAll(async () => {
-    await server.init({
-        productsCsvPath: path.join(__dirname, 'fixtures/e2e-products.csv'),
-        initialData: myInitialData,
-        customerCount: 2,
-    });
-    await adminClient.asSuperAdmin();
+  await server.init({
+    productsCsvPath: path.join(__dirname, 'fixtures/e2e-products.csv'),
+    initialData: myInitialData,
+    customerCount: 2,
+  });
+  await adminClient.asSuperAdmin();
 }, 60000);
 
 afterAll(async () => {
-    await server.destroy();
+  await server.destroy();
 });
 ```
 
@@ -105,21 +161,22 @@ Now we are all set up to create a test. Let's test one of the GraphQL queries us
 
 ```TypeScript
 import gql from 'graphql-tag';
+import { it, expect } from 'vitest';
 
 it('myNewQuery returns the expected result', async () => {
-    adminClient.asSuperAdmin(); // log in as the SuperAdmin user
-
-    const query = gql`
-        query MyNewQuery($id: ID!) {
-            myNewQuery(id: $id) {
-                field1
-                field2
-            }
-        }
-    `;
-    const result = await adminClient.query(query, { id: 123 });
-
-    expect(result.myNewQuery).toEqual({ /* ... */ })
+  adminClient.asSuperAdmin(); // log in as the SuperAdmin user
+
+  const query = gql`
+    query MyNewQuery($id: ID!) {
+      myNewQuery(id: $id) {
+        field1
+        field2
+      }
+    }
+  `;
+  const result = await adminClient.query(query, { id: 123 });
+
+  expect(result.myNewQuery).toEqual({ /* ... */ })
 });
 ```
 
@@ -130,5 +187,5 @@ Running the test will then assert that your new query works as expected.
 All that's left is to run your tests to find out whether your code behaves as expected!
 
 {{< alert "warning" >}} 
-**Note:** When using **Jest**, make sure you run with the [`--runInBand` option](https://jestjs.io/docs/cli#--runinband), which ensures that your tests run in series rather than in parallel.
+**Note:** When using **Vitest**, make sure you run with the [`--runInBand` option](https://jestjs.io/docs/cli#--runinband), which ensures that your tests run in series rather than in parallel.
 {{< /alert >}}

+ 1 - 1
docs/content/migrating-from-v1/breaking-api-changes.md

@@ -165,8 +165,8 @@ If you are using the `@vendure/ui-devkit` package to generate custom ui extensio
    ```
 - The Admin UI component `vdr-product-selector` has been renamed to `vdr-product-variant-selector` to more accurately represent what it does. If you are using `vdr-product-selector` if any ui extensions code, update it to use the new selector.
 
-
 ### Other breaking API changes
+- **End-to-end tests using Jest** will likely run into issues due to our move towards using some dependencies that make use of ES modules. We have found the best solution to be to migrate tests over to [Vitest](https://vitest.dev), which can handle this and is also significantly faster than Jest. See the updated [Testing guide]({{< relref "/developer-guide/testing" >}}) for instructions on getting started with Vitest.
 - Internal `ErrorResult` classes now take a single object argument rather than multiple args.
 - All monetary values are now represented in the GraphQL APIs with a new `Money` scalar type. If you use [graphql-code-generator](https://the-guild.dev/graphql/codegen), you'll want to tell it to treat this scalar as a number:
     ```ts

+ 3 - 3
docs/content/plugins/_index.md

@@ -20,7 +20,7 @@ These abilities make plugins a very versatile and powerful means of implementing
 
 This section details the official Vendure plugins included in the main Vendure repo, as well as a guide on writing your own plugins for Vendure.
 
-{{< alert "primary" >}}
-  Vendure provides a set of **core plugins** covering common functionality such as assets handling, email sending, and search. For 
+## Core Plugins
+
+Vendure provides a set of **core plugins** covering common functionality such as assets handling, email sending, and search. For 
 documentation on these, see the [Core Plugins section]({{< relref "core-plugins" >}}).
-{{< /alert >}}

BIN
docs/content/user-guide/catalog/screen-facet-add.webp


BIN
docs/content/user-guide/catalog/screen-facet-list.webp


BIN
docs/content/user-guide/catalog/screen-inventory.webp


BIN
docs/content/user-guide/customers/screen-customer-group.webp


BIN
docs/content/user-guide/localization/screen-ui-language.webp


BIN
docs/content/user-guide/orders/screen-fulfillment-shipped.webp


BIN
docs/content/user-guide/orders/screen-fulfillment.webp


BIN
docs/content/user-guide/orders/screen-modification.webp


BIN
docs/content/user-guide/orders/screen-modify-button.webp


BIN
docs/content/user-guide/orders/screen-refund-button.webp


BIN
docs/content/user-guide/orders/screen-settle-payment.webp


BIN
docs/content/user-guide/settings/screen-shipping-test.webp


BIN
docs/content/user-guide/settings/screen-translations.webp


+ 2 - 2
docs/layouts/partials/top-bar.html

@@ -8,8 +8,8 @@
         class="absolute w-full h-1 bg-gradient-to-r from-blue-300 via-green-300 to-blue-100"
     ></div>
     {{ partial "announcement-banner" (dict "content" ` 🚨 Announcing
-    <a href="/blog/2023/04/announcing-vendure-2.0-beta/" class="text-white font-medium underline"
-        >Vendure v2 Beta<span aria-hidden="true">&rarr;</span></a
+    <a href="https://vendure.io/" class="text-white font-medium underline"
+        >Announcing Vendure v2<span aria-hidden="true">&rarr;</span></a
     >
     `) }}
     <div class="max-w-screen-2xl mx-auto px-4 py-4 sm:px-6 md:pr-10">

+ 4 - 4
docs/layouts/shortcodes/alert.html

@@ -1,10 +1,10 @@
 {{ $type := index .Params 0 | default "primary" }}
 <div x-data="{ type: '{{ $type }}' }" class='border rounded px-4 py-2 my-6'
     :class="{
-        'bg-blue-50 text-blue-900 border-blue-200': type === 'primary',
-        'bg-red-50 text-red-900 border-red-200': type === 'danger',
-        'bg-yellow-50 text-yellow-900 border-yellow-200': type === 'warning',
-        'bg-green-50 text-green-900 border-green-200': type === 'success',
+        'bg-blue-50 text-blue-900 border-blue-100': type === 'primary',
+        'bg-red-50 text-red-900 border-red-100': type === 'danger',
+        'bg-yellow-50 text-yellow-900 border-yellow-100': type === 'warning',
+        'bg-green-50 text-green-900 border-green-100': type === 'success',
     }"
 >
     <p>{{ .Inner | markdownify }}</p>

+ 7 - 1
packages/core/src/config/order/default-order-process.ts

@@ -152,6 +152,10 @@ export interface DefaultOrderProcessOptions {
  *   },
  * };
  * ```
+ * The {@link DefaultOrderProcessOptions} type defines all available options. If you require even
+ * more customization, you can create your own implementation of the {@link OrderProcess} interface.
+ *
+ *
  * @docsCategory Orders
  * @docsPage OrderProcess
  * @since 2.0.0
@@ -454,7 +458,9 @@ export function configureDefaultOrderProcess(options: DefaultOrderProcessOptions
 
 /**
  * @description
- * This is the built-in {@link OrderProcess} that ships with Vendure.
+ * This is the built-in {@link OrderProcess} that ships with Vendure. A customized version of this process
+ * can be created using the {@link configureDefaultOrderProcess} function, which allows you to pass in an object
+ * to enable/disable certain checks.
  *
  * @docsCategory Orders
  * @docsPage OrderProcess