diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..cfbd418
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,14 @@
+.class
+.settings
+.project
+.classpath
+target
+bin
+#intellij
+.idea/
+*.iml
+*.iws
+spy.log
+*.log
+src/test/java/generated
+*/generated-test-sources
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8dada3e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright {yyyy} {name of copyright owner}
+
+ 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.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1db72c8
--- /dev/null
+++ b/README.md
@@ -0,0 +1,140 @@
+
+# Restdoc Helper
+
+## Overview
+
+The main goal of this project is to simplify the documentation process of your RESTFul services by combining hand-written using [Asciidoctor][3] docs, [Sprinfox static docs][1] generated docs or [Spring REST Docs][10] generated docs.
+Restdoc helper is a Maven plugin that generates [Asciidoctor][3] files and [FieldDescriptors][7] from [Swagger 2][6] annotations
+
+The purpose of [Spring REST Docs][10] is to document your API with auto-generated snippets produced with [Spring MVC Test][4].
+To achieve that, it's necessary to document all fields of data classes involved in your call-flows. If as me you use [Swagger 2][6] to describe your API contracts, the job is almost done.
+What'is missing is the translation of your swagger annotations to a [FieldDescriptors][7]. This is the purpose of this contribution.
+It's also possible to generate asciidoctor files only that describe your model from custom annotation.
+
+## Doc generation
+
+### Add dependencies to your pom
+
+```
+
+ com.pconil.restdoc
+ restdoc-annotation
+ 1.0-SNAPSHOT
+ `
+
+ ...
+
+
+ com.pconil.restdoc
+ restdoc-helper-plugin
+ 1.0-SNAPSHOT
+
+ ${basedir}/target/classes/
+ com.pconil.restdoc.model
+ ${basedir}/target/generated-test-sources
+ ${basedir}/target/generated-snippets
+
+
+
+ generate
+ generate-test-sources
+
+ restdoc-helper
+
+
+
+
+
+ org.springframework
+ spring-web
+ 4.3.3.RELEASE
+
+
+
+ ...
+
+```
+
+### Howto Generate adoc files from swagger annotations
+
+As simple as adding @InspectToDocument to your model class
+
+```
+@InspectToDocument(description = "This is the Class1 description")
+public class ClassSwaggerDTO {
+ @ApiModelProperty(required = true, value="This is the field field1")
+ private String field1;
+
+ @ApiModelProperty(value="This is the optional field field2")
+ private String field2 = null;
+
+ @ApiModelProperty(required = true, value="This is the field field3")
+ private String field3 = null;
+
+ public ClassSwaggerDTO(String field1, String field2) {
+ this.field1 = field1;
+ this.field2 = field2;
+ }
+}
+```
+
+
+### And If you don't use swagger or want to document classes that doesn't contain @ApiModelProperty
+
+Add a @AsciidocAnnotation to your fields
+
+```
+@InspectToDocument(description = "This is the Class1 description")
+public class Class1DTO {
+ @AsciidocAnnotation(description="This field describe name of Class1DTO", constraints = "Length must be between 4 and 6")
+ String field1;
+
+ @AsciidocAnnotation(description="field 2")
+ String field2 = null;
+
+ public Class1DTO(String field1, String field2) {
+ this.field1 = field1;
+ this.field2 = field2;
+ }
+}
+```
+
+## Go further
+
+See the springboot-sample to see how it works.
+
+## Prerequisite
+
+Java 8, maven 3
+
+## Building from source
+
+You will need Java 8 or later to build restdoc-helper. It is built using [maven][2]:
+
+```
+mvn clean install
+```
+
+## Contributing
+
+There are several ways to contribute to restdoc-helper:
+
+ - Open a [pull request][12].
+ - Ask and answer questions on Stack Overflow using the [`restdoc-helper`][15] tag.
+
+## Licence
+
+Restdoc helper is open source software released under the [Apache 2.0 license][14].
+
+
+[1]: https://springfox.github.io/springfox/docs/snapshot/#configuring-springfox-staticdocs
+[2]: https://maven.apache.org/download.cgi
+[3]: http://asciidoctor.org
+[4]: http://docs.spring.io/spring-framework/docs/4.1.x/spring-framework-reference/htmlsingle/#spring-mvc-test-framework
+[5]: https://developer.github.com/v3/
+[6]: http://swagger.io/specification/
+[7]: http://docs.spring.io/spring-restdocs/docs/current/reference/html5/#documenting-your-api-request-response-payloads-reusing-field-descriptors
+[10]: http://docs.spring.io/spring-restdocs/docs/current/reference/html5/
+[12]: https://help.github.com/articles/using-pull-requests/
+[14]: http://www.apache.org/licenses/LICENSE-2.0.html
+[15]: http://stackoverflow.com/tags/restdoc-helper
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..891e1ad
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,132 @@
+
+
+ 4.0.0
+
+ restdoc-annotation
+ restdoc-helper-plugin
+ springboot-sample
+
+
+ com.pconil.restdoc
+ restdoc-helper
+ 1.0-SNAPSHOT
+ pom
+
+
+ UTF-8
+ UTF-8
+ 1.8
+ 1.4.0.RELEASE
+ 4.3.2.RELEASE
+ 2.5.0
+ 4.2.5.RELEASE
+ 1.1.2.RELEASE
+ 4.12
+ 1.5.9
+
+
+
+
+
+ org.springframework.restdocs
+ spring-restdocs-mockmvc
+ ${spring-restdoc.version}
+
+
+
+ io.springfox
+ springfox-swagger2
+ ${springfox.version}
+
+
+ io.swagger
+ swagger-models
+ ${swagger-model.version}
+
+
+ io.springfox
+ springfox-staticdocs
+ ${springfox.version}
+
+
+
+ junit
+ junit
+ ${junit.version}
+
+
+ org.hamcrest
+ hamcrest-all
+ 1.3
+
+
+ org.mockito
+ mockito-core
+ 1.10.19
+
+
+ com.jayway.jsonpath
+ json-path
+ 2.0.0
+
+
+ org.codehaus.plexus
+ plexus-utils
+ 2.0.5
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+ 1.4.1
+
+
+ enforce
+
+
+
+
+
+
+ enforce
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.5.1
+
+ ${java.version}
+ ${java.version}
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ 1.4.1.RELEASE
+
+
+
+
+
+
+ spring-releases
+ https://repo.spring.io/libs-release
+
+
+ central
+ http://repo1.maven.org/maven2/
+
+
+
+
\ No newline at end of file
diff --git a/restdoc-annotation/pom.xml b/restdoc-annotation/pom.xml
new file mode 100644
index 0000000..77c6d09
--- /dev/null
+++ b/restdoc-annotation/pom.xml
@@ -0,0 +1,14 @@
+
+
+
+ restdoc-helper
+ com.pconil.restdoc
+ 1.0-SNAPSHOT
+
+ 4.0.0
+
+ restdoc-annotation
+
+
\ No newline at end of file
diff --git a/restdoc-annotation/src/main/java/com/pconil/restdoc/annotation/AsciidocAnnotation.java b/restdoc-annotation/src/main/java/com/pconil/restdoc/annotation/AsciidocAnnotation.java
new file mode 100644
index 0000000..5e78c30
--- /dev/null
+++ b/restdoc-annotation/src/main/java/com/pconil/restdoc/annotation/AsciidocAnnotation.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to document field.
+ * @author patrice_conil
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.FIELD)
+@Inherited
+@Documented
+public @interface AsciidocAnnotation {
+ /**
+ * Contains description of the annotated field.
+ *
+ * @return the description of the annotated field
+ */
+ String description() default "";
+
+ /**
+ * Contains constraints to apply to the annotated field.
+ *
+ * @return the constrains that apply to the annotated field
+ */
+ String constraints() default "";
+
+ /**
+ * Tells if the field is mandatory or not.
+ *
+ * @return true if field is mandatory, false otherwise
+ */
+ boolean required() default true;
+}
diff --git a/restdoc-annotation/src/main/java/com/pconil/restdoc/annotation/InspectToDocument.java b/restdoc-annotation/src/main/java/com/pconil/restdoc/annotation/InspectToDocument.java
new file mode 100644
index 0000000..70d5fc8
--- /dev/null
+++ b/restdoc-annotation/src/main/java/com/pconil/restdoc/annotation/InspectToDocument.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation used to tell AnnotationParser to generate Class.adoc file for this class.
+ * @author patrice_conil
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Inherited
+@Documented
+public @interface InspectToDocument {
+ /**
+ * Description of the class in restdoc corresponding file.
+ * @return the description of the class
+ */
+ String description() default "";
+}
diff --git a/restdoc-helper-plugin/pom.xml b/restdoc-helper-plugin/pom.xml
new file mode 100644
index 0000000..eff342f
--- /dev/null
+++ b/restdoc-helper-plugin/pom.xml
@@ -0,0 +1,256 @@
+
+
+ 4.0.0
+
+
+ com.pconil.restdoc
+ restdoc-helper
+ 1.0-SNAPSHOT
+
+
+ restdoc-helper-plugin
+ maven-plugin
+
+
+
+ org.apache.maven
+ maven-plugin-api
+ 3.3.9
+
+
+ org.apache.maven
+ maven-model
+ 3.3.9
+
+
+ org.apache.maven.plugin-testing
+ maven-plugin-testing-harness
+ 3.3.0
+ test
+
+
+ org.apache.maven
+ maven-core
+ 3.3.9
+
+
+ com.google.guava
+ guava
+
+
+ test
+
+
+ com.google.guava
+ guava
+ 18.0
+
+
+ org.apache.maven
+ maven-compat
+ 3.3.9
+ test
+
+
+ junit
+ junit
+ test
+
+
+ io.swagger
+ swagger-annotations
+ 1.5.10
+
+
+ org.slf4j
+ slf4j-api
+
+
+
+
+ org.springframework.restdocs
+ spring-restdocs-mockmvc
+ test
+
+
+ org.slf4j
+ slf4j-api
+ 1.7.21
+
+
+ org.slf4j
+ slf4j-jdk14
+ 1.7.21
+
+
+ org.assertj
+ assertj-core
+
+ 3.5.2
+ test
+
+
+ com.pconil.restdoc
+ restdoc-annotation
+ 1.0-SNAPSHOT
+
+
+ org.apache.maven.plugins
+ maven-plugin-plugin
+ 3.3
+
+
+ org.apache.maven
+ maven-plugin-api
+
+
+ org.apache.maven
+ maven-settings
+
+
+ org.apache.maven
+ maven-plugin-annotations
+
+
+ org.codehaus.plexus
+ plexus-component-annotations
+
+
+ org.codehaus.plexus
+ plexus-container-default
+
+
+ org.apache.maven.doxia
+ doxia-sink-api
+
+
+ org.apache.maven.doxia
+ doxia-logging-api
+
+
+ org.codehaus.plexus
+ plexus-velocity
+
+
+ org.codehaus.plexus
+ plexus-interpolation
+
+
+ org.apache.maven
+ maven-core
+
+
+ org.apache.maven.reporting
+ maven-reporting-impl
+
+
+ org.apache.maven.wagon
+ wagon-provider-api
+
+
+ org.apache.maven
+ maven-repository-metadata
+
+
+ org.apache.maven
+ maven-model
+
+
+ org.apache.maven
+ maven-artifact
+
+
+ org.apache.velocity
+ velocity
+
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 2.5.1
+
+ 1.8
+ 1.8
+
+
+
+ org.apache.maven.plugins
+ maven-plugin-plugin
+ 3.5
+
+
+ true
+
+
+
+ mojo-descriptor
+
+ descriptor
+
+ process-classes
+
+
+
+ help-goal
+
+ helpmojo
+
+
+
+
+
+
+ org.asciidoctor
+ asciidoctor-maven-plugin
+ 1.5.3
+
+
+ generate-docs
+
+ prepare-package
+
+ process-asciidoc
+
+
+ html
+ book
+
+
+ ${project.build.directory}/generated-snippets
+
+
+
+
+
+
+
+ maven-resources-plugin
+
+
+ copy-resources
+ prepare-package
+
+ copy-resources
+
+
+ ${project.build.outputDirectory}/static/docs
+
+
+ ${project.build.directory}/generated-docs
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/restdoc-helper-plugin/src/main/asciidoc/ApiDocumentation.adoc b/restdoc-helper-plugin/src/main/asciidoc/ApiDocumentation.adoc
new file mode 100644
index 0000000..e58e231
--- /dev/null
+++ b/restdoc-helper-plugin/src/main/asciidoc/ApiDocumentation.adoc
@@ -0,0 +1,26 @@
+= Live API Getting Started Guide
+:doctype: book
+:icons: font
+:source-highlighter: highlightjs
+:toc: left
+:toclevels: 4
+:sectlinks:
+
+[introduction]
+= Introduction
+ Live API is a RESTful microservice to retrieve data from TV Live Domain (Channels, Programs and so on)
+
+[[com.pconil.restdoc.model]]
+= Models
+Object models used to describe resources
+
+:leveloffset: +1
+include::{snippets}/Class1DTO.adoc[]
+include::{snippets}/Class2DTO.adoc[]
+
+include::{snippets}/SimilarContent.adoc[]
+
+:leveloffset: -1
+
+
+
diff --git a/restdoc-helper-plugin/src/main/java/com/pconil/restdoc/AbstractParser.java b/restdoc-helper-plugin/src/main/java/com/pconil/restdoc/AbstractParser.java
new file mode 100644
index 0000000..afb9c42
--- /dev/null
+++ b/restdoc-helper-plugin/src/main/java/com/pconil/restdoc/AbstractParser.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc;
+
+import com.pconil.restdoc.annotation.InspectToDocument;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.util.List;
+import java.util.regex.Pattern;
+
+/**
+ * This parser produces target/ClassName.restdoc files for all classes matching package-name or its subpackages.
+ *
+ * @author patrice_conil
+ */
+@InspectToDocument(description = "Parser used to generate restdoc files from swagger annotation")
+abstract class AbstractParser {
+
+
+ /**
+ * The logger.
+ */
+ private static final Logger LOGGER = LoggerFactory.getLogger(AbstractParser.class);
+
+ /**
+ * File where to write restdoc file.
+ */
+ FileOutputStream adocFile = null;
+
+ /**
+ * Java dir.
+ */
+ String targetJavaDirName = null;
+
+ /**
+ * Directory where we want to put restdoc files.
+ */
+ String targetAdocDirName = null;
+
+ /**
+ * Directory that contains .class to parse.
+ */
+ File sourceDir;
+
+ /**
+ * Directory where we want to put java file.
+ */
+ File targetJavaDir = null;
+
+ /**
+ * Directory where we want to put adoc file.
+ */
+ File targetAdocDir = null;
+
+ /**
+ * Package name.
+ */
+ String packageName;
+
+ /**
+ * AbstractParser Constructor.
+ *
+ * @param packageName name of package that we want to parse
+ * @param adocDirName project for which we want to generate restdoc and java files
+ * @param javaDirName project for which we want to generate restdoc and java files
+ * @param source directory where .class will be found
+ * @throws ParserException if packageName is malformed or target creation isn't possible
+ */
+ AbstractParser(String packageName, String adocDirName, String javaDirName, String source) throws ParserException {
+
+ this.packageName = packageName;
+ targetAdocDirName = adocDirName;
+ targetJavaDirName = javaDirName;
+
+ targetJavaDir = new File(targetJavaDirName);
+ targetAdocDir = new File(targetAdocDirName);
+
+ sourceDir = new File(source);
+
+ if (!sourceDir.isDirectory()) {
+ logErrorAndThrowParserException(String.format(" %s is not a directory", targetJavaDirName));
+
+ }
+ validatePackageName(packageName);
+
+ if (!targetJavaDir.exists() && !targetJavaDir.mkdirs()) {
+ logErrorAndThrowParserException(String.format("Can't create directory %s for java sources",
+ targetJavaDirName));
+ }
+ if (!targetAdocDir.exists() && !targetAdocDir.mkdirs()) {
+ logErrorAndThrowParserException(String.format("Can't create directory %s for restdoc files",
+ targetAdocDirName));
+ }
+ }
+
+
+ /**
+ * Parses packageName and its sub packages to generate Class.restdoc files for Class tagged @InspectToDocument.
+ * Class.restdoc files are generated in target/generated-snippet/com.pconil.restdoc.model targetJavaDir.
+ *
+ * @throws ParserException if we can't create Class.adhoc files in target/generated-snippets/com.pconil.restdoc
+ * .model.
+ */
+ void parse() throws ParserException {
+
+ List classes;
+ boolean hasDocumentedField;
+
+ ParserClassLoader classLoader = null;
+
+ try {
+ // Add source url to local classloader
+ classLoader = new ParserClassLoader(sourceDir);
+ classes = classLoader.getClasses(sourceDir.getAbsolutePath(), packageName);
+
+ for (Class c : classes) {
+ LOGGER.debug("Parsing class {} to generate java code", c.getSimpleName());
+ if (c.isAnnotationPresent(InspectToDocument.class)) {
+ hasDocumentedField = false;
+ // Parse all fields declared in this class
+ initializeFields();
+ for (Field field : c.getDeclaredFields()) {
+ hasDocumentedField = parseField(c, field, hasDocumentedField);
+ }
+ if (hasDocumentedField) {
+ completeFields();
+ completeClass();
+ writeClass();
+ } else {
+ LOGGER.debug(String.format("You declared your class %s as documented "
+ + "but there is no documented field in it",
+ c.getSimpleName()));
+ }
+ }
+ }
+ classLoader.restoreLoader();
+ } catch (ClassNotFoundException | IOException e) {
+ logErrorAndThrowParserException("Class not found while parsing your model");
+ }
+ }
+
+ /**
+ * Logs the error message and send a ParserException.
+ *
+ * @param message the error message
+ */
+ protected void logErrorAndThrowParserException(String message) {
+ LOGGER.error(message);
+ throw new ParserException(message);
+ }
+
+ /**
+ * Parses current field to check if it's a field to document.
+ *
+ * @param c the class we're parsing
+ * @param field the current field to parse
+ * @param hasDocumentation tells if class c already have a documented field
+ * @return hasDocumented || this field is documented
+ */
+ protected abstract boolean parseField(Class c, Field field, boolean hasDocumentation);
+
+ /**
+ * Initializes header(s) for fields.
+ *
+ * @param c the classe for which we initialize field header
+ */
+ protected abstract void writeFieldStart(Class c);
+
+ /**
+ * Initializes info buffer for class c.
+ *
+ * @param c the class we're parsing
+ * @throws IOException if write failed
+ */
+ protected abstract void writeClassStart(Class c) throws IOException;
+
+ /**
+ * Initializes writing for fields.
+ *
+ * @throws IOException if something goes wrong with file creation
+ */
+ protected abstract void initializeFields() throws IOException;
+
+ /**
+ * Generates the file.
+ *
+ * @return the FileOutputStream if all is ok.
+ */
+ protected abstract FileOutputStream writeClass();
+
+
+ /**
+ * Ends class writing.
+ *
+ * @throws IOException if write failed
+ */
+ protected abstract void completeClass() throws IOException;
+
+ /**
+ * Ends field writing.
+ *
+ * @throws IOException if write failed
+ */
+ protected abstract void completeFields() throws IOException;
+
+ /**
+ * Check if packageName is well formed ... but not if its a java keyword.
+ *
+ * @param packageName the name of the package to check
+ * @throws ParserException if the package name is invalid
+ */
+ private void validatePackageName(String packageName) throws ParserException {
+ Pattern p = Pattern.compile("^[a-zA-Z_\\$][\\w\\$]*(?:\\.[a-zA-Z_\\$][\\w\\$]*)*$");
+ if (!p.matcher(packageName).matches()) {
+ logErrorAndThrowParserException(String.format("invalid package name: %s", packageName));
+ }
+ }
+
+ /**
+ * Create file targetDir/simpleName+suffix.
+ *
+ * @param simpleName the class name
+ * @param targetDir the target directory
+ * @param suffix the file suffix to add
+ * @return a file descriptor
+ * @throws ParserException if creation fail
+ */
+ FileOutputStream createClassFile(String simpleName, String targetDir, String suffix) throws ParserException {
+ FileOutputStream file = null;
+ String classFileName = targetDir + "/" + simpleName + suffix;
+ try {
+ file = new FileOutputStream(classFileName, false);
+ LOGGER.debug("File {} created", classFileName);
+ } catch (FileNotFoundException e) {
+ logErrorAndThrowParserException(String.format("Can't create file %s", classFileName));
+ }
+ return file;
+ }
+
+}
diff --git a/restdoc-helper-plugin/src/main/java/com/pconil/restdoc/Annotation2AsciiDocParser.java b/restdoc-helper-plugin/src/main/java/com/pconil/restdoc/Annotation2AsciiDocParser.java
new file mode 100644
index 0000000..157afca
--- /dev/null
+++ b/restdoc-helper-plugin/src/main/java/com/pconil/restdoc/Annotation2AsciiDocParser.java
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc;
+
+import com.pconil.restdoc.annotation.AsciidocAnnotation;
+import com.pconil.restdoc.annotation.InspectToDocument;
+import io.swagger.annotations.ApiModelProperty;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * This parser produces target/ClassName.restdoc files for all classes matching package-name or its subpackages.
+ *
+ * @author patrice_conil
+ */
+@InspectToDocument(description = "Parser used to generate restdoc files from swagger annotation")
+public class Annotation2AsciiDocParser extends AbstractParser {
+
+ /**
+ * Logger.
+ */
+ private static final Logger LOGGER = LoggerFactory.getLogger(Annotation2AsciiDocParser.class);
+
+
+ /**
+ * Annotation2AsciiDocParser Constructor.
+ *
+ * @param packageName name of package that we want to parse
+ * @param adocDirName project for which we want to generate restdoc and java files
+ * @param javaDirName project for which we want to generate restdoc and java files
+ * @param source directory where .class will be found
+ * @throws ParserException if packageName is malformed or target creation isn't possible
+ */
+ public Annotation2AsciiDocParser(String packageName, String adocDirName, String javaDirName,
+ String source) throws ParserException {
+ super(packageName, adocDirName, javaDirName, source);
+ }
+
+ @Override
+ protected boolean parseField(Class c, Field field, boolean hasDocumentation) {
+ boolean isDocumented = false;
+ if (field.isAnnotationPresent(AsciidocAnnotation.class) || field.isAnnotationPresent(ApiModelProperty.class)) {
+ try {
+ //Is it the first documented field of the class?
+ if (!hasDocumentation) {
+ isDocumented = true;
+ writeClassStart(c);
+ writeFieldStart(c);
+ }
+ // Iterates all the annotations available in the method
+ for (Annotation anno : field.getDeclaredAnnotations()) {
+ LOGGER.debug("\tAnnotation {} in class {} for field {} \n",
+ anno.annotationType().getSimpleName(),
+ c.getSimpleName(), field.getName());
+ if (anno instanceof AsciidocAnnotation) {
+ writeFieldInfo(field, (AsciidocAnnotation) anno, adocFile);
+ } else if (anno instanceof ApiModelProperty) {
+ writeFieldInfo(field, (ApiModelProperty) anno, adocFile);
+ }
+ }
+ } catch (Throwable ex) {
+ ex.printStackTrace();
+ }
+ }
+ return hasDocumentation || isDocumented;
+ }
+
+ @Override
+ protected void writeFieldStart(Class c) {
+
+ }
+
+ @Override
+ protected void writeClassStart(Class c) throws IOException {
+ adocFile = createClassFile(c.getSimpleName(), targetAdocDirName, ".adoc");
+ writeClassHeader(c, adocFile);
+ writeFieldHeader(adocFile);
+ }
+
+ @Override
+ protected void initializeFields() throws IOException {
+ }
+
+ @Override
+ protected FileOutputStream writeClass() {
+ return null;
+ }
+
+ @Override
+ protected void completeClass() throws IOException {
+
+ }
+
+ @Override
+ protected void completeFields() throws IOException {
+ writeFieldFooter(adocFile);
+ }
+
+ /**
+ * Write field footer to stream.
+ *
+ * @param stream file where to write
+ * @throws IOException If something goes wrong wile writing to file
+ */
+ private void writeFieldFooter(FileOutputStream stream) throws IOException {
+ byte[] content = Constant.FIELD_FOOTER.getBytes(StandardCharsets.UTF_8);
+ stream.write(content);
+ }
+
+ /**
+ * Writes Field informations contained in anno in restdoc file.
+ *
+ * @param field Field
+ * @param anno An AsciidocAnnotation
+ * @param stream file where to write
+ * @throws IOException If something goes wrong wile writing to file
+ */
+ private void writeFieldInfo(Field field, AsciidocAnnotation anno, FileOutputStream stream) throws IOException {
+ byte[] content = String.format(Constant.FIELD_FORMAT, field.getName(), field.getType().getSimpleName(),
+ anno.description(), anno.constraints())
+ .getBytes(StandardCharsets.UTF_8);
+ stream.write(content);
+ stream.flush();
+ }
+
+ /**
+ * Writes Field informations contained in anno in restdoc file.
+ *
+ * @param field Field
+ * @param anno An ApiModelProperty annotation
+ * @param stream file where to write
+ * @throws IOException If something goes wrong wile writing to file
+ */
+ private void writeFieldInfo(Field field, ApiModelProperty anno,
+ FileOutputStream stream) throws IOException {
+ byte[] content = String.format(Constant.FIELD_FORMAT, field.getName(), field.getType().getSimpleName(),
+ anno.value(), anno.required() ? "required" : "optional")
+ .getBytes(StandardCharsets.UTF_8);
+ stream.write(content);
+ stream.flush();
+ }
+
+ /**
+ * Writes Field header in restdoc file.
+ *
+ * @param stream file where to write
+ * @throws IOException If something goes wrong wile writing to file
+ */
+ private void writeFieldHeader(FileOutputStream stream) throws IOException {
+ byte[] content = Constant.FIELD_HEADER_FORMAT.getBytes(StandardCharsets.UTF_8);
+ stream.write(content);
+ stream.flush();
+ }
+
+ /**
+ * Writes Class header in restdoc file.
+ *
+ * @param c class targeted
+ * @param stream file where to write
+ * @throws IOException If something goes wrong wile writing to file
+ */
+ private void writeClassHeader(Class c, FileOutputStream stream) throws IOException {
+ byte[] content;
+ InspectToDocument anno = (InspectToDocument) c.getAnnotation(InspectToDocument.class);
+ // Write class header
+ if ("".equals(anno.description())) {
+
+ content = String.format(Constant.CLASS_HEADER_FORMAT, c.getSimpleName(), c.getSimpleName())
+ .getBytes(StandardCharsets.UTF_8);
+ } else {
+ content = String.format(Constant.CLASS_HEADER_WITH_DESCRIPTION_FORMAT, c.getSimpleName(), c.getSimpleName(),
+ anno.description()).getBytes(StandardCharsets.UTF_8);
+ }
+ stream.write(content);
+ stream.flush();
+ }
+}
diff --git a/restdoc-helper-plugin/src/main/java/com/pconil/restdoc/Annotation2JavaParser.java b/restdoc-helper-plugin/src/main/java/com/pconil/restdoc/Annotation2JavaParser.java
new file mode 100644
index 0000000..f985739
--- /dev/null
+++ b/restdoc-helper-plugin/src/main/java/com/pconil/restdoc/Annotation2JavaParser.java
@@ -0,0 +1,243 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc;
+
+import com.pconil.restdoc.annotation.AsciidocAnnotation;
+import io.swagger.annotations.ApiModelProperty;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.nio.charset.StandardCharsets;
+
+import static com.pconil.restdoc.Constant.IN_LIST;
+import static com.pconil.restdoc.Constant.OUT_OF_LIST;
+
+
+/**
+ * This parser produces target/ClassName.restdoc files for all classes matching package-name or its subpackages.
+ *
+ * @author patrice_conil
+ */
+public class Annotation2JavaParser extends AbstractParser {
+
+ /**
+ * Logger.
+ */
+ private static final Logger LOGGER = LoggerFactory.getLogger(Annotation2JavaParser.class);
+
+ /**
+ * Used to construct field information.
+ */
+ private StringBuilder sbField = null;
+
+ /**
+ * Used to construct field information for list retrieval.
+ */
+ private StringBuilder sbListField = null;
+
+ /**
+ * Classloader.
+ */
+ private ParserClassLoader classLoader = null;
+
+ /**
+ * File where we write java code.
+ */
+ private FileOutputStream classFile = null;
+
+ /**
+ * Annotation2JavaParser Constructor.
+ *
+ * @param packageName name of package that we want to parse
+ * @param adocDirName project for which we want to generate restdoc and java files
+ * @param javaDirName project for which we want to generate restdoc and java files
+ * @param source directory where .class will be found
+ * @throws ParserException if packageName is malformed or target creation isn't possible
+ */
+ public Annotation2JavaParser(String packageName, String adocDirName, String javaDirName, String source) throws ParserException {
+ super(packageName, adocDirName, javaDirName, source);
+ }
+
+ @Override
+ protected boolean parseField(Class c, Field field, boolean hasDocumentation) {
+ boolean isDocumented = false;
+ boolean firstField = false;
+ if (field.isAnnotationPresent(ApiModelProperty.class) || field.isAnnotationPresent(AsciidocAnnotation.class)) {
+ try {
+ //Is it the first documented field of the class?
+ if (!hasDocumentation) {
+ firstField = true;
+ isDocumented = true;
+ writeClassStart(c);
+ sbField.append(String.format(Constant.FIELD_START, c.getSimpleName()));
+ sbListField.append(String.format(Constant.FIELD_LIST_START, c.getSimpleName()));
+ }
+ // Iterates all the annotations available in the method
+ for (Annotation anno : field.getDeclaredAnnotations()) {
+ LOGGER.debug("\tAnnotation {} in class {} for field {} \n",
+ anno.annotationType().getSimpleName(),
+ c.getSimpleName(), field.getName());
+ if (anno instanceof ApiModelProperty) {
+ sbField.append(writeFieldInfo(field, (ApiModelProperty) anno, firstField, OUT_OF_LIST));
+ sbListField.append(writeFieldInfo(field, (ApiModelProperty) anno, firstField, IN_LIST));
+ firstField = false;
+ } else if (anno instanceof AsciidocAnnotation) {
+ sbField.append(writeFieldInfo(field, (AsciidocAnnotation) anno, firstField, OUT_OF_LIST));
+ sbListField.append(writeFieldInfo(field, (AsciidocAnnotation) anno, firstField, IN_LIST));
+ firstField = false;
+ }
+ }
+ } catch (Throwable ex) {
+ ex.printStackTrace();
+ }
+ }
+ return hasDocumentation || isDocumented;
+ }
+
+ @Override
+ protected void writeFieldStart(Class c) {
+
+ }
+
+ @Override
+ protected void writeClassStart(Class c) throws IOException {
+ byte[] content = String.format(Constant.CLASS_HEADER, packageName, c.getSimpleName()).getBytes(StandardCharsets.UTF_8);
+ classFile = createClassFile(c.getSimpleName(), targetJavaDirName, "FieldDescriptor.java");
+ classFile.write(content);
+ classFile.flush();
+ }
+
+ @Override
+ protected void initializeFields() {
+ sbField = new StringBuilder("");
+ sbListField = new StringBuilder("");
+ }
+
+ @Override
+ protected FileOutputStream writeClass() {
+ return null;
+ }
+
+ @Override
+ protected void completeClass() throws IOException {
+ writeClassEnd(classFile);
+ }
+
+ @Override
+ protected void completeFields() throws IOException {
+ sbField.append(Constant.FIELD_END);
+ sbListField.append(Constant.FIELD_END);
+ writeFieldDescriptors(classFile, sbField.toString(), sbListField.toString());
+ }
+
+
+ /**
+ * Write class footer to stream.
+ *
+ * @param stream file where to write
+ * @throws IOException If something goes wrong wile writing to file
+ */
+ private void writeClassEnd(FileOutputStream stream) throws IOException {
+ byte[] content = Constant.CLASS_END.getBytes(StandardCharsets.UTF_8);
+ stream.write(content);
+ }
+
+ /**
+ * Write field footer to stream.
+ *
+ * @param stream file where to write
+ * @param fd the string that describe fields
+ * @param fdList the string that describe fields for list retrieval
+ * @throws IOException If something goes wrong wile writing to file
+ */
+ private void writeFieldDescriptors(FileOutputStream stream, String fd, String fdList) throws IOException {
+ byte[] content = (fd + "\n\n" + fdList + "\n\n").getBytes(StandardCharsets.UTF_8);
+ stream.write(content);
+ }
+
+ /**
+ * Construct field info.
+ *
+ * @param field Field
+ * @param anno An ApiModelProperty annotation
+ * @param first is it the first documented field of the class ?
+ * @param list are we writing for list retrieval ?
+ * @throws IOException If something goes wrong wile writing to file
+ * @return the string completed with field info
+ */
+ private String writeFieldInfo(Field field, ApiModelProperty anno, boolean first, boolean list) throws IOException {
+ String fieldName;
+ String content = "";
+
+ // We prefix the name with "[]." in case of list to comply with restdoc behavior.
+ if (list) {
+ fieldName = "[]." + field.getName();
+ } else {
+ fieldName = field.getName();
+ }
+
+ // If it is not the first field we need to write separator.
+ if (!first) {
+ content += Constant.FIELD_SEPARATOR;
+ }
+ // This is a required field
+ if (((ApiModelProperty) anno).required()) {
+ content += String.format(Constant.REQUIRED_FIELD_FORMAT, fieldName, ((ApiModelProperty) anno).value());
+ } else {
+ content += String.format(Constant.OPTIONAL_FIELD_FORMAT, fieldName, ((ApiModelProperty) anno).value());
+ }
+ return content;
+ }
+
+ /**
+ * Construct field info.
+ *
+ * @param field Field
+ * @param anno An AsciidocAnnotation annotation
+ * @param first is it the first documented field of the class ?
+ * @param list are we writing for list retrieval ?
+ * @throws IOException If something goes wrong wile writing to file
+ * @return the string completed with field info
+ */
+ private String writeFieldInfo(Field field, AsciidocAnnotation anno, boolean first, boolean list) throws IOException {
+ String fieldName;
+ String content = "";
+
+ // We prefix the name with "[]." in case of list to comply with restdoc behavior.
+ if (list) {
+ fieldName = "[]." + field.getName();
+ } else {
+ fieldName = field.getName();
+ }
+
+ // If it is not the first field we need to write separator.
+ if (!first) {
+ content += Constant.FIELD_SEPARATOR;
+ }
+ // This is a required field
+ if ( anno.required() ) {
+ content += String.format(Constant.REQUIRED_FIELD_FORMAT, fieldName, anno.description());
+ } else {
+ content += String.format(Constant.OPTIONAL_FIELD_FORMAT, fieldName, anno.description());
+ }
+ return content;
+ }
+
+}
diff --git a/restdoc-helper-plugin/src/main/java/com/pconil/restdoc/Constant.java b/restdoc-helper-plugin/src/main/java/com/pconil/restdoc/Constant.java
new file mode 100644
index 0000000..a3be625
--- /dev/null
+++ b/restdoc-helper-plugin/src/main/java/com/pconil/restdoc/Constant.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc;
+
+/**
+ * All constants.
+ *
+ * @author patrice_conil
+ */
+public class Constant {
+
+ // Constant to generate AsciiDoc generation.
+
+ /**
+ * Format of line that must be generated as class-CLASS_HEADER_FORMAT header.
+ */
+ static final String CLASS_HEADER_FORMAT = "[%s]\n= %s\n|===\n";
+
+ /**
+ * Format of line that must be generated as class-CLASS_HEADER_FORMAT header.
+ */
+ static final String CLASS_HEADER_WITH_DESCRIPTION_FORMAT = "[%s]\n= %s\n%s\n|===\n";
+
+ /**
+ * Format of line that must be generated as field CLASS_HEADER_FORMAT.
+ */
+ static final String FIELD_FORMAT = "\n| %s\n| %s\n| %s\n| %s\n";
+
+ /**
+ * Format of line that must be generated as field-header CLASS_HEADER_FORMAT.
+ */
+ static final String FIELD_HEADER_FORMAT = "| Field| Type| Description| Constraints\n";
+
+ /**
+ * Line to add to terminate table of field properly.
+ */
+ static final String FIELD_FOOTER = "|===\n\n";
+
+ /**
+ * Constants that describe if a field is in a list or not.
+ */
+ static final boolean IN_LIST = true;
+ static final boolean OUT_OF_LIST = false;
+
+
+
+ // Java classes generation.
+ /**
+ * Class start.
+ */
+ static final String CLASS_HEADER =
+ "package %s;\n\n"
+ + "import org.springframework.restdocs.payload.FieldDescriptor; \n"
+ + "import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;\n\n"
+ + "public class %sFieldDescriptor {\n";
+
+ /**
+ * Class end.
+ */
+ static final String CLASS_END = "}\n";
+
+ /**
+ * Field start.
+ */
+ static final String FIELD_LIST_START = " public static FieldDescriptor[] fd%sList = new FieldDescriptor[] {\n";
+
+ /**
+ * Field list start.
+ */
+ static final String FIELD_START = " public static FieldDescriptor[] fd%s = new FieldDescriptor[] {\n";
+
+ /**
+ * Format for a required field.
+ */
+ static final String REQUIRED_FIELD_FORMAT = " fieldWithPath(\"%s\").description(\"%s\")";
+
+ /**
+ * Format for an optional field.
+ */
+ static final String OPTIONAL_FIELD_FORMAT = " fieldWithPath(\"%s\").description(\"%s\").optional()";
+
+ /**
+ * Field end.
+ */
+ static final String FIELD_END = " };\n";
+
+ /**
+ * Field separator.
+ */
+ static final String FIELD_SEPARATOR = ",\n";
+
+}
diff --git a/restdoc-helper-plugin/src/main/java/com/pconil/restdoc/ParserClassLoader.java b/restdoc-helper-plugin/src/main/java/com/pconil/restdoc/ParserClassLoader.java
new file mode 100644
index 0000000..14deaa4
--- /dev/null
+++ b/restdoc-helper-plugin/src/main/java/com/pconil/restdoc/ParserClassLoader.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+/**
+ * This class is used to include directory in classpath and to load java .class.
+ *
+ * @author patrice_conil
+ */
+class ParserClassLoader {
+
+ /**
+ * Logger.
+ */
+ private static final Logger LOGGER = LoggerFactory.getLogger(ParserClassLoader.class);
+
+ /**
+ * The previous Classloader that must be used to restore context.
+ */
+ private ClassLoader previous = null;
+
+ /**
+ * The current one used to load and parse classes.
+ */
+ private ClassLoader current = null;
+
+ /**
+ * Constructor that adds the URL sourceDir to the classpath.
+ * @param sourceDir where to find classes
+ * @throws MalformedURLException if something goes wrong with URL construction.
+ */
+ ParserClassLoader(File sourceDir) throws MalformedURLException {
+ previous = Thread.currentThread().getContextClassLoader();
+ current = changeLoader(new URL[]{new URL("file:" + sourceDir.getAbsolutePath() + "/")});
+ }
+
+ /**
+ * Adds urls to classLoader.
+ *
+ * @param urls urls to add to classpath
+ * @return updated ClassLoader
+ * @throws MalformedURLException if one of urls is bad formatted
+ */
+ private ClassLoader changeLoader(URL[] urls) throws MalformedURLException {
+
+ // Create class loader using given codebase
+ // Use prevCl as parent to maintain current visibility
+ ClassLoader newCl = URLClassLoader.newInstance(urls, previous);
+
+ // Sets class loader
+ Thread.currentThread().setContextClassLoader(newCl);
+
+ return newCl;
+ }
+
+
+ /**
+ * Restores previous classLoader.
+ * @throws MalformedURLException if something goes wrong while restoring classloader
+ */
+ void restoreLoader() throws MalformedURLException {
+
+ ClassLoader tmp = current;
+ Thread.currentThread().setContextClassLoader(previous);
+ current = previous;
+ previous = tmp;
+
+ }
+
+ /**
+ * Scans all classes accessible from the context class loader which belong to the given package and subpackages.
+ *
+ * @param packageName The base package
+ * @param dir the directory where to find classes
+ * @return The classes
+ * @throws ClassNotFoundException if no class are found
+ * @throws IOException if an IO error occur
+ */
+ List getClasses(String dir, String packageName)
+ throws ClassNotFoundException, IOException {
+
+ ArrayList classes = new ArrayList<>();
+ try {
+ String path = packageName.replace('.', '/');
+ Enumeration resources = current.getResources(path);
+ List dirs = new ArrayList<>();
+ while (resources.hasMoreElements()) {
+ URL resource = resources.nextElement();
+ dirs.add(new File(resource.getFile()));
+ }
+
+ for (File dire : dirs) {
+ classes.addAll(findClasses(current, dire, packageName));
+ }
+
+ for (Class clazz : classes) {
+ LOGGER.debug("Found class {}", clazz.getSimpleName());
+ }
+ } catch (IOException cnfe) {
+ cnfe.printStackTrace();
+ }
+ return classes;
+ }
+
+
+ /**
+ * Recursive method used to find all classes in a given directory and subdirs.
+ *
+ * @param cl The ClassLoader to load class into
+ * @param directory The base directory
+ * @param packageName The package name for classes found inside the base directory
+ * @return The classes
+ * @throws ClassNotFoundException if no class are found
+ */
+ private List findClasses(ClassLoader cl, File directory,
+ String packageName) throws ClassNotFoundException {
+ List classes = new ArrayList<>();
+ String classSuffix = ".class";
+ if (!directory.exists()) {
+ return classes;
+ }
+ File[] files = directory.listFiles();
+ assert files != null;
+ for (File file : files) {
+ if (file.isDirectory()) {
+ assert !file.getName().contains(".");
+ if ("".equals(packageName)) {
+ classes.addAll(findClasses(cl, file, file.getName()));
+ } else {
+ classes.addAll(findClasses(cl, file, packageName + "." + file.getName()));
+ }
+ } else if (file.getName().endsWith(classSuffix)) {
+ String className = packageName + '.' + file.getName().substring(0, file.getName().length()
+ - classSuffix.length());
+ try {
+ classes.add(cl.loadClass(className));
+ } catch (ClassNotFoundException | NoClassDefFoundError e) {
+ LOGGER.error("Unable to load classes");
+ }
+ }
+ }
+ return classes;
+ }
+
+}
diff --git a/restdoc-helper-plugin/src/main/java/com/pconil/restdoc/ParserException.java b/restdoc-helper-plugin/src/main/java/com/pconil/restdoc/ParserException.java
new file mode 100644
index 0000000..3cc53b2
--- /dev/null
+++ b/restdoc-helper-plugin/src/main/java/com/pconil/restdoc/ParserException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc;
+
+/**
+ * Exception that indicates an error while creating the parser.
+ *
+ * @author patrice_conil
+ */
+public class ParserException extends RuntimeException {
+
+ public String getMessage() {
+ return message;
+ }
+
+ /**
+ * Explanation of error.
+ */
+ private String message = null;
+
+ /**
+ * Constructor.
+ *
+ * @param message the messages that explains the error.
+ */
+ public ParserException(String message) {
+ this.message = message;
+ }
+}
diff --git a/restdoc-helper-plugin/src/main/java/com/pconil/restdoc/RestdocMojo.java b/restdoc-helper-plugin/src/main/java/com/pconil/restdoc/RestdocMojo.java
new file mode 100644
index 0000000..6d81ac0
--- /dev/null
+++ b/restdoc-helper-plugin/src/main/java/com/pconil/restdoc/RestdocMojo.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc;
+
+import org.apache.maven.plugin.AbstractMojo;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+
+/**
+ * Generate restdoc files and java fieldDescriptor classes.
+ *
+ * @author patrice_conil
+ */
+@Mojo(name = "restdoc-helper")
+public class RestdocMojo extends AbstractMojo {
+
+ /**
+ * Where we want to start searching sources.
+ */
+ @Parameter
+ private String basePackageName;
+
+ /**
+ * Where to find sources (including subdirs).
+ */
+ @Parameter(defaultValue = "target/classes/")
+ private String sourceDir;
+
+ /**
+ * Where to write adoc snippets.
+ */
+ @Parameter(defaultValue = "target/generated-snippets")
+ private String adocDir;
+
+ /**
+ * Where to write java sources.
+ */
+ @Parameter(defaultValue = "target/generated-test-sources")
+ private String javaDir;
+
+
+ /**
+ * Launches the parsers to generate restdoc and java files.
+ * @throws MojoExecutionException if something goes wrong
+ */
+ public void execute() throws MojoExecutionException {
+
+ getLog().info("restdoc-helper starts with basePackageName=" + basePackageName + ", sourceDir=" + sourceDir
+ + ", adocDir=" + adocDir + ", javaDir=" + javaDir + "\n");
+ Annotation2JavaParser parser = new Annotation2JavaParser(basePackageName, adocDir, javaDir, sourceDir);
+ parser.parse();
+
+ Annotation2AsciiDocParser annotation2AsciiDocParser = new Annotation2AsciiDocParser(basePackageName, adocDir, javaDir, sourceDir);
+ annotation2AsciiDocParser.parse();
+ getLog().info("restdoc-helper ends.");
+ }
+
+}
+
+
diff --git a/restdoc-helper-plugin/src/site/LICENSE-2.0.txt b/restdoc-helper-plugin/src/site/LICENSE-2.0.txt
new file mode 100644
index 0000000..d645695
--- /dev/null
+++ b/restdoc-helper-plugin/src/site/LICENSE-2.0.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ 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.
diff --git a/restdoc-helper-plugin/src/site/markdown/README.md b/restdoc-helper-plugin/src/site/markdown/README.md
new file mode 100644
index 0000000..1db72c8
--- /dev/null
+++ b/restdoc-helper-plugin/src/site/markdown/README.md
@@ -0,0 +1,140 @@
+
+# Restdoc Helper
+
+## Overview
+
+The main goal of this project is to simplify the documentation process of your RESTFul services by combining hand-written using [Asciidoctor][3] docs, [Sprinfox static docs][1] generated docs or [Spring REST Docs][10] generated docs.
+Restdoc helper is a Maven plugin that generates [Asciidoctor][3] files and [FieldDescriptors][7] from [Swagger 2][6] annotations
+
+The purpose of [Spring REST Docs][10] is to document your API with auto-generated snippets produced with [Spring MVC Test][4].
+To achieve that, it's necessary to document all fields of data classes involved in your call-flows. If as me you use [Swagger 2][6] to describe your API contracts, the job is almost done.
+What'is missing is the translation of your swagger annotations to a [FieldDescriptors][7]. This is the purpose of this contribution.
+It's also possible to generate asciidoctor files only that describe your model from custom annotation.
+
+## Doc generation
+
+### Add dependencies to your pom
+
+```
+
+ com.pconil.restdoc
+ restdoc-annotation
+ 1.0-SNAPSHOT
+ `
+
+ ...
+
+
+ com.pconil.restdoc
+ restdoc-helper-plugin
+ 1.0-SNAPSHOT
+
+ ${basedir}/target/classes/
+ com.pconil.restdoc.model
+ ${basedir}/target/generated-test-sources
+ ${basedir}/target/generated-snippets
+
+
+
+ generate
+ generate-test-sources
+
+ restdoc-helper
+
+
+
+
+
+ org.springframework
+ spring-web
+ 4.3.3.RELEASE
+
+
+
+ ...
+
+```
+
+### Howto Generate adoc files from swagger annotations
+
+As simple as adding @InspectToDocument to your model class
+
+```
+@InspectToDocument(description = "This is the Class1 description")
+public class ClassSwaggerDTO {
+ @ApiModelProperty(required = true, value="This is the field field1")
+ private String field1;
+
+ @ApiModelProperty(value="This is the optional field field2")
+ private String field2 = null;
+
+ @ApiModelProperty(required = true, value="This is the field field3")
+ private String field3 = null;
+
+ public ClassSwaggerDTO(String field1, String field2) {
+ this.field1 = field1;
+ this.field2 = field2;
+ }
+}
+```
+
+
+### And If you don't use swagger or want to document classes that doesn't contain @ApiModelProperty
+
+Add a @AsciidocAnnotation to your fields
+
+```
+@InspectToDocument(description = "This is the Class1 description")
+public class Class1DTO {
+ @AsciidocAnnotation(description="This field describe name of Class1DTO", constraints = "Length must be between 4 and 6")
+ String field1;
+
+ @AsciidocAnnotation(description="field 2")
+ String field2 = null;
+
+ public Class1DTO(String field1, String field2) {
+ this.field1 = field1;
+ this.field2 = field2;
+ }
+}
+```
+
+## Go further
+
+See the springboot-sample to see how it works.
+
+## Prerequisite
+
+Java 8, maven 3
+
+## Building from source
+
+You will need Java 8 or later to build restdoc-helper. It is built using [maven][2]:
+
+```
+mvn clean install
+```
+
+## Contributing
+
+There are several ways to contribute to restdoc-helper:
+
+ - Open a [pull request][12].
+ - Ask and answer questions on Stack Overflow using the [`restdoc-helper`][15] tag.
+
+## Licence
+
+Restdoc helper is open source software released under the [Apache 2.0 license][14].
+
+
+[1]: https://springfox.github.io/springfox/docs/snapshot/#configuring-springfox-staticdocs
+[2]: https://maven.apache.org/download.cgi
+[3]: http://asciidoctor.org
+[4]: http://docs.spring.io/spring-framework/docs/4.1.x/spring-framework-reference/htmlsingle/#spring-mvc-test-framework
+[5]: https://developer.github.com/v3/
+[6]: http://swagger.io/specification/
+[7]: http://docs.spring.io/spring-restdocs/docs/current/reference/html5/#documenting-your-api-request-response-payloads-reusing-field-descriptors
+[10]: http://docs.spring.io/spring-restdocs/docs/current/reference/html5/
+[12]: https://help.github.com/articles/using-pull-requests/
+[14]: http://www.apache.org/licenses/LICENSE-2.0.html
+[15]: http://stackoverflow.com/tags/restdoc-helper
diff --git a/restdoc-helper-plugin/src/site/site.xml b/restdoc-helper-plugin/src/site/site.xml
new file mode 100644
index 0000000..f25edbc
--- /dev/null
+++ b/restdoc-helper-plugin/src/site/site.xml
@@ -0,0 +1,27 @@
+
+
+
+ restdoc-helper-plugin
+ http://maven.apache.org/images/apache-maven-project.png
+ https://github.com/patrice-conil/restdoc-helper
+
+
+ http://maven.apache.org/images/maven-small.gif
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/restdoc-helper-plugin/src/test/java/com/pconil/restdoc/Annotation2JavaParserTest.java b/restdoc-helper-plugin/src/test/java/com/pconil/restdoc/Annotation2JavaParserTest.java
new file mode 100644
index 0000000..57849b3
--- /dev/null
+++ b/restdoc-helper-plugin/src/test/java/com/pconil/restdoc/Annotation2JavaParserTest.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc;
+
+import org.junit.Test;
+
+import java.io.File;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Test java generation from swagger ApiModelProperty.
+ *
+ * @author patrice_conil
+ */
+public class Annotation2JavaParserTest {
+ public static final String PACKAGE = "com.pconil.restdoc";
+ public static final String ADOC_DIR = "target/generated-snippets";
+ public static final String JAVA_DIR = "target/generated-test-sources";
+ /**
+ * Generates java sources for classes in the com.pconil.restdoc.model package and checks the result.
+ * @throws Exception if the result isn't the expected one
+ */
+ @Test
+ public void generateJavaFromAnnotation() throws Exception {
+ Annotation2JavaParser parser = new Annotation2JavaParser(PACKAGE, ADOC_DIR, JAVA_DIR, "target/classes");
+ parser.parse();
+
+ //Verify generation from ApiModelProperty annotation
+ String filename = "./target/generated-test-sources/ClassSwaggerDTOFieldDescriptor.java";
+ File java = new File(filename);
+ File expected = new File("./src/test/resources/ClassSwaggerDTOFieldDescriptor.java.expected");
+ assertThat(java).exists();
+ assertThat(java).hasSameContentAs(expected);
+
+ //Verify generation from AsciidocAnnotation annotation
+ filename = "./target/generated-test-sources/ClassWithoutApiModelPropertyFieldDescriptor.java";
+ java = new File(filename);
+ expected = new File("./src/test/resources/ClassWithoutApiModelPropertyFieldDescriptor.java.expected");
+ assertThat(java).exists();
+ assertThat(java).hasSameContentAs(expected);
+ }
+}
\ No newline at end of file
diff --git a/restdoc-helper-plugin/src/test/java/com/pconil/restdoc/ParserTest.java b/restdoc-helper-plugin/src/test/java/com/pconil/restdoc/ParserTest.java
new file mode 100644
index 0000000..74b4404
--- /dev/null
+++ b/restdoc-helper-plugin/src/test/java/com/pconil/restdoc/ParserTest.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc;
+
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.attribute.PosixFilePermission;
+import java.nio.file.attribute.PosixFilePermissions;
+import java.util.EnumSet;
+import java.util.Set;
+
+import static java.nio.file.attribute.PosixFilePermission.OWNER_READ;
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Tests Annotation2AsciiDocParser.
+ *
+ * @author patrice_conil
+ */
+public class ParserTest {
+
+ public static final String PACKAGE = "com.pconil.restdoc";
+ public static final String ADOC_DIR = "target/generated-snippets";
+ public static final String JAVA_DIR = "target/generated-test-sources";
+
+ /**
+ * Parses java class that contains only description on its fields.
+ *
+ * @throws Exception if something goes wrong
+ */
+ @Test
+ public void parseClassWithDescription() throws Exception {
+ Annotation2AsciiDocParser annotation2AsciiDocParser = new Annotation2AsciiDocParser(PACKAGE, ADOC_DIR, JAVA_DIR,
+ "target/classes");
+ annotation2AsciiDocParser.parse();
+ String filename = "target/generated-snippets/Class1DTO.adoc";
+ File adhoc = new File(filename);
+ assertThat(adhoc).exists();
+ File expected = new File("./src/test/resources/Class1DTOExpected.adoc");
+ assertThat(adhoc).hasSameContentAs(expected);
+ }
+
+ /**
+ * Parses java class that contains description and constraint on its fields.
+ *
+ * @throws Exception if something goes wrong
+ */
+ @Test
+ public void parseClassWithDescriptionAndConstraints() throws Exception {
+ Annotation2AsciiDocParser annotation2AsciiDocParser = new Annotation2AsciiDocParser(PACKAGE, ADOC_DIR, JAVA_DIR,
+ "target/classes");
+ annotation2AsciiDocParser.parse();
+ String filename = "target/generated-snippets/SimilarContent.adoc";
+ File adhoc = new File(filename);
+ assertThat(adhoc).exists();
+ File expected = new File("./src/test/resources/SimilarContent.adoc");
+ assertThat(adhoc).hasSameContentAs(expected);
+ }
+
+ /**
+ * Parses java class that contains no description.
+ *
+ * @throws Exception if something goes wrong
+ */
+ @Test
+ public void parseClassWithoutDescription() throws Exception {
+ Annotation2AsciiDocParser annotation2AsciiDocParser = new Annotation2AsciiDocParser(PACKAGE, ADOC_DIR, JAVA_DIR,
+ "target/classes");
+ annotation2AsciiDocParser.parse();
+ String filename = "target/generated-snippets/Class2DTO.adoc";
+ File adhoc = new File(filename);
+ assertThat(adhoc).exists();
+ File expected = new File("./src/test/resources/Class2DTOExpected.adoc");
+ assertThat(adhoc).hasSameContentAs(expected);
+ }
+
+ /**
+ * Parses java class that contains swagger description.
+ *
+ * @throws Exception if something goes wrong
+ */
+ @Test
+ public void parseClassWithSwaggerAnnotationOnly() throws Exception {
+ Annotation2AsciiDocParser annotation2AsciiDocParser = new Annotation2AsciiDocParser(PACKAGE, ADOC_DIR, JAVA_DIR,
+ "target/classes");
+ annotation2AsciiDocParser.parse();
+ String filename = "target/generated-snippets/ClassSwaggerDTO.adoc";
+ File adhoc = new File(filename);
+ assertThat(adhoc).exists();
+ File expected = new File("./src/test/resources/ClassSwaggerDTOExpected.adoc");
+ assertThat(adhoc).hasSameContentAs(expected);
+ }
+
+ @Test(expected = ParserException.class)
+ public void badPackageName() throws Exception {
+ new Annotation2AsciiDocParser("com.pconil/restdoc", ADOC_DIR, JAVA_DIR, "target/classes");
+ }
+
+
+ @Test(expected = ParserException.class)
+ public void unwritableTargetName() throws Exception {
+ createTmpDirWithoutWriteRight("target/unwritableDir");
+ new Annotation2AsciiDocParser(PACKAGE, "target/unwritableDir/test", "target/unwritableDir/test", "target/classes");
+ }
+
+ /**
+ * Creates tmp dir that doesn't permit write access.
+ *
+ * @param name
+ */
+ private void createTmpDirWithoutWriteRight(String name) throws IOException {
+ Path path = Paths.get(name);
+ Set perms =
+ EnumSet.of(OWNER_READ);
+ Files.deleteIfExists(path);
+ Files.createFile(path, PosixFilePermissions.asFileAttribute(perms));
+ }
+}
\ No newline at end of file
diff --git a/restdoc-helper-plugin/src/test/java/com/pconil/restdoc/RestdocMojoTest.java b/restdoc-helper-plugin/src/test/java/com/pconil/restdoc/RestdocMojoTest.java
new file mode 100644
index 0000000..33f9509
--- /dev/null
+++ b/restdoc-helper-plugin/src/test/java/com/pconil/restdoc/RestdocMojoTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc;
+
+import org.apache.maven.plugin.testing.MojoRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+import java.io.File;
+
+import static org.codehaus.plexus.PlexusTestCase.getTestFile;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Mojo execution test.
+ *
+ * @author patrice_conil
+ */
+@RunWith(JUnit4.class)
+public class RestdocMojoTest {
+
+ @Rule
+ public MojoRule rule = new MojoRule() {
+ @Override
+ protected void before() throws Throwable {
+ }
+
+ @Override
+ protected void after() {
+ }
+ };
+
+ @Test
+ public void execute() throws Exception {
+ File pom = getTestFile("src/test/resources/pom.xml");
+ assertNotNull(pom);
+ assertTrue(pom.exists());
+ RestdocMojo myMojo = (RestdocMojo) rule.lookupMojo("restdoc-helper", pom);
+ assertNotNull(myMojo);
+ myMojo.execute();
+ }
+
+}
\ No newline at end of file
diff --git a/restdoc-helper-plugin/src/test/java/com/pconil/restdoc/Utils.java b/restdoc-helper-plugin/src/test/java/com/pconil/restdoc/Utils.java
new file mode 100644
index 0000000..2374bc6
--- /dev/null
+++ b/restdoc-helper-plugin/src/test/java/com/pconil/restdoc/Utils.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+
+/**
+ * Utilities.
+ *
+ * @author patrice_conil
+ */
+public class Utils {
+
+ /**
+ * Logger.
+ */
+ private static final Logger LOGGER = LoggerFactory.getLogger(Utils.class);
+
+ /**
+ * Load a resource as an UTF-8 string.
+ *
+ * @param name name of the resource
+ * @return the content of the resource
+ */
+ public static String loadResource(String name) {
+ try {
+ URL url = Utils.class.getClassLoader().getResource(name);
+ if (url == null) {
+ return "";
+ }
+ return new String(Files.readAllBytes(Paths.get(url.toURI())), "UTF-8");
+ } catch (Exception e) {
+ return "";
+ }
+ }
+
+
+}
diff --git a/restdoc-helper-plugin/src/test/java/com/pconil/restdoc/model/Class1DTO.java b/restdoc-helper-plugin/src/test/java/com/pconil/restdoc/model/Class1DTO.java
new file mode 100644
index 0000000..b83da82
--- /dev/null
+++ b/restdoc-helper-plugin/src/test/java/com/pconil/restdoc/model/Class1DTO.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc.model;
+
+import com.pconil.restdoc.annotation.AsciidocAnnotation;
+import com.pconil.restdoc.annotation.InspectToDocument;
+
+/**
+ * Annotations to generate restdoc file from pojo classes.
+ *
+ * @author patrice_conil
+ */
+@InspectToDocument(description = "This is the Class1 description")
+public class Class1DTO {
+ @AsciidocAnnotation(description="This field describe name of Class1DTO", constraints = "Length must be between 4 and 6")
+ String field1;
+
+ @AsciidocAnnotation(description="field 2")
+ String field2 = null;
+
+ public Class1DTO(String field1, String field2) {
+ this.field1 = field1;
+ this.field2 = field2;
+ }
+}
diff --git a/restdoc-helper-plugin/src/test/java/com/pconil/restdoc/model/Class2DTO.java b/restdoc-helper-plugin/src/test/java/com/pconil/restdoc/model/Class2DTO.java
new file mode 100644
index 0000000..a2b22ad
--- /dev/null
+++ b/restdoc-helper-plugin/src/test/java/com/pconil/restdoc/model/Class2DTO.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc.model;
+
+import com.pconil.restdoc.annotation.AsciidocAnnotation;
+import com.pconil.restdoc.annotation.InspectToDocument;
+
+/**
+ * Test class.
+ *
+ * @author patrice_conil
+ */
+@InspectToDocument
+public class Class2DTO {
+ @AsciidocAnnotation(description="field 1")
+ private String field1;
+
+ @AsciidocAnnotation(description="field 2")
+ private String field2 = null;
+
+ public Class2DTO(String field1, String field2) {
+ this.field1 = field1;
+ this.field2 = field2;
+ }
+}
diff --git a/restdoc-helper-plugin/src/test/java/com/pconil/restdoc/model/ClassSwaggerDTO.java b/restdoc-helper-plugin/src/test/java/com/pconil/restdoc/model/ClassSwaggerDTO.java
new file mode 100644
index 0000000..6488444
--- /dev/null
+++ b/restdoc-helper-plugin/src/test/java/com/pconil/restdoc/model/ClassSwaggerDTO.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc.model;
+
+import com.pconil.restdoc.annotation.InspectToDocument;
+import io.swagger.annotations.ApiModelProperty;
+
+/**
+ * Annotations to generate restdoc file from pojo classes.
+ *
+ * @author patrice_conil
+ */
+@InspectToDocument(description = "This is the Class1 description")
+public class ClassSwaggerDTO {
+ @ApiModelProperty(required = true, value="This is the field field1")
+ private String field1;
+
+ @ApiModelProperty(value="This is the optional field field2")
+ private String field2 = null;
+
+ @ApiModelProperty(required = true, value="This is the field field3")
+ private String field3 = null;
+
+ public ClassSwaggerDTO(String field1, String field2) {
+ this.field1 = field1;
+ this.field2 = field2;
+ }
+}
diff --git a/restdoc-helper-plugin/src/test/java/com/pconil/restdoc/model/ClassWithoutApiModelProperty.java b/restdoc-helper-plugin/src/test/java/com/pconil/restdoc/model/ClassWithoutApiModelProperty.java
new file mode 100755
index 0000000..91a61d6
--- /dev/null
+++ b/restdoc-helper-plugin/src/test/java/com/pconil/restdoc/model/ClassWithoutApiModelProperty.java
@@ -0,0 +1,412 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.pconil.restdoc.annotation.AsciidocAnnotation;
+import com.pconil.restdoc.annotation.InspectToDocument;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Object that 'summarize' a Program.
+ *
+ * @author patrice_conil
+ */
+@InspectToDocument(description = "Electronic Program Guide Item")
+public class ClassWithoutApiModelProperty {
+
+ /**
+ * Program identifier.
+ */
+ @AsciidocAnnotation(description = "Program identifier", required = true)
+ private String id = null;
+
+ /**
+ * Program type.
+ */
+ @AsciidocAnnotation(description = "Program type", required = true)
+ private String programType = null;
+
+ /**
+ * Program title.
+ */
+ @AsciidocAnnotation(description = "Program title", required = true)
+ private String title = null;
+
+ /**
+ * Id of channel broadcasting this program.
+ */
+ @AsciidocAnnotation(description = "Id of channel broadcasting this program", required = true)
+ private String channelId = null;
+
+ /**
+ * Diffusion date in seconds.
+ */
+ @AsciidocAnnotation(description = "Diffusion date in seconds", required = true)
+ private Long diffusionDate = null;
+
+ /**
+ * Program duration in second.
+ */
+ @AsciidocAnnotation(description = "Program duration in second", required = true)
+ private Long duration = null;
+
+ /**
+ * Program CSA level.
+ */
+ @AsciidocAnnotation(description = "Program CSA level", required = true)
+ private Long csa = null;
+
+ /**
+ * Kind of program (ex: variety).
+ */
+ @AsciidocAnnotation(description = "Kind of program (ex: comedy)", required = true)
+ private String kind = null;
+
+ /**
+ * More detailed kind (e.g Comedy-drama).
+ */
+ @AsciidocAnnotation(description = "More detailed kind (e.g Comedy-drama)", required = true)
+ private String kindDetailed = null;
+
+ /**
+ * Program sysnopsis.
+ */
+ @AsciidocAnnotation(description = "Program sysnopsis", required = true)
+ private String synopsis = null;
+
+ /**
+ * Program language.
+ */
+ @AsciidocAnnotation(description = "Program language", required = true)
+ private String languageVersion = null;
+
+ /**
+ * Indicates if the program has some hearing-impaired subtitle.
+ */
+ @AsciidocAnnotation(description = "Indicates if the program has some hearing-impaired subtitle", required = true)
+ private Boolean hearingImpaired = null;
+
+ /**
+ * Indicates if the program has an audio description track.
+ */
+ @AsciidocAnnotation(description = "Indicates if the program has an audio description track", required = true)
+ private Boolean audioDescription = null;
+
+ /**
+ * If the type of the program is EPISODE, gives the season number.
+ */
+ @AsciidocAnnotation(description = "If the type of the program is EPISODE, gives the season number", required = true)
+ private Long season = null;
+
+ /**
+ * If the type of the program is EPISODE, gives the position of the episode in the season.
+ */
+ @AsciidocAnnotation(description = "If the type of the program is EPISODE, gives the position of the episode in "
+ + "the season", required = true)
+ private String episodeNumber = null;
+
+ /**
+ * Program definition.
+ */
+ @AsciidocAnnotation(description = "Program definition", required = true, constraints = "SD or HD")
+ private String definition = null;
+
+ /**
+ * List of links which points to entity in relation with the current program.
+ */
+ @AsciidocAnnotation(description = "List of links which points to entities in relation with"
+ + " the current program", required = true)
+ private List links = new ArrayList<>();
+
+ /**
+ * Part of day when program is broadcast(ed).
+ */
+ @AsciidocAnnotation(description = "Part of day when program start", required = true, constraints = "PT1, PT2, PT3, OTHER")
+ private String dayPart = null;
+
+ /**
+ * Identifier of the catchup program related to this live program.
+ */
+ @AsciidocAnnotation(description = "Identifier of the catchup program related to this live program", required = true)
+ private String catchupId = null;
+
+
+ //
+ /**
+ * Full param constructor.
+ */
+ @SuppressWarnings("all")
+ @JsonIgnore
+ public ClassWithoutApiModelProperty(String id, String programType, String title, String channelId,
+ Long diffusionDate, Long duration, Long csa, String kind, String kindDetailed,
+ String synopsis,
+ String languageVersion, Boolean hearingImpaired, Boolean audioDescription,
+ Long season,
+ String episodeNumber, String definition, List links,
+ String dayPart,
+ String catchupId) {
+ this.id = id;
+ this.programType = programType;
+ this.title = title;
+ this.channelId = channelId;
+ this.diffusionDate = diffusionDate;
+ this.duration = duration;
+ this.csa = csa;
+ this.kind = kind;
+ this.kindDetailed = kindDetailed;
+ this.synopsis = synopsis;
+ this.languageVersion = languageVersion;
+ this.hearingImpaired = hearingImpaired;
+ this.audioDescription = audioDescription;
+ this.season = season;
+ this.episodeNumber = episodeNumber;
+ this.definition = definition;
+ this.links = links;
+ this.dayPart = dayPart;
+ this.catchupId = catchupId;
+ }
+
+ /**
+ * Default constructor.
+ */
+ public ClassWithoutApiModelProperty() {
+
+ }
+ //
+
+ //
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getProgramType() {
+ return programType;
+ }
+
+ public void setProgramType(String programType) {
+ this.programType = programType;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getChannelId() {
+ return channelId;
+ }
+
+ public void setChannelId(String channelId) {
+ this.channelId = channelId;
+ }
+
+ public Long getDiffusionDate() {
+ return diffusionDate;
+ }
+
+ public void setDiffusionDate(Long diffusionDate) {
+ this.diffusionDate = diffusionDate;
+ }
+
+ public Long getDuration() {
+ return duration;
+ }
+
+ public void setDuration(Long duration) {
+ this.duration = duration;
+ }
+
+ public Long getCsa() {
+ return csa;
+ }
+
+ public void setCsa(Long csa) {
+ this.csa = csa;
+ }
+
+ public String getKind() {
+ return kind;
+ }
+
+ public void setKind(String kind) {
+ this.kind = kind;
+ }
+
+ public String getKindDetailed() {
+ return kindDetailed;
+ }
+
+ public void setKindDetailed(String kindDetailed) {
+ this.kindDetailed = kindDetailed;
+ }
+
+ public String getSynopsis() {
+ return synopsis;
+ }
+
+ public void setSynopsis(String synopsis) {
+ this.synopsis = synopsis;
+ }
+
+ public String getLanguageVersion() {
+ return languageVersion;
+ }
+
+ public void setLanguageVersion(String languageVersion) {
+ this.languageVersion = languageVersion;
+ }
+
+ public Boolean getHearingImpaired() {
+ return hearingImpaired;
+ }
+
+ public void setHearingImpaired(Boolean hearingImpaired) {
+ this.hearingImpaired = hearingImpaired;
+ }
+
+ public Boolean getAudioDescription() {
+ return audioDescription;
+ }
+
+ public void setAudioDescription(Boolean audioDescription) {
+ this.audioDescription = audioDescription;
+ }
+
+ public Long getSeason() {
+ return season;
+ }
+
+ public void setSeason(Long season) {
+ this.season = season;
+ }
+
+ public String getEpisodeNumber() {
+ return episodeNumber;
+ }
+
+ public void setEpisodeNumber(String episodeNumber) {
+ this.episodeNumber = episodeNumber;
+ }
+
+ public String getDefinition() {
+ return definition;
+ }
+
+ public void setDefinition(String definition) {
+ this.definition = definition;
+ }
+
+ public List getLinks() {
+ return links;
+ }
+
+ public void setLinks(List links) {
+ this.links = links;
+ }
+
+ public String getDayPart() {
+ return dayPart;
+ }
+
+ public void setDayPart(String dayPart) {
+ this.dayPart = dayPart;
+ }
+
+ public String getCatchupId() {
+ return catchupId;
+ }
+
+ public void setCatchupId(String catchupId) {
+ this.catchupId = catchupId;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ ClassWithoutApiModelProperty programLight = (ClassWithoutApiModelProperty) o;
+ return Objects.equals(id, programLight.id)
+ && Objects.equals(programType, programLight.programType)
+ && Objects.equals(title, programLight.title)
+ && Objects.equals(channelId, programLight.channelId)
+ && Objects.equals(diffusionDate, programLight.diffusionDate)
+ && Objects.equals(duration, programLight.duration)
+ && Objects.equals(csa, programLight.csa)
+ && Objects.equals(kind, programLight.kind)
+ && Objects.equals(kindDetailed, programLight.kindDetailed)
+ && Objects.equals(synopsis, programLight.synopsis)
+ && Objects.equals(languageVersion, programLight.languageVersion)
+ && Objects.equals(hearingImpaired, programLight.hearingImpaired)
+ && Objects.equals(audioDescription, programLight.audioDescription)
+ && Objects.equals(season, programLight.season)
+ && Objects.equals(episodeNumber, programLight.episodeNumber)
+ && Objects.equals(definition, programLight.definition)
+ && Objects.equals(links, programLight.links)
+ && Objects.equals(dayPart, programLight.dayPart)
+ && Objects.equals(catchupId, programLight.catchupId);
+ }
+
+
+ @Override
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "class ProgramLight {\n"
+ + " id: " + id + "\n"
+ + " programType: " + programType + "\n"
+ + " title: " + title + "\n"
+ + " channelId: " + channelId + "\n"
+ + " diffusionDate: " + diffusionDate + "\n"
+ + " duration: " + duration + "\n"
+ + " csa: " + csa + "\n"
+ + " kind: " + kind + "\n"
+ + " kindDetailed: " + kindDetailed + "\n"
+ + " synopsis: " + synopsis + "\n"
+ + " languageVersion: " + languageVersion + "\n"
+ + " hearingImpaired: " + hearingImpaired + "\n"
+ + " audioDescription: " + audioDescription + "\n"
+ + " season: " + season + "\n"
+ + " episodeNumber: " + episodeNumber + "\n"
+ + " definition: " + definition + "\n"
+ + " links: " + links + "\n"
+ + " dayPart: " + dayPart + "\n"
+ + " catchupId: " + catchupId + "\n"
+ + "}\n";
+ }
+ //
+}
+
+
diff --git a/restdoc-helper-plugin/src/test/java/com/pconil/restdoc/model/SimilarContent.java b/restdoc-helper-plugin/src/test/java/com/pconil/restdoc/model/SimilarContent.java
new file mode 100755
index 0000000..34e85d0
--- /dev/null
+++ b/restdoc-helper-plugin/src/test/java/com/pconil/restdoc/model/SimilarContent.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc.model;
+
+import com.pconil.restdoc.annotation.AsciidocAnnotation;
+import com.pconil.restdoc.annotation.InspectToDocument;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * SimilarContent describes a similar content as the one it refers to.
+ *
+ * @author patrice_conil
+ */
+@InspectToDocument(description = "SimilarContent describes a similar content as the one it refers to")
+class SimilarContent {
+
+ /**
+ * Title.
+ **/
+ @AsciidocAnnotation(description = "Title")
+ private String title = null;
+
+ /**
+ * Content identifier : the content identifier depends on its contentType. For MOVIE contentType, id is a videoId
+ * (aka externalAssetId in RTV). For SEASON contentType, id is a seasonId (aka serieId in RTV).
+ **/
+ @AsciidocAnnotation(description = "Content identifier")
+ private String id = null;
+
+ /**
+ * Offer identifier.
+ **/
+ @AsciidocAnnotation(description = "Offer identifier")
+ private String offerId = null;
+
+ /**
+ * Type of service.
+ */
+ @AsciidocAnnotation(description = "Type of service")
+ private String serviceType = null;
+
+ /**
+ * Type of content.
+ */
+ @AsciidocAnnotation(description = "Type of content")
+ private String contentType = null;
+
+ /**
+ * Type of similarity.
+ */
+ @AsciidocAnnotation(description = "Type of similarity see <>")
+ private String similarity = null;
+
+ /**
+ * CSA Level between 1 and 5.
+ **/
+ @AsciidocAnnotation(description = "CSA Level", constraints = "between 1 and 5")
+ private Integer csaLevel = null;
+
+ /**
+ * List of available images.
+ **/
+ @AsciidocAnnotation(description = "Available images")
+ private List images = new ArrayList<>();
+
+
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ SimilarContent similarContent = (SimilarContent) o;
+ return Objects.equals(title, similarContent.title)
+ && Objects.equals(id, similarContent.id)
+ && Objects.equals(offerId, similarContent.offerId)
+ && Objects.equals(serviceType, similarContent.serviceType)
+ && Objects.equals(contentType, similarContent.contentType)
+ && Objects.equals(similarity, similarContent.similarity)
+ && Objects.equals(csaLevel, similarContent.csaLevel)
+ && Objects.equals(images, similarContent.images);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(title, id, offerId, serviceType, contentType, similarity, csaLevel, images);
+ }
+
+ @Override
+ public String toString() {
+
+ return "class SimilarContent {\n"
+ + " title: " + title + "\n"
+ + " id: " + id + "\n"
+ + " offerId: " + offerId + "\n"
+ + " serviceType: " + serviceType + "\n"
+ + " contentType: " + contentType + "\n"
+ + " similarity: " + similarity + "\n"
+ + " csaLevel: " + csaLevel + "\n"
+ + " images: " + images + "\n"
+ + "}\n";
+ }
+ //
+}
diff --git a/restdoc-helper-plugin/src/test/resources/ChannelFieldDescriptor.java.expected b/restdoc-helper-plugin/src/test/resources/ChannelFieldDescriptor.java.expected
new file mode 100644
index 0000000..f5b455f
--- /dev/null
+++ b/restdoc-helper-plugin/src/test/resources/ChannelFieldDescriptor.java.expected
@@ -0,0 +1,31 @@
+package com.pconil.restdoc.otmllive.com.pconil.restdoc.model;
+
+import org.springframework.restdocs.payload.FieldDescriptor;
+import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
+
+public class ChannelFieldDescriptor {
+ public static FieldDescriptor[] fdChannel = new FieldDescriptor[] {
+ fieldWithPath("id").description("Identifier of the channel (Corresponds to the EPG identifier)"),
+ fieldWithPath("name").description("Name of the channel"),
+ fieldWithPath("zappingNumber").description("Code used to zap on the channel (on a STB ZE)"),
+ fieldWithPath("slogan").description("The channel slogan (used in case of no EPG description)"),
+ fieldWithPath("catchupId").description("The associated catchup channel identifier"),
+ fieldWithPath("bouquets").description("List of bouquets linked to the channel (PA_xxx)"),
+ fieldWithPath("links").description("List of links which points to entity in relation with the current channel (For now, points to the list of programs for the whole day)"),
+ fieldWithPath("logos").description("List of logos available for the given channel"),
+ fieldWithPath("terminalModels").description("List of terminal models available for the given channel") };
+
+
+ public static FieldDescriptor[] fdChannelList = new FieldDescriptor[] {
+ fieldWithPath("[].id").description("Identifier of the channel (Corresponds to the EPG identifier)"),
+ fieldWithPath("[].name").description("Name of the channel"),
+ fieldWithPath("[].zappingNumber").description("Code used to zap on the channel (on a STB ZE)"),
+ fieldWithPath("[].slogan").description("The channel slogan (used in case of no EPG description)"),
+ fieldWithPath("[].catchupId").description("The associated catchup channel identifier"),
+ fieldWithPath("[].bouquets").description("List of bouquets linked to the channel (PA_xxx)"),
+ fieldWithPath("[].links").description("List of links which points to entity in relation with the current channel (For now, points to the list of programs for the whole day)"),
+ fieldWithPath("[].logos").description("List of logos available for the given channel"),
+ fieldWithPath("[].terminalModels").description("List of terminal models available for the given channel") };
+
+
+}
diff --git a/restdoc-helper-plugin/src/test/resources/Class1DTOExpected.adoc b/restdoc-helper-plugin/src/test/resources/Class1DTOExpected.adoc
new file mode 100644
index 0000000..3f83c23
--- /dev/null
+++ b/restdoc-helper-plugin/src/test/resources/Class1DTOExpected.adoc
@@ -0,0 +1,17 @@
+[Class1DTO]
+= Class1DTO
+This is the Class1 description
+|===
+| Field| Type| Description| Constraints
+
+| field1
+| String
+| This field describe name of Class1DTO
+| Length must be between 4 and 6
+
+| field2
+| String
+| field 2
+|
+|===
+
diff --git a/restdoc-helper-plugin/src/test/resources/Class2DTOExpected.adoc b/restdoc-helper-plugin/src/test/resources/Class2DTOExpected.adoc
new file mode 100644
index 0000000..6eecb47
--- /dev/null
+++ b/restdoc-helper-plugin/src/test/resources/Class2DTOExpected.adoc
@@ -0,0 +1,16 @@
+[Class2DTO]
+= Class2DTO
+|===
+| Field| Type| Description| Constraints
+
+| field1
+| String
+| field 1
+|
+
+| field2
+| String
+| field 2
+|
+|===
+
diff --git a/restdoc-helper-plugin/src/test/resources/ClassSwaggerDTOExpected.adoc b/restdoc-helper-plugin/src/test/resources/ClassSwaggerDTOExpected.adoc
new file mode 100644
index 0000000..9de843c
--- /dev/null
+++ b/restdoc-helper-plugin/src/test/resources/ClassSwaggerDTOExpected.adoc
@@ -0,0 +1,22 @@
+[ClassSwaggerDTO]
+= ClassSwaggerDTO
+This is the Class1 description
+|===
+| Field| Type| Description| Constraints
+
+| field1
+| String
+| This is the field field1
+| required
+
+| field2
+| String
+| This is the optional field field2
+| optional
+
+| field3
+| String
+| This is the field field3
+| required
+|===
+
diff --git a/restdoc-helper-plugin/src/test/resources/ClassSwaggerDTOFieldDescriptor.java.expected b/restdoc-helper-plugin/src/test/resources/ClassSwaggerDTOFieldDescriptor.java.expected
new file mode 100644
index 0000000..cbd0eda
--- /dev/null
+++ b/restdoc-helper-plugin/src/test/resources/ClassSwaggerDTOFieldDescriptor.java.expected
@@ -0,0 +1,19 @@
+package com.pconil.restdoc;
+
+import org.springframework.restdocs.payload.FieldDescriptor;
+import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
+
+public class ClassSwaggerDTOFieldDescriptor {
+ public static FieldDescriptor[] fdClassSwaggerDTO = new FieldDescriptor[] {
+ fieldWithPath("field1").description("This is the field field1"),
+ fieldWithPath("field2").description("This is the optional field field2").optional(),
+ fieldWithPath("field3").description("This is the field field3") };
+
+
+ public static FieldDescriptor[] fdClassSwaggerDTOList = new FieldDescriptor[] {
+ fieldWithPath("[].field1").description("This is the field field1"),
+ fieldWithPath("[].field2").description("This is the optional field field2").optional(),
+ fieldWithPath("[].field3").description("This is the field field3") };
+
+
+}
diff --git a/restdoc-helper-plugin/src/test/resources/ClassWithoutApiModelPropertyFieldDescriptor.java.expected b/restdoc-helper-plugin/src/test/resources/ClassWithoutApiModelPropertyFieldDescriptor.java.expected
new file mode 100644
index 0000000..67ab05a
--- /dev/null
+++ b/restdoc-helper-plugin/src/test/resources/ClassWithoutApiModelPropertyFieldDescriptor.java.expected
@@ -0,0 +1,51 @@
+package com.pconil.restdoc;
+
+import org.springframework.restdocs.payload.FieldDescriptor;
+import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
+
+public class ClassWithoutApiModelPropertyFieldDescriptor {
+ public static FieldDescriptor[] fdClassWithoutApiModelProperty = new FieldDescriptor[] {
+ fieldWithPath("id").description("Program identifier"),
+ fieldWithPath("programType").description("Program type"),
+ fieldWithPath("title").description("Program title"),
+ fieldWithPath("channelId").description("Id of channel broadcasting this program"),
+ fieldWithPath("diffusionDate").description("Diffusion date in seconds"),
+ fieldWithPath("duration").description("Program duration in second"),
+ fieldWithPath("csa").description("Program CSA level"),
+ fieldWithPath("kind").description("Kind of program (ex: comedy)"),
+ fieldWithPath("kindDetailed").description("More detailed kind (e.g Comedy-drama)"),
+ fieldWithPath("synopsis").description("Program sysnopsis"),
+ fieldWithPath("languageVersion").description("Program language"),
+ fieldWithPath("hearingImpaired").description("Indicates if the program has some hearing-impaired subtitle"),
+ fieldWithPath("audioDescription").description("Indicates if the program has an audio description track"),
+ fieldWithPath("season").description("If the type of the program is EPISODE, gives the season number"),
+ fieldWithPath("episodeNumber").description("If the type of the program is EPISODE, gives the position of the episode in the season"),
+ fieldWithPath("definition").description("Program definition"),
+ fieldWithPath("links").description("List of links which points to entities in relation with the current program"),
+ fieldWithPath("dayPart").description("Part of day when program start"),
+ fieldWithPath("catchupId").description("Identifier of the catchup program related to this live program") };
+
+
+ public static FieldDescriptor[] fdClassWithoutApiModelPropertyList = new FieldDescriptor[] {
+ fieldWithPath("[].id").description("Program identifier"),
+ fieldWithPath("[].programType").description("Program type"),
+ fieldWithPath("[].title").description("Program title"),
+ fieldWithPath("[].channelId").description("Id of channel broadcasting this program"),
+ fieldWithPath("[].diffusionDate").description("Diffusion date in seconds"),
+ fieldWithPath("[].duration").description("Program duration in second"),
+ fieldWithPath("[].csa").description("Program CSA level"),
+ fieldWithPath("[].kind").description("Kind of program (ex: comedy)"),
+ fieldWithPath("[].kindDetailed").description("More detailed kind (e.g Comedy-drama)"),
+ fieldWithPath("[].synopsis").description("Program sysnopsis"),
+ fieldWithPath("[].languageVersion").description("Program language"),
+ fieldWithPath("[].hearingImpaired").description("Indicates if the program has some hearing-impaired subtitle"),
+ fieldWithPath("[].audioDescription").description("Indicates if the program has an audio description track"),
+ fieldWithPath("[].season").description("If the type of the program is EPISODE, gives the season number"),
+ fieldWithPath("[].episodeNumber").description("If the type of the program is EPISODE, gives the position of the episode in the season"),
+ fieldWithPath("[].definition").description("Program definition"),
+ fieldWithPath("[].links").description("List of links which points to entities in relation with the current program"),
+ fieldWithPath("[].dayPart").description("Part of day when program start"),
+ fieldWithPath("[].catchupId").description("Identifier of the catchup program related to this live program") };
+
+
+}
diff --git a/restdoc-helper-plugin/src/test/resources/SimilarContent.adoc b/restdoc-helper-plugin/src/test/resources/SimilarContent.adoc
new file mode 100644
index 0000000..d55103b
--- /dev/null
+++ b/restdoc-helper-plugin/src/test/resources/SimilarContent.adoc
@@ -0,0 +1,47 @@
+[SimilarContent]
+= SimilarContent
+SimilarContent describes a similar content as the one it refers to
+|===
+| Field| Type| Description| Constraints
+
+| title
+| String
+| Title
+|
+
+| id
+| String
+| Content identifier
+|
+
+| offerId
+| String
+| Offer identifier
+|
+
+| serviceType
+| String
+| Type of service
+|
+
+| contentType
+| String
+| Type of content
+|
+
+| similarity
+| String
+| Type of similarity see <>
+|
+
+| csaLevel
+| Integer
+| CSA Level
+| between 1 and 5
+
+| images
+| List
+| Available images
+|
+|===
+
diff --git a/restdoc-helper-plugin/src/test/resources/pom.xml b/restdoc-helper-plugin/src/test/resources/pom.xml
new file mode 100644
index 0000000..c59dedc
--- /dev/null
+++ b/restdoc-helper-plugin/src/test/resources/pom.xml
@@ -0,0 +1,43 @@
+
+
+
+ test-mojo
+ com.pconil.restdoc
+ 1.0.0-SNAPSHOT
+
+ 4.0.0
+
+
+
+ com.pconil.restdoc
+ restdoc-helper-plugin
+ 1.0-SNAPSHOT
+
+ ${basedir}/target/classes/
+ com.pconil.restdoc.model
+ ${basedir}/target/generated-test-sources
+ ${basedir}/target/generated-snippets
+
+
+
+ generate
+ generate-test-sources
+
+ restdoc-helper
+
+
+
+
+
+ org.springframework
+ spring-web
+ 4.3.3.RELEASE
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/springboot-sample/pom.xml b/springboot-sample/pom.xml
new file mode 100644
index 0000000..c090992
--- /dev/null
+++ b/springboot-sample/pom.xml
@@ -0,0 +1,335 @@
+
+
+
+ 4.0.0
+
+ com.pconil.restdoc
+ springboot-sample
+ 1.0.0-SNAPSHOT
+ jar
+ springboot-sample
+ Demo project for restdoc-helper annotation-parser
+
+
+
+ UTF-8
+ UTF-8
+ 1.8
+ 1.4.0.RELEASE
+ 4.3.2.RELEASE
+ 1.1.3.RELEASE
+ 2.5.0
+ 4.2.5.RELEASE
+ 1.1.2.RELEASE
+ 4.12
+ 3.3.2
+ 1.5.9
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ pom
+ ${spring-boot.version}
+ import
+
+
+
+ org.springframework.cloud
+ spring-cloud-netflix
+ ${spring-cloud.version}
+ pom
+ import
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ Brixton.SR5
+ pom
+ import
+
+
+ org.springframework.restdocs
+ spring-restdocs-mockmvc
+ ${spring-restdoc.version}
+
+
+
+ io.springfox
+ springfox-swagger2
+ ${springfox.version}
+
+
+ io.springfox
+ springfox-swagger-ui
+ ${springfox.version}
+
+
+ io.springfox
+ springfox-core
+ ${springfox.version}
+
+
+ io.springfox
+ springfox-spi
+ ${springfox.version}
+
+
+ io.springfox
+ springfox-spring-web
+ ${springfox.version}
+
+
+ io.swagger
+ swagger-models
+ ${swagger-model.version}
+
+
+ io.springfox
+ springfox-staticdocs
+ ${springfox.version}
+
+
+ junit
+ junit
+ ${junit.version}
+
+
+ org.hamcrest
+ hamcrest-all
+ 1.3
+
+
+ org.mockito
+ mockito-core
+ 1.10.19
+
+
+ com.jayway.jsonpath
+ json-path
+ 2.0.0
+
+
+ org.codehaus.plexus
+ plexus-utils
+ 2.0.5
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-rest
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.springframework.restdocs
+ spring-restdocs-mockmvc
+ test
+
+
+
+
+ io.springfox
+ springfox-swagger2
+
+
+ org.slf4j
+ slf4j-api
+
+
+
+
+ io.springfox
+ springfox-swagger-ui
+
+
+ io.springfox
+ springfox-staticdocs
+ test
+
+
+
+ com.pconil.restdoc
+ restdoc-annotation
+ 1.0-SNAPSHOT
+
+
+
+ org.slf4j
+ slf4j-api
+
+
+
+ io.springfox
+ springfox-core
+
+
+ io.springfox
+ springfox-spi
+
+
+ io.springfox
+ springfox-spring-web
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-enforcer-plugin
+ 1.4.1
+
+
+ enforce
+
+
+
+
+
+
+ enforce
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ 1.4.1.RELEASE
+
+ false
+
+
+
+
+ repackage
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.5.1
+
+ 1.8
+ 1.8
+
+
+
+ org.codehaus.mojo
+ build-helper-maven-plugin
+ 1.12
+
+
+ generate-test-sources
+
+ add-test-source
+
+
+
+ ${basedir}/target/generated-test-sources
+
+
+
+
+
+
+ com.pconil.restdoc
+ restdoc-helper-plugin
+ 1.0-SNAPSHOT
+
+ ${basedir}/target/classes/
+ com.pconil.restdoc.model
+ ${basedir}/target/generated-test-sources
+ ${basedir}/target/generated-snippets
+
+
+
+ generate
+ generate-test-sources
+
+ restdoc-helper
+
+
+
+
+
+ org.springframework
+ spring-web
+ 4.3.3.RELEASE
+
+
+
+
+ org.asciidoctor
+ asciidoctor-maven-plugin
+ 1.5.3
+
+
+ generate-doc
+
+ prepare-package
+
+ process-asciidoc
+
+
+ html
+ book
+ ${basedir}/src/main/asciidoc
+
+
+ ${basedir}/target/generated-snippets
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+ 3.0.1
+
+
+ @
+
+ false
+
+
+
+ copy-resources
+ prepare-package
+
+ copy-resources
+
+
+ ${basedir}/target/classes/static/docs
+
+
+ ${basedir}/target/generated-docs
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/springboot-sample/src/main/asciidoc/ApiDocumentation.adoc b/springboot-sample/src/main/asciidoc/ApiDocumentation.adoc
new file mode 100644
index 0000000..fbc8d34
--- /dev/null
+++ b/springboot-sample/src/main/asciidoc/ApiDocumentation.adoc
@@ -0,0 +1,43 @@
+= Sample API - Getting Started Guide
+:doctype: book
+:icons: font
+:source-highlighter: highlightjs
+:toc: left
+:toclevels: 4
+:sectlinks:
+
+[Introduction]
+= Introduction
+Sample API documentation for RESTful microservice.
+
+[Overview]
+= Overview generated by swagger
+include::{snippets}/overview.adoc[]
+
+[Model]
+= Models generated by restdoc-helper
+:leveloffset: +1
+//insert here your generated classes docs
+include::{snippets}/Program.adoc[]
+:leveloffset: -1
+
+[Definitions]
+= Model/Definitions generated via swagger
+include::{snippets}/definitions.adoc[]
+
+[Paths]
+= Path Generated via swagger
+include::{snippets}/paths.adoc[]
+
+[Tests]
+= Documentation from test
+
+== GET programs
+See swagger
+include::{snippets}/get-programs/response-fields.adoc[]
+
+=== Example request
+include::{snippets}/get-programs/curl-request.adoc[]
+
+=== Example response
+include::{snippets}/get-programs/http-response.adoc[]
\ No newline at end of file
diff --git a/springboot-sample/src/main/java/com/pconil/restdoc/SpringbootSampleApplication.java b/springboot-sample/src/main/java/com/pconil/restdoc/SpringbootSampleApplication.java
new file mode 100644
index 0000000..3478d86
--- /dev/null
+++ b/springboot-sample/src/main/java/com/pconil/restdoc/SpringbootSampleApplication.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc;
+
+import org.springframework.boot.Banner;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.context.annotation.ComponentScan;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+/**
+ * Spring Boot Application class.
+ *
+ * @author patrice_conil
+ */
+@SpringBootApplication
+@ComponentScan(basePackages = "com.pconil.restdoc")
+@EnableSwagger2
+public class SpringbootSampleApplication {
+
+ /**
+ * Main for app.
+ *
+ * @param args arguments
+ */
+ public static void main(String[] args) {
+
+ new SpringApplicationBuilder()
+ .sources(SpringbootSampleApplication.class)
+ .bannerMode(Banner.Mode.LOG)
+ .run(args);
+ }
+}
diff --git a/springboot-sample/src/main/java/com/pconil/restdoc/configuration/StaticDocServerConfiguration.java b/springboot-sample/src/main/java/com/pconil/restdoc/configuration/StaticDocServerConfiguration.java
new file mode 100644
index 0000000..e6ecd09
--- /dev/null
+++ b/springboot-sample/src/main/java/com/pconil/restdoc/configuration/StaticDocServerConfiguration.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc.configuration;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+
+/**
+ * Adds static/docs as resource location to handle them via servlet.
+ *
+ * @author Patrice_Conil
+ */
+@Configuration
+@EnableWebMvc
+public class StaticDocServerConfiguration extends WebMvcConfigurerAdapter {
+ @Override
+ public void addResourceHandlers(ResourceHandlerRegistry registry) {
+ registry.addResourceHandler("/docs/**").addResourceLocations("classpath:/static/docs/");
+ }
+}
diff --git a/springboot-sample/src/main/java/com/pconil/restdoc/configuration/SwaggerDocumentationConfig.java b/springboot-sample/src/main/java/com/pconil/restdoc/configuration/SwaggerDocumentationConfig.java
new file mode 100755
index 0000000..3493423
--- /dev/null
+++ b/springboot-sample/src/main/java/com/pconil/restdoc/configuration/SwaggerDocumentationConfig.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc.configuration;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.Contact;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+
+
+/**
+ * Swagger doc.
+ *
+ * @author patrice_conil
+ */
+@Configuration
+public class SwaggerDocumentationConfig {
+
+ /**
+ * Api info.
+ *
+ * @return Api info
+ */
+ ApiInfo apiInfo() {
+ return new ApiInfoBuilder()
+ .title("Sample API")
+ .version("1.0.0-SNAPSHOT")
+ .contact(new Contact("Me", "", "me@myserver.com"))
+ .build();
+ }
+
+ /**
+ * Custom implementation.
+ *
+ * @return custom implementation doclet
+ */
+ @Bean
+ public Docket customImplementation() {
+ return new Docket(DocumentationType.SWAGGER_2)
+ .select()
+ .apis(RequestHandlerSelectors.basePackage("com.pconil.restdoc"))
+ .build()
+ .useDefaultResponseMessages(false)
+ .apiInfo(apiInfo());
+ }
+
+}
diff --git a/springboot-sample/src/main/java/com/pconil/restdoc/controller/ProgramController.java b/springboot-sample/src/main/java/com/pconil/restdoc/controller/ProgramController.java
new file mode 100644
index 0000000..0a31f05
--- /dev/null
+++ b/springboot-sample/src/main/java/com/pconil/restdoc/controller/ProgramController.java
@@ -0,0 +1,130 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc.controller;
+
+import com.pconil.restdoc.model.Constant;
+import com.pconil.restdoc.model.Program;
+import com.pconil.restdoc.service.ProgramsService;
+import com.pconil.restdoc.exception.SampleException;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import io.swagger.annotations.ResponseHeader;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import java.time.LocalDate;
+import java.time.format.DateTimeParseException;
+import java.util.List;
+
+import static org.springframework.http.MediaType.APPLICATION_JSON_UTF8_VALUE;
+
+/**
+ * Controller that manages TV-EPG resources.
+ *
+ * @author patrice_conil
+ */
+@Controller
+@RequestMapping(value = "/v1", produces = {APPLICATION_JSON_UTF8_VALUE})
+@Api(value = "/programs", tags = {"/v1/programs"}, description = "the programs API")
+public class ProgramController {
+
+ /**
+ * Logger.
+ */
+ private static final Logger LOGGER = LoggerFactory.getLogger(ProgramController.class);
+
+ /**
+ * Programs com.pconil.restdoc.service.
+ */
+ @Autowired
+ private ProgramsService programsService;
+
+
+ /**
+ * Default constructor.
+ */
+ public ProgramController() {
+ }
+
+ /**
+ * Get a list of programs (light information) for a range of channels (one, a list with commas or all)
+ * and a specific date formatted as 'YYYY-MM-DD'.
+ *
+ * @param date date: a specific date formatted as 'YYYY-MM-DD'
+ * @param application application requesting
+ * @param channels channels for which you need to retrieve programs
+ * @param offset offset of first program to retrieve
+ * @param limit maximum number of items to retrieve
+ * @return list of program summaries
+ */
+ @RequestMapping(value = "/programs", produces = {"application/json;charset=UTF-8"}, method = RequestMethod.GET)
+ @ApiOperation(value = "Get a list of programs given a date for a given range of channels possibly",
+ notes = "Get a list of programs for a range of channels (one, a list with commas or "
+ + "all) a specific date formatted as 'YYYY-MM-DD'.",
+ response = Program.class,
+ responseContainer = "List")
+ @ApiResponses(value = {
+ @ApiResponse(code = Constant.OK, message = "OK", response = Program.class, responseContainer = "List",
+ responseHeaders = {
+ @ResponseHeader(name = "X-Result-Count",
+ description = "The actual number of items contained in the response body.",
+ response = String.class),
+ @ResponseHeader(name = "X-Total-Count",
+ description = "The total number of items in the collection.", response = String.class),
+ @ResponseHeader(name = "Cache-Control[max-age,public]",
+ description = "Contains max-age in seconds", response =
+ String.class),
+ @ResponseHeader(name = "ETag", description = "The Entity Tag", response = String.class)}),
+ @ApiResponse(code = Constant.BAD_REQUEST, message = "Bad Request", response = Error.class),
+ @ApiResponse(code = Constant.INTERNAL_EROR, message = "Internal Server Error", response = Error.class)})
+ public ResponseEntity> getPrograms(
+ @RequestParam(value = "date", required = false, defaultValue = "current") String date,
+ @RequestParam(value = "application", required = false, defaultValue = "PC") String application,
+ @RequestParam(value = "channels", required = false, defaultValue = "all") List channels,
+ @RequestParam(value = "offset", required = false, defaultValue = "0") Integer offset,
+ @RequestParam(value = "limit", required = false, defaultValue = "10") Integer limit) {
+
+ LOGGER.info("[getPrograms] date={}, application={}, channels={}, offset={}, limit={}",
+ date, application, channels.toString(), offset, limit);
+ if (date.equals("current")) {
+ date = LocalDate.now().toString();
+ } else {
+ try {
+ LocalDate.parse(date);
+ } catch (DateTimeParseException e) {
+ throw new SampleException("Bad date format");
+ }
+ }
+ List lightPaginated = programsService.findAllPrograms(date, application, channels);
+ ResponseEntity> responseEntity = ResponseEntity.ok()
+ .body(lightPaginated);
+ LOGGER.debug("[getPrograms] response = {}", responseEntity.toString());
+ return responseEntity;
+ }
+
+
+
+
+}
diff --git a/springboot-sample/src/main/java/com/pconil/restdoc/exception/SampleException.java b/springboot-sample/src/main/java/com/pconil/restdoc/exception/SampleException.java
new file mode 100644
index 0000000..0fb86af
--- /dev/null
+++ b/springboot-sample/src/main/java/com/pconil/restdoc/exception/SampleException.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc.exception;
+
+/**
+ * Sample com.pconil.restdoc.exception to show how exceptionHandling works.
+ *
+ * @author patrice_conil
+ */
+public class SampleException extends RuntimeException {
+
+ /**
+ * The error.
+ */
+ private Error error;
+
+ /**
+ * Constructor.
+ * @param message the message to store
+ */
+ public SampleException(String message) {
+ super(message);
+ error = new Error(message);
+ }
+
+ public Error getError() {
+ return error;
+ }
+}
diff --git a/springboot-sample/src/main/java/com/pconil/restdoc/model/Constant.java b/springboot-sample/src/main/java/com/pconil/restdoc/model/Constant.java
new file mode 100644
index 0000000..25b91d8
--- /dev/null
+++ b/springboot-sample/src/main/java/com/pconil/restdoc/model/Constant.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc.model;
+
+/**
+ * All constants.
+ *
+ * @author patrice_conil
+ */
+public class Constant {
+
+ /**
+ * 200 OK reply.
+ */
+ public static final int OK = 200;
+
+ /**
+ * 400 BAD_REQUEST reply.
+ */
+ public static final int BAD_REQUEST = 400;
+
+ /**
+ * 500 INTERNAL_ERROR 500.
+ */
+ public static final int INTERNAL_EROR = 500;
+
+}
diff --git a/springboot-sample/src/main/java/com/pconil/restdoc/model/ExceptionHandlingControllerAdvice.java b/springboot-sample/src/main/java/com/pconil/restdoc/model/ExceptionHandlingControllerAdvice.java
new file mode 100644
index 0000000..9b4891c
--- /dev/null
+++ b/springboot-sample/src/main/java/com/pconil/restdoc/model/ExceptionHandlingControllerAdvice.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc.model;
+
+import com.pconil.restdoc.exception.SampleException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+
+/**
+ * Performs exception handling globally. The exceptions below could be raised by any
+ * controller and they would be handled here, if not handled in the controller.
+ *
+ * @author patrice_conil
+ */
+@ControllerAdvice
+public class ExceptionHandlingControllerAdvice {
+
+ /**
+ * Logger.
+ */
+ private static Logger logger = LoggerFactory.getLogger(ExceptionHandlingControllerAdvice.class);
+
+ /**
+ * Resource Not found handler.
+ * @param exception the com.pconil.restdoc.exception
+ * @return NotFound Error
+ */
+ @ExceptionHandler(SampleException.class)
+ public ResponseEntity handleException(SampleException exception) {
+ logger.warn("OTMLException: ", exception.getMessage());
+ return new ResponseEntity<>(new Error(exception.getMessage()), HttpStatus.BAD_REQUEST);
+ }
+}
diff --git a/springboot-sample/src/main/java/com/pconil/restdoc/model/Program.java b/springboot-sample/src/main/java/com/pconil/restdoc/model/Program.java
new file mode 100755
index 0000000..cfbefe3
--- /dev/null
+++ b/springboot-sample/src/main/java/com/pconil/restdoc/model/Program.java
@@ -0,0 +1,454 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.pconil.restdoc.annotation.AsciidocAnnotation;
+import com.pconil.restdoc.annotation.InspectToDocument;
+import io.swagger.annotations.ApiModelProperty;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * Object that 'summarize' a Program (light data).
+ *
+ * @author patrice_conil
+ */
+@InspectToDocument(description = "Electronic Program Guide Item")
+public class Program {
+
+ /**
+ * Program identifier.
+ */
+ @AsciidocAnnotation(description = "Program identifier")
+ @ApiModelProperty(required = true, value = "Program identifier")
+ @JsonProperty("id")
+ private String id = null;
+
+ /**
+ * Program type.
+ */
+ @AsciidocAnnotation(description = "Program type")
+ @ApiModelProperty(required = true, value = "Program type")
+ @JsonProperty("programType")
+ private String programType = null;
+
+ /**
+ * Program title.
+ */
+ @AsciidocAnnotation(description = "Program title")
+ @ApiModelProperty(required = true, value = "Program title")
+ @JsonProperty("title")
+ private String title = null;
+
+ /**
+ * Id of channel broadcasting this program.
+ */
+ @AsciidocAnnotation(description = "Id of channel broadcasting this program")
+ @ApiModelProperty(required = true, value = "Identifier of the channel where the program is broadcasted")
+ @JsonProperty("channelId")
+ private String channelId = null;
+
+ /**
+ * Diffusion date in seconds.
+ */
+ @AsciidocAnnotation(description = "Diffusion date in seconds")
+ @ApiModelProperty(required = true, value = "Diffusion date in seconds")
+ @JsonProperty("diffusionDate")
+ private Long diffusionDate = null;
+
+ /**
+ * Program duration in second.
+ */
+ @AsciidocAnnotation(description = "Program duration in second")
+ @ApiModelProperty(required = true, value = "Duration in seconds")
+ @JsonProperty("duration")
+ private Long duration = null;
+
+ /**
+ * Program CSA level.
+ */
+ @AsciidocAnnotation(description = "Program CSA level")
+ @ApiModelProperty(required = true, value = "Audience maturity level")
+ @JsonProperty("csa")
+ private Long csa = null;
+
+ /**
+ * Kind of program (ex: variety).
+ */
+ @AsciidocAnnotation(description = "Kind of program (ex: comedy)")
+ @ApiModelProperty(required = true, value = "Program kind (e.g Comedy)")
+ @JsonProperty("kind")
+ private String kind = null;
+
+ /**
+ * More detailed kind (e.g Comedy-drama).
+ */
+ @AsciidocAnnotation(description = "More detailed kind (e.g Comedy-drama)")
+ @ApiModelProperty(required = true, value = "More detailed kind (e.g Comedy-drama)")
+ @JsonProperty("kindDetailed")
+ private String kindDetailed = null;
+
+ /**
+ * Program sysnopsis.
+ */
+ @AsciidocAnnotation(description = "Program sysnopsis")
+ @ApiModelProperty(required = true, value = "Program synopsis")
+ @JsonProperty("synopsis")
+ private String synopsis = null;
+
+ /**
+ * Program language.
+ */
+ @AsciidocAnnotation(description = "Program language")
+ @ApiModelProperty(required = true, value = "Program language")
+ @JsonProperty("languageVersion")
+ private String languageVersion = null;
+
+ /**
+ * Indicates if the program has some hearing-impaired subtitle.
+ */
+ @AsciidocAnnotation(description = "Indicates if the program has some hearing-impaired subtitle")
+ @ApiModelProperty(required = true, value = "Indicates if the program has some hearing-impaired subtitle")
+ @JsonProperty("hearingImpaired")
+ private Boolean hearingImpaired = null;
+
+ /**
+ * Indicates if the program has an audio description track.
+ */
+ @ApiModelProperty(required = true, value = "Indicates if the program has an audio description track")
+ @JsonProperty("audioDescription")
+ @AsciidocAnnotation(description = "Indicates if the program has an audio description track")
+ private Boolean audioDescription = null;
+
+ /**
+ * If the type of the program is EPISODE, gives the season number.
+ */
+ @AsciidocAnnotation(description = "If the type of the program is EPISODE, gives the season number")
+ @ApiModelProperty(required = true, value = "If the type of the program is EPISODE, gives the season number")
+ @JsonProperty("season")
+ private Long season = null;
+
+ /**
+ * If the type of the program is EPISODE, gives the position of the episode in the season.
+ */
+ @AsciidocAnnotation(description = "If the type of the program is EPISODE, gives the position of the episode in "
+ + "the season")
+ @ApiModelProperty(required = true, value = "if the type of the program is EPISODE, gives the position of the episode"
+ + "in the season")
+ @JsonProperty("episodeNumber")
+ private String episodeNumber = null;
+
+ /**
+ * Program definition.
+ */
+ @AsciidocAnnotation(description = "Program definition")
+ @ApiModelProperty(required = true, value = "Program definition", allowableValues = "SD, HD")
+ @JsonProperty("definition")
+ private String definition = null;
+
+ /**
+ * List of links which points to entity in relation with the current program.
+ */
+ @AsciidocAnnotation(description = "List of <> which points to entity in relation with"
+ + " the current program")
+ @ApiModelProperty(required = true, value = "List of links which points to entity in relation with the current program")
+ @JsonProperty("links")
+ private List links = new ArrayList<>();
+
+ /**
+ * Part of day when program is broadcast(ed).
+ */
+ @AsciidocAnnotation(description = "Part of day when program start")
+ @ApiModelProperty(required = true, value = "Part of day when program start, PT for prime time",
+ allowableValues = "PT1, PT2, PT3, OTHER")
+ @JsonProperty("dayPart")
+ private String dayPart = null;
+
+ /**
+ * Identifier of the catchup program related to this live program.
+ */
+ @AsciidocAnnotation(description = "Identifier of the catchup program related to this live program")
+ @ApiModelProperty(required = true, value = "Identifier of the catchup program related to this live program")
+ @JsonProperty("catchupId")
+ private String catchupId = null;
+
+
+ //
+ /**
+ * Full param constructor.
+ */
+ @SuppressWarnings("all")
+ @JsonIgnore
+ public Program(String id, String programType, String title, String channelId,
+ Long diffusionDate, Long duration, Long csa, String kind, String kindDetailed,
+ String synopsis,
+ String languageVersion, Boolean hearingImpaired, Boolean audioDescription,
+ Long season,
+ String episodeNumber, String definition, List links,
+ String dayPart,
+ String catchupId) {
+ this.id = id;
+ this.programType = programType;
+ this.title = title;
+ this.channelId = channelId;
+ this.diffusionDate = diffusionDate;
+ this.duration = duration;
+ this.csa = csa;
+ this.kind = kind;
+ this.kindDetailed = kindDetailed;
+ this.synopsis = synopsis;
+ this.languageVersion = languageVersion;
+ this.hearingImpaired = hearingImpaired;
+ this.audioDescription = audioDescription;
+ this.season = season;
+ this.episodeNumber = episodeNumber;
+ this.definition = definition;
+ this.links = links;
+ this.dayPart = dayPart;
+ this.catchupId = catchupId;
+ }
+
+ /**
+ * Default constructor.
+ */
+ public Program() {
+
+ }
+ //
+
+ //
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getProgramType() {
+ return programType;
+ }
+
+ public void setProgramType(String programType) {
+ this.programType = programType;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getChannelId() {
+ return channelId;
+ }
+
+ public void setChannelId(String channelId) {
+ this.channelId = channelId;
+ }
+
+ public Long getDiffusionDate() {
+ return diffusionDate;
+ }
+
+ public void setDiffusionDate(Long diffusionDate) {
+ this.diffusionDate = diffusionDate;
+ }
+
+ public Long getDuration() {
+ return duration;
+ }
+
+ public void setDuration(Long duration) {
+ this.duration = duration;
+ }
+
+ public Long getCsa() {
+ return csa;
+ }
+
+ public void setCsa(Long csa) {
+ this.csa = csa;
+ }
+
+ public String getKind() {
+ return kind;
+ }
+
+ public void setKind(String kind) {
+ this.kind = kind;
+ }
+
+ public String getKindDetailed() {
+ return kindDetailed;
+ }
+
+ public void setKindDetailed(String kindDetailed) {
+ this.kindDetailed = kindDetailed;
+ }
+
+ public String getSynopsis() {
+ return synopsis;
+ }
+
+ public void setSynopsis(String synopsis) {
+ this.synopsis = synopsis;
+ }
+
+ public String getLanguageVersion() {
+ return languageVersion;
+ }
+
+ public void setLanguageVersion(String languageVersion) {
+ this.languageVersion = languageVersion;
+ }
+
+ public Boolean getHearingImpaired() {
+ return hearingImpaired;
+ }
+
+ public void setHearingImpaired(Boolean hearingImpaired) {
+ this.hearingImpaired = hearingImpaired;
+ }
+
+ public Boolean getAudioDescription() {
+ return audioDescription;
+ }
+
+ public void setAudioDescription(Boolean audioDescription) {
+ this.audioDescription = audioDescription;
+ }
+
+ public Long getSeason() {
+ return season;
+ }
+
+ public void setSeason(Long season) {
+ this.season = season;
+ }
+
+ public String getEpisodeNumber() {
+ return episodeNumber;
+ }
+
+ public void setEpisodeNumber(String episodeNumber) {
+ this.episodeNumber = episodeNumber;
+ }
+
+ public String getDefinition() {
+ return definition;
+ }
+
+ public void setDefinition(String definition) {
+ this.definition = definition;
+ }
+
+ public List getLinks() {
+ return links;
+ }
+
+ public void setLinks(List links) {
+ this.links = links;
+ }
+
+ public String getDayPart() {
+ return dayPart;
+ }
+
+ public void setDayPart(String dayPart) {
+ this.dayPart = dayPart;
+ }
+
+ public String getCatchupId() {
+ return catchupId;
+ }
+
+ public void setCatchupId(String catchupId) {
+ this.catchupId = catchupId;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Program programLight = (Program) o;
+ return Objects.equals(id, programLight.id)
+ && Objects.equals(programType, programLight.programType)
+ && Objects.equals(title, programLight.title)
+ && Objects.equals(channelId, programLight.channelId)
+ && Objects.equals(diffusionDate, programLight.diffusionDate)
+ && Objects.equals(duration, programLight.duration)
+ && Objects.equals(csa, programLight.csa)
+ && Objects.equals(kind, programLight.kind)
+ && Objects.equals(kindDetailed, programLight.kindDetailed)
+ && Objects.equals(synopsis, programLight.synopsis)
+ && Objects.equals(languageVersion, programLight.languageVersion)
+ && Objects.equals(hearingImpaired, programLight.hearingImpaired)
+ && Objects.equals(audioDescription, programLight.audioDescription)
+ && Objects.equals(season, programLight.season)
+ && Objects.equals(episodeNumber, programLight.episodeNumber)
+ && Objects.equals(definition, programLight.definition)
+ && Objects.equals(links, programLight.links)
+ && Objects.equals(dayPart, programLight.dayPart)
+ && Objects.equals(catchupId, programLight.catchupId);
+ }
+
+
+ @Override
+ public int hashCode() {
+ return super.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ return "class ProgramLight {\n"
+ + " id: " + id + "\n"
+ + " programType: " + programType + "\n"
+ + " title: " + title + "\n"
+ + " channelId: " + channelId + "\n"
+ + " diffusionDate: " + diffusionDate + "\n"
+ + " duration: " + duration + "\n"
+ + " csa: " + csa + "\n"
+ + " kind: " + kind + "\n"
+ + " kindDetailed: " + kindDetailed + "\n"
+ + " synopsis: " + synopsis + "\n"
+ + " languageVersion: " + languageVersion + "\n"
+ + " hearingImpaired: " + hearingImpaired + "\n"
+ + " audioDescription: " + audioDescription + "\n"
+ + " season: " + season + "\n"
+ + " episodeNumber: " + episodeNumber + "\n"
+ + " definition: " + definition + "\n"
+ + " links: " + links + "\n"
+ + " dayPart: " + dayPart + "\n"
+ + " catchupId: " + catchupId + "\n"
+ + "}\n";
+ }
+ //
+}
+
+
diff --git a/springboot-sample/src/main/java/com/pconil/restdoc/service/ProgramsService.java b/springboot-sample/src/main/java/com/pconil/restdoc/service/ProgramsService.java
new file mode 100644
index 0000000..0054a7d
--- /dev/null
+++ b/springboot-sample/src/main/java/com/pconil/restdoc/service/ProgramsService.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc.service;
+
+import com.pconil.restdoc.model.Program;
+
+import java.util.List;
+
+
+/**
+ * Interface that services must implement to comply with controller.
+ *
+ * @author patrice_conil
+ */
+public interface ProgramsService {
+
+ /**
+ * Retrieves all the programs from EPG.
+ * @param date the date to consider
+ * @param application the application that request the data
+ * @param channels list of channel to consider
+ * @return List of programs
+ */
+ List findAllPrograms(String date, String application, List channels);
+}
diff --git a/springboot-sample/src/main/java/com/pconil/restdoc/service/ProgramsServiceImpl.java b/springboot-sample/src/main/java/com/pconil/restdoc/service/ProgramsServiceImpl.java
new file mode 100644
index 0000000..8d9dd15
--- /dev/null
+++ b/springboot-sample/src/main/java/com/pconil/restdoc/service/ProgramsServiceImpl.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc.service;
+
+
+import com.pconil.restdoc.model.Program;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Service that retrieves program resources.
+ *
+ * @author patrice_conil
+ */
+@Service("programsService")
+public class ProgramsServiceImpl implements ProgramsService {
+
+
+ /**
+ * Constructor.
+ */
+ public ProgramsServiceImpl() {
+ }
+
+ @Override
+ public List findAllPrograms(String date, String application, List channels) {
+
+ Program programLight = new Program("0", "EPISODE", "House of cards",
+ "Canal+", 0L, 1L, 1L, "comedy", "romantic comedy", "a beautiful synopsis",
+ "VF", false, false, 2L, "1", "HD", null, "PT1",
+ "catchupId");
+ List programLights = new ArrayList<>();
+ programLights.add(programLight);
+ programLight = new Program("0", "EPISODE", "House of cards", "Canal+", 1L,
+ 1L, 1L, "comedy", "romantic comedy", "a beautiful synopsis", "VF", false, false,
+ 2L, "2", "HD", null, "PT1", "catchupId");
+ programLights.add(programLight);
+ return programLights;
+ }
+}
diff --git a/springboot-sample/src/main/resources/application.properties b/springboot-sample/src/main/resources/application.properties
new file mode 100644
index 0000000..d623bb4
--- /dev/null
+++ b/springboot-sample/src/main/resources/application.properties
@@ -0,0 +1 @@
+logging.level=debug
\ No newline at end of file
diff --git a/springboot-sample/src/test/java/SpringbootSampleApplicationTests.java b/springboot-sample/src/test/java/SpringbootSampleApplicationTests.java
new file mode 100644
index 0000000..b61e962
--- /dev/null
+++ b/springboot-sample/src/test/java/SpringbootSampleApplicationTests.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2016-2018 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.
+ */
+import com.pconil.restdoc.SpringbootSampleApplication;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.context.junit4.SpringRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+
+@RunWith(SpringRunner.class)
+@SpringBootTest(classes = SpringbootSampleApplication.class)
+@WebAppConfiguration
+public class SpringbootSampleApplicationTests {
+
+ @Test
+ public void contextLoads() {
+ }
+
+}
diff --git a/springboot-sample/src/test/java/SwaggerStaticDocGeneratorTest.java b/springboot-sample/src/test/java/SwaggerStaticDocGeneratorTest.java
new file mode 100644
index 0000000..fc8dba6
--- /dev/null
+++ b/springboot-sample/src/test/java/SwaggerStaticDocGeneratorTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2016-2018 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.
+ */
+import com.pconil.restdoc.SpringbootSampleApplication;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.MockitoAnnotations;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.web.context.WebApplicationContext;
+import springfox.documentation.staticdocs.Swagger2MarkupResultHandler;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+
+import static junit.framework.TestCase.assertTrue;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;
+
+/**
+ * Adoc generation from swagger /v2/api-docs.
+ * This test produces 3 docs : definitions.adoc, overview.adoc, paths.adoc.
+ * These docs can be referenced by other adoc files to produce more precise documentation on your API
+ * (for example sequence diagram, or other additional information).
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringBootTest(classes = SpringbootSampleApplication.class)
+@ActiveProfiles("test")
+@WebAppConfiguration
+public class SwaggerStaticDocGeneratorTest {
+
+ private MockMvc mockMvc;
+
+ @Autowired
+ private WebApplicationContext webApplicationContext;
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mockMvc = webAppContextSetup(webApplicationContext).build();
+ }
+
+
+ /**
+ * Calls Swagger2MarkupResultHandler to generate adoc files from swagger.json/swagger.yaml.
+ * @throws Exception if something goes wrong
+ */
+ @Test
+ public void generateAsciiDoc() throws Exception {
+ String outDir = System.getProperty("asciiDocOutputDir", "target/generated-snippets");
+ Swagger2MarkupResultHandler resultHandler = Swagger2MarkupResultHandler
+ .outputDirectory(outDir)
+ .build();
+ this.mockMvc.perform(get("/v2/api-docs").accept(MediaType.APPLICATION_JSON))
+ .andDo(resultHandler)
+ .andExpect(status().isOk());
+
+ assertTrue(allDocsAreGenerated(outDir));
+ }
+
+ /**
+ * Verify that the three expected docs are generated.
+ * definitions.adoc, overview.adoc, paths.adoc
+ * @param outDir the directory where the doc must be generated
+ * @return true if the three docs are present
+ */
+ private boolean allDocsAreGenerated(String outDir) {
+ boolean result = true;
+ String[] names = {"overview.adoc", "paths.adoc", "definitions.adoc"};
+ List fileNames = Arrays.asList(names);
+ for (String file : fileNames) {
+ if (!new File(outDir + '/' + file).exists()) {
+ result = false;
+ }
+ }
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/springboot-sample/src/test/java/com/pconil/restdoc/ProgramControllerTest.java b/springboot-sample/src/test/java/com/pconil/restdoc/ProgramControllerTest.java
new file mode 100644
index 0000000..a5532ba
--- /dev/null
+++ b/springboot-sample/src/test/java/com/pconil/restdoc/ProgramControllerTest.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2016-2018 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 com.pconil.restdoc;
+
+
+import com.pconil.restdoc.controller.ProgramController;
+import com.pconil.restdoc.model.Program;
+import com.pconil.restdoc.model.ProgramFieldDescriptor;
+import com.pconil.restdoc.service.ProgramsService;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.http.MediaType;
+import org.springframework.restdocs.JUnitRestDocumentation;
+import org.springframework.restdocs.mockmvc.RestDocumentationResultHandler;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+import org.springframework.test.context.web.WebAppConfiguration;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
+import org.springframework.util.LinkedMultiValueMap;
+import org.springframework.util.MultiValueMap;
+import org.springframework.web.context.WebApplicationContext;
+
+import java.io.File;
+import java.time.LocalDate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static org.hamcrest.Matchers.hasSize;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.when;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
+import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessRequest;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.preprocessResponse;
+import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint;
+import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
+import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup;
+
+/**
+ * Unit test that document your API.
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringBootTest(classes = SpringbootSampleApplication.class)
+@WebAppConfiguration
+public class ProgramControllerTest {
+ private MockMvc mockMvc;
+
+ @Mock
+ ProgramsService programsService;
+
+ @Autowired
+ private WebApplicationContext webApplicationContext;
+
+ @InjectMocks
+ @Autowired
+ private ProgramController programController;
+
+
+ @Rule
+ //Need to generate in parent project dir to collect all needed data to produce html
+ public JUnitRestDocumentation restDocumentation =
+ new JUnitRestDocumentation("target/generated-snippets");
+
+ private RestDocumentationResultHandler restDocumentationResultHandler;
+
+
+ @Before
+ public void setUp() throws Exception {
+ // Creates snippets named using method-name expansion
+ restDocumentationResultHandler = document("{method-name}", preprocessRequest(prettyPrint()),
+ preprocessResponse(prettyPrint()));
+
+ MockitoAnnotations.initMocks(this);
+ this.mockMvc = webAppContextSetup(webApplicationContext).apply(documentationConfiguration(restDocumentation))
+ .alwaysDo(restDocumentationResultHandler).build();
+
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ //Reset Mock
+
+ }
+
+
+ @Test
+ public void getPrograms() throws Exception {
+
+ when(programsService.findAllPrograms(any(), any(), any()))
+ .thenReturn(findAllPrograms(LocalDate.now().toString(), "PC", Collections.singletonList("19")));
+
+ mockMvc.perform(get("/v1/programs").contentType(MediaType.APPLICATION_JSON_UTF8).header("Host", "localhost")
+ .accept(MediaType.APPLICATION_JSON_UTF8))
+ /*
+ * Here's the magic doc
+ */
+ .andDo(document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint()),
+ responseFields(
+ /*
+ * Here we use the java code generated accordingly to swagger annotations
+ */
+ ProgramFieldDescriptor.fdProgramList
+ ))
+ )
+ /*
+ * Expect your response is valid
+ */
+ .andExpect(MockMvcResultMatchers.jsonPath("$", hasSize(2)))
+ .andExpect(MockMvcResultMatchers.jsonPath("$[0].id").value("0"))
+ .andExpect(MockMvcResultMatchers.jsonPath("$[0].programType").value("EPISODE"))
+ .andExpect(status().isOk());
+
+ //Checks that snippet exists
+ assertEquals(true, new File("target/generated-snippets/get-programs").exists());
+ }
+
+ @Test
+ public void getProgramsWithBadDate() throws Exception {
+
+ MultiValueMap params = new LinkedMultiValueMap();
+ params.set("date", "badDate");
+
+ mockMvc.perform(get("/v1/programs").contentType(MediaType.APPLICATION_JSON_UTF8).header("Host", "localhost")
+ .params(params)
+ .accept(MediaType.APPLICATION_JSON_UTF8))
+ /*
+ * Here's the magic doc
+ */
+ .andDo(document("{method-name}", preprocessRequest(prettyPrint()), preprocessResponse(prettyPrint())
+ )
+ )
+ // Expect your response is an error
+ .andExpect(status().is4xxClientError());
+
+ //Checks that snippet exists
+ assertEquals(true, new File("target/generated-snippets/get-programs-with-bad-date").exists());
+ }
+
+
+ private List findAllPrograms(String date, String application, List channels) {
+
+ Program programLight = new Program("0", "EPISODE", "House of cards",
+ "Canal+", 0L, 2400L, 1L, "comedy", "romantic comedy", "a beautiful synopsis",
+ "VF", false, false, 2L, "1", "HD", null, "PT1",
+ "catchupId");
+ List programLights = new ArrayList<>();
+ programLights.add(programLight);
+ programLight = new Program("0", "EPISODE", "House of cards", "Canal+", 1L,
+ 2400L, 1L, "comedy", "romantic comedy", "a beautiful synopsis", "VF", false, false,
+ 2L, "2", "HD", null, "PT1", "catchupId");
+ programLights.add(programLight);
+ return programLights;
+ }
+
+}
\ No newline at end of file