فهرست منبع

feat(docs): Add docs for Plugins

Michael Bromley 7 سال پیش
والد
کامیت
b10011c4ab

+ 11 - 2
docs/assets/styles/_markdown.scss

@@ -84,7 +84,16 @@ $block-border-radius: 4px;
         :last-child { margin-bottom: 0; }
     }
 
-    table tr td {
-        padding: $padding-8;
+    table {
+        width: 100%;
+        th {
+            text-align: left;
+        }
+        td, th {
+            padding: $padding-4;
+        }
+        tr:nth-child(odd) td {
+            background-color: $gray-100;
+        }
     }
 }

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

@@ -8,6 +8,7 @@
     border: 1px solid;
     border-radius: 2px;
     padding: 0 18px;
+    margin-bottom: 12px;
 
     &.primary { border-color: $color-default; background: transparentize($color-default, 0.9); }
     &.danger { border-color: $color-danger; background: transparentize($color-danger, 0.9); }

+ 1 - 1
docs/assets/styles/main.scss

@@ -177,7 +177,7 @@ ul.contents-list {
 
 .book-footer {
     height: 200px;
-    background-color: $gray-100;
+    // background-color: $gray-100;
     margin-top: 60px;
 }
 

+ 4 - 0
docs/content/docs/_index.md

@@ -5,3 +5,7 @@ showtoc: false
 ---
 
 # Vendure Documentation
+
+{{% alert "warning" %}}
+**Note**: Vendure is currently in alpha and as such, the information and APIs documented here are subject to change.
+{{% /alert %}}

+ 1 - 0
docs/content/docs/configuration/_index.md

@@ -1,6 +1,7 @@
 ---
 title: "Configuration"
 weight: 9
+showtoc: false
 ---
 
 # Vendure Configuration Docs

+ 42 - 5
docs/content/docs/getting-started.md

@@ -7,7 +7,7 @@ weight: 0
 
 ## Requirements
  
-* [Node.js](https://nodejs.org/en/) v8 or above
+* [Node.js](https://nodejs.org/en/) **v8.9.0** or above
 * An SQL database compatible with [TypeORM](http://typeorm.io/#/), i.e. MySQL, MariaDB, PostgreSQL, SQLite, Microsoft SQL Server, sql.js, Oracle.
  
 ## Installation
@@ -16,25 +16,49 @@ The following instructions describe how to run a development instance of Vendure
 
 ### Set up the database
 
-You'll need a database to store your shop data. The simplest way to try out Vendure is to use SQLite, since it does not 
-require a separate database server to work.
+You'll need a database to store your shop data.
 
+{{% tab "SQLite" %}}
+The simplest way to try out Vendure is to use SQLite, since it does not require a separate database server to work. You only need to install the [sqlite3 driver](https://www.npmjs.com/package/sqlite3) to allow Vendure to read and write to an SQLite database file:
 ```bash
 $ npm install sqlite3
+
+# or with Yarn
+$ yarn add sqlite3
+
+```
+{{% /tab %}}
+{{% tab "MySQL/MariaDB" %}}
+You'll need a MySQL or MariaDB server available to your local machine. For development we can recommend the [bitnami-docker-phpmyadmin](https://github.com/bitnami/bitnami-docker-phpmyadmin) Docker image, which is MariaDB including phpMyAdmin.
+
+In addition, you must install the [mysql driver](https://www.npmjs.com/package/mysql) for Node.js:
+```bash
+$ npm install mysql
+
+# or with Yarn
+$ yarn add mysql
+
 ```
+{{% /tab %}}
 
 ### Install ts-node
 
-This allows us to run TypeScript directly without a compilation step. Useful for development.
+**TypeScript only:** If you want to use TypeScript, [ts-node](https://www.npmjs.com/package/ts-node) allows you to run TypeScript directly without a compilation step, which is convenient for development.
 
 ```bash
 $ npm install --save-dev ts-node
+
+# or with Yarn
+$ yarn add --dev ts-node 
 ```
 
 ### Install Vendure
 
 ```bash
 $ npm install @vendure/core
+
+# or with Yarn
+$ yarn add @vendure/core
 ```
 
 ### Initialize with the Vendure CLI
@@ -43,6 +67,9 @@ Vendure includes a CLI program which can generate the initial configuration and
 
 ```bash
 $ npx vendure init
+
+# or with Yarn
+$ yarn vendure init
 ```
 
 The init command will ask a series of questions which allow the CLI to generate a configuration and index file.
@@ -51,9 +78,19 @@ The init command will ask a series of questions which allow the CLI to generate
 
 Once the init script has completed, the server can be started.
 
+{{% tab "TypeScript" %}}
+```bash
+$ npx ts-node index
+
+# or with Yarn
+$ yarn ts-node init
+```
+{{% /tab %}}
+{{% tab "JavaScript" %}}
 ```bash
-$ ts-node index
+$ node index
 ```
+{{% /tab %}}
 
 Assuming the default config settings, you can now access:
 

+ 1 - 0
docs/content/docs/graphql-api/_index.md

@@ -1,6 +1,7 @@
 ---
 title: "GraphQL API"
 weight: 3
+showtoc: false
 ---
 
 # GraphQL API Docs

+ 0 - 6
docs/content/docs/plugins.md

@@ -1,6 +0,0 @@
----
-title: "Plugins"
-weight: 2
----
- 
-# Plugins

+ 18 - 0
docs/content/docs/plugins/_index.md

@@ -0,0 +1,18 @@
+---
+title: "Plugins"
+weight: 2
+showtoc: false
+---
+ 
+# Plugins
+
+Plugins in Vendure allow one to:
+
+1. Modify the [VendureConfig]({{< relref "vendure-config" >}}) object.
+2. Extend the GraphQL API, including modifying existing types and adding completely new queries and mutations.
+3. Define new database entities and interact directly with the database.
+4. Run code before the server bootstraps, such as starting webservers.
+
+These abilities make plugins a very versatile and powerful means of implementing custom business requirements.
+
+This section details the built-in plugins which ship with Vendure as well as a guide to writing your own plugins.

+ 19 - 0
docs/content/docs/plugins/admin-ui-plugin.md

@@ -0,0 +1,19 @@
+---
+title: "AdminUiPlugin"
+---
+
+# AdminUiPlugin
+
+This plugin starts a static server for the Admin UI app, and proxies it via the `/admin/` path of the main Vendure server.
+
+The Admin UI allows you to administer all aspects of your store, from inventory management to order tracking. It is the tool used by store administrators on a day-to-day basis for the management of the store.
+
+
+```ts 
+const config: VendureConfig = {
+  // Add an instance of the plugin to the plugins array
+  plugins: [
+    new AdminUiPlugin({ port: 3002 }),
+  ],
+};
+```

+ 67 - 0
docs/content/docs/plugins/default-asset-server-plugin.md

@@ -0,0 +1,67 @@
+---
+title: "DefaultAssetServerPlugin"
+---
+
+# DefaultAssetServerPlugin
+
+The `DefaultAssetServerPlugin` serves assets (images and other files) from the local file system. It can also perform on-the-fly image transformations and caches the results for subsequent calls.
+
+```ts
+const config: VendureConfig = {
+  // Add an instance of the plugin to the plugins array
+  plugins: [
+    new DefaultAssetServerPlugin({
+      route: 'assets',
+      assetUploadDir: path.join(__dirname, 'assets'),
+      port: 4000,
+    }),
+  ],
+};
+```
+
+The full configuration is documented at [DefaultAssetServerOptions]({{< relref "default-asset-server-options" >}})
+
+## Image transformation
+
+Asset preview images can be transformed (resized & cropped) on the fly by appending query parameters to the url:
+
+`http://localhost:3000/assets/some-asset.jpg?w=500&h=300&mode=resize`
+
+The above URL will return `some-asset.jpg`, resized to fit in the bounds of a 500px x 300px rectangle.
+
+### Preview mode
+
+The `mode` parameter can be either `crop` or `resize`. See the [ImageTransformMode]({{< relref "image-transform-mode" >}}) docs for details.
+
+### Transform presets
+
+Presets can be defined which allow a single preset name to be used instead of specifying the width, height and mode. Presets are configured via the DefaultAssetServerOptions [presets property]({{< relref "default-asset-server-options" >}}#presets).
+
+For example, defining the following preset:
+
+```ts
+new DefaultAssetServerPlugin({
+  // ...
+  presets: [
+    { name: 'my-preset', width: 85, height: 85, mode: 'crop' },
+  ],
+}),
+```
+
+means that a request to:
+
+`http://localhost:3000/assets/some-asset.jpg?preset=my-preset`
+
+is equivalent to:
+
+`http://localhost:3000/assets/some-asset.jpg?w=85&h=85&mode=crop`
+
+The DefaultAssetServerPlugin comes pre-configured with the following presets:
+
+name | width | height | mode
+-----|-------|--------|-----
+tiny | 50px | 50px | crop
+thumb | 150px | 150px | crop
+small | 300px | 300px | resize
+medium | 500px | 500px | resize
+large | 800px | 800px | resize

+ 67 - 0
docs/content/docs/plugins/default-email-plugin.md

@@ -0,0 +1,67 @@
+---
+title: "DefaultEmailPlugin"
+---
+
+# DefaultEmailPlugin
+
+The DefaultEmailPlugin configures the the [EmailOptions]({{< relref "email-options" >}}) to use an [MJML](https://mjml.io/)-based email generator and presents a simplified interface for typical email requirements.
+
+```ts 
+const config: VendureConfig = {
+  // Add an instance of the plugin to the plugins array
+  plugins: [
+    new DefaultEmailPlugin({
+      templatePath: path.join(__dirname, 'vendure/email/templates'),
+      transport: {
+        type: 'smtp',
+        host: 'smtp.example.com',
+        port: 587,
+        auth: {
+          user: 'username',
+          pass: 'password',
+        }
+      },
+    }),
+  ],
+};
+```
+
+## Customizing templates
+
+Emails are generated from templates which use [MJML](https://mjml.io/) syntax. MJML is an open-source HTML-like markup language which makes the task of creating responsive email markup simple. By default, the templates are installed to `<project root>/vendure/email/templates` and can be freely edited.
+
+Dynamic data such as the recipient's name or order items are specified using [Handlebars syntax](https://handlebarsjs.com/):
+
+```HTML
+<p>Dear {{ order.customer.firstName }} {{ order.customer.lastName }},</p>
+
+<p>Thank you for your order!</p>
+
+<mj-table cellpadding="6px">
+  {{#each order.lines }}
+    <tr class="order-row">
+      <td>{{ quantity }} x {{ productVariant.name }}</td>
+      <td>{{ productVariant.quantity }}</td>
+      <td>{{ formatMoney totalPrice }}</td>
+    </tr>
+  {{/each}}
+</mj-table>
+```
+
+### Handlebars helpers
+
+The following helper functions are available for use in email templates:
+
+* `formatMoney`: Formats an amount of money (which are always stored as integers in Vendure) as a decimal, e.g. `123` => `1.23`
+* `formatDate`: Formats a Date value with the [dateformat](https://www.npmjs.com/package/dateformat) package.
+
+## Dev mode
+
+For development, the `transport` option can be replaced by `devMode: true`. Doing so configures Vendure to use the [file transport]({{< relref "file-transport-options" >}}) and outputs emails as rendered HTML files in a directory named "test-emails" which is located adjacent to the directory configured in the `templatePath`.
+
+```ts 
+new DefaultEmailPlugin({
+  templatePath: path.join(__dirname, 'vendure/email/templates'),
+  devMode: true,
+})
+```

+ 20 - 0
docs/content/docs/plugins/default-search-plugin.md

@@ -0,0 +1,20 @@
+---
+title: "DefaultSearchPlugin"
+---
+
+# DefaultSearchPlugin
+
+The DefaultSearchPlugin provides a full-text Product search based on the full-text searching capabilities of the underlying database.
+
+```ts
+const config: VendureConfig = {
+  // Add an instance of the plugin to the plugins array
+  plugins: [
+    new DefaultSearchPlugin(),
+  ],
+};
+```
+
+{{% alert "warning" %}}
+Note that the current implementation of the DefaultSearchPlugin is only implemented and tested against a MySQL/MariaDB database. In addition, the search result quality has not yet been optimized.
+{{% /alert %}}

+ 255 - 0
docs/content/docs/plugins/writing-a-vendure-plugin.md

@@ -0,0 +1,255 @@
+---
+title: "Writing a Vendure Plugin"
+weight: 0
+---
+
+# Writing a Vendure Plugin
+
+A Vendure plugin is a class which implements the [`VendurePlugin` interface]({{< relref "vendure-plugin" >}}). This interface allows the plugin to:
+
+1. Modify the [VendureConfig]({{< relref "vendure-config" >}}) object.
+2. Extend the GraphQL API, including modifying existing types and adding completely new queries and mutations.
+3. Define new database entities and interact directly with the database.
+4. Run code before the server bootstraps, such as starting webservers.
+
+## Example: RandomCatPlugin
+
+Let's learn about these capabilities by writing a plugin which defines a new database entity and GraphQL mutation.
+
+This plugin will add a new mutation, `addRandomCat`, to the GraphQL API which allows us to conveniently link a random cat image from [http://random.cat](http://random.cat) to any product in out catalog.
+
+### Step 1: Define a new custom field
+
+We need a place to store the url of the cat image, so we will add a custom field to the Product entity. This is done by modifying the VendureConfig object in the the plugin's [`configure` method]({{< relref "vendure-plugin" >}}#configure):
+
+```ts 
+import { VendurePlugin } from '@vendure/core';
+
+export class RandomCatPlugin implements VendurePlugin {
+    configure(config) {
+        config.customFields.Product.push({
+            type: 'string',
+            name: 'catImageUrl',
+        });
+        return config;
+    }
+}
+```
+
+### Step 2: Create a service to fetch the data
+
+Now we will create a service which is responsible for making the HTTP call to the random.cat API and returning the URL of a random cat image:
+
+```ts 
+import http from 'http';
+import { Injectable } from '@nestjs/common';
+
+@Injectable()
+export class CatFetcher {
+    /** Fetch a random cat image url from random.cat */
+    fetchCat(): Promise<string> {
+        return new Promise((resolve) => {
+            http.get('http://aws.random.cat/meow', (resp) => {
+                let data = '';
+                resp.on('data', chunk => data += chunk);
+                resp.on('end', () => resolve(JSON.parse(data).file));
+            });
+        });
+    }
+}
+```
+
+{{% alert %}}
+The `@Injectable()` decorator is part of the underlying [Nest framework](https://nestjs.com/), and allows us to make use of Nest's powerful dependency injection features. In this case, we'll be able to inject the `CatFetcher` service into the resolver which we will soon create.
+{{% /alert %}}
+
+
+{{% alert "warning" %}}
+To use decorators with TypeScript, you must set the "emitDecoratorMetadata" and "experimentalDecorators" compiler options to `true` in your tsconfig.json file.
+{{% /alert %}}
+
+### Step 3: Extend the GraphQL API
+
+Next we will extend the Vendure GraphQL API to add our new mutation. This is done by implementing the [`defineGraphQlTypes` method](({{< relref "vendure-plugin" >}}#definegraphqltypes)) in our plugin.
+
+```ts 
+import gql from 'graphql-tag';
+
+export class RandomCatPlugin implements VendurePlugin {
+
+    configure(config) {
+        // as above
+    }
+
+    defineGraphQlTypes() {
+        return gql`
+            extend type Mutation {
+                addRandomCat(id: ID!): Product!
+            }
+        `;
+    }
+}
+```
+
+### Step 4: Create a resolver
+
+Now that we've defined the new mutation, we'll need a resolver function to handle it. To do this, we'll create a new resolver class, following the [Nest GraphQL resolver architecture](https://docs.nestjs.com/graphql/resolvers-map). In short, this will be a class which has the `@Resolver()` decorator and features a method to handle our new mutation.
+
+```ts 
+import { Args, Mutation, Resolver } from '@nestjs/graphql';
+import { Ctx, Allow, ProductService, RequestContext } from '@vendure/core';
+
+@Resolver()
+export class RandomCatResolver {
+
+    constructor(private productService: ProductService, private catFetcher: CatFetcher) {}
+
+    @Mutation()
+    @Allow(Permission.UpdateCatalog)
+    async addRandomCat(@Ctx() ctx: RequestContext, @Args() args) {
+        const catImageUrl = await this.catFetcher.fetchCat();
+        return this.productService.update(ctx, {
+            id: args.id,
+            customFields: { catImageUrl },
+        });
+    }
+}
+```
+
+Some explanations of this code are in order:
+
+* The `@Resolver()` decorator tells Nest that this class contains GraphQL resolvers.
+* We are able to use Nest's dependency injection to inject an instance of our `CatFetcher` class into the constructor of the resolver. We are also injecting an instance of the built-in `ProductService` class, which is responsible for operations on Products.
+* We use the `@Mutation()` decorator to mark this method as a resolver for a mutation with the corresponding name.
+* The `@Allow()` decorator enables us to define permissions restrictions on the mutation. Only those users whose permissions include `UpdateCatalog` may perform this operation. For a full list of available permissions, see the [Permission enum]({{< relref "enums" >}}#permission).
+* The `@Ctx()` decorator injects the current `RequestContext` into the resolver. This provides information about the current request such as the current Session, User and Channel. It is required by most of the internal service methods.
+* The `@Args()` decorator injects the arguments passed to the mutation as an object.
+
+### Step 5: Export the providers
+
+In order that the Vendure server (and the underlying Nest framework) is able to use the `CatFetcher` and `RandomCatResolver` classes, we must export them via the [`defineProviders` method](({{< relref "vendure-plugin" >}}#defineproviders)) in our plugin:
+
+```ts 
+export class RandomCatPlugin implements VendurePlugin {
+
+    configure(config) {
+        // as above
+    }
+
+    defineGraphQlTypes() {
+        // as above
+    }
+
+    defineProviders() {
+        return [CatFetcher, RandomCatResolver];
+    }
+}
+```
+
+### Step 6: Add the plugin to the Vendure config
+
+Finally we need to add an instance of our plugin to the config object with which we bootstrap out Vendure server:
+
+```ts 
+import { bootstrap } from '@vendure/core';
+
+bootstrap({
+    // .. config options
+    plugins: [
+        new RandomCatPlugin(),
+    ],
+});
+```
+
+### Step 7: Test the plugin
+
+Once we have started the Vendure server with the new config, we should be able to send the following GraphQL query:
+
+```GraphQL
+mutation {
+  addRandomCat(id: "1") {
+    id
+    name
+    customFields {
+      catImageUrl
+    }
+  }
+}
+```
+
+which should yield the following response:
+
+```JSON 
+{
+  "data": {
+    "addRandomCat": {
+      "id": "1",
+      "name": "Spiky Cactus",
+      "customFields": {
+        "catImageUrl": "https://purr.objects-us-east-1.dream.io/i/OoNx6.jpg"
+      }
+    }
+  }
+}
+```
+
+### Full example plugin
+
+```ts
+import { Injectable } from '@nestjs/common';
+import { Args, Mutation, Resolver } from '@nestjs/graphql';
+import gql from 'graphql-tag';
+import http from 'http';
+import { Allow, Ctx, Permission, ProductService, RequestContext, VendureConfig, VendurePlugin } from '@vendure/core';
+
+export class RandomCatPlugin implements VendurePlugin {
+    configure(config: Required<VendureConfig>) {
+        config.customFields.Product.push({
+            type: 'string',
+            name: 'catImageUrl',
+        });
+        return config;
+    }
+
+    defineGraphQlTypes() {
+        return gql`
+            extend type Mutation {
+                addRandomCat(id: ID!): Product!
+            }
+        `;
+    }
+
+    defineProviders() {
+        return [CatFetcher, RandomCatResolver];
+    }
+}
+
+@Injectable()
+export class CatFetcher {
+    /** Fetch a random cat image url from random.cat */
+    fetchCat(): Promise<string> {
+        return new Promise((resolve) => {
+            http.get('http://aws.random.cat/meow', (resp) => {
+                let data = '';
+                resp.on('data', chunk => data += chunk);
+                resp.on('end', () => resolve(JSON.parse(data).file));
+            });
+        });
+    }
+}
+
+@Resolver()
+export class RandomCatResolver {
+    constructor(private productService: ProductService, private catFetcher: CatFetcher) {}
+
+    @Mutation()
+    @Allow(Permission.UpdateCatalog)
+    async addRandomCat(@Ctx() ctx: RequestContext, @Args() args) {
+        const catImageUrl = await this.catFetcher.fetchCat();
+        return this.productService.update(ctx, {
+            id: args.id,
+            customFields: { catImageUrl },
+        });
+    }
+}
+```