OpenApi schema validation with ajv

784 views Asked by At

I used to validate the response body against the piece of the OpenAPI schema (I parsed the schema file and took only the needed response schema).

But when the response schema references another components.schema (from the same file) - it won't work. I've read this doc about combining schemas: https://ajv.js.org/guide/combining-schemas.html

But it's not clear how to work with multiple references/schemas.

It looks like the "ajv" library cannot work with the whole OpenAPI schema object (Error: strict mode: unknown keyword: "openapi").

Is the whole OpenAPI schema validation possible at all? How do I combine more than 2 schemas (if there are many references)?

Currently I'm trying to combine at least 2 schemas and I'm getting the resolve reference error:

can't resolve reference testObject.json#/schemas/testObject from id http://example.com/schemas/searchResults.json

Here's my test file (it's a piece of the OpenAPI to test only this specific reference case):

## test.spec.yaml    
    schemas:
        testObject:
          $id: http://example.com/schemas/testObject.json
          type: object
          properties:
            prop1:
              type: string
        searchResults:
          $id: http://example.com/schemas/searchResults.json
          type: object
          properties:
            testObjects:
              type: array
              items:
                $ref: "testObject.json#/schemas/testObject"

Here's my code:

import * as fs from "fs";

import Ajv from "ajv";
import * as yaml from "js-yaml";

describe("test", () => {
  it("should validate the openapi schema", async () => {
    const schema: any = yaml.load(fs.readFileSync("test.spec.yaml", "utf8"));

    const a = schema.schemas.searchResults;
    const b = schema.schemas.testObject;

    const ajv = new Ajv({
      strict: true,
      allErrors: true,
      verbose: false,
      schemas: [a, b],
    });

    const validate: any = ajv.getSchema(
      "http://example.com/schemas/searchResults.json"
    );
    const valid = validate({ testObjects: "foo" });
    if (!valid) throw new Error(JSON.stringify(validate.errors));
  });
});
3

There are 3 answers

0
Medvedscak On BEST ANSWER

Ok, after some investigation I realized the ajv doesn't work with openapi schema format and it needs some efforts to prepare JSONs out of OpenApi schemas which might be tricky when it comes to working with refs and nesting.

As for my example, there were 2 issues:

  1. $ref: "testObject.json#/schemas/testObject" -> incorrect. It should be something like "testObject.json#/definitionId"
  2. The structure of the definition should be different, as in this example from docs:
    const testObject = {
        $id: "http://example.com/schemas/testObject.json",
        definitions: {
            int: {type: "integer"},
            str: {type: "string"},
        },
    }

So I can refer to the exact object, like this: $ref: "testObject.json#/definitions/int"

0
Jeremy Fiel On

https://stackoverflow.com/a/77520493/8564731

I answered this more thoroughly in another question today. Hope this helps you.

0
Erik Erikson On

You can load the OpenAPI specification into AJV and validate against it. To address strict mode you'll need to add vocabulary to AJV or disable/defang strict mode (with new Ajv({ strict: 'log' })).

The ID with which you load OAI spec can be used along with valid path references. Within the document a self reference starts with # for the root of the document. From a separate schema file that uses an API type you would use a $ref via $ID#. For example, if my schema has the id https://www.example.com/openapi.yml then the ID for a type defined therein might be $ref: 'https://www.example.com/openapi.yml#/components/schemas/MyType' while inside the OpenAPI spec I would use $ref: '#/components/schemas/MyType'. For a response schema that would be $ref: 'https://www.example.com/openapi.yml#/components/responses/MyResponseType'

relative: $ref: '#/components/schemas/MyType'
full ref: $ref: 'https://www.example.com/openapi.yml#/components/schemas/MyType'
response: $ref: 'https://www.example.com/openapi.yml#/components/responses/MyResponseType'

Vocabulary additions:


  ajv.addVocabulary([
    // OpenAPI root elements
    'components',
    'externalDocs',
    'info',
    'openapi',
    'paths',
    'security',
    'servers',
    // OpenAPI Request/Response (relative) root element
    'content',
  ])