diff --git a/spring-cloud-dataflow-docs/pom.xml b/spring-cloud-dataflow-docs/pom.xml
index 107de6072e..d6e1dffdca 100644
--- a/spring-cloud-dataflow-docs/pom.xml
+++ b/spring-cloud-dataflow-docs/pom.xml
@@ -53,12 +53,47 @@
org.springframework.cloud
spring-cloud-starter-dataflow-server-local
+
+
+ org.springframework.cloud
+ spring-cloud-dataflow-server-local
+ ${project.version}
+ test-jar
+ test
+
+
+ org.springframework.restdocs
+ spring-restdocs-mockmvc
+ 1.1.2.RELEASE
+ test
+
+
+ org.springframework.restdocs
+ spring-restdocs-core
+ 1.1.2.RELEASE
+ test
+
+
+ com.jayway.jsonpath
+ json-path
+ test
+
full
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+
+
+ **/*Documentation.java
+ **/*Tests.java
+
+
+
org.apache.maven.plugins
maven-javadoc-plugin
@@ -95,7 +130,7 @@
generate-docbook
- generate-resources
+ prepare-package
process-asciidoc
@@ -109,6 +144,7 @@
warn
${project.version}
${project.artifactId}
+ ${basedir}/target/generated-snippets
@@ -158,7 +194,7 @@
generate-html
- generate-resources
+ prepare-package
${basedir}/src/main/docbook/xsl/html-singlepage.xsl
${basedir}/target/docbook/htmlsingle
@@ -182,7 +218,7 @@
generate-html
- generate-resources
+ prepare-package
${basedir}/src/main/docbook/xsl/html-multipage.xsl
${basedir}/target/docbook/html
@@ -207,7 +243,7 @@
generate-pdf
- generate-resources
+ prepare-package
${basedir}/src/main/docbook/xsl/pdf.xsl
${basedir}/target/docbook/pdf
@@ -226,7 +262,7 @@
generate-epub3
- generate-resources
+ prepare-package
${basedir}/src/main/docbook/xsl/epub.xsl
${basedir}/target/docbook/epub
diff --git a/spring-cloud-dataflow-docs/src/main/asciidoc/api-guide.adoc b/spring-cloud-dataflow-docs/src/main/asciidoc/api-guide.adoc
new file mode 100644
index 0000000000..b302bbc8d8
--- /dev/null
+++ b/spring-cloud-dataflow-docs/src/main/asciidoc/api-guide.adoc
@@ -0,0 +1,147 @@
+[[api-guide]]
+= REST API Guide
+
+[partintro]
+--
+In this section you will learn all about the Spring Cloud Data Flow REST API.
+--
+
+[[api-guide-overview]]
+== Overview
+
+Spring Cloud Data Flow provides a REST API allowing you to access all aspects of
+the server. In fact the Spring Cloud Data Flow Shell is a first-class consumer of
+that API.
+
+TIP: If you plan on using the REST API using Java, please also consider using the
+provided Java client (DataflowTemplate) that uses the REST API internally.
+
+[[api-guide-overview-http-verbs]]
+=== HTTP verbs
+
+Spring Cloud Data Flow tries to adhere as closely as possible to standard HTTP and REST conventions in its use of HTTP verbs.
+
+|===
+| Verb | Usage
+
+| `GET`
+| Used to retrieve a resource
+
+| `POST`
+| Used to create a new resource
+
+| `PUT`
+| Used to update an existing resource, including partial updates. Also used for
+resources that imply the concept of `restarts` such as Tasks.
+
+| `DELETE`
+| Used to delete an existing resource
+|===
+
+[[api-guide-overview-http-status-codes]]
+=== HTTP status codes
+
+RESTful notes tries to adhere as closely as possible to standard HTTP and REST conventions in its use of HTTP status codes.
+
+|===
+| Status code | Usage
+
+| `200 OK`
+| The request completed successfully
+
+| `201 Created`
+| A new resource has been created successfully. The resource's URI is available from the response's `Location` header
+
+| `204 No Content`
+| An update to an existing resource has been applied successfully
+
+| `400 Bad Request`
+| The request was malformed. The response body will include an error providing further information
+
+| `404 Not Found`
+| The requested resource did not exist
+
+| `409 Conflict`
+| The requested resource already exists, e.g. the task already exists or the stream
+was already being deployed
+
+| `422 Unprocessable Entity`
+| Returned in cases the Job Execution cannot be stopped or restarted
+
+|===
+
+[[api-guide-overview-headers]]
+=== Headers
+
+Every response has the following header(s):
+
+include::{snippets}/api-documentation/headers/response-headers.adoc[]
+
+[[api-guide-overview-errors]]
+=== Errors
+
+include::{snippets}/api-documentation/errors/response-fields.adoc[]
+
+[[api-guide-overview-hypermedia]]
+=== Hypermedia
+
+Spring Cloud Data Flow uses hypermedia and resources include links to other resources
+in their responses. Responses are in http://stateless.co/hal_specification.html[Hypertext Application from resource to resource Language (HAL)] format. Links can be found beneath the `_links` key. Users of the API should not create URIs themselves, instead they should use the above-described links to navigate.
+
+[[api-guide-resources]]
+== Resources
+
+[[api-guide-resources-index]]
+=== Index
+
+The index provides the entry point into Spring Cloud Data Flow's REST API.
+
+[[api-guide-resources-index-access]]
+==== Accessing the index
+
+A `GET` request is used to access the index
+
+===== Request structure
+
+include::{snippets}/api-documentation/index/http-request.adoc[]
+
+===== Example request
+
+include::{snippets}/api-documentation/index/curl-request.adoc[]
+
+===== Response structure
+
+include::{snippets}/api-documentation/index/response-fields.adoc[]
+
+===== Example response
+
+include::{snippets}/api-documentation/index/http-response.adoc[]
+
+[[api-guide-resources-index-links]]
+===== Links
+
+The main element of the index are the links as they allow you to traverse the API
+and execute the desired functionality:
+
+include::{snippets}/api-documentation/index/links.adoc[]
+
+[[resources-app-registry-list]]
+=== Listing Applications
+
+A `GET` request will list all applications known to Spring Cloud Data Flow.
+
+==== Request structure
+
+include::{snippets}/app-registry-documentation/get-applications-filtered/http-request.adoc[]
+
+==== Request parameters
+
+include::{snippets}/app-registry-documentation/get-applications-filtered/request-parameters.adoc[]
+
+==== Example request
+
+include::{snippets}/app-registry-documentation/get-applications-filtered/curl-request.adoc[]
+
+==== Response structure
+
+include::{snippets}/app-registry-documentation/get-applications-filtered/http-response.adoc[]
diff --git a/spring-cloud-dataflow-docs/src/main/asciidoc/index.adoc b/spring-cloud-dataflow-docs/src/main/asciidoc/index.adoc
index ab506b4fed..4996483e64 100644
--- a/spring-cloud-dataflow-docs/src/main/asciidoc/index.adoc
+++ b/spring-cloud-dataflow-docs/src/main/asciidoc/index.adoc
@@ -26,6 +26,7 @@ include::streams.adoc[]
include::tasks.adoc[]
include::dashboard.adoc[]
include::howto.adoc[]
+include::api-guide.adoc[]
include::appendix.adoc[]
diff --git a/spring-cloud-dataflow-docs/src/test/java/org/springframework/cloud/dataflow/server/rest/documentation/ApiDocumentation.java b/spring-cloud-dataflow-docs/src/test/java/org/springframework/cloud/dataflow/server/rest/documentation/ApiDocumentation.java
new file mode 100644
index 0000000000..a69cc65695
--- /dev/null
+++ b/spring-cloud-dataflow-docs/src/test/java/org/springframework/cloud/dataflow/server/rest/documentation/ApiDocumentation.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2016 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.dataflow.server.rest.documentation;
+
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
+import static org.springframework.restdocs.headers.HeaderDocumentation.responseHeaders;
+import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel;
+import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links;
+import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
+import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import javax.servlet.RequestDispatcher;
+
+import org.junit.Test;
+
+/**
+ * @author Gunnar Hillert
+ */
+public class ApiDocumentation extends BaseDocumentation {
+
+ @Test
+ public void headers() throws Exception {
+ this.mockMvc
+ .perform(get("/"))
+ .andExpect(status().isOk())
+ .andDo(this.documentationHandler.document(
+ responseHeaders(
+ headerWithName("Content-Type").description("The Content-Type of the payload, e.g. `application/hal+json`"))));
+ }
+
+ @Test
+ public void errors() throws Exception {
+ this.mockMvc
+ .perform(get("/error")
+ .requestAttr(RequestDispatcher.ERROR_STATUS_CODE, 400)
+ .requestAttr(RequestDispatcher.ERROR_REQUEST_URI, "/apps")
+ .requestAttr(RequestDispatcher.ERROR_MESSAGE, "The app 'http://localhost:8080/apps/123' does not exist"))
+ .andExpect(status().isBadRequest())
+ .andExpect(jsonPath("error", is("Bad Request")))
+ .andExpect(jsonPath("timestamp", is(notNullValue())))
+ .andExpect(jsonPath("status", is(400)))
+ .andExpect(jsonPath("path", is(notNullValue())))
+ .andDo(this.documentationHandler.document(
+ responseFields(
+ fieldWithPath("error").description("The HTTP error that occurred, e.g. `Bad Request`"),
+ fieldWithPath("message").description("A description of the cause of the error"),
+ fieldWithPath("path").description("The path to which the request was made"),
+ fieldWithPath("status").description("The HTTP status code, e.g. `400`"),
+ fieldWithPath("timestamp").description("The time, in milliseconds, at which the error occurred"))));
+ }
+
+ @Test
+ public void index() throws Exception {
+ this.mockMvc.perform(get("/"))
+ .andExpect(status().isOk())
+ .andDo(this.documentationHandler.document(
+ links(
+ linkWithRel("apps").description("Handle registered applications"),
+ linkWithRel("completions/stream").description("Exposes the DSL completion features"),
+ linkWithRel("jobs/executions").description("Provides the JobExecution resource"),
+ linkWithRel("jobs/executions/execution").description("Provides details for a specific JobExecution"),
+ linkWithRel("jobs/executions/execution/steps").description("Provides the steps for a JobExecution"),
+ linkWithRel("jobs/executions/execution/steps/step").description("Returns the details for a specific step"),
+ linkWithRel("jobs/executions/execution/steps/step/progress").description("Provides progress information for a specific step"),
+ linkWithRel("jobs/executions/name").description("Retrieve Job Executions by Job name"),
+ linkWithRel("jobs/instances/instance").description("Provides the job instance resource for a specific job instance"),
+ linkWithRel("jobs/instances/name").description("Provides the Job instance resource for a specific job name"),
+ linkWithRel("runtime/apps").description("Provides the runtime application resource"),
+ linkWithRel("runtime/apps/app").description("Exposes the runtime status for a specific app"),
+ linkWithRel("runtime/apps/instances").description("Provides the status for app instances"),
+ linkWithRel("tasks/definitions").description("Provides the task definition resource"),
+ linkWithRel("tasks/definitions/definition").description("Provides details for a specific task definition"),
+ linkWithRel("tasks/deployments").description("Provides the resource for deployment operations"),
+ linkWithRel("tasks/deployments/deployment").description("Launch a task"),
+ linkWithRel("tasks/executions").description("Returns Task executions"),
+ linkWithRel("tasks/executions/name").description("Returns all task executions for a given Task name"),
+ linkWithRel("tasks/executions/execution").description("Provides details for a specific task execution"),
+ linkWithRel("streams/definitions").description("Exposes the Streams resource"),
+ linkWithRel("streams/definitions/definition").description("Handle a specific Stream definition"),
+ linkWithRel("streams/deployments").description("Provides Stream deployment operations"),
+ linkWithRel("streams/deployments/deployment").description("Request (un-)deployment of an existing stream definition"),
+ linkWithRel("counters").description("Exposes the resource for dealing with Counters"),
+ linkWithRel("counters/counter").description("Handle a specific counter"),
+ linkWithRel("aggregate-counters").description("Provides the resource for dealing with aggregate counters"),
+ linkWithRel("aggregate-counters/counter").description("Handle a specific aggregate counter"),
+ linkWithRel("field-value-counters").description("Provides the resource for dealing with field-value-counters"),
+ linkWithRel("field-value-counters/counter").description("Handle a specific field-value-counter")),
+ responseFields(
+ fieldWithPath("_links").description("Links to other resources"))));
+ }
+}
diff --git a/spring-cloud-dataflow-docs/src/test/java/org/springframework/cloud/dataflow/server/rest/documentation/AppRegistryDocumentation.java b/spring-cloud-dataflow-docs/src/test/java/org/springframework/cloud/dataflow/server/rest/documentation/AppRegistryDocumentation.java
new file mode 100644
index 0000000000..747e8addc5
--- /dev/null
+++ b/spring-cloud-dataflow-docs/src/test/java/org/springframework/cloud/dataflow/server/rest/documentation/AppRegistryDocumentation.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2016 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.dataflow.server.rest.documentation;
+
+import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
+import static org.springframework.restdocs.request.RequestDocumentation.requestParameters;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.springframework.http.MediaType;
+
+/**
+ * @author Gunnar Hillert
+ */
+public class AppRegistryDocumentation extends BaseDocumentation {
+
+ @Test
+ public void getApplicationsFiltered() throws Exception {
+ this.mockMvc.perform(get("/apps").param("type", "source")
+ .accept(MediaType.APPLICATION_JSON))
+ .andExpect(status().isOk())
+ .andDo(this.documentationHandler.document(requestParameters(
+ parameterWithName("type").description("Restrict the returned apps to the type of the app."))));
+ }
+}
diff --git a/spring-cloud-dataflow-docs/src/test/java/org/springframework/cloud/dataflow/server/rest/documentation/BaseDocumentation.java b/spring-cloud-dataflow-docs/src/test/java/org/springframework/cloud/dataflow/server/rest/documentation/BaseDocumentation.java
new file mode 100644
index 0000000000..6e0e4e398c
--- /dev/null
+++ b/spring-cloud-dataflow-docs/src/test/java/org/springframework/cloud/dataflow/server/rest/documentation/BaseDocumentation.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2016 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.dataflow.server.rest.documentation;
+
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
+
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.springframework.cloud.dataflow.server.local.LocalDataflowResource;
+import org.springframework.restdocs.JUnitRestDocumentation;
+import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.setup.MockMvcBuilders;
+
+/**
+ * @author Gunnar Hillert
+ */
+public abstract class BaseDocumentation {
+
+ protected String TARGET_DIRECTORY = "target/generated-snippets";
+
+ @Rule
+ public JUnitRestDocumentation restDocumentation =
+ new JUnitRestDocumentation(TARGET_DIRECTORY);
+
+ @ClassRule
+ public final static LocalDataflowResource springDataflowServer =
+ new LocalDataflowResource(null);
+
+ protected MockMvc mockMvc;
+ protected RestDocumentationResultHandler documentationHandler;
+
+ @Before
+ public void setupMocks() {
+ prepareDocumentationTests(restDocumentation);
+ }
+
+ protected void prepareDocumentationTests(JUnitRestDocumentation restDocumentation) {
+ this.documentationHandler = document("{class-name}/{method-name}",
+ preprocessResponse(prettyPrint()));
+ this.mockMvc = MockMvcBuilders.webAppContextSetup(springDataflowServer.getWebApplicationContext())
+ .apply(documentationConfiguration(restDocumentation))
+ .alwaysDo(this.documentationHandler)
+ .build();
+ }
+
+}
diff --git a/spring-cloud-dataflow-server-core/pom.xml b/spring-cloud-dataflow-server-core/pom.xml
index b8926be021..3f7527d718 100644
--- a/spring-cloud-dataflow-server-core/pom.xml
+++ b/spring-cloud-dataflow-server-core/pom.xml
@@ -163,9 +163,9 @@
- pl.project13.maven
- git-commit-id-plugin
-
-
+ pl.project13.maven
+ git-commit-id-plugin
+
+
diff --git a/spring-cloud-dataflow-server-local/pom.xml b/spring-cloud-dataflow-server-local/pom.xml
index d2b7d09f7e..61c7e36474 100644
--- a/spring-cloud-dataflow-server-local/pom.xml
+++ b/spring-cloud-dataflow-server-local/pom.xml
@@ -40,6 +40,17 @@
org.springframework.boot
spring-boot-maven-plugin
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+
+
+
+ test-jar
+
+
+
+
diff --git a/spring-cloud-dataflow-server-local/src/test/java/org/springframework/cloud/dataflow/server/local/LocalDataflowResource.java b/spring-cloud-dataflow-server-local/src/test/java/org/springframework/cloud/dataflow/server/local/LocalDataflowResource.java
index 679826ef01..72bffaf6b4 100644
--- a/spring-cloud-dataflow-server-local/src/test/java/org/springframework/cloud/dataflow/server/local/LocalDataflowResource.java
+++ b/spring-cloud-dataflow-server-local/src/test/java/org/springframework/cloud/dataflow/server/local/LocalDataflowResource.java
@@ -85,4 +85,10 @@ public MockMvc getMockMvc() {
public String getDataflowPort() {
return dataflowPort;
}
+
+ public WebApplicationContext getWebApplicationContext() {
+ return configurableApplicationContext;
+ }
+
}
+