Skip to content

Commit

Permalink
gh-824 Add Spring Restdocs Infrastructure
Browse files Browse the repository at this point in the history
  • Loading branch information
ghillert authored and ericbottard committed Sep 13, 2016
1 parent ff4d27e commit e5db3d9
Show file tree
Hide file tree
Showing 9 changed files with 426 additions and 9 deletions.
46 changes: 41 additions & 5 deletions spring-cloud-dataflow-docs/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,47 @@
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-dataflow-server-local</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dataflow-server-local</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<version>1.1.2.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-core</artifactId>
<version>1.1.2.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.jayway.jsonpath</groupId>
<artifactId>json-path</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<profiles>
<profile>
<id>full</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>**/*Documentation.java</include>
<include>**/*Tests.java</include>
</includes>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
Expand Down Expand Up @@ -95,7 +130,7 @@
<executions>
<execution>
<id>generate-docbook</id>
<phase>generate-resources</phase>
<phase>prepare-package</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
Expand All @@ -109,6 +144,7 @@
<attribute-missing>warn</attribute-missing>
<project-version>${project.version}</project-version>
<project-artifactId>${project.artifactId}</project-artifactId>
<snippets>${basedir}/target/generated-snippets</snippets>
</attributes>
</configuration>
</execution>
Expand Down Expand Up @@ -158,7 +194,7 @@
<goals>
<goal>generate-html</goal>
</goals>
<phase>generate-resources</phase>
<phase>prepare-package</phase>
<configuration>
<htmlCustomization>${basedir}/src/main/docbook/xsl/html-singlepage.xsl</htmlCustomization>
<targetDirectory>${basedir}/target/docbook/htmlsingle</targetDirectory>
Expand All @@ -182,7 +218,7 @@
<goals>
<goal>generate-html</goal>
</goals>
<phase>generate-resources</phase>
<phase>prepare-package</phase>
<configuration>
<htmlCustomization>${basedir}/src/main/docbook/xsl/html-multipage.xsl</htmlCustomization>
<targetDirectory>${basedir}/target/docbook/html</targetDirectory>
Expand All @@ -207,7 +243,7 @@
<goals>
<goal>generate-pdf</goal>
</goals>
<phase>generate-resources</phase>
<phase>prepare-package</phase>
<configuration>
<foCustomization>${basedir}/src/main/docbook/xsl/pdf.xsl</foCustomization>
<targetDirectory>${basedir}/target/docbook/pdf</targetDirectory>
Expand All @@ -226,7 +262,7 @@
<goals>
<goal>generate-epub3</goal>
</goals>
<phase>generate-resources</phase>
<phase>prepare-package</phase>
<configuration>
<epubCustomization>${basedir}/src/main/docbook/xsl/epub.xsl</epubCustomization>
<targetDirectory>${basedir}/target/docbook/epub</targetDirectory>
Expand Down
147 changes: 147 additions & 0 deletions spring-cloud-dataflow-docs/src/main/asciidoc/api-guide.adoc
Original file line number Diff line number Diff line change
@@ -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[]
1 change: 1 addition & 0 deletions spring-cloud-dataflow-docs/src/main/asciidoc/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ include::streams.adoc[]
include::tasks.adoc[]
include::dashboard.adoc[]
include::howto.adoc[]
include::api-guide.adoc[]
include::appendix.adoc[]


Expand Down
Original file line number Diff line number Diff line change
@@ -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"))));
}
}

0 comments on commit e5db3d9

Please sign in to comment.