diff --git a/docs/src/main/asciidoc/spring-cloud-wiremock.adoc b/docs/src/main/asciidoc/spring-cloud-wiremock.adoc index ba88b8f67c..dfbdc11f23 100644 --- a/docs/src/main/asciidoc/spring-cloud-wiremock.adoc +++ b/docs/src/main/asciidoc/spring-cloud-wiremock.adoc @@ -58,6 +58,14 @@ NOTE: Actually, WireMock always loads mappings from `src/test/resources/mappings well as* the custom locations in the stubs attribute. To change this behavior, you can also specify a files root as described in the next section of this document. +If you're using Spring Cloud Contract's default stub jars, then your +stubs are stored under `/META-INF/group-id/artifact-id/versions/mappings/` folder. If you want to register all stubs from that location, from all embedded JARs, then it's enough to use the following syntax. + +[source,java,indent=0] +---- +include::{wiremock_tests}/src/test/java/org/springframework/cloud/contract/wiremock/AutoConfigureWireMockFilesApplicationWithUrlResourceTests.java[tags=load_all_stubs] +---- + === Using Files to Specify the Stub Bodies WireMock can read response bodies from files on the classpath or the file system. In that diff --git a/spring-cloud-contract-wiremock/pom.xml b/spring-cloud-contract-wiremock/pom.xml index b1c7ea6efa..7bfab6d312 100644 --- a/spring-cloud-contract-wiremock/pom.xml +++ b/spring-cloud-contract-wiremock/pom.xml @@ -89,5 +89,31 @@ spring-cloud-contract-verifier test + + sagan + sagan-site + stubs + 1.0.0.BUILD-SNAPSHOT + test + + + * + * + + + + + com.example.github + github-webhook + stubs + 0.0.1-SNAPSHOT + test + + + * + * + + + diff --git a/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/AutoConfigureWireMockFilesApplicationWithUrlResourceTests.java b/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/AutoConfigureWireMockFilesApplicationWithUrlResourceTests.java new file mode 100644 index 0000000000..3bd0d0d356 --- /dev/null +++ b/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/AutoConfigureWireMockFilesApplicationWithUrlResourceTests.java @@ -0,0 +1,190 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.springframework.cloud.contract.wiremock; + +import java.util.ArrayList; +import java.util.List; + +import com.fasterxml.jackson.annotation.JsonInclude; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.client.RestTemplate; + +import static org.assertj.core.api.BDDAssertions.then; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = AutoConfigureWireMockFilesApplicationWithUrlResourceTests.Config.class, + properties = "app.baseUrl=http://localhost:${wiremock.server.port}", + webEnvironment = WebEnvironment.NONE) +// tag::load_all_stubs[] +@AutoConfigureWireMock(port = 0, stubs = "classpath*:/META-INF/**/mappings/**/*.json") +// end::load_all_stubs[] +public class AutoConfigureWireMockFilesApplicationWithUrlResourceTests { + + @Autowired + private RestTemplateSaganClient client; + + @Test + public void contextLoads() throws Exception { + Release release = this.client.getRelease("spring-framework", "5.0.0.RC4"); + + then(release.releaseStatus).isEqualTo("PRERELEASE"); + then(release.refDocUrl).isEqualTo("http://docs.spring.io/spring/docs/{version}/spring-framework-reference/"); + then(release.apiDocUrl).isEqualTo("http://docs.spring.io/spring/docs/{version}/javadoc-api/"); + then(release.groupId).isEqualTo("org.springframework"); + then(release.artifactId).isEqualTo("spring-context"); + then(release.repository.id).isEqualTo("spring-milestones"); + then(release.repository.name).isEqualTo("Spring Milestones"); + then(release.repository.url).isEqualTo("https://repo.spring.io/libs-milestone"); + then(release.repository.snapshotsEnabled).isFalse(); + then(release.version).isEqualTo("5.0.0.RC4"); + then(release.current).isFalse(); + then(release.generalAvailability).isFalse(); + then(release.preRelease).isTrue(); + then(release.versionDisplayName).isEqualTo("5.0.0 RC4"); + then(release.snapshot).isFalse(); + } + + @Configuration + @EnableAutoConfiguration + static class Config { + @Bean + RestTemplateSaganClient restTemplateSaganClient(@Value("${app.baseUrl}") String url) { + return new RestTemplateSaganClient(new RestTemplate(), url); + } + } + +} + +/** + * @author Marcin Grzejszczak + */ +class RestTemplateSaganClient { + + private final RestTemplate restTemplate; + private final String baseUrl; + + RestTemplateSaganClient(RestTemplate restTemplate, String url) { + this.restTemplate = restTemplate; + this.baseUrl = url; + } + + public Release getRelease(String projectName, String releaseVersion) { + return this.restTemplate.getForObject(this.baseUrl + "/project_metadata/{projectName}/releases/{releaseVersion}", Release.class, projectName, releaseVersion); + } +} + +/** + * @author Marcin Grzejszczak + */ +@JsonInclude(value = JsonInclude.Include.NON_NULL) +class Project { + public String id = ""; + public String name = ""; + public String repoUrl = ""; + public String siteUrl = ""; + public String category = ""; + public String stackOverflowTags; + public List projectReleases = new ArrayList<>(); + public List stackOverflowTagList = new ArrayList<>(); + public Boolean aggregator; + + @Override public String toString() { + return "Project{" + "id='" + this.id + '\'' + ", name='" + this.name + '\'' + ", repoUrl='" + + this.repoUrl + '\'' + ", siteUrl='" + this.siteUrl + '\'' + ", category='" + + this.category + '\'' + ", stackOverflowTags='" + this.stackOverflowTags + '\'' + + ", projectReleases=" + this.projectReleases + ", stackOverflowTagList=" + + this.stackOverflowTagList + ", aggregator=" + this.aggregator + '}'; + } +} + +/** + * @author Marcin Grzejszczak + */ +@JsonInclude(value = JsonInclude.Include.NON_NULL) +class Release { + public String releaseStatus = ""; + public String refDocUrl = ""; + public String apiDocUrl = ""; + public String groupId = ""; + public String artifactId = ""; + public Repository repository; + public String version = ""; + public boolean current; + public boolean generalAvailability; + public boolean preRelease; + public String versionDisplayName = ""; + public boolean snapshot; + + @Override public String toString() { + return "Release{" + "releaseStatus='" + this.releaseStatus + '\'' + ", refDocUrl='" + + this.refDocUrl + '\'' + ", apiDocUrl='" + this.apiDocUrl + '\'' + ", groupId='" + + this.groupId + '\'' + ", artifactId='" + this.artifactId + '\'' + ", repository=" + + this.repository + ", version='" + this.version + '\'' + ", current=" + this.current + + ", generalAvailability=" + this.generalAvailability + ", preRelease=" + + this.preRelease + ", versionDisplayName='" + this.versionDisplayName + '\'' + + ", snapshot=" + this.snapshot + '}'; + } +} + +/** + * @author Marcin Grzejszczak + */ +@JsonInclude(value = JsonInclude.Include.NON_NULL) +class ReleaseUpdate { + public String groupId = ""; + public String artifactId = ""; + public String version = ""; + public String releaseStatus = ""; + public String refDocUrl = ""; + public String apiDocUrl = ""; + public Boolean current; + public Repository repository; + + @Override public String toString() { + return "ReleaseUpdate{" + "groupId='" + this.groupId + '\'' + ", artifactId='" + + this.artifactId + '\'' + ", version='" + this.version + '\'' + ", releaseStatus='" + + this.releaseStatus + '\'' + ", refDocUrl='" + this.refDocUrl + '\'' + + ", apiDocUrl='" + this.apiDocUrl + '\'' + ", repository=" + this.repository + '}'; + } +} + +/** + * @author Marcin Grzejszczak + */ +@JsonInclude(value = JsonInclude.Include.NON_NULL) +class Repository { + public String id; + public String name; + public String url; + public Boolean snapshotsEnabled; + + @Override public String toString() { + return "Repository{" + "id='" + this.id + '\'' + ", name='" + this.name + '\'' + ", url='" + + this.url + '\'' + ", snapshotsEnabled=" + this.snapshotsEnabled + '}'; + } +} \ No newline at end of file diff --git a/spring-cloud-contract-wiremock/src/test/resources/root/mappings/accept.json b/spring-cloud-contract-wiremock/src/test/resources/root/mappings/accept.json new file mode 100644 index 0000000000..54d3a13c51 --- /dev/null +++ b/spring-cloud-contract-wiremock/src/test/resources/root/mappings/accept.json @@ -0,0 +1,11 @@ +{ + "request" : { + "urlPath" : "/poster", + "method" : "POST", + "headers" : { "Accept" : { "equalTo" : "text/plain" }} + }, + "response" : { + "status" : 200, + "body" : "Accepted World" + } +} \ No newline at end of file diff --git a/spring-cloud-contract-wiremock/src/test/resources/root/mappings/url-matches.json b/spring-cloud-contract-wiremock/src/test/resources/root/mappings/url-matches.json new file mode 100644 index 0000000000..781c28907c --- /dev/null +++ b/spring-cloud-contract-wiremock/src/test/resources/root/mappings/url-matches.json @@ -0,0 +1,10 @@ +{ + "request": { + "method": "GET", + "urlPattern": "^.*[0-9]{3}/hello-url-matcher/" + }, + "response" : { + "status" : 200, + "body" : "Hello Url Matcher" + } +} \ No newline at end of file