如何将公共存储库与 Contract 一起使用,而不是将它们存储在 Producer 中?
另一种存储合同的方法,而不是将它们与生产者一起保存 他们在一个共同的地方。这种情况可能与安全问题有关(其中 使用者无法克隆创建者的代码)。此外,如果您将 Contract 保存在一个地方, 然后,作为生产者,您知道您有多少个使用者以及您可能会打破哪个使用者 替换为您的本地更改。
存储库结构
假设我们有一个坐标为 和 3 的 producer
使用者:、 、 和 。然后,在具有 common 的
contracts,您可以进行以下设置(您可以在 Spring Cloud Contract 的 repository 子文件夹中查看)。
下面的清单显示了这样的结构:com.example:server
client1
client2
client3
samples/standalone/contracts
├── com
│ └── example
│ └── server
│ ├── client1
│ │ └── expectation.groovy
│ ├── client2
│ │ └── expectation.groovy
│ ├── client3
│ │ └── expectation.groovy
│ └── pom.xml
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
└── assembly
└── contracts.xml
在斜杠分隔的文件夹 () 下,您有
三个使用者的期望 (, , 和 )。期望是标准的 Groovy DSL
Contract 文件,如本文档中所述。此存储库必须生成一个 JAR 文件,该文件将
一对一到存储库的内容。groupid/artifact id
com/example/server
client1
client2
client3
以下示例显示了文件夹内的文件:pom.xml
server
除了 Spring Cloud Contract Maven 插件之外,没有其他依赖项。
这些文件是消费者端运行本地安装所必需的
Producer 项目的存根。pom.xml
mvn clean install -DskipTests
根文件夹中的文件可能如下所示:pom.xml
它使用汇编插件来构建包含所有协定的 JAR。以下示例 显示了这样的设置:
工作流
该工作流假定 Spring Cloud Contract 同时在使用者和 producer 端。在公共存储库中还有适当的插件设置,其中 合同。CI 作业是为公共存储库设置的,以构建所有 合同并将其上传到 Nexus 或 Artifactory。下图显示了此 UML 工作流程:
消费者
当消费者想要离线处理 Contract 而不是克隆 producer 时
code 中,消费者团队会克隆公共仓库,然后转到所需的生产者的
文件夹(例如 )并运行
在本地安装从 Contract 转换的 stub。com/example/server
mvn clean install -DskipTests
您需要在本地安装 Maven。 |
制作人
作为生产者,您可以更改 Spring Cloud Contract Verifier 以提供 URL 和 包含 Contract 的 JAR 的依赖项,如下所示:
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<configuration>
<contractsMode>REMOTE</contractsMode>
<contractsRepositoryUrl>
https://link/to/your/nexus/or/artifactory/or/sth
</contractsRepositoryUrl>
<contractDependency>
<groupId>com.example.standalone</groupId>
<artifactId>contracts</artifactId>
</contractDependency>
</configuration>
</plugin>
通过此设置,将从 下载带有 of 和 of 的 JAR 。是的
然后解压到本地临时文件夹中,并选择其中的 Contract 作为用于生成测试和存根的 Contract。由于
根据此约定,生产者团队可以知道哪些 Consumer 团队在何时中断
进行了一些不兼容的更改。groupid
com.example.standalone
artifactid
contracts
link/to/your/nexus/or/artifactory/or/sth
com/example/server
流的其余部分看起来相同。
如何定义每个主题而不是每个生产者的消息收发合同?
为避免公共存储库中的消息传递合同重复,当几个生产者将消息写入一个主题时, 我们可以创建一个结构,其中 REST 合同放置在每个生产者和消息传递的文件夹中 合同放置在每个主题的文件夹中。
对于 Maven 项目
为了能够在生产者端工作,我们应该为
按我们感兴趣的消息主题筛选常见的存储库 JAR 文件。Maven Spring Cloud Contract 插件的属性
让我们这样做。此外,还需要指定,因为默认路径为
公共存储库 。以下示例显示了一个 Maven
Spring Cloud Contract 插件:includedFiles
contractsPath
groupid/artifactid
<plugin>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
<version>${spring-cloud-contract.version}</version>
<configuration>
<contractsMode>REMOTE</contractsMode>
<contractsRepositoryUrl>https://link/to/your/nexus/or/artifactory/or/sth</contractsRepositoryUrl>
<contractDependency>
<groupId>com.example</groupId>
<artifactId>common-repo-with-contracts</artifactId>
<version>+</version>
</contractDependency>
<contractsPath>/</contractsPath>
<baseClassMappings>
<baseClassMapping>
<contractPackageRegex>.*messaging.*</contractPackageRegex>
<baseClassFQN>com.example.services.MessagingBase</baseClassFQN>
</baseClassMapping>
<baseClassMapping>
<contractPackageRegex>.*rest.*</contractPackageRegex>
<baseClassFQN>com.example.services.TestBase</baseClassFQN>
</baseClassMapping>
</baseClassMappings>
<includedFiles>
<includedFile>**/${project.artifactId}/**</includedFile>
<includedFile>**/${first-topic}/**</includedFile>
<includedFile>**/${second-topic}/**</includedFile>
</includedFiles>
</configuration>
</plugin>
上述 Maven 插件中的许多值都可以更改。我们将其包括 说明目的,而不是试图提供一个 “典型 ”的例子。 |
对于 Gradle 项目
要使用 Gradle 项目,请执行以下操作:
-
为常见的存储库依赖项添加自定义配置,如下所示:
ext { contractsGroupId = "com.example" contractsArtifactId = "common-repo" contractsVersion = "1.2.3" } configurations { contracts { transitive = false } }
-
将 common repository 依赖项添加到您的 Classpath 中,如下所示:
dependencies { contracts "${contractsGroupId}:${contractsArtifactId}:${contractsVersion}" testCompile "${contractsGroupId}:${contractsArtifactId}:${contractsVersion}" }
-
将依赖项下载到相应的文件夹,如下所示:
task getContracts(type: Copy) { from configurations.contracts into new File(project.buildDir, "downloadedContracts") }
-
解压缩 JAR,如下所示:
task unzipContracts(type: Copy) { def zipFile = new File(project.buildDir, "downloadedContracts/${contractsArtifactId}-${contractsVersion}.jar") def outputDir = file("${buildDir}/unpackedContracts") from zipTree(zipFile) into outputDir }
-
清理未使用的合约,如下所示:
task deleteUnwantedContracts(type: Delete) { delete fileTree(dir: "${buildDir}/unpackedContracts", include: "**/*", excludes: [ "**/${project.name}/**"", "**/${first-topic}/**", "**/${second-topic}/**"]) }
-
创建任务依赖关系,如下所示:
unzipContracts.dependsOn("getContracts") deleteUnwantedContracts.dependsOn("unzipContracts") build.dependsOn("deleteUnwantedContracts")
-
通过指定包含 Contract 的目录来配置插件,方法是将 属性,如下所示:
contractsDslDir
contracts { contractsDslDir = new File("${buildDir}/unpackedContracts") }