Compare commits

...

142 Commits

Author SHA1 Message Date
thombergs
7cd125ee33 added to build-all.sh 2020-03-11 06:40:03 +11:00
thombergs
76e97842a2 Merge branch 'reflect-91' of https://github.com/Petros0/code-examples into openapi 2020-03-11 06:18:52 +11:00
thombergs
92aef0228e billingmodule -> billing 2020-03-11 05:45:01 +11:00
thombergs
e8ef69917a billingmodule -> billing 2020-03-10 07:05:08 +11:00
Petros Stergioulas
5da17c5a20 Implement UserApiDelegate, instead of the UserApi 2020-03-09 20:08:40 +01:00
thombergs
8a36085425 added missing Gradle files 2020-03-07 08:42:37 +11:00
thombergs
1916f5b2af example for clean architecture boundaries with Spring Boot 2020-03-07 08:26:15 +11:00
thombergs
c69957d1b8 listContributors -> contributors 2020-03-06 06:40:27 +11:00
thombergs
426d1e77c6 Merge remote-tracking branch 'origin/master' 2020-03-05 06:23:17 +11:00
thombergs
ec681b5abc Repository -> GitRepository 2020-03-05 06:23:01 +11:00
Tom Hombergs
57ff03707c Merge pull request #19 from arkuksin/spring-boot-password-emcoding
add project spring-boot/password-encoding
2020-03-04 05:50:59 +11:00
thombergs
761fdd4d7a Merge remote-tracking branch 'origin/master'
# Conflicts:
#	build-all.sh
2020-03-02 21:17:41 +11:00
thombergs
939e864a4a example of converters in Spring Data JDBC 2020-03-02 21:16:33 +11:00
akuksin
84c593016b replace @Autowired by @Bean annotation for the authentication provider 2020-03-01 11:41:50 +01:00
akuksin
e0a456724d remove custom code for password migration, use the feature of DelegatingPasswordEncoder instead 2020-03-01 10:48:52 +01:00
Tom Hombergs
c8a15d4d4d Merge pull request #20 from thombergs/argumentresolver
Argumentresolver
2020-03-01 09:37:07 +11:00
thombergs
34e0bfc156 argumentresolver example 2020-03-01 09:20:29 +11:00
thombergs
dfbc0446c9 MethodArgumentResolver example 2020-02-29 15:18:13 +11:00
akuksin
0b9a101b08 rename JdbcUserDetailPasswordService to DatabaseUserDetailPasswordService 2020-02-28 19:32:50 +01:00
akuksin
1b9f2426a4 remove @Transactional annotation from RegistrationResource 2020-02-27 22:42:36 +01:00
akuksin
3ab8517320 rename UserResources to RegistrationResource 2020-02-27 22:40:11 +01:00
akuksin
77151d1752 rename daoAuthenticationProvider to provider 2020-02-27 22:36:06 +01:00
akuksin
b73731159f rename JdbcUserDetailsService to DatabaseUserDetailsService 2020-02-27 22:22:16 +01:00
akuksin
bfd004b73a make classes package private, apply google-java-formatter 2020-02-27 22:17:40 +01:00
Tom Hombergs
71d2a32f2f Merge pull request #14 from mukul-s/master
Reflect-76 ISP explained added code examples
2020-02-27 06:21:22 +11:00
thombergs
1e4e88c05e small changes to Main 2020-02-27 06:19:59 +11:00
akuksin
ab5143cb2f remove unnecessary comment 2020-02-25 21:58:33 +01:00
Mukul Sharma
5fab70b931 Merge branch 'master' of https://github.com/mukul-s/code-examples 2020-02-26 01:55:03 +05:30
Mukul Sharma
3be25df928 Reflect-76 Added package 2020-02-26 01:54:18 +05:30
akuksin
74f0e28e8e simplify the configuration 2020-02-25 20:12:30 +01:00
Mukul Sharma
f66aa05afb Merge branch 'master' of https://github.com/mukul-s/code-examples 2020-02-25 18:20:32 +05:30
Mukul Sharma
66d275d667 Merge branch 'master' of https://github.com/mukul-s/code-examples 2020-02-25 18:19:31 +05:30
Mukul Sharma
34211bfac9 Reflect 76 - Resolved merge conflicts 2020-02-25 18:18:53 +05:30
Mukul Sharma
1380621113 Resolved merge conflicts 2020-02-25 18:11:04 +05:30
akuksin
d55ee992f3 reformat code 2020-02-22 23:39:48 +01:00
akuksin
9a378b5763 add calculation of bcryt strength with Divide-and-conquer algorithm 2020-02-22 23:35:21 +01:00
akuksin
4ca9b30b94 add simple examples for password encoders 2020-02-20 22:10:17 +01:00
Tom Hombergs
6e0af82475 Merge pull request #13 from petromir/master
Code examples for REFLECT-2 Contributor article
2020-02-21 06:24:57 +11:00
Tom Hombergs
7494ff7f89 Merge branch 'master' into master 2020-02-20 21:38:54 +11:00
thombergs
327e849575 included flyway module in build-all.sh 2020-02-20 21:38:00 +11:00
Petromir Dzhunev
a0ad81a332 REFLECT-2 Add missing bits
Add undo migration, add Jenkins files, add Java-base migration
2020-02-18 17:38:33 +02:00
akuksin
0847972b5a add project spring-boot/password-encoding 2020-02-17 22:24:01 +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
8ca0e6e380 changes after review 2020-02-02 08:29:36 +11:00
thombergs
b6d167f4ae finished reactive example 2020-02-02 06:45:58 +11:00
Mukul Sharma
7934eed0ab Reflect-76 ISP explained added code examples 2020-02-01 04:13:37 +05:30
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
Petromir Dzhunev
1809361301 REFLECT-2 Remove redundant parameters to Gradle plugin 2020-01-29 23:17:09 +02:00
Petromir Dzhunev
01828e504d REFLECT-2 Integrating H2 with Flyway 2020-01-24 22:35:17 +02:00
Tom Hombergs
3461549263 AWS Hello World docker file 2020-01-23 06:59:18 +11:00
Petromir Dzhunev
a2bb00faf1 Initial set-up of Flyway data migration project 2020-01-01 19:35:57 +02: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
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
664 changed files with 41854 additions and 369 deletions

3
.gitignore vendored Normal file
View File

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

View File

@@ -1,11 +0,0 @@
<component name="libraryTable">
<library name="Gradle: org.projectlombok:lombok:1.16.20">
<CLASSES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.projectlombok/lombok/1.16.20/ac76d9b956045631d1561a09289cbf472e077c01/lombok-1.16.20.jar!/" />
</CLASSES>
<JAVADOC />
<SOURCES>
<root url="jar://$USER_HOME$/.gradle/caches/modules-2/files-2.1/org.projectlombok/lombok/1.16.20/69ebf81bb97bdb3c9581c171762bb4929cb5289c/lombok-1.16.20-sources.jar!/" />
</SOURCES>
</library>
</component>

View File

@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id=":spring-boot:spring-boot-testing" external.linked.project.path="$MODULE_DIR$/../../spring-boot/spring-boot-testing" external.root.project.path="$MODULE_DIR$/../.." external.system.id="GRADLE" external.system.module.group="reflectoring.io" external.system.module.version="0.0.1-SNAPSHOT" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$/../../spring-boot/spring-boot-testing">
<excludeFolder url="file://$MODULE_DIR$/../../spring-boot/spring-boot-testing/.gradle" />
<excludeFolder url="file://$MODULE_DIR$/../../spring-boot/spring-boot-testing/build" />
<excludeFolder url="file://$MODULE_DIR$/../../spring-boot/spring-boot-testing/out" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@@ -1,5 +1,5 @@
before_install:
- chmod +x gradlew
- chmod +x build-all.sh
- |
if ! git diff --name-only $TRAVIS_COMMIT_RANGE | grep -qvE '(.md)|^(LICENSE)'
then
@@ -7,7 +7,12 @@ before_install:
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,6 +1,5 @@
#Sun Jul 30 16:58:54 CEST 2017
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-4.2-all.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" "$@"

0
gradlew.bat → aws/aws-hello-world/gradlew.bat vendored Normal file → Executable file
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() {
}
}

100
build-all.sh Normal file
View File

@@ -0,0 +1,100 @@
#!/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
}
}
build_maven_module() {
MODULE_PATH=$1
echo ""
echo "+++"
echo "+++ BUILDING MODULE $MODULE_PATH"
echo "+++"
cd $MODULE_PATH && {
chmod +x mvnw
./mvnw clean package
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_maven_module "spring-boot/spring-boot-openapi"
build_gradle_module "spring-boot/boundaries"
build_gradle_module "spring-boot/argumentresolver"
build_gradle_module "spring-data/spring-data-jdbc-converter"
build_gradle_module "solid"
build_gradle_module "spring-boot/data-migration/flyway"
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-boot/password-encoding"
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

@@ -7,7 +7,7 @@ buildscript {
apply plugin: 'java'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
sourceCompatibility = 11
repositories {
mavenLocal()
@@ -23,3 +23,6 @@ dependencies {
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-4.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-bin.zip

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

@@ -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

View File

@@ -1,5 +0,0 @@
userservice:
ribbon:
eureka:
enabled: false
listOfServers: localhost:8080

View File

@@ -35,16 +35,29 @@ dependencies {
compile('org.springframework.cloud:spring-cloud-starter-openfeign')
compile('org.springframework.cloud:spring-cloud-starter-netflix-ribbon')
compile('com.h2database:h2:1.4.196')
// add jaxb since it's no longer available in Java 11
runtime('javax.xml.bind:jaxb-api:2.3.1')
// add javassist >= 3.23.1-GA since earlier versions are broken in Java 11
// see https://github.com/jboss-javassist/javassist/issues/194
runtime('org.javassist:javassist:3.23.1-GA')
testCompile('org.codehaus.groovy:groovy-all:2.4.6')
testCompile("au.com.dius:pact-jvm-consumer-junit5_2.12:${pact_version}")
testCompile('org.springframework.boot:spring-boot-starter-test')
testRuntimeOnly( 'org.junit.jupiter:junit-jupiter-engine:5.1.0')
}
pact {
publish {
pactDirectory = 'target/pacts'
pactBrokerUrl = 'URL'
pactBrokerUsername = 'USERNAME'
pactBrokerPassword = 'PASSWORD'
pactBrokerUrl = 'TODO'
pactBrokerUsername = 'TODO'
pactBrokerPassword = 'TODO'
}
}
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-4.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-bin.zip

View File

@@ -11,6 +11,8 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.*;
@ExtendWith(PactConsumerTestExt.class)
@@ -27,6 +29,7 @@ class UserServiceConsumerTest {
@Pact(state = "provider accepts a new person", provider = "userservice", consumer = "userclient")
RequestResponsePact createPersonPact(PactDslWithProvider builder) {
// @formatter:off
return builder
.given("provider accepts a new person")

View File

@@ -1,15 +1,7 @@
# Consumer-Driven-Contract Test for a Spring Boot Provider
This repo contains an example of consumer-driven-contract testing for a Spring
Boot API provider. The corresponding consumer to the contract is
implemented in the module `pact-angular`.
# Consumer-Driven-Contract Test for a Spring Boot Message Consumer
The contract is created and verified with [Pact](https://docs.pact.io/).
This module shows how to use Pact to implement a contract test for a message provider.
Before running the build, you need to follow the instructions on the [consumer-side](../pact-angular/)
to create the consumer-driven contract file (pact file).
## Companion Articles
[Testing a Spring Message Producer and Consumer against a Contract with Pact](https://reflectoring.io/cdc-pact-messages/)
## Running the application
The interesting part in this code base is the class `UserControllerProviderTest`.
You can run the tests with `gradlew test` on Windows or `./gradlew test` on Unix.

View File

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

View File

@@ -13,7 +13,7 @@ apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
sourceCompatibility = 11
repositories {
mavenLocal()
@@ -21,11 +21,18 @@ repositories {
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-starter-amqp')
compile('com.h2database:h2:1.4.196')
compileOnly('org.projectlombok:lombok')
compileOnly('org.projectlombok:lombok:1.18.2')
// add jaxb since it's no longer available in Java 11
runtime('javax.xml.bind:jaxb-api:2.3.1')
// add javassist >= 3.23.1-GA since earlier versions are broken in Java 11
// see https://github.com/jboss-javassist/javassist/issues/194
runtime('org.javassist:javassist:3.23.1-GA')
testCompile("au.com.dius:pact-jvm-consumer-junit_2.12:${pact_version}")
testCompile("au.com.dius:pact-jvm-consumer-groovy_2.12:${pact_version}")
testCompile('org.springframework.boot:spring-boot-starter-test')

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-4.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-bin.zip

View File

@@ -13,13 +13,13 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class UserCreatedMessageConsumer {
public class MessageConsumer {
private Logger logger = LoggerFactory.getLogger(UserCreatedMessageConsumer.class);
private Logger logger = LoggerFactory.getLogger(MessageConsumer.class);
private ObjectMapper objectMapper;
public UserCreatedMessageConsumer(ObjectMapper objectMapper) {
public MessageConsumer(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}

View File

@@ -40,6 +40,7 @@ public class MessageConsumerConfiguration {
public SimpleMessageListenerContainer container(ConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConsumerStartTimeout(1000); // we don't want to wait in this example project
container.setConnectionFactory(connectionFactory);
container.setQueueNames(QUEUE_NAME);
container.setMessageListener(listenerAdapter);
@@ -47,13 +48,13 @@ public class MessageConsumerConfiguration {
}
@Bean
public MessageListenerAdapter listenerAdapter(UserCreatedMessageConsumer userCreatedMessageConsumer) {
return new MessageListenerAdapter(userCreatedMessageConsumer, "consumeStringMessage");
public MessageListenerAdapter listenerAdapter(MessageConsumer messageConsumer) {
return new MessageListenerAdapter(messageConsumer, "consumeStringMessage");
}
@Bean
public UserCreatedMessageConsumer eventReceiver(ObjectMapper objectMapper) {
return new UserCreatedMessageConsumer(objectMapper);
public MessageConsumer eventReceiver(ObjectMapper objectMapper) {
return new MessageConsumer(objectMapper);
}
}

View File

@@ -17,14 +17,14 @@ import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserCreatedMessageConsumerTest {
public class MessageConsumerTest {
@Rule
public MessagePactProviderRule mockProvider = new MessagePactProviderRule(this);
private byte[] currentMessage;
@Autowired
private UserCreatedMessageConsumer userCreatedMessageConsumer;
private MessageConsumer messageConsumer;
@Pact(provider = "userservice", consumer = "userclient")
public MessagePact userCreatedMessagePact(MessagePactBuilder builder) {
@@ -46,7 +46,7 @@ public class UserCreatedMessageConsumerTest {
@Test
@PactVerification("userCreatedMessagePact")
public void verifyCreatePersonPact() throws IOException {
userCreatedMessageConsumer.consumeStringMessage(new String(this.currentMessage));
messageConsumer.consumeStringMessage(new String(this.currentMessage));
}
/**

View File

@@ -14,8 +14,8 @@
"contents": {
"messageUuid": "string",
"user": {
"name": "Zaphod Beeblebrox",
"id": 42
"id": 42,
"name": "Zaphod Beeblebrox"
}
},
"matchingRules": {

View File

@@ -0,0 +1,3 @@
# Consumer-Driven-Contract Test for a Spring Boot Message Provider
This module shows how to use Pact to implement a contract test for a message provider.

View File

@@ -13,7 +13,7 @@ apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8
sourceCompatibility = 11
repositories {
mavenLocal()
@@ -21,11 +21,22 @@ repositories {
}
dependencies {
compile('org.springframework.boot:spring-boot-starter-data-jpa')
compile('org.springframework.boot:spring-boot-starter-web')
compile('org.springframework.boot:spring-boot-starter-amqp')
compile('com.h2database:h2:1.4.196')
compileOnly('org.projectlombok:lombok')
compileOnly('org.projectlombok:lombok:1.18.2')
// add jaxb since it's no longer available in Java 11
runtime('javax.xml.bind:jaxb-api:2.3.1')
// add javassist >= 3.23.1-GA since earlier versions are broken in Java 11
// see https://github.com/jboss-javassist/javassist/issues/194
runtime('org.javassist:javassist:3.23.1-GA')
// overriding bytebuddy to newer version that supports Java 11
// see https://github.com/raphw/byte-buddy/issues/428
testCompile('net.bytebuddy:byte-buddy:1.9.12')
testCompile("au.com.dius:pact-jvm-provider-junit_2.12:${pact_version}")
testCompile('org.springframework.boot:spring-boot-starter-test')
}

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

@@ -0,0 +1,31 @@
package io.reflectoring;
import java.io.IOException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Takes a {@link UserCreatedMessage}, converts it to a {@link String} and sends it to be published.
*/
class MessageProducer {
private Logger logger = LoggerFactory.getLogger(MessageProducer.class);
private ObjectMapper objectMapper;
private MessagePublisher messagePublisher;
MessageProducer(ObjectMapper objectMapper, MessagePublisher messagePublisher) {
this.objectMapper = objectMapper;
this.messagePublisher = messagePublisher;
}
void produceUserCreatedMessage(UserCreatedMessage message) throws IOException {
String stringMessage = objectMapper.writeValueAsString(message);
messagePublisher.publishMessage(stringMessage, "user.created");
logger.info("Published message '{}'", stringMessage);
}
}

View File

@@ -18,18 +18,18 @@ class MessageProviderConfiguration {
@Bean
UserCreatedMessageProvider messageProvider(ObjectMapper objectMapper, UserCreatedMessagePublisher publisher) {
return new UserCreatedMessageProvider(objectMapper, publisher);
MessageProducer messageProvider(ObjectMapper objectMapper, MessagePublisher publisher) {
return new MessageProducer(objectMapper, publisher);
}
@Bean
UserCreatedMessagePublisher messagePublisher(RabbitTemplate rabbitTemplate, TopicExchange topicExchange) {
return new UserCreatedMessagePublisher(rabbitTemplate, topicExchange);
MessagePublisher messagePublisher(RabbitTemplate rabbitTemplate, TopicExchange topicExchange) {
return new MessagePublisher(rabbitTemplate, topicExchange);
}
@Bean
SendMessageJob job(UserCreatedMessageProvider messageProvider) {
return new SendMessageJob(messageProvider);
SendMessageJob job(MessageProducer messageProducer) {
return new SendMessageJob(messageProducer);
}

View File

@@ -6,13 +6,13 @@ import org.springframework.amqp.rabbit.core.RabbitTemplate;
/**
* Publishes a String message to RabbitMQ.
*/
class UserCreatedMessagePublisher {
class MessagePublisher {
private RabbitTemplate rabbitTemplate;
private TopicExchange topicExchange;
UserCreatedMessagePublisher(RabbitTemplate rabbitTemplate, TopicExchange topicExchange) {
MessagePublisher(RabbitTemplate rabbitTemplate, TopicExchange topicExchange) {
this.rabbitTemplate = rabbitTemplate;
this.topicExchange = topicExchange;
}

View File

@@ -10,10 +10,10 @@ class SendMessageJob {
private Random random = new Random();
private UserCreatedMessageProvider messageProvider;
private MessageProducer messageProducer;
SendMessageJob(UserCreatedMessageProvider messageProvider) {
this.messageProvider = messageProvider;
SendMessageJob(MessageProducer messageProducer) {
this.messageProducer = messageProducer;
}
/**
@@ -29,7 +29,7 @@ class SendMessageJob {
.name("Zaphpod Beeblebrox")
.build())
.build();
messageProvider.sendUserCreatedMessage(userCreatedMessage);
messageProducer.produceUserCreatedMessage(userCreatedMessage);
} catch (IOException e) {
throw new RuntimeException(e);
}

View File

@@ -10,6 +10,12 @@ import au.com.dius.pact.provider.ProviderVerifier;
import au.com.dius.pact.provider.junit.target.AmqpTarget;
import org.jetbrains.annotations.NotNull;
/**
* Custom implementation of {@link AmqpTarget} since with {@link AmqpTarget}
* I had classpath troubles (see https://github.com/DiUS/pact-jvm/issues/763).
* Use at own risk, since the implementation is not complete and may behave
* a little different that the original class.
*/
public class CustomAmqpTarget extends AmqpTarget {
public CustomAmqpTarget(List<String> packagesToScan) {

View File

@@ -8,16 +8,12 @@ import au.com.dius.pact.provider.PactVerifyProvider;
import au.com.dius.pact.provider.junit.PactRunner;
import au.com.dius.pact.provider.junit.Provider;
import au.com.dius.pact.provider.junit.loader.PactFolder;
import au.com.dius.pact.provider.junit.target.AmqpTarget;
import au.com.dius.pact.provider.junit.target.Target;
import au.com.dius.pact.provider.junit.target.TestTarget;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import static org.mockito.Mockito.*;
@RunWith(PactRunner.class)
@@ -28,9 +24,9 @@ public class UserCreatedMessageProviderTest {
@TestTarget
public final Target target = new CustomAmqpTarget(Collections.singletonList("io.reflectoring"));
private UserCreatedMessagePublisher publisher = Mockito.mock(UserCreatedMessagePublisher.class);
private MessagePublisher publisher = Mockito.mock(MessagePublisher.class);
private UserCreatedMessageProvider messageProvider = new UserCreatedMessageProvider(new ObjectMapper(), publisher);
private MessageProducer messageProducer = new MessageProducer(new ObjectMapper(), publisher);
@PactVerifyProvider("a user created message")
public String verifyUserCreatedMessage() throws IOException {
@@ -45,7 +41,7 @@ public class UserCreatedMessageProviderTest {
.name("Zaphod Beeblebrox")
.build())
.build();
messageProvider.sendUserCreatedMessage(message);
messageProducer.produceUserCreatedMessage(message);
// then
ArgumentCaptor<String> messageCapture = ArgumentCaptor.forClass(String.class);

View File

@@ -1,15 +0,0 @@
# Consumer-Driven-Contract Test for a Spring Boot Provider
This repo contains an example of consumer-driven-contract testing for a Spring
Boot API provider. The corresponding consumer to the contract is
implemented in the module `pact-angular`.
The contract is created and verified with [Pact](https://docs.pact.io/).
Before running the build, you need to follow the instructions on the [consumer-side](../pact-angular/)
to create the consumer-driven contract file (pact file).
## Running the application
The interesting part in this code base is the class `UserControllerProviderTest`.
You can run the tests with `gradlew test` on Windows or `./gradlew test` on Unix.

View File

@@ -1,31 +0,0 @@
package io.reflectoring;
import java.io.IOException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Takes a {@link UserCreatedMessage}, converts it to a {@link String} and sends it to be published.
*/
class UserCreatedMessageProvider {
private Logger logger = LoggerFactory.getLogger(UserCreatedMessageProvider.class);
private ObjectMapper objectMapper;
private UserCreatedMessagePublisher userCreatedMessagePublisher;
UserCreatedMessageProvider(ObjectMapper objectMapper, UserCreatedMessagePublisher userCreatedMessagePublisher) {
this.objectMapper = objectMapper;
this.userCreatedMessagePublisher = userCreatedMessagePublisher;
}
void sendUserCreatedMessage(UserCreatedMessage message) throws IOException {
String stringMessage = objectMapper.writeValueAsString(message);
userCreatedMessagePublisher.publishMessage(stringMessage, "user.created");
logger.info("Published message '{}'", stringMessage);
}
}

View File

@@ -1,59 +0,0 @@
{
"consumer": {
"name": "userclient"
},
"provider": {
"name": "userservice"
},
"messages": [
{
"description": "a user created message",
"metaData": {
"Content-Type": "application/json; charset=UTF-8"
},
"contents": {
"messageUuid": "string",
"user": {
"name": "Zaphod Beeblebrox",
"id": 42
}
},
"matchingRules": {
"body": {
"$.messageUuid": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
},
"$.user.id": {
"matchers": [
{
"match": "number"
}
],
"combine": "AND"
},
"$.user.name": {
"matchers": [
{
"match": "type"
}
],
"combine": "AND"
}
}
}
}
],
"metadata": {
"pactSpecification": {
"version": "3.0.0"
},
"pact-jvm": {
"version": "3.5.20"
}
}
}

24
pact/pact-node-messages/.gitignore vendored Normal file
View File

@@ -0,0 +1,24 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.nyc_output/*
coverage/*

2543
pact/pact-node-messages/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,17 @@
{
"name": "pact-node-messages",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test:pact:consumer": "mocha src/consumer/*.spec.js --exit",
"test:pact:provider": "mocha src/provider/*.spec.js --exit",
"publish:pact": "node pact/publish.js"
},
"author": "Tom Hombergs",
"license": "MIT",
"devDependencies": {
"@pact-foundation/pact": "^7.0.3",
"mocha": "^5.2.0"
}
}

View File

@@ -0,0 +1,12 @@
let publisher = require('@pact-foundation/pact-node');
let path = require('path');
let opts = {
pactFilesOrDirs: [path.resolve(process.cwd(), 'pacts')],
pactBroker: 'https://adesso.pact.dius.com.au/',
pactBrokerUsername: process.env.PACT_USERNAME,
pactBrokerPassword: process.env.PACT_PASSWORD,
consumerVersion: '2.0.0'
};
publisher.publishPacts(opts).then(() => console.log("Pacts successfully published"));

View File

@@ -0,0 +1,63 @@
{
"consumer": {
"name": "node-message-consumer"
},
"provider": {
"name": "node-message-provider"
},
"messages": [
{
"description": "a hero created message",
"providerStates": [
],
"contents": {
"id": 42,
"name": "Superman",
"superpower": "flying",
"universe": "DC"
},
"matchingRules": {
"body": {
"$.id": {
"matchers": [
{
"match": "type"
}
]
},
"$.name": {
"matchers": [
{
"match": "type"
}
]
},
"$.superpower": {
"matchers": [
{
"match": "type"
}
]
},
"$.universe": {
"matchers": [
{
"match": "regex",
"regex": "^(DC|Marvel)$"
}
]
}
}
},
"metaData": {
"content-type": "application/json"
}
}
],
"metadata": {
"pactSpecification": {
"version": "3.0.0"
}
}
}

View File

@@ -0,0 +1,35 @@
class HeroCreatedEvent {
constructor(name, superpower, universe, id) {
this.id = id;
this.name = name;
this.superpower = superpower;
this.universe = universe;
}
static validateUniverse(event) {
if (typeof event.universe !== 'string') {
throw new Error(`Hero universe must be a string! Invalid value: ${event.universe}`)
}
}
static validateSuperpower(event) {
if (typeof event.superpower !== 'string') {
throw new Error(`Hero superpower must be a string! Invalid value: ${event.superpower}`)
}
}
static validateName(event) {
if (typeof event.name !== 'string') {
throw new Error(`Hero name must be a string! Invalid value: ${event.name}`);
}
}
static validateId(event) {
if (typeof event.id !== 'number') {
throw new Error(`Hero id must be a number! Invalid value: ${event.id}`)
}
}
}
module.exports = HeroCreatedEvent;

View File

@@ -0,0 +1,15 @@
const HeroCreatedEvent = require('../common/hero-created-event');
exports.HeroEventHandler = {
handleHeroCreatedEvent: (message) => {
HeroCreatedEvent.validateId(message);
HeroCreatedEvent.validateName(message);
HeroCreatedEvent.validateSuperpower(message);
HeroCreatedEvent.validateUniverse(message);
// pass message into business logic
// note that the business logic should be mocked away for the contract test
}
};

View File

@@ -0,0 +1,35 @@
const {MessageConsumerPact, Matchers, synchronousBodyHandler} = require('@pact-foundation/pact');
const {HeroEventHandler} = require('./hero-event-handler');
const path = require('path');
describe("message consumer", () => {
const messagePact = new MessageConsumerPact({
consumer: "node-message-consumer",
provider: "node-message-provider",
dir: path.resolve(process.cwd(), "pacts"),
pactfileWriteMode: "update",
logLevel: "info",
});
describe("'hero created' message Handler", () => {
it("should accept a valid hero created message", (done) => {
messagePact
.expectsToReceive("a hero created message")
.withContent({
id: Matchers.like(42),
name: Matchers.like("Superman"),
superpower: Matchers.like("flying"),
universe: Matchers.term({generate: "DC", matcher: "^(DC|Marvel)$"})
})
.withMetadata({
"content-type": "application/json",
})
.verify(synchronousBodyHandler(HeroEventHandler.handleHeroCreatedEvent))
.then(() => done(), (error) => done(error));
}).timeout(5000);
});
});

View File

@@ -0,0 +1,9 @@
const HeroCreatedEvent = require('../common/hero-created-event');
exports.CreateHeroEventProducer = {
produceHeroCreatedEvent: () => {
return new Promise((resolve, reject) => {
resolve(new HeroCreatedEvent("Superman", "Flying", "DC", 42));
});
}
};

View File

@@ -0,0 +1,37 @@
const {MessageProviderPact} = require('@pact-foundation/pact');
const {CreateHeroEventProducer} = require('./hero-event-producer');
const path = require('path');
describe("message producer", () => {
const messagePact = new MessageProviderPact({
messageProviders: {
"a hero created message": () => CreateHeroEventProducer.produceHeroCreatedEvent(),
},
log: path.resolve(process.cwd(), "logs", "pact.log"),
logLevel: "info",
provider: "node-message-provider",
pactUrls: [path.resolve(process.cwd(), "pacts", "node-message-consumer-node-message-provider.json")],
// Pact seems not to load a pact file from a pact broker, so we have to make do with the local pact file
// see https://github.com/pact-foundation/pact-js/issues/248
// pactBrokerUrl: "https://adesso.pact.dius.com.au",
// pactBrokerUsername: process.env.PACT_USERNAME,
// pactBrokerPassword: process.env.PACT_PASSWORD,
// publishVerificationResult: true,
// providerVersion: '1.0.0',
tags: ['latest']
});
describe("'hero created' message producer", () => {
it("should create a valid hero created message", (done) => {
messagePact
.verify()
.then(() => done(), (error) => done(error));
}).timeout(5000);
});
});

21
pact/pact-node-provider/.gitignore vendored Normal file
View File

@@ -0,0 +1,21 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*

View File

@@ -0,0 +1,7 @@
# Node Pact example
This example project shows how to setup an Express server with Node and create
a contract test for the provider side of a contract with Pact Node.
## Relevant Blog Post
[Step By Step Tutorial for Implementing Consumer-Driven Contracts for a Node Express Server with Pact](https://reflectoring.io/pact-node-provider/)

View File

@@ -0,0 +1,29 @@
const express = require('express');
const path = require('path');
const cookieParser = require('cookie-parser');
const logger = require('morgan');
const indexRouter = require('./routes/index');
const usersRouter = require('./routes/users');
const heroesRouter = require('./routes/heroes');
const graphqlRouter = require('./routes/graphql');
const providerStateRouter = require ('./routes/provider_state');
const app = express();
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/heroes', heroesRouter);
app.use('/graphql', graphqlRouter);
if (process.env.PACT_MODE === 'true') {
app.use('/provider-state', providerStateRouter);
}
module.exports = app;

View File

@@ -0,0 +1,90 @@
#!/usr/bin/env node
/**
* Module dependencies.
*/
var app = require('../app');
var debug = require('debug')('test:server');
var http = require('http');
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
/**
* Create HTTP server.
*/
var server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
}

2802
pact/pact-node-provider/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,23 @@
{
"name": "test",
"version": "0.0.0",
"private": true,
"scripts": {
"start": "node ./bin/www",
"pact:providerTests": "node ./pact/provider_tests.js",
"pact:providerTests:graphql": "node ./pact/provider_tests_graphql.js",
"test:pact": "start-server-and-test start http://localhost:3000 pact:providerTests",
"test:pact:graphql": "start-server-and-test start http://localhost:3000 pact:providerTests:graphql"
},
"dependencies": {
"cookie-parser": "~1.4.3",
"debug": "~2.6.9",
"express": "~4.16.0",
"express-graphql": "^0.7.1",
"morgan": "~1.9.0"
},
"devDependencies": {
"@pact-foundation/pact": "7.0.3",
"start-server-and-test": "^1.7.5"
}
}

View File

@@ -0,0 +1,17 @@
const { Verifier } = require('@pact-foundation/pact');
const packageJson = require('../package.json');
let opts = {
providerBaseUrl: 'http://localhost:3000',
provider: 'hero-provider',
pactBrokerUrl: 'https://adesso.pact.dius.com.au',
pactBrokerUsername: process.env.PACT_USERNAME,
pactBrokerPassword: process.env.PACT_PASSWORD,
publishVerificationResult: true,
providerVersion: packageJson.version,
providerStatesSetupUrl: 'http://localhost:3000/provider-state'
};
new Verifier().verifyProvider(opts).then(function () {
console.log("Pacts successfully verified!");
});

View File

@@ -0,0 +1,16 @@
const { Verifier } = require('@pact-foundation/pact');
const packageJson = require('../package.json');
let opts = {
providerBaseUrl: 'http://localhost:3000',
provider: 'graphql-hero-provider',
pactBrokerUrl: 'https://adesso.pact.dius.com.au',
pactBrokerUsername: process.env.PACT_USERNAME,
pactBrokerPassword: process.env.PACT_PASSWORD,
publishVerificationResult: true,
providerVersion: packageJson.version,
};
new Verifier().verifyProvider(opts).then(function () {
console.log("Pacts successfully verified!");
});

View File

@@ -0,0 +1,13 @@
<html>
<head>
<title>Express</title>
<link rel="stylesheet" href="/stylesheets/style.css">
</head>
<body>
<h1>Express</h1>
<p>Welcome to Express</p>
</body>
</html>

View File

@@ -0,0 +1,8 @@
body {
padding: 50px;
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
}
a {
color: #00B7FF;
}

View File

@@ -0,0 +1,36 @@
const graphqlHTTP = require('express-graphql');
const {buildSchema} = require("graphql");
const heroesSchema = buildSchema(`
type Query {
hero(id: Int!): Hero
}
type Hero {
id: Int!
name: String!
superpower: String!
universe: String!
}
`);
const getHero = function () {
return {
id: 42,
name: "Superman",
superpower: "Flying",
universe: "DC"
}
};
const root = {
hero: getHero
};
const router = graphqlHTTP({
schema: heroesSchema,
graphiql: true,
rootValue: root
});
module.exports = router;

View File

@@ -0,0 +1,27 @@
const express = require('express');
const router = express.Router();
router.route('/:hero_id')
.get(function (req, res) {
const heroId = parseInt(req.params['hero_id']);
res.status(200);
res.json({
id: heroId,
superpower: 'flying',
name: 'Superman',
universe: 'DC'
});
});
router.route('/')
.post(function (req, res) {
res.status(201);
res.json({
id: 42,
superpower: 'flying',
name: 'Superman',
universe: 'DC'
});
});
module.exports = router;

View File

@@ -0,0 +1,9 @@
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
module.exports = router;

View File

@@ -0,0 +1,13 @@
const express = require('express');
const router = express.Router();
router.route('/')
.post(function (req, res) {
const consumer = req.query['consumer'];
const providerState = req.query['state'];
// imagine we're setting the server into a certain state
res.send(`changed to provider state "${providerState}" for consumer "${consumer}"`);
res.status(200);
});
module.exports = router;

View File

@@ -0,0 +1,9 @@
var express = require('express');
var router = express.Router();
/* GET users listing. */
router.get('/', function(req, res, next) {
res.send('respond with a resource');
});
module.exports = router;

23
pact/pact-react-consumer/.gitignore vendored Normal file
View File

@@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
/.idea

View File

@@ -0,0 +1,8 @@
# React Pact example
This example project shows how to setup a React application to use [Pact](http://pact.io)
in order to create Pact files from a consumer test and validate the
a consumer against the Pact.
## Relevant Blog Post
[Step By Step Tutorial for Implementing Consumer-Driven Contracts for a React App with Pact and Jest](https://reflectoring.io/pact-react-consumer/)

15763
pact/pact-react-consumer/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,40 @@
{
"name": "pact-react-consumer",
"version": "0.1.0",
"private": true,
"dependencies": {
"apollo-cache-inmemory": "^1.3.9",
"apollo-client": "^2.4.5",
"apollo-link-http": "^1.5.5",
"axios": "0.18.0",
"graphql": "^14.0.2",
"graphql-tag": "^2.10.0",
"node-fetch": "^2.2.1",
"react": "^16.5.2",
"react-dom": "^16.5.2",
"react-scripts": "2.0.5"
},
"devDependencies": {
"@pact-foundation/pact": "7.0.3",
"@pact-foundation/pact-node": "6.20.0",
"cross-env": "^5.2.0"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"test:pact": "cross-env CI=true react-scripts test --runInBand --setupFiles ./pact/setup.js --setupTestFrameworkScriptFile ./pact/jest-wrapper.js --testMatch \"**/*.test.pact.js\"",
"test:pact:graphql": "cross-env CI=true react-scripts test --runInBand --setupFiles ./pact/setup-graphql.js --setupTestFrameworkScriptFile ./pact/jest-wrapper.js --testMatch \"**/*.test.graphql.pact.js\"",
"publish:pact": "node pact/publish.js",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": [
">0.2%",
"not dead",
"not ie <= 11",
"not op_mini all"
]
}

View File

@@ -0,0 +1,7 @@
beforeAll((done) => {
global.provider.setup().then(() => done());
});
afterAll((done) => {
global.provider.finalize().then(() => done());
});

View File

@@ -0,0 +1,13 @@
let publisher = require('@pact-foundation/pact-node');
let path = require('path');
let opts = {
providerBaseUrl: 'http://localhost:8080',
pactFilesOrDirs: [path.resolve(process.cwd(), 'pacts')],
pactBroker: 'https://adesso.pact.dius.com.au/',
pactBrokerUsername: process.env.PACT_USERNAME,
pactBrokerPassword: process.env.PACT_PASSWORD,
consumerVersion: '2.0.0'
};
publisher.publishPacts(opts).then(() => console.log("Pacts successfully published"));

View File

@@ -0,0 +1,16 @@
const path = require('path');
const Pact = require('@pact-foundation/pact').Pact;
global.port = 8080;
global.provider = new Pact({
cors: true,
port: global.port,
log: path.resolve(process.cwd(), 'logs', 'pact.log'),
loglevel: 'debug',
dir: path.resolve(process.cwd(), 'pacts'),
spec: 2,
pactfileWriteMode: 'update',
consumer: 'graphql-hero-consumer',
provider: 'graphql-hero-provider',
host: '127.0.0.1'
});

View File

@@ -0,0 +1,16 @@
const path = require('path');
const Pact = require('@pact-foundation/pact').Pact;
global.port = 8080;
global.provider = new Pact({
cors: true,
port: global.port,
log: path.resolve(process.cwd(), 'logs', 'pact.log'),
loglevel: 'debug',
dir: path.resolve(process.cwd(), 'pacts'),
spec: 2,
pactfileWriteMode: 'update',
consumer: 'hero-consumer',
provider: 'hero-provider',
host: '127.0.0.1'
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<!--
manifest.json provides metadata used when your web app is added to the
homescreen on Android. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json">
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>

View File

@@ -0,0 +1,15 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}

View File

@@ -0,0 +1,32 @@
.App {
text-align: center;
}
.App-logo {
animation: App-logo-spin infinite 20s linear;
height: 40vmin;
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
@keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

View File

@@ -0,0 +1,28 @@
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
}
export default App;

View File

@@ -0,0 +1,10 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
it('renders without crashing', () => {
const div = document.createElement('div');
ReactDOM.render(<App/>, div);
ReactDOM.unmountComponentAtNode(div);
expect(1 + 2).toBe(3);
});

View File

@@ -0,0 +1,52 @@
import {ApolloClient} from "apollo-client"
import {InMemoryCache} from "apollo-cache-inmemory"
import {HttpLink} from "apollo-link-http"
import gql from "graphql-tag"
import Hero from "../hero";
class GraphQLHeroService {
constructor(baseUrl, port, fetch) {
this.client = new ApolloClient({
link: new HttpLink({
uri: `${baseUrl}:${port}/graphql`,
fetch: fetch
}),
cache: new InMemoryCache()
});
}
getHero(heroId) {
if (heroId == null) {
throw new Error("heroId must not be null!");
}
return this.client.query({
query: gql`
query GetHero($heroId: Int!) {
hero(id: $heroId) {
name
superpower
}
}
`,
variables: {
heroId: heroId
}
}).then((response) => {
return new Promise((resolve, reject) => {
try {
const hero = new Hero(response.data.hero.name, response.data.hero.superpower, null, heroId);
Hero.validateName(hero);
Hero.validateSuperpower(hero);
resolve(hero);
} catch (error) {
reject(error);
}
})
});
};
}
export default GraphQLHeroService;

View File

@@ -0,0 +1,70 @@
import GraphQLHeroService from './hero.service.graphql';
import * as Pact from '@pact-foundation/pact';
import fetch from 'node-fetch';
describe('HeroService GraphQL API', () => {
const heroService = new GraphQLHeroService('http://localhost', global.port, fetch);
// a matcher for the content type "application/json" in UTF8 charset
// that ignores the spaces between the ";2 and "charset"
const contentTypeJsonMatcher = Pact.Matchers.term({
matcher: "application\\/json; *charset=utf-8",
generate: "application/json; charset=utf-8"
});
describe('getHero()', () => {
beforeEach((done) => {
global.provider.addInteraction(new Pact.GraphQLInteraction()
.uponReceiving('a GetHero Query')
.withRequest({
path: '/graphql',
method: 'POST',
})
.withOperation("GetHero")
.withQuery(`
query GetHero($heroId: Int!) {
hero(id: $heroId) {
name
superpower
__typename
}
}`)
.withVariables({
heroId: 42
})
.willRespondWith({
status: 200,
headers: {
'Content-Type': contentTypeJsonMatcher
},
body: {
data: {
hero: {
name: Pact.Matchers.somethingLike('Superman'),
superpower: Pact.Matchers.somethingLike('Flying'),
__typename: 'Hero'
}
}
}
})).then(() => done());
});
it('sends a request according to contract', (done) => {
heroService.getHero(42)
.then(hero => {
expect(hero.name).toEqual('Superman');
})
.then(() => {
global.provider.verify()
.then(() => done(), error => {
done.fail(error)
})
});
});
});
});

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