├── .gitattributes ├── .gitignore ├── LICENSE.txt ├── Procfile ├── README.md ├── bin ├── boot-rest.jar └── run.sh ├── build.gradle ├── docs └── html5 │ └── index.html ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── src ├── asciidoc │ └── index.adoc ├── main │ ├── java │ │ └── net │ │ │ └── robi42 │ │ │ └── boot │ │ │ ├── Application.java │ │ │ ├── BeanConfig.java │ │ │ ├── JerseyConfig.java │ │ │ ├── dao │ │ │ ├── MessageRepository.java │ │ │ └── RepositoryRoot.java │ │ │ ├── domain │ │ │ ├── ErrorDto.java │ │ │ ├── Message.java │ │ │ └── MessageDto.java │ │ │ ├── rest │ │ │ ├── MessageResource.java │ │ │ ├── NotFoundWebException.java │ │ │ └── ObjectMapperProvider.java │ │ │ ├── service │ │ │ ├── MessageService.java │ │ │ └── MessageServiceImpl.java │ │ │ └── util │ │ │ ├── CustomEntityMapper.java │ │ │ ├── Manifest.java │ │ │ └── MessageFactory.java │ └── resources │ │ ├── application.yml │ │ ├── banner.txt │ │ ├── messages.properties │ │ ├── messages_de.properties │ │ ├── public │ │ ├── api-docs │ │ │ ├── css │ │ │ │ ├── highlight.default.css │ │ │ │ └── screen.css │ │ │ ├── images │ │ │ │ ├── explorer_icons.png │ │ │ │ ├── logo_small.png │ │ │ │ ├── pet_store_api.png │ │ │ │ ├── throbber.gif │ │ │ │ └── wordnik_api.png │ │ │ ├── index.html │ │ │ ├── lib │ │ │ │ ├── backbone-min.js │ │ │ │ ├── handlebars-1.0.0.js │ │ │ │ ├── highlight.7.3.pack.js │ │ │ │ ├── jquery-1.8.0.min.js │ │ │ │ ├── jquery.ba-bbq.min.js │ │ │ │ ├── jquery.slideto.min.js │ │ │ │ ├── jquery.wiggle.min.js │ │ │ │ ├── shred.bundle.js │ │ │ │ ├── shred │ │ │ │ │ └── content.js │ │ │ │ ├── swagger-oauth.js │ │ │ │ ├── swagger.js │ │ │ │ └── underscore-min.js │ │ │ ├── o2c.html │ │ │ ├── swagger-ui.js │ │ │ └── swagger-ui.min.js │ │ ├── docs │ │ │ └── index.html │ │ ├── favicon.ico │ │ ├── fonts │ │ │ ├── fontawesome-webfont.eot │ │ │ ├── fontawesome-webfont.svg │ │ │ ├── fontawesome-webfont.ttf │ │ │ ├── fontawesome-webfont.woff │ │ │ ├── fontawesome-webfont.woff2 │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ ├── glyphicons-halflings-regular.svg │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ ├── glyphicons-halflings-regular.woff │ │ │ └── glyphicons-halflings-regular.woff2 │ │ ├── images │ │ │ └── yeoman.png │ │ ├── index.html │ │ ├── robots.txt │ │ ├── scripts │ │ │ └── bundle.min-beecf942.js │ │ ├── styles │ │ │ ├── main.min-40ef19ff.css │ │ │ └── vendor.min-f81b665d.css │ │ └── views │ │ │ ├── footer.html │ │ │ └── home.html │ │ └── templates │ │ └── error.html ├── test │ ├── java │ │ └── net │ │ │ └── robi42 │ │ │ └── boot │ │ │ ├── TestApplication.java │ │ │ ├── TestBeanConfig.java │ │ │ ├── rest │ │ │ └── MessageResourceTest.java │ │ │ └── util │ │ │ └── IntegrationTestBase.java │ └── resources │ │ └── application-test.yml └── webapp │ ├── .editorconfig │ ├── .gitignore │ ├── .jshintrc │ ├── api-docs │ ├── css │ │ ├── highlight.default.css │ │ └── screen.css │ ├── images │ │ ├── explorer_icons.png │ │ ├── logo_small.png │ │ ├── pet_store_api.png │ │ ├── throbber.gif │ │ └── wordnik_api.png │ ├── index.html │ ├── lib │ │ ├── backbone-min.js │ │ ├── handlebars-1.0.0.js │ │ ├── highlight.7.3.pack.js │ │ ├── jquery-1.8.0.min.js │ │ ├── jquery.ba-bbq.min.js │ │ ├── jquery.slideto.min.js │ │ ├── jquery.wiggle.min.js │ │ ├── shred.bundle.js │ │ ├── shred │ │ │ └── content.js │ │ ├── swagger-oauth.js │ │ ├── swagger.js │ │ └── underscore-min.js │ ├── o2c.html │ ├── swagger-ui.js │ └── swagger-ui.min.js │ ├── app │ ├── favicon.ico │ ├── images │ │ └── yeoman.png │ ├── index.html │ ├── robots.txt │ ├── scripts │ │ ├── config.js │ │ ├── controllers │ │ │ └── home.js │ │ ├── directives │ │ │ └── tooltip.js │ │ ├── main.js │ │ ├── models │ │ │ └── message.js │ │ └── routing.js │ ├── styles │ │ └── main.less │ └── views │ │ ├── footer.html │ │ └── home.html │ ├── config.js │ ├── gulpfile.js │ └── package.json └── system.properties /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # General 2 | .DS_Store 3 | *~ 4 | *.swp 5 | *.log 6 | 7 | # IDEA 8 | *.iml 9 | *.iws 10 | *.ipr 11 | .idea/ 12 | /out 13 | /classes 14 | 15 | # Gradle 16 | .gradle/ 17 | /build 18 | 19 | # Elasticsearch 20 | /data 21 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2015 Robert Thurnher 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | web: java $JAVA_OPTS -Dfile.encoding=UTF-8 -server -jar build/libs/boot-rest.jar --server.port=$PORT --spring.profiles.active=prod 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Boot REST 2 | ========= 3 | 4 | Ensure having JDK 8 installed. 5 | 6 | Get it here:
7 | http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 8 | 9 | Then, e.g.: 10 | 11 | export JAVA_HOME=`/usr/libexec/java_home -v 1.8` 12 | 13 | To build & run via command line shell: 14 | 15 | ./gradlew clean build && java -server -jar build/libs/boot-rest.jar 16 | 17 | Or simply run `Application.main()` via IDEA (14.1+ with latest Lombok plugin recommended). 18 | 19 | Also (pre-packaged): 20 | 21 | bin/run.sh 22 | 23 | An endpoint to play with: 24 | 25 | curl -i localhost:8888/api/messages 26 | 27 | SPA frontend UI resides at root URL, BTW.
28 | Admin endpoints, powered by Spring Boot, at:
29 | `/manage/*` (auth-protected)
30 | REST API docs, powered by Swagger (UI), at:
31 | `/api-docs/index.html`
32 | Generated ones, via Asciidoctor, at:
33 | `/docs/index.html` 34 | 35 | Note: this thing is ready to be deployed on Heroku (and verified to run packaged as WAR in Tomcat 8 as `ROOT` context). 36 | 37 | FYI: using Spring Data Elasticsearch as embedded data source provider for fun & DRYness. 38 | 39 | PS: Bootstrap/Angular SPA is developed & built with the help of Yeoman, Gulp, and JSPM/ES6...
40 | So, you'll need to have Node.js/NPM as well as JSPM and Gulp CLI (globally) installed. 41 | -------------------------------------------------------------------------------- /bin/boot-rest.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robi42/boot-rest/5510bec14a24f219463b536512044ab948153eea/bin/boot-rest.jar -------------------------------------------------------------------------------- /bin/run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec java -server -jar `dirname "$0"`/boot-rest.jar 4 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext { 3 | // Versions 4 | // ======== 5 | springBootVersion = '1.2.5.RELEASE' 6 | asciidoctorVersion = '1.5.2' 7 | } 8 | repositories { jcenter() } 9 | dependencies { 10 | classpath "org.springframework.boot:spring-boot-gradle-plugin:$springBootVersion" 11 | classpath "org.asciidoctor:asciidoctor-gradle-plugin:$asciidoctorVersion" 12 | } 13 | } 14 | 15 | apply plugin: 'java' 16 | apply plugin: 'idea' 17 | apply plugin: 'spring-boot' 18 | apply plugin: 'org.asciidoctor.gradle.asciidoctor' 19 | 20 | sourceCompatibility = 1.8 21 | targetCompatibility = 1.8 22 | 23 | ext { 24 | // Versions 25 | // ======== 26 | springDataEsVersion = '1.2.1.RELEASE' 27 | jerseyVersion = '2.14' 28 | metricsVersion = '3.1.2' 29 | guavaVersion = '18.0' 30 | lombokVersion = '1.16.4' 31 | swaggerVersion = '1.3.12' 32 | assertJVersion = '3.1.0' 33 | } 34 | 35 | jar { 36 | doFirst { 37 | manifest { 38 | attributes 'Implementation-Title': project.name, 'Implementation-Version': version 39 | } 40 | } 41 | baseName = 'boot-rest' 42 | archiveName = "${baseName}.jar" 43 | version = '0.1.0-SNAPSHOT' 44 | } 45 | 46 | asciidoctor { 47 | sourceDir = file('src/asciidoc') 48 | outputDir = file('docs') 49 | } 50 | 51 | repositories { jcenter() } 52 | 53 | configurations { compile.exclude module: 'spring-boot-starter-tomcat' } 54 | 55 | dependencies { 56 | // Core 57 | // ==== 58 | compile 'org.springframework.boot:spring-boot-starter-web' 59 | compile('org.springframework.boot:spring-boot-starter-jersey') { exclude module: 'jersey-media-multipart' } 60 | compile 'org.springframework.boot:spring-boot-starter-jetty' 61 | compile 'org.springframework.boot:spring-boot-starter-actuator' 62 | compile 'org.springframework.boot:spring-boot-starter-security' 63 | compile "org.springframework.data:spring-data-elasticsearch:$springDataEsVersion" 64 | 65 | // Extensions 66 | // ========== 67 | compile 'org.thymeleaf:thymeleaf-spring4' 68 | compile 'org.eclipse.jetty:jetty-servlets' 69 | compile 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' 70 | compile "org.glassfish.jersey.media:jersey-media-multipart:$jerseyVersion" 71 | compile "io.dropwizard.metrics:metrics-core:$metricsVersion" 72 | 73 | // Utilities 74 | // ========= 75 | compile "com.google.guava:guava:$guavaVersion" 76 | compile "org.projectlombok:lombok:$lombokVersion" 77 | compile("com.wordnik:swagger-jersey2-jaxrs_2.10:$swaggerVersion") { 78 | exclude module: 'jsr311-api' 79 | exclude module: 'scala-compiler' 80 | } 81 | 82 | // Testing 83 | // ======= 84 | testCompile "org.springframework.boot:spring-boot-starter-test" 85 | testCompile "org.assertj:assertj-core:$assertJVersion" 86 | testCompile "org.glassfish.jersey.connectors:jersey-apache-connector:$jerseyVersion" 87 | } 88 | 89 | // Tasks 90 | // ===== 91 | task wrapper(type: Wrapper) { gradleVersion = '2.3' } 92 | 93 | // Webapp 94 | // ------ 95 | def webappDir = 'src/webapp' 96 | 97 | task npmInstall(type: Exec) { 98 | workingDir webappDir 99 | commandLine 'npm', 'install' 100 | } 101 | 102 | task gulpBuild(type: Exec) { 103 | dependsOn npmInstall 104 | workingDir webappDir 105 | commandLine 'gulp' 106 | } 107 | 108 | task gulpServe(type: Exec) { 109 | workingDir webappDir 110 | commandLine 'gulp', 'serve' 111 | } 112 | 113 | // Docs 114 | // ---- 115 | def docsDir = 'src/main/resources/public/docs' 116 | 117 | task makeDocsDir(type: Exec) { 118 | dependsOn asciidoctor 119 | workingDir projectDir 120 | commandLine 'mkdir', '-p', docsDir 121 | } 122 | 123 | task copyDocs(type: Copy) { 124 | dependsOn makeDocsDir 125 | from "${asciidoctor.outputDir}/html5/index.html" 126 | into docsDir 127 | } 128 | 129 | processResources.dependsOn copyDocs 130 | 131 | // Dist 132 | // ---- 133 | task copyDistJar(type: Copy) { 134 | from "build/libs/${jar.archiveName}" 135 | into "$projectDir/bin" 136 | } 137 | 138 | task dist(dependsOn: [gulpBuild, build, copyDistJar]) 139 | 140 | build.mustRunAfter gulpBuild 141 | 142 | // Heroku 143 | // ------ 144 | task stage(dependsOn: [classes, jar, assemble]) 145 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robi42/boot-rest/5510bec14a24f219463b536512044ab948153eea/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Apr 12 15:42:20 CEST 2015 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.3-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /src/asciidoc/index.adoc: -------------------------------------------------------------------------------- 1 | # Boot REST Docs 2 | 3 | These are the Boot REST docs. 4 | -------------------------------------------------------------------------------- /src/main/java/net/robi42/boot/Application.java: -------------------------------------------------------------------------------- 1 | package net.robi42.boot; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import lombok.val; 5 | import net.robi42.boot.dao.RepositoryRoot; 6 | import net.robi42.boot.util.Manifest; 7 | import org.springframework.boot.SpringApplication; 8 | import org.springframework.boot.autoconfigure.SpringBootApplication; 9 | import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer; 10 | import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer; 11 | import org.springframework.boot.context.embedded.MimeMappings; 12 | import org.springframework.context.annotation.Import; 13 | import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories; 14 | 15 | import javax.ws.rs.core.MediaType; 16 | 17 | import static org.springframework.boot.context.embedded.MimeMappings.DEFAULT; 18 | 19 | @Slf4j 20 | @Import(BeanConfig.class) 21 | @EnableElasticsearchRepositories(basePackageClasses = RepositoryRoot.class) 22 | public @SpringBootApplication class Application implements EmbeddedServletContainerCustomizer { 23 | 24 | public static void main(final String[] args) { 25 | SpringApplication.run(Application.class, args); 26 | log.info("Version: {}", Manifest.version()); 27 | } 28 | 29 | public @Override void customize(ConfigurableEmbeddedServletContainer container) { 30 | val mappings = new MimeMappings(DEFAULT); 31 | mappings.add("html", MediaType.TEXT_HTML + "; charset=UTF-8"); 32 | mappings.add("woff", "application/font-woff; charset=UTF-8"); 33 | mappings.add("woff2", "application/font-woff2; charset=UTF-8"); 34 | container.setMimeMappings(mappings); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/net/robi42/boot/BeanConfig.java: -------------------------------------------------------------------------------- 1 | package net.robi42.boot; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.wordnik.swagger.config.ConfigFactory; 5 | import com.wordnik.swagger.config.ScannerFactory; 6 | import com.wordnik.swagger.config.SwaggerConfig; 7 | import com.wordnik.swagger.jaxrs.config.DefaultJaxrsScanner; 8 | import com.wordnik.swagger.jersey.JerseyApiReader; 9 | import com.wordnik.swagger.reader.ClassReaders; 10 | import lombok.val; 11 | import net.robi42.boot.dao.MessageRepository; 12 | import net.robi42.boot.domain.Message; 13 | import net.robi42.boot.service.MessageService; 14 | import net.robi42.boot.service.MessageServiceImpl; 15 | import net.robi42.boot.util.CustomEntityMapper; 16 | import net.robi42.boot.util.Manifest; 17 | import net.robi42.boot.util.MessageFactory; 18 | import org.springframework.boot.CommandLineRunner; 19 | import org.springframework.boot.actuate.health.Health; 20 | import org.springframework.boot.actuate.health.HealthIndicator; 21 | import org.springframework.context.annotation.Bean; 22 | import org.springframework.context.annotation.Configuration; 23 | import org.springframework.data.elasticsearch.core.ElasticsearchOperations; 24 | import org.springframework.data.elasticsearch.core.ElasticsearchTemplate; 25 | 26 | import javax.inject.Inject; 27 | 28 | import static org.elasticsearch.node.NodeBuilder.nodeBuilder; 29 | 30 | @Configuration class BeanConfig { 31 | @Inject ObjectMapper objectMapper; 32 | @Inject MessageRepository messageRepository; 33 | 34 | @Bean JerseyConfig jerseyConfig() { 35 | return new JerseyConfig(messageService(), objectMapper); 36 | } 37 | 38 | @Bean CommandLineRunner swaggerConfig() { 39 | val swaggerConfig = new SwaggerConfig(); 40 | swaggerConfig.setBasePath("/"); 41 | swaggerConfig.setApiVersion(Manifest.version()); 42 | 43 | return args -> { 44 | ConfigFactory.setConfig(swaggerConfig); 45 | ScannerFactory.setScanner(new DefaultJaxrsScanner()); 46 | ClassReaders.setReader(new JerseyApiReader()); 47 | }; 48 | } 49 | 50 | @Bean CustomEntityMapper entityMapper() { 51 | return new CustomEntityMapper(objectMapper); 52 | } 53 | 54 | @Bean ElasticsearchOperations elasticsearchTemplate() { 55 | return new ElasticsearchTemplate(nodeBuilder().local(true).node().client(), entityMapper()); 56 | } 57 | 58 | @Bean MessageFactory messageFactory() { 59 | return new MessageFactory.Impl(); 60 | } 61 | 62 | @Bean MessageService messageService() { 63 | return new MessageServiceImpl(messageRepository, messageFactory()); 64 | } 65 | 66 | @Bean HealthIndicator messageIndexHealthIndicator(ElasticsearchOperations elasticsearchTemplate) { 67 | return () -> elasticsearchTemplate.typeExists(Message.INDEX_NAME, Message.TYPE_NAME) 68 | ? Health.up().build() : Health.down().build(); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/net/robi42/boot/JerseyConfig.java: -------------------------------------------------------------------------------- 1 | package net.robi42.boot; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.wordnik.swagger.jaxrs.listing.ApiListingResourceJSON; 5 | import com.wordnik.swagger.jersey.listing.JerseyApiDeclarationProvider; 6 | import com.wordnik.swagger.jersey.listing.JerseyResourceListingProvider; 7 | import net.robi42.boot.rest.MessageResource; 8 | import net.robi42.boot.rest.ObjectMapperProvider; 9 | import net.robi42.boot.service.MessageService; 10 | import org.glassfish.jersey.server.ResourceConfig; 11 | 12 | import javax.ws.rs.ApplicationPath; 13 | 14 | import static org.glassfish.jersey.server.ServerProperties.BV_SEND_ERROR_IN_RESPONSE; 15 | 16 | @ApplicationPath("/api") 17 | public class JerseyConfig extends ResourceConfig { 18 | 19 | public JerseyConfig(MessageService messageService, ObjectMapper objectMapper) { 20 | registerInstances( 21 | new MessageResource(messageService), 22 | new ObjectMapperProvider(objectMapper) 23 | ); 24 | registerClasses( // Swagger API Docs 25 | ApiListingResourceJSON.class, JerseyApiDeclarationProvider.class, JerseyResourceListingProvider.class 26 | ); 27 | property(BV_SEND_ERROR_IN_RESPONSE, true); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/net/robi42/boot/dao/MessageRepository.java: -------------------------------------------------------------------------------- 1 | package net.robi42.boot.dao; 2 | 3 | import net.robi42.boot.domain.Message; 4 | import org.springframework.data.elasticsearch.repository.ElasticsearchCrudRepository; 5 | 6 | import java.util.List; 7 | 8 | public interface MessageRepository extends ElasticsearchCrudRepository { 9 | List findByBodyLike(String term); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/net/robi42/boot/dao/RepositoryRoot.java: -------------------------------------------------------------------------------- 1 | package net.robi42.boot.dao; 2 | 3 | public interface RepositoryRoot {} 4 | -------------------------------------------------------------------------------- /src/main/java/net/robi42/boot/domain/ErrorDto.java: -------------------------------------------------------------------------------- 1 | package net.robi42.boot.domain; 2 | 3 | import lombok.Value; 4 | 5 | public @Value class ErrorDto { 6 | String message; 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/net/robi42/boot/domain/Message.java: -------------------------------------------------------------------------------- 1 | package net.robi42.boot.domain; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import lombok.val; 8 | import org.springframework.beans.BeanUtils; 9 | import org.springframework.data.elasticsearch.annotations.Document; 10 | 11 | import java.util.Date; 12 | 13 | @NoArgsConstructor 14 | @AllArgsConstructor 15 | @Document(indexName = Message.INDEX_NAME, type = Message.TYPE_NAME) 16 | public @Data @Builder class Message { 17 | public static final String INDEX_NAME = "messages"; 18 | public static final String TYPE_NAME = "message"; 19 | 20 | // Note: Spring Data ES doesn't support `UUID` typed IDs (yet?) 21 | private String id; 22 | private Date lastModifiedAt; 23 | private String body; 24 | 25 | public MessageDto toDto() { 26 | val dto = new MessageDto(); 27 | BeanUtils.copyProperties(this, dto); 28 | return dto; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/net/robi42/boot/domain/MessageDto.java: -------------------------------------------------------------------------------- 1 | package net.robi42.boot.domain; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | import org.hibernate.validator.constraints.NotEmpty; 8 | 9 | import javax.validation.constraints.Size; 10 | import java.util.Date; 11 | 12 | @NoArgsConstructor 13 | @AllArgsConstructor 14 | public @Data @Builder class MessageDto { 15 | private String id; 16 | private Date lastModifiedAt; 17 | private @NotEmpty @Size(max = 140) String body; 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/net/robi42/boot/rest/MessageResource.java: -------------------------------------------------------------------------------- 1 | package net.robi42.boot.rest; 2 | 3 | import com.wordnik.swagger.annotations.Api; 4 | import com.wordnik.swagger.annotations.ApiOperation; 5 | import lombok.NonNull; 6 | import lombok.RequiredArgsConstructor; 7 | import lombok.extern.slf4j.Slf4j; 8 | import lombok.val; 9 | import net.robi42.boot.domain.Message; 10 | import net.robi42.boot.domain.MessageDto; 11 | import net.robi42.boot.service.MessageService; 12 | import org.hibernate.validator.constraints.NotEmpty; 13 | 14 | import javax.validation.Valid; 15 | import javax.validation.constraints.NotNull; 16 | import javax.ws.rs.Consumes; 17 | import javax.ws.rs.DELETE; 18 | import javax.ws.rs.GET; 19 | import javax.ws.rs.POST; 20 | import javax.ws.rs.PUT; 21 | import javax.ws.rs.Path; 22 | import javax.ws.rs.PathParam; 23 | import javax.ws.rs.Produces; 24 | import javax.ws.rs.QueryParam; 25 | import javax.ws.rs.core.MediaType; 26 | import javax.ws.rs.core.Response; 27 | import java.net.URI; 28 | import java.util.List; 29 | import java.util.UUID; 30 | 31 | import static java.util.stream.Collectors.toList; 32 | 33 | @Slf4j 34 | @RequiredArgsConstructor 35 | @Api(value = MessageResource.BASE_PATH, description = "CRUD & Search") 36 | @Path(MessageResource.BASE_PATH) 37 | public class MessageResource { 38 | public static final String BASE_PATH = "messages"; 39 | 40 | private final @NonNull MessageService service; 41 | 42 | @ApiOperation("Create a new message") 43 | @Consumes(MediaType.APPLICATION_JSON) 44 | @Produces(MediaType.APPLICATION_JSON) 45 | public @POST Response create(@NotNull @Valid MessageDto dto) { 46 | val message = service.create(dto.getBody()).toDto(); 47 | val path = String.format("%s/%s", BASE_PATH, message.getId()); 48 | return Response.created(URI.create(path)) 49 | .entity(message).build(); 50 | } 51 | 52 | @ApiOperation("Get all messages") 53 | @Produces(MediaType.APPLICATION_JSON) 54 | public @GET List getAll() { 55 | val messages = service.getAll(); 56 | log.debug("Number of messages to serve: {}", messages.size()); 57 | return messages.stream().map(Message::toDto).collect(toList()); 58 | } 59 | 60 | @ApiOperation("Get a message by ID") 61 | @Produces(MediaType.APPLICATION_JSON) 62 | public @GET @Path("/{id}") MessageDto get(@PathParam("id") UUID id) { 63 | return service.get(id).map(Message::toDto) 64 | .orElseThrow(() -> new NotFoundWebException(notFoundMessageWith(id))); 65 | } 66 | 67 | @ApiOperation("Update a message") 68 | @Consumes(MediaType.APPLICATION_JSON) 69 | @Produces(MediaType.APPLICATION_JSON) 70 | public @PUT @Path("/{id}") MessageDto update(@PathParam("id") UUID id, 71 | @NotNull @Valid MessageDto dto) { 72 | return service.update(id, dto.getBody()) 73 | .map(Message::toDto).orElseThrow(() -> 74 | new NotFoundWebException(notFoundMessageWith(id))); 75 | } 76 | 77 | @ApiOperation("Delete a message") 78 | public @DELETE @Path("/{id}") void delete(@PathParam("id") UUID id) { 79 | service.delete(id); 80 | } 81 | 82 | @ApiOperation("Search for messages by body content") 83 | @Produces(MediaType.APPLICATION_JSON) 84 | public @GET @Path("/search") List search(@NotEmpty @QueryParam("q") String term) { 85 | return service.search(term).stream() 86 | .map(Message::toDto).collect(toList()); 87 | } 88 | 89 | private String notFoundMessageWith(UUID id) { 90 | return String.format("Message with ID '%s' not found", id); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/net/robi42/boot/rest/NotFoundWebException.java: -------------------------------------------------------------------------------- 1 | package net.robi42.boot.rest; 2 | 3 | import com.google.common.collect.ImmutableSet; 4 | import net.robi42.boot.domain.ErrorDto; 5 | 6 | import javax.ws.rs.WebApplicationException; 7 | import javax.ws.rs.core.Response; 8 | 9 | import static javax.ws.rs.core.Response.Status.NOT_FOUND; 10 | 11 | public class NotFoundWebException extends WebApplicationException { 12 | 13 | public NotFoundWebException(String message) { 14 | super(message, Response.status(NOT_FOUND) 15 | .entity(ImmutableSet.of(new ErrorDto(message))) 16 | .build()); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/net/robi42/boot/rest/ObjectMapperProvider.java: -------------------------------------------------------------------------------- 1 | package net.robi42.boot.rest; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import lombok.NonNull; 5 | import lombok.RequiredArgsConstructor; 6 | 7 | import javax.ws.rs.ext.ContextResolver; 8 | import javax.ws.rs.ext.Provider; 9 | 10 | @RequiredArgsConstructor 11 | public @Provider class ObjectMapperProvider implements ContextResolver { 12 | private final @NonNull ObjectMapper objectMapper; 13 | 14 | public @Override ObjectMapper getContext(Class type) { 15 | return objectMapper; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/net/robi42/boot/service/MessageService.java: -------------------------------------------------------------------------------- 1 | package net.robi42.boot.service; 2 | 3 | import net.robi42.boot.domain.Message; 4 | 5 | import java.util.List; 6 | import java.util.Optional; 7 | import java.util.UUID; 8 | 9 | public interface MessageService { 10 | Message create(String text); 11 | 12 | Optional get(UUID id); 13 | 14 | List getAll(); 15 | 16 | Optional update(UUID id, String text); 17 | 18 | void delete(UUID id); 19 | 20 | List search(String term); 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/net/robi42/boot/service/MessageServiceImpl.java: -------------------------------------------------------------------------------- 1 | package net.robi42.boot.service; 2 | 3 | import lombok.NonNull; 4 | import lombok.RequiredArgsConstructor; 5 | import lombok.val; 6 | import net.robi42.boot.dao.MessageRepository; 7 | import net.robi42.boot.domain.Message; 8 | import net.robi42.boot.util.MessageFactory; 9 | import org.springframework.data.domain.Sort; 10 | 11 | import java.util.Date; 12 | import java.util.List; 13 | import java.util.Optional; 14 | import java.util.UUID; 15 | 16 | import static jersey.repackaged.com.google.common.collect.Lists.newArrayList; 17 | import static org.springframework.data.domain.Sort.Direction.DESC; 18 | 19 | @RequiredArgsConstructor 20 | public class MessageServiceImpl implements MessageService { 21 | private final @NonNull MessageRepository repository; 22 | private final @NonNull MessageFactory factory; 23 | 24 | public @Override Message create(String text) { 25 | val messageToSave = factory.newMessage(text); 26 | return repository.save(messageToSave); 27 | } 28 | 29 | public @Override Optional get(UUID id) { 30 | val message = repository.findOne(id.toString()); 31 | return Optional.ofNullable(message); 32 | } 33 | 34 | public @Override List getAll() { 35 | return newArrayList(repository.findAll(new Sort(DESC, "lastModifiedAt"))); 36 | } 37 | 38 | public @Override Optional update(UUID id, String text) { 39 | val messageToUpdate = repository.findOne(id.toString()); 40 | return Optional.ofNullable(messageToUpdate) 41 | .map(message -> updateAndSave(text, message)); 42 | } 43 | 44 | private Message updateAndSave(String text, Message message) { 45 | message.setBody(text); 46 | message.setLastModifiedAt(new Date()); 47 | return repository.save(message); 48 | } 49 | 50 | public @Override void delete(UUID id) { 51 | repository.delete(id.toString()); 52 | } 53 | 54 | public @Override List search(String term) { 55 | return repository.findByBodyLike(term); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/net/robi42/boot/util/CustomEntityMapper.java: -------------------------------------------------------------------------------- 1 | package net.robi42.boot.util; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import lombok.NonNull; 5 | import lombok.RequiredArgsConstructor; 6 | import org.springframework.data.elasticsearch.core.EntityMapper; 7 | 8 | import java.io.IOException; 9 | 10 | @RequiredArgsConstructor 11 | public class CustomEntityMapper implements EntityMapper { 12 | private final @NonNull ObjectMapper objectMapper; 13 | 14 | public @Override String mapToString(Object object) throws IOException { 15 | return objectMapper.writeValueAsString(object); 16 | } 17 | 18 | public @Override T mapToObject(String source, Class clazz) throws IOException { 19 | return objectMapper.readValue(source, clazz); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/net/robi42/boot/util/Manifest.java: -------------------------------------------------------------------------------- 1 | package net.robi42.boot.util; 2 | 3 | import lombok.experimental.UtilityClass; 4 | import lombok.val; 5 | 6 | import static com.google.common.base.MoreObjects.firstNonNull; 7 | 8 | public @UtilityClass class Manifest { 9 | 10 | public static String version() { 11 | val implementationVersion = Manifest.class.getPackage().getImplementationVersion(); 12 | return firstNonNull(implementationVersion, "SNAPSHOT"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/net/robi42/boot/util/MessageFactory.java: -------------------------------------------------------------------------------- 1 | package net.robi42.boot.util; 2 | 3 | import net.robi42.boot.domain.Message; 4 | 5 | import java.util.Date; 6 | 7 | import static java.util.UUID.randomUUID; 8 | 9 | public interface MessageFactory { 10 | 11 | default Message newMessage(String text) { 12 | return Message.builder() 13 | .id(randomUUID().toString()) 14 | .lastModifiedAt(new Date()) 15 | .body(text) 16 | .build(); 17 | } 18 | 19 | class Impl implements MessageFactory {} 20 | } 21 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application.name: boot-rest 3 | jackson.serialization.write-dates-as-timestamps: false 4 | data.elasticsearch: 5 | cluster-name: ${spring.application.name} 6 | properties: 7 | node.name: Boot REST 8 | discovery.zen.ping.multicast.enabled: false 9 | 10 | server.port: 8888 11 | 12 | management: 13 | port: 8889 14 | context-path: /manage 15 | 16 | security: 17 | basic.path: ${management.context-path}/** 18 | user: 19 | name: admin 20 | password: secret 21 | 22 | logging.level.net.robi42.boot: DEBUG 23 | -------------------------------------------------------------------------------- /src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | __________ __ _____________________ ____________________ 2 | \______ \ ____ _____/ |\______ \_ _____// _____/\__ ___/ 3 | | | _// _ \ / _ \ __\ _/| __)_ \_____ \ | | 4 | | | ( <_> | <_> ) | | | \| \/ \ | | 5 | |______ /\____/ \____/|__| |____|_ /_______ /_______ / |____| 6 | \/ \/ \/ \/ 7 | -------------------------------------------------------------------------------- /src/main/resources/messages.properties: -------------------------------------------------------------------------------- 1 | errorPage.title=Error {0} 2 | errorPage.text=There was an unexpected error of type "{0}". 3 | -------------------------------------------------------------------------------- /src/main/resources/messages_de.properties: -------------------------------------------------------------------------------- 1 | errorPage.title=Fehler {0} 2 | errorPage.text=Es gab einen unerwarteten Fehler vom Typ "{0}". 3 | -------------------------------------------------------------------------------- /src/main/resources/public/api-docs/css/highlight.default.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Original style from softwaremaniacs.org (c) Ivan Sagalaev 4 | 5 | */ 6 | 7 | pre code { 8 | display: block; padding: 0.5em; 9 | background: #F0F0F0; 10 | } 11 | 12 | pre code, 13 | pre .subst, 14 | pre .tag .title, 15 | pre .lisp .title, 16 | pre .clojure .built_in, 17 | pre .nginx .title { 18 | color: black; 19 | } 20 | 21 | pre .string, 22 | pre .title, 23 | pre .constant, 24 | pre .parent, 25 | pre .tag .value, 26 | pre .rules .value, 27 | pre .rules .value .number, 28 | pre .preprocessor, 29 | pre .ruby .symbol, 30 | pre .ruby .symbol .string, 31 | pre .aggregate, 32 | pre .template_tag, 33 | pre .django .variable, 34 | pre .smalltalk .class, 35 | pre .addition, 36 | pre .flow, 37 | pre .stream, 38 | pre .bash .variable, 39 | pre .apache .tag, 40 | pre .apache .cbracket, 41 | pre .tex .command, 42 | pre .tex .special, 43 | pre .erlang_repl .function_or_atom, 44 | pre .markdown .header { 45 | color: #800; 46 | } 47 | 48 | pre .comment, 49 | pre .annotation, 50 | pre .template_comment, 51 | pre .diff .header, 52 | pre .chunk, 53 | pre .markdown .blockquote { 54 | color: #888; 55 | } 56 | 57 | pre .number, 58 | pre .date, 59 | pre .regexp, 60 | pre .literal, 61 | pre .smalltalk .symbol, 62 | pre .smalltalk .char, 63 | pre .go .constant, 64 | pre .change, 65 | pre .markdown .bullet, 66 | pre .markdown .link_url { 67 | color: #080; 68 | } 69 | 70 | pre .label, 71 | pre .javadoc, 72 | pre .ruby .string, 73 | pre .decorator, 74 | pre .filter .argument, 75 | pre .localvars, 76 | pre .array, 77 | pre .attr_selector, 78 | pre .important, 79 | pre .pseudo, 80 | pre .pi, 81 | pre .doctype, 82 | pre .deletion, 83 | pre .envvar, 84 | pre .shebang, 85 | pre .apache .sqbracket, 86 | pre .nginx .built_in, 87 | pre .tex .formula, 88 | pre .erlang_repl .reserved, 89 | pre .prompt, 90 | pre .markdown .link_label, 91 | pre .vhdl .attribute, 92 | pre .clojure .attribute, 93 | pre .coffeescript .property { 94 | color: #88F 95 | } 96 | 97 | pre .keyword, 98 | pre .id, 99 | pre .phpdoc, 100 | pre .title, 101 | pre .built_in, 102 | pre .aggregate, 103 | pre .css .tag, 104 | pre .javadoctag, 105 | pre .phpdoc, 106 | pre .yardoctag, 107 | pre .smalltalk .class, 108 | pre .winutils, 109 | pre .bash .variable, 110 | pre .apache .tag, 111 | pre .go .typename, 112 | pre .tex .command, 113 | pre .markdown .strong, 114 | pre .request, 115 | pre .status { 116 | font-weight: bold; 117 | } 118 | 119 | pre .markdown .emphasis { 120 | font-style: italic; 121 | } 122 | 123 | pre .nginx .built_in { 124 | font-weight: normal; 125 | } 126 | 127 | pre .coffeescript .javascript, 128 | pre .javascript .xml, 129 | pre .tex .formula, 130 | pre .xml .javascript, 131 | pre .xml .vbscript, 132 | pre .xml .css, 133 | pre .xml .cdata { 134 | opacity: 0.5; 135 | } 136 | -------------------------------------------------------------------------------- /src/main/resources/public/api-docs/images/explorer_icons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robi42/boot-rest/5510bec14a24f219463b536512044ab948153eea/src/main/resources/public/api-docs/images/explorer_icons.png -------------------------------------------------------------------------------- /src/main/resources/public/api-docs/images/logo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robi42/boot-rest/5510bec14a24f219463b536512044ab948153eea/src/main/resources/public/api-docs/images/logo_small.png -------------------------------------------------------------------------------- /src/main/resources/public/api-docs/images/pet_store_api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robi42/boot-rest/5510bec14a24f219463b536512044ab948153eea/src/main/resources/public/api-docs/images/pet_store_api.png -------------------------------------------------------------------------------- /src/main/resources/public/api-docs/images/throbber.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robi42/boot-rest/5510bec14a24f219463b536512044ab948153eea/src/main/resources/public/api-docs/images/throbber.gif -------------------------------------------------------------------------------- /src/main/resources/public/api-docs/images/wordnik_api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robi42/boot-rest/5510bec14a24f219463b536512044ab948153eea/src/main/resources/public/api-docs/images/wordnik_api.png -------------------------------------------------------------------------------- /src/main/resources/public/api-docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Swagger UI 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 62 | 63 | 64 | 65 | 75 | 76 |
 
77 |
78 | 79 | 80 | -------------------------------------------------------------------------------- /src/main/resources/public/api-docs/lib/backbone-min.js: -------------------------------------------------------------------------------- 1 | // Backbone.js 0.9.2 2 | 3 | // (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc. 4 | // Backbone may be freely distributed under the MIT license. 5 | // For all details and documentation: 6 | // http://backbonejs.org 7 | (function(){var l=this,y=l.Backbone,z=Array.prototype.slice,A=Array.prototype.splice,g;g="undefined"!==typeof exports?exports:l.Backbone={};g.VERSION="0.9.2";var f=l._;!f&&"undefined"!==typeof require&&(f=require("underscore"));var i=l.jQuery||l.Zepto||l.ender;g.setDomLibrary=function(a){i=a};g.noConflict=function(){l.Backbone=y;return this};g.emulateHTTP=!1;g.emulateJSON=!1;var p=/\s+/,k=g.Events={on:function(a,b,c){var d,e,f,g,j;if(!b)return this;a=a.split(p);for(d=this._callbacks||(this._callbacks= 8 | {});e=a.shift();)f=(j=d[e])?j.tail:{},f.next=g={},f.context=c,f.callback=b,d[e]={tail:g,next:j?j.next:f};return this},off:function(a,b,c){var d,e,h,g,j,q;if(e=this._callbacks){if(!a&&!b&&!c)return delete this._callbacks,this;for(a=a?a.split(p):f.keys(e);d=a.shift();)if(h=e[d],delete e[d],h&&(b||c))for(g=h.tail;(h=h.next)!==g;)if(j=h.callback,q=h.context,b&&j!==b||c&&q!==c)this.on(d,j,q);return this}},trigger:function(a){var b,c,d,e,f,g;if(!(d=this._callbacks))return this;f=d.all;a=a.split(p);for(g= 9 | z.call(arguments,1);b=a.shift();){if(c=d[b])for(e=c.tail;(c=c.next)!==e;)c.callback.apply(c.context||this,g);if(c=f){e=c.tail;for(b=[b].concat(g);(c=c.next)!==e;)c.callback.apply(c.context||this,b)}}return this}};k.bind=k.on;k.unbind=k.off;var o=g.Model=function(a,b){var c;a||(a={});b&&b.parse&&(a=this.parse(a));if(c=n(this,"defaults"))a=f.extend({},c,a);b&&b.collection&&(this.collection=b.collection);this.attributes={};this._escapedAttributes={};this.cid=f.uniqueId("c");this.changed={};this._silent= 10 | {};this._pending={};this.set(a,{silent:!0});this.changed={};this._silent={};this._pending={};this._previousAttributes=f.clone(this.attributes);this.initialize.apply(this,arguments)};f.extend(o.prototype,k,{changed:null,_silent:null,_pending:null,idAttribute:"id",initialize:function(){},toJSON:function(){return f.clone(this.attributes)},get:function(a){return this.attributes[a]},escape:function(a){var b;if(b=this._escapedAttributes[a])return b;b=this.get(a);return this._escapedAttributes[a]=f.escape(null== 11 | b?"":""+b)},has:function(a){return null!=this.get(a)},set:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c||(c={});if(!d)return this;d instanceof o&&(d=d.attributes);if(c.unset)for(e in d)d[e]=void 0;if(!this._validate(d,c))return!1;this.idAttribute in d&&(this.id=d[this.idAttribute]);var b=c.changes={},h=this.attributes,g=this._escapedAttributes,j=this._previousAttributes||{};for(e in d){a=d[e];if(!f.isEqual(h[e],a)||c.unset&&f.has(h,e))delete g[e],(c.silent?this._silent: 12 | b)[e]=!0;c.unset?delete h[e]:h[e]=a;!f.isEqual(j[e],a)||f.has(h,e)!=f.has(j,e)?(this.changed[e]=a,c.silent||(this._pending[e]=!0)):(delete this.changed[e],delete this._pending[e])}c.silent||this.change(c);return this},unset:function(a,b){(b||(b={})).unset=!0;return this.set(a,null,b)},clear:function(a){(a||(a={})).unset=!0;return this.set(f.clone(this.attributes),a)},fetch:function(a){var a=a?f.clone(a):{},b=this,c=a.success;a.success=function(d,e,f){if(!b.set(b.parse(d,f),a))return!1;c&&c(b,d)}; 13 | a.error=g.wrapError(a.error,b,a);return(this.sync||g.sync).call(this,"read",this,a)},save:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c=c?f.clone(c):{};if(c.wait){if(!this._validate(d,c))return!1;e=f.clone(this.attributes)}a=f.extend({},c,{silent:!0});if(d&&!this.set(d,c.wait?a:c))return!1;var h=this,i=c.success;c.success=function(a,b,e){b=h.parse(a,e);if(c.wait){delete c.wait;b=f.extend(d||{},b)}if(!h.set(b,c))return false;i?i(h,a):h.trigger("sync",h,a,c)};c.error=g.wrapError(c.error, 14 | h,c);b=this.isNew()?"create":"update";b=(this.sync||g.sync).call(this,b,this,c);c.wait&&this.set(e,a);return b},destroy:function(a){var a=a?f.clone(a):{},b=this,c=a.success,d=function(){b.trigger("destroy",b,b.collection,a)};if(this.isNew())return d(),!1;a.success=function(e){a.wait&&d();c?c(b,e):b.trigger("sync",b,e,a)};a.error=g.wrapError(a.error,b,a);var e=(this.sync||g.sync).call(this,"delete",this,a);a.wait||d();return e},url:function(){var a=n(this,"urlRoot")||n(this.collection,"url")||t(); 15 | return this.isNew()?a:a+("/"==a.charAt(a.length-1)?"":"/")+encodeURIComponent(this.id)},parse:function(a){return a},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return null==this.id},change:function(a){a||(a={});var b=this._changing;this._changing=!0;for(var c in this._silent)this._pending[c]=!0;var d=f.extend({},a.changes,this._silent);this._silent={};for(c in d)this.trigger("change:"+c,this,this.get(c),a);if(b)return this;for(;!f.isEmpty(this._pending);){this._pending= 16 | {};this.trigger("change",this,a);for(c in this.changed)!this._pending[c]&&!this._silent[c]&&delete this.changed[c];this._previousAttributes=f.clone(this.attributes)}this._changing=!1;return this},hasChanged:function(a){return!arguments.length?!f.isEmpty(this.changed):f.has(this.changed,a)},changedAttributes:function(a){if(!a)return this.hasChanged()?f.clone(this.changed):!1;var b,c=!1,d=this._previousAttributes,e;for(e in a)if(!f.isEqual(d[e],b=a[e]))(c||(c={}))[e]=b;return c},previous:function(a){return!arguments.length|| 17 | !this._previousAttributes?null:this._previousAttributes[a]},previousAttributes:function(){return f.clone(this._previousAttributes)},isValid:function(){return!this.validate(this.attributes)},_validate:function(a,b){if(b.silent||!this.validate)return!0;var a=f.extend({},this.attributes,a),c=this.validate(a,b);if(!c)return!0;b&&b.error?b.error(this,c,b):this.trigger("error",this,c,b);return!1}});var r=g.Collection=function(a,b){b||(b={});b.model&&(this.model=b.model);b.comparator&&(this.comparator=b.comparator); 18 | this._reset();this.initialize.apply(this,arguments);a&&this.reset(a,{silent:!0,parse:b.parse})};f.extend(r.prototype,k,{model:o,initialize:function(){},toJSON:function(a){return this.map(function(b){return b.toJSON(a)})},add:function(a,b){var c,d,e,g,i,j={},k={},l=[];b||(b={});a=f.isArray(a)?a.slice():[a];c=0;for(d=a.length;c=b))this.iframe=i('