spring-cloud-contract 是否可以为合同和合同测试规范制定一个 OpenApi3 定义?

Is it possible to have one OpenApi3 definition for contract and for contract test specification which will be parsable by spring-cloud-contract?

我们的想法是将一个文件用于合同定义和合同测试规范。

我找到了插件 'guru.springframework:spring-cloud-contract-oa3:2.1.2.0',它应该可以满足我的需求,但对我来说却失败了。 我的配置基于此 repo 中的示例 https://github.com/springframeworkguru/sccoa3-fraud-example

这是执行后的错误信息

gradle build clean

Error Processing yaml file. Skipping Contract Generation 
java.lang.IllegalStateException: Exception occurred while processing the file [.../build/stubs/META-INF/com.bla.bla/api-mlpe/0.0.1-SNAPSHOT/contracts/v1/0/openapi.yml]
...
Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "openapi" (class org.springframework.cloud.contract.verifier.converter.YamlContract), not marked as ignorable (10 known properties: "response", "ignored", "label", "outputMessage", "input", "name", "description", "request", "inProgress", "priority"])
 at [Source: UNKNOWN; line: -1, column: -1] (through reference chain: org.springframework.cloud.contract.verifier.converter.YamlContract["openapi"])

这是我的 build.gradle 配置

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.springframework.cloud:spring-cloud-contract-gradle-plugin:2.2.4.RELEASE'
    }
}

plugins {
    id 'org.springframework.boot' version '2.3.4.RELEASE'
    id 'io.spring.dependency-management' version '1.0.10.RELEASE'
    id 'java'
}

apply plugin: 'spring-cloud-contract'

group = 'com.bla.bla'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'

repositories {
    mavenCentral()
}

ext {
    set('springCloudVersion', "Hoxton.SR8")
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework:spring-web:5.2.9.RELEASE'
    implementation 'guru.springframework:spring-cloud-contract-oa3:2.1.2.0'
    testImplementation('org.springframework.boot:spring-boot-starter-test') {
        exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
    }
    testImplementation 'org.springframework.cloud:spring-cloud-starter-contract-verifier'
}

dependencyManagement {
    imports {
        mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
    }
}

contracts {
    testFramework = org.springframework.cloud.contract.verifier.config.TestFramework.JUNIT5
}

test {
    useJUnitPlatform()
    testLogging {
        exceptionFormat = 'full'
    }
}

这里是 openapi.yml(基于前面提到的回购协议,放在 test/resources/contracts 下)

openapi: 3.0.3
info:
  description: Spring Cloud Contract Verifier Http Server OA3 Sample
  version: "1.0.0"
  title: Fraud Service API
paths:
  /v1/consumers/global-consumers:
    put:
      summary: Perform Fraud Check
      x-contracts:
        - contractId: 1
          name: Should Mark Client as Fraud
          priority: 1
        - contractId: 2
          name: Should Not Mark Client as Fraud
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                "client.id":
                  type: integer
                loanAmount:
                  type: integer
#START REQUEST - part with SPEC definitions
        x-contracts:
          - contractId: 1
            body:
              "client.id": 1234567890
              loanAmount: 99999
            matchers:
              body:
                - path: $.['client.id']
                  type: by_regex
                  value: "[0-9]{10}"
          - contractId: 2
            body:
              "client.id": 1234567890
              loanAmount: 123.123
            matchers:
              body:
                - path: $.['client.id']
                  type: by_regex
                  value: "[0-9]{10}"
#END REQUEST - part with SPEC definitions
      responses:
        '200':
          description: created ok
          content:
            application/json:
              schema:
                type: object
                properties:
                  fraudCheckStatus:
                    type: string
                  "rejection.reason":
                    type: string
#START RESPONSE - part with SPEC definitions
          x-contracts:
            - contractId: 1
              body:
                fraudCheckStatus: "FRAUD"
                "rejection.reason": "Amount too high"
              headers:
                Content-Type: application/json;charset=UTF-8
            - contractId: 2
              body:
                fraudCheckStatus: "OK"
                "rejection.reason": null
              headers:
                Content-Type: application/json;charset=UTF-8
              matchers:
                body:
                  - path: $.['rejection.reason']
                    type: by_command
                    value: assertThatRejectionReasonIsNull($it)
#END RESPONSE - part with SPEC definitions

除了失败之外,我不确定我的配置是否适合实现我的目标:-) 我的目标是拥有一个描述合同的文件(在 OpenApi3 标准中),包括合同测试规范(在同一个文件中,不应破坏 OpenAPI3 规范标准)并基于我要生成的文件:

是否可以按照我指定的方式全部拥有?如果是 - 如何实现? 提到的插件在提供的配置中对我不起作用。

您需要将 guru.springframework:spring-cloud-contract-oa3:2.1.2.0 依赖添加到插件的类路径而不是项目的类路径。

不是这样的:

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.springframework.cloud:spring-cloud-contract-gradle-plugin:2.2.4.RELEASE'
    }
}

plugins {
    id 'org.springframework.boot' version '2.3.4.RELEASE'
    id 'io.spring.dependency-management' version '1.0.10.RELEASE'
    id 'java'
}

像这样:

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.springframework.cloud:spring-cloud-contract-gradle-plugin:2.2.4.RELEASE'
        classpath 'guru.springframework:spring-cloud-contract-oa3:2.1.2.0'
    }
}

plugins {
    id 'org.springframework.boot' version '2.3.4.RELEASE'
    id 'io.spring.dependency-management' version '1.0.10.RELEASE'
    id 'java'
}

我们也在文档中描述了这一点(在另一个例子中)https://docs.spring.io/spring-cloud-contract/docs/2.2.4.RELEASE/reference/html/advanced.html#customization-plugin-dep


哦,真是个蹩脚的错误。谢谢@marcin-grzejszczak 应用更改后,它看起来更好。解析 yaml 规范有效。

现在我在 generateClientStubs

中遇到问题

在 运行 之后 gradle clean build -x test --stacktrace

我正在

Execution failed for task ':generateClientStubs'.
13:43:33.759 [ERROR] [org.gradle.internal.buildevents.BuildExceptionReporter] > Class org.springframework.cloud.contract.verifier.converter.YamlContract does not implement the requested interface groovy.lang.GroovyObject
….
Caused by: java.lang.IncompatibleClassChangeError: Class org.springframework.cloud.contract.verifier.converter.YamlContract does not implement the requested interface groovy.lang.GroovyObject
        at org.springframework.cloud.contract.verifier.converter.OpenApiContractConverter$_convertFrom_closure2$_closure5$_closure6.doCall(OpenApiContractConverter.groovy:84)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

我发现前段时间报告的问题看起来很相似 [https://github.com/spring-cloud/spring-cloud-contract/issues/1289] 现在我的构建 gradle 中定义的版本是两天前通过 spring initializr 生成的版本,所以我假设它们应该是兼容的。 这个问题有没有可能是因为添加了‘guru.springframework:spring-cloud-contract-oa3’插件? 乍一看,它很像提到的类似报告问题。