Add documentation

This commit is contained in:
soupette 2018-12-07 17:05:25 +01:00
parent 0cc6b76b58
commit 59436ec2e9
3 changed files with 1435 additions and 646 deletions

View File

@ -0,0 +1,280 @@
# The Documentation Plugin
Now that you have created your API it's really important to document its available end-points. The documentation plugin takes out most of your pain to generate your documentation. This plugin uses [SWAGGER UI](https://swagger.io/solutions/api-documentation/) to visualize your API's documentation.
If installed, this plugin will scan all the routes available from your `./api` folder and will try to create the appropriate documentation, infer on the parameters needed to create data, the responses you will receive.
You'll be able to visualize all your end-points directly from the SWAGGER UI.
## Installation
As usual run the following in your terminal:
```
# Go to your strapi project
$ cd my-strapi-project
# Install the documentation plugin using the CLI
$ strapi install documentation
# Start your server
$ strapi start
```
Once the plugin is installed it will create a `documentation` folder in each model of your project, so you can easily modify the default generated documentation. Then, each documentation file is merged into the `full_documentation.json` located in the plugin.
The administration panel lets you configure the basic settings of this plugin.
## Architecture and Generated Files
This plugin follows the OpenApi Specifications ([0AS.3.0.2](https://swagger.io/specification/)) and generates an OpenAPI Document called `full_documentation.json`.
### Plugin's architecture
```
./plugins
└─── documentation
| └─── admin // The plugin's UI in the administration panel
| |
| └─── config // Contains the configurations of the plugin
| | └─── functions
| | | └─── bootstrap.js // Logic to create the documentation file (if needed) when the server starts
| | |
| | └─── policies
| | | └─── index.js // The plugin's policies
| | |
| | └─── routes.json // The plugin's available end-points
| | └─── settings.json // The OpenAPI Document basic settings
| |
| └─── controllers
| |
| └─── documentation // Folder containing your OpenAPI Documents
| | └─── 1.0.0 // OpenAPI Document's version
| | | └─── full_documentation.json // OpenAPI Document used by SWAGGER UI
| | |
| | └─── 2.0.0
| | └─── full_documentation.json
| |
| └─── middlewares
| | └─── documentation
| | └─── default.json
| | └─── index.js
| |
| └─── public
| | └─── index.html // SWAGGER UI
| | └─── login.html // Login page (customisable)
| |
| └─── services
| | └─── utils
| | | └─── components.json // Default components automatically added to the OpenAPI Document
| | |
| | | └─── forms.json // Form that is sent to the plugin's UI
| | | └─── parametersOptions.json // Default parameters for GET requests
| | | └─── unknownComponent.json // Component used when the algorithm can't infer the response schema
| | |
| | └─── Documentation.js // Plugin's service, most of the logic happens here
| |
| └─── package.json
| |
| └─── README.md // Contains some informations
```
### Generated files
When you start your server with this plugin installed it will automatically create the following files in your APIs (we will see how it works for the plugins). The plugin scans all the routes available in your model to create the `paths` field.
```
/my-strapi-project
└─── admin
|
└─── api
└─── Foo
└── documentation // Folder added to your model
└── 1.0.0
└── foo.json // File containing all the paths where the responses can be infered
└── unclassified.json // File containing the manually added route of your `routes.json` file
|
└── overrides // Folder to override the generated documentation
```
## Basic Configurations
This plugin comes with an interface that is available in your administration panel and a configuration file.
### Administration Panel Settings
From your administration panel you can:
- Retrieve your jwt token(1):
- Restrict the access to your API's documentation
- Regenerate or delete a documentation
- Open/Update/Delete a specific documentation version
### Manual Configurations
The OpenAPI object (here the `full_documentation.json`) has the following structure:
```
{
"openapi": "3.0.0" // do not change this version
"info": {}
"x-strapi-config": {},
"servers" {} // Your servers config (it will be automated),
"externalDocs": {},
"paths": {} // All your Api routes,
"tags": [] // Group of routes
"components": {} // Default generated components and custom ones
}
```
The `openapi`, `info`, `x-strapi-config`, `servers`, `externalDocs` and `security` fields are located in the `./plugins/documentation/config/settings.json` file. Here you can specify all your environment variables, licences, external documentation and so one...
You can add all the entries listed in the [specification](https://swagger.io/specification/).
#### Usage of the `settings.json` File
**Do not change the `openapi` field of the `settings.json`.**
> **When you change a field in the settings.json file you need to manually restart your server.**
```
{
"openapi": "3.0.0" // Do not change this version
"info": {
"version": "1.0.0" // Change this line to create a new version
"title": "DOCUMENTATION",
"description": "",
"termsOfService": "YOUR_TERMS_OF_SERVICE_URL",
"contact": {
"name": "TEAM",
"email": "contact-email@something.io",
"url": "mywebsite.io"
},
"license": {
"name": "Apache 2.0",
"url": "https://www.apache.org/licenses/LICENSE-2.0.html"
}
}
"x-strapi-config": {
"path": "/documentation", // Change this line to change to url of the doc
"showGeneratedFiles": true
},
"servers" [ // Your servers configuration you can add as many as you want
{
"url": "http://localhost:1337",
"description": "Development server"
},
{
"url": "YOUR_STAGING_SERVER",
"description": "Staging server server"
},
{
"url": "YOUR_PRODUCTION_SERVER",
"description": "Production server"
}
],
"externalDocs": {
"description": "Find out more",
"url": "https://strapi.io/documentation/3.x.x/"
},
"security": [ // This field is important to add your jwt token in the SWAGGER UI
{
"bearerAuth": []
}
]
```
(1) _Strapi is secured by default which means that most of your end-points require your user to be authenticated. You will need to paste this token in your SWAGGER UI to try out your end-points._
## Overriding the Suggested Documentation
Currently the plugin writes a json file for each API.
In order to customize the responses or to add informations to a path you need to create a file in the associated `overrides/<file-name>.json` (the name of the file matters so make sure they are similar). Then you just need to identify the path you want to modify.
You can modify the default generated tags by adding a new one at the end of the file, it works the same way for the components.
**_NOTE 1_**
Overriding the `full_documentation.json` is a bad idea since it will be regenerated each time you change a model.
**_NOTE 2_**
You can easily modify the description, summary, parameters of a path however, for a response like the `200` you will need to write the full object. Take a look at the `./plugins/users-permissions/documentation/1.0.0/overrides/users-permissions-User.json` for a complete example.
## FAQ
### How does it generate the other plugins' documentation?
In order to display a plugin's end-point in the documentation you need to add a `description` key in the `config` object.
For example this is the plugin email `routes.json` file:
```
{
"routes": [
{
"method": "POST",
"path": "/",
"handler": "Email.send",
"config": {
"policies": [],
"description": "Send an email",
"tag": {
"plugin": "email",
"name": "Email"
}
}
},
{
"method": "GET",
"path": "/environments",
"handler": "Email.getEnvironments",
"config": {
"policies": []
}
},
{
"method": "GET",
"path": "/settings/:environment",
"handler": "Email.getSettings",
"config": {
"policies": []
}
},
{
"method": "PUT",
"path": "/settings/:environment",
"handler": "Email.updateSettings",
"config": {
"policies": []
}
}
]
}
```
In this file we have only one route that we want to reference in our documentation (`/`). Usually, the tag object is used for the SWAGGER UI, it will group this route under the `Email - Email` dropdown in the documentation. Furthermore, the algorithm will try to find the model to generate the best response possible. If the model is unknown it generates a response like the following `{ foo: "string" }` that you can easily override later.
There's another property to guide the algorithm to create the best response possible, the `actionType` key.
When we can't know by the controller name the type of the returned response (like `find` and `findOne`) you can specify it with this key. Here's an example from the `./plugins/users-permissions/config/routes.json` file.
```
{
"method": "GET",
"path": "/users/me",
"handler": "User.me",
"config": {
"policies": [],
"prefix": "",
"description": "Retrieve the logged in user informations",
"tag": {
"plugin": "users-permissions",
"name": "User",
"actionType": "findOne"
}
}
}
```
### I have created a route in a common API (like product) that queries another model. How to automate this ?
You can use the `tag` key in your route. If you provide a `tag` which is a string like `"tag": "Product"` the algorithm will know that the end-point retrieves data from the **`Product`** table. Creating a tag object `{ "tag": { "name": "User", "plugin": "User-Permissions } }` will result in generating a response with the **`User`** model from the plugin users-permissions.

View File

@ -3,560 +3,6 @@
This plugin automates your API documentation creation. It basically generates a swagger file. It follows the [Open API specification version 3.0.1](https://swagger.io/specification/).
The documentation plugin is not release on npm yet, Here's how to install it.
## Installation (temporary)
**In your existing app**
- copy paste the documentation plugin into `my-app/plugins`
*NOTE: you should shut down your server while installing it...*
### Configuring the email plugin
**Path** `my-app/plugins/email`
- copy-paste the `documentation/overrides/1.0.0` files from the GitHub monorepo here's the [link](https://github.com/strapi/strapi/tree/add-description-to-plugins-routes/packages/strapi- plugin-email/documentation/overrides/1.0.0) into the `my-app/plugins/email/documentation/overrides/1.0.0`
- If you didn't add routes into the plugin copy paste the following into `my-app/plugins/email/config/routes`.
```
{
"routes": [
{
"method": "POST",
"path": "/",
"handler": "Email.send",
"config": {
"policies": [],
"description": "Send an email",
"tag": {
"plugin": "email",
"name": "Email"
}
}
},
{
"method": "GET",
"path": "/environments",
"handler": "Email.getEnvironments",
"config": {
"policies": []
}
},
{
"method": "GET",
"path": "/settings/:environment",
"handler": "Email.getSettings",
"config": {
"policies": []
}
},
{
"method": "PUT",
"path": "/settings/:environment",
"handler": "Email.updateSettings",
"config": {
"policies": []
}
}
]
}
```
### Configuring the upload plugin
- **Path** `my-app/plugins/upload`
- copy-paste the `documentation/overrides/1.0.0` files from the GitHub monorepo here's the [link](https://github.com/strapi/strapi/tree/add-description-to-plugins-routes/packages/strapi- plugin-upload/documentation/overrides/1.0.0) into the `my-app/plugins/upload/documentation/overrides/1.0.0`
- If you didn't add routes into the plugin copy paste the following into `my-app/plugins/upload/config/routes`.
```
{
"routes": [
{
"method": "POST",
"path": "/",
"handler": "Upload.upload",
"config": {
"policies": [],
"description": "Upload a file",
"tag": {
"plugin": "upload",
"name": "File"
}
}
},
{
"method": "GET",
"path": "/environments",
"handler": "Upload.getEnvironments",
"config": {
"policies": []
}
},
{
"method": "GET",
"path": "/settings/:environment",
"handler": "Upload.getSettings",
"config": {
"policies": []
}
},
{
"method": "PUT",
"path": "/settings/:environment",
"handler": "Upload.updateSettings",
"config": {
"policies": []
}
},
{
"method": "GET",
"path": "/files/count",
"handler": "Upload.count",
"config": {
"policies": [],
"description": "Retrieve the total number of uploaded files",
"tag": {
"plugin": "upload",
"name": "File"
}
}
},
{
"method": "GET",
"path": "/files",
"handler": "Upload.find",
"config": {
"policies": [],
"description": "Retrieve all file documents",
"tag": {
"plugin": "upload",
"name": "File"
}
}
},
{
"method": "GET",
"path": "/files/:_id",
"handler": "Upload.findOne",
"config": {
"policies": [],
"description": "Retrieve a single file depending on its id",
"tag": {
"plugin": "upload",
"name": "File"
}
}
},
{
"method": "GET",
"path": "/search/:id",
"handler": "Upload.search",
"config": {
"policies": [],
"description": "Search for an uploaded file",
"tag": {
"plugin": "upload",
"name": "File"
}
}
},
{
"method": "DELETE",
"path": "/files/:_id",
"handler": "Upload.destroy",
"config": {
"policies": [],
"description": "Delete an uploaded file",
"tag": {
"plugin": "upload",
"name": "File"
}
}
}
]
}
```
### Configuring the users-permissions plugin
- **Path** `my-app/plugins/users-permissions`
- copy-paste the `documentation/overrides/1.0.0` files from the GitHub monorepo here's the [link](https://github.com/strapi/strapi/tree/add-description-to-plugins-routes/packages/strapi- plugin-users-permissions/documentation/overrides/1.0.0) into the `my-app/plugins/users-permissions/documentation/overrides/1.0.0`
- If you didn't add routes into the plugin copy paste the following into `my-app/plugins/users-permissions/config/routes`.
```
{
"routes": [
{
"method": "GET",
"path": "/",
"handler": "UsersPermissions.index",
"config": {
"policies": []
}
},
{
"method": "GET",
"path": "/init",
"handler": "UsersPermissions.init",
"config": {
"policies": [],
"description": "Check if the first admin user has already been registered",
"tag": {
"plugin": "users-permissions",
"name": "Role"
}
}
},
{
"method": "GET",
"path": "/search/:id",
"handler": "UsersPermissions.searchUsers",
"config": {
"policies": [],
"description": "Search for users",
"tag": {
"plugin": "users-permissions",
"name": "User",
"actionType": "find"
}
}
},
{
"method": "GET",
"path": "/policies",
"handler": "UsersPermissions.getPolicies",
"config": {
"policies": []
}
},
{
"method": "GET",
"path": "/roles/:id",
"handler": "UsersPermissions.getRole",
"config": {
"policies": [],
"description": "Retrieve a role depending on its id",
"tag": {
"plugin": "users-permissions",
"name": "Role",
"actionType": "findOne"
}
}
},
{
"method": "GET",
"path": "/roles",
"handler": "UsersPermissions.getRoles",
"config": {
"policies": [],
"description": "Retrieve all role documents",
"tag": {
"plugin": "users-permissions",
"name": "Role",
"actionType": "find"
}
}
},
{
"method": "GET",
"path": "/routes",
"handler": "UsersPermissions.getRoutes",
"config": {
"policies": []
}
},
{
"method": "GET",
"path": "/email-templates",
"handler": "UsersPermissions.getEmailTemplate",
"config": {
"policies": []
}
},
{
"method": "PUT",
"path": "/email-templates",
"handler": "UsersPermissions.updateEmailTemplate",
"config": {
"policies": []
}
},
{
"method": "GET",
"path": "/advanced",
"handler": "UsersPermissions.getAdvancedSettings",
"config": {
"policies": []
}
},
{
"method": "PUT",
"path": "/advanced",
"handler": "UsersPermissions.updateAdvancedSettings",
"config": {
"policies": []
}
},
{
"method": "GET",
"path": "/providers",
"handler": "UsersPermissions.getProviders",
"config": {
"policies": []
}
},
{
"method": "PUT",
"path": "/providers",
"handler": "UsersPermissions.updateProviders",
"config": {
"policies": []
}
},
{
"method": "POST",
"path": "/roles",
"handler": "UsersPermissions.createRole",
"config": {
"policies": [],
"description": "Create a new role",
"tag": {
"plugin": "users-permissions",
"name": "Role",
"actionType": "create"
}
}
},
{
"method": "PUT",
"path": "/roles/:role",
"handler": "UsersPermissions.updateRole",
"config": {
"policies": [],
"description": "Update a role",
"tag": {
"plugin": "users-permissions",
"name": "Role",
"actionType": "update"
}
}
},
{
"method": "DELETE",
"path": "/roles/:role",
"handler": "UsersPermissions.deleteRole",
"config": {
"policies": [],
"description": "Delete a role",
"tag": {
"plugin": "users-permissions",
"name": "Role",
"actionType": "destroy"
}
}
},
{
"method": "DELETE",
"path": "/providers/:provider",
"handler": "UsersPermissions.deleteProvider",
"config": {
"policies": []
}
},
{
"method": "GET",
"path": "/connect/*",
"handler": "Auth.connect",
"config": {
"policies": ["plugins.users-permissions.ratelimit"],
"prefix": "",
"description": "Connect a provider",
"tag": {
"plugin": "users-permissions",
"name": "User"
}
}
},
{
"method": "POST",
"path": "/auth/local",
"handler": "Auth.callback",
"config": {
"policies": ["plugins.users-permissions.ratelimit"],
"prefix": "",
"description": "Login a user using the identifiers email and password",
"tag": {
"plugin": "users-permissions",
"name": "User"
}
}
},
{
"method": "POST",
"path": "/auth/local/register",
"handler": "Auth.register",
"config": {
"policies": ["plugins.users-permissions.ratelimit"],
"prefix": "",
"description": "Register a new user with the default role",
"tag": {
"plugin": "users-permissions",
"name": "User",
"actionType": "create"
}
}
},
{
"method": "GET",
"path": "/auth/:provider/callback",
"handler": "Auth.callback",
"config": {
"policies": [],
"prefix": "",
"description": "Successfull redirection after approving a provider",
"tag": {
"plugin": "users-permissions",
"name": "User"
}
}
},
{
"method": "POST",
"path": "/auth/forgot-password",
"handler": "Auth.forgotPassword",
"config": {
"policies": ["plugins.users-permissions.ratelimit"],
"prefix": "",
"description": "Send the reset password email link",
"tag": {
"plugin": "users-permissions",
"name": "User"
}
}
},
{
"method": "POST",
"path": "/auth/reset-password",
"handler": "Auth.changePassword",
"config": {
"policies": ["plugins.users-permissions.ratelimit"],
"prefix": "",
"description": "Change a user's password",
"tag": {
"plugin": "users-permissions",
"name": "User"
}
}
},
{
"method": "GET",
"path": "/auth/email-confirmation",
"handler": "Auth.emailConfirmation",
"config": {
"policies": [],
"prefix": "",
"description": "Validate a user account",
"tag": {
"plugin": "users-permissions",
"name": "User"
}
}
},
{
"method": "GET",
"path": "/users",
"handler": "User.find",
"config": {
"policies": [],
"prefix": "",
"description": "Retrieve all user documents",
"tag": {
"plugin": "users-permissions",
"name": "User",
"actionType": "find"
}
}
},
{
"method": "GET",
"path": "/users/me",
"handler": "User.me",
"config": {
"policies": [],
"prefix": "",
"description": "Retrieve the logged in user informations",
"tag": {
"plugin": "users-permissions",
"name": "User",
"actionType": "findOne"
}
}
},
{
"method": "GET",
"path": "/users/:_id",
"handler": "User.findOne",
"config": {
"policies": [],
"prefix": "",
"description": "Retrieve a single user depending on his id",
"tag": {
"plugin": "users-permissions",
"name": "User",
"actionType": "findOne"
}
}
},
{
"method": "POST",
"path": "/users",
"handler": "User.create",
"config": {
"policies": [],
"prefix": ""
}
},
{
"method": "PUT",
"path": "/users/:_id",
"handler": "User.update",
"config": {
"policies": [],
"prefix": "",
"description": "Update an existing user",
"tag": {
"plugin": "users-permissions",
"name": "User",
"actionType": "update"
}
}
},
{
"method": "DELETE",
"path": "/users/:_id",
"handler": "User.destroy",
"config": {
"policies": [],
"prefix": "",
"description": "Delete an existing user",
"tag": {
"plugin": "users-permissions",
"name": "User",
"actionType": "destroy"
}
}
}
]
}
```
---
At this point if you start your server the documentation will be generated for both your APIs and the plugins.
## Usage
- Config
@ -571,10 +17,10 @@ At this point if you start your server the documentation will be generated for b
### Config
The plugin provides a `settings.json` located in `./config` where you can specify all your environment variables, licences, external documentation...
The plugin comes with a `settings.json` file located in `my-app/plugins/documentation/config` folder where you can specify all your environment variables, licences, external documentation and so one...
You can add all the entries listed in the [specification](https://swagger.io/specification/).
*NOTE* if you need to add a custom entry you can do it by prefixing your key by `x-{something}`
_NOTE_ if you need to add a custom key you can do it by prefixing your key by `x-{something}`
### Creating a new documentation version
@ -592,8 +38,7 @@ When you start your server with this plugin installed it will automatically crea
- documentationVersion // 1.0.0
- my-api.json // File containing all the identified path
- unclassified.json // File containing the manually added paths
- overrides
- 1.0.0
- overrides // Folder to override the generated documentation
- plugins
- ...
- documentation
@ -603,7 +48,7 @@ When you start your server with this plugin installed it will automatically crea
#### full_documentation.json
The combined documentation is merged into full_documentation.json it's located in `./plugins/documentation/{version}/full_documentation.json`
The combined documentation is merged into the `full_documentation.json` file and it's located in `./plugins/documentation/{version}/full_documentation.json`
It has the following structure
@ -615,7 +60,7 @@ It has the following structure
...
}
"x-strapi-config": {
"path": "/documentation", // Change this line to change to url of the doc
"path": "/documentation", // Change this line to change to url of the doc
"showGeneratedFiles": true // Do not change this line at the moment...
},
"servers" {} // Your servers config (it will be automated),
@ -626,94 +71,82 @@ It has the following structure
}
```
### Overriding the suggested documentation
Currently the plugin write a json file for each API, there's an option that will prevent this generation (if you want to).
Currently the plugin writes a json file for each API.
In order to customize the response or to add informations to a path you need to create a file in the associated `overrides/versionNumber.json` (the name of the file matters make sure they are similar). Then you just need to identify the path you want to modify.
In order to customize the responses or to add informations to a path you need to create a file in the associated `overrides/<file-name>.json` (the name of the file matters so make sure they are similar). Then you just need to identify the path you want to modify.
You can modify the default generated tags by adding a new one at the end of the file. Same for the components.
***NOTE 1**
**_NOTE 1_**
Overriding the `full_documentation.json` is a bad idea since it will be regenerated each time you change a model.
***NOTE 2***
**_NOTE 2_**
You can easily modify the description, summary, parameters of a path however, for a response like the `200` you will need to write the full object. Take a look at the `./plugins/users-permissions/documentation/overrides/1.0.0/users-permissions-User.json` for a complete example.
### FAQ
#### How does it generate the others plugins documentation ?
#### How does it generate the others plugins documentation ?
In other to reference a plugin's route into the documentation you need to add a `description` key in the `config` object.
In other to reference a plugin's route into the documentation you need to add a `description` key in the `config` object.
For example this is the plugin email routes.json file
For example this is the plugin email routes.json file
```
```
{
"routes": [
{
"routes": [
{
"method": "POST",
"path": "/",
"handler": "Email.send",
"config": {
"policies": [],
"description": "Send an email",
"tag": {
"plugin": "email",
"name": "Email"
}
}
},
{
"method": "GET",
"path": "/environments",
"handler": "Email.getEnvironments",
"config": {
"policies": []
}
},
{
"method": "GET",
"path": "/settings/:environment",
"handler": "Email.getSettings",
"config": {
"policies": []
}
},
{
"method": "PUT",
"path": "/settings/:environment",
"handler": "Email.updateSettings",
"config": {
"policies": []
"method": "POST",
"path": "/",
"handler": "Email.send",
"config": {
"policies": [],
"description": "Send an email",
"tag": {
"plugin": "email",
"name": "Email"
}
}
]
},
{
"method": "GET",
"path": "/environments",
"handler": "Email.getEnvironments",
"config": {
"policies": []
}
},
{
"method": "GET",
"path": "/settings/:environment",
"handler": "Email.getSettings",
"config": {
"policies": []
}
},
{
"method": "PUT",
"path": "/settings/:environment",
"handler": "Email.updateSettings",
"config": {
"policies": []
}
}
]
}
```
In this file we have only one route that we want to reference in our documentation (`/`). Usually, the tag object is a UI things, it will group this route under the `Email - Email` dropdown in the documentation. Furthermore, the algorithm will try to find the model to generate the best response possible. If the model is unknown it generate a response like the following:
```
{ foo: "string" }
```
that you can easily override later.
In this file we have only one route that we want to reference in our documentation (`/`). Usually, the tag object is used for the SWAGGER UI, it will group this route under the `Email - Email` dropdown in the documentation. Furthermore, the algorithm will try to find the model to generate the best response possible. If the model is unknown it generates a response like the following `{ foo: "string" }` that you can easily override later.
There's another property to guide the algorithm in created the best response possible the `actionType` key.
There's another property to guide the algorithm to create the best response possible, the `actionType` key.
When we can't know by the controller name the type of the returned response (like `find` and `findOne`) you can specify it with this key. There's an example in `./plugins/users-permissions/config/routes.json`.
#### I have created a route in a common API (like product) that query another model. How to automate this ?
#### I have created a route into a common API (like product) that query another model. How to automate this ?
You can use the `tag` key in your route. If you provide a `tag` which is a string like `"tag": "Product"` the algorithm will know that the path retrieves data in the Product table. Creating a tag object `{ "tag": { "name": "User", "plugin": "User-Permissions } }` will result in the algorithm generating a response with the model User from the plugin users-permissions.
You can use the `tag` key in your route. If you provide a `tag` which is a string like `"tag": "Product"` the algorithm will know that the end-point retrieves data from the **`Product`** table. Creating a tag object `{ "tag": { "name": "User", "plugin": "User-Permissions } }` will result in generating a response with the **`User`** model from the plugin users-permissions.
---
Each entry of the object is easily customisable take look at the users-permissions ones they are a good example on how to do it.
---
### TODO
- [ ] Disable the generated files in each API and plugins.