Проверить ответ API на схему JSON с помощью AJV

Я много дней читаю и делаю несколько тестов с этим, но без ожидаемых результатов. Мне нужно проверить ответ API с его схемой JSON (Swagger 2.0). JSON длиннее, но я сократил его до простого. Мне нужно знать, имеют ли ключевые слова "код" и "сообщение" в ответе определенный тип и значения. Вот код, который я использую:

var Ajv = require('ajv');
var ajv = new Ajv();

var schema = {
  "host": "cert",
  "paths": {
    "products": {
      "get": {
        "responses": {
          "401": {
            "description": "Problem with the client request",
            "headers": {
              "x-correlator": {
                "type": "string",
                "format": "uuid",
                "description": "Correlation id"
              }
            },
            "schema": {
              "$ref": "file:///../errors.json#/definitions/Unauthenticated"
            }
          }
        }
      }
    },
    "products": {
      "get": {
        "responses": {
          "401": {
            "description": "Problem with the client request",
            "headers": {
              "x-correlator": {
                "type": "string",
                "format": "uuid",
                "description": "Correlation id"
              }
            },
            "schema": {
              "$ref": "file:///../errors.json#/definitions/Unauthenticated"
            },
            "examples": {
              "application/json": {
                "code": "UNAUTHENTICATED",
                "message": "Authentication error"
              }
            }
          }
        }
      }
    }
  }
}

var errors_schema = {
  "info": {
    "description": "Common errors",
    "version": "3.0.1",
    "title": "Common errors",
    "contact": {
      "name": "Team"
    }
  },
  "definitions": {
    "ModelError": {
      "type": "object",
      "required": [
        "message"
      ],
      "properties": {
        "message": {
          "type": "string",
          "description": "A human readable description"
        }
      }
    },
    "Unauthenticated": {
      "allOf": [
        {
          "type": "object",
          "required": [
            "code"
          ],
          "properties": {
            "code": {
              "type": "string",
              "enum": [
                "UNAUTHENTICATED"
              ],
              "default": "UNAUTHENTICATED",
              "description": "Request not authenticated due to missing, invalid, or expired credentials."
            }
          }
        },
        {
          "$ref": "#/definitions/ModelError"
        }
      ]
    }
  }
}

ajv.addSchema(errors_schema, 'file:///../errors.json');
var testajv = ajv.compile(schema);

var response = {"code": 123, "message":"token expired"}

var valid = testajv(response);

console.log(valid);

if(!valid) {
    console.log(testajv.errors);
}

Как вы видите, ключевое слово «код» в ответе представляет собой целое число «123», но в схеме оно определено как строка. Независимо от значения проверка всегда принимает значение «истина». Что мне делать, чтобы удовлетворить свои потребности? Заранее спасибо.


person Sebastian Diaz    schedule 03.10.2018    source источник


Ответы (2)


Схема Swagger содержит несколько схем JSON, организованных в структуру API, вам необходимо указать правильную часть схемы Swagger, чтобы использовать схему JSON для проверки.

Пожалуйста, проверьте пример кода (https://runkit.com/embed/bwj42juwyjo4):

var Ajv = require('ajv');
var ajv = new Ajv();

var schema = {
  "host": "cert",
  "paths": {
    "products": {
      "get": {
        "responses": {
          "401": {
            "description": "Problem with the client request",
            "headers": {
              "x-correlator": {
                "type": "string",
                "format": "uuid",
                "description": "Correlation id"
              }
            },
            "schema": {
              "$ref": "errors.json#/definitions/Unauthenticated"
            }
          }
        }
      }
    },
    "products": {
      "get": {
        "responses": {
          "401": {
            "description": "Problem with the client request",
            "headers": {
              "x-correlator": {
                "type": "string",
                "format": "uuid",
                "description": "Correlation id"
              }
            },
            "schema": {
              "$ref": "errors.json#/definitions/Unauthenticated"
            },
            "examples": {
              "application/json": {
                "code": "UNAUTHENTICATED",
                "message": "Authentication error"
              }
            }
          }
        }
      }
    }
  }
}

var errors_schema = {
  "info": {
    "description": "Common errors",
    "version": "3.0.1",
    "title": "Common errors",
    "contact": {
      "name": "Team"
    }
  },
  "definitions": {
    "ModelError": {
      "type": "object",
      "required": [
        "message"
      ],
      "properties": {
        "message": {
          "type": "string",
          "description": "A human readable description"
        }
      }
    },
    "Unauthenticated": {
      "allOf": [
        {
          "type": "object",
          "required": [
            "code"
          ],
          "properties": {
            "code": {
              "type": "string",
              "enum": [
                "UNAUTHENTICATED"
              ],
              "default": "UNAUTHENTICATED",
              "description": "Request not authenticated due to missing, invalid, or expired credentials."
            }
          }
        },
        {
          "$ref": "#/definitions/ModelError"
        }
      ]
    }
  }
}

ajv.addSchema(errors_schema, 'errors.json');
ajv.addSchema(schema, 'swagger.json')



var testajv = ajv.compile({ $ref: 'errors.json#/definitions/Unauthenticated' });

console.log(testajv({"code": 123, "message":"token expired"}), testajv.errors); // Fails

console.log(testajv({"code": "AAA", "message":"token expired"}), testajv.errors); // Fails

console.log(testajv({"code": "UNAUTHENTICATED", "message":"token expired"}), testajv.errors); // Passes

var testajv2 = ajv.compile({ $ref: 'swagger.json#/paths/products/get/responses/401/schema' });

console.log(testajv2({"code": 123, "message":"token expired"}), testajv2.errors); // Fails

console.log(testajv2({"code": "AAA", "message":"token expired"}), testajv2.errors); // Fails

console.log(testajv2({"code": "UNAUTHENTICATED", "message":"token expired"}), testajv2.errors); // Passes

Дополнительная информация в соответствующем ajv выпуске: https://github.com/epoberezkin/ajv/issues/195

person vearutop    schedule 04.10.2018
comment
Спасибо @vearutop, работает! Что касается, я вижу, два файла ajv.compile, которые вы использовали, пытаются показать мне два пути к одному и тому же, верно? Затем мой json-файл swagger длиннее, чем здесь, с несколькими номерами ответов HTML (200, 400, 401, 503 ...). Итак, должен ли я компилировать каждую схему номеров ответов HTML? - person Sebastian Diaz; 05.10.2018
comment
И напоследок (может быть, для открытия нового вопроса, в таком случае скажите мне, чтобы я это сделал). Настоящее имя ключевого слова, которое я упомянул как продукты, - / users / {user_id} / products. Как я могу написать это в функции компиляции? Я попытался экранировать символы '/' и '{}' с помощью '\', но это не сработало. - person Sebastian Diaz; 05.10.2018
comment
Да, два ajv.compile просто показывают два пути достижения вашей схемы. Вам нужен отдельный экземпляр валидатора (ajv.compile) для каждой схемы, поэтому, если вы хотите проверить несколько типов ответа, вам понадобится несколько валидаторов. При желании вы можете использовать ajv.validate(schema, data), но компиляция и повторное использование валидатора, вероятно, улучшит производительность. Указатель JSON требует экранирования ~ = ›~0 и / =› ~1 (tools.ietf.org / html / rfc6901 # section-3). Итак, ваш /users/{user_id}/products ref будет ~1users~1{user_id}~1products. - person vearutop; 05.10.2018
comment
Отлично, спасибо за вашу помощь. Оно работает! У меня есть новый вопрос по этому поводу. Может быть, ты и здесь мне поможешь. - person Sebastian Diaz; 09.10.2018

Используйте тип как число вместо строки в свойстве кода.

"properties": {
        "code": {
          "type": "number",
          "enum": [
            "UNAUTHENTICATED"
          ],
          "default": "UNAUTHENTICATED",
          "description": "Request not authenticated due to missing, invalid, or expired credentials."
        }
      }
person Usira    schedule 03.10.2018
comment
Я пробовал это, но тот же результат, правда, для ответа с кодом: 123 или code: UNAUTHENTICATED. - person Sebastian Diaz; 03.10.2018