From 8cdad73158167bdb3e23337133982be795a06560 Mon Sep 17 00:00:00 2001 From: Dave Syer Date: Fri, 16 Jun 2017 14:36:49 +0100 Subject: [PATCH] Add support for placeholder resolution in stub file names E.g. document("{method-name}/{step}"). Fixes gh-84 --- .../com/example/fraud/StubGeneratorTests.java | 2 +- .../wiremock/restdocs/WireMockSnippet.java | 56 ++++++++++++------- .../restdocs/WireMockSnippetTests.java | 32 +++++++++-- 3 files changed, 64 insertions(+), 26 deletions(-) diff --git a/samples/standalone/restdocs/http-server/src/test/java/com/example/fraud/StubGeneratorTests.java b/samples/standalone/restdocs/http-server/src/test/java/com/example/fraud/StubGeneratorTests.java index 8e8a1462bf..5c5c2a853a 100644 --- a/samples/standalone/restdocs/http-server/src/test/java/com/example/fraud/StubGeneratorTests.java +++ b/samples/standalone/restdocs/http-server/src/test/java/com/example/fraud/StubGeneratorTests.java @@ -56,7 +56,7 @@ public class StubGeneratorTests { .andDo(verify().jsonPath("$.clientId") .jsonPath("$[?(@.loanAmount > 1000)]") .contentType(MediaType.valueOf("application/vnd.fraud.v1+json")) - .stub("markClientAsFraud")); + .stub("markClientAsFraud/{step}")); } @Test diff --git a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/WireMockSnippet.java b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/WireMockSnippet.java index b09a3821f4..2592456c9f 100644 --- a/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/WireMockSnippet.java +++ b/spring-cloud-contract-wiremock/src/main/java/org/springframework/cloud/contract/wiremock/restdocs/WireMockSnippet.java @@ -16,10 +16,7 @@ package org.springframework.cloud.contract.wiremock.restdocs; -import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.OutputStreamWriter; import java.io.Writer; import java.util.Arrays; import java.util.HashSet; @@ -27,11 +24,6 @@ import java.util.LinkedHashSet; import java.util.Set; import java.util.regex.Pattern; -import org.springframework.http.MediaType; -import org.springframework.restdocs.RestDocumentationContext; -import org.springframework.restdocs.operation.Operation; -import org.springframework.restdocs.snippet.Snippet; - import com.github.tomakehurst.wiremock.client.MappingBuilder; import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder; import com.github.tomakehurst.wiremock.common.Json; @@ -40,19 +32,29 @@ import com.github.tomakehurst.wiremock.http.HttpHeaders; import com.github.tomakehurst.wiremock.matching.UrlPattern; import com.github.tomakehurst.wiremock.stubbing.StubMapping; +import org.springframework.http.MediaType; +import org.springframework.restdocs.RestDocumentationContext; +import org.springframework.restdocs.operation.Operation; +import org.springframework.restdocs.snippet.RestDocumentationContextPlaceholderResolverFactory; +import org.springframework.restdocs.snippet.Snippet; +import org.springframework.restdocs.snippet.StandardWriterResolver; +import org.springframework.restdocs.snippet.WriterResolver; +import org.springframework.restdocs.templates.TemplateFormat; +import org.springframework.util.PropertyPlaceholderHelper; + import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; import static com.github.tomakehurst.wiremock.client.WireMock.delete; import static com.github.tomakehurst.wiremock.client.WireMock.equalTo; import static com.github.tomakehurst.wiremock.client.WireMock.equalToJson; import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static com.github.tomakehurst.wiremock.client.WireMock.head; import static com.github.tomakehurst.wiremock.client.WireMock.matching; import static com.github.tomakehurst.wiremock.client.WireMock.matchingJsonPath; -import static com.github.tomakehurst.wiremock.client.WireMock.patch; -import static com.github.tomakehurst.wiremock.client.WireMock.head; import static com.github.tomakehurst.wiremock.client.WireMock.options; -import static com.github.tomakehurst.wiremock.client.WireMock.trace; +import static com.github.tomakehurst.wiremock.client.WireMock.patch; import static com.github.tomakehurst.wiremock.client.WireMock.post; import static com.github.tomakehurst.wiremock.client.WireMock.put; +import static com.github.tomakehurst.wiremock.client.WireMock.trace; import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo; public class WireMockSnippet implements Snippet { @@ -70,6 +72,22 @@ public class WireMockSnippet implements Snippet { private boolean hasJsonBodyRequestToMatch = false; + private final PropertyPlaceholderHelper propertyPlaceholderHelper = new PropertyPlaceholderHelper( + "{", "}"); + + private static final TemplateFormat TEMPLATE_FORMAT = new TemplateFormat() { + + @Override + public String getId() { + return "json"; + } + + @Override + public String getFileExtension() { + return "json"; + } + }; + @Override public void document(Operation operation) throws IOException { extractMatchers(operation); @@ -79,10 +97,12 @@ public class WireMockSnippet implements Snippet { String json = Json.write(this.stubMapping); RestDocumentationContext context = (RestDocumentationContext) operation .getAttributes().get(RestDocumentationContext.class.getName()); - File output = new File(context.getOutputDirectory(), - this.snippetName + "/" + operation.getName() + ".json"); - output.getParentFile().mkdirs(); - try (Writer writer = new OutputStreamWriter(new FileOutputStream(output))) { + RestDocumentationContextPlaceholderResolverFactory placeholders = new RestDocumentationContextPlaceholderResolverFactory(); + WriterResolver writerResolver = new StandardWriterResolver(placeholders, "UTF-8", + TEMPLATE_FORMAT); + String path = this.propertyPlaceholderHelper.replacePlaceholders(operation.getName(), + placeholders.create(context)); + try (Writer writer = writerResolver.resolve(this.snippetName, path, context)) { writer.append(json); } } @@ -120,8 +140,7 @@ public class WireMockSnippet implements Snippet { return requestHeaders(requestBuilder(operation), operation); } - private MappingBuilder requestHeaders(MappingBuilder request, - Operation operation) { + private MappingBuilder requestHeaders(MappingBuilder request, Operation operation) { org.springframework.http.HttpHeaders headers = operation.getRequest() .getHeaders(); // TODO: whitelist headers @@ -167,8 +186,7 @@ public class WireMockSnippet implements Snippet { } } - private MappingBuilder bodyPattern(MappingBuilder builder, - String content) { + private MappingBuilder bodyPattern(MappingBuilder builder, String content) { if (this.jsonPaths != null && !this.jsonPaths.isEmpty()) { for (String jsonPath : this.jsonPaths) { builder.withRequestBody(matchingJsonPath(jsonPath)); diff --git a/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/restdocs/WireMockSnippetTests.java b/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/restdocs/WireMockSnippetTests.java index 47e1822c0c..6896d4ed01 100644 --- a/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/restdocs/WireMockSnippetTests.java +++ b/spring-cloud-contract-wiremock/src/test/java/org/springframework/cloud/contract/wiremock/restdocs/WireMockSnippetTests.java @@ -7,6 +7,9 @@ import java.nio.charset.Charset; import java.nio.file.Files; import java.util.Collection; +import com.github.tomakehurst.wiremock.matching.EqualToJsonPattern; +import com.github.tomakehurst.wiremock.stubbing.StubMapping; + import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -15,11 +18,13 @@ import org.junit.runner.RunWith; import org.mockito.Answers; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; + import org.springframework.cloud.contract.wiremock.WireMockStubMapping; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; +import org.springframework.restdocs.ManualRestDocumentation; import org.springframework.restdocs.RestDocumentationContext; import org.springframework.restdocs.operation.Operation; import org.springframework.restdocs.operation.OperationRequest; @@ -27,9 +32,6 @@ import org.springframework.restdocs.operation.OperationRequestPart; import org.springframework.restdocs.operation.OperationResponse; import org.springframework.restdocs.operation.Parameters; -import com.github.tomakehurst.wiremock.matching.EqualToJsonPattern; -import com.github.tomakehurst.wiremock.stubbing.StubMapping; - import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; import static org.mockito.Matchers.anyString; @@ -49,9 +51,9 @@ public class WireMockSnippetTests { @Before public void setup() throws IOException { this.outputFolder = this.tmp.newFolder(); - RestDocumentationContext context = new RestDocumentationContext(this.getClass(), - "method", this.outputFolder); - given(this.operation.getName()).willReturn("foo"); + ManualRestDocumentation restDocumentation = new ManualRestDocumentation(this.outputFolder.getAbsolutePath()); + restDocumentation.beforeTest(this.getClass(), "method"); + RestDocumentationContext context = restDocumentation.beforeOperation(); given(this.operation.getAttributes().get(anyString())).willReturn(null); given(this.operation.getAttributes() .get(RestDocumentationContext.class.getName())).willReturn(context); @@ -62,6 +64,7 @@ public class WireMockSnippetTests { @Test public void should_maintain_the_response_status_when_generating_stub() throws Exception { + given(this.operation.getName()).willReturn("foo"); WireMockSnippet snippet = new WireMockSnippet(); snippet.document(this.operation); @@ -74,9 +77,26 @@ public class WireMockSnippetTests { .isEqualTo(HttpStatus.ACCEPTED.value()); } + @Test + public void should_use_placeholders_in_stub_file_name() + throws Exception { + given(this.operation.getName()).willReturn("{method-name}/{step}"); + WireMockSnippet snippet = new WireMockSnippet(); + + snippet.document(this.operation); + + File stub = new File(this.outputFolder, "stubs/method/1.json"); + assertThat(stub).exists(); + StubMapping stubMapping = WireMockStubMapping + .buildFrom(new String(Files.readAllBytes(stub.toPath()))); + assertThat(stubMapping.getResponse().getStatus()) + .isEqualTo(HttpStatus.ACCEPTED.value()); + } + @Test public void should_use_equal_to_json_pattern_for_body_when_request_content_type_is_json_when_generating_stub() throws Exception { + given(this.operation.getName()).willReturn("foo"); WireMockSnippet snippet = new WireMockSnippet(); given(this.operation.getRequest()).willReturn(requestPostWithJsonContentType());