OAS Validator | English

OpenAPI V3 Spec校验工具。

项目结构

  • oas-validator-core,核心API及骨架实现
  • oas-validator-core-spring,骨架的Spring Boot Starter
  • oas-validator-test,核心API的测试帮助类
  • oas-validator-style,风格校验实现
  • oas-validator-compatibility,兼容性校验实现
  • oas-validator-compatibility-spring,兼容性校验实现的Spring Boot Starter
  • oas-validator-web,校验工具的操作UI

风格校验

OAS必须符合OAS 3.0.2规范(比如属性的名称、REQUIED要求)。除此之外则是我们自己的定义的风格检查。

一些字符串匹配规则

  • lower-camel-case:首字母小写的驼峰,对应的正则^[a-z]+((\d)|([A-Z0-9][a-z0-9]+))*([A-Z])?$
  • upper-camel-case:首字母大写的驼峰,对应的正则^[A-Z]([a-z0-9]+[A-Z]?)*$
  • upper-hyphen-case:单词首字母大写,多个单词用-连接,比如Content-TypeAcceptX-Rate-Limit-Limit。对应的正则:^([A-Z][a-z0-9]*-)*([A-Z][a-z0-9]*)$

配置校验规则

下面是配置文件的例子style-check-rule.properties

#######################
# OpenAPI Object
#######################
# openapi property must be 3.0.x and >= 3.0.2
openAPI.openapi.gte=3.0.2
# tags property size should >= 1
openAPI.tags.size.gte=1
# security property size must == 0
openAPI.security.size.eq=0

#######################
# Info Object
#######################
# description property is required
info.description.required=true

#######################
# Tag Object
#######################
# name property, must be upper-camel-case
tag.name.case=upper-camel-case
# tag should be referenced by at least one Operation Object
tag.name.must_be_referenced=true
# description property, required
tag.description.required=true

#######################
# Paths Object
#######################
# path must be lower-camel-case, including Path Templating variable
paths.key.case=lower-camel-case

#######################
# Operation Object
#######################
# summary property, required
operation.summary.required=true
# operationId property, must be lower-camel-case
operation.operationId.case=lower-camel-case
# tags property, size must == 1
operation.tags.size.eq=1
# all tags should references which are defined in $.tags
operation.tags.element.must_reference_root_tags=true
# servers property, size must == 0
operations.servers.size.eq=0

#######################
# Parameter Object
#######################
# description property, required
parameter.description.required=true
# name property, for header parameter, must be upper-hyphen-case
parameter.name.header.case=upper-hyphen-case
# name property, for cookie parameter, must be lower-camel-case
parameter.name.cookie.case=lower-camel-case
# name property, for path parameter, must be lower-camel-case
parameter.name.path.case=lower-camel-case
# name property, for query parameter, must be lower-camel-case
parameter.name.query.case=lower-camel-case

#######################
# RequestBody Object
#######################
# description property, required
requestBody.description.required=true

#######################
# Response Object
#######################
# headers property's key must be upper-hyphen-case
response.headers.key.case=upper-hyphen-case

#######################
# Schema Object
#######################
# title property, required if parent is Schema Object or Components Object
schema.title.required=true
# properties property, name(properties key) must be lower-camel-case
schema.properties.key.case=lower-camel-case

#######################
# Encoding Object
#######################
# headers property's key must be upper-hyphen-case
encoding.headers.key.case=upper-hyphen-case

#######################
# Header Object
#######################
# description property, required
header.description.required=true

#######################
# Components Object
#######################
# schemas property's key must be upper-camel-case
components.schemas.key.case=upper-camel-case
# responses property's key must be upper-camel-case
components.responses.key.case=upper-camel-case
# parameters property's key must be upper-camel-case
components.parameters.key.case=upper-camel-case
# examples property's key must be upper-camel-case
components.examples.key.case=upper-camel-case
# requestBodies property's key must be upper-camel-case
components.requestBodies.key.case=upper-camel-case
# headers property's key must be upper-hyphen-case
components.headers.key.case=upper-hyphen-case
# links property's key must be upper-camel-case
components.links.key.case=upper-hyphen-case
# callbacks property's key must be upper-camel-case
components.callbacks.key.case=upper-camel-case
# headers property's key must be upper-camel-case
components.headers.key.case=upper-camel-case

兼容性检查

对新旧两个版本的OAS做兼容性检查。

OAS可以使用Reference Object来描述Spec,两个不同的OAS会出现描述不同但语义相同的情况。比如下面的旧OAS没有使用Reference Object,而新OAS则使用了的情况:

旧OAS

openapi: "3.0.0"
info:
  version: 1.0.0
  title: Swagger Petstore
  license:
    name: MIT
servers:
  - url: http://petstore.swagger.io/v1
paths:
  /pets:
    post:
      summary: List all pets
      operationId: listPets
      requestBody:
        content:
          application/json:
            schema:
              type: array
              items:
                type: object
                properties:
                  Foo:
                    type: string
      responses:
        '200':
          description: A paged array of pets

新OAS

paths:
  /pets:
    post:
      operationId: listPets
      requestBody:
        content:
          application/json: 
            schema:
              $ref: '#/components/schemas/Foo'
      responses:
        '200':
          description: A paged array of pets
components:
  schemas:
    Foo:
      type: array
      items:
        type: object
        properties:
          Foo:
            type: string

因此在检查兼容性的时候会将新旧OAS的Reference Object做解析,然后再检查,下面是一段swagger-parser的例子:

OpenAPIV3Parser parser = new OpenAPIV3Parser();

ParseOptions parseOptions = new ParseOptions();
parseOptions.setResolve(true);
parseOptions.setResolveCombinators(true);
parseOptions.setResolveFully(true);
parseOptions.setFlatten(false);

SwaggerParseResult parseResult = parser.readContents(content, null, parseOptions);

因此,检查下来如果发现不兼容,那么所报告的位置会和原文档有所不同。

Paths Object doc

  • 新OAS必须包含旧OAS的所有的path,如果path使用了Path Templating,只要变量名发生了变化,那么即使语义上相同也会被认为不同,比如/pets/{foo}/pets/{bar}会被认定为不同。

Path Item Object doc

Operation Object doc

Parameter Object doc

  • required属性,只允许true(旧) -> false(新)
  • allowEmptyValue属性,只允许false(旧) -> true(新)的变化
  • style属性,新旧OAS必须保持一致
  • explode属性,新旧OAS必须保持一致
  • allowReserved属性,只允许false(旧) -> true(新)的变化
  • schema属性,见Schema Object兼容性检查
  • content属性,新OAS必须包含旧OAS的所有media type(content的key),且不能新增media type

Request Body Object doc

  • content属性,新OAS必须包含旧OAS的所有media type(content的key)
  • required属性,只允许true(旧) -> false(新)的变化

Media Type Object doc

Responses Object doc

  • default属性,如果旧OAS没有定义default,那么新OAS也不能定义default
  • {Http Status Code}属性,新OAS不允许新增。
  • Response Object兼容性检查

Response Object doc

Schema Object doc

OAS中定义,Schema Object可以直接或间接用在:

不同用途的兼容性检查规则有所不同。

用做请求时

原则上,当Schema Object用在请求时,只允许从紧到松的变化。

  • type, format组合所允许的变化范围
旧(type,format)新(type,format)
integer, nullinteger, int64
number, double
number, null
integer, int32integer, int64
integer, null
number, float
number, double
number, null
integer, int64integer, null
number, double
number, null
number, nullnumber, double
number, floatnumber, null
number, double
number, doublenumber, null
string, nullstring, password
string, passwordstring, null
  • allOfoneOfanyOf属性,在combine之后再做检查
  • multipleOf属性,如果旧OAS为null。新OAS必须==旧OAS或者新OAS是旧OAS的因子,比如6(旧)->3(新)
  • maximummaxLengthmaxItemsmaxProperties,如果旧OAS为null,那么新OAS也必须是null。其他情况,新OAS必须>=旧OAS。
  • minimumminLenghtminItemsminProperties,如果旧OAS为null,那么新OAS也必须是null。其他情况,新OAS必须<=旧OAS。
  • exclusiveMaximumexclusiveMinimum属性,仅允许true(旧)->false(新)的变化
  • uniqueItems属性,只允许true(旧)->false(新)的变化。
  • required属性,新OAS必须==旧OAS,或者新OAS是旧OAS的子集。
  • enum属性,新OAS必须==旧OAS,或者新OAS是旧OAS的超集。
  • properties属性,新OAS可以新增property name(properties的key)或者减少property name。
  • nullable属性,只允许false(旧)->true(新)的变化。
  • discriminator属性,新旧OAS必须完全一致。
  • xml属性,新旧OAS必须完全一致。
  • readOnlywriteOnly,新旧OAS必须完全一致。

用做响应时

原则上,当Schema Object用在响应时,只允许从松到紧的变化。

  • type, format组合所允许的变化范围
旧(type,format)新(type,format)
integer, nullinteger, int64
integer, int32
integer, int64integer, null
interger, int32
number, nullnumber, double
number, float
number, doublenumber, null
number, float
string, nullstring, password
string, passwordstring, null
  • allOfoneOfanyOf属性,在combine之后再做检查
  • multipleOf属性,如果旧OAS为null。新OAS必须==旧OAS或者新OAS是旧OAS的倍数,比如3(旧)->6(新)
  • maximummaxLengthmaxItemsmaxProperties,如果旧OAS为null,那么新OAS也必须是null。其他情况,新OAS必须<=旧OAS。
  • minimumminLenghtminItemsminProperties,如果旧OAS为null,那么新OAS也必须是null。其他情况,新OAS必须>=旧OAS。
  • exclusiveMaximumexclusiveMinimum属性,仅允许false(旧)->true(新)的变化
  • uniqueItems属性,只允许false(旧)->true(新)的变化。
  • required属性,新OAS必须==旧OAS或者,新OAS是旧OAS的超集。
  • enum属性,新OAS必须==旧OAS,或者新OAS是旧OAS的子集。
  • properties属性,新OAS可以新增property name(properties的key)或者减少property name。
  • nullable属性,只允许true(旧)->false(新)的变化。
  • discriminator属性,新旧OAS必须完全一致。
  • xml属性,新旧OAS必须完全一致。
  • readOnlywriteOnly,新旧OAS必须完全一致。

Encoding Object doc

PS. Encoding Object仅针对Request Body Object有用

  • contentType属性,新旧OAS必须保持一致
  • headers属性,新OAS不能新增旧OAS的header name(headers的key),但是可以删除header name
  • style属性,新旧OAS必须保持一致
  • explode属性,新旧OAS必须保持一致
  • allowReserved属性,只允许false(旧) -> true(新)的变化

Header Object doc

Components Object doc

Components Object定义的都是可复用OAS Object,而在检查兼容性的时候所有$ref都已被解析了,因此不需要对Components Object的属性做兼容性检查。