Skip to content

Commit

Permalink
feat!(schema): add possibility to consume dove json schemas
Browse files Browse the repository at this point in the history
breaking: change the default naming of list and pagination schemas to be camel case
  • Loading branch information
Mairu committed Oct 23, 2022
1 parent 75b7e7b commit 321ef1f
Show file tree
Hide file tree
Showing 35 changed files with 5,203 additions and 3,331 deletions.
1 change: 1 addition & 0 deletions docs/_sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
47 changes: 45 additions & 2 deletions docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions docs/examples/_sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
233 changes: 233 additions & 0 deletions docs/examples/authentication_v5.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
### Example with feathers generated authentication on feathersjs v5 (dove) <!-- {docsify-ignore} -->

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')
Loading

0 comments on commit 321ef1f

Please sign in to comment.