如何使用 Git 作為契約和服務存根的儲存?

在多語言(Polyglot)世界中,有些語言不使用像 Artifactory 和 Nexus 那樣的二進位制儲存。從 Spring Cloud Contract 2.0.0 版本開始,我們提供了將契約和服務存根儲存在 SCM(原始碼控制管理)倉庫中的機制。目前,唯一支援的 SCM 是 Git。

該倉庫必須具有以下設定(您可以從此處簽出)

.
└── META-INF
    └── com.example
        └── beer-api-producer-git
            └── 0.0.1-SNAPSHOT
                ├── contracts
                │   └── beer-api-consumer
                │       ├── messaging
                │       │   ├── shouldSendAcceptedVerification.groovy
                │       │   └── shouldSendRejectedVerification.groovy
                │       └── rest
                │           ├── shouldGrantABeerIfOldEnough.groovy
                │           └── shouldRejectABeerIfTooYoung.groovy
                └── mappings
                    └── beer-api-consumer
                        └── rest
                            ├── shouldGrantABeerIfOldEnough.json
                            └── shouldRejectABeerIfTooYoung.json

META-INF 資料夾下

  • 我們按 groupId 對應用進行分組(例如 com.example)。

  • 每個應用都由其 artifactId 表示(例如 beer-api-producer-git)。

  • 接下來,每個應用都按其版本進行組織(例如 0.0.1-SNAPSHOT)。從 Spring Cloud Contract 2.1.0 版本開始,您可以按以下方式指定版本(假設您的版本遵循語義化版本控制):

    • +latest:查詢最新版本的存根(假設快照始終是給定修訂號的最新 artifact)。這意味著

      • 如果您有 1.0.0.RELEASE2.0.0.BUILD-SNAPSHOT2.0.0.RELEASE,我們假設最新版本是 2.0.0.BUILD-SNAPSHOT

      • 如果您有 1.0.0.RELEASE2.0.0.RELEASE,我們假設最新版本是 2.0.0.RELEASE

      • 如果您有一個名為 latest+ 的版本,我們將選擇該資料夾。

    • release:查詢最新版本的發行版存根。這意味著

      • 如果您有 1.0.0.RELEASE2.0.0.BUILD-SNAPSHOT2.0.0.RELEASE,我們假設最新版本是 2.0.0.RELEASE

      • 如果您有一個名為 release 的版本,我們將選擇該資料夾。

最後,還有兩個資料夾

  • contracts:推薦的做法是將每個消費者所需的契約儲存在以消費者名稱命名的資料夾中(例如 beer-api-consumer)。這樣,您就可以使用 stubs-per-consumer 特性。進一步的目錄結構是任意的。

  • mappings:Maven 或 Gradle Spring Cloud Contract 外掛將存根伺服器對映推送到此資料夾。在消費者端,Stub Runner 會掃描此資料夾以啟動帶有存根定義的存根伺服器。資料夾結構是 contracts 子資料夾中建立的結構的複製。

協議約定

為了控制契約源(無論是二進位制儲存還是 SCM 倉庫)的型別和位置,您可以使用倉庫 URL 中的協議。Spring Cloud Contract 會遍歷註冊的協議解析器,並嘗試獲取契約(透過外掛)或存根(從 Stub Runner)。

對於 SCM 功能,目前我們支援 Git 倉庫。要使用它,在需要放置倉庫 URL 的屬性中,您必須在連線 URL 前加上 git:// 字首。以下列表顯示了一些示例:

git://file:///foo/bar
git://https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git
git://[email protected]:spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git

生產者

對於生產者,要使用 SCM(原始碼控制管理)方式,我們可以重用用於外部契約的相同機制。我們將 Spring Cloud Contract 路由到使用以 git:// 協議開頭的 URL 中的 SCM 實現。

您必須在 Maven 中手動新增 pushStubsToScm goal,或在 Gradle 中使用(繫結)pushStubsToScm task。我們不會將存根推送到 Git 倉庫的 origin

以下列表包含 Maven 和 Gradle 構建檔案中的相關部分:

Maven
<plugin>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-contract-maven-plugin</artifactId>
    <version>${spring-cloud-contract.version}</version>
    <extensions>true</extensions>
    <configuration>
        <!-- Base class mappings etc. -->

        <!-- We want to pick contracts from a Git repository -->
        <contractsRepositoryUrl>git://https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git</contractsRepositoryUrl>

        <!-- We reuse the contract dependency section to set up the path
        to the folder that contains the contract definitions. In our case the
        path will be /groupId/artifactId/version/contracts -->
        <contractDependency>
            <groupId>${project.groupId}</groupId>
            <artifactId>${project.artifactId}</artifactId>
            <version>${project.version}</version>
        </contractDependency>

        <!-- The contracts mode can't be classpath -->
        <contractsMode>REMOTE</contractsMode>
    </configuration>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <!-- By default we will not push the stubs back to SCM,
                you have to explicitly add it as a goal -->
                <goal>pushStubsToScm</goal>
            </goals>
        </execution>
    </executions>
</plugin>
Gradle
contracts {
	// We want to pick contracts from a Git repository
	contractDependency {
		stringNotation = "${project.group}:${project.name}:${project.version}"
	}
	/*
	We reuse the contract dependency section to set up the path
	to the folder that contains the contract definitions. In our case the
	path will be /groupId/artifactId/version/contracts
	 */
	contractRepository {
		repositoryUrl = "git://https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git"
	}
	// The mode can't be classpath
	contractsMode = "REMOTE"
	// Base class mappings etc.
}

/*
In this scenario we want to publish stubs to SCM whenever
the `publish` task is invoked
*/
publish.dependsOn("publishStubsToScm")

您還可以進一步自定義 publishStubsToScm gradle task。在以下示例中,自定義了此 task 以從本地 Git 倉庫獲取契約:

gradle
publishStubsToScm {
	// We want to modify the default set up of the plugin when publish stubs to scm is called
	// We want to pick contracts from a Git repository
	contractDependency {
		stringNotation = "${project.group}:${project.name}:${project.version}"
	}
	/*
	We reuse the contract dependency section to set up the path
	to the folder that contains the contract definitions. In our case the
	path will be /groupId/artifactId/version/contracts
	 */
	contractRepository {
		repositoryUrl = "git://file://${new File(project.rootDir, "../target")}/contract_empty_git/"
	}
	// We set the contracts mode to `LOCAL`
	contractsMode = "LOCAL"
	}
重要

2.3.0.RELEASE 版本開始,以前用於 publishStubsToScm 自定義的 customize{} 閉包不再可用。設定應直接應用於 publishStubsToScm 閉包內部,如前面的示例所示。

透過此設定

  • 將 Git 專案克隆到臨時目錄

  • SCM 存根下載器會訪問 META-INF/groupId/artifactId/version/contracts 資料夾查詢契約。例如,對於 com.example:foo:1.0.0,路徑將是 META-INF/com.example/foo/1.0.0/contracts

  • 根據契約生成測試。

  • 根據契約建立存根。

  • 測試通過後,將存根提交到克隆的倉庫中。

  • 最後,向該倉庫的 origin 傳送 push 操作。

在本地儲存契約的生產者

另一種使用 SCM 作為存根和契約目標的選項是將契約與生產者一起儲存在本地,然後只將契約和存根推送到 SCM。以下連結展示了使用 Maven 和 Gradle 實現此目標所需的設定。

透過此設定

  • 從預設的 src/test/resources/contracts 目錄獲取契約。

  • 根據契約生成測試。

  • 根據契約建立存根。

  • 測試通過後

    • 將 Git 專案克隆到臨時目錄。

    • 將存根和契約提交到克隆的倉庫中。

  • 最後,向該倉庫的 origin 執行 push 操作。

將契約與生產者一起儲存,並將存根儲存在外部倉庫

您還可以將契約儲存在生產者倉庫中,但將存根儲存在外部 Git 倉庫中。當您想使用基本的消費者-生產者協作流程但無法使用 artifact 倉庫來儲存存根時,這非常有用。

為此,請使用常規的生產者設定,然後新增 pushStubsToScm goal,並將 contractsRepositoryUrl 設定為您要儲存存根的倉庫。

消費者

在消費者端,當傳遞 repositoryRoot 引數時,無論是透過 @AutoConfigureStubRunner 註解、JUnit 4 rule、JUnit 5 extension 還是屬性,您都可以傳遞以 git:// 協議為字首的 SCM 倉庫 URL。以下示例展示瞭如何操作:

@AutoConfigureStubRunner(
    stubsMode="REMOTE",
    repositoryRoot="git://https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git",
    ids="com.example:bookstore:0.0.1.RELEASE"
)

透過此設定

  • 將 Git 專案克隆到臨時目錄。

  • SCM 存根下載器會訪問 META-INF/groupId/artifactId/version/ 資料夾查詢存根定義和契約。例如,對於 com.example:foo:1.0.0,路徑將是 META-INF/com.example/foo/1.0.0/

  • 啟動存根伺服器並載入對映。

  • 讀取訊息傳遞定義並在訊息傳遞測試中使用。