CoolFace
Apppublic

strong-tie/inbound-calls

sourceHugging Faceupdated 2y agoView on Hugging Face
0likes
schema-examples.test.js662 linesDownload Raw Back to test
1'use strict'2 3const { test } = require('node:test')4const localize = require('ajv-i18n')5const Fastify = require('..')6 7test('Example - URI $id', (t, done) => {8  t.plan(1)9  const fastify = Fastify()10  fastify.addSchema({11    $id: 'http://example.com/',12    type: 'object',13    properties: {14      hello: { type: 'string' }15    }16  })17 18  fastify.post('/', {19    handler () { },20    schema: {21      body: {22        type: 'array',23        items: { $ref: 'http://example.com#/properties/hello' }24      }25    }26  })27 28  fastify.ready(err => {29    t.assert.ifError(err)30    done()31  })32})33 34test('Example - string $id', (t, done) => {35  t.plan(1)36  const fastify = Fastify()37  fastify.addSchema({38    $id: 'commonSchema',39    type: 'object',40    properties: {41      hello: { type: 'string' }42    }43  })44 45  fastify.post('/', {46    handler () { },47    schema: {48      body: { $ref: 'commonSchema#' },49      headers: { $ref: 'commonSchema#' }50    }51  })52 53  fastify.ready(err => {54    t.assert.ifError(err)55    done()56  })57})58 59test('Example - get schema', (t, done) => {60  t.plan(1)61  const fastify = Fastify()62  fastify.addSchema({63    $id: 'schemaId',64    type: 'object',65    properties: {66      hello: { type: 'string' }67    }68  })69 70  const mySchemas = fastify.getSchemas()71  const mySchema = fastify.getSchema('schemaId')72  t.assert.deepStrictEqual(mySchemas.schemaId, mySchema)73  done()74})75 76test('Example - get schema encapsulated', async t => {77  const fastify = Fastify()78 79  fastify.addSchema({ $id: 'one', my: 'hello' })80  // will return only `one` schema81  fastify.get('/', (request, reply) => { reply.send(fastify.getSchemas()) })82 83  fastify.register((instance, opts, done) => {84    instance.addSchema({ $id: 'two', my: 'ciao' })85    // will return `one` and `two` schemas86    instance.get('/sub', (request, reply) => { reply.send(instance.getSchemas()) })87 88    instance.register((subinstance, opts, done) => {89      subinstance.addSchema({ $id: 'three', my: 'hola' })90      // will return `one`, `two` and `three`91      subinstance.get('/deep', (request, reply) => { reply.send(subinstance.getSchemas()) })92      done()93    })94    done()95  })96 97  const r1 = await fastify.inject('/')98  const r2 = await fastify.inject('/sub')99  const r3 = await fastify.inject('/deep')100 101  t.assert.deepStrictEqual(Object.keys(r1.json()), ['one'])102  t.assert.deepStrictEqual(Object.keys(r2.json()), ['one', 'two'])103  t.assert.deepStrictEqual(Object.keys(r3.json()), ['one', 'two', 'three'])104})105 106test('Example - validation', (t, done) => {107  t.plan(1)108  const fastify = Fastify({109    ajv: {110      customOptions: {111        allowUnionTypes: true112      }113    }114  })115  const handler = () => { }116 117  const bodyJsonSchema = {118    type: 'object',119    required: ['requiredKey'],120    properties: {121      someKey: { type: 'string' },122      someOtherKey: { type: 'number' },123      requiredKey: {124        type: 'array',125        maxItems: 3,126        items: { type: 'integer' }127      },128      nullableKey: { type: ['number', 'null'] }, // or { type: 'number', nullable: true }129      multipleTypesKey: { type: ['boolean', 'number'] },130      multipleRestrictedTypesKey: {131        oneOf: [132          { type: 'string', maxLength: 5 },133          { type: 'number', minimum: 10 }134        ]135      },136      enumKey: {137        type: 'string',138        enum: ['John', 'Foo']139      },140      notTypeKey: {141        not: { type: 'array' }142      }143    }144  }145 146  const queryStringJsonSchema = {147    type: 'object',148    properties: {149      name: { type: 'string' },150      excitement: { type: 'integer' }151    }152  }153 154  const paramsJsonSchema = {155    type: 'object',156    properties: {157      par1: { type: 'string' },158      par2: { type: 'number' }159    }160  }161 162  const headersJsonSchema = {163    type: 'object',164    properties: {165      'x-foo': { type: 'string' }166    },167    required: ['x-foo']168  }169 170  const schema = {171    body: bodyJsonSchema,172    querystring: queryStringJsonSchema,173    params: paramsJsonSchema,174    headers: headersJsonSchema175  }176 177  fastify.post('/the/url', { schema }, handler)178  fastify.ready(err => {179    t.assert.ifError(err)180    done()181  })182})183 184test('Example - ajv config', (t, done) => {185  t.plan(1)186 187  const fastify = Fastify({188    ajv: {189      plugins: [190        require('ajv-merge-patch')191      ]192    }193  })194 195  fastify.post('/', {196    handler (req, reply) { reply.send({ ok: 1 }) },197    schema: {198      body: {199        $patch: {200          source: {201            type: 'object',202            properties: {203              q: {204                type: 'string'205              }206            }207          },208          with: [209            {210              op: 'add',211              path: '/properties/q',212              value: { type: 'number' }213            }214          ]215        }216      }217    }218  })219 220  fastify.post('/foo', {221    handler (req, reply) { reply.send({ ok: 1 }) },222    schema: {223      body: {224        $merge: {225          source: {226            type: 'object',227            properties: {228              q: {229                type: 'string'230              }231            }232          },233          with: {234            required: ['q']235          }236        }237      }238    }239  })240 241  fastify.ready(err => {242    t.assert.ifError(err)243    done()244  })245})246 247test('Example Joi', (t, done) => {248  t.plan(1)249  const fastify = Fastify()250  const handler = () => { }251 252  const Joi = require('joi')253  fastify.post('/the/url', {254    schema: {255      body: Joi.object().keys({256        hello: Joi.string().required()257      }).required()258    },259    validatorCompiler: ({ schema, method, url, httpPart }) => {260      return data => schema.validate(data)261    }262  }, handler)263 264  fastify.ready(err => {265    t.assert.ifError(err)266    done()267  })268})269 270test('Example yup', (t, done) => {271  t.plan(1)272  const fastify = Fastify()273  const handler = () => { }274 275  const yup = require('yup')276  // Validation options to match ajv's baseline options used in Fastify277  const yupOptions = {278    strict: false,279    abortEarly: false, // return all errors280    stripUnknown: true, // remove additional properties281    recursive: true282  }283 284  fastify.post('/the/url', {285    schema: {286      body: yup.object({287        age: yup.number().integer().required(),288        sub: yup.object().shape({289          name: yup.string().required()290        }).required()291      })292    },293    validatorCompiler: ({ schema, method, url, httpPart }) => {294      return function (data) {295        // with option strict = false, yup `validateSync` function returns the coerced value if validation was successful, or throws if validation failed296        try {297          const result = schema.validateSync(data, yupOptions)298          return { value: result }299        } catch (e) {300          return { error: e }301        }302      }303    }304  }, handler)305 306  fastify.ready(err => {307    t.assert.ifError(err)308    done()309  })310})311 312test('Example - serialization', (t, done) => {313  t.plan(1)314  const fastify = Fastify()315  const handler = () => { }316 317  const schema = {318    response: {319      200: {320        type: 'object',321        properties: {322          value: { type: 'string' },323          otherValue: { type: 'boolean' }324        }325      }326    }327  }328 329  fastify.post('/the/url', { schema }, handler)330  fastify.ready(err => {331    t.assert.ifError(err)332    done()333  })334})335 336test('Example - serialization 2', (t, done) => {337  t.plan(1)338  const fastify = Fastify()339  const handler = () => { }340 341  const schema = {342    response: {343      '2xx': {344        type: 'object',345        properties: {346          value: { type: 'string' },347          otherValue: { type: 'boolean' }348        }349      },350      201: {351        // the contract syntax352        value: { type: 'string' }353      }354    }355  }356 357  fastify.post('/the/url', { schema }, handler)358  fastify.ready(err => {359    t.assert.ifError(err)360    done()361  })362})363 364test('Example - serializator', (t, done) => {365  t.plan(1)366  const fastify = Fastify()367 368  fastify.setSerializerCompiler(({ schema, method, url, httpStatus }) => {369    return data => JSON.stringify(data)370  })371 372  fastify.get('/user', {373    handler (req, reply) {374      reply.send({ id: 1, name: 'Foo', image: 'BIG IMAGE' })375    },376    schema: {377      response: {378        '2xx': {379          type: 'object',380          properties: {381            id: { type: 'number' },382            name: { type: 'string' }383          }384        }385      }386    }387  })388 389  fastify.ready(err => {390    t.assert.ifError(err)391    done()392  })393})394 395test('Example - schemas examples', (t, done) => {396  t.plan(1)397  const fastify = Fastify()398  const handler = () => { }399 400  fastify.addSchema({401    $id: 'http://foo/common.json',402    type: 'object',403    definitions: {404      foo: {405        $id: '#address',406        type: 'object',407        properties: {408          city: { type: 'string' }409        }410      }411    }412  })413 414  fastify.addSchema({415    $id: 'http://foo/shared.json',416    type: 'object',417    definitions: {418      foo: {419        type: 'object',420        properties: {421          city: { type: 'string' }422        }423      }424    }425  })426 427  const refToId = {428    type: 'object',429    definitions: {430      foo: {431        $id: '#address',432        type: 'object',433        properties: {434          city: { type: 'string' }435        }436      }437    },438    properties: {439      home: { $ref: '#address' },440      work: { $ref: '#address' }441    }442  }443 444  const refToDefinitions = {445    type: 'object',446    definitions: {447      foo: {448        $id: '#address',449        type: 'object',450        properties: {451          city: { type: 'string' }452        }453      }454    },455    properties: {456      home: { $ref: '#/definitions/foo' },457      work: { $ref: '#/definitions/foo' }458    }459  }460 461  const refToSharedSchemaId = {462    type: 'object',463    properties: {464      home: { $ref: 'http://foo/common.json#address' },465      work: { $ref: 'http://foo/common.json#address' }466    }467  }468 469  const refToSharedSchemaDefinitions = {470    type: 'object',471    properties: {472      home: { $ref: 'http://foo/shared.json#/definitions/foo' },473      work: { $ref: 'http://foo/shared.json#/definitions/foo' }474    }475  }476 477  fastify.post('/', {478    handler,479    schema: {480      body: refToId,481      headers: refToDefinitions,482      params: refToSharedSchemaId,483      query: refToSharedSchemaDefinitions484    }485 486  })487 488  fastify.ready(err => {489    t.assert.ifError(err)490    done()491  })492})493 494test('should return custom error messages with ajv-errors', (t, done) => {495  t.plan(3)496 497  const fastify = Fastify({498    ajv: {499      customOptions: { allErrors: true },500      plugins: [501        require('ajv-errors')502      ]503    }504  })505 506  const schema = {507    body: {508      type: 'object',509      properties: {510        name: { type: 'string' },511        work: { type: 'string' },512        age: {513          type: 'number',514          errorMessage: {515            type: 'bad age - should be num'516          }517        }518      },519      required: ['name', 'work'],520      errorMessage: {521        required: {522          name: 'name please',523          work: 'work please',524          age: 'age please'525        }526      }527    }528  }529 530  fastify.post('/', { schema }, function (req, reply) {531    reply.code(200).send(req.body.name)532  })533 534  fastify.inject({535    method: 'POST',536    payload: {537      hello: 'salman',538      age: 'bad'539    },540    url: '/'541  }, (err, res) => {542    t.assert.ifError(err)543    t.assert.deepStrictEqual(JSON.parse(res.payload), {544      statusCode: 400,545      code: 'FST_ERR_VALIDATION',546      error: 'Bad Request',547      message: 'body/age bad age - should be num, body name please, body work please'548    })549    t.assert.strictEqual(res.statusCode, 400)550    done()551  })552})553 554test('should be able to handle formats of ajv-formats when added by plugins option', (t, done) => {555  t.plan(3)556 557  const fastify = Fastify({558    ajv: {559      plugins: [560        require('ajv-formats')561      ]562    }563  })564 565  const schema = {566    body: {567      type: 'object',568      properties: {569        id: { type: 'string', format: 'uuid' },570        email: { type: 'string', format: 'email' }571      },572      required: ['id', 'email']573    }574  }575 576  fastify.post('/', { schema }, function (req, reply) {577    reply.code(200).send(req.body.id)578  })579 580  fastify.inject({581    method: 'POST',582    payload: {583      id: '254381a5-888c-4b41-8116-e3b1a54980bd',584      email: 'info@fastify.dev'585    },586    url: '/'587  }, (_err, res) => {588    t.assert.strictEqual(res.body, '254381a5-888c-4b41-8116-e3b1a54980bd')589    t.assert.strictEqual(res.statusCode, 200)590  })591 592  fastify.inject({593    method: 'POST',594    payload: {595      id: 'invalid',596      email: 'info@fastify.dev'597    },598    url: '/'599  }, (_err, res) => {600    t.assert.deepStrictEqual(JSON.parse(res.payload), {601      statusCode: 400,602      code: 'FST_ERR_VALIDATION',603      error: 'Bad Request',604      message: 'body/id must match format "uuid"'605    })606    done()607  })608})609 610test('should return localized error messages with ajv-i18n', (t, done) => {611  t.plan(3)612 613  const schema = {614    body: {615      type: 'object',616      properties: {617        name: { type: 'string' },618        work: { type: 'string' }619      },620      required: ['name', 'work']621    }622  }623 624  const fastify = Fastify({625    ajv: {626      customOptions: { allErrors: true }627    }628  })629 630  fastify.setErrorHandler(function (error, request, reply) {631    if (error.validation) {632      localize.ru(error.validation)633      reply.status(400).send(error.validation)634      return635    }636    reply.send(error)637  })638 639  fastify.post('/', { schema }, function (req, reply) {640    reply.code(200).send(req.body.name)641  })642 643  fastify.inject({644    method: 'POST',645    payload: {646      name: 'salman'647    },648    url: '/'649  }, (err, res) => {650    t.assert.ifError(err)651    t.assert.deepStrictEqual(JSON.parse(res.payload), [{652      instancePath: '',653      keyword: 'required',654      message: 'должно иметь обязательное поле work',655      params: { missingProperty: 'work' },656      schemaPath: '#/required'657    }])658    t.assert.strictEqual(res.statusCode, 400)659    done()660  })661})662