diff --git a/README.adoc b/README.adoc
index 01bd8126..1a3039a8 100644
--- a/README.adoc
+++ b/README.adoc
@@ -114,17 +114,7 @@ following command:
The generated eclipse projects can be imported by selecting `import existing projects`
from the `file` menu.
-==== Importing into Intellij
-Spring Cloud projects need a specific version of Maven and a profile enabled.
-Intellij 14.1+ requires some configuration to ensure these are setup properly.
- 1. Click New, Project from Existing Sources, choose your spring-cloud project directory
- 2. Choose Maven, and select Environment Settings. *Ensure you are using Maven 3.3.3*
- 3. In the next screen, *Select the profile `spring`* click Next until Finish.
- 4. Click Build, Rebuild Project, and you are ready to go!
-
-==== Importing into other IDEs
-Maven is well supported by most Java IDEs. Refer to you vendor documentation.
== Contributing
diff --git a/circle.yml b/circle.yml
index d0f842d6..02016fd5 100644
--- a/circle.yml
+++ b/circle.yml
@@ -9,6 +9,7 @@ machine:
_JAVA_OPTIONS: "-Xms1024m -Xmx2048m"
dependencies:
override:
+ - cd spring-cloud-netflix-hystrix-contract && ../mvnw clean install
- ./mvnw -s .settings.xml -U --fail-never dependency:go-offline || true
test:
override:
diff --git a/config/releaser.yml b/config/releaser.yml
new file mode 100644
index 00000000..20b9f16a
--- /dev/null
+++ b/config/releaser.yml
@@ -0,0 +1,3 @@
+releaser:
+ maven:
+ buildCommand: ./scripts/build.sh
diff --git a/pom.xml b/pom.xml
index ccecb3a4..83be56bd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -27,6 +27,8 @@
2.0.0.BUILD-SNAPSHOT
2.0.0.BUILD-SNAPSHOT
Elmhurst.BUILD-SNAPSHOT
+
+ 1.1.2.RELEASE
3.6.1
@@ -70,6 +72,12 @@
+
+ org.springframework.cloud
+ spring-cloud-netflix-hystrix-contract
+ ${project.version}
+ test
+
org.springframework.cloud
spring-cloud-netflix-dependencies
@@ -104,6 +112,13 @@
pom
import
+
+ org.springframework.cloud
+ spring-cloud-contract-dependencies
+ ${spring-cloud-contract.version}
+ pom
+ import
+
io.netty
netty-codec-http
@@ -124,6 +139,8 @@
spring-cloud-netflix-dependencies
spring-cloud-netflix-archaius
+
+
spring-cloud-netflix-core
spring-cloud-netflix-hystrix-dashboard
spring-cloud-netflix-hystrix-stream
diff --git a/scripts/build.sh b/scripts/build.sh
new file mode 100755
index 00000000..9a06ffea
--- /dev/null
+++ b/scripts/build.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+(cd spring-cloud-netflix-hystrix-contract && ./mvnw clean install)
+./mvnw clean install
diff --git a/spring-cloud-netflix-eureka-server/pom.xml b/spring-cloud-netflix-eureka-server/pom.xml
index 3c025026..edb97b94 100644
--- a/spring-cloud-netflix-eureka-server/pom.xml
+++ b/spring-cloud-netflix-eureka-server/pom.xml
@@ -96,11 +96,28 @@
com.thoughtworks.xstream
xstream
+
+ org.projectlombok
+ lombok
+
+ compile
+ true
+
+
+ org.springframework.restdocs
+ spring-restdocs-restassured
+ test
+
org.springframework.boot
spring-boot-starter-test
test
+
+ org.springframework.cloud
+ spring-cloud-contract-wiremock
+ test
+
@@ -116,7 +133,7 @@
maven-resources-plugin
-
copy-resources
validate
@@ -133,6 +150,25 @@
+
+ copy-docs
+ prepare-package
+
+ copy-resources
+
+
+
+ ${project.build.outputDirectory}/static/docs
+
+
+
+
+ ${project.build.directory}/generated-docs
+
+
+
+
+
@@ -168,6 +204,23 @@
+
+ org.asciidoctor
+ asciidoctor-maven-plugin
+
+
+ generate-docs
+ prepare-package
+
+ process-asciidoc
+
+
+ html
+ book
+
+
+
+
diff --git a/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/doc/AbstractDocumentationTests.java b/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/doc/AbstractDocumentationTests.java
new file mode 100644
index 00000000..76860c20
--- /dev/null
+++ b/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/doc/AbstractDocumentationTests.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2013-2015 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.netflix.eureka.server.doc;
+
+import java.util.UUID;
+
+import com.jayway.restassured.RestAssured;
+import com.jayway.restassured.builder.RequestSpecBuilder;
+import com.jayway.restassured.filter.Filter;
+import com.jayway.restassured.specification.RequestSpecification;
+import com.netflix.appinfo.ApplicationInfoManager;
+import com.netflix.appinfo.InstanceInfo;
+import com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl;
+
+import org.junit.After;
+import org.junit.Rule;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.context.embedded.LocalServerPort;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
+import org.springframework.cloud.contract.wiremock.restdocs.WireMockSnippet;
+import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean;
+import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
+import org.springframework.cloud.netflix.eureka.server.doc.AbstractDocumentationTests.Application;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.restdocs.JUnitRestDocumentation;
+import org.springframework.restdocs.restassured.RestAssuredRestDocumentation;
+import org.springframework.restdocs.restassured.RestDocumentationFilter;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.util.ReflectionTestUtils;
+
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
+import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.documentationConfiguration;
+import static org.springframework.restdocs.restassured.operation.preprocess.RestAssuredPreprocessors.modifyUris;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringBootTest(classes = Application.class, webEnvironment = WebEnvironment.RANDOM_PORT, value = {
+ "spring.jmx.enabled=true", "management.security.enabled=false" })
+@DirtiesContext
+public abstract class AbstractDocumentationTests {
+
+ @LocalServerPort
+ private int port = 0;
+
+ @Autowired
+ private PeerAwareInstanceRegistryImpl registry;
+
+ @Autowired
+ private EurekaInstanceConfigBean instanceConfig;
+
+ @Autowired
+ private ApplicationInfoManager applicationInfoManager;
+
+ @Rule
+ public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(
+ "target/generated-snippets");
+
+ @After
+ public void init() {
+ registry.clearRegistry();
+ ReflectionTestUtils.setField(registry, "responseCache", null);
+ registry.initializedResponseCache();
+ }
+
+ protected InstanceInfo register(String name) {
+ return register(name, UUID.randomUUID().toString());
+ }
+
+ protected InstanceInfo register(String name, String id) {
+ registry.register(instance(name, id), false);
+ return instance();
+ }
+
+ protected InstanceInfo instance(String name) {
+ return instance(name, UUID.randomUUID().toString());
+ }
+
+ protected InstanceInfo instance(String name, String id) {
+ instanceConfig.setAppname(name);
+ instanceConfig.setInstanceId(id);
+ instanceConfig.setHostname("foo.example.com");
+ applicationInfoManager.initComponent(instanceConfig);
+ return applicationInfoManager.getInfo();
+ }
+
+ protected InstanceInfo instance() {
+ return applicationInfoManager.getInfo();
+ }
+
+ private RestDocumentationFilter filter(String name) {
+ return RestAssuredRestDocumentation.document(name,
+ preprocessRequest(modifyUris().host("eureka.example.com").removePort(),
+ prettyPrint()),
+ preprocessResponse(prettyPrint()));
+ }
+
+ private RequestSpecification spec(Filter... filters) {
+ return spec(null, filters);
+ }
+
+ private RequestSpecification spec(Object body, Filter... filters) {
+ RequestSpecBuilder builder = new RequestSpecBuilder()
+ .addFilter(documentationConfiguration(this.restDocumentation).snippets()
+ .withAdditionalDefaults(new WireMockSnippet()));
+ for (Filter filter : filters) {
+ builder = builder.addFilter(filter);
+ }
+ RequestSpecification spec = builder.setPort(this.port).build();
+ if (body != null) {
+ spec.contentType("application/json").body(body, new EurekaObjectMapper());
+ }
+ return spec;
+ }
+
+ protected RequestSpecification document() {
+ return document("{method-name}");
+ }
+
+ protected RequestSpecification document(Object body) {
+ RestDocumentationFilter filter = filter("{method-name}");
+ RequestSpecification assured = RestAssured.given(spec(body, filter));
+ return assured.filter(filter);
+ }
+
+ protected RequestSpecification document(String name, Object body) {
+ RestDocumentationFilter filter = filter(name);
+ RequestSpecification assured = RestAssured.given(spec(body, filter));
+ return assured.filter(filter);
+ }
+
+ protected RequestSpecification document(String name) {
+ RestDocumentationFilter filter = filter(name);
+ return RestAssured.given(spec(filter)).filter(filter);
+ }
+
+ @Configuration
+ @EnableAutoConfiguration
+ @EnableEurekaServer
+ protected static class Application {
+ public static void main(String[] args) {
+ new SpringApplicationBuilder(Application.class).properties(
+ "spring.application.name=eureka", "management.security.enabled=false",
+ "eureka.client.registerWithEureka=true").run(args);
+ }
+ }
+
+}
diff --git a/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/doc/AppRegistrationTests.java b/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/doc/AppRegistrationTests.java
new file mode 100644
index 00000000..a7160d32
--- /dev/null
+++ b/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/doc/AppRegistrationTests.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2013-2015 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.netflix.eureka.server.doc;
+
+import java.util.UUID;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import static com.github.tomakehurst.wiremock.client.WireMock.delete;
+import static com.github.tomakehurst.wiremock.client.WireMock.get;
+import static com.github.tomakehurst.wiremock.client.WireMock.matching;
+import static com.github.tomakehurst.wiremock.client.WireMock.put;
+import static com.github.tomakehurst.wiremock.client.WireMock.urlPathMatching;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.Matchers.emptyIterable;
+import static org.hamcrest.Matchers.hasSize;
+import static org.springframework.cloud.netflix.eureka.server.doc.RequestVerifierFilter.verify;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+public class AppRegistrationTests extends AbstractDocumentationTests {
+
+ @Test
+ public void startingApp() throws Exception {
+ register("foo");
+ document().accept("application/json").when().get("/eureka/apps").then()
+ .assertThat()
+ .body("applications.application", hasSize(1),
+ "applications.application[0].instance[0].status",
+ equalTo("STARTING"))
+ .statusCode(is(200));
+ }
+
+ @Test
+ public void addInstance() throws Exception {
+ document(instance("foo"))
+ .filter(verify("$.instance.app").json("$.instance.hostName")
+ .json("$.instance[?(@.status=='STARTING')]")
+ .json("$.instance.instanceId")
+ .json("$.instance.dataCenterInfo.name"))
+ .when().post("/eureka/apps/FOO").then().assertThat().statusCode(is(204));
+ }
+
+ @Test
+ public void setStatus() throws Exception {
+ String id = register("foo").getInstanceId();
+ document()
+ .filter(verify(put(urlPathMatching("/eureka/apps/FOO/.*/status"))
+ .withQueryParam("value", matching("UP"))))
+ .when().put("/eureka/apps/FOO/{id}/status?value={value}", id, "UP").then()
+ .assertThat().statusCode(is(200));
+ }
+
+ @Test
+ public void allApps() throws Exception {
+ register("foo");
+ document().accept("application/json").when().get("/eureka/apps").then()
+ .assertThat().body("applications.application", hasSize(1))
+ .statusCode(is(200));
+ }
+
+ @Test
+ public void delta() throws Exception {
+ register("foo");
+ document().accept("application/json").when().get("/eureka/apps/delta").then()
+ .assertThat().body("applications.application", hasSize(1))
+ .statusCode(is(200));
+ }
+
+ @Test
+ public void oneInstance() throws Exception {
+ String id = UUID.randomUUID().toString();
+ register("foo", id);
+ document().filter(verify(get(urlPathMatching("/eureka/apps/FOO/.*"))))
+ .accept("application/json").when().get("/eureka/apps/FOO/{id}", id).then()
+ .assertThat().body("instance.app", equalTo("FOO")).statusCode(is(200));
+ }
+
+ @Test
+ public void lookupInstance() throws Exception {
+ String id = register("foo").getInstanceId();
+ document().filter(verify(get(urlPathMatching("/eureka/instances/.*"))))
+ .accept("application/json").when().get("/eureka/instances/{id}", id)
+ .then().assertThat().body("instance.app", equalTo("FOO"))
+ .statusCode(is(200));
+ }
+
+ @Test
+ public void renew() throws Exception {
+ String id = register("foo").getInstanceId();
+ document().filter(verify(put(urlPathMatching("/eureka/apps/FOO/.*"))))
+ .accept("application/json").when().put("/eureka/apps/FOO/{id}", id).then()
+ .assertThat().statusCode(is(200));
+ }
+
+ @Test
+ public void updateMetadata() throws Exception {
+ String id = register("foo").getInstanceId();
+ document()
+ .filter(verify(put(urlPathMatching("/eureka/apps/FOO/.*/metadata"))
+ .withQueryParam("key", matching(".*"))))
+ .accept("application/json").when()
+ .put("/eureka/apps/FOO/{id}/metadata?key=value", id).then().assertThat()
+ .statusCode(is(200));
+ assertThat(instance().getMetadata()).containsEntry("key", "value");
+ }
+
+ @Test
+ public void deleteInstance() throws Exception {
+ String id = register("foo").getInstanceId();
+ document().filter(verify(delete(urlPathMatching("/eureka/apps/FOO/.*")))).when()
+ .delete("/eureka/apps/FOO/{id}", id).then().assertThat()
+ .statusCode(is(200));
+ }
+
+ @Test
+ public void emptyApps() {
+ document().when().accept("application/json").get("/eureka/apps").then()
+ .assertThat().body("applications.application", emptyIterable())
+ .statusCode(is(200));
+ }
+
+}
diff --git a/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/doc/EurekaObjectMapper.java b/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/doc/EurekaObjectMapper.java
new file mode 100644
index 00000000..c5b28ef0
--- /dev/null
+++ b/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/doc/EurekaObjectMapper.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2016-2017 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.netflix.eureka.server.doc;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import javax.ws.rs.core.MediaType;
+
+import com.jayway.restassured.mapper.ObjectMapperDeserializationContext;
+import com.jayway.restassured.mapper.ObjectMapperSerializationContext;
+import com.netflix.discovery.converters.EntityBodyConverter;
+
+final class EurekaObjectMapper
+ implements com.jayway.restassured.mapper.ObjectMapper {
+ private EntityBodyConverter converter = new EntityBodyConverter();
+
+ @Override
+ public Object serialize(ObjectMapperSerializationContext context) {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try {
+ converter.write(context.getObjectToSerialize(), out,
+ MediaType.APPLICATION_JSON_TYPE);
+ }
+ catch (IOException e) {
+ throw new IllegalStateException("Cannot serialize", e);
+ }
+ return out.toByteArray();
+ }
+
+ @Override
+ public Object deserialize(
+ ObjectMapperDeserializationContext context) {
+ try {
+ return converter.read(
+ context.getDataToDeserialize().asInputStream(),
+ context.getType(), MediaType.APPLICATION_JSON_TYPE);
+ }
+ catch (IOException e) {
+ throw new IllegalStateException("Cannot deserialize", e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/doc/EurekaServerTests.java b/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/doc/EurekaServerTests.java
new file mode 100644
index 00000000..8e540c4d
--- /dev/null
+++ b/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/doc/EurekaServerTests.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2013-2015 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.netflix.eureka.server.doc;
+
+import java.util.UUID;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.test.context.TestPropertySource;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+// TODO: maybe this should be the default (the test fails without it because the JSON is
+// invalid)
+@TestPropertySource(properties = "eureka.server.minAvailableInstancesForPeerReplication=0")
+public class EurekaServerTests extends AbstractDocumentationTests {
+
+ @Test
+ public void serverStatus() throws Exception {
+ register("foo", UUID.randomUUID().toString());
+ document().accept("application/json").when().get("/eureka/status").then()
+ .assertThat().body("generalStats", notNullValue(), "applicationStats",
+ notNullValue(), "instanceInfo", notNullValue())
+ .statusCode(is(200));
+ }
+
+}
diff --git a/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/doc/RequestVerifierFilter.java b/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/doc/RequestVerifierFilter.java
new file mode 100644
index 00000000..d3475a72
--- /dev/null
+++ b/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/doc/RequestVerifierFilter.java
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2016-2017 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.netflix.eureka.server.doc;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import com.github.tomakehurst.wiremock.client.MappingBuilder;
+import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder;
+import com.github.tomakehurst.wiremock.http.ContentTypeHeader;
+import com.github.tomakehurst.wiremock.http.Cookie;
+import com.github.tomakehurst.wiremock.http.HttpHeader;
+import com.github.tomakehurst.wiremock.http.HttpHeaders;
+import com.github.tomakehurst.wiremock.http.QueryParameter;
+import com.github.tomakehurst.wiremock.http.Request;
+import com.github.tomakehurst.wiremock.http.RequestMethod;
+import com.github.tomakehurst.wiremock.matching.MatchResult;
+import com.github.tomakehurst.wiremock.stubbing.StubMapping;
+import com.jayway.jsonpath.JsonPath;
+import com.jayway.restassured.filter.Filter;
+import com.jayway.restassured.filter.FilterContext;
+import com.jayway.restassured.response.Header;
+import com.jayway.restassured.response.Response;
+import com.jayway.restassured.specification.FilterableRequestSpecification;
+import com.jayway.restassured.specification.FilterableResponseSpecification;
+
+import org.springframework.util.Base64Utils;
+import org.springframework.util.ObjectUtils;
+import org.springframework.util.StringUtils;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author Dave Syer
+ *
+ */
+public class RequestVerifierFilter implements Filter {
+
+ static final String CONTEXT_KEY_CONFIGURATION = "org.springframework.restdocs.configuration";
+ private Map jsonPaths = new LinkedHashMap<>();
+ private MappingBuilder builder;
+
+ public static RequestVerifierFilter verify(String path) {
+ return new RequestVerifierFilter(path);
+ }
+
+ public static RequestVerifierFilter verify(MappingBuilder builder) {
+ return new RequestVerifierFilter().wiremock(builder);
+ }
+
+ private RequestVerifierFilter(String expression, Object... args) {
+ expression = String.format(expression, args);
+ this.jsonPaths.put(expression, JsonPath.compile(expression));
+ }
+
+ private RequestVerifierFilter() {
+ }
+
+ public RequestVerifierFilter json(String expression, Object... args) {
+ expression = String.format(expression, args);
+ this.jsonPaths.put(expression, JsonPath.compile(expression));
+ return this;
+ }
+
+ public RequestVerifierFilter wiremock(MappingBuilder builder) {
+ this.builder = builder;
+ return this;
+ }
+
+ @Override
+ public Response filter(FilterableRequestSpecification requestSpec,
+ FilterableResponseSpecification responseSpec, FilterContext context) {
+ Map configuration = getConfiguration(requestSpec, context);
+ configuration.put("contract.jsonPaths", this.jsonPaths.keySet());
+ Response response = context.next(requestSpec, responseSpec);
+ if (requestSpec.getBody() != null && !this.jsonPaths.isEmpty()) {
+ String actual = new String((byte[]) requestSpec.getBody());
+ for (JsonPath jsonPath : this.jsonPaths.values()) {
+ new JsonPathValue(jsonPath, actual).assertHasValue(Object.class,
+ "an object");
+ }
+ }
+ if (this.builder != null) {
+ this.builder.willReturn(getResponseDefinition(response));
+ StubMapping stubMapping = this.builder.build();
+ MatchResult match = stubMapping.getRequest()
+ .match(new WireMockRestAssuredRequestAdapter(requestSpec));
+ assertThat(match.isExactMatch()).as("wiremock did not match request")
+ .isTrue();
+ configuration.put("contract.stubMapping", stubMapping);
+ }
+ return response;
+ }
+
+ private ResponseDefinitionBuilder getResponseDefinition(Response response) {
+ ResponseDefinitionBuilder definition = ResponseDefinitionBuilder
+ .responseDefinition().withBody(response.getBody().asString())
+ .withStatus(response.getStatusCode());
+ addResponseHeaders(definition, response);
+ return definition;
+ }
+
+ private void addResponseHeaders(ResponseDefinitionBuilder definition,
+ Response input) {
+ for (Header header : input.getHeaders().asList()) {
+ String name = header.getName();
+ definition.withHeader(name, input.getHeader(name));
+ }
+ }
+
+ protected Map getConfiguration(
+ FilterableRequestSpecification requestSpec, FilterContext context) {
+ Map configuration = context
+ .