Add RESTful Notes Spring Data REST sample
This commit is contained in:
4
restful-notes-spring-data-rest/.gitignore
vendored
Normal file
4
restful-notes-spring-data-rest/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
.classpath
|
||||
.project
|
||||
.settings/
|
||||
target/
|
||||
BIN
restful-notes-spring-data-rest/.mvn/wrapper/maven-wrapper.jar
vendored
Normal file
BIN
restful-notes-spring-data-rest/.mvn/wrapper/maven-wrapper.jar
vendored
Normal file
Binary file not shown.
18
restful-notes-spring-data-rest/.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
18
restful-notes-spring-data-rest/.mvn/wrapper/maven-wrapper.properties
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you 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
|
||||
#
|
||||
# https://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.
|
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.3/apache-maven-3.8.3-bin.zip
|
||||
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar
|
||||
17
restful-notes-spring-data-rest/README.md
Normal file
17
restful-notes-spring-data-rest/README.md
Normal file
@@ -0,0 +1,17 @@
|
||||
# Restful Notes Spring Data REST Sample
|
||||
A RESTful web service for creating and storing notes.
|
||||
It uses hypermedia to describe the relationships between resources and to allow navigation between them.
|
||||
Demonstrates using Spring REST Docs with MockMvc and Spring Data REST.
|
||||
|
||||
|
||||
|
||||
## Building and Running the Sample
|
||||
You will need Java 17 to build and run the sample.
|
||||
It is built using Maven:
|
||||
|
||||
```
|
||||
./mvnw verify
|
||||
```
|
||||
|
||||
As part of the build, files named `build/docs/asciidoc/api-guide.html` and `build/docs/asciidoc/getting-started-guide.html` are created.
|
||||
They are produced using Asciidoctor and include snippets generated by the sample's tests using Spring REST Docs.
|
||||
234
restful-notes-spring-data-rest/mvnw
vendored
Executable file
234
restful-notes-spring-data-rest/mvnw
vendored
Executable file
@@ -0,0 +1,234 @@
|
||||
#!/bin/sh
|
||||
# ----------------------------------------------------------------------------
|
||||
# Licensed to the Apache Software Foundation (ASF) under one
|
||||
# or more contributor license agreements. See the NOTICE file
|
||||
# distributed with this work for additional information
|
||||
# regarding copyright ownership. The ASF licenses this file
|
||||
# to you 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
|
||||
#
|
||||
# https://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.
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------
|
||||
# Maven2 Start Up Batch script
|
||||
#
|
||||
# Required ENV vars:
|
||||
# ------------------
|
||||
# JAVA_HOME - location of a JDK home dir
|
||||
#
|
||||
# Optional ENV vars
|
||||
# -----------------
|
||||
# M2_HOME - location of maven2's installed home dir
|
||||
# MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
# e.g. to debug Maven itself, use
|
||||
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
# ----------------------------------------------------------------------------
|
||||
|
||||
if [ -z "$MAVEN_SKIP_RC" ] ; then
|
||||
|
||||
if [ -f /etc/mavenrc ] ; then
|
||||
. /etc/mavenrc
|
||||
fi
|
||||
|
||||
if [ -f "$HOME/.mavenrc" ] ; then
|
||||
. "$HOME/.mavenrc"
|
||||
fi
|
||||
|
||||
fi
|
||||
|
||||
# OS specific support. $var _must_ be set to either true or false.
|
||||
cygwin=false;
|
||||
darwin=false;
|
||||
mingw=false
|
||||
case "`uname`" in
|
||||
CYGWIN*) cygwin=true ;;
|
||||
MINGW*) mingw=true;;
|
||||
Darwin*) darwin=true
|
||||
#
|
||||
# Look for the Apple JDKs first to preserve the existing behaviour, and then look
|
||||
# for the new JDKs provided by Oracle.
|
||||
#
|
||||
if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK ] ; then
|
||||
#
|
||||
# Apple JDKs
|
||||
#
|
||||
export JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ] && [ -L /System/Library/Java/JavaVirtualMachines/CurrentJDK ] ; then
|
||||
#
|
||||
# Apple JDKs
|
||||
#
|
||||
export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ] && [ -L "/Library/Java/JavaVirtualMachines/CurrentJDK" ] ; then
|
||||
#
|
||||
# Oracle JDKs
|
||||
#
|
||||
export JAVA_HOME=/Library/Java/JavaVirtualMachines/CurrentJDK/Contents/Home
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ] && [ -x "/usr/libexec/java_home" ]; then
|
||||
#
|
||||
# Apple JDKs
|
||||
#
|
||||
export JAVA_HOME=`/usr/libexec/java_home`
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
if [ -r /etc/gentoo-release ] ; then
|
||||
JAVA_HOME=`java-config --jre-home`
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$M2_HOME" ] ; then
|
||||
## resolve links - $0 may be a link to maven's home
|
||||
PRG="$0"
|
||||
|
||||
# need this for relative symlinks
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG="`dirname "$PRG"`/$link"
|
||||
fi
|
||||
done
|
||||
|
||||
saveddir=`pwd`
|
||||
|
||||
M2_HOME=`dirname "$PRG"`/..
|
||||
|
||||
# make it fully qualified
|
||||
M2_HOME=`cd "$M2_HOME" && pwd`
|
||||
|
||||
cd "$saveddir"
|
||||
# echo Using m2 at $M2_HOME
|
||||
fi
|
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched
|
||||
if $cygwin ; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME=`cygpath --unix "$M2_HOME"`
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
|
||||
fi
|
||||
|
||||
# For Migwn, ensure paths are in UNIX format before anything is touched
|
||||
if $mingw ; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME="`(cd "$M2_HOME"; pwd)`"
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`"
|
||||
# TODO classpath?
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ]; then
|
||||
javaExecutable="`which javac`"
|
||||
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then
|
||||
# readlink(1) is not available as standard on Solaris 10.
|
||||
readLink=`which readlink`
|
||||
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then
|
||||
if $darwin ; then
|
||||
javaHome="`dirname \"$javaExecutable\"`"
|
||||
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac"
|
||||
else
|
||||
javaExecutable="`readlink -f \"$javaExecutable\"`"
|
||||
fi
|
||||
javaHome="`dirname \"$javaExecutable\"`"
|
||||
javaHome=`expr "$javaHome" : '\(.*\)/bin'`
|
||||
JAVA_HOME="$javaHome"
|
||||
export JAVA_HOME
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$JAVACMD" ] ; then
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
else
|
||||
JAVACMD="`which java`"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
echo "Error: JAVA_HOME is not defined correctly." >&2
|
||||
echo " We cannot execute $JAVACMD" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then
|
||||
echo "Warning: JAVA_HOME environment variable is not set."
|
||||
fi
|
||||
|
||||
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin; then
|
||||
[ -n "$M2_HOME" ] &&
|
||||
M2_HOME=`cygpath --path --windows "$M2_HOME"`
|
||||
[ -n "$JAVA_HOME" ] &&
|
||||
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"`
|
||||
[ -n "$CLASSPATH" ] &&
|
||||
CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
|
||||
fi
|
||||
|
||||
# traverses directory structure from process work directory to filesystem root
|
||||
# first directory with .mvn subdirectory is considered project base directory
|
||||
find_maven_basedir() {
|
||||
local basedir=$(pwd)
|
||||
local wdir=$(pwd)
|
||||
while [ "$wdir" != '/' ] ; do
|
||||
if [ -d "$wdir"/.mvn ] ; then
|
||||
basedir=$wdir
|
||||
break
|
||||
fi
|
||||
wdir=$(cd "$wdir/.."; pwd)
|
||||
done
|
||||
echo "${basedir}"
|
||||
}
|
||||
|
||||
# concatenates all lines of a file
|
||||
concat_lines() {
|
||||
if [ -f "$1" ]; then
|
||||
echo "$(tr -s '\n' ' ' < "$1")"
|
||||
fi
|
||||
}
|
||||
|
||||
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-$(find_maven_basedir)}
|
||||
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
|
||||
|
||||
# Provide a "standardized" way to retrieve the CLI args that will
|
||||
# work with both Windows and non-Windows executions.
|
||||
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
|
||||
export MAVEN_CMD_LINE_ARGS
|
||||
|
||||
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
exec "$JAVACMD" \
|
||||
$MAVEN_OPTS \
|
||||
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
|
||||
"-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
|
||||
${WRAPPER_LAUNCHER} $MAVEN_CMD_LINE_ARGS
|
||||
|
||||
145
restful-notes-spring-data-rest/mvnw.cmd
vendored
Normal file
145
restful-notes-spring-data-rest/mvnw.cmd
vendored
Normal file
@@ -0,0 +1,145 @@
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Licensed to the Apache Software Foundation (ASF) under one
|
||||
@REM or more contributor license agreements. See the NOTICE file
|
||||
@REM distributed with this work for additional information
|
||||
@REM regarding copyright ownership. The ASF licenses this file
|
||||
@REM to you under the Apache License, Version 2.0 (the
|
||||
@REM "License"); you may not use this file except in compliance
|
||||
@REM with the License. You may obtain a copy of the License at
|
||||
@REM
|
||||
@REM https://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM
|
||||
@REM Unless required by applicable law or agreed to in writing,
|
||||
@REM software distributed under the License is distributed on an
|
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
@REM KIND, either express or implied. See the License for the
|
||||
@REM specific language governing permissions and limitations
|
||||
@REM under the License.
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM ----------------------------------------------------------------------------
|
||||
@REM Maven2 Start Up Batch script
|
||||
@REM
|
||||
@REM Required ENV vars:
|
||||
@REM JAVA_HOME - location of a JDK home dir
|
||||
@REM
|
||||
@REM Optional ENV vars
|
||||
@REM M2_HOME - location of maven2's installed home dir
|
||||
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
|
||||
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending
|
||||
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
|
||||
@REM e.g. to debug Maven itself, use
|
||||
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
|
||||
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
|
||||
@REM ----------------------------------------------------------------------------
|
||||
|
||||
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
|
||||
@echo off
|
||||
@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on'
|
||||
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
|
||||
|
||||
@REM set %HOME% to equivalent of $HOME
|
||||
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
|
||||
|
||||
@REM Execute a user defined script before this one
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
|
||||
@REM check for pre script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
|
||||
if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
|
||||
:skipRcPre
|
||||
|
||||
@setlocal
|
||||
|
||||
set ERROR_CODE=0
|
||||
|
||||
@REM To isolate internal variables from possible post scripts, we use another setlocal
|
||||
@setlocal
|
||||
|
||||
@REM ==== START VALIDATION ====
|
||||
if not "%JAVA_HOME%" == "" goto OkJHome
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME not found in your environment. >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
:OkJHome
|
||||
if exist "%JAVA_HOME%\bin\java.exe" goto init
|
||||
|
||||
echo.
|
||||
echo Error: JAVA_HOME is set to an invalid directory. >&2
|
||||
echo JAVA_HOME = "%JAVA_HOME%" >&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2
|
||||
echo location of your Java installation. >&2
|
||||
echo.
|
||||
goto error
|
||||
|
||||
@REM ==== END VALIDATION ====
|
||||
|
||||
:init
|
||||
|
||||
set MAVEN_CMD_LINE_ARGS=%MAVEN_CONFIG% %*
|
||||
|
||||
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
|
||||
@REM Fallback to current working directory if not found.
|
||||
|
||||
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
|
||||
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
|
||||
|
||||
set EXEC_DIR=%CD%
|
||||
set WDIR=%EXEC_DIR%
|
||||
:findBaseDir
|
||||
IF EXIST "%WDIR%"\.mvn goto baseDirFound
|
||||
cd ..
|
||||
IF "%WDIR%"=="%CD%" goto baseDirNotFound
|
||||
set WDIR=%CD%
|
||||
goto findBaseDir
|
||||
|
||||
:baseDirFound
|
||||
set MAVEN_PROJECTBASEDIR=%WDIR%
|
||||
cd "%EXEC_DIR%"
|
||||
goto endDetectBaseDir
|
||||
|
||||
:baseDirNotFound
|
||||
set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
|
||||
cd "%EXEC_DIR%"
|
||||
|
||||
:endDetectBaseDir
|
||||
|
||||
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
|
||||
|
||||
@setlocal EnableExtensions EnableDelayedExpansion
|
||||
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
|
||||
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
|
||||
|
||||
:endReadAdditionalConfig
|
||||
|
||||
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
|
||||
|
||||
set WRAPPER_JAR=""%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar""
|
||||
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
|
||||
|
||||
%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CMD_LINE_ARGS%
|
||||
if ERRORLEVEL 1 goto error
|
||||
goto end
|
||||
|
||||
:error
|
||||
set ERROR_CODE=1
|
||||
|
||||
:end
|
||||
@endlocal & set ERROR_CODE=%ERROR_CODE%
|
||||
|
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
|
||||
@REM check for post script, once with legacy .bat ending and once with .cmd ending
|
||||
if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
|
||||
if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
|
||||
:skipRcPost
|
||||
|
||||
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
|
||||
if "%MAVEN_BATCH_PAUSE%" == "on" pause
|
||||
|
||||
if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
|
||||
|
||||
exit /B %ERROR_CODE%
|
||||
135
restful-notes-spring-data-rest/pom.xml
Normal file
135
restful-notes-spring-data-rest/pom.xml
Normal file
@@ -0,0 +1,135 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<groupId>com.example</groupId>
|
||||
<artifactId>restful-notes-spring-data-rest</artifactId>
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>3.0.0-M4</version>
|
||||
<relativePath />
|
||||
</parent>
|
||||
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<java.version>17</java.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-rest</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-jpa</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.restdocs</groupId>
|
||||
<artifactId>spring-restdocs-mockmvc</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
<configuration>
|
||||
<includes>
|
||||
<include>**/*Documentation.java</include>
|
||||
</includes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.asciidoctor</groupId>
|
||||
<artifactId>asciidoctor-maven-plugin</artifactId>
|
||||
<version>2.2.2</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>generate-docs</id>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>process-asciidoc</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<backend>html</backend>
|
||||
<doctype>book</doctype>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.restdocs</groupId>
|
||||
<artifactId>spring-restdocs-asciidoctor</artifactId>
|
||||
<version>${spring-restdocs.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-resources</id>
|
||||
<phase>prepare-package</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>${project.build.outputDirectory}/static/docs</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${project.build.directory}/generated-docs</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring milestones</name>
|
||||
<url>https://repo.spring.io/milestone</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</repository>
|
||||
</repositories>
|
||||
<pluginRepositories>
|
||||
<pluginRepository>
|
||||
<id>spring-milestones</id>
|
||||
<name>Spring milestones</name>
|
||||
<url>https://repo.spring.io/milestone</url>
|
||||
<snapshots>
|
||||
<enabled>false</enabled>
|
||||
</snapshots>
|
||||
</pluginRepository>
|
||||
</pluginRepositories>
|
||||
|
||||
</project>
|
||||
228
restful-notes-spring-data-rest/src/main/asciidoc/api-guide.adoc
Normal file
228
restful-notes-spring-data-rest/src/main/asciidoc/api-guide.adoc
Normal file
@@ -0,0 +1,228 @@
|
||||
= RESTful Notes API Guide
|
||||
Andy Wilkinson;
|
||||
:doctype: book
|
||||
:icons: font
|
||||
:source-highlighter: highlightjs
|
||||
:toc: left
|
||||
:toclevels: 4
|
||||
:sectlinks:
|
||||
:operation-curl-request-title: Example request
|
||||
:operation-http-response-title: Example response
|
||||
|
||||
[[overview]]
|
||||
= Overview
|
||||
|
||||
[[overview_http_verbs]]
|
||||
== HTTP verbs
|
||||
|
||||
RESTful notes tries to adhere as closely as possible to standard HTTP and REST conventions in its
|
||||
use of HTTP verbs.
|
||||
|
||||
|===
|
||||
| Verb | Usage
|
||||
|
||||
| `GET`
|
||||
| Used to retrieve a resource
|
||||
|
||||
| `POST`
|
||||
| Used to create a new resource
|
||||
|
||||
| `PATCH`
|
||||
| Used to update an existing resource, including partial updates
|
||||
|
||||
| `DELETE`
|
||||
| Used to delete an existing resource
|
||||
|===
|
||||
|
||||
[[overview_http_status_codes]]
|
||||
== HTTP status codes
|
||||
|
||||
RESTful notes tries to adhere as closely as possible to standard HTTP and REST conventions in its
|
||||
use of HTTP status codes.
|
||||
|
||||
|===
|
||||
| Status code | Usage
|
||||
|
||||
| `200 OK`
|
||||
| The request completed successfully
|
||||
|
||||
| `201 Created`
|
||||
| A new resource has been created successfully. The resource's URI is available from the response's
|
||||
`Location` header
|
||||
|
||||
| `204 No Content`
|
||||
| An update to an existing resource has been applied successfully
|
||||
|
||||
| `400 Bad Request`
|
||||
| The request was malformed. The response body will include an error providing further information
|
||||
|
||||
| `404 Not Found`
|
||||
| The requested resource did not exist
|
||||
|===
|
||||
|
||||
[[overview_errors]]
|
||||
== Errors
|
||||
|
||||
Whenever an error response (status code >= 400) is returned, the body will contain a JSON object
|
||||
that describes the problem. The error object has the following structure:
|
||||
|
||||
include::{snippets}/error-example/response-fields.adoc[]
|
||||
|
||||
For example, a request that attempts to apply a non-existent tag to a note will produce a
|
||||
`400 Bad Request` response:
|
||||
|
||||
include::{snippets}/error-example/http-response.adoc[]
|
||||
|
||||
[[overview_hypermedia]]
|
||||
== Hypermedia
|
||||
|
||||
RESTful Notes uses hypermedia and resources include links to other resources in their
|
||||
responses. Responses are in https://github.com/mikekelly/hal_specification[Hypertext
|
||||
Application Language (HAL)] format. Links can be found beneath the `_links` key. Users of
|
||||
the API should not create URIs themselves, instead they should use the above-described
|
||||
links to navigate from resource to resource.
|
||||
|
||||
[[resources]]
|
||||
= Resources
|
||||
|
||||
|
||||
|
||||
[[resources_index]]
|
||||
== Index
|
||||
|
||||
The index provides the entry point into the service.
|
||||
|
||||
|
||||
|
||||
[[resources_index_access]]
|
||||
=== Accessing the index
|
||||
|
||||
A `GET` request is used to access the index
|
||||
|
||||
operation::index-example[snippets='response-fields,http-response,links']
|
||||
|
||||
|
||||
|
||||
[[resources_notes]]
|
||||
== Notes
|
||||
|
||||
The Notes resources is used to create and list notes
|
||||
|
||||
|
||||
|
||||
[[resources_notes_list]]
|
||||
=== Listing notes
|
||||
|
||||
A `GET` request will list all of the service's notes.
|
||||
|
||||
operation::notes-list-example[snippets='response-fields,curl-request,http-response,links']
|
||||
|
||||
|
||||
|
||||
[[resources_notes_create]]
|
||||
=== Creating a note
|
||||
|
||||
A `POST` request is used to create a note.
|
||||
|
||||
operation::notes-create-example[snippets='request-fields,curl-request,http-response']
|
||||
|
||||
|
||||
|
||||
[[resources_tags]]
|
||||
== Tags
|
||||
|
||||
The Tags resource is used to create and list tags.
|
||||
|
||||
|
||||
|
||||
[[resources_tags_list]]
|
||||
=== Listing tags
|
||||
|
||||
A `GET` request will list all of the service's tags.
|
||||
|
||||
operation::tags-list-example[snippets='response-fields,curl-request,http-response,links']
|
||||
|
||||
|
||||
|
||||
[[resources_tags_create]]
|
||||
=== Creating a tag
|
||||
|
||||
A `POST` request is used to create a note
|
||||
|
||||
operation::tags-create-example[snippets='request-fields,curl-request,http-response']
|
||||
|
||||
|
||||
|
||||
[[resources_note]]
|
||||
== Note
|
||||
|
||||
The Note resource is used to retrieve, update, and delete individual notes
|
||||
|
||||
|
||||
|
||||
[[resources_note_links]]
|
||||
=== Links
|
||||
|
||||
include::{snippets}/note-get-example/links.adoc[]
|
||||
|
||||
|
||||
|
||||
[[resources_note_retrieve]]
|
||||
=== Retrieve a note
|
||||
|
||||
A `GET` request will retrieve the details of a note
|
||||
|
||||
operation::note-get-example[snippets='response-fields,curl-request,http-response']
|
||||
|
||||
|
||||
|
||||
[[resources_note_update]]
|
||||
=== Update a note
|
||||
|
||||
A `PATCH` request is used to update a note
|
||||
|
||||
==== Request structure
|
||||
|
||||
include::{snippets}/note-update-example/request-fields.adoc[]
|
||||
|
||||
To leave an attribute of a note unchanged, any of the above may be omitted from the request.
|
||||
|
||||
==== Example request
|
||||
|
||||
include::{snippets}/note-update-example/curl-request.adoc[]
|
||||
|
||||
==== Example response
|
||||
|
||||
include::{snippets}/note-update-example/http-response.adoc[]
|
||||
|
||||
|
||||
|
||||
[[resources_tag]]
|
||||
== Tag
|
||||
|
||||
The Tag resource is used to retrieve, update, and delete individual tags
|
||||
|
||||
|
||||
|
||||
[[resources_tag_links]]
|
||||
=== Links
|
||||
|
||||
include::{snippets}/tag-get-example/links.adoc[]
|
||||
|
||||
|
||||
|
||||
[[resources_tag_retrieve]]
|
||||
=== Retrieve a tag
|
||||
|
||||
A `GET` request will retrieve the details of a tag
|
||||
|
||||
operation::tag-get-example[snippets='response-fields,curl-request,http-response']
|
||||
|
||||
|
||||
|
||||
[[resources_tag_update]]
|
||||
=== Update a tag
|
||||
|
||||
A `PATCH` request is used to update a tag
|
||||
|
||||
operation::tag-update-example[snippets='request-fields,curl-request,http-response']
|
||||
@@ -0,0 +1,175 @@
|
||||
= RESTful Notes Getting Started Guide
|
||||
Andy Wilkinson;
|
||||
:doctype: book
|
||||
:icons: font
|
||||
:source-highlighter: highlightjs
|
||||
:toc: left
|
||||
:toclevels: 4
|
||||
:sectlinks:
|
||||
|
||||
[[introduction]]
|
||||
= Introduction
|
||||
|
||||
RESTful Notes is a RESTful web service for creating and storing notes. It uses hypermedia
|
||||
to describe the relationships between resources and to allow navigation between them.
|
||||
|
||||
|
||||
|
||||
[[getting_started_running_the_service]]
|
||||
== Running the service
|
||||
RESTful Notes is written using https://projects.spring.io/spring-boot[Spring Boot] which
|
||||
makes it easy to get it up and running so that you can start exploring the REST API.
|
||||
|
||||
The first step is to clone the Git repository:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
$ git clone https://github.com/spring-projects/spring-restdocs-samples
|
||||
----
|
||||
|
||||
Once the clone is complete, you're ready to get the service up and running:
|
||||
|
||||
[source,bash]
|
||||
----
|
||||
$ cd restful-notes-spring-data-rest
|
||||
$ ./mvnw clean package
|
||||
$ java -jar target/*.jar
|
||||
----
|
||||
|
||||
You can check that the service is up and running by executing a simple request using
|
||||
cURL:
|
||||
|
||||
include::{snippets}/index/1/curl-request.adoc[]
|
||||
|
||||
This request should yield the following response in the
|
||||
https://github.com/mikekelly/hal_specification[Hypertext Application Language (HAL)]
|
||||
format:
|
||||
|
||||
include::{snippets}/index/1/http-response.adoc[]
|
||||
|
||||
Note the `_links` in the JSON response. They are key to navigating the API.
|
||||
|
||||
|
||||
|
||||
[[getting_started_creating_a_note]]
|
||||
== Creating a note
|
||||
Now that you've started the service and verified that it works, the next step is to use
|
||||
it to create a new note. As you saw above, the URI for working with notes is included as
|
||||
a link when you perform a `GET` request against the root of the service:
|
||||
|
||||
include::{snippets}/index/1/http-response.adoc[]
|
||||
|
||||
To create a note, you need to execute a `POST` request to this URI including a JSON
|
||||
payload containing the title and body of the note:
|
||||
|
||||
include::{snippets}/creating-a-note/1/curl-request.adoc[]
|
||||
|
||||
The response from this request should have a status code of `201 Created` and contain a
|
||||
`Location` header whose value is the URI of the newly created note:
|
||||
|
||||
include::{snippets}/creating-a-note/1/http-response.adoc[]
|
||||
|
||||
To work with the newly created note you use the URI in the `Location` header. For example,
|
||||
you can access the note's details by performing a `GET` request:
|
||||
|
||||
include::{snippets}/creating-a-note/2/curl-request.adoc[]
|
||||
|
||||
This request will produce a response with the note's details in its body:
|
||||
|
||||
include::{snippets}/creating-a-note/2/http-response.adoc[]
|
||||
|
||||
Note the `tags` link which we'll make use of later.
|
||||
|
||||
|
||||
|
||||
[[getting_started_creating_a_tag]]
|
||||
== Creating a tag
|
||||
To make a note easier to find, it can be associated with any number of tags. To be able
|
||||
to tag a note, you must first create the tag.
|
||||
|
||||
Referring back to the response for the service's index, the URI for working with tags is
|
||||
include as a link:
|
||||
|
||||
include::{snippets}/index/1/http-response.adoc[]
|
||||
|
||||
To create a tag you need to execute a `POST` request to this URI, including a JSON
|
||||
payload containing the name of the tag:
|
||||
|
||||
include::{snippets}/creating-a-note/3/curl-request.adoc[]
|
||||
|
||||
The response from this request should have a status code of `201 Created` and contain a
|
||||
`Location` header whose value is the URI of the newly created tag:
|
||||
|
||||
include::{snippets}/creating-a-note/3/http-response.adoc[]
|
||||
|
||||
To work with the newly created tag you use the URI in the `Location` header. For example
|
||||
you can access the tag's details by performing a `GET` request:
|
||||
|
||||
include::{snippets}/creating-a-note/4/curl-request.adoc[]
|
||||
|
||||
This request will produce a response with the tag's details in its body:
|
||||
|
||||
include::{snippets}/creating-a-note/4/http-response.adoc[]
|
||||
|
||||
|
||||
|
||||
[[getting_started_tagging_a_note]]
|
||||
== Tagging a note
|
||||
A tag isn't particularly useful until it's been associated with one or more notes. There
|
||||
are two ways to tag a note: when the note is first created or by updating an existing
|
||||
note. We'll look at both of these in turn.
|
||||
|
||||
|
||||
|
||||
[[getting_started_tagging_a_note_creating]]
|
||||
=== Creating a tagged note
|
||||
The process is largely the same as we saw before, but this time, in addition to providing
|
||||
a title and body for the note, we'll also provide the tag that we want to be associated
|
||||
with it.
|
||||
|
||||
Once again we execute a `POST` request. However, this time, in an array named tags, we
|
||||
include the URI of the tag we just created:
|
||||
|
||||
include::{snippets}/creating-a-note/5/curl-request.adoc[]
|
||||
|
||||
Once again, the response's `Location` header tells us the URI of the newly created note:
|
||||
|
||||
include::{snippets}/creating-a-note/5/http-response.adoc[]
|
||||
|
||||
As before, a `GET` request executed against this URI will retrieve the note's details:
|
||||
|
||||
include::{snippets}/creating-a-note/6/curl-request.adoc[]
|
||||
include::{snippets}/creating-a-note/6/http-response.adoc[]
|
||||
|
||||
To verify that the tag has been associated with the note, we can perform a `GET` request
|
||||
against the URI from the `tags` link:
|
||||
|
||||
include::{snippets}/creating-a-note/7/curl-request.adoc[]
|
||||
|
||||
The response embeds information about the tag that we've just associated with the note:
|
||||
|
||||
include::{snippets}/creating-a-note/7/http-response.adoc[]
|
||||
|
||||
|
||||
|
||||
[[getting_started_tagging_a_note_existing]]
|
||||
=== Tagging an existing note
|
||||
An existing note can be tagged by executing a `PATCH` request against the note's URI with
|
||||
a body that contains the array of tags to be associated with the note. We'll used the
|
||||
URI of the untagged note that we created earlier:
|
||||
|
||||
include::{snippets}/creating-a-note/8/curl-request.adoc[]
|
||||
|
||||
This request should produce a `204 No Content` response:
|
||||
|
||||
include::{snippets}/creating-a-note/8/http-response.adoc[]
|
||||
|
||||
When we first created this note, we noted the tags link included in its details:
|
||||
|
||||
include::{snippets}/creating-a-note/2/http-response.adoc[]
|
||||
|
||||
We can use that link now and execute a `GET` request to see that the note now has a
|
||||
single tag:
|
||||
|
||||
include::{snippets}/creating-a-note/9/curl-request.adoc[]
|
||||
include::{snippets}/creating-a-note/9/http-response.adoc[]
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2014-2022 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
|
||||
*
|
||||
* https://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.example.notes;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.ManyToMany;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
@Entity
|
||||
public class Note {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
private long id;
|
||||
|
||||
private String title;
|
||||
|
||||
private String body;
|
||||
|
||||
@ManyToMany
|
||||
private List<Tag> tags;
|
||||
|
||||
@JsonIgnore
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getBody() {
|
||||
return body;
|
||||
}
|
||||
|
||||
public void setBody(String body) {
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
public List<Tag> getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
public void setTags(List<Tag> tags) {
|
||||
this.tags = tags;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2014-2022 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
|
||||
*
|
||||
* https://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.example.notes;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
public interface NoteRepository extends CrudRepository<Note, Long> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2014-2022 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
|
||||
*
|
||||
* https://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.example.notes;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
@SpringBootApplication
|
||||
public class RestNotesSpringDataRest {
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(RestNotesSpringDataRest.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2014-2022 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
|
||||
*
|
||||
* https://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.example.notes;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.ManyToMany;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
|
||||
@Entity
|
||||
public class Tag {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.AUTO)
|
||||
private long id;
|
||||
|
||||
private String name;
|
||||
|
||||
@ManyToMany(mappedBy = "tags")
|
||||
private List<Note> notes;
|
||||
|
||||
@JsonIgnore
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public List<Note> getNotes() {
|
||||
return notes;
|
||||
}
|
||||
|
||||
public void setNotes(List<Note> notes) {
|
||||
this.notes = notes;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2014-2022 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
|
||||
*
|
||||
* https://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.example.notes;
|
||||
|
||||
import org.springframework.data.repository.CrudRepository;
|
||||
|
||||
public interface TagRepository extends CrudRepository<Tag, Long> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
spring.jackson.serialization.indent_output: true
|
||||
@@ -0,0 +1,347 @@
|
||||
/*
|
||||
* Copyright 2014-2022 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
|
||||
*
|
||||
* https://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.example.notes;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel;
|
||||
import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.links;
|
||||
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
|
||||
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
|
||||
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
|
||||
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.patch;
|
||||
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.requestFields;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
|
||||
import static org.springframework.restdocs.payload.PayloadDocumentation.subsectionWithPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.hateoas.MediaTypes;
|
||||
import org.springframework.restdocs.RestDocumentationContextProvider;
|
||||
import org.springframework.restdocs.RestDocumentationExtension;
|
||||
import org.springframework.restdocs.payload.JsonFieldType;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
||||
import jakarta.servlet.RequestDispatcher;
|
||||
|
||||
@SpringBootTest
|
||||
@ExtendWith(RestDocumentationExtension.class)
|
||||
class ApiDocumentation {
|
||||
|
||||
@Autowired
|
||||
private NoteRepository noteRepository;
|
||||
|
||||
@Autowired
|
||||
private TagRepository tagRepository;
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Autowired
|
||||
private WebApplicationContext context;
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@BeforeEach
|
||||
public void setUp(RestDocumentationContextProvider restDocumentation) {
|
||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
|
||||
.apply(documentationConfiguration(restDocumentation)).build();
|
||||
}
|
||||
|
||||
@Test
|
||||
void errorExample() throws Exception {
|
||||
this.mockMvc
|
||||
.perform(get("/error")
|
||||
.requestAttr(RequestDispatcher.ERROR_STATUS_CODE, 400)
|
||||
.requestAttr(RequestDispatcher.ERROR_REQUEST_URI,
|
||||
"/notes")
|
||||
.requestAttr(RequestDispatcher.ERROR_MESSAGE,
|
||||
"The tag 'http://localhost:8080/tags/123' does not exist"))
|
||||
.andDo(print()).andExpect(status().isBadRequest())
|
||||
.andExpect(jsonPath("error", is("Bad Request")))
|
||||
.andExpect(jsonPath("timestamp", is(notNullValue())))
|
||||
.andExpect(jsonPath("status", is(400)))
|
||||
.andExpect(jsonPath("path", is(notNullValue())))
|
||||
.andDo(document("error-example",
|
||||
responseFields(
|
||||
fieldWithPath("error").description("The HTTP error that occurred, e.g. `Bad Request`"),
|
||||
fieldWithPath("path").description("The path to which the request was made"),
|
||||
fieldWithPath("status").description("The HTTP status code, e.g. `400`"),
|
||||
fieldWithPath("timestamp").description("The time, in milliseconds, at which the error occurred"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void indexExample() throws Exception {
|
||||
this.mockMvc.perform(get("/"))
|
||||
.andExpect(status().isOk())
|
||||
.andDo(document("index-example",
|
||||
links(
|
||||
linkWithRel("notes").description("The <<resources_notes,Notes resource>>"),
|
||||
linkWithRel("tags").description("The <<resources_tags,Tags resource>>"),
|
||||
linkWithRel("profile").description("The ALPS profile for the service")),
|
||||
responseFields(
|
||||
subsectionWithPath("_links").description("<<resources_index_access_links,Links>> to other resources"))));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void notesListExample() throws Exception {
|
||||
this.noteRepository.deleteAll();
|
||||
|
||||
createNote("REST maturity model",
|
||||
"https://martinfowler.com/articles/richardsonMaturityModel.html");
|
||||
createNote("Hypertext Application Language (HAL)",
|
||||
"https://github.com/mikekelly/hal_specification");
|
||||
createNote("Application-Level Profile Semantics (ALPS)", "https://github.com/alps-io/spec");
|
||||
|
||||
this.mockMvc.perform(get("/notes"))
|
||||
.andExpect(status().isOk())
|
||||
.andDo(document("notes-list-example",
|
||||
links(
|
||||
linkWithRel("self").description("Canonical link for this resource"),
|
||||
linkWithRel("profile").description("The ALPS profile for this resource")),
|
||||
responseFields(
|
||||
subsectionWithPath("_embedded.notes").description("An array of <<resources_note, Note resources>>"),
|
||||
subsectionWithPath("_links").description("<<resources_tags_list_links, Links>> to other resources"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void notesCreateExample() throws Exception {
|
||||
Map<String, String> tag = new HashMap<String, String>();
|
||||
tag.put("name", "REST");
|
||||
|
||||
String tagLocation = this.mockMvc
|
||||
.perform(
|
||||
post("/tags").contentType(MediaTypes.HAL_JSON).content(
|
||||
this.objectMapper.writeValueAsString(tag)))
|
||||
.andExpect(status().isCreated()).andReturn().getResponse()
|
||||
.getHeader("Location");
|
||||
|
||||
Map<String, Object> note = new HashMap<String, Object>();
|
||||
note.put("title", "REST maturity model");
|
||||
note.put("body", "https://martinfowler.com/articles/richardsonMaturityModel.html");
|
||||
note.put("tags", Arrays.asList(tagLocation));
|
||||
|
||||
this.mockMvc.perform(
|
||||
post("/notes").contentType(MediaTypes.HAL_JSON).content(
|
||||
this.objectMapper.writeValueAsString(note))).andExpect(
|
||||
status().isCreated())
|
||||
.andDo(document("notes-create-example",
|
||||
requestFields(
|
||||
fieldWithPath("title").description("The title of the note"),
|
||||
fieldWithPath("body").description("The body of the note"),
|
||||
fieldWithPath("tags").description("An array of tag resource URIs"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void noteGetExample() throws Exception {
|
||||
Map<String, String> tag = new HashMap<String, String>();
|
||||
tag.put("name", "REST");
|
||||
|
||||
String tagLocation = this.mockMvc
|
||||
.perform(
|
||||
post("/tags").contentType(MediaTypes.HAL_JSON).content(
|
||||
this.objectMapper.writeValueAsString(tag)))
|
||||
.andExpect(status().isCreated()).andReturn().getResponse()
|
||||
.getHeader("Location");
|
||||
|
||||
Map<String, Object> note = new HashMap<String, Object>();
|
||||
note.put("title", "REST maturity model");
|
||||
note.put("body", "https://martinfowler.com/articles/richardsonMaturityModel.html");
|
||||
note.put("tags", Arrays.asList(tagLocation));
|
||||
|
||||
String noteLocation = this.mockMvc
|
||||
.perform(
|
||||
post("/notes").contentType(MediaTypes.HAL_JSON).content(
|
||||
this.objectMapper.writeValueAsString(note)))
|
||||
.andExpect(status().isCreated()).andReturn().getResponse()
|
||||
.getHeader("Location");
|
||||
|
||||
this.mockMvc.perform(get(noteLocation))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("title", is(note.get("title"))))
|
||||
.andExpect(jsonPath("body", is(note.get("body"))))
|
||||
.andExpect(jsonPath("_links.self.href", is(noteLocation)))
|
||||
.andExpect(jsonPath("_links.tags", is(notNullValue())))
|
||||
.andDo(print())
|
||||
.andDo(document("note-get-example",
|
||||
links(
|
||||
linkWithRel("self").description("Canonical link for this <<resources_note,note>>"),
|
||||
linkWithRel("note").description("This <<resources_note,note>>"),
|
||||
linkWithRel("tags").description("This note's tags")),
|
||||
responseFields(
|
||||
fieldWithPath("title").description("The title of the note"),
|
||||
fieldWithPath("body").description("The body of the note"),
|
||||
subsectionWithPath("_links").description("<<resources_note_links,Links>> to other resources"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void tagsListExample() throws Exception {
|
||||
this.noteRepository.deleteAll();
|
||||
this.tagRepository.deleteAll();
|
||||
|
||||
createTag("REST");
|
||||
createTag("Hypermedia");
|
||||
createTag("HTTP");
|
||||
|
||||
this.mockMvc.perform(get("/tags"))
|
||||
.andExpect(status().isOk())
|
||||
.andDo(document("tags-list-example",
|
||||
links(
|
||||
linkWithRel("self").description("Canonical link for this resource"),
|
||||
linkWithRel("profile").description("The ALPS profile for this resource")),
|
||||
responseFields(
|
||||
subsectionWithPath("_embedded.tags").description("An array of <<resources_tag,Tag resources>>"),
|
||||
subsectionWithPath("_links").description("<<resources_tags_list_links, Links>> to other resources"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void tagsCreateExample() throws Exception {
|
||||
Map<String, String> tag = new HashMap<String, String>();
|
||||
tag.put("name", "REST");
|
||||
|
||||
this.mockMvc.perform(
|
||||
post("/tags").contentType(MediaTypes.HAL_JSON).content(
|
||||
this.objectMapper.writeValueAsString(tag)))
|
||||
.andExpect(status().isCreated())
|
||||
.andDo(document("tags-create-example",
|
||||
requestFields(
|
||||
fieldWithPath("name").description("The name of the tag"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void noteUpdateExample() throws Exception {
|
||||
Map<String, Object> note = new HashMap<String, Object>();
|
||||
note.put("title", "REST maturity model");
|
||||
note.put("body", "https://martinfowler.com/articles/richardsonMaturityModel.html");
|
||||
|
||||
String noteLocation = this.mockMvc
|
||||
.perform(
|
||||
post("/notes").contentType(MediaTypes.HAL_JSON).content(
|
||||
this.objectMapper.writeValueAsString(note)))
|
||||
.andExpect(status().isCreated()).andReturn().getResponse()
|
||||
.getHeader("Location");
|
||||
|
||||
this.mockMvc.perform(get(noteLocation)).andExpect(status().isOk())
|
||||
.andExpect(jsonPath("title", is(note.get("title"))))
|
||||
.andExpect(jsonPath("body", is(note.get("body"))))
|
||||
.andExpect(jsonPath("_links.self.href", is(noteLocation)))
|
||||
.andExpect(jsonPath("_links.tags", is(notNullValue())));
|
||||
|
||||
Map<String, String> tag = new HashMap<String, String>();
|
||||
tag.put("name", "REST");
|
||||
|
||||
String tagLocation = this.mockMvc
|
||||
.perform(
|
||||
post("/tags").contentType(MediaTypes.HAL_JSON).content(
|
||||
this.objectMapper.writeValueAsString(tag)))
|
||||
.andExpect(status().isCreated()).andReturn().getResponse()
|
||||
.getHeader("Location");
|
||||
|
||||
Map<String, Object> noteUpdate = new HashMap<String, Object>();
|
||||
noteUpdate.put("tags", Arrays.asList(tagLocation));
|
||||
|
||||
this.mockMvc.perform(
|
||||
patch(noteLocation).contentType(MediaTypes.HAL_JSON).content(
|
||||
this.objectMapper.writeValueAsString(noteUpdate)))
|
||||
.andExpect(status().isNoContent())
|
||||
.andDo(document("note-update-example",
|
||||
requestFields(
|
||||
fieldWithPath("title").description("The title of the note").type(JsonFieldType.STRING).optional(),
|
||||
fieldWithPath("body").description("The body of the note").type(JsonFieldType.STRING).optional(),
|
||||
fieldWithPath("tags").description("An array of tag resource URIs").optional())));
|
||||
}
|
||||
|
||||
@Test
|
||||
void tagGetExample() throws Exception {
|
||||
Map<String, String> tag = new HashMap<String, String>();
|
||||
tag.put("name", "REST");
|
||||
|
||||
String tagLocation = this.mockMvc
|
||||
.perform(
|
||||
post("/tags").contentType(MediaTypes.HAL_JSON).content(
|
||||
this.objectMapper.writeValueAsString(tag)))
|
||||
.andExpect(status().isCreated()).andReturn().getResponse()
|
||||
.getHeader("Location");
|
||||
|
||||
this.mockMvc.perform(get(tagLocation))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("name", is(tag.get("name"))))
|
||||
.andDo(document("tag-get-example",
|
||||
links(
|
||||
linkWithRel("self").description("Canonical link for this <<resources_tag,tag>>"),
|
||||
linkWithRel("tag").description("This <<resources_tag,tag>>"),
|
||||
linkWithRel("notes").description("The notes that have this tag")),
|
||||
responseFields(
|
||||
fieldWithPath("name").description("The name of the tag"),
|
||||
subsectionWithPath("_links").description("<<resources_tag_links,Links>> to other resources"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
void tagUpdateExample() throws Exception {
|
||||
Map<String, String> tag = new HashMap<String, String>();
|
||||
tag.put("name", "REST");
|
||||
|
||||
String tagLocation = this.mockMvc
|
||||
.perform(
|
||||
post("/tags").contentType(MediaTypes.HAL_JSON).content(
|
||||
this.objectMapper.writeValueAsString(tag)))
|
||||
.andExpect(status().isCreated()).andReturn().getResponse()
|
||||
.getHeader("Location");
|
||||
|
||||
Map<String, Object> tagUpdate = new HashMap<String, Object>();
|
||||
tagUpdate.put("name", "RESTful");
|
||||
|
||||
this.mockMvc.perform(
|
||||
patch(tagLocation).contentType(MediaTypes.HAL_JSON).content(
|
||||
this.objectMapper.writeValueAsString(tagUpdate)))
|
||||
.andExpect(status().isNoContent())
|
||||
.andDo(document("tag-update-example",
|
||||
requestFields(
|
||||
fieldWithPath("name").description("The name of the tag"))));
|
||||
}
|
||||
|
||||
private void createNote(String title, String body) {
|
||||
Note note = new Note();
|
||||
note.setTitle(title);
|
||||
note.setBody(body);
|
||||
|
||||
this.noteRepository.save(note);
|
||||
}
|
||||
|
||||
private void createTag(String name) {
|
||||
Tag tag = new Tag();
|
||||
tag.setName(name);
|
||||
this.tagRepository.save(tag);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright 2014-2022 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
|
||||
*
|
||||
* https://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.example.notes;
|
||||
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
|
||||
import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
|
||||
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get;
|
||||
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.patch;
|
||||
import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.post;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.hateoas.MediaTypes;
|
||||
import org.springframework.restdocs.RestDocumentationContextProvider;
|
||||
import org.springframework.restdocs.RestDocumentationExtension;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.MvcResult;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.context.WebApplicationContext;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.jayway.jsonpath.JsonPath;
|
||||
|
||||
@SpringBootTest
|
||||
@ExtendWith(RestDocumentationExtension.class)
|
||||
class GettingStartedDocumentation {
|
||||
|
||||
@Autowired
|
||||
private ObjectMapper objectMapper;
|
||||
|
||||
@Autowired
|
||||
private WebApplicationContext context;
|
||||
|
||||
private MockMvc mockMvc;
|
||||
|
||||
@BeforeEach
|
||||
void setUp(RestDocumentationContextProvider restDocumentation) {
|
||||
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
|
||||
.apply(documentationConfiguration(restDocumentation))
|
||||
.alwaysDo(document("{method-name}/{step}/"))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
void index() throws Exception {
|
||||
this.mockMvc.perform(get("/").accept(MediaTypes.HAL_JSON))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("_links.notes", is(notNullValue())))
|
||||
.andExpect(jsonPath("_links.tags", is(notNullValue())));
|
||||
}
|
||||
|
||||
@Test
|
||||
void creatingANote() throws JsonProcessingException, Exception {
|
||||
String noteLocation = createNote();
|
||||
MvcResult note = getNote(noteLocation);
|
||||
|
||||
String tagLocation = createTag();
|
||||
getTag(tagLocation);
|
||||
|
||||
String taggedNoteLocation = createTaggedNote(tagLocation);
|
||||
MvcResult taggedNote = getNote(taggedNoteLocation);
|
||||
getTags(getLink(taggedNote, "tags"));
|
||||
|
||||
tagExistingNote(noteLocation, tagLocation);
|
||||
getTags(getLink(note, "tags"));
|
||||
}
|
||||
|
||||
private String createNote() throws Exception {
|
||||
Map<String, String> note = new HashMap<String, String>();
|
||||
note.put("title", "Note creation with cURL");
|
||||
note.put("body", "An example of how to create a note using cURL");
|
||||
|
||||
String noteLocation = this.mockMvc
|
||||
.perform(
|
||||
post("/notes").contentType(MediaTypes.HAL_JSON).content(
|
||||
objectMapper.writeValueAsString(note)))
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(header().string("Location", notNullValue()))
|
||||
.andReturn().getResponse().getHeader("Location");
|
||||
return noteLocation;
|
||||
}
|
||||
|
||||
private MvcResult getNote(String noteLocation) throws Exception {
|
||||
return this.mockMvc.perform(get(noteLocation))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("title", is(notNullValue())))
|
||||
.andExpect(jsonPath("body", is(notNullValue())))
|
||||
.andExpect(jsonPath("_links.tags", is(notNullValue())))
|
||||
.andReturn();
|
||||
}
|
||||
|
||||
private String createTag() throws Exception, JsonProcessingException {
|
||||
Map<String, String> tag = new HashMap<String, String>();
|
||||
tag.put("name", "getting-started");
|
||||
|
||||
String tagLocation = this.mockMvc
|
||||
.perform(
|
||||
post("/tags").contentType(MediaTypes.HAL_JSON).content(
|
||||
objectMapper.writeValueAsString(tag)))
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(header().string("Location", notNullValue()))
|
||||
.andReturn().getResponse().getHeader("Location");
|
||||
return tagLocation;
|
||||
}
|
||||
|
||||
private void getTag(String tagLocation) throws Exception {
|
||||
this.mockMvc.perform(get(tagLocation)).andExpect(status().isOk())
|
||||
.andExpect(jsonPath("name", is(notNullValue())))
|
||||
.andExpect(jsonPath("_links.notes", is(notNullValue())));
|
||||
}
|
||||
|
||||
private String createTaggedNote(String tag) throws Exception {
|
||||
Map<String, Object> note = new HashMap<String, Object>();
|
||||
note.put("title", "Tagged note creation with cURL");
|
||||
note.put("body", "An example of how to create a tagged note using cURL");
|
||||
note.put("tags", Arrays.asList(tag));
|
||||
|
||||
String noteLocation = this.mockMvc
|
||||
.perform(
|
||||
post("/notes").contentType(MediaTypes.HAL_JSON).content(
|
||||
objectMapper.writeValueAsString(note)))
|
||||
.andExpect(status().isCreated())
|
||||
.andExpect(header().string("Location", notNullValue()))
|
||||
.andReturn().getResponse().getHeader("Location");
|
||||
return noteLocation;
|
||||
}
|
||||
|
||||
private void getTags(String noteTagsLocation) throws Exception {
|
||||
this.mockMvc.perform(get(noteTagsLocation))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("_embedded.tags", hasSize(1)));
|
||||
}
|
||||
|
||||
private void tagExistingNote(String noteLocation, String tagLocation) throws Exception {
|
||||
Map<String, Object> update = new HashMap<String, Object>();
|
||||
update.put("tags", Arrays.asList(tagLocation));
|
||||
|
||||
this.mockMvc.perform(
|
||||
patch(noteLocation).contentType(MediaTypes.HAL_JSON).content(
|
||||
objectMapper.writeValueAsString(update)))
|
||||
.andExpect(status().isNoContent());
|
||||
}
|
||||
|
||||
private String getLink(MvcResult result, String rel)
|
||||
throws UnsupportedEncodingException {
|
||||
return JsonPath.parse(result.getResponse().getContentAsString()).read(
|
||||
"_links." + rel + ".href");
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user