diff --git a/build.gradle b/build.gradle index e61cbffe..3337251d 100644 --- a/build.gradle +++ b/build.gradle @@ -1,33 +1,25 @@ -allprojects { - ext { - springVersion = '4.1.1.RELEASE' - } - group = 'org.springframework.restdocs' -} +project(':spring-restdocs') { + + ext { + jacksonVersion = '2.3.4' + junitVersion = '4.11' + servletApiVersion = '3.1.0' + springVersion = '4.1.4.RELEASE' + } + + group = 'org.springframework.restdocs' -project(':spring-restdocs-core') { apply plugin: 'java' + apply plugin: 'eclipse' + apply plugin: 'maven' sourceCompatibility = 1.7 targetCompatibility = 1.7 - dependencies { - compile 'junit:junit:4.11' - compile "org.springframework:spring-test:$springVersion" - compile "org.springframework:spring-web:$springVersion" - compile 'javax.servlet:javax.servlet-api:3.1.0' - compile 'com.fasterxml.jackson.core:jackson-databind:2.3.4' - } -} - -subprojects { repositories { jcenter() } - apply plugin: 'eclipse' - apply plugin: 'maven' - task sourcesJar(type: Jar) { classifier = 'sources' from project.sourceSets.main.allSource @@ -45,4 +37,57 @@ subprojects { eclipseJdt.onlyIf { false } cleanEclipseJdt.onlyIf { false } + + dependencies { + compile "junit:junit:$junitVersion" + compile "org.springframework:spring-test:$springVersion" + compile "org.springframework:spring-web:$springVersion" + compile "javax.servlet:javax.servlet-api:$servletApiVersion" + compile "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion" + } +} + +task buildSamples { + description = 'Assembles and tests the sample projects using both Maven and Gradle' + group = 'Build' + dependsOn 'buildMavenSamples' + dependsOn 'buildGradleSamples' +} + +task buildMavenSamples { + dependsOn 'buildRestNotesSpringHateoasSampleWithMaven' + dependsOn 'buildRestNotesSpringDataRestSampleWithMaven' +} + +task buildGradleSamples { + dependsOn 'buildRestNotesSpringHateoasSampleWithGradle' + dependsOn 'buildRestNotesSpringDataRestSampleWithGradle' +} + +task buildRestNotesSpringHateoasSampleWithGradle(type: GradleBuild) { + dependsOn 'spring-restdocs:install' + dir = 'samples/rest-notes-spring-hateoas' + tasks = ['clean', 'build'] +} + +task buildRestNotesSpringDataRestSampleWithGradle(type: GradleBuild) { + dependsOn 'spring-restdocs:install' + dir = 'samples/rest-notes-spring-data-rest' + tasks = ['clean', 'build'] +} + +task buildRestNotesSpringHateoasSampleWithMaven(type: Exec) { + dependsOn 'spring-restdocs:install' + workingDir 'samples/rest-notes-spring-hateoas' + commandLine 'mvn', 'clean', 'package' +} + +task buildRestNotesSpringDataRestSampleWithMaven(type: Exec) { + dependsOn 'spring-restdocs:install' + workingDir 'samples/rest-notes-spring-data-rest' + commandLine 'mvn', 'clean', 'package' +} + +wrapper { + gradleVersion = '2.2' } \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a8952509..0cf48989 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Oct 08 10:04:39 BST 2014 +#Mon Feb 16 13:50:18 GMT 2015 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.2-bin.zip diff --git a/rest-notes-spring-data-rest/.settings/org.eclipse.jdt.core.prefs b/samples/rest-notes-spring-data-rest/.settings/org.eclipse.jdt.core.prefs similarity index 100% rename from rest-notes-spring-data-rest/.settings/org.eclipse.jdt.core.prefs rename to samples/rest-notes-spring-data-rest/.settings/org.eclipse.jdt.core.prefs diff --git a/rest-notes-spring-data-rest/.settings/org.eclipse.jdt.ui.prefs b/samples/rest-notes-spring-data-rest/.settings/org.eclipse.jdt.ui.prefs similarity index 100% rename from rest-notes-spring-data-rest/.settings/org.eclipse.jdt.ui.prefs rename to samples/rest-notes-spring-data-rest/.settings/org.eclipse.jdt.ui.prefs diff --git a/rest-notes-spring-data-rest/build.gradle b/samples/rest-notes-spring-data-rest/build.gradle similarity index 93% rename from rest-notes-spring-data-rest/build.gradle rename to samples/rest-notes-spring-data-rest/build.gradle index fa6262c9..21ba3459 100644 --- a/rest-notes-spring-data-rest/build.gradle +++ b/samples/rest-notes-spring-data-rest/build.gradle @@ -31,7 +31,7 @@ dependencies { testCompile 'com.jayway.jsonpath:json-path' testCompile 'org.springframework.boot:spring-boot-starter-test' - testCompile 'org.springframework.restdocs:spring-restdocs-core:0.1.0.BUILD-SNAPSHOT' + testCompile 'org.springframework.restdocs:spring-restdocs:0.1.0.BUILD-SNAPSHOT' } diff --git a/rest-notes-spring-data-rest/gradle/wrapper/gradle-wrapper.jar b/samples/rest-notes-spring-data-rest/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from rest-notes-spring-data-rest/gradle/wrapper/gradle-wrapper.jar rename to samples/rest-notes-spring-data-rest/gradle/wrapper/gradle-wrapper.jar diff --git a/rest-notes-spring-data-rest/gradle/wrapper/gradle-wrapper.properties b/samples/rest-notes-spring-data-rest/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from rest-notes-spring-data-rest/gradle/wrapper/gradle-wrapper.properties rename to samples/rest-notes-spring-data-rest/gradle/wrapper/gradle-wrapper.properties diff --git a/rest-notes-spring-data-rest/gradlew b/samples/rest-notes-spring-data-rest/gradlew similarity index 100% rename from rest-notes-spring-data-rest/gradlew rename to samples/rest-notes-spring-data-rest/gradlew diff --git a/rest-notes-spring-data-rest/gradlew.bat b/samples/rest-notes-spring-data-rest/gradlew.bat similarity index 100% rename from rest-notes-spring-data-rest/gradlew.bat rename to samples/rest-notes-spring-data-rest/gradlew.bat diff --git a/rest-notes-spring-data-rest/pom.xml b/samples/rest-notes-spring-data-rest/pom.xml similarity index 98% rename from rest-notes-spring-data-rest/pom.xml rename to samples/rest-notes-spring-data-rest/pom.xml index 37b10e6b..1cc246de 100644 --- a/rest-notes-spring-data-rest/pom.xml +++ b/samples/rest-notes-spring-data-rest/pom.xml @@ -48,7 +48,7 @@ org.springframework.restdocs - spring-restdocs-core + spring-restdocs 0.1.0.BUILD-SNAPSHOT test diff --git a/rest-notes-spring-data-rest/src/main/asciidoc/api-guide.asciidoc b/samples/rest-notes-spring-data-rest/src/main/asciidoc/api-guide.asciidoc similarity index 100% rename from rest-notes-spring-data-rest/src/main/asciidoc/api-guide.asciidoc rename to samples/rest-notes-spring-data-rest/src/main/asciidoc/api-guide.asciidoc diff --git a/rest-notes-spring-data-rest/src/main/asciidoc/getting-started-guide.asciidoc b/samples/rest-notes-spring-data-rest/src/main/asciidoc/getting-started-guide.asciidoc similarity index 100% rename from rest-notes-spring-data-rest/src/main/asciidoc/getting-started-guide.asciidoc rename to samples/rest-notes-spring-data-rest/src/main/asciidoc/getting-started-guide.asciidoc diff --git a/rest-notes-spring-data-rest/src/main/java/com/example/notes/Note.java b/samples/rest-notes-spring-data-rest/src/main/java/com/example/notes/Note.java similarity index 100% rename from rest-notes-spring-data-rest/src/main/java/com/example/notes/Note.java rename to samples/rest-notes-spring-data-rest/src/main/java/com/example/notes/Note.java diff --git a/rest-notes-spring-data-rest/src/main/java/com/example/notes/NoteRepository.java b/samples/rest-notes-spring-data-rest/src/main/java/com/example/notes/NoteRepository.java similarity index 100% rename from rest-notes-spring-data-rest/src/main/java/com/example/notes/NoteRepository.java rename to samples/rest-notes-spring-data-rest/src/main/java/com/example/notes/NoteRepository.java diff --git a/rest-notes-spring-data-rest/src/main/java/com/example/notes/RestNotesSpringDataRest.java b/samples/rest-notes-spring-data-rest/src/main/java/com/example/notes/RestNotesSpringDataRest.java similarity index 100% rename from rest-notes-spring-data-rest/src/main/java/com/example/notes/RestNotesSpringDataRest.java rename to samples/rest-notes-spring-data-rest/src/main/java/com/example/notes/RestNotesSpringDataRest.java diff --git a/rest-notes-spring-data-rest/src/main/java/com/example/notes/Tag.java b/samples/rest-notes-spring-data-rest/src/main/java/com/example/notes/Tag.java similarity index 100% rename from rest-notes-spring-data-rest/src/main/java/com/example/notes/Tag.java rename to samples/rest-notes-spring-data-rest/src/main/java/com/example/notes/Tag.java diff --git a/rest-notes-spring-data-rest/src/main/java/com/example/notes/TagRepository.java b/samples/rest-notes-spring-data-rest/src/main/java/com/example/notes/TagRepository.java similarity index 100% rename from rest-notes-spring-data-rest/src/main/java/com/example/notes/TagRepository.java rename to samples/rest-notes-spring-data-rest/src/main/java/com/example/notes/TagRepository.java diff --git a/rest-notes-spring-data-rest/src/test/java/com/example/notes/ApiDocumentation.java b/samples/rest-notes-spring-data-rest/src/test/java/com/example/notes/ApiDocumentation.java similarity index 97% rename from rest-notes-spring-data-rest/src/test/java/com/example/notes/ApiDocumentation.java rename to samples/rest-notes-spring-data-rest/src/test/java/com/example/notes/ApiDocumentation.java index c7ae0946..55fe34e8 100644 --- a/rest-notes-spring-data-rest/src/test/java/com/example/notes/ApiDocumentation.java +++ b/samples/rest-notes-spring-data-rest/src/test/java/com/example/notes/ApiDocumentation.java @@ -18,8 +18,8 @@ package com.example.notes; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; -import static org.springframework.restdocs.core.RestDocumentation.document; -import static org.springframework.restdocs.core.RestDocumentation.linkWithRel; +import static org.springframework.restdocs.RestDocumentation.document; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -39,7 +39,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.hateoas.MediaTypes; -import org.springframework.restdocs.core.RestDocumentationConfiguration; +import org.springframework.restdocs.RestDocumentationConfigurer; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; @@ -70,7 +70,7 @@ public class ApiDocumentation { @Before public void setUp() { this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(new RestDocumentationConfiguration()).build(); + .apply(new RestDocumentationConfigurer()).build(); } @Test diff --git a/rest-notes-spring-data-rest/src/test/java/com/example/notes/GettingStartedDocumentation.java b/samples/rest-notes-spring-data-rest/src/test/java/com/example/notes/GettingStartedDocumentation.java similarity index 97% rename from rest-notes-spring-data-rest/src/test/java/com/example/notes/GettingStartedDocumentation.java rename to samples/rest-notes-spring-data-rest/src/test/java/com/example/notes/GettingStartedDocumentation.java index 8687ff2a..ec54ff5d 100644 --- a/rest-notes-spring-data-rest/src/test/java/com/example/notes/GettingStartedDocumentation.java +++ b/samples/rest-notes-spring-data-rest/src/test/java/com/example/notes/GettingStartedDocumentation.java @@ -19,7 +19,7 @@ package com.example.notes; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; -import static org.springframework.restdocs.core.RestDocumentation.document; +import static org.springframework.restdocs.RestDocumentation.document; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -38,7 +38,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.hateoas.MediaTypes; -import org.springframework.restdocs.core.RestDocumentationConfiguration; +import org.springframework.restdocs.RestDocumentationConfigurer; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; @@ -66,7 +66,7 @@ public class GettingStartedDocumentation { @Before public void setUp() { this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(new RestDocumentationConfiguration()).build(); + .apply(new RestDocumentationConfigurer()).build(); } @Test diff --git a/rest-notes-spring-hateoas/.settings/org.eclipse.jdt.core.prefs b/samples/rest-notes-spring-hateoas/.settings/org.eclipse.jdt.core.prefs similarity index 100% rename from rest-notes-spring-hateoas/.settings/org.eclipse.jdt.core.prefs rename to samples/rest-notes-spring-hateoas/.settings/org.eclipse.jdt.core.prefs diff --git a/rest-notes-spring-hateoas/.settings/org.eclipse.jdt.ui.prefs b/samples/rest-notes-spring-hateoas/.settings/org.eclipse.jdt.ui.prefs similarity index 100% rename from rest-notes-spring-hateoas/.settings/org.eclipse.jdt.ui.prefs rename to samples/rest-notes-spring-hateoas/.settings/org.eclipse.jdt.ui.prefs diff --git a/rest-notes-spring-hateoas/build.gradle b/samples/rest-notes-spring-hateoas/build.gradle similarity index 91% rename from rest-notes-spring-hateoas/build.gradle rename to samples/rest-notes-spring-hateoas/build.gradle index fff6d0e1..6f324f81 100644 --- a/rest-notes-spring-hateoas/build.gradle +++ b/samples/rest-notes-spring-hateoas/build.gradle @@ -8,7 +8,7 @@ buildscript { } plugins { - id "org.asciidoctor.convert" version "1.5.2" + id "org.asciidoctor.convert" version "1.5.2" } apply plugin: 'java' @@ -34,7 +34,7 @@ dependencies { testCompile 'com.jayway.jsonpath:json-path' testCompile 'org.springframework.boot:spring-boot-starter-test' - testCompile 'org.springframework.restdocs:spring-restdocs-core:0.1.0.BUILD-SNAPSHOT' + testCompile 'org.springframework.restdocs:spring-restdocs:0.1.0.BUILD-SNAPSHOT' } ext { diff --git a/rest-notes-spring-hateoas/gradle/wrapper/gradle-wrapper.jar b/samples/rest-notes-spring-hateoas/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from rest-notes-spring-hateoas/gradle/wrapper/gradle-wrapper.jar rename to samples/rest-notes-spring-hateoas/gradle/wrapper/gradle-wrapper.jar diff --git a/rest-notes-spring-hateoas/gradle/wrapper/gradle-wrapper.properties b/samples/rest-notes-spring-hateoas/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from rest-notes-spring-hateoas/gradle/wrapper/gradle-wrapper.properties rename to samples/rest-notes-spring-hateoas/gradle/wrapper/gradle-wrapper.properties diff --git a/rest-notes-spring-hateoas/gradlew b/samples/rest-notes-spring-hateoas/gradlew similarity index 100% rename from rest-notes-spring-hateoas/gradlew rename to samples/rest-notes-spring-hateoas/gradlew diff --git a/rest-notes-spring-hateoas/gradlew.bat b/samples/rest-notes-spring-hateoas/gradlew.bat similarity index 100% rename from rest-notes-spring-hateoas/gradlew.bat rename to samples/rest-notes-spring-hateoas/gradlew.bat diff --git a/rest-notes-spring-hateoas/pom.xml b/samples/rest-notes-spring-hateoas/pom.xml similarity index 98% rename from rest-notes-spring-hateoas/pom.xml rename to samples/rest-notes-spring-hateoas/pom.xml index 1193a258..1c9bfbba 100644 --- a/rest-notes-spring-hateoas/pom.xml +++ b/samples/rest-notes-spring-hateoas/pom.xml @@ -64,7 +64,7 @@ org.springframework.restdocs - spring-restdocs-core + spring-restdocs 0.1.0.BUILD-SNAPSHOT test diff --git a/rest-notes-spring-hateoas/src/main/asciidoc/api-guide.asciidoc b/samples/rest-notes-spring-hateoas/src/main/asciidoc/api-guide.asciidoc similarity index 100% rename from rest-notes-spring-hateoas/src/main/asciidoc/api-guide.asciidoc rename to samples/rest-notes-spring-hateoas/src/main/asciidoc/api-guide.asciidoc diff --git a/rest-notes-spring-hateoas/src/main/asciidoc/getting-started-guide.asciidoc b/samples/rest-notes-spring-hateoas/src/main/asciidoc/getting-started-guide.asciidoc similarity index 100% rename from rest-notes-spring-hateoas/src/main/asciidoc/getting-started-guide.asciidoc rename to samples/rest-notes-spring-hateoas/src/main/asciidoc/getting-started-guide.asciidoc diff --git a/rest-notes-spring-hateoas/src/main/java/com/example/notes/AbstractNoteInput.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/AbstractNoteInput.java similarity index 100% rename from rest-notes-spring-hateoas/src/main/java/com/example/notes/AbstractNoteInput.java rename to samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/AbstractNoteInput.java diff --git a/rest-notes-spring-hateoas/src/main/java/com/example/notes/AbstractTagInput.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/AbstractTagInput.java similarity index 100% rename from rest-notes-spring-hateoas/src/main/java/com/example/notes/AbstractTagInput.java rename to samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/AbstractTagInput.java diff --git a/rest-notes-spring-hateoas/src/main/java/com/example/notes/ExceptionSupressingErrorAttributes.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/ExceptionSupressingErrorAttributes.java similarity index 100% rename from rest-notes-spring-hateoas/src/main/java/com/example/notes/ExceptionSupressingErrorAttributes.java rename to samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/ExceptionSupressingErrorAttributes.java diff --git a/rest-notes-spring-hateoas/src/main/java/com/example/notes/IndexController.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/IndexController.java similarity index 100% rename from rest-notes-spring-hateoas/src/main/java/com/example/notes/IndexController.java rename to samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/IndexController.java diff --git a/rest-notes-spring-hateoas/src/main/java/com/example/notes/NestedContentResource.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NestedContentResource.java similarity index 100% rename from rest-notes-spring-hateoas/src/main/java/com/example/notes/NestedContentResource.java rename to samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NestedContentResource.java diff --git a/rest-notes-spring-hateoas/src/main/java/com/example/notes/Note.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/Note.java similarity index 100% rename from rest-notes-spring-hateoas/src/main/java/com/example/notes/Note.java rename to samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/Note.java diff --git a/rest-notes-spring-hateoas/src/main/java/com/example/notes/NoteInput.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NoteInput.java similarity index 100% rename from rest-notes-spring-hateoas/src/main/java/com/example/notes/NoteInput.java rename to samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NoteInput.java diff --git a/rest-notes-spring-hateoas/src/main/java/com/example/notes/NotePatchInput.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NotePatchInput.java similarity index 100% rename from rest-notes-spring-hateoas/src/main/java/com/example/notes/NotePatchInput.java rename to samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NotePatchInput.java diff --git a/rest-notes-spring-hateoas/src/main/java/com/example/notes/NoteRepository.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NoteRepository.java similarity index 100% rename from rest-notes-spring-hateoas/src/main/java/com/example/notes/NoteRepository.java rename to samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NoteRepository.java diff --git a/rest-notes-spring-hateoas/src/main/java/com/example/notes/NoteResourceAssembler.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NoteResourceAssembler.java similarity index 100% rename from rest-notes-spring-hateoas/src/main/java/com/example/notes/NoteResourceAssembler.java rename to samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NoteResourceAssembler.java diff --git a/rest-notes-spring-hateoas/src/main/java/com/example/notes/NotesController.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NotesController.java similarity index 100% rename from rest-notes-spring-hateoas/src/main/java/com/example/notes/NotesController.java rename to samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NotesController.java diff --git a/rest-notes-spring-hateoas/src/main/java/com/example/notes/NullOrNotBlank.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NullOrNotBlank.java similarity index 100% rename from rest-notes-spring-hateoas/src/main/java/com/example/notes/NullOrNotBlank.java rename to samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/NullOrNotBlank.java diff --git a/rest-notes-spring-hateoas/src/main/java/com/example/notes/ResourceDoesNotExistException.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/ResourceDoesNotExistException.java similarity index 100% rename from rest-notes-spring-hateoas/src/main/java/com/example/notes/ResourceDoesNotExistException.java rename to samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/ResourceDoesNotExistException.java diff --git a/rest-notes-spring-hateoas/src/main/java/com/example/notes/RestNotesControllerAdvice.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/RestNotesControllerAdvice.java similarity index 100% rename from rest-notes-spring-hateoas/src/main/java/com/example/notes/RestNotesControllerAdvice.java rename to samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/RestNotesControllerAdvice.java diff --git a/rest-notes-spring-hateoas/src/main/java/com/example/notes/RestNotesSpringHateoas.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/RestNotesSpringHateoas.java similarity index 100% rename from rest-notes-spring-hateoas/src/main/java/com/example/notes/RestNotesSpringHateoas.java rename to samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/RestNotesSpringHateoas.java diff --git a/rest-notes-spring-hateoas/src/main/java/com/example/notes/Tag.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/Tag.java similarity index 100% rename from rest-notes-spring-hateoas/src/main/java/com/example/notes/Tag.java rename to samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/Tag.java diff --git a/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagInput.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagInput.java similarity index 100% rename from rest-notes-spring-hateoas/src/main/java/com/example/notes/TagInput.java rename to samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagInput.java diff --git a/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagPatchInput.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagPatchInput.java similarity index 100% rename from rest-notes-spring-hateoas/src/main/java/com/example/notes/TagPatchInput.java rename to samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagPatchInput.java diff --git a/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagRepository.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagRepository.java similarity index 100% rename from rest-notes-spring-hateoas/src/main/java/com/example/notes/TagRepository.java rename to samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagRepository.java diff --git a/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagResourceAssembler.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagResourceAssembler.java similarity index 100% rename from rest-notes-spring-hateoas/src/main/java/com/example/notes/TagResourceAssembler.java rename to samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagResourceAssembler.java diff --git a/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagsController.java b/samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagsController.java similarity index 100% rename from rest-notes-spring-hateoas/src/main/java/com/example/notes/TagsController.java rename to samples/rest-notes-spring-hateoas/src/main/java/com/example/notes/TagsController.java diff --git a/rest-notes-spring-hateoas/src/main/resources/application.properties b/samples/rest-notes-spring-hateoas/src/main/resources/application.properties similarity index 100% rename from rest-notes-spring-hateoas/src/main/resources/application.properties rename to samples/rest-notes-spring-hateoas/src/main/resources/application.properties diff --git a/rest-notes-spring-hateoas/src/test/java/com/example/notes/ApiDocumentation.java b/samples/rest-notes-spring-hateoas/src/test/java/com/example/notes/ApiDocumentation.java similarity index 97% rename from rest-notes-spring-hateoas/src/test/java/com/example/notes/ApiDocumentation.java rename to samples/rest-notes-spring-hateoas/src/test/java/com/example/notes/ApiDocumentation.java index 7e201ed2..d82451c8 100644 --- a/rest-notes-spring-hateoas/src/test/java/com/example/notes/ApiDocumentation.java +++ b/samples/rest-notes-spring-hateoas/src/test/java/com/example/notes/ApiDocumentation.java @@ -18,8 +18,8 @@ package com.example.notes; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; -import static org.springframework.restdocs.core.RestDocumentation.document; -import static org.springframework.restdocs.core.RestDocumentation.linkWithRel; +import static org.springframework.restdocs.RestDocumentation.document; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -39,7 +39,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.hateoas.MediaTypes; -import org.springframework.restdocs.core.RestDocumentationConfiguration; +import org.springframework.restdocs.RestDocumentationConfigurer; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; @@ -70,7 +70,7 @@ public class ApiDocumentation { @Before public void setUp() { this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(new RestDocumentationConfiguration()).build(); + .apply(new RestDocumentationConfigurer()).build(); } @Test diff --git a/rest-notes-spring-hateoas/src/test/java/com/example/notes/GettingStartedDocumentation.java b/samples/rest-notes-spring-hateoas/src/test/java/com/example/notes/GettingStartedDocumentation.java similarity index 97% rename from rest-notes-spring-hateoas/src/test/java/com/example/notes/GettingStartedDocumentation.java rename to samples/rest-notes-spring-hateoas/src/test/java/com/example/notes/GettingStartedDocumentation.java index a37e573e..49892b52 100644 --- a/rest-notes-spring-hateoas/src/test/java/com/example/notes/GettingStartedDocumentation.java +++ b/samples/rest-notes-spring-hateoas/src/test/java/com/example/notes/GettingStartedDocumentation.java @@ -19,7 +19,7 @@ package com.example.notes; import static org.hamcrest.Matchers.hasSize; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; -import static org.springframework.restdocs.core.RestDocumentation.document; +import static org.springframework.restdocs.RestDocumentation.document; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -39,7 +39,7 @@ import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.SpringApplicationConfiguration; import org.springframework.hateoas.MediaTypes; -import org.springframework.restdocs.core.RestDocumentationConfiguration; +import org.springframework.restdocs.RestDocumentationConfigurer; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; @@ -67,7 +67,7 @@ public class GettingStartedDocumentation { @Before public void setUp() { this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context) - .apply(new RestDocumentationConfiguration()).build(); + .apply(new RestDocumentationConfigurer()).build(); } @Test diff --git a/settings.gradle b/settings.gradle index c74ec6c2..80e754ca 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,3 @@ -rootProject.name = 'spring-restdocs' +rootProject.name = 'spring-restdocs-build' -include 'spring-restdocs-core' \ No newline at end of file +include 'spring-restdocs' \ No newline at end of file diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentationConfiguration.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentationConfiguration.java deleted file mode 100644 index 70a7c70e..00000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentationConfiguration.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2014 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.restdocs.core; - -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.test.web.servlet.request.RequestPostProcessor; -import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder; -import org.springframework.test.web.servlet.setup.MockMvcConfigurerAdapter; -import org.springframework.web.context.WebApplicationContext; - -public class RestDocumentationConfiguration extends MockMvcConfigurerAdapter { - - private String scheme = "http"; - - private String host = "localhost"; - - private int port = 8080; - - public RestDocumentationConfiguration withScheme(String scheme) { - this.scheme = scheme; - return this; - } - - public RestDocumentationConfiguration withHost(String host) { - this.host = host; - return this; - } - - public RestDocumentationConfiguration withPort(int port) { - this.port = port; - return this; - } - - @Override - public RequestPostProcessor beforeMockMvcCreated( - ConfigurableMockMvcBuilder builder, WebApplicationContext context) { - return new RequestPostProcessor() { - - @Override - public MockHttpServletRequest postProcessRequest( - MockHttpServletRequest request) { - request.setScheme(RestDocumentationConfiguration.this.scheme); - request.setRemotePort(RestDocumentationConfiguration.this.port); - request.setServerPort(RestDocumentationConfiguration.this.port); - request.setRemoteHost(RestDocumentationConfiguration.this.host); - return request; - } - }; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentationResultHandler.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentationResultHandler.java deleted file mode 100644 index a5464bfd..00000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentationResultHandler.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2014 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.restdocs.core; - -import static org.springframework.restdocs.core.RestDocumentationResultHandlers.documentCurlRequest; -import static org.springframework.restdocs.core.RestDocumentationResultHandlers.documentCurlRequestAndResponse; -import static org.springframework.restdocs.core.RestDocumentationResultHandlers.documentCurlResponse; - -import java.util.Arrays; - -import org.springframework.restdocs.core.RestDocumentationResultHandlers.LinkDocumentingResultHandler; -import org.springframework.test.web.servlet.MvcResult; -import org.springframework.test.web.servlet.ResultHandler; - -public class RestDocumentationResultHandler implements ResultHandler { - - private final String outputDir; - - private ResultHandler linkDocumentingResultHandler; - - public RestDocumentationResultHandler(String outputDir) { - this.outputDir = outputDir; - } - - @Override - public void handle(MvcResult result) throws Exception { - documentCurlRequest(this.outputDir).includeResponseHeaders().handle(result); - documentCurlResponse(this.outputDir).includeResponseHeaders().handle(result); - documentCurlRequestAndResponse(this.outputDir).includeResponseHeaders().handle( - result); - if (this.linkDocumentingResultHandler != null) { - this.linkDocumentingResultHandler.handle(result); - } - } - - public RestDocumentationResultHandler withLinks(LinkDescriptor... descriptors) { - return withLinks(null, descriptors); - } - - public RestDocumentationResultHandler withLinks(LinkExtractor linkExtractor, - LinkDescriptor... descriptors) { - this.linkDocumentingResultHandler = new LinkDocumentingResultHandler( - this.outputDir, linkExtractor, Arrays.asList(descriptors)); - return this; - } - -} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentationResultHandlers.java b/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentationResultHandlers.java deleted file mode 100644 index 7b066cc2..00000000 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentationResultHandlers.java +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Copyright 2014-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.restdocs.core; - -import static org.junit.Assert.fail; -import static org.springframework.restdocs.core.IterableEnumeration.iterable; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintStream; -import java.io.StringWriter; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import org.springframework.http.HttpStatus; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.restdocs.core.DocumentationWriter.DocumentationAction; -import org.springframework.test.web.servlet.MvcResult; -import org.springframework.test.web.servlet.ResultHandler; -import org.springframework.util.Assert; -import org.springframework.util.FileCopyUtils; -import org.springframework.web.bind.annotation.RequestMethod; - -public abstract class RestDocumentationResultHandlers { - - public static CurlResultHandler documentCurlRequest(String outputDir) { - return new CurlResultHandler(outputDir, "request") { - - @Override - public void handle(MvcResult result, DocumentationWriter writer) - throws Exception { - writer.shellCommand(new CurlRequestDocumentationAction(writer, result, - getCurlConfiguration())); - } - }; - } - - public static CurlResultHandler documentCurlResponse(String outputDir) { - return new CurlResultHandler(outputDir, "response") { - - @Override - public void handle(MvcResult result, DocumentationWriter writer) - throws Exception { - writer.codeBlock("http", new CurlResponseDocumentationAction(writer, - result, getCurlConfiguration())); - } - }; - } - - public static CurlResultHandler documentCurlRequestAndResponse(String outputDir) { - return new CurlResultHandler(outputDir, "request-response") { - - @Override - public void handle(MvcResult result, DocumentationWriter writer) - throws Exception { - writer.shellCommand(new CurlRequestDocumentationAction(writer, result, - getCurlConfiguration())); - writer.codeBlock("http", new CurlResponseDocumentationAction(writer, - result, getCurlConfiguration())); - } - }; - } - - private static final class CurlRequestDocumentationAction implements - DocumentationAction { - - private final DocumentationWriter writer; - - private final MvcResult result; - - private final CurlConfiguration curlConfiguration; - - CurlRequestDocumentationAction(DocumentationWriter writer, MvcResult result, - CurlConfiguration curlConfiguration) { - this.writer = writer; - this.result = result; - this.curlConfiguration = curlConfiguration; - } - - @Override - public void perform() throws Exception { - MockHttpServletRequest request = this.result.getRequest(); - this.writer.print(String.format("curl %s://%s:%d%s", request.getScheme(), - request.getRemoteHost(), request.getRemotePort(), - request.getRequestURI())); - - if (this.curlConfiguration.includeResponseHeaders) { - this.writer.print(" -i"); - } - - RequestMethod requestMethod = RequestMethod.valueOf(request.getMethod()); - if (requestMethod != RequestMethod.GET) { - this.writer.print(String.format(" -X %s", requestMethod.toString())); - } - - for (String headerName : iterable(request.getHeaderNames())) { - for (String header : iterable(request.getHeaders(headerName))) { - this.writer - .print(String.format(" -H \"%s: %s\"", headerName, header)); - } - } - - if (request.getContentLengthLong() > 0) { - this.writer.print(String.format(" -d '%s'", getContent(request))); - } - - this.writer.println(); - } - - private String getContent(MockHttpServletRequest request) throws IOException { - StringWriter writer = new StringWriter(); - FileCopyUtils.copy(request.getReader(), writer); - return writer.toString(); - } - } - - private static final class CurlResponseDocumentationAction implements - DocumentationAction { - - private final DocumentationWriter writer; - - private final MvcResult result; - - private final CurlConfiguration curlConfiguration; - - CurlResponseDocumentationAction(DocumentationWriter writer, MvcResult result, - CurlConfiguration curlConfiguration) { - this.writer = writer; - this.result = result; - this.curlConfiguration = curlConfiguration; - } - - @Override - public void perform() throws Exception { - if (this.curlConfiguration.includeResponseHeaders) { - HttpStatus status = HttpStatus.valueOf(this.result.getResponse() - .getStatus()); - this.writer.println(String.format("HTTP/1.1 %d %s", status.value(), - status.getReasonPhrase())); - for (String headerName : this.result.getResponse().getHeaderNames()) { - for (String header : this.result.getResponse().getHeaders(headerName)) { - this.writer.println(String.format("%s: %s", headerName, header)); - } - } - this.writer.println(); - } - this.writer.println(this.result.getResponse().getContentAsString()); - } - } - - private static class CurlConfiguration { - - private boolean includeResponseHeaders = false; - - } - - public static abstract class RestDocumentationResultHandler implements ResultHandler { - - private String outputDir; - - private String fileName; - - public RestDocumentationResultHandler(String outputDir, String fileName) { - this.outputDir = outputDir; - this.fileName = fileName; - } - - abstract void handle(MvcResult result, DocumentationWriter writer) - throws Exception; - - @Override - public void handle(MvcResult result) throws Exception { - PrintStream printStream = createPrintStream(); - try { - handle(result, new DocumentationWriter(printStream)); - } - finally { - printStream.close(); - } - } - - protected PrintStream createPrintStream() throws FileNotFoundException { - - File outputFile = new File(this.outputDir, this.fileName + ".asciidoc"); - if (!outputFile.isAbsolute()) { - outputFile = makeAbsolute(outputFile); - } - - if (outputFile != null) { - outputFile.getParentFile().mkdirs(); - return new PrintStream(new FileOutputStream(outputFile)); - } - - return System.out; - } - - private static File makeAbsolute(File outputFile) { - File outputDir = new DocumentationProperties().getOutputDir(); - if (outputDir != null) { - return new File(outputDir, outputFile.getPath()); - } - return null; - } - } - - public static abstract class CurlResultHandler extends RestDocumentationResultHandler { - - private final CurlConfiguration curlConfiguration = new CurlConfiguration(); - - public CurlResultHandler(String outputDir, String fileName) { - super(outputDir, fileName); - } - - CurlConfiguration getCurlConfiguration() { - return this.curlConfiguration; - } - - public CurlResultHandler includeResponseHeaders() { - this.curlConfiguration.includeResponseHeaders = true; - return this; - } - } - - static class LinkDocumentingResultHandler extends RestDocumentationResultHandler { - - private final Map descriptorsByRel = new HashMap(); - - private final LinkExtractor extractor; - - public LinkDocumentingResultHandler(String outputDir, - LinkExtractor linkExtractor, List descriptors) { - super(outputDir, "links"); - this.extractor = linkExtractor; - for (LinkDescriptor descriptor : descriptors) { - Assert.hasText(descriptor.getRel()); - Assert.hasText(descriptor.getDescription()); - this.descriptorsByRel.put(descriptor.getRel(), descriptor); - } - } - - @Override - void handle(MvcResult result, DocumentationWriter writer) throws Exception { - Map> links; - if (this.extractor != null) { - links = this.extractor.extractLinks(result.getResponse()); - } - else { - String contentType = result.getResponse().getContentType(); - LinkExtractor extractorForContentType = LinkExtractors - .extractorForContentType(contentType); - if (extractorForContentType != null) { - links = extractorForContentType.extractLinks(result.getResponse()); - } - else { - throw new IllegalStateException( - "No LinkExtractor has been provided and one is not available for the content type " - + contentType); - } - - } - - Set actualRels = links.keySet(); - Set expectedRels = this.descriptorsByRel.keySet(); - - Set undocumentedRels = new HashSet(actualRels); - undocumentedRels.removeAll(expectedRels); - - Set missingRels = new HashSet(expectedRels); - missingRels.removeAll(actualRels); - - if (!undocumentedRels.isEmpty() || !missingRels.isEmpty()) { - String message = ""; - if (!undocumentedRels.isEmpty()) { - message += "Links with the following relations were not documented: " - + undocumentedRels; - } - if (!missingRels.isEmpty()) { - message += "Links with the following relations were not found in the response: " - + missingRels; - } - fail(message); - } - - Assert.isTrue(actualRels.equals(expectedRels)); - - writer.println("|==="); - writer.println("| Relation | Description"); - - for (Entry entry : this.descriptorsByRel.entrySet()) { - writer.println(); - writer.println("| " + entry.getKey()); - writer.println("| " + entry.getValue().getDescription()); - } - - writer.println("|==="); - } - - } -} diff --git a/spring-restdocs-core/.settings/org.eclipse.jdt.core.prefs b/spring-restdocs/.settings/org.eclipse.jdt.core.prefs similarity index 100% rename from spring-restdocs-core/.settings/org.eclipse.jdt.core.prefs rename to spring-restdocs/.settings/org.eclipse.jdt.core.prefs diff --git a/spring-restdocs-core/.settings/org.eclipse.jdt.ui.prefs b/spring-restdocs/.settings/org.eclipse.jdt.ui.prefs similarity index 100% rename from spring-restdocs-core/.settings/org.eclipse.jdt.ui.prefs rename to spring-restdocs/.settings/org.eclipse.jdt.ui.prefs diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/RestDocumentation.java b/spring-restdocs/src/main/java/org/springframework/restdocs/RestDocumentation.java new file mode 100644 index 00000000..87cd79e4 --- /dev/null +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/RestDocumentation.java @@ -0,0 +1,45 @@ +/* + * Copyright 2014-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.restdocs; + +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + +/** + * Static factory methods for documenting RESTful APIs using Spring MVC Test + * + * @author Andy Wilkinson + */ +public abstract class RestDocumentation { + + private RestDocumentation() { + + } + + /** + * Documents the API call to the given {@code outputDir}. + * + * @param outputDir The directory to which the documentation will be written + * @return a Mock MVC {@code ResultHandler} that will produce the documentation + * @see MockMvc#perform(org.springframework.test.web.servlet.RequestBuilder) + * @see ResultActions#andDo(org.springframework.test.web.servlet.ResultHandler) + */ + public static RestDocumentationResultHandler document(String outputDir) { + return new RestDocumentationResultHandler(outputDir); + } + +} diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/RestDocumentationConfigurer.java b/spring-restdocs/src/main/java/org/springframework/restdocs/RestDocumentationConfigurer.java new file mode 100644 index 00000000..f644cbbd --- /dev/null +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/RestDocumentationConfigurer.java @@ -0,0 +1,112 @@ +/* + * Copyright 2014-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.restdocs; + +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.test.web.servlet.request.RequestPostProcessor; +import org.springframework.test.web.servlet.setup.ConfigurableMockMvcBuilder; +import org.springframework.test.web.servlet.setup.MockMvcConfigurer; +import org.springframework.test.web.servlet.setup.MockMvcConfigurerAdapter; +import org.springframework.web.context.WebApplicationContext; + +/** + * A {@link MockMvcConfigurer} that can be used to configure the documentation + * + * @author Andy Wilkinson + * @see ConfigurableMockMvcBuilder#apply(MockMvcConfigurer) + * + */ +public class RestDocumentationConfigurer extends MockMvcConfigurerAdapter { + + /** + * The default scheme for documented URIs + * @see #withScheme(String) + */ + public static final String DEFAULT_SCHEME = "http"; + + /** + * The defalt host for documented URIs + * @see #withHost(String) + */ + public static final String DEFAULT_HOST = "localhost"; + + /** + * The default port for documented URIs + * @see #withPort(int) + */ + public static final int DEFAULT_PORT = 8080; + + private String scheme = DEFAULT_SCHEME; + + private String host = DEFAULT_HOST; + + private int port = DEFAULT_PORT; + + /** + * Configures any documented URIs to use the given {@code scheme}. The default is + * {@code http}. + * + * @param scheme The URI scheme + * @return {@code this} + */ + public RestDocumentationConfigurer withScheme(String scheme) { + this.scheme = scheme; + return this; + } + + /** + * Configures any documented URIs to use the given {@code host}. The default is + * {@code localhost}. + * + * @param host The URI host + * @return {@code this} + */ + public RestDocumentationConfigurer withHost(String host) { + this.host = host; + return this; + } + + /** + * Configures any documented URIs to use the given {@code port}. The default is + * {@code 8080}. + * + * @param port The URI port + * @return {@code this} + */ + public RestDocumentationConfigurer withPort(int port) { + this.port = port; + return this; + } + + @Override + public RequestPostProcessor beforeMockMvcCreated( + ConfigurableMockMvcBuilder builder, WebApplicationContext context) { + return new RequestPostProcessor() { + + @Override + public MockHttpServletRequest postProcessRequest( + MockHttpServletRequest request) { + request.setScheme(RestDocumentationConfigurer.this.scheme); + request.setRemotePort(RestDocumentationConfigurer.this.port); + request.setServerPort(RestDocumentationConfigurer.this.port); + request.setRemoteHost(RestDocumentationConfigurer.this.host); + return request; + } + }; + } + +} diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/RestDocumentationResultHandler.java b/spring-restdocs/src/main/java/org/springframework/restdocs/RestDocumentationResultHandler.java new file mode 100644 index 00000000..571f2bec --- /dev/null +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/RestDocumentationResultHandler.java @@ -0,0 +1,100 @@ +/* + * Copyright 2014-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.restdocs; + +import static org.springframework.restdocs.curl.CurlDocumentation.documentCurlRequest; +import static org.springframework.restdocs.curl.CurlDocumentation.documentCurlRequestAndResponse; +import static org.springframework.restdocs.curl.CurlDocumentation.documentCurlResponse; +import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.documentLinks; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.restdocs.hypermedia.HypermediaDocumentation; +import org.springframework.restdocs.hypermedia.LinkDescriptor; +import org.springframework.restdocs.hypermedia.LinkExtractor; +import org.springframework.restdocs.hypermedia.LinkExtractors; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.ResultHandler; + +/** + * A Spring MVC Test {@code ResultHandler} for documenting RESTful APIs. + * + * @author Andy Wilkinson + * @see RestDocumentation#document(String) + */ +public class RestDocumentationResultHandler implements ResultHandler { + + private final String outputDir; + + private List delegates; + + RestDocumentationResultHandler(String outputDir) { + this.outputDir = outputDir; + + this.delegates = new ArrayList(); + this.delegates.add(documentCurlRequest(this.outputDir)); + this.delegates.add(documentCurlResponse(this.outputDir)); + this.delegates.add(documentCurlRequestAndResponse(this.outputDir)); + } + + @Override + public void handle(MvcResult result) throws Exception { + for (ResultHandler delegate : this.delegates) { + delegate.handle(result); + } + } + + /** + * Document the links in the response using the given {@code descriptors}. The links + * are extracted from the response based on its content type. + *

+ * If a link is present in the response but is not described by one of the descriptors + * a failure will occur when this handler is invoked. Similarly, if a link is + * described but is not present in the response a failure will also occur when this + * handler is invoked. + * + * @param descriptors the link descriptors + * @return {@code this} + * @see HypermediaDocumentation#linkWithRel(String) + * @see LinkExtractors#extractorForContentType(String) + */ + public RestDocumentationResultHandler withLinks(LinkDescriptor... descriptors) { + return withLinks(null, descriptors); + } + + /** + * Document the links in the response using the given {@code descriptors}. The links + * are extracted from the response using the given {@code linkExtractor}. + *

+ * If a link is present in the response but is not described by one of the descriptors + * a failure will occur when this handler is invoked. Similarly, if a link is + * described but is not present in the response a failure will also occur when this + * handler is invoked. + * + * @param linkExtractor used to extract the links from the response + * @param descriptors the link descriptors + * @return {@code this} + * @see HypermediaDocumentation#linkWithRel(String) + */ + public RestDocumentationResultHandler withLinks(LinkExtractor linkExtractor, + LinkDescriptor... descriptors) { + this.delegates.add(documentLinks(this.outputDir, linkExtractor, descriptors)); + return this; + } + +} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentation.java b/spring-restdocs/src/main/java/org/springframework/restdocs/curl/CurlConfiguration.java similarity index 60% rename from spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentation.java rename to spring-restdocs/src/main/java/org/springframework/restdocs/curl/CurlConfiguration.java index 974e1dbb..c8c8c944 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/RestDocumentation.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/curl/CurlConfiguration.java @@ -14,17 +14,22 @@ * limitations under the License. */ -package org.springframework.restdocs.core; +package org.springframework.restdocs.curl; -public class RestDocumentation { +/** + * Configuration for documenting Curl requests and responses + * + * @author Andy Wilkinson + */ +class CurlConfiguration { - public static RestDocumentationResultHandler document(String outputDir) - throws Exception { - return new RestDocumentationResultHandler(outputDir); + private boolean includeResponseHeaders = true; + + boolean isIncludeResponseHeaders() { + return this.includeResponseHeaders; } - public static LinkDescriptor linkWithRel(String rel) { - return new LinkDescriptor(rel); + void setIncludeResponseHeaders(boolean includeResponseHeaders) { + this.includeResponseHeaders = includeResponseHeaders; } - -} +} \ No newline at end of file diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/curl/CurlDocumentation.java b/spring-restdocs/src/main/java/org/springframework/restdocs/curl/CurlDocumentation.java new file mode 100644 index 00000000..82005876 --- /dev/null +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/curl/CurlDocumentation.java @@ -0,0 +1,189 @@ +/* + * Copyright 2014-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.restdocs.curl; + +import static org.springframework.restdocs.util.IterableEnumeration.iterable; + +import java.io.IOException; +import java.io.StringWriter; + +import org.springframework.http.HttpStatus; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.restdocs.snippet.DocumentationWriter; +import org.springframework.restdocs.snippet.DocumentationWriter.DocumentationAction; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.util.FileCopyUtils; +import org.springframework.web.bind.annotation.RequestMethod; + +/** + * Static factory methods for documenting a RESTful API as if it were being driven using + * the cURL command-line utility. + * + * @author Andy Wilkinson + */ +public abstract class CurlDocumentation { + + private CurlDocumentation() { + + } + + /** + * Produces a documentation snippet containing the request formatted as a cURL command + * + * @param outputDir The directory to which snippet should be written + * @return the handler that will produce the snippet + */ + public static CurlSnippetResultHandler documentCurlRequest(String outputDir) { + return new CurlSnippetResultHandler(outputDir, "request") { + + @Override + public void handle(MvcResult result, DocumentationWriter writer) + throws Exception { + writer.shellCommand(new CurlRequestDocumentationAction(writer, result, + getCurlConfiguration())); + } + }; + } + + /** + * Produces a documentation snippet containing the response formatted as the response + * to a cURL command + * + * @param outputDir The directory to which snippet should be written + * @return the handler that will produce the snippet + */ + public static CurlSnippetResultHandler documentCurlResponse(String outputDir) { + return new CurlSnippetResultHandler(outputDir, "response") { + + @Override + public void handle(MvcResult result, DocumentationWriter writer) + throws Exception { + writer.codeBlock("http", new CurlResponseDocumentationAction(writer, + result, getCurlConfiguration())); + } + }; + } + + /** + * Produces a documentation snippet containing both the request formatted as a cURL + * command and the response formatted formatted s the response to a cURL command. + * + * @param outputDir The directory to which the snippet should be written + * @return the handler that will produce the snippet + */ + public static CurlSnippetResultHandler documentCurlRequestAndResponse(String outputDir) { + return new CurlSnippetResultHandler(outputDir, "request-response") { + + @Override + public void handle(MvcResult result, DocumentationWriter writer) + throws Exception { + writer.shellCommand(new CurlRequestDocumentationAction(writer, result, + getCurlConfiguration())); + writer.codeBlock("http", new CurlResponseDocumentationAction(writer, + result, getCurlConfiguration())); + } + }; + } + + private static final class CurlRequestDocumentationAction implements + DocumentationAction { + + private final DocumentationWriter writer; + + private final MvcResult result; + + private final CurlConfiguration curlConfiguration; + + CurlRequestDocumentationAction(DocumentationWriter writer, MvcResult result, + CurlConfiguration curlConfiguration) { + this.writer = writer; + this.result = result; + this.curlConfiguration = curlConfiguration; + } + + @Override + public void perform() throws Exception { + MockHttpServletRequest request = this.result.getRequest(); + this.writer.print(String.format("curl %s://%s:%d%s", request.getScheme(), + request.getRemoteHost(), request.getRemotePort(), + request.getRequestURI())); + + if (this.curlConfiguration.isIncludeResponseHeaders()) { + this.writer.print(" -i"); + } + + RequestMethod requestMethod = RequestMethod.valueOf(request.getMethod()); + if (requestMethod != RequestMethod.GET) { + this.writer.print(String.format(" -X %s", requestMethod.toString())); + } + + for (String headerName : iterable(request.getHeaderNames())) { + for (String header : iterable(request.getHeaders(headerName))) { + this.writer + .print(String.format(" -H \"%s: %s\"", headerName, header)); + } + } + + if (request.getContentLengthLong() > 0) { + this.writer.print(String.format(" -d '%s'", getContent(request))); + } + + this.writer.println(); + } + + private String getContent(MockHttpServletRequest request) throws IOException { + StringWriter bodyWriter = new StringWriter(); + FileCopyUtils.copy(request.getReader(), bodyWriter); + return bodyWriter.toString(); + } + } + + private static final class CurlResponseDocumentationAction implements + DocumentationAction { + + private final DocumentationWriter writer; + + private final MvcResult result; + + private final CurlConfiguration curlConfiguration; + + CurlResponseDocumentationAction(DocumentationWriter writer, MvcResult result, + CurlConfiguration curlConfiguration) { + this.writer = writer; + this.result = result; + this.curlConfiguration = curlConfiguration; + } + + @Override + public void perform() throws Exception { + if (this.curlConfiguration.isIncludeResponseHeaders()) { + HttpStatus status = HttpStatus.valueOf(this.result.getResponse() + .getStatus()); + this.writer.println(String.format("HTTP/1.1 %d %s", status.value(), + status.getReasonPhrase())); + for (String headerName : this.result.getResponse().getHeaderNames()) { + for (String header : this.result.getResponse().getHeaders(headerName)) { + this.writer.println(String.format("%s: %s", headerName, header)); + } + } + this.writer.println(); + } + this.writer.println(this.result.getResponse().getContentAsString()); + } + } + +} diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/curl/CurlSnippetResultHandler.java b/spring-restdocs/src/main/java/org/springframework/restdocs/curl/CurlSnippetResultHandler.java new file mode 100644 index 00000000..31a687cf --- /dev/null +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/curl/CurlSnippetResultHandler.java @@ -0,0 +1,44 @@ +/* + * Copyright 2014-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.restdocs.curl; + +import org.springframework.restdocs.snippet.SnippetWritingResultHandler; +import org.springframework.test.web.servlet.ResultHandler; + +/** + * Abstract base class for Spring Mock MVC {@link ResultHandler ResultHandlers} that + * produce documentation snippets relating to cURL. + * + * @author Andy Wilkinson + */ +public abstract class CurlSnippetResultHandler extends SnippetWritingResultHandler { + + private final CurlConfiguration curlConfiguration = new CurlConfiguration(); + + public CurlSnippetResultHandler(String outputDir, String fileName) { + super(outputDir, fileName); + } + + CurlConfiguration getCurlConfiguration() { + return this.curlConfiguration; + } + + public CurlSnippetResultHandler includeResponseHeaders() { + this.curlConfiguration.setIncludeResponseHeaders(true); + return this; + } +} \ No newline at end of file diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/hypermedia/HypermediaDocumentation.java b/spring-restdocs/src/main/java/org/springframework/restdocs/hypermedia/HypermediaDocumentation.java new file mode 100644 index 00000000..65a51bd0 --- /dev/null +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/hypermedia/HypermediaDocumentation.java @@ -0,0 +1,63 @@ +/* + * Copyright 2014-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.restdocs.hypermedia; + +import java.util.Arrays; + +import org.springframework.restdocs.RestDocumentationResultHandler; + +/** + * Static factory methods for documenting a RESTful API that utilises Hypermedia. + * + * @author Andy Wilkinson + */ +public abstract class HypermediaDocumentation { + + private HypermediaDocumentation() { + + } + + /** + * Creates a {@code LinkDescriptor} that describes a link with the given {@code rel}. + * + * @param rel The rel of the link + * @return a {@code LinkDescriptor} ready for further configuration + * @see RestDocumentationResultHandler#withLinks(LinkDescriptor...) + * @see RestDocumentationResultHandler#withLinks(LinkExtractor, LinkDescriptor...) + */ + public static LinkDescriptor linkWithRel(String rel) { + return new LinkDescriptor(rel); + } + + /** + * Creates a {@code LinkSnippetResultHandler} that will produce a documentation + * snippet for a response's links. + * + * @param outputDir The directory to which the snippet should be written + * @param linkExtractor Used to extract the links from the response + * @param descriptors The descriptions of the response's links + * @return the handler + * @see RestDocumentationResultHandler#withLinks(LinkDescriptor...) + * @see RestDocumentationResultHandler#withLinks(LinkExtractor, LinkDescriptor...) + */ + public static LinkSnippetResultHandler documentLinks(String outputDir, + LinkExtractor linkExtractor, LinkDescriptor... descriptors) { + return new LinkSnippetResultHandler(outputDir, linkExtractor, + Arrays.asList(descriptors)); + } + +} diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/Link.java b/spring-restdocs/src/main/java/org/springframework/restdocs/hypermedia/Link.java similarity index 97% rename from spring-restdocs-core/src/main/java/org/springframework/restdocs/core/Link.java rename to spring-restdocs/src/main/java/org/springframework/restdocs/hypermedia/Link.java index ef153487..694eec9e 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/Link.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/hypermedia/Link.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.restdocs.core; +package org.springframework.restdocs.hypermedia; import org.springframework.core.style.ToStringCreator; diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/LinkDescriptor.java b/spring-restdocs/src/main/java/org/springframework/restdocs/hypermedia/LinkDescriptor.java similarity index 68% rename from spring-restdocs-core/src/main/java/org/springframework/restdocs/core/LinkDescriptor.java rename to spring-restdocs/src/main/java/org/springframework/restdocs/hypermedia/LinkDescriptor.java index e28a8af4..24a2044c 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/LinkDescriptor.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/hypermedia/LinkDescriptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-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. @@ -14,18 +14,31 @@ * limitations under the License. */ -package org.springframework.restdocs.core; +package org.springframework.restdocs.hypermedia; +/** + * A description of a link found in a hypermedia API + * + * @see HypermediaDocumentation#linkWithRel(String) + * + * @author Andy Wilkinson + */ public class LinkDescriptor { private final String rel; private String description; - public LinkDescriptor(String rel) { + LinkDescriptor(String rel) { this.rel = rel; } + /** + * Specifies the description of the link + * + * @param description The link's description + * @return {@code this} + */ public LinkDescriptor description(String description) { this.description = description; return this; diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/LinkExtractor.java b/spring-restdocs/src/main/java/org/springframework/restdocs/hypermedia/LinkExtractor.java similarity index 96% rename from spring-restdocs-core/src/main/java/org/springframework/restdocs/core/LinkExtractor.java rename to spring-restdocs/src/main/java/org/springframework/restdocs/hypermedia/LinkExtractor.java index f63d2c4f..0627a306 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/LinkExtractor.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/hypermedia/LinkExtractor.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.restdocs.core; +package org.springframework.restdocs.hypermedia; import java.io.IOException; import java.util.List; diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/LinkExtractors.java b/spring-restdocs/src/main/java/org/springframework/restdocs/hypermedia/LinkExtractors.java similarity index 96% rename from spring-restdocs-core/src/main/java/org/springframework/restdocs/core/LinkExtractors.java rename to spring-restdocs/src/main/java/org/springframework/restdocs/hypermedia/LinkExtractors.java index df35102e..07ffd203 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/LinkExtractors.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/hypermedia/LinkExtractors.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.restdocs.core; +package org.springframework.restdocs.hypermedia; import java.io.IOException; import java.util.ArrayList; @@ -35,7 +35,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; * * @author Andy Wilkinson */ -public class LinkExtractors { +public abstract class LinkExtractors { + + private LinkExtractors() { + + } /** * Returns a {@code LinkExtractor} capable of extracting links in Hypermedia @@ -75,7 +79,7 @@ public class LinkExtractors { return null; } - private static abstract class JsonContentLinkExtractor implements LinkExtractor { + private abstract static class JsonContentLinkExtractor implements LinkExtractor { private final ObjectMapper objectMapper = new ObjectMapper(); diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/hypermedia/LinkSnippetResultHandler.java b/spring-restdocs/src/main/java/org/springframework/restdocs/hypermedia/LinkSnippetResultHandler.java new file mode 100644 index 00000000..ee8fc828 --- /dev/null +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/hypermedia/LinkSnippetResultHandler.java @@ -0,0 +1,113 @@ +/* + * Copyright 2014-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.restdocs.hypermedia; + +import static org.junit.Assert.fail; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.springframework.restdocs.snippet.DocumentationWriter; +import org.springframework.restdocs.snippet.SnippetWritingResultHandler; +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.util.Assert; + +/** + * A {@link SnippetWritingResultHandler} that produces a snippet documenting a RESTful + * resource's links. + * + * @author Andy Wilkinson + */ +public class LinkSnippetResultHandler extends SnippetWritingResultHandler { + + private final Map descriptorsByRel = new HashMap(); + + private final LinkExtractor extractor; + + LinkSnippetResultHandler(String outputDir, LinkExtractor linkExtractor, + List descriptors) { + super(outputDir, "links"); + this.extractor = linkExtractor; + for (LinkDescriptor descriptor : descriptors) { + Assert.hasText(descriptor.getRel()); + Assert.hasText(descriptor.getDescription()); + this.descriptorsByRel.put(descriptor.getRel(), descriptor); + } + } + + @Override + protected void handle(MvcResult result, DocumentationWriter writer) throws Exception { + Map> links; + if (this.extractor != null) { + links = this.extractor.extractLinks(result.getResponse()); + } + else { + String contentType = result.getResponse().getContentType(); + LinkExtractor extractorForContentType = LinkExtractors + .extractorForContentType(contentType); + if (extractorForContentType != null) { + links = extractorForContentType.extractLinks(result.getResponse()); + } + else { + throw new IllegalStateException( + "No LinkExtractor has been provided and one is not available for the content type " + + contentType); + } + + } + + Set actualRels = links.keySet(); + Set expectedRels = this.descriptorsByRel.keySet(); + + Set undocumentedRels = new HashSet(actualRels); + undocumentedRels.removeAll(expectedRels); + + Set missingRels = new HashSet(expectedRels); + missingRels.removeAll(actualRels); + + if (!undocumentedRels.isEmpty() || !missingRels.isEmpty()) { + String message = ""; + if (!undocumentedRels.isEmpty()) { + message += "Links with the following relations were not documented: " + + undocumentedRels; + } + if (!missingRels.isEmpty()) { + message += "Links with the following relations were not found in the response: " + + missingRels; + } + fail(message); + } + + Assert.isTrue(actualRels.equals(expectedRels)); + + writer.println("|==="); + writer.println("| Relation | Description"); + + for (Entry entry : this.descriptorsByRel.entrySet()) { + writer.println(); + writer.println("| " + entry.getKey()); + writer.println("| " + entry.getValue().getDescription()); + } + + writer.println("|==="); + } + +} \ No newline at end of file diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/DocumentationWriter.java b/spring-restdocs/src/main/java/org/springframework/restdocs/snippet/AsciidoctorWriter.java similarity index 50% rename from spring-restdocs-core/src/main/java/org/springframework/restdocs/core/DocumentationWriter.java rename to spring-restdocs/src/main/java/org/springframework/restdocs/snippet/AsciidoctorWriter.java index 178bb37b..5791694b 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/DocumentationWriter.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/snippet/AsciidoctorWriter.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-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. @@ -14,47 +14,48 @@ * limitations under the License. */ -package org.springframework.restdocs.core; +package org.springframework.restdocs.snippet; -import java.io.OutputStream; -import java.io.PrintWriter; +import java.io.Writer; -public class DocumentationWriter extends PrintWriter { +/** + * A {@link DocumentationWriter} that produces output in Asciidoctor. + * + * @author Andy Wilkinson + */ +public class AsciidoctorWriter extends DocumentationWriter { - public DocumentationWriter(OutputStream stream) { - super(stream, true); + /** + * Creates a new {@code AsciidoctorWriter} that will write to the given {@code writer} + * @param writer The writer to which output will be written + */ + public AsciidoctorWriter(Writer writer) { + super(writer); } - public void shellCommand(final DocumentationAction... actions) throws Exception { + @Override + public void shellCommand(final DocumentationAction action) throws Exception { codeBlock("bash", new DocumentationAction() { @Override public void perform() throws Exception { - DocumentationWriter.this.print("$ "); - for (DocumentationAction action : actions) { - action.perform(); - } + AsciidoctorWriter.this.print("$ "); + action.perform(); } }); } - public void codeBlock(String language, DocumentationAction... actions) - throws Exception { + @Override + public void codeBlock(String language, DocumentationAction action) throws Exception { println(); if (language != null) { println("[source," + language + "]"); } println("----"); - for (DocumentationAction action : actions) { - action.perform(); - } + action.perform(); println("----"); println(); } - public interface DocumentationAction { - - void perform() throws Exception; - } - } diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/DocumentationProperties.java b/spring-restdocs/src/main/java/org/springframework/restdocs/snippet/DocumentationProperties.java similarity index 93% rename from spring-restdocs-core/src/main/java/org/springframework/restdocs/core/DocumentationProperties.java rename to spring-restdocs/src/main/java/org/springframework/restdocs/snippet/DocumentationProperties.java index fe644def..f8ca87b1 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/DocumentationProperties.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/snippet/DocumentationProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-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. @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.restdocs.core; +package org.springframework.restdocs.snippet; import java.io.File; import java.io.IOException; diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/snippet/DocumentationWriter.java b/spring-restdocs/src/main/java/org/springframework/restdocs/snippet/DocumentationWriter.java new file mode 100644 index 00000000..83d0ee01 --- /dev/null +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/snippet/DocumentationWriter.java @@ -0,0 +1,74 @@ +/* + * Copyright 2014-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.restdocs.snippet; + +import java.io.PrintWriter; +import java.io.Writer; + +/** + * A {@link PrintWriter} that provides additional methods which are useful for producing + * API documentation. + * + * @author Andy Wilkinson + */ +public abstract class DocumentationWriter extends PrintWriter { + + protected DocumentationWriter(Writer writer) { + super(writer, true); + } + + /** + * Calls the given {@code action} to document a shell command. Any prefix necessary + * for the documentation format is written prior to calling the {@code action}. Having + * called the action, any necessary suffix is then written. + * + * @param action the action that will produce the shell command + * @throws Exception if the documentation fails + */ + public abstract void shellCommand(DocumentationAction action) throws Exception; + + /** + * Calls the given {@code action} to document a code block. The code block will be + * annotated as containing code written in the given {@code language}. Any prefix + * necessary for the documentation format is written prior to calling the + * {@code action}. Having called the action, any necessary suffix is the written. + * + * @param language the language in which the code is written + * @param action the action that will produce the code + * @throws Exception if the documentation fails + */ + public abstract void codeBlock(String language, DocumentationAction action) + throws Exception; + + /** + * Encapsulates an action that outputs some documentation. Typically implemented as a + * lamda or, pre-Java 8, as an anonymous inner class. + * + * @author Andy Wilkinson + * @see DocumentationWriter#shellCommand + * @see DocumentationWriter#codeBlock + */ + public interface DocumentationAction { + + /** + * Perform the encapsulated action + * + * @throws Exception if the action fails + */ + void perform() throws Exception; + } +} diff --git a/spring-restdocs/src/main/java/org/springframework/restdocs/snippet/SnippetWritingResultHandler.java b/spring-restdocs/src/main/java/org/springframework/restdocs/snippet/SnippetWritingResultHandler.java new file mode 100644 index 00000000..5785cc8d --- /dev/null +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/snippet/SnippetWritingResultHandler.java @@ -0,0 +1,79 @@ +/* + * Copyright 2014-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.restdocs.snippet; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.Writer; + +import org.springframework.test.web.servlet.MvcResult; +import org.springframework.test.web.servlet.ResultHandler; + +/** + * Base class for a {@link ResultHandler} that writes a documentation snippet + * + * @author Andy Wilkinson + */ +public abstract class SnippetWritingResultHandler implements ResultHandler { + + private String outputDir; + + private String fileName; + + protected SnippetWritingResultHandler(String outputDir, String fileName) { + this.outputDir = outputDir; + this.fileName = fileName; + } + + protected abstract void handle(MvcResult result, DocumentationWriter writer) + throws Exception; + + @Override + public void handle(MvcResult result) throws Exception { + Writer writer = createWriter(); + try { + handle(result, new AsciidoctorWriter(writer)); + } + finally { + writer.close(); + } + } + + private Writer createWriter() throws IOException { + File outputFile = new File(this.outputDir, this.fileName + ".asciidoc"); + if (!outputFile.isAbsolute()) { + outputFile = makeRelativeToConfiguredOutputDir(outputFile); + } + + if (outputFile != null) { + outputFile.getParentFile().mkdirs(); + return new FileWriter(outputFile); + } + + return new OutputStreamWriter(System.out); + } + + private File makeRelativeToConfiguredOutputDir(File outputFile) { + File configuredOutputDir = new DocumentationProperties().getOutputDir(); + if (configuredOutputDir != null) { + return new File(configuredOutputDir, outputFile.getPath()); + } + return null; + } +} \ No newline at end of file diff --git a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/IterableEnumeration.java b/spring-restdocs/src/main/java/org/springframework/restdocs/util/IterableEnumeration.java similarity index 65% rename from spring-restdocs-core/src/main/java/org/springframework/restdocs/core/IterableEnumeration.java rename to spring-restdocs/src/main/java/org/springframework/restdocs/util/IterableEnumeration.java index b374fbc0..0dc04bb1 100644 --- a/spring-restdocs-core/src/main/java/org/springframework/restdocs/core/IterableEnumeration.java +++ b/spring-restdocs/src/main/java/org/springframework/restdocs/util/IterableEnumeration.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-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. @@ -14,16 +14,23 @@ * limitations under the License. */ -package org.springframework.restdocs.core; +package org.springframework.restdocs.util; import java.util.Enumeration; import java.util.Iterator; -public final class IterableEnumeration implements Iterable { +/** + * An adapter to expose an {@link Enumeration} as an {@link Iterable}. + * + * @author Andy Wilkinson + * + * @param the type of the Enumeration's contents + */ +public class IterableEnumeration implements Iterable { private final Enumeration enumeration; - public IterableEnumeration(Enumeration enumeration) { + private IterableEnumeration(Enumeration enumeration) { this.enumeration = enumeration; } @@ -49,6 +56,13 @@ public final class IterableEnumeration implements Iterable { }; } + /** + * Creates a new {@code Iterable} that will iterate over the given {@code enumeration} + * + * @param the type of the enumeration's elements + * @param enumeration The enumeration to expose as an {@code Iterable} + * @return the iterable + */ public static Iterable iterable(Enumeration enumeration) { return new IterableEnumeration(enumeration); } diff --git a/spring-restdocs-core/src/test/java/org/springframework/restdocs/core/LinkExtractorsTests.java b/spring-restdocs/src/test/java/org/springframework/restdocs/core/LinkExtractorsTests.java similarity index 95% rename from spring-restdocs-core/src/test/java/org/springframework/restdocs/core/LinkExtractorsTests.java rename to spring-restdocs/src/test/java/org/springframework/restdocs/core/LinkExtractorsTests.java index 1794b7d1..f4a77439 100644 --- a/spring-restdocs-core/src/test/java/org/springframework/restdocs/core/LinkExtractorsTests.java +++ b/spring-restdocs/src/test/java/org/springframework/restdocs/core/LinkExtractorsTests.java @@ -34,6 +34,9 @@ import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.restdocs.hypermedia.Link; +import org.springframework.restdocs.hypermedia.LinkExtractor; +import org.springframework.restdocs.hypermedia.LinkExtractors; import org.springframework.util.FileCopyUtils; /** diff --git a/spring-restdocs/src/test/java/org/springframework/restdocs/core/RestDocumentationConfigurerTests.java b/spring-restdocs/src/test/java/org/springframework/restdocs/core/RestDocumentationConfigurerTests.java new file mode 100644 index 00000000..5ff872ba --- /dev/null +++ b/spring-restdocs/src/test/java/org/springframework/restdocs/core/RestDocumentationConfigurerTests.java @@ -0,0 +1,78 @@ +/* + * Copyright 2014-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.restdocs.core; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.restdocs.RestDocumentationConfigurer; +import org.springframework.test.web.servlet.request.RequestPostProcessor; + +/** + * Tests for {@link RestDocumentationConfigurer}. + * + * @author Andy Wilkinson + */ +public class RestDocumentationConfigurerTests { + + private MockHttpServletRequest request = new MockHttpServletRequest(); + + @Test + public void defaultConfiguration() { + RequestPostProcessor postProcessor = new RestDocumentationConfigurer() + .beforeMockMvcCreated(null, null); + postProcessor.postProcessRequest(this.request); + + assertUriConfiguration("http", "localhost", 8080); + } + + @Test + public void customScheme() { + RequestPostProcessor postProcessor = new RestDocumentationConfigurer() + .withScheme("https").beforeMockMvcCreated(null, null); + postProcessor.postProcessRequest(this.request); + + assertUriConfiguration("https", "localhost", 8080); + } + + @Test + public void customHost() { + RequestPostProcessor postProcessor = new RestDocumentationConfigurer().withHost( + "api.example.com").beforeMockMvcCreated(null, null); + postProcessor.postProcessRequest(this.request); + + assertUriConfiguration("http", "api.example.com", 8080); + } + + @Test + public void customPort() { + RequestPostProcessor postProcessor = new RestDocumentationConfigurer().withPort( + 8081).beforeMockMvcCreated(null, null); + postProcessor.postProcessRequest(this.request); + + assertUriConfiguration("http", "localhost", 8081); + } + + private void assertUriConfiguration(String scheme, String host, int port) { + assertEquals(scheme, this.request.getScheme()); + assertEquals(host, this.request.getRemoteHost()); + assertEquals(port, this.request.getRemotePort()); + assertEquals(port, this.request.getServerPort()); + } + +} diff --git a/spring-restdocs/src/test/java/org/springframework/restdocs/snippet/AsciidoctorWriterTests.java b/spring-restdocs/src/test/java/org/springframework/restdocs/snippet/AsciidoctorWriterTests.java new file mode 100644 index 00000000..33831d0b --- /dev/null +++ b/spring-restdocs/src/test/java/org/springframework/restdocs/snippet/AsciidoctorWriterTests.java @@ -0,0 +1,67 @@ +/* + * Copyright 2014-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.restdocs.snippet; + +import static org.junit.Assert.assertEquals; + +import java.io.StringWriter; +import java.io.Writer; + +import org.junit.Test; +import org.springframework.restdocs.snippet.AsciidoctorWriter; +import org.springframework.restdocs.snippet.DocumentationWriter; +import org.springframework.restdocs.snippet.DocumentationWriter.DocumentationAction; + +/** + * Tests for {@link AsciidoctorWriter} + * + * @author Andy Wilkinson + */ +public class AsciidoctorWriterTests { + + private Writer output = new StringWriter(); + + private DocumentationWriter documentationWriter = new AsciidoctorWriter(this.output); + + @Test + public void codeBlock() throws Exception { + this.documentationWriter.codeBlock("java", new DocumentationAction() { + + @Override + public void perform() throws Exception { + AsciidoctorWriterTests.this.documentationWriter.println("foo"); + } + }); + + String expectedOutput = String.format("\n[source,java]\n----\nfoo\n----\n\n"); + assertEquals(expectedOutput, this.output.toString()); + } + + @Test + public void shellCommand() throws Exception { + this.documentationWriter.shellCommand(new DocumentationAction() { + + @Override + public void perform() throws Exception { + AsciidoctorWriterTests.this.documentationWriter.println("foo"); + } + }); + + String expectedOutput = String.format("\n[source,bash]\n----\n$ foo\n----\n\n"); + assertEquals(expectedOutput, this.output.toString()); + } +} diff --git a/spring-restdocs-core/src/test/resources/link-payloads/atom/multiple-links-different-rels.json b/spring-restdocs/src/test/resources/link-payloads/atom/multiple-links-different-rels.json similarity index 100% rename from spring-restdocs-core/src/test/resources/link-payloads/atom/multiple-links-different-rels.json rename to spring-restdocs/src/test/resources/link-payloads/atom/multiple-links-different-rels.json diff --git a/spring-restdocs-core/src/test/resources/link-payloads/atom/multiple-links-same-rels.json b/spring-restdocs/src/test/resources/link-payloads/atom/multiple-links-same-rels.json similarity index 100% rename from spring-restdocs-core/src/test/resources/link-payloads/atom/multiple-links-same-rels.json rename to spring-restdocs/src/test/resources/link-payloads/atom/multiple-links-same-rels.json diff --git a/spring-restdocs-core/src/test/resources/link-payloads/atom/no-links.json b/spring-restdocs/src/test/resources/link-payloads/atom/no-links.json similarity index 100% rename from spring-restdocs-core/src/test/resources/link-payloads/atom/no-links.json rename to spring-restdocs/src/test/resources/link-payloads/atom/no-links.json diff --git a/spring-restdocs-core/src/test/resources/link-payloads/atom/single-link.json b/spring-restdocs/src/test/resources/link-payloads/atom/single-link.json similarity index 100% rename from spring-restdocs-core/src/test/resources/link-payloads/atom/single-link.json rename to spring-restdocs/src/test/resources/link-payloads/atom/single-link.json diff --git a/spring-restdocs-core/src/test/resources/link-payloads/atom/wrong-format.json b/spring-restdocs/src/test/resources/link-payloads/atom/wrong-format.json similarity index 100% rename from spring-restdocs-core/src/test/resources/link-payloads/atom/wrong-format.json rename to spring-restdocs/src/test/resources/link-payloads/atom/wrong-format.json diff --git a/spring-restdocs-core/src/test/resources/link-payloads/hal/multiple-links-different-rels.json b/spring-restdocs/src/test/resources/link-payloads/hal/multiple-links-different-rels.json similarity index 100% rename from spring-restdocs-core/src/test/resources/link-payloads/hal/multiple-links-different-rels.json rename to spring-restdocs/src/test/resources/link-payloads/hal/multiple-links-different-rels.json diff --git a/spring-restdocs-core/src/test/resources/link-payloads/hal/multiple-links-same-rels.json b/spring-restdocs/src/test/resources/link-payloads/hal/multiple-links-same-rels.json similarity index 100% rename from spring-restdocs-core/src/test/resources/link-payloads/hal/multiple-links-same-rels.json rename to spring-restdocs/src/test/resources/link-payloads/hal/multiple-links-same-rels.json diff --git a/spring-restdocs-core/src/test/resources/link-payloads/hal/no-links.json b/spring-restdocs/src/test/resources/link-payloads/hal/no-links.json similarity index 100% rename from spring-restdocs-core/src/test/resources/link-payloads/hal/no-links.json rename to spring-restdocs/src/test/resources/link-payloads/hal/no-links.json diff --git a/spring-restdocs-core/src/test/resources/link-payloads/hal/single-link.json b/spring-restdocs/src/test/resources/link-payloads/hal/single-link.json similarity index 100% rename from spring-restdocs-core/src/test/resources/link-payloads/hal/single-link.json rename to spring-restdocs/src/test/resources/link-payloads/hal/single-link.json diff --git a/spring-restdocs-core/src/test/resources/link-payloads/hal/wrong-format.json b/spring-restdocs/src/test/resources/link-payloads/hal/wrong-format.json similarity index 100% rename from spring-restdocs-core/src/test/resources/link-payloads/hal/wrong-format.json rename to spring-restdocs/src/test/resources/link-payloads/hal/wrong-format.json