Add Grails sample app

Closes gh-250
This commit is contained in:
Jennifer Strater
2016-05-30 12:22:35 -05:00
committed by Andy Wilkinson
parent c1d245f7d4
commit 64803b5fae
21 changed files with 922 additions and 0 deletions

View File

@@ -0,0 +1,148 @@
= Grails RESTful Notes API Guide
Andy Wilkinson; Jenn Strater
:doctype: book
:icons: font
:source-highlighter: highlightjs
:toc: left
:toclevels: 4
:sectlinks:
[[overview]]
= Overview
[[overview-http-verbs]]
== HTTP verbs
Grails RESTful notes 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
| `PATCH`
| Used to update an existing resource, including partial updates
| `DELETE`
| Used to delete an existing resource
|===
[[overview-http-status-codes]]
== HTTP status codes
Grails 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
|===
[[resources]]
= Resources
[[resources-index]]
== Index
The index provides the entry point into the service.
[[resources-index-access]]
=== Accessing the index
A `GET` request is used to access the index
==== Example request
include::{snippets}/index-example/curl-request.adoc[]
==== Response structure
include::{snippets}/index-example/response-fields.adoc[]
==== Example response
include::{snippets}/index-example/http-response.adoc[]
[[resources-notes]]
== Notes
The Notes resources is used to create and list notes
[[resources-notes-list]]
=== Listing notes
A `GET` request will list all of the service's notes.
==== Response structure
include::{snippets}/notes-list-example/response-fields.adoc[]
==== Example request
include::{snippets}/notes-list-example/curl-request.adoc[]
==== Example response
include::{snippets}/notes-list-example/http-response.adoc[]
[[resources-notes-create]]
=== Creating a note
A `POST` request is used to create a note
==== Request structure
include::{snippets}/notes-create-example/request-fields.adoc[]
==== Example request
include::{snippets}/notes-create-example/curl-request.adoc[]
==== Example response
include::{snippets}/notes-create-example/http-response.adoc[]
[[resources-note-retrieve]]
=== Retrieve a note
A `GET` request will retrieve the details of a note
==== Response structure
include::{snippets}/note-get-example/response-fields.adoc[]
==== Example request
include::{snippets}/note-get-example/curl-request.adoc[]
==== Example response
include::{snippets}/note-get-example/http-response.adoc[]

View File

@@ -0,0 +1,146 @@
package com.example
import org.springframework.restdocs.payload.JsonFieldType
import static com.jayway.restassured.RestAssured.given
import static org.hamcrest.CoreMatchers.is
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.payload.PayloadDocumentation.fieldWithPath
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields
import static org.springframework.restdocs.restassured.operation.preprocess.RestAssuredPreprocessors.modifyUris
import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.document
import static org.springframework.restdocs.restassured.RestAssuredRestDocumentation.documentationConfiguration
import com.jayway.restassured.builder.RequestSpecBuilder
import com.jayway.restassured.specification.RequestSpecification
import grails.test.mixin.integration.Integration
import grails.transaction.Rollback
import org.junit.Rule
import org.springframework.restdocs.JUnitRestDocumentation
import org.springframework.http.MediaType
import spock.lang.Specification
@Integration
@Rollback
class ApiDocumentationSpec extends Specification {
@Rule
JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation('src/docs/generated-snippets')
protected RequestSpecification documentationSpec
void setup() {
this.documentationSpec = new RequestSpecBuilder()
.addFilter(documentationConfiguration(restDocumentation))
.build()
}
void 'test and document get request for /index'() {
expect:
given(this.documentationSpec)
.accept(MediaType.APPLICATION_JSON.toString())
.filter(document('index-example',
preprocessRequest(modifyUris()
.host('api.example.com')
.removePort()),
preprocessResponse(prettyPrint()),
responseFields(
fieldWithPath('message').description('Welcome to Grails!'),
fieldWithPath('environment').description("The running environment"),
fieldWithPath('appversion').description('version of the app that is running'),
fieldWithPath('grailsversion').description('the version of grails used in this project'),
fieldWithPath('appprofile').description('the profile of grails used in this project'),
fieldWithPath('groovyversion').description('the version of groovy used in this project'),
fieldWithPath('jvmversion').description('the version of the jvm used in this project'),
fieldWithPath('controllers').type(JsonFieldType.ARRAY).description('the list of available controllers'),
fieldWithPath('plugins').type(JsonFieldType.ARRAY).description('the plugins active for this project'),
)))
.when()
.port(8080)
.get('/')
.then()
.assertThat()
.statusCode(is(200))
}
void 'test and document notes list request'() {
expect:
given(this.documentationSpec)
.accept(MediaType.APPLICATION_JSON.toString())
.filter(document('notes-list-example',
preprocessRequest(modifyUris()
.host('api.example.com')
.removePort()),
preprocessResponse(prettyPrint()),
responseFields(
fieldWithPath('[].class').description('the class of the resource'),
fieldWithPath('[].id').description('the id of the note'),
fieldWithPath('[].title').description('the title of the note'),
fieldWithPath('[].body').description('the body of the note'),
fieldWithPath('[].tags').type(JsonFieldType.ARRAY).description('the list of tags associated with the note'),
)))
.when()
.port(8080)
.get('/notes')
.then()
.assertThat()
.statusCode(is(200))
}
void 'test and document create new note'() {
expect:
given(this.documentationSpec)
.accept(MediaType.APPLICATION_JSON.toString())
.contentType(MediaType.APPLICATION_JSON.toString())
.filter(document('notes-create-example',
preprocessRequest(modifyUris()
.host('api.example.com')
.removePort()),
preprocessResponse(prettyPrint()),
requestFields(
fieldWithPath('title').description('the title of the note'),
fieldWithPath('body').description('the body of the note'),
fieldWithPath('tags').type(JsonFieldType.ARRAY).description('a list of tags associated to the note')
),
responseFields(
fieldWithPath('class').description('the class of the resource'),
fieldWithPath('id').description('the id of the note'),
fieldWithPath('title').description('the title of the note'),
fieldWithPath('body').description('the body of the note'),
fieldWithPath('tags').type(JsonFieldType.ARRAY).description('the list of tags associated with the note'),
)))
.body('{ "body": "My test example", "title": "Eureka!", "tags": [{"name": "testing123"}] }')
.when()
.port(8080)
.post('/notes')
.then()
.assertThat()
.statusCode(is(201))
}
void 'test and document getting specific note'() {
expect:
given(this.documentationSpec)
.accept(MediaType.APPLICATION_JSON.toString())
.filter(document('note-get-example',
preprocessRequest(modifyUris()
.host('api.example.com')
.removePort()),
preprocessResponse(prettyPrint()),
responseFields(
fieldWithPath('class').description('the class of the resource'),
fieldWithPath('id').description('the id of the note'),
fieldWithPath('title').description('the title of the note'),
fieldWithPath('body').description('the body of the note'),
fieldWithPath('tags').type(JsonFieldType.ARRAY).description('the list of tags associated with the note'),
)))
.when()
.port(8080)
.get('/notes/1')
.then()
.assertThat()
.statusCode(is(200))
}
}