Compare commits

..

154 Commits

Author SHA1 Message Date
Tom Hombergs
e316c2dc04 rename module to "spring-boot-springdoc" 2020-06-05 07:10:41 +10:00
Petros Stergioulas
c36bedd679 Remove actuator 2020-05-25 19:01:49 +02:00
Petros Stergioulas
eff3c3c935 Add security and actuator 2020-05-16 19:49:07 +02:00
Petros Stergioulas
94f88c4489 Add security and actuator 2020-05-16 19:46:43 +02:00
Petros Stergioulas
bb7ac442b1 Init 2020-04-29 20:07:36 +02:00
Petros Stergioulas
5da17c5a20 Implement UserApiDelegate, instead of the UserApi 2020-03-09 20:08:40 +01:00
Petros Stergioulas
26739aada2 Rename module to spring-boot-openapi, also add maven wrapper in root 2020-02-11 20:59:00 +01:00
Petros Stergioulas
dabc601384 Reflect-91 init 2020-02-09 22:25:26 +01:00
thombergs
0be1335d24 add EXPOSE 2020-02-06 06:45:21 +11:00
thombergs
1437ac4429 added warning do code that isn't working 2020-02-03 06:56:10 +11:00
thombergs
5b76cbac62 reduces visibility 2020-02-03 06:37:25 +11:00
thombergs
b6d167f4ae finished reactive example 2020-02-02 06:45:58 +11:00
Tom Hombergs
ec2bdc9327 Reactive Batch Processing Example 2020-01-31 19:48:06 +11:00
Tom Hombergs
5fe5deca66 added Single.defer to fan out messages over multiple threads 2020-01-31 09:18:02 +11:00
Tom Hombergs
3fb809911a Reactive Batch Processing Example 2020-01-30 21:35:03 +11:00
Tom Hombergs
3461549263 AWS Hello World docker file 2020-01-23 06:59:18 +11:00
Tom Hombergs
9e9b030c3f add README 2019-12-31 08:47:14 +11:00
Tom Hombergs
71af6ee95d add example for Spring Boot profiles 2019-12-31 08:40:49 +11:00
Tom Hombergs
b8e0845744 added missing gradle wrapper 2019-12-14 08:06:21 +11:00
Tom Hombergs
8baf275c8f isolated all modules 2019-12-14 07:59:28 +11:00
Tom Hombergs
92c266586c isolated some modules 2019-12-13 06:38:02 +11:00
Tom Hombergs
802046466f finish startup code example 2019-12-02 06:08:28 +11:00
Tom Hombergs
895d48c163 add startup examples 2019-11-30 07:46:18 +11:00
Tom Hombergs
6667bcde4d added @ConstructorBinding annotation 2019-11-17 06:33:05 +11:00
Tom Hombergs
5975aff396 modified code to match blog post 2019-11-17 06:25:58 +11:00
Tom Hombergs
44fb22ff50 fixed build 2019-11-10 10:29:35 +11:00
Tom Hombergs
dff7cf37f0 added missing gradle wrapper 2019-11-10 09:31:28 +11:00
Tom Hombergs
659a602e97 added code example for static data 2019-11-10 09:18:16 +11:00
Tom Hombergs
d34df7ad4e fixed build 2019-09-25 10:10:02 +02:00
Tom Hombergs
4c1318cc50 added immutable example code 2019-09-25 09:05:26 +02:00
Tom Hombergs
aa09ca31cb added mocking example 2019-09-18 08:03:33 +02:00
Tom Hombergs
7712f41e3e travis skip install 2019-09-03 06:44:50 +02:00
Tom Hombergs
2690f6b14d empty install, single call to each sub module in build-all.sh 2019-09-03 06:43:06 +02:00
Tom Hombergs
fa74e78e5d fixed travis build 2019-09-03 06:30:59 +02:00
Tom Hombergs
2c50fa9a12 fixed travis build 2019-09-03 06:01:01 +02:00
Tom Hombergs
c48921f554 fixed travis build 2019-09-03 05:46:55 +02:00
Tom Hombergs
df9575152f Merge remote-tracking branch 'origin/master' 2019-09-03 05:23:19 +02:00
Tom Hombergs
203c8d053a separated validation module from the rest 2019-09-03 05:23:07 +02:00
Tom Hombergs
9cf783c0a4 fixed URL 2019-08-30 21:14:09 +02:00
Tom Hombergs
9049c0f970 finalized code example for spring boot starter 2019-08-30 06:09:09 +02:00
Tom Hombergs
58c58e02d4 added permission for build.gradle 2019-08-28 21:38:13 +02:00
Tom Hombergs
9f84fcd49f added event-starter to travis build 2019-08-28 21:25:11 +02:00
Tom Hombergs
5646e0f169 added example Spring Boot Starter 2019-08-28 21:21:05 +02:00
Tom Hombergs
43e82f71d2 introduced BDDMockito 2019-07-01 21:39:09 +02:00
Tom Hombergs
8877f30ebc upgraded to Java 11 and fixed build 2019-03-30 08:02:10 +01:00
Tom Hombergs
b0ae545cd8 fixed compile error 2019-03-30 06:54:26 +01:00
Tom Hombergs
fb0aeb0a36 paging examples 2019-03-30 06:47:49 +01:00
Tom Hombergs
b3532e2f1e paging 2019-03-28 06:09:03 +01:00
Tom Hombergs
2c8d828ca1 pagination example 2019-03-25 15:35:32 +01:00
Tom Hombergs
d660463d74 added code examples for @ConfigurationProperties 2019-03-17 11:52:32 +01:00
Tom Hombergs
3207a3bc82 added README 2019-03-07 21:23:10 +01:00
Tom Hombergs
14039b84d5 added missing gradle files 2019-03-07 21:20:21 +01:00
Tom Hombergs
7528cd0069 code examples for Spring Boot @Conditionals 2019-03-07 06:44:14 +01:00
Tom Hombergs
f7e158a83c added missing flyway and liquibase scripts 2019-03-01 16:24:24 +01:00
Tom Hombergs
d01cccb574 refactored @SpringBootConfiguration examples 2019-03-01 11:52:52 +01:00
Tom Hombergs
71fad7e002 added code examples for @SpringBootTest article 2019-02-24 06:55:20 +01:00
Tom Hombergs
93f8074914 added missing test classes 2019-02-03 06:20:48 +01:00
Tom Hombergs
5ecb279f64 fixed build 2019-02-03 06:19:55 +01:00
Tom Hombergs
d5713b6ca3 code examples for @DataJpaTest 2019-02-03 06:15:17 +01:00
Tom Hombergs
188b53f526 moved jacocoTestReport to test instead of build phase 2019-01-31 21:00:14 +01:00
Tom Hombergs
86e8458980 minor changes 2019-01-21 06:36:50 +01:00
Tom Hombergs
8d8937c6bf tutorial about testing web controllers 2019-01-21 05:20:05 +01:00
Tom Hombergs
5352631522 Update README.md 2019-01-12 15:39:17 +01:00
Tom Hombergs
5f85a1cf67 added spring boot unit test example 2019-01-12 08:28:18 +01:00
Tom Hombergs
576a726cbd refactoring 2018-11-14 22:49:31 +01:00
Tom Hombergs
027abc1271 fixed node messaging example 2018-11-14 21:11:30 +01:00
Tom Hombergs
037dca0127 added .gitignore 2018-11-12 23:15:57 +01:00
Tom Hombergs
b245679a55 replaced broker config with local pact 2018-11-12 23:02:54 +01:00
Tom Hombergs
375b46309b added example for a node message provider 2018-11-12 23:00:33 +01:00
Tom Hombergs
38d9b4ee72 removed trailing comma 2018-11-11 21:56:28 +01:00
Tom Hombergs
93edb8c6f8 renamed test script 2018-11-11 21:55:48 +01:00
Tom Hombergs
a4c3f21391 node message consumer example 2018-11-11 21:55:18 +01:00
Tom Hombergs
382c307ae9 fixes for graphql consumer 2018-11-10 11:45:39 +01:00
Tom Hombergs
3e2c23cf45 added graphql provider test 2018-11-08 21:49:22 +01:00
Tom Hombergs
01242c5674 added missing setup script 2018-11-08 21:05:01 +01:00
Tom Hombergs
a58b5e670d added graphql consumer example 2018-11-08 16:31:46 +01:00
Tom Hombergs
a5a1381e1b added article link 2018-10-28 08:31:35 +01:00
Tom Hombergs
da30072399 re-created example repeating tutorial steps 2018-10-28 04:46:48 +01:00
Tom Hombergs
65c49a3ea7 added pact-node-provider example 2018-10-28 04:17:28 +01:00
Tom Hombergs
5fb2d69ca7 removed users endpoint 2018-10-27 06:02:57 +02:00
Tom Hombergs
a2df3dde0b basic heroes endpoint 2018-10-27 06:00:02 +02:00
Tom Hombergs
ff2030eafd basic express server 2018-10-27 05:47:23 +02:00
Tom Hombergs
4e45af2fbd basic express server 2018-10-27 05:47:06 +02:00
Tom Hombergs
8b384c602b updated README 2018-10-25 21:10:22 +02:00
Tom Hombergs
c11403d8fa added react pact example 2018-10-25 13:31:45 +02:00
Tom Hombergs
e8786b1bc3 ignored failing test due to complicated CI setup 2018-10-14 15:57:25 +02:00
Tom Hombergs
dab12b2c3c ignored failing test due to complicated CI setup 2018-10-14 15:37:33 +02:00
Tom Hombergs
31515b8a35 added missing classes 2018-10-14 15:07:06 +02:00
Tom Hombergs
aa05723b18 removed local jdk11 reference 2018-10-14 14:58:54 +02:00
Tom Hombergs
cac0c62b71 fixed jacoco build 2018-10-14 14:54:50 +02:00
Tom Hombergs
24d00fae37 Spring Boot Bean Validation example 2018-10-14 14:48:57 +02:00
Tom Hombergs
03f1680103 added jacoco code example 2018-10-05 21:06:13 +02:00
Tom Hombergs
884546b69c added dependency to spring-cloud-contract-provider 2018-10-02 06:42:40 +02:00
Tom Hombergs
be8be2efc4 added --info to travis build 2018-10-02 06:21:48 +02:00
Tom Hombergs
0e2f10862e changed port 2018-10-02 06:11:47 +02:00
Tom Hombergs
441cd4f801 updated gradle wrapper and activated junit 5 tests in gradle build 2018-09-15 12:08:15 +02:00
Tom Hombergs
de7fa53d55 refactored message pact examples 2018-09-13 06:24:27 +02:00
Tom Hombergs
b4bec09a3e updated readmes 2018-09-10 21:45:22 +02:00
Tom Hombergs
818e162c7f Merge branch 'pact-message-provider' 2018-09-10 21:42:04 +02:00
Tom Hombergs
a18ad84da2 working (but awkward) example of a message provider test 2018-09-10 21:41:49 +02:00
Tom Hombergs
d60f95b6c0 added readme with link to article 2018-08-31 09:00:24 +02:00
Tom Hombergs
52bc347d12 added object mother with fluent builder example 2018-08-31 08:59:04 +02:00
Tom Hombergs
5e93dcb84b updated messaging example for cleaner separation of publishing and converting messages 2018-08-26 22:58:14 +02:00
Tom Hombergs
3b0eb77b5d example for message provider with pact (not working yet) 2018-08-24 11:16:30 +02:00
Tom Hombergs
a9c9faba1d added example for a message consumer test with pact 2018-08-22 21:32:54 +02:00
Tom Hombergs
8672d163e2 updated spring-cloud-contract exampled to current versions 2018-08-16 22:27:41 +02:00
Tom Hombergs
3eefd80192 removed the manual truncating 2018-08-11 21:35:37 +02:00
Tom Hombergs
5a36a2365e updated pact provider example to JUnit 5 and Spring Boot 2 2018-08-11 16:47:03 +02:00
Tom Hombergs
5f675f91a8 changed logging configuration 2018-08-11 00:22:08 +02:00
Tom Hombergs
3559378d1a logging example with Spring Boot and Logback 2018-08-11 00:00:15 +02:00
Tom Hombergs
b72ab0d580 Merge pull request #8 from thombergs/cdc-updates
updated pact-feign example to Spring Boot 2 and JUnit 5
2018-08-10 00:14:21 +02:00
Tom Hombergs
ed45066496 updated pact-feign example to Spring Boot 2 and JUnit 5 2018-08-10 00:09:11 +02:00
Tom Hombergs
ddb3e08ee5 Merge pull request #7 from thombergs/logging-format
removed deprecated examples
2018-08-04 09:21:55 +02:00
Tom Hombergs
888a89e541 removed deprecated examples
added logging example
2018-08-04 09:03:43 +02:00
Tom Hombergs
fb148fcd14 added article link 2018-06-11 22:33:27 +02:00
Tom Hombergs
a07407dc9f changed CI config to skip build on docs-only-changes 2018-06-11 22:15:15 +02:00
Tom Hombergs
5e4c962b21 Merge pull request #6 from thombergs/spring-boot-testing
Spring boot testing
2018-05-27 22:31:06 +02:00
Tom Hombergs
500ee0a04f ignored test because it cannot run on CI 2018-05-27 21:55:26 +02:00
Tom Hombergs
4d67dd44d0 fix 2018-05-27 21:10:15 +02:00
Tom Hombergs
a4d70d5835 Example application for structuring a Spring Boot app 2018-05-27 20:55:24 +02:00
Tom Hombergs
f2b83425ac inital checking for spring-boot-test example project 2018-05-20 20:38:28 +02:00
Tom Hombergs
fe9e15b813 Update README.md 2018-03-20 21:48:25 +01:00
Tom Hombergs
9e66b87b18 Update README.md 2018-03-20 21:47:40 +01:00
Tom Hombergs
9bae6d560a Update README.md 2018-03-20 21:36:10 +01:00
Tom Hombergs
9d58b1323e Update README.md 2018-03-20 21:32:59 +01:00
Tom Hombergs
612863cb2a Update README.md 2018-03-20 21:32:10 +01:00
Tom Hombergs
f854262f80 changed package name 2018-03-18 22:44:54 +01:00
Tom Hombergs
11d70c0c12 pact feign consumer example 2018-03-17 12:58:11 +01:00
Tom Hombergs
4e26aa589e moved deprecated modules 2018-03-17 12:04:05 +01:00
Tom Hombergs
5d76ba1375 added example for modularizing a Spring Boot application 2018-02-01 23:32:01 +01:00
Tom Hombergs
1d50d8f513 ignoring offline cdc test 2018-01-18 22:48:20 +01:00
Tom Hombergs
0e4b84b72e polishing spring-cloud-contract consumer example 2018-01-18 22:19:42 +01:00
Tom Hombergs
60de5fafac Merge remote-tracking branch 'origin/master' 2018-01-17 21:45:54 +01:00
Tom Hombergs
21b2bccbad added spring cloud contract consumer example 2018-01-17 21:45:43 +01:00
Tom Hombergs
f4a0addd24 Update README.md 2018-01-15 16:40:22 +01:00
Tom Hombergs
a5e7696775 added pact contract 2018-01-13 12:53:21 +01:00
Tom Hombergs
d68a99e92b polishing and readme 2018-01-10 00:09:05 +01:00
Tom Hombergs
3a6149b190 added links to blog posts 2018-01-08 22:51:48 +01:00
Tom Hombergs
2c775cdaa2 refactored gradle project structure 2018-01-08 22:32:55 +01:00
Tom Hombergs
387d9a5711 Merge pull request #5 from thombergs/spring-cloud-contract
added example with spring cloud contract
2018-01-08 22:05:48 +01:00
Tom Hombergs
99f6e3fb71 added example with spring cloud contract 2018-01-08 20:23:04 +01:00
Tom Hombergs
7bce17b055 upgraded pact-web and pact-jvm-provider-spring 2018-01-02 23:01:38 +01:00
Tom Hombergs
c9ae2ca600 updated pact between Angular app and Spring provider 2017-12-31 15:25:57 +01:00
Tom Hombergs
5f46041ccd updated gradle 2017-12-31 15:25:06 +01:00
Tom Hombergs
d139eecb2d updated angular-pact example 2017-12-10 20:24:55 +01:00
Tom Hombergs
671f5e501e moved angular-pact to pact-angular 2017-12-09 13:30:06 +01:00
Tom Hombergs
a885f98f2f fixed dependency to pact-node 2017-11-13 23:17:08 +01:00
Tom Hombergs
f10c8d2bfc added angular pact example 2017-11-06 23:16:47 +01:00
Tom Hombergs
b93c05fbe5 fixed unit test 2017-10-18 21:01:54 +02:00
Tom Hombergs
8e69a627cf Merge pull request #3 from thombergs/angular-pact
Angular pact
2017-10-18 20:32:07 +02:00
Tom Hombergs
c8af61e065 Merge remote-tracking branch 'origin/master' 2017-10-10 23:14:27 +02:00
Tom Hombergs
d1c5091ee8 added junit examples 2017-10-10 23:14:19 +02:00
Tom Hombergs
83b7eb429e Merge pull request #1 from snicoll/patch-1
Remove unnecessary EnableAutoConfiguration
2017-09-19 20:23:18 +02:00
Stéphane Nicoll
3e866fbe44 Remove unnecessary EnableAutoConfiguration 2017-09-19 08:41:04 +02:00
808 changed files with 44436 additions and 1630 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
**/.idea/
**/*.iml

View File

@@ -1,7 +1,18 @@
before_install:
- chmod +x gradlew
- chmod +x build-all.sh
- |
if ! git diff --name-only $TRAVIS_COMMIT_RANGE | grep -qvE '(.md)|^(LICENSE)'
then
echo "Not running CI since only docs were changed."
exit
fi
install: skip
script:
- ./build-all.sh
language: java
jdk:
- oraclejdk8
- oraclejdk11

View File

@@ -2,7 +2,20 @@
[![Travis CI Status](https://travis-ci.org/thombergs/code-examples.svg?branch=master)](https://travis-ci.org/thombergs/code-examples)
This repo contains example projects which show how to use different java technologies.
This repo contains example projects which show how to use different (not only) Java technologies.
The examples are usually accompanied by a blog post on [https://reflectoring.io](https://reflectoring.io).
See the READMEs in each subdirectory of this repo for more information.
See the READMEs in each subdirectory of this repo for more information on each module.
## Java Modules
All Java modules require **Java 11** to compile and run.
### Building with Gradle
Each module should be an independent build and can be built by calling `./gradlew clean build` in the module directory.
All modules are listed in [build-all.sh](build-all.sh) to run in the CI pipeline.
### Non-Java Modules
Some folders contain non-Java projects. For those, refer to the README within the module folder.

32
aws/aws-hello-world/.gitignore vendored Normal file
View File

@@ -0,0 +1,32 @@
HELP.md
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**
!**/src/test/**
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
out/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/

View File

@@ -0,0 +1,5 @@
FROM openjdk:8-jdk-alpine
ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
EXPOSE 8080

View File

@@ -0,0 +1,24 @@
plugins {
id 'org.springframework.boot' version '2.2.4.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
}
group = 'io.reflectoring'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '1.8'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
}
test {
useJUnitPlatform()
}

Binary file not shown.

View File

@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.0.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.5.1-bin.zip

172
aws/aws-hello-world/gradlew vendored Executable file
View File

@@ -0,0 +1,172 @@
#!/usr/bin/env sh
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
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
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
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
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=$(save "$@")
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
cd "$(dirname "$0")"
fi
exec "$JAVACMD" "$@"

View File

View File

@@ -0,0 +1 @@
rootProject.name = 'aws-hello-world'

View File

@@ -0,0 +1,13 @@
package io.reflectoring.awshelloworld;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class AwsHelloWorldApplication {
public static void main(String[] args) {
SpringApplication.run(AwsHelloWorldApplication.class, args);
}
}

View File

@@ -0,0 +1,14 @@
package io.reflectoring.awshelloworld;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloWorldController {
@GetMapping("/hello")
public String helloWorld(){
return "Hello AWS!";
}
}

View File

@@ -0,0 +1 @@

View File

@@ -0,0 +1,13 @@
package io.reflectoring.awshelloworld;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class AwsHelloWorldApplicationTests {
@Test
void contextLoads() {
}
}

68
build-all.sh Executable file
View File

@@ -0,0 +1,68 @@
#!/bin/bash
MAIN_DIR=$PWD
build_gradle_module() {
MODULE_PATH=$1
echo ""
echo "+++"
echo "+++ BUILDING MODULE $MODULE_PATH"
echo "+++"
cd $MODULE_PATH && {
chmod +x gradlew
./gradlew clean build --info --stacktrace
if [ $? -ne 0 ]
then
echo ""
echo "+++"
echo "+++ BUILDING MODULE $MODULE_PATH FAILED"
echo "+++"
exit 1
else
echo ""
echo "+++"
echo "+++ BUILDING MODULE $MODULE_PATH SUCCESSFUL"
echo "+++"
fi
cd $MAIN_DIR
}
}
chmod +x gradlew
build_gradle_module "spring-boot/spring-boot-springdoc"
build_gradle_module "reactive"
build_gradle_module "junit/assumptions"
build_gradle_module "logging"
build_gradle_module "pact/pact-feign-consumer"
# currently disabled since the consumer build won't run
# build_gradle_module "pact/pact-message-consumer"
# build_gradle_module "pact/pact-message-producer"
build_gradle_module "pact/pact-spring-provider"
build_gradle_module "patterns"
build_gradle_module "spring-boot/conditionals"
build_gradle_module "spring-boot/configuration"
build_gradle_module "spring-boot/mocking"
build_gradle_module "spring-boot/modular"
build_gradle_module "spring-boot/paging"
build_gradle_module "spring-boot/rabbitmq-event-brokering"
build_gradle_module "spring-boot/spring-boot-logging"
build_gradle_module "spring-boot/spring-boot-testing"
build_gradle_module "spring-boot/starter"
build_gradle_module "spring-boot/startup"
build_gradle_module "spring-boot/static"
build_gradle_module "spring-boot/validation"
build_gradle_module "spring-boot/profiles"
build_gradle_module "spring-cloud/feign-with-spring-data-rest"
build_gradle_module "spring-cloud/sleuth-downstream-service"
build_gradle_module "spring-cloud/sleuth-upstream-service"
build_gradle_module "spring-cloud/spring-cloud-contract-consumer"
build_gradle_module "spring-cloud/spring-cloud-contract-provider"
build_gradle_module "spring-data/spring-data-rest-associations"
build_gradle_module "spring-data/spring-data-rest-springfox"
build_gradle_module "tools/jacoco"
echo ""
echo "+++"
echo "+++ ALL MODULES SUCCESSFUL"
echo "+++"

View File

@@ -1,6 +0,0 @@
#Sun Jul 30 16:58:54 CEST 2017
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.5.1-all.zip

View File

@@ -0,0 +1,3 @@
# Examples with JUnit 4 and JUnit 5
Have a look at [the code](/src/test/java/com/example/demo/)

View File

@@ -0,0 +1,28 @@
buildscript {
repositories {
mavenCentral()
}
}
apply plugin: 'java'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 11
repositories {
mavenLocal()
mavenCentral()
}
ext {
springCloudVersion = 'Dalston.SR2'
}
dependencies {
testCompile 'org.junit.jupiter:junit-jupiter-engine:5.0.1'
testCompile 'junit:junit:4.12'
}
test {
useJUnitPlatform()
}

View File

@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-3.5.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-bin.zip

View File

@@ -0,0 +1,15 @@
package com.example.demo.connectionchecking;
public class ConnectionChecker {
private String uri;
public ConnectionChecker(String uri) {
this.uri = uri;
}
public boolean connect() {
return false;
}
}

View File

@@ -0,0 +1,31 @@
package com.example.demo.connectionchecking.junit4;
import com.example.demo.connectionchecking.ConnectionChecker;
import org.junit.AssumptionViolatedException;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
public class AssumingConnection implements TestRule {
private ConnectionChecker checker;
public AssumingConnection(ConnectionChecker checker) {
this.checker = checker;
}
@Override
public Statement apply(Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
if (!checker.connect()) {
throw new AssumptionViolatedException("Could not connect. Skipping test!");
} else {
base.evaluate();
}
}
};
}
}

View File

@@ -0,0 +1,19 @@
package com.example.demo.connectionchecking.junit4;
import com.example.demo.connectionchecking.ConnectionChecker;
import org.junit.ClassRule;
import org.junit.Test;
import static org.junit.Assert.fail;
public class ConnectionCheckingJunit4Test {
@ClassRule
public static AssumingConnection assumingConnection = new AssumingConnection(new ConnectionChecker("http://my.integration.system"));
@Test
public void testOnlyWhenConnected() {
fail("Booh!");
}
}

View File

@@ -0,0 +1,14 @@
package com.example.demo.connectionchecking.junit5;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import org.junit.jupiter.api.extension.ExtendWith;
@Retention(RetentionPolicy.RUNTIME)
@ExtendWith(AssumeConnectionCondition.class)
public @interface AssumeConnection {
String uri();
}

View File

@@ -0,0 +1,28 @@
package com.example.demo.connectionchecking.junit5;
import java.util.Optional;
import com.example.demo.connectionchecking.ConnectionChecker;
import org.junit.jupiter.api.extension.ConditionEvaluationResult;
import org.junit.jupiter.api.extension.ExecutionCondition;
import org.junit.jupiter.api.extension.ExtensionContext;
import static org.junit.platform.commons.util.AnnotationUtils.findAnnotation;
public class AssumeConnectionCondition implements ExecutionCondition {
@Override
public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
Optional<AssumeConnection> annotation = findAnnotation(context.getElement(), AssumeConnection.class);
if (annotation.isPresent()) {
String uri = annotation.get().uri();
ConnectionChecker checker = new ConnectionChecker(uri);
if (!checker.connect()) {
return ConditionEvaluationResult.disabled(String.format("Could not connect to '%s'. Skipping test!", uri));
} else {
return ConditionEvaluationResult.enabled(String.format("Successfully connected to '%s'. Continuing test!", uri));
}
}
return ConditionEvaluationResult.enabled("No AssumeConnection annotation found. Continuing test.");
}
}

View File

@@ -0,0 +1,14 @@
package com.example.demo.connectionchecking.junit5;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.fail;
@AssumeConnection(uri = "http://my.integration.system")
public class ConnectionCheckingJunit5Test {
@Test
public void testOnlyWhenConnected() {
fail("Booh!");
}
}

View File

@@ -0,0 +1,120 @@
{
"provider": {
"name": "customerServiceProvider"
},
"consumer": {
"name": "addressClient"
},
"interactions": [
{
"description": "a request to the address collection resource",
"request": {
"method": "GET",
"path": "/addresses/"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/hal+json"
},
"body": {
"_embedded": {
"addresses": [
{
"street": "Elm Street",
"_links": {
"self": {
"href": "http://localhost:8080/addresses/1"
},
"address": {
"href": "http://localhost:8080/addresses/1"
},
"customer": {
"href": "http://localhost:8080/addresses/1/customer"
}
}
},
{
"street": "High Street",
"_links": {
"self": {
"href": "http://localhost:8080/addresses/2"
},
"address": {
"href": "http://localhost:8080/addresses/2"
},
"customer": {
"href": "http://localhost:8080/addresses/2/customer"
}
}
}
]
},
"_links": {
"self": {
"href": "http://localhost:8080/addresses{?page,size,sort}",
"templated": true
},
"profile": {
"href": "http://localhost:8080/profile/addresses"
},
"search": {
"href": "http://localhost:8080/addresses/search"
}
},
"page": {
"size": 20,
"totalElements": 2,
"totalPages": 1,
"number": 0
}
}
},
"providerStates": [
{
"name": "a collection of 2 addresses"
}
]
},
{
"description": "a request to the address resource",
"request": {
"method": "GET",
"path": "/addresses/1"
},
"response": {
"status": 200,
"headers": {
"Content-Type": "application/hal+json"
},
"body": {
"street": "Elm Street",
"_links": {
"self": {
"href": "http://localhost:8080/addresses/1"
},
"address": {
"href": "http://localhost:8080/addresses/1"
},
"customer": {
"href": "http://localhost:8080/addresses/1/customer"
}
}
}
},
"providerStates": [
{
"name": "a single address"
}
]
}
],
"metadata": {
"pact-specification": {
"version": "3.0.0"
},
"pact-jvm": {
"version": "3.5.2"
}
}
}

7
logging/README.md Normal file
View File

@@ -0,0 +1,7 @@
# Logging Code Examples
## Related Blog Articles
* [Use Logging Levels Consistently](https://reflectoring.io/logging-levels/)
* [Use a Human-Readable Logging Format](https://reflectoring.io/logging-format/)
* [Configuring a Human-Readable Logging Format with Logback and Descriptive Logger](https://reflectoring.io/logging-format-logback/)

20
logging/build.gradle Normal file
View File

@@ -0,0 +1,20 @@
apply plugin: 'java'
buildscript {
repositories {
mavenLocal()
jcenter()
}
}
repositories {
mavenLocal()
jcenter()
}
dependencies {
compile("ch.qos.logback:logback-classic:1.2.3")
compile("io.reflectoring:descriptive-logger:1.0")
testCompile("org.junit.jupiter:junit-jupiter-engine:5.0.1")
}

View File

@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-bin.zip

View File

View File

@@ -0,0 +1,18 @@
package io.reflectoring.logging;
import io.reflectoring.descriptivelogger.LoggerFactory;
import org.junit.jupiter.api.Test;
public class LoggingFormatTest {
private MyLogger logger = LoggerFactory.getLogger(MyLogger.class, LoggingFormatTest.class);
@Test
public void testLogPattern(){
Thread.currentThread().setName("very-long-thread-name");
logger.logDebugMessage();
Thread.currentThread().setName("short");
logger.logInfoMessage();
logger.logMessageWithLongId();
}
}

View File

@@ -0,0 +1,19 @@
package io.reflectoring.logging;
import io.reflectoring.descriptivelogger.DescriptiveLogger;
import io.reflectoring.descriptivelogger.LogMessage;
import org.slf4j.event.Level;
@DescriptiveLogger
public interface MyLogger {
@LogMessage(level=Level.DEBUG, message="This is a DEBUG message.", id=14556)
void logDebugMessage();
@LogMessage(level=Level.INFO, message="This is an INFO message.", id=5456)
void logInfoMessage();
@LogMessage(level=Level.ERROR, message="This is an ERROR message with a very long ID.", id=1548654)
void logMessageWithLongId();
}

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<conversionRule conversionWord="truncatedThread"
converterClass="io.reflectoring.logging.TruncatedThreadConverter" />
<conversionRule conversionWord="truncatedLogger"
converterClass="io.reflectoring.logging.TruncatedLoggerConverter" />
<!-- Appender to log to console -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd} | %d{HH:mm:ss.SSS} | %-20.20thread | %5p | %-25.25logger{25} | %12(ID: %8mdc{id}) | %m%n</pattern>
<charset>utf8</charset>
</encoder>
</appender>
<root level="DEBUG">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>

View File

@@ -1,15 +0,0 @@
# Consumer-Driven-Contract Test for a Feign Consumer
This repo contains an example of consumer-driven-contract testing for a Feign client
that consumes a REST API provided by the module `pact-spring-data-rest-provider`.
The contract is created and verified with [Pact](https://docs.pact.io/).
## Companion Blog Post
The Companion Blog Post to this project can be found [here](https://reflectoring.io/consumer-driven-contracts-with-pact-feign-spring-data-rest/).
## Running the application
The interesting part in this code base is the class `ConsumerPactVerificationTest`.
You can run the tests with `gradlew test` on Windows or `./gradlew test` on Unix.

View File

@@ -1,46 +0,0 @@
buildscript {
ext {
springBootVersion = '1.5.4.RELEASE'
}
repositories {
mavenCentral()
}
dependencies {
classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
}
}
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
repositories {
mavenLocal()
mavenCentral()
}
ext {
springCloudVersion = 'Dalston.SR2'
}
dependencies {
compile('org.springframework.cloud:spring-cloud-starter-feign')
// locking transitive guava version to work around "java.lang.IllegalAccessError: tried to access method com.google.common.collect.Lists.cartesianProduct"
compile('com.google.guava:guava:22.0')
compile('org.springframework.boot:spring-boot-starter-hateoas')
testCompile group: 'au.com.dius', name: 'pact-jvm-consumer-junit_2.11', version: '3.5.2'
testCompile('org.springframework.boot:spring-boot-starter-test')
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
bootRun{
jvmArgs = ["-Xdebug", "-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5006"]
}

View File

@@ -1,19 +0,0 @@
package com.example.demo;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.Resources;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@FeignClient(value = "addresses", path = "/addresses")
public interface AddressClient {
@RequestMapping(method = RequestMethod.GET, path = "/")
Resources<Address> getAddresses();
@RequestMapping(method = RequestMethod.GET, path = "/{id}")
Resource<Address> getAddress(@PathVariable("id") long id);
}

View File

@@ -1,19 +0,0 @@
package com.example.demo;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.Resources;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@FeignClient(value = "customers", path = "/customers_mto")
public interface CustomerClient {
@RequestMapping(method = RequestMethod.GET, value = "/")
Resources<Customer> getCustomers();
@RequestMapping(method = RequestMethod.GET, value = "/{id}")
Resource<Customer> getCustomer(@PathVariable("id") long id);
}

View File

@@ -1,16 +0,0 @@
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
import org.springframework.hateoas.config.EnableHypermediaSupport;
@SpringBootApplication
@EnableFeignClients
@EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL)
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}

View File

@@ -1,137 +0,0 @@
package com.example.demo;
import au.com.dius.pact.consumer.Pact;
import au.com.dius.pact.consumer.PactProviderRuleMk2;
import au.com.dius.pact.consumer.PactVerification;
import au.com.dius.pact.consumer.dsl.PactDslWithProvider;
import au.com.dius.pact.model.RequestResponsePact;
import org.apache.http.entity.ContentType;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.hateoas.Resource;
import org.springframework.hateoas.Resources;
import org.springframework.test.context.junit4.SpringRunner;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest(properties = {
// overriding provider address
"addresses.ribbon.listOfServers: localhost:8888"
})
public class ConsumerPactVerificationTest {
@Rule
public PactProviderRuleMk2 stubProvider = new PactProviderRuleMk2("customerServiceProvider", "localhost", 8888, this);
@Autowired
private AddressClient addressClient;
@Pact(state = "a collection of 2 addresses", provider = "customerServiceProvider", consumer = "addressClient")
public RequestResponsePact createAddressCollectionResourcePact(PactDslWithProvider builder) {
return builder
.given("a collection of 2 addresses")
.uponReceiving("a request to the address collection resource")
.path("/addresses/")
.method("GET")
.willRespondWith()
.status(200)
.body("{\n" +
" \"_embedded\": {\n" +
" \"addresses\": [\n" +
" {\n" +
" \"street\": \"Elm Street\",\n" +
" \"_links\": {\n" +
" \"self\": {\n" +
" \"href\": \"http://localhost:8080/addresses/1\"\n" +
" },\n" +
" \"address\": {\n" +
" \"href\": \"http://localhost:8080/addresses/1\"\n" +
" },\n" +
" \"customer\": {\n" +
" \"href\": \"http://localhost:8080/addresses/1/customer\"\n" +
" }\n" +
" }\n" +
" },\n" +
" {\n" +
" \"street\": \"High Street\",\n" +
" \"_links\": {\n" +
" \"self\": {\n" +
" \"href\": \"http://localhost:8080/addresses/2\"\n" +
" },\n" +
" \"address\": {\n" +
" \"href\": \"http://localhost:8080/addresses/2\"\n" +
" },\n" +
" \"customer\": {\n" +
" \"href\": \"http://localhost:8080/addresses/2/customer\"\n" +
" }\n" +
" }\n" +
" }\n" +
" ]\n" +
" },\n" +
" \"_links\": {\n" +
" \"self\": {\n" +
" \"href\": \"http://localhost:8080/addresses{?page,size,sort}\",\n" +
" \"templated\": true\n" +
" },\n" +
" \"profile\": {\n" +
" \"href\": \"http://localhost:8080/profile/addresses\"\n" +
" },\n" +
" \"search\": {\n" +
" \"href\": \"http://localhost:8080/addresses/search\"\n" +
" }\n" +
" },\n" +
" \"page\": {\n" +
" \"size\": 20,\n" +
" \"totalElements\": 2,\n" +
" \"totalPages\": 1,\n" +
" \"number\": 0\n" +
" }\n" +
"}", "application/hal+json")
.toPact();
}
@Pact(state = "a single address", provider = "customerServiceProvider", consumer = "addressClient")
public RequestResponsePact createAddressResourcePact(PactDslWithProvider builder) {
return builder
.given("a single address")
.uponReceiving("a request to the address resource")
.path("/addresses/1")
.method("GET")
.willRespondWith()
.status(200)
.body("{\n" +
" \"street\": \"Elm Street\",\n" +
" \"_links\": {\n" +
" \"self\": {\n" +
" \"href\": \"http://localhost:8080/addresses/1\"\n" +
" },\n" +
" \"address\": {\n" +
" \"href\": \"http://localhost:8080/addresses/1\"\n" +
" },\n" +
" \"customer\": {\n" +
" \"href\": \"http://localhost:8080/addresses/1/customer\"\n" +
" }\n" +
" }\n" +
"}", "application/hal+json")
.toPact();
}
@Test
@PactVerification(fragment = "createAddressCollectionResourcePact")
public void verifyAddressCollectionPact() {
Resources<Address> addresses = addressClient.getAddresses();
assertThat(addresses).hasSize(2);
}
@Test
@PactVerification(fragment = "createAddressResourcePact")
public void verifyAddressPact() {
Resource<Address> address = addressClient.getAddress(1L);
assertThat(address).isNotNull();
}
}

View File

@@ -1,16 +0,0 @@
# Consumer-Driven-Contract Test for a Spring Data Rest Provider
This repo contains an example of consumer-driven-contract testing for a Spring
Data REST API provider. The corresponding consumer to the contract is
implemented in the module `pact-feign-consumer`.
The contract is created and verified with [Pact](https://docs.pact.io/).
## Companion Blog Post
The Companion Blog Post to this project can be found [here](https://reflectoring.io/consumer-driven-contracts-with-pact-feign-spring-data-rest/).
## Running the application
The interesting part in this code base is the class `ProviderPactVerificationTest`.
You can run the tests with `gradlew test` on Windows or `./gradlew test` on Unix.

View File

@@ -1,43 +0,0 @@
package com.example.demo;
import javax.persistence.*;
@Entity
public class Address {
@GeneratedValue
@Id
private Long id;
@Column
private String street;
@ManyToOne
private Customer customer;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}

View File

@@ -1,14 +0,0 @@
package com.example.demo;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;
import org.springframework.hateoas.Resources;
import java.util.List;
@RepositoryRestResource(path = "addresses")
public interface AddressRepository extends PagingAndSortingRepository<Address, Long> {
List<Address> findByCustomerId(long customerId);
}

View File

@@ -1,30 +0,0 @@
package com.example.demo;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class Customer {
@Id
@GeneratedValue
private long id;
@Column
private String name;
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@@ -1,7 +0,0 @@
spring.datasource.url=jdbc:h2:mem:AZ;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.h2.console.enabled=true
logging.level.org.hibernate.SQL=OFF

View File

@@ -1,41 +0,0 @@
package com.example.demo;
import au.com.dius.pact.provider.junit.PactRunner;
import au.com.dius.pact.provider.junit.Provider;
import au.com.dius.pact.provider.junit.State;
import au.com.dius.pact.provider.junit.loader.PactFolder;
import au.com.dius.pact.provider.junit.target.HttpTarget;
import au.com.dius.pact.provider.junit.target.Target;
import au.com.dius.pact.provider.junit.target.TestTarget;
import com.example.framework.DatabaseStateHolder;
import com.example.framework.SpringBootStarter;
import org.junit.ClassRule;
import org.junit.runner.RunWith;
@RunWith(PactRunner.class)
@Provider("customerServiceProvider")
@PactFolder("../pact-feign-consumer/target/pacts")
public class ProviderPactVerificationTest {
@ClassRule
public static SpringBootStarter appStarter = SpringBootStarter.builder()
.withApplicationClass(DemoApplication.class)
.withArgument("--spring.config.location=classpath:/application-pact.properties")
.withDatabaseState("single-address", "/initial-schema.sql", "/single-address.sql")
.withDatabaseState("address-collection", "/initial-schema.sql", "/address-collection.sql")
.build();
@State("a single address")
public void toSingleAddressState() {
DatabaseStateHolder.setCurrentDatabaseState("single-address");
}
@State("a collection of 2 addresses")
public void toAddressCollectionState() {
DatabaseStateHolder.setCurrentDatabaseState("address-collection");
}
@TestTarget
public final Target target = new HttpTarget(8080);
}

View File

@@ -1,32 +0,0 @@
package com.example.framework;
/**
* Defines a state of the database, which is defined by a set of SQL scripts.
*/
public class DatabaseState {
private final String stateName;
private final String[] sqlscripts;
/**
* Constructor.
*
* @param stateName unique name of this database state.
* @param sqlscripts paths to SQL scripts within the classpath. These scripts will be executed to put the
* database into the database state described by this object.
*/
public DatabaseState(String stateName, String... sqlscripts) {
this.stateName = stateName;
this.sqlscripts = sqlscripts;
}
public String getStateName() {
return stateName;
}
public String[] getSqlscripts() {
return sqlscripts;
}
}

View File

@@ -1,36 +0,0 @@
package com.example.framework;
/**
* Holds the current database state.
* <p/>
* TODO: replace the static state variable with a thread-safe alternative. Potentially use a special HTTP header
* that is intercepted by a Bean defined in {@link PactDatabaseStatesAutoConfiguration} and sets the database
* state in a {@link ThreadLocal} variable.
*/
public class DatabaseStateHolder {
private static String currentDatabaseState;
/**
* Sets the database to the state with the specified name.
* <p/>
* <strong>WARNING:</strong> the database state is not thread safe. If there are multiple threads accessing
* the database in different states at the same time, apocalypse will come!
*
* @param databaseStateName the name of the {@link DatabaseState} to put the database in.
*/
public static void setCurrentDatabaseState(String databaseStateName) {
currentDatabaseState = databaseStateName;
}
/**
* Returns the name of the current {@link DatabaseState}.
* <p/>
* <strong>WARNING:</strong> the database state is not thread safe. If there are multiple threads accessing
* the database in different states at the same time, apocalypse will come!
*/
public static String getCurrentDatabaseState() {
return currentDatabaseState;
}
}

View File

@@ -1,50 +0,0 @@
package com.example.framework;
import org.springframework.core.io.ClassPathResource;
import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
import javax.annotation.PostConstruct;
import javax.sql.DataSource;
import java.util.List;
/**
* Initializes a {@link DataSource} to a specified List of {@link DatabaseState}s. It is assumed that the {@link DataSource}
* can switch between several states.
*/
public class DatabaseStatesInitializer {
private final DataSource dataSource;
private final List<DatabaseState> databaseStates;
/**
* Constructor.
*
* @param dataSource the {@link DataSource} to execute SQL scripts against.
* @param databaseStates the {@link DatabaseState}s to create within the {@link DataSource}.
*/
public DatabaseStatesInitializer(DataSource dataSource, List<DatabaseState> databaseStates) {
this.dataSource = dataSource;
this.databaseStates = databaseStates;
}
/**
* Executes SQL scripts to initialize the {@link DataSource} with several states.
* <p/>
* For each {@link DatabaseState}, the {@link DatabaseStateHolder} will be called to set the {@link DataSource}
* into that state. Then, the SQL scripts of that {@link DatabaseState} are executed against the {@link DataSource}
* to initialize that state.
*/
@PostConstruct
public void initialize() {
for (DatabaseState databaseState : this.databaseStates) {
DatabaseStateHolder.setCurrentDatabaseState(databaseState.getStateName());
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
for (String script : databaseState.getSqlscripts()) {
populator.addScript(new ClassPathResource(script));
}
populator.execute(this.dataSource);
}
}
}

View File

@@ -1,70 +0,0 @@
package com.example.framework;
import au.com.dius.pact.provider.junit.PactRunner;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import javax.sql.DataSource;
import java.util.HashMap;
import java.util.Map;
/**
* <p>
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration AutoConfiguration} that is activated when
* the pact-jvm-provider-junit module is in the classpath.
* </p>
* <p>
* This configuration provides a {@link DataSource} which allows to switch between multiple database states.
* Each database state is defined by a name and a set of SQL scripts which set the database into the desired state.
* The database states are configured via properties:
* <pre>
* pact.databaseStates.&lt;NAME&gt;=/path/to/script1.sql,/path/to/script2.sql,...
* </pre>
* The NAME of the databaseState can be used with {@link DatabaseStateHolder#setCurrentDatabaseState(String)}
* to set the {@link DataSource} into that state.
* </p>
*/
@Configuration
@ConditionalOnClass(PactRunner.class)
@EnableConfigurationProperties(PactProperties.class)
public class PactDatabaseStatesAutoConfiguration {
@Bean
public DataSource dataSource(PactProperties pactProperties) {
AbstractRoutingDataSource dataSource = new AbstractRoutingDataSource() {
@Override
protected Object determineCurrentLookupKey() {
return DatabaseStateHolder.getCurrentDatabaseState();
}
};
Map<Object, Object> targetDataSources = new HashMap<>();
dataSource.setTargetDataSources(targetDataSources);
// create a DataSource for each DatabaseState
for (DatabaseState databaseState : pactProperties.getDatabaseStatesList()) {
DataSource ds = DataSourceBuilder
.create()
.url(String.format("jdbc:h2:mem:%s;DB_CLOSE_ON_EXIT=FALSE", databaseState.getStateName()))
.driverClassName("org.h2.Driver")
.username("sa")
.password("")
.build();
targetDataSources.put(databaseState.getStateName(), ds);
DatabaseStateHolder.setCurrentDatabaseState(databaseState.getStateName());
}
return dataSource;
}
@Bean
public DatabaseStatesInitializer databaseStatesInitializer(DataSource routingDataSource, PactProperties pactProperties) {
return new DatabaseStatesInitializer(routingDataSource, pactProperties.getDatabaseStatesList());
}
}

View File

@@ -1,64 +0,0 @@
package com.example.framework;
import org.springframework.boot.context.properties.ConfigurationProperties;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Loads the properties "pact.databaseStates.&lt;NAME&gt;" into the Spring environment.
*/
@ConfigurationProperties("pact")
public class PactProperties {
private Map<String, String> databaseStates;
/**
* Retrieves a map with the names of the configured database states as keys and {@link DatabaseState} objects
* as values.
*/
public List<DatabaseState> getDatabaseStatesList() {
List<DatabaseState> databaseStatesList = new ArrayList<>();
for (Map.Entry<String, String> entry : databaseStates.entrySet()) {
String stateName = entry.getKey();
// When reading a property as a Map, as is done for databaseStates, Spring Boot automatically adds numeric
// keys and moves the actual key into the value, separated by a comma. Thus, we have all entries duplicated
// and have to remove the entries with numeric keys.
if (!stateName.matches("^[0-9]+$")) {
String sqlScriptsString = entry.getValue();
String[] sqlScripts = sqlScriptsString.split(",");
databaseStatesList.add(new DatabaseState(stateName, sqlScripts));
}
}
return databaseStatesList;
}
static List<String> toCommandLineArguments(List<DatabaseState> databaseStates) {
List<String> args = new ArrayList<>();
for (DatabaseState databaseState : databaseStates) {
String argString = String.format("--pact.databaseStates.%s=", databaseState.getStateName());
int i = 0;
for (String scriptPath : databaseState.getSqlscripts()) {
argString += String.format("%s", scriptPath);
if (i < databaseState.getSqlscripts().length - 1) {
argString += ",";
}
i++;
}
args.add(argString);
}
return args;
}
public Map<String, String> getDatabaseStates() {
return databaseStates;
}
public void setDatabaseStates(Map<String, String> databaseStates) {
this.databaseStates = databaseStates;
}
}

View File

@@ -1,111 +0,0 @@
package com.example.framework;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ApplicationContext;
import java.util.ArrayList;
import java.util.List;
/**
* <p>
* Starts a Spring Boot application.
* </p>
* <p>
* When included in a JUnit test with the {@link org.junit.ClassRule} annotation as in the example below, the Spring Boot application will be
* started before any of the test methods are run.
* <pre>
* public class MyTest {
*
* &#064;ClassRule
* public static SpringBootStarter starter = SpringBootStarter.builder()
* .withApplicationClass(MyApplication.class)
* ...
* .build();
*
* &#064;Test
* public void test(){
* ...
* }
*
* }
* </pre>
* </p>
*/
public class SpringBootStarter implements TestRule {
private final Class<?> applicationClass;
private final List<String> args;
private final List<DatabaseState> databaseStates;
/**
* Constructor.
*
* @param applicationClass the Spring Boot application class.
* @param databaseStates list containing {@link DatabaseState} objects, each describing a database state
* in form of one or more SQL scripts.
* @param args the command line arguments the application should be started with.
*/
public SpringBootStarter(Class<?> applicationClass, List<DatabaseState> databaseStates, List<String> args) {
this.args = args;
this.applicationClass = applicationClass;
this.databaseStates = databaseStates;
}
@Override
public Statement apply(Statement base, Description description) {
return new Statement() {
@Override
public void evaluate() throws Throwable {
List<String> args = new ArrayList<>();
args.addAll(SpringBootStarter.this.args);
args.addAll(PactProperties.toCommandLineArguments(SpringBootStarter.this.databaseStates));
ApplicationContext context = SpringApplication.run(SpringBootStarter.this.applicationClass, args.toArray(new String[]{}));
base.evaluate();
SpringApplication.exit(context);
}
};
}
/**
* Creates a builder that provides a fluent API to create a new {@link SpringBootStarter} instance.
*/
public static SpringBootStarterBuilder builder() {
return new SpringBootStarterBuilder();
}
public static class SpringBootStarterBuilder {
private Class<?> applicationClass;
private List<String> args = new ArrayList<>();
private List<DatabaseState> databaseStates = new ArrayList<>();
public SpringBootStarterBuilder withApplicationClass(Class<?> clazz) {
this.applicationClass = clazz;
return this;
}
public SpringBootStarterBuilder withArgument(String argument) {
this.args.add(argument);
return this;
}
public SpringBootStarterBuilder withDatabaseState(String stateName, String... sqlScripts) {
this.databaseStates.add(new DatabaseState(stateName, sqlScripts));
return this;
}
public SpringBootStarter build() {
return new SpringBootStarter(this.applicationClass, this.databaseStates, args);
}
}
}

View File

@@ -1 +0,0 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.example.framework.PactDatabaseStatesAutoConfiguration

View File

@@ -1,2 +0,0 @@
insert into address (id, street) values (1, 'Elm Street');
insert into address (id, street) values (2, 'High Street');

View File

@@ -1,6 +0,0 @@
server.port=8080
#spring.jpa.generate-ddl=false
spring.jpa.hibernate.ddl-auto=none
pact.databaseStates[0]=single-address,/initial-schema.sql,/single-address.sql
pact.databaseStates[1]=address-collection,/initial-schema.sql,/address-collection.sql

View File

@@ -1,11 +0,0 @@
create table CUSTOMER (
id NUMBER,
name VARCHAR
);
create table ADDRESS (
id NUMBER,
customer_id NUMBER,
street VARCHAR,
FOREIGN KEY (customer_id) REFERENCES CUSTOMER(id)
);

View File

@@ -1 +0,0 @@
insert into address (id, street) values (1, 'Elm Street');

View File

@@ -4,6 +4,9 @@ This example project shows how to setup an Angular application to use [Pact](htt
in order to create Pact files from a consumer test and validate the
a consumer against the Pact.
## Relevant Blog Post
[Creating a Consumer-Driven Contract with Angular and Pact](https://reflectoring.io/consumer-driven-contracts-with-angular-and-pact/)
## Key Files
* [`user.service.ts`](src/app/user.service.ts): Angular service that calls a REST
@@ -18,3 +21,13 @@ a consumer against the Pact.
Run `npm install` to load the needed javascript libraries and then `npm run test` to
run the tests. After the tests have successfully run the created pact file will be
created in the folder `pacts`.
Then, you can call `npm run publish-pacts` to publish the pact files to a [Pact Broker](https://github.com/pact-foundation/pact_broker).
You must set the following npm configs for the `publish-pacts` script to work:
```
npm config set angular-pact:brokerUrl <URL>
npm config set angular-pact:brokerUsername <USER>
npm config set angular-pact:brokerPassword <PASS>
```

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,8 @@
"build": "ng build",
"test": "cross-env LOGLEVEL=DEBUG ng test",
"lint": "ng lint",
"e2e": "ng e2e"
"e2e": "ng e2e",
"publish-pacts": "node publish-pacts.js"
},
"private": true,
"dependencies": {
@@ -45,9 +46,9 @@
"ts-node": "~3.2.0",
"tslint": "~5.7.0",
"typescript": "~2.3.3",
"@pact-foundation/pact-node": "~4.12",
"@pact-foundation/karma-pact": "~2.1.0",
"pact-web": "~3.0",
"@pact-foundation/pact-node": "6.5.0",
"@pact-foundation/karma-pact": "2.1.3",
"@pact-foundation/pact-web": "5.3.0",
"cross-env": "^5.0.5"
}
}

View File

@@ -0,0 +1,78 @@
{
"consumer": {
"name": "ui"
},
"provider": {
"name": "userservice"
},
"interactions": [
{
"description": "a request to POST a person",
"providerState": "provider accepts a new person",
"request": {
"method": "POST",
"path": "/user-service/users",
"headers": {
"Content-Type": "application/json"
},
"body": {
"firstName": "Arthur",
"lastName": "Dent"
}
},
"response": {
"status": 201,
"headers": {
"Content-Type": "application/json"
},
"body": {
"id": 42
},
"matchingRules": {
"$.body": {
"match": "type"
}
}
}
},
{
"description": "a request to PUT a person",
"providerState": "person 42 exists",
"request": {
"method": "PUT",
"path": "/user-service/users/42",
"headers": {
"Content-Type": "application/json"
},
"body": {
"firstName": "Zaphod",
"lastName": "Beeblebrox"
},
"matchingRules": {
"$.body": {
"match": "type"
}
}
},
"response": {
"status": 200,
"headers": {
},
"body": {
"firstName": "Zaphod",
"lastName": "Beeblebrox"
},
"matchingRules": {
"$.body": {
"match": "type"
}
}
}
}
],
"metadata": {
"pactSpecification": {
"version": "2.0.0"
}
}
}

View File

@@ -0,0 +1,20 @@
let projectFolder = __dirname;
let pact = require('@pact-foundation/pact-node');
let project = require('./package.json');
let pactBrokerUrl = process.env.npm_package_config_brokerUrl;
let pactBrokerUsername = process.env.npm_package_config_brokerUsername;
let pactBrokerPassword = process.env.npm_package_config_brokerPassword;
let options = {
pactFilesOrDirs: [projectFolder + '/pacts'],
pactBroker: pactBrokerUrl,
consumerVersion: project.version,
tags: ['latest'],
pactBrokerUsername: pactBrokerUsername,
pactBrokerPassword: pactBrokerPassword
};
pact.publishPacts(options).then(function () {
console.log("Pacts successfully published!");
});

View File

@@ -2,20 +2,18 @@ import {TestBed} from '@angular/core/testing';
import {HttpClientModule} from '@angular/common/http';
import {UserService} from './user.service';
import {User} from './user';
import * as Pact from 'pact-web';
import {PactWeb, Matchers} from '@pact-foundation/pact-web';
describe('UserService', () => {
let provider;
beforeAll(function (done) {
provider = Pact({
provider = new PactWeb({
consumer: 'ui',
provider: 'userservice',
web: true,
port: 1234,
host: '127.0.0.1',
logLevel: 'DEBUG'
});
// required for slower CI environments
@@ -72,7 +70,7 @@ describe('UserService', () => {
},
willRespondWith: {
status: 201,
body: Pact.Matchers.somethingLike({
body: Matchers.somethingLike({
id: createdUserId
}),
headers: {
@@ -94,4 +92,41 @@ describe('UserService', () => {
});
describe('update()', () => {
const expectedUser: User = {
firstName: 'Zaphod',
lastName: 'Beeblebrox'
};
beforeAll((done) => {
provider.addInteraction({
state: `person 42 exists`,
uponReceiving: 'a request to PUT a person',
withRequest: {
method: 'PUT',
path: '/user-service/users/42',
body: Matchers.somethingLike(expectedUser),
headers: {
'Content-Type': 'application/json'
}
},
willRespondWith: {
status: 200,
body: Matchers.somethingLike(expectedUser)
}
}).then(done, error => done.fail(error));
});
it('should update a Person', (done) => {
const userService: UserService = TestBed.get(UserService);
userService.update(expectedUser, 42).subscribe(response => {
done();
}, error => {
done.fail(error);
});
});
});
});

View File

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

Some files were not shown because too many files have changed in this diff Show More