diff --git a/pom.xml b/pom.xml
index 1eeb4108..16b684e2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -27,6 +27,8 @@
1.3.0.BUILD-SNAPSHOT
1.4.0.BUILD-SNAPSHOT
Ditmars.BUILD-SNAPSHOT
+
+ 1.1.2.BUILD-SNAPSHOT
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
@@ -105,6 +113,13 @@
pom
import
+
+ org.springframework.cloud
+ spring-cloud-contract-dependencies
+ ${spring-cloud-contract.version}
+ pom
+ import
+
io.netty
netty-codec-http
diff --git a/spring-cloud-netflix-eureka-server/pom.xml b/spring-cloud-netflix-eureka-server/pom.xml
index 94533bd7..7a3ab77d 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-resources
+ prepare-package
+
+ copy-resources
+
+
+
+ ${project.build.outputDirectory}/static/docs
+
+
+
+
+ ${project.build.directory}/generated-docs
+
+
+
+
+
@@ -168,6 +204,30 @@
+
+ org.asciidoctor
+ asciidoctor-maven-plugin
+
+
+ generate-docs
+ prepare-package
+
+ process-asciidoc
+
+
+ html
+ book
+
+
+
+
+
+ org.springframework.restdocs
+ spring-restdocs-asciidoctor
+ 1.1.3.RELEASE
+
+
+
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..597c093d
--- /dev/null
+++ b/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/doc/AbstractDocumentationTests.java
@@ -0,0 +1,111 @@
+/*
+ * 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 com.jayway.restassured.RestAssured;
+import com.jayway.restassured.builder.RequestSpecBuilder;
+import com.jayway.restassured.filter.Filter;
+import com.jayway.restassured.specification.RequestSpecification;
+
+import org.junit.Rule;
+import org.junit.runner.RunWith;
+
+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.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 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.DEFINED_PORT, value = {
+ "spring.jmx.enabled=true", "management.security.enabled=false" })
+@DirtiesContext
+public abstract class AbstractDocumentationTests {
+
+ @LocalServerPort
+ private int port = 0;
+
+ @Rule
+ public JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation(
+ "target/generated-snippets");
+
+ private RestDocumentationFilter filter(String name) {
+ return RestAssuredRestDocumentation.document(name,
+ preprocessRequest(modifyUris().host("eureka.example.com").removePort(),
+ prettyPrint()),
+ preprocessResponse(prettyPrint()));
+ }
+
+ private RequestSpecification document(Filter... filters) {
+ return document(null, filters);
+ }
+
+ private RequestSpecification document(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 assure(String name, Object body) {
+ RestDocumentationFilter filter = filter(name);
+ return RestAssured.given(document(body, filter)).filter(filter);
+ }
+
+ protected RequestSpecification assure(String name) {
+ RestDocumentationFilter filter = filter(name);
+ return RestAssured.given(document(filter)).filter(filter);
+ }
+
+ protected RequestSpecification assure() {
+ return assure("{method-name}");
+ }
+
+ @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/EmptyAppsTests.java b/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/doc/EmptyAppsTests.java
new file mode 100644
index 00000000..2813a041
--- /dev/null
+++ b/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/doc/EmptyAppsTests.java
@@ -0,0 +1,59 @@
+/*
+ * 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 com.netflix.appinfo.ApplicationInfoManager;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.Matchers.emptyIterable;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+public class EmptyAppsTests extends AbstractDocumentationTests {
+
+ @Autowired
+ private EurekaInstanceConfigBean instanceConfig;
+ @Autowired
+ private ApplicationInfoManager applicationInfoManager;
+
+ @Test
+ public void addApp() throws Exception {
+ instanceConfig.setAppname("foo");
+ instanceConfig.setInstanceId("unique-id");
+ instanceConfig.setHostname("foo.example.com");
+ applicationInfoManager.initComponent(instanceConfig);
+ assure("add-app", applicationInfoManager.getInfo()).when()
+ .post("/eureka/apps/FOO").then().assertThat().statusCode(is(204));
+ assure("delete-app").when()
+ .delete("/eureka/apps/FOO/{id}",
+ applicationInfoManager.getInfo().getInstanceId())
+ .then().assertThat().statusCode(is(200));
+ }
+
+ @Test
+ public void emptyApps() {
+ assure().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/RegisteredAppsTests.java b/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/doc/RegisteredAppsTests.java
new file mode 100644
index 00000000..fd28e6ec
--- /dev/null
+++ b/spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/doc/RegisteredAppsTests.java
@@ -0,0 +1,80 @@
+/*
+ * 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 com.netflix.appinfo.ApplicationInfoManager;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cloud.netflix.eureka.CloudEurekaClient;
+import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean;
+import org.springframework.cloud.netflix.eureka.EurekaInstanceConfigBean;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.Matchers.hasSize;
+
+@RunWith(SpringJUnit4ClassRunner.class)
+public class RegisteredAppsTests extends AbstractDocumentationTests {
+
+ @Autowired
+ private EurekaInstanceConfigBean instanceConfig;
+ @Autowired
+ private EurekaClientConfigBean clientConfig;
+ @Autowired
+ private ApplicationInfoManager applicationInfoManager;
+ @Autowired
+ private ApplicationEventPublisher publisher;
+
+ private static CloudEurekaClient client;
+
+ @Before
+ public void setUp() throws Exception {
+ if (client == null) {
+ instanceConfig.setAppname("foo");
+ instanceConfig.setLeaseRenewalIntervalInSeconds(1);
+ applicationInfoManager.initComponent(instanceConfig);
+ clientConfig.setInitialInstanceInfoReplicationIntervalSeconds(0);
+ clientConfig.setRegisterWithEureka(true);
+ client = new CloudEurekaClient(applicationInfoManager, clientConfig,
+ publisher);
+ // Give registration a chance to work
+ while (client.getLastSuccessfulHeartbeatTimePeriod() < 0) {
+ Thread.sleep(100L);
+ }
+ }
+ }
+
+ @AfterClass
+ public static void cleanUp() {
+ if (client != null) {
+ client.shutdown();
+ }
+ }
+
+ @Test
+ public void registeredApps() throws Exception {
+ assure().accept("application/json").when().get("/eureka/apps").then().assertThat()
+ .body("applications.application", hasSize(1)).statusCode(is(200));
+ }
+
+}
diff --git a/spring-cloud-netflix-eureka-server/src/test/resources/application.properties b/spring-cloud-netflix-eureka-server/src/test/resources/application.properties
index 6897a296..1d7b7452 100644
--- a/spring-cloud-netflix-eureka-server/src/test/resources/application.properties
+++ b/spring-cloud-netflix-eureka-server/src/test/resources/application.properties
@@ -1,4 +1,6 @@
-server.port=8761
+server.port=${local.server.port:8761}
spring.application.name=eureka
eureka.client.registerWithEureka=false
-eureka.client.fetchRegistry=false
\ No newline at end of file
+eureka.client.fetchRegistry=false
+logging.level.org.springframework.web.client=DEBUG
+logging.level.com.netflix.discovery=DEBUG
\ No newline at end of file
diff --git a/spring-cloud-netflix-hystrix-contract/pom.xml b/spring-cloud-netflix-hystrix-contract/pom.xml
index 4f9a3421..7b1b8359 100644
--- a/spring-cloud-netflix-hystrix-contract/pom.xml
+++ b/spring-cloud-netflix-hystrix-contract/pom.xml
@@ -29,7 +29,6 @@
org.springframework.cloud
spring-cloud-contract-verifier
- ${spring-cloud-contract.version}
diff --git a/spring-cloud-netflix-hystrix-stream/pom.xml b/spring-cloud-netflix-hystrix-stream/pom.xml
index 951356e6..8fb84f8d 100644
--- a/spring-cloud-netflix-hystrix-stream/pom.xml
+++ b/spring-cloud-netflix-hystrix-stream/pom.xml
@@ -14,7 +14,6 @@
Spring Cloud Netflix Hystrix Stream
${basedir}/..
- 1.0.5.RELEASE
@@ -79,7 +78,11 @@
org.springframework.cloud
spring-cloud-netflix-hystrix-contract
- ${project.version}
+ test
+
+
+ org.springframework.cloud
+ spring-cloud-contract-verifier
test