diff --git a/docs/_sidebar.md b/docs/_sidebar.md index cc6bd20..d219d0d 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -5,3 +5,4 @@ * Migrations * * [Migrations for Version 1](/migrations/MIGRATIONS_v1.md) * * [Migrations for Version 2](/migrations/MIGRATIONS_v2.md) +* * [Migrations for version 3](/migrations/MIGRATIONS_v3.md) diff --git a/docs/api.md b/docs/api.md index 0568221..bb1dff6 100644 --- a/docs/api.md +++ b/docs/api.md @@ -48,6 +48,9 @@ __Options:__ - `getOperationArgs({ service, path, config, apiPath, version })` - Function to generate args that the methods for operations will consume, can also customize default tag and model generation - `getOperationsRefs(model, service)` - Function to generate refs that the methods for operations will consume, see service.docs.refs option - `schemasGenerator(service, model, modelName, schemas)` - Function to generate the json schemas for a service + - `schemaNames` + - `list(model)` - function to return the default name for the list schema, defaults to `${model}List` if not provided + - `pagination(model)` - function to return the default name for the list schema, defaults to `${model}Pagination` if not provided - `operationGenerators` - Generator functions to fully customize specification generation for operations. To disable the generation of a method return false. - `find`|`get`|`create`|`update`|`patch`|`remove` - Generator function for the specific operation. - `updateMulti`|`patchMulti`|`removeMulti` - Generator function for the "multi mode" version of the specific operation @@ -121,10 +124,50 @@ __Options:__ - `find`|`get`|`create`|`update`|`patch`|`remove`|`nameOfCustomMethod` - Custom (parts of the) specification for the operation, can alternatively be set as doc property of the method. To disable the generation set to false. - `updateMulti`|`patchMulti`|`removeMulti` - if "multi mode" is enabled for the specific method, custom parts of the specification can be overwritten. To disable the generation set to false. - `all` - Custom (parts of the) specification for all operations. -- `refs` (*optional*) - Change the refs that are used for different operations: findResponse, getResponse, createRequest, createResponse, updateRequest, updateResponse, patchRequest, patchResponse, removeResponse, {customMethodName[Request|Response]} +- `refs` (*optional*) - Object with mapping of refs that are used for different operations, by setting the schema name + - `findResponse` + - `getResponse` + - `createRequest` + - `createResponse` + - `createMultiRequest` - only with `openApiVersion` 3 + - `createMultiResponse` - only with `openApiVersion` 3 + - `updateRequest` + - `updateResponse` + - `updateMultiRequest` + - `updateMultiResponse` + - `patchRequest` + - `patchResponse` + - `patchMultiRequest` + - `patchMultiResponse` + - `removeResponse` + - `removeMultiResponse` + - `filterParameter` + - `sortParameter` + - `queryParameters` - only with `openApiVersion` 3 + - `{customMethodName[Request|Response]}` +- `schemaNames` (*optional*) - Adjust the automatic generation of schema names + - `list(model)` - Function to return the name for the list schema, defaults to `${model}List` if not provided + - `pagination(model)` - Function to return the name for the list schema, defaults to `${model}Pagination` if not provided - `pathParams` (*optional*) - Object with param name as key and the definition as value, should be used when using "global" path parameters - `overwriteTagSpec` (*optional*, default: `false`) - If tag is already defined in the specs, should be overwritten from this service +### `swagger.createSwaggerServiceOptions(options)` + +Helper function to create a `service.docs` object, based on the provided schemas. +Only the `schemas`, `refs` and `model` properties will be generated. + +__Options:__ +- `schemas` - Object with TypeBox or json schemas + - Provide the generated schemas with `xxxSchema`, `xxxDataSchema` and `xxxQuerySchema` naming schema + - This will generate the `schemas` and `refs` and `model` based on the provided schemas + - Provide any schema that has an `$id` with any name + - This will just add the schema that can be consumed in refs or customized operations specifications + - Provide any schema that has an `$id` with a ref as key, check `service.docs.refs` for allowed refs + - This will add the schema and use it for the given ref +- `docs` - Any service.docs options that will be merged into the resulting object and would overwrite anything that will be generated +- `sanitizeSchema` - A function that sanitizes the schema from properties that are not allowed in an OpenApi specification. + If not provided a default implementation will be used. + ### Path support to update nested structures To be able to set only parts of a nested structure the keys of a specification object (used to define operation specifications) can be the path that should be updated. @@ -140,7 +183,7 @@ Valid unshift syntax: - `path[-]` - `path[-D]` with D being digits (needed to be able to define more than one element to unshift, the digits does not refer to a position) -### `swaggerUI.customMethod(verb, path)` +### `swagger.customMethod(verb, path)` To define the rest method of a custom method it has to be wrapped/annotated with `customMethod`. For Feathers 5 the custom method has to be added to the methods when registering the service. diff --git a/docs/examples/_sidebar.md b/docs/examples/_sidebar.md index 36a1cf4..2f14ee9 100644 --- a/docs/examples/_sidebar.md +++ b/docs/examples/_sidebar.md @@ -7,3 +7,5 @@ * * [SwaggerUI](/examples/ui.md) * * [Prefixed Routes](/examples/prefixed_routes.md) * * [Custom Methods](/examples/custom_methods.md) +* * [Using Schemas of Feathersjs v5 (dove)](/examples/generated_service_v5.md) +* * [Authentication with Feathersjs v5 (dove)](/examples/authentication_v5.md) diff --git a/docs/examples/authentication_v5.md b/docs/examples/authentication_v5.md new file mode 100644 index 0000000..2a12167 --- /dev/null +++ b/docs/examples/authentication_v5.md @@ -0,0 +1,233 @@ +### Example with feathers generated authentication on feathersjs v5 (dove) + +1. When you have generated the app with authentication you have to add some things to the initial + specs when registering feathers swagger. + + ```typescript + app.configure( + swagger({ + specs: { + info: { + title: 'Feathers app with swagger with authentication', + version: '1.0.0', + description: '...', + }, + components: { + securitySchemes: { + BearerAuth: { + type: 'http', + scheme: 'bearer', + }, + }, + }, + security: [{ BearerAuth: [] }], + }, + // other options ... + }), + ); + ``` + +2. Add documentation to the authentication service (`src/authentication.ts`). + This example shows local authentication. + + ```typescript + import { AuthenticationService, JWTStrategy } from '@feathersjs/authentication'; + import { LocalStrategy } from '@feathersjs/authentication-local'; + import type { Application } from './declarations'; + import { ServiceSwaggerOptions } from '../../feathers-swagger'; + + declare module './declarations' { + interface ServiceTypes { + authentication: AuthenticationService; + } + } + + declare module '@feathersjs/authentication' { + class AuthenticationService { + docs: ServiceSwaggerOptions; + } + } + + export const authentication = (app: Application) => { + const authentication = new AuthenticationService(app); + + authentication.docs = { + idNames: { + remove: 'accessToken', + }, + idType: 'string', + securities: ['remove', 'removeMulti'], + multi: ['remove'], + schemas: { + authRequest: { + type: 'object', + properties: { + strategy: { type: 'string' }, + email: { type: 'string' }, + password: { type: 'string' }, + }, + }, + authResult: { + type: 'object', + properties: { + accessToken: { type: 'string' }, + authentication: { + type: 'object', + properties: { + strategy: { type: 'string' }, + }, + }, + payload: { + type: 'object', + properties: {}, // TODO + }, + user: { $ref: '#/components/schemas/User' }, + }, + }, + }, + refs: { + createRequest: 'authRequest', + createResponse: 'authResult', + removeResponse: 'authResult', + removeMultiResponse: 'authResult', + }, + operations: { + remove: { + description: 'Logout the currently logged in user', + 'parameters[0].description': 'accessToken of the currently logged in user', + }, + removeMulti: { + description: 'Logout the currently logged in user', + parameters: [], + }, + }, + }; + + authentication.register('jwt', new JWTStrategy()); + authentication.register('local', new LocalStrategy()); + + app.use('authentication', authentication); + }; + ``` + +3. Set the `security` option for `service.docs` like shown in + [Using Schemas of Feathersjs v5 (dove)](/examples/generated_service_v5.md) for methods that are protected. + If all methods are protected `all` can be used. + +4. If you want to provide simple authentication usage on the SwaggerUI using Email/Username and Password, + you can use the [Swagger UI Plugin ApiKeyAuthForm](https://github.com/Mairu/swagger-ui-apikey-auth-form). + + Here is an example of an `openapi.ts` swagger configuration file, that can used with `api.configure()`; + + ```typescript + import swagger, { FnSwaggerUiGetInitializerScript } from 'feathers-swagger'; + import type { Application } from './declarations'; + + const getSwaggerInitializerScript: FnSwaggerUiGetInitializerScript = ({ docsJsonPath, ctx }) => { + const headers = ctx && ctx.headers; + const basePath = headers!['x-forwarded-prefix'] ?? ''; + + // language=JavaScript + return ` + window.onload = function () { + var script = document.createElement('script'); + script.onload = function () { + window.ui = SwaggerUIBundle({ + url: "${basePath}${docsJsonPath}", + dom_id: '#swagger-ui', + deepLinking: true, + presets: [ + SwaggerUIBundle.presets.apis, + SwaggerUIStandalonePreset, + SwaggerUIApiKeyAuthFormPlugin, + ], + plugins: [ + SwaggerUIBundle.plugins.DownloadUrl + ], + layout: "StandaloneLayout", + configs: { + apiKeyAuthFormPlugin: { + forms: { + BearerAuth: { + fields: { + email: { + type: 'text', + label: 'E-Mail-Address', + }, + password: { + type: 'password', + label: 'Password', + }, + }, + authCallback(values, callback) { + window.ui.fn.fetch({ + url: '/authentication', + method: 'post', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + strategy: 'local', + ...values, + }), + }).then(function (response) { + const json = JSON.parse(response.data); + if (json.accessToken) { + callback(null, json.accessToken); + } else { + callback('error while login'); + } + }).catch(function (err) { + console.log(err, Object.entries(err)); + callback('error while login'); + }); + }, + } + }, + localStorage: { + BearerAuth: {} + } + } + } + }); + }; + + script.src = '//cdn.jsdelivr.net/npm/@mairu/swagger-ui-apikey-auth-form@1/dist/swagger-ui-apikey-auth-form.js'; + document.head.appendChild(script) + }; + `; + }; + + export default (app: Application) => { + // If you don't use custom methods this line can be removed + app.configure(swagger.customMethodsHandler); + + app.configure( + swagger({ + specs: { + info: { + title: 'Example with Authentication', + version: '1.0.0', + description: 'Example with Authentication and SwaggerUI ApiKeyAuthForm plugin', + }, + components: { + securitySchemes: { + BearerAuth: { + type: 'http', + scheme: 'bearer', + }, + }, + }, + security: [{ BearerAuth: [] }], + }, + ui: swagger.swaggerUI({ getSwaggerInitializerScript }), + }), + ); + }; + ``` + + Here is a preview together with a user service from [Using Schemas of Feathersjs v5 (dove)](/examples/generated_service_v5.md): + + +[filename](../swagger-ui/index.html?url=../examples/authentication_v5_plugin.json ':include class=swui-preview') diff --git a/docs/examples/authentication_v5_plugin.json b/docs/examples/authentication_v5_plugin.json new file mode 100644 index 0000000..f3429a3 --- /dev/null +++ b/docs/examples/authentication_v5_plugin.json @@ -0,0 +1,708 @@ +{ + "info": { + "title": "Testing feathers swagger with dove", + "version": "1.0.0", + "description": "Testing feathers swagger with dove using koa" + }, + "components": { + "securitySchemes": { + "BearerAuth": { + "type": "http", + "scheme": "bearer" + } + }, + "schemas": { + "authRequest": { + "type": "object", + "properties": { + "strategy": { + "type": "string" + }, + "email": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "authResult": { + "type": "object", + "properties": { + "accessToken": { + "type": "string" + }, + "authentication": { + "type": "object", + "properties": { + "strategy": { + "type": "string" + } + } + }, + "payload": { + "type": "object", + "properties": {} + }, + "user": { + "$ref": "#/components/schemas/User" + } + } + }, + "User": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string" + }, + "id": { + "type": "number" + } + }, + "required": [ + "email", + "password", + "id" + ] + }, + "UserData": { + "additionalProperties": false, + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string" + } + }, + "required": [ + "email", + "password" + ] + }, + "UserQuery": { + "type": "object", + "properties": { + "$sort": { + "type": "object", + "additionalProperties": false, + "properties": { + "email": { + "minimum": -1, + "maximum": 1, + "type": "integer" + }, + "password": { + "minimum": -1, + "maximum": 1, + "type": "integer" + }, + "id": { + "minimum": -1, + "maximum": 1, + "type": "integer" + } + } + }, + "$select": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "email", + "password", + "id" + ] + } + }, + "email": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "$gt": { + "type": "string" + }, + "$gte": { + "type": "string" + }, + "$lt": { + "type": "string" + }, + "$lte": { + "type": "string" + }, + "$ne": { + "type": "string" + }, + "$in": { + "type": "array", + "items": { + "type": "string" + } + }, + "$nin": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + ] + }, + "password": { + "anyOf": [ + { + "type": "string" + }, + { + "type": "object", + "properties": { + "$gt": { + "type": "string" + }, + "$gte": { + "type": "string" + }, + "$lt": { + "type": "string" + }, + "$lte": { + "type": "string" + }, + "$ne": { + "type": "string" + }, + "$in": { + "type": "array", + "items": { + "type": "string" + } + }, + "$nin": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + ] + }, + "id": { + "anyOf": [ + { + "type": "number" + }, + { + "type": "object", + "properties": { + "$gt": { + "type": "number" + }, + "$gte": { + "type": "number" + }, + "$lt": { + "type": "number" + }, + "$lte": { + "type": "number" + }, + "$ne": { + "type": "number" + }, + "$in": { + "type": "array", + "items": { + "type": "number" + } + }, + "$nin": { + "type": "array", + "items": { + "type": "number" + } + } + } + } + ] + } + } + }, + "UserPatchData": { + "additionalProperties": false, + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "password": { + "type": "string" + } + } + }, + "UserList": { + "type": "array", + "items": { + "$ref": "#/components/schemas/User" + } + }, + "UserPagination": { + "title": "User pagination result", + "type": "object", + "properties": { + "total": { + "type": "integer" + }, + "limit": { + "type": "integer" + }, + "skip": { + "type": "integer" + }, + "data": { + "$ref": "#/components/schemas/UserList" + } + } + } + } + }, + "security": [ + { + "BearerAuth": [] + } + ], + "paths": { + "/authentication": { + "post": { + "parameters": [], + "responses": { + "201": { + "description": "created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/authResult" + } + } + } + }, + "401": { + "description": "not authenticated" + }, + "500": { + "description": "general error" + } + }, + "description": "Creates a new resource with data.", + "summary": "", + "tags": [ + "authentication" + ], + "security": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/authRequest" + } + } + } + } + }, + "delete": { + "parameters": [], + "responses": { + "200": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/authResult" + } + } + } + }, + "401": { + "description": "not authenticated" + }, + "500": { + "description": "general error" + } + }, + "description": "Logout the currently logged in user", + "summary": "", + "tags": [ + "authentication" + ], + "security": [ + { + "BearerAuth": [] + } + ] + } + }, + "/authentication/{accessToken}": { + "delete": { + "parameters": [ + { + "in": "path", + "name": "accessToken", + "description": "accessToken of the currently logged in user", + "schema": { + "type": "string" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/authResult" + } + } + } + }, + "401": { + "description": "not authenticated" + }, + "404": { + "description": "not found" + }, + "500": { + "description": "general error" + } + }, + "description": "Logout the currently logged in user", + "summary": "", + "tags": [ + "authentication" + ], + "security": [ + { + "BearerAuth": [] + } + ] + } + }, + "/users": { + "get": { + "parameters": [ + { + "description": "Number of results to return", + "in": "query", + "name": "$limit", + "schema": { + "type": "integer" + } + }, + { + "description": "Number of results to skip", + "in": "query", + "name": "$skip", + "schema": { + "type": "integer" + } + }, + { + "description": "Query parameters", + "in": "query", + "name": "filter", + "style": "form", + "explode": true, + "schema": { + "$ref": "#/components/schemas/UserQuery" + } + } + ], + "responses": { + "200": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserPagination" + } + } + } + }, + "401": { + "description": "not authenticated" + }, + "500": { + "description": "general error" + } + }, + "description": "Retrieves a list of all resources from the service.", + "summary": "", + "tags": [ + "users" + ], + "security": [ + { + "BearerAuth": [] + } + ] + }, + "post": { + "parameters": [], + "responses": { + "201": { + "description": "created", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "401": { + "description": "not authenticated" + }, + "500": { + "description": "general error" + } + }, + "description": "Creates a new resource with data.", + "summary": "", + "tags": [ + "users" + ], + "security": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserData" + } + } + } + } + } + }, + "/users/{id}": { + "get": { + "parameters": [ + { + "in": "path", + "name": "id", + "description": "ID of User to return", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "401": { + "description": "not authenticated" + }, + "404": { + "description": "not found" + }, + "500": { + "description": "general error" + } + }, + "description": "Retrieves a single resource with the given id from the service.", + "summary": "", + "tags": [ + "users" + ], + "security": [ + { + "BearerAuth": [] + } + ] + }, + "put": { + "parameters": [ + { + "in": "path", + "name": "id", + "description": "ID of User to update", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "401": { + "description": "not authenticated" + }, + "404": { + "description": "not found" + }, + "500": { + "description": "general error" + } + }, + "description": "Updates the resource identified by id using data.", + "summary": "", + "tags": [ + "users" + ], + "security": [ + { + "BearerAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserData" + } + } + } + } + }, + "patch": { + "parameters": [ + { + "in": "path", + "name": "id", + "description": "ID of User to update", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "401": { + "description": "not authenticated" + }, + "404": { + "description": "not found" + }, + "500": { + "description": "general error" + } + }, + "description": "Updates the resource identified by id using data.", + "summary": "", + "tags": [ + "users" + ], + "security": [ + { + "BearerAuth": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/UserPatchData" + } + } + } + } + }, + "delete": { + "parameters": [ + { + "in": "path", + "name": "id", + "description": "ID of User to remove", + "schema": { + "type": "integer" + }, + "required": true + } + ], + "responses": { + "200": { + "description": "success", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/User" + } + } + } + }, + "401": { + "description": "not authenticated" + }, + "404": { + "description": "not found" + }, + "500": { + "description": "general error" + } + }, + "description": "Removes the resource with id.", + "summary": "", + "tags": [ + "users" + ], + "security": [ + { + "BearerAuth": [] + } + ] + } + } + }, + "openapi": "3.0.3", + "tags": [ + { + "name": "authentication", + "description": "A authentication service" + }, + { + "name": "users", + "description": "A users service" + } + ] +} diff --git a/docs/examples/generated_service_v5.md b/docs/examples/generated_service_v5.md new file mode 100644 index 0000000..dbe85de --- /dev/null +++ b/docs/examples/generated_service_v5.md @@ -0,0 +1,39 @@ +### Example with feathers generated service based on feathersjs v5 (dove) + +1. Go into your `src/services/{$name}` folder, and open the service you want to edit `${name}.[tj]s` +2. Import the helper function and import the schemas (example for user service): +```js + import { createSwaggerServiceOptions } from 'feathers-swagger'; + import { + userDataValidator, + userQueryValidator, + userResolver, + userDataResolver, + userQueryResolver, + userExternalResolver, + // the lines below are added + userDataSchema, + userQuerySchema, + userSchema, + } from './users.schema'; +``` +adjust the options when the service is generated +```js + app.use('users', new UserService(getOptions(app)), { + // A list of all methods this service exposes externally + methods: ['find', 'get', 'create', 'update', 'patch', 'remove'], + // You can add additional custom events to be sent to clients here + events: [], + docs: createSwaggerServiceOptions({ + schemas: { userDataSchema, userQuerySchema, userSchema }, + docs: { + // any options for service.docs can be added here + securities: ['find', 'get', 'update', 'patch', 'remove'], + } + }), + }); +``` + +If you are using Typescript don't forget to add the docs property to the ServiceOptions interface +which is described on the +[Getting Started - Configure the documentation for a feathers service](/#/?id=configure-the-documentation-for-a-feathers-service) diff --git a/docs/examples/index.md b/docs/examples/index.md index f76b3ef..5b62197 100644 --- a/docs/examples/index.md +++ b/docs/examples/index.md @@ -10,3 +10,5 @@ It contains many configurations and can be start with `npm start` when feathers- * [SwaggerUI](/examples/ui.md) * [Prefixed Routes](/examples/prefixed_routes.md) * [Custom Methods](/examples/custom_methods.md) +* [Using Schemas of Feathersjs v5 (dove)](/examples/generated_service_v5.md) +* [Authentication with Feathersjs v5 (dove)](/examples/authentication_v5.md) diff --git a/docs/getting-started.md b/docs/getting-started.md index c945a77..793b396 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -120,12 +120,51 @@ const app = express(feathers()) ### Configure the documentation for a feathers service -To customize the documentation of feathers service a docs property has to be added to a service before it is registered. +To customize the documentation of feathers service a docs property has to be added. This can be done as property before the service is registered or as service path option. There are many options please check out the [examples](examples/index.md) and the [API documentation](api.md#servicedocs) to get more details. +#### **Service path option (TS)** +The docs property can be set as [path service options](https://dove.feathersjs.com/api/application.html#use-path-service-options) introduced with feathers dove. + +```typescript +import type { createSwaggerServiceOptions } from 'feathers-swagger'; +import { + messageDataSchema, + messageQuerySchema, + messageSchema, +} from './message.schema'; + +// ... + +app.use('message', new MessageService(), { + methods: ['find', 'get', 'create', 'update', 'patch', 'remove'], + events: [], + docs: createSwaggerServiceOptions({ + schemas: { messageDataSchema, messageQuerySchema, messageSchema }, + docs: { description: 'My custom service description' } + }) +}); +``` + +To be able to set the docs property on the service options, the interface has to be adjusted in the `declarations.ts` of the project. + +```typescript +import { ServiceSwaggerOptions } from 'feathers-swagger'; + +// ... + +declare module '@feathersjs/feathers' { + interface ServiceOptions { + docs?: ServiceSwaggerOptions; + } +} +``` + #### **Simple** +The docs property can be set as property of a service instance before it is registered. + ```js // definition of service @@ -169,6 +208,7 @@ To resolve, either manually define your model schemas or consider automated alte * [Migrations for version 1](migrations/MIGRATIONS_v1.md) * [Migrations for version 2](migrations/MIGRATIONS_v2.md) +* [Migrations for version 3](migrations/MIGRATIONS_v3.md) ## License diff --git a/docs/migrations/MIGRATIONS_v3.md b/docs/migrations/MIGRATIONS_v3.md new file mode 100644 index 0000000..00acb5b --- /dev/null +++ b/docs/migrations/MIGRATIONS_v3.md @@ -0,0 +1,30 @@ +## Migrations for Version 3 (from 2) + +Version 3.0.0 introduces some breaking changes to previous 2.x.x versions. These changes and ways to migrate to the new release will be described here. + +### Adjust the default schema / definition name of generated lists and pagination schemas + +#### Before +Generated names were `${model}_list` and `${model}_pagination` and could not be adjusted. + +#### After +Generated names will by default be `${model}List` and `${model}Pagination` but can be adjusted. + +To get the old behavior you can override the schemaNames generation (on service level) or as defaults. +```js +swagger({ + specs: { + info: { + title: 'A test', + description: 'A description', + version: '1.0.0' + }, + defaults: { + schemaNames: { + list: name => `${name}_list`, + pagination: name => `${name}_pagination`, + } + } + }, +}) +``` diff --git a/example/app.js b/example/app.js index f78425e..571aeb4 100644 --- a/example/app.js +++ b/example/app.js @@ -20,6 +20,7 @@ const openApiV3Security = require('./openapi-v3/security'); const openApiV3CustomMethods = require('./openapi-v3/customMethods'); const openApiV3Multi = require('./openapi-v3/multi'); const openApiV3IdNames = require('./openapi-v3/idNames'); +const openApiV3DoveSchemas = require('./openapi-v3/doveSchemas'); const app = express(feathers()) .use(express.json()) @@ -52,9 +53,11 @@ const app = express(feathers()) .configure(openApiV3CustomMethods) .configure(openApiV3Multi) .configure(openApiV3IdNames) + .configure(openApiV3DoveSchemas) ; -console.log('Simple app with multiple feathers-swagger examples running on http://localhost:3030/'); +const port = process.env.PORT || 3030; +console.log(`Simple app with multiple feathers-swagger examples running on http://localhost:${port}/`); -app.listen(process.env.PORT || 3030); +app.listen(port); diff --git a/example/docs.html b/example/docs.html index 90e51eb..a274847 100644 --- a/example/docs.html +++ b/example/docs.html @@ -71,7 +71,7 @@