Skip to content

Commit

Permalink
Feature/addSchema to serialization (#1437)
Browse files Browse the repository at this point in the history
* add: basic impl for propagating shared schema to serializer

* add: test to cover all the supported usage of $ref

* fix: CI removed spread operator

* add: example of usage of the $ref attribute
  • Loading branch information
Eomm authored and mcollina committed Feb 8, 2019
1 parent 8d320f0 commit 972f2e7
Show file tree
Hide file tree
Showing 4 changed files with 386 additions and 8 deletions.
126 changes: 126 additions & 0 deletions docs/Validation-and-Serialization.md
Expand Up @@ -98,6 +98,7 @@ fastify.register((instance, opts, next) => {
next()
})
```

You can use the shared schema everywhere, as top level schema or nested inside other schemas:
```js
const fastify = require('fastify')()
Expand Down Expand Up @@ -302,6 +303,131 @@ fastify.setErrorHandler(function (error, request, reply) {
})
```

### JSON Schema and Shared Schema support

JSON Schema has some type of utilities in order to optimize your schemas that,
in conjuction with the Fastify's shared schema, let you reuse all your schemas easily.

| Use Case | Validator | Serializer |
|-----------------------------------|-----------|------------|
| shared schema | ✔️ | ✔️ |
| `$ref` to `$id` || ✔️ |
| `$ref` to `/definitions` | ✔️ | ✔️ |
| `$ref` to shared schema `$id` || ✔️ |
| `$ref` to shared schema `/definitions` || ✔️ |

#### Examples

```js
// Usage of the Shared Schema feature
fastify.addSchema({
$id: 'sharedAddress',
type: 'object',
properties: {
city: { 'type': 'string' }
}
})

const sharedSchema = {
type: 'object',
properties: {
home: 'sharedAddress#',
work: 'sharedAddress#'
}
}
```

```js
// Usage of $ref to $id in same JSON Schema
const refToId = {
type: 'object',
definitions: {
foo: {
$id: '#address',
type: 'object',
properties: {
city: { 'type': 'string' }
}
}
},
properties: {
home: { $ref: '#address' },
work: { $ref: '#address' }
}
}
```


```js
// Usage of $ref to /definitions in same JSON Schema
const refToDefinitions = {
type: 'object',
definitions: {
foo: {
$id: '#address',
type: 'object',
properties: {
city: { 'type': 'string' }
}
}
},
properties: {
home: { $ref: '#/definitions/foo' },
work: { $ref: '#/definitions/foo' }
}
}
```

```js
// Usage $ref to a shared schema $id as external schema
fastify.addSchema({
$id: 'http://foo/common.json',
type: 'object',
definitions: {
foo: {
$id: '#address',
type: 'object',
properties: {
city: { 'type': 'string' }
}
}
}
})

const refToSharedSchemaId = {
type: 'object',
properties: {
home: { $ref: 'http://foo/common.json#address' },
work: { $ref: 'http://foo/common.json#address' }
}
}
```


```js
// Usage $ref to a shared schema /definitions as external schema
fastify.addSchema({
$id: 'http://foo/common.json',
type: 'object',
definitions: {
foo: {
type: 'object',
properties: {
city: { 'type': 'string' }
}
}
}
})

const refToSharedSchemaDefinitions = {
type: 'object',
properties: {
home: { $ref: 'http://foo/common.json#/definitions/foo' },
work: { $ref: 'http://foo/common.json#/definitions/foo' }
}
}
```

<a name="resources"></a>
### Resources
- [JSON Schema](http://json-schema.org/)
Expand Down
3 changes: 2 additions & 1 deletion lib/schemas.js
Expand Up @@ -70,7 +70,8 @@ Schemas.prototype.resolveRefs = function (routeSchemas) {

Schemas.prototype.traverse = function (schema) {
for (var key in schema) {
if (typeof schema[key] === 'string' && key !== '$schema' && schema[key].slice(-1) === '#') {
// resolve the `sharedSchemaId#' only if is not a standard $ref JSON Pointer
if (typeof schema[key] === 'string' && key !== '$schema' && key !== '$ref' && schema[key].slice(-1) === '#') {
schema[key] = this.resolve(schema[key].slice(0, -1))
}

Expand Down
10 changes: 5 additions & 5 deletions lib/validation.js
Expand Up @@ -9,14 +9,14 @@ const paramsSchema = Symbol('params-schema')
const responseSchema = Symbol('response-schema')
const headersSchema = Symbol('headers-schema')

function getValidatorForStatusCodeSchema (statusCodeDefinition) {
return fastJsonStringify(statusCodeDefinition)
function getValidatorForStatusCodeSchema (statusCodeDefinition, externalSchema) {
return fastJsonStringify(statusCodeDefinition, { schema: externalSchema })
}

function getResponseSchema (responseSchemaDefinition) {
function getResponseSchema (responseSchemaDefinition, sharedSchemas) {
var statusCodes = Object.keys(responseSchemaDefinition)
return statusCodes.reduce(function (r, statusCode) {
r[statusCode] = getValidatorForStatusCodeSchema(responseSchemaDefinition[statusCode])
r[statusCode] = getValidatorForStatusCodeSchema(responseSchemaDefinition[statusCode], sharedSchemas)
return r
}, {})
}
Expand Down Expand Up @@ -50,7 +50,7 @@ function build (context, compile, schemas) {
}

if (context.schema.response) {
context[responseSchema] = getResponseSchema(context.schema.response)
context[responseSchema] = getResponseSchema(context.schema.response, schemas.getSchemas())
}

if (context.schema.body) {
Expand Down

0 comments on commit 972f2e7

Please sign in to comment.