diff --git a/.circleci/config.yml b/.circleci/config.yml index c752f2f6..4534586d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,85 +1,21 @@ -version: 2 +version: 2.1 +setup: true +orbs: + eventuate-gradle-build-and-test: "eventuate_io/eventuate-gradle-build-and-test@0.2.1" + continuation: circleci/continuation@0.1.2 jobs: - build: - machine: true - working_directory: ~/ftgo-application + setup: + executor: continuation/default steps: - - checkout - - restore_cache: - key: ftgo-application-{{ checksum "gradle.properties" }} - - run: TERM=dumb ./build-contracts.sh - - run: TERM=dumb ./gradlew testClasses :ftgo-order-service:compileComponentTestJava - - save_cache: - paths: - - ~/.gradle - - ~/.m2 - key: ftgo-application-{{ checksum "gradle.properties" }} - - run: - command: | - ./.circleci/upgrade-docker-compose.sh - . ./setenv-circle-ci.sh - ./build-and-test-all.sh - - run: - name: Save test results - command: | - mkdir -p ~/junit/ - find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; - when: always - - store_test_results: - path: ~/junit - - store_artifacts: - path: ~/junit - build-mariadb: - machine: true - working_directory: ~/ftgo-application - steps: - - checkout - - restore_cache: - key: ftgo-application-{{ checksum "gradle.properties" }} - - run: TERM=dumb ./build-contracts.sh - - run: TERM=dumb ./gradlew testClasses :ftgo-order-service:compileComponentTestJava - - save_cache: - paths: - - ~/.gradle - - ~/.m2 - key: ftgo-application-{{ checksum "gradle.properties" }} - - run: - command: | - ./.circleci/upgrade-docker-compose.sh - . ./setenv-circle-ci.sh - ./build-and-test-all-mariadb.sh - - run: - name: Save test results - command: | - mkdir -p ~/junit/ - find . -type f -regex ".*/build/test-results/.*xml" -exec cp {} ~/junit/ \; - when: always - - store_test_results: - path: ~/junit - - store_artifacts: - path: ~/junit - publish: - machine: true - working_directory: ~/ftgo-application - steps: - - checkout - - restore_cache: - key: ftgo-application-{{ checksum "gradle.properties" }} - - run: - command: | - ./.circleci/upgrade-docker-compose.sh - . ./setenv-circle-ci.sh - ./gradlew assemble - docker-compose build - ./publish-docker-images.sh + - checkout # checkout code + - run: # run a command + name: Generate config + command: | + ./.circleci/generate-config.sh > generated_config.yml + - continuation/continue: + configuration_path: generated_config.yml + workflows: - version: 2 - build-test-and-deploy: + setup: jobs: - - build - - build-mariadb - - publish: - requires: - - build - - build-mariadb - + - setup diff --git a/.circleci/generate-config.sh b/.circleci/generate-config.sh new file mode 100755 index 00000000..12bbcc7d --- /dev/null +++ b/.circleci/generate-config.sh @@ -0,0 +1,20 @@ +#! /bin/bash -e + +cat > generated_config.yml <> generated_config.yml < ~/docker-compose +curl -L https://github.com/docker/compose/releases/download/1.25.4/docker-compose-`uname -s`-`uname -m` > ~/docker-compose chmod +x ~/docker-compose sudo mv ~/docker-compose /usr/local/bin/docker-compose diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..23e7c9ef --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +mysql/*.sh text eol=lf +dynamodblocal-init/*.sh text eol=lf diff --git a/README.adoc b/README.adoc index a044a266..f878f3d2 100644 --- a/README.adoc +++ b/README.adoc @@ -1,6 +1,6 @@ = FTGO example application -This is the example code for my book https://www.manning.com/books/microservice-patterns[Microservice patterns]. +This is the example code for my book https://microservices.io/book[Microservice patterns]. image::http://microservices.io/i/Microservices-Patterns-Cover.png[width=50%] @@ -12,10 +12,9 @@ image::http://microservices.io/i/Microservices-Patterns-Cover.png[width=50%] * The application consists of many services and so requires a lot of memory. It runs well, for example, on a 16GB Macbook pro. * The application's services and the infrastructure services, such as MySQL and Apache Kafka, are deployed using Docker containers using either Docker Compose or Kubernetes. -== Got questions +== Got questions? -Please post a message to the https://forums.manning.com/forums/microservice-patterns[book's discussion forum] or create a github issue. -I'll do my best to help you. +Please create a https://github.com/microservices-patterns/ftgo-application/issues[github issue] and I'll do my best to help you. == Application architecture @@ -119,7 +118,7 @@ All the services' business logic is implemented using Domain-Driven design aggre Temporary: Build the Spring Cloud Contracts using this command: ``` -./build-contracts.sh +./gradlew buildContracts ``` Build the services using this command: @@ -128,68 +127,39 @@ Build the services using this command: ./gradlew assemble ``` -=== Setting environment variables - - -==== Quick way - -A quick way to set the environment variables is to run the script `./set-env.sh` - -==== Long way - -To run the application you must set the `DOCKER_HOST_IP` environment variable to the IP address of where the Docker containers are running: - -* Docker toolbox/Virtual machine - IP address of the virtual machine -* Docker for Windows/Mac/Linux - IP address of your laptop/desktop - -The value of `DOCKER_HOST_IP` must be meaningful to both Java services/tests running on your desktop/laptop and to Docker containers. -Please do NOT set it to the unresolvable hostname of your machine, `localhost` or `127.0.0.1` since the Docker containers will probably not work correctly. - -=== Verifying that DOCKER_HOST_IP is set correctly - -You can verify that `DOCKER_HOST_IP` is set correctly by running this command: - ----- -docker run -p 8889:8888 -e DOCKER_DIAGNOSTICS_PORT=8889 -e DOCKER_HOST_IP \ - --rm eventuateio/eventuateio-docker-networking-diagnostics:0.2.0.RELEASE ----- - -==== Setting the environment variable in your IDE - -If you want to run Java services/tests within your IDE on your desktop/laptop AND the Docker containers are not accessible via `localhost` THEN you will need to set `DOCKER_HOST_IP` within your IDE. -How to do this depends on your operating system and IDE. -For example, I find it convenient to launch my IDE from the command line and after setting this environment variable. - - === Running the application Run the application using this command: ``` -docker-compose up -d +./gradlew :composeUp ``` +Note: the ':' + This can take a while. === Using the application Use the services Swagger UIs to invoke the services. -* Create consumer - `http://${DOCKER_HOST_IP?}:8081/swagger-ui.html` -* Create a restaurant - `http://${DOCKER_HOST_IP?}:8084/swagger-ui.html` -* Create an order - `http://${DOCKER_HOST_IP?}:8082/swagger-ui.html` -* View the order - `http://${DOCKER_HOST_IP?}:8082/swagger-ui.html` -* View the order history - `http://${DOCKER_HOST_IP?}:8086/swagger-ui.html` +* Create consumer - `http://localhost:8081/swagger-ui/index.html` +* Create a restaurant - `http://localhost:8084/swagger-ui/index.html` +* Create an order - `http://localhost:8082/swagger-ui/index.html` +* View the order - `http://localhost:8082/swagger-ui/index.html` +* View the order history - `http://localhost:8086/swagger-ui/index.html` -You can also access the application via the `API Gateway` at `http://${DOCKER_HOST_IP?}:8087`. +You can also access the application via the `API Gateway` at `http://localhost:8087`. However, currently it doesn't have a Swagger UI so you will have to use `curl`, for example. +Note: if the containers aren't accessible via `localhost` - e.g. you are using Docker Toolbox, you will have to use `${DOCKER_HOST_IP}` as described below. + === Stopping the application Stop the application using this command: ``` -docker-compose down -v +./gradlew :composeDown ``` == Deploying the application on Kubernetes @@ -218,3 +188,35 @@ If you want to delete the persistent volumes for Apache Kafka, Zookeeper and MyS ``` ./deployment/kubernetes/scripts/kubernetes-delete-volumes.sh ``` + +== Setting environment variables to do development + +You should not need to set any environment variables. +To run the application, you certainly do not. +Similarly, to do development (e.g. run tests), you typically do not need to set any environment variables. +That's because Docker containers are generally accessible (e.g. Docker for Windows/Mac) on the host via `localhost`. +However, if Docker is running elsewhere (e.g. you are using Docker Toolbox) you will need to set `DOCKER_HOST_IP`. + +=== Quick way + +A quick way to set the environment variables is to run the script `./set-env.sh`. + +=== Long way + +The value of `DOCKER_HOST_IP` must be meaningful to both Java services/tests running on your desktop/laptop and to Docker containers. +Please do NOT set it to the unresolvable hostname of your machine, `localhost` or `127.0.0.1` since the Docker containers will probably not work correctly. + +=== Verifying that DOCKER_HOST_IP is set correctly + +You can verify that `DOCKER_HOST_IP` is set correctly by running this command: + +---- +docker run -p 8889:8888 -e DOCKER_DIAGNOSTICS_PORT=8889 -e DOCKER_HOST_IP \ + --rm eventuateio/eventuateio-docker-networking-diagnostics:0.2.0.RELEASE +---- + +=== Setting the environment variable in your IDE + +If you want to run Java services/tests within your IDE on your desktop/laptop AND the Docker containers are not accessible via `localhost` THEN you will need to set `DOCKER_HOST_IP` within your IDE. +How to do this depends on your operating system and IDE. +For example, I find it convenient to launch my IDE from the command line and after setting this environment variable. diff --git a/_wait-for-services.sh b/_wait-for-services.sh index 70f5828b..a2fbc699 100755 --- a/_wait-for-services.sh +++ b/_wait-for-services.sh @@ -7,19 +7,20 @@ ports=$* echo $path echo $ports -host=$DOCKER_HOST_IP +host=${DOCKER_HOST_IP:-localhost} done=false while [[ "$done" = false ]]; do for port in $ports; do - curl --fail http://${host}:${port}$path >& /dev/null + url=http://${host}:${port}$path + curl --fail $url >& /dev/null if [[ "$?" -eq "0" ]]; then done=true else done=false - echo http://${host}:${port}/health + echo $url break fi done diff --git a/build-and-run.sh b/build-and-run.sh deleted file mode 100755 index bb3555e7..00000000 --- a/build-and-run.sh +++ /dev/null @@ -1,29 +0,0 @@ -#! /bin/bash -e - -. ./set-env.sh - -./gradlew assemble - -docker-compose build - -. ./set-env.sh - -initializeDynamoDB() { - echo preparing dynamodblocal table data - cd dynamodblocal-init - ./create-dynamodb-tables.sh - cd .. - echo data is prepared -} - - -docker-compose down -v -docker-compose up -d --build dynamodblocal mysql - -./wait-for-mysql.sh - -initializeDynamoDB - -docker-compose up -d - -./show-swagger-ui-urls.sh diff --git a/build-and-test-all-mariadb.sh b/build-and-test-all-mariadb.sh deleted file mode 100755 index 33ede6c7..00000000 --- a/build-and-test-all-mariadb.sh +++ /dev/null @@ -1,5 +0,0 @@ -#! /bin/bash -e - -export FTGO_DOCKER_COMPOSE_FILES=docker-compose.yml,docker-compose-mariadb.yml - -DOCKER_COMPOSE="docker-compose -f docker-compose.yml -f docker-compose-mariadb.yml" ./build-and-test-all.sh $* diff --git a/build-and-test-all.sh b/build-and-test-all.sh index ffaa3ebc..6be666ad 100755 --- a/build-and-test-all.sh +++ b/build-and-test-all.sh @@ -2,11 +2,9 @@ KEEP_RUNNING= ASSEMBLE_ONLY= -DATABASE_SERVICES="dynamodblocal mysql dynamodblocal-init" +USE_EXISTING_CONTAINERS= -if [ -z "$DOCKER_COMPOSE" ] ; then - DOCKER_COMPOSE=docker-compose -fi +DATABASE_SERVICES="dynamodblocal mysql dynamodblocal-init" while [ ! -z "$*" ] ; do case $1 in @@ -16,8 +14,11 @@ while [ ! -z "$*" ] ; do "--assemble-only" ) ASSEMBLE_ONLY=yes ;; + "--use-existing-containers" ) + USE_EXISTING_CONTAINERS=yes + ;; "--help" ) - echo ./build-and-test-all.sh --keep-running --assemble-only + echo ./build-and-test-all.sh --keep-running --assemble-only --use-existing-containers exit 0 ;; esac @@ -26,71 +27,55 @@ done echo KEEP_RUNNING=$KEEP_RUNNING -. ./set-env.sh - # TODO Temporarily -./build-contracts.sh +./gradlew --parallel buildContracts -./gradlew testClasses +./gradlew --parallel compileAll -${DOCKER_COMPOSE?} down --remove-orphans -v -${DOCKER_COMPOSE?} up -d --build ${DATABASE_SERVICES?} +if [ -z "$USE_EXISTING_CONTAINERS" ] ; then + ./gradlew :composeDown +fi -./gradlew waitForMySql +./gradlew infrastructureComposeUp echo mysql is started -${DOCKER_COMPOSE?} up -d --build eventuate-local-cdc-service tram-cdc-service +# Test ./mysql-cli.sh +echo 'show databases;' | ./mysql-cli.sh -i if [ -z "$ASSEMBLE_ONLY" ] ; then ./gradlew -x :ftgo-end-to-end-tests:test $* build - ${DOCKER_COMPOSE?} build - ./gradlew $* integrationTest - # Component tests need to use the per-service database schema - - - SPRING_DATASOURCE_URL=jdbc:mysql://${DOCKER_HOST_IP?}/ftgoorderservice ./gradlew :ftgo-order-service:cleanComponentTest :ftgo-order-service:componentTest + ./gradlew infrastructureComposeDown + ./gradlew infrastructureComposeUp - # Reset the DB/messages + ./gradlew cleanComponentTest - ${DOCKER_COMPOSE?} down --remove-orphans -v - - ${DOCKER_COMPOSE?} up -d ${DATABASE_SERVICES?} - - ./gradlew waitForMySql - - echo mysql is started - - ${DOCKER_COMPOSE?} up -d + # ./gradlew :ftgo-delivery-service:componentTest + # ./gradlew :ftgo-order-service:componentTest + + ./gradlew componentTest + ./gradlew :composeDown else - ./gradlew $* assemble - ${DOCKER_COMPOSE?} up -d --build ${DATABASE_SERVICES?} - - ./gradlew waitForMySql - - echo mysql is started - - ${DOCKER_COMPOSE?} up -d --build - fi -./wait-for-services.sh +./gradlew :composeUp ./run-end-to-end-tests.sh -./run-graphql-api-gateway-tests.sh +# NEED TO FIX +# ./run-graphql-api-gateway-tests.sh if [ -z "$KEEP_RUNNING" ] ; then - ${DOCKER_COMPOSE?} down --remove-orphans -v + ./gradlew :composeDown fi diff --git a/build-contracts.sh b/build-contracts.sh deleted file mode 100755 index 552f7a18..00000000 --- a/build-contracts.sh +++ /dev/null @@ -1,20 +0,0 @@ -#! /bin/bash -e - -CONTRACT_DIRS="ftgo-accounting-service-contracts ftgo-consumer-service-contracts ftgo-order-service-contracts ftgo-kitchen-service-contracts" - -COMMAND=$* - -if [[ -z "$COMMAND" ]] ; then - COMMAND=publish -fi - -echo Using $COMMAND - -ARGS= - -for dir in $CONTRACT_DIRS ; do - ARGS="$ARGS :$dir:$COMMAND" -done - -./gradlew $ARGS - diff --git a/build.gradle b/build.gradle index 323963ac..28274e56 100644 --- a/build.gradle +++ b/build.gradle @@ -1,23 +1,24 @@ buildscript { repositories { mavenCentral() + jcenter() } dependencies { classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}" classpath "io.spring.gradle:dependency-management-plugin:$springDependencyManagementPluginVersion" + classpath "com.avast.gradle:gradle-docker-compose-plugin:$dockerComposePluginVersion" + classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:$springCloudContractDependenciesVersion" } - - apply plugin: WaitForMySqlPlugin } -plugins { - id "com.github.ben-manes.versions" version "0.20.0" -} +apply plugin: WaitForMySqlPlugin +apply plugin: 'docker-compose' subprojects { apply plugin: "java" - apply plugin: 'pmd' + + // TODO apply plugin: 'pmd' sourceCompatibility = '1.8' targetCompatibility = '1.8' @@ -25,13 +26,13 @@ subprojects { group = "net.chrisrichardson.ftgo" repositories { + eventuateMavenRepoUrl.split('[ ,]').each { repoUrl -> maven { url repoUrl.trim() } } + mavenCentral() jcenter() - eventuateMavenRepoUrl.split(',').each { repoUrl -> maven { url repoUrl } } - maven { - url 'https://repo.spring.io/libs-milestone' + url 'https://jitpack.io' } maven { @@ -40,4 +41,65 @@ subprojects { } + dependencies { + + implementation(platform("io.eventuate.platform:eventuate-platform-dependencies:$eventuatePlatformVersion")) + constraints { + compile ("io.netty:netty-codec-http2") { + version { + strictly("4.1.72.Final") + } + } + } + + + } +} + +task buildContracts(type: GradleBuild) { + tasks = subprojects.collect { it.name }.findAll { it.endsWith("-contracts") }.collect { ":" + it + ":publish"} +} + +task compileAll(type: GradleBuild) { + tasks = ["testClasses", "compileIntegrationTestJava", "compileComponentTestJava"] +} + +dockerCompose { + + environment.put "EVENTUATE_COMMON_VERSION", eventuateCommonImageVersion + environment.put "EVENTUATE_CDC_VERSION", eventuateCdcImageVersion + environment.put "EVENTUATE_SAGA_VERSION", eventuateTramSagasImageVersion + environment.put "EVENTUATE_JAVA_BASE_IMAGE_VERSION", eventuateExamplesBaseImageVersion + environment.put "EVENTUATE_MESSAGING_KAFKA_IMAGE_VERSION", eventuateMessagingKafkaImageVersion + + projectName = null + removeOrphans = true + retainContainersOnStartupFailure = true + + if (project.hasProperty('startedServices')) + startedServices= project.ext.startedServices.split(',') + + mysql { + projectName = null + startedServices = ["mysql"] + } + + infrastructure { + projectName = null + startedServices = ["mysql", "cdc-service", "dynamodblocal-init", "kafka", "zookeeper"] + } + + kafkaGui { + projectName = null + startedServices = ["kafka-gui"] + } } + +composeUp.dependsOn(infrastructureComposeUp) + +subprojects.each { + if (it.name.endsWith("-service") || it.name.endsWith("-gateway")) { + composeBuild.dependsOn(":" + it.name + ":assemble") + composeUp.dependsOn(":" + it.name + ":assemble") + } + } \ No newline at end of file diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index c45f99bf..abee6f48 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -2,10 +2,18 @@ test.enabled=false repositories { mavenCentral() + jcenter() + maven { + url "https://plugins.gradle.org/m2/" + } } dependencies { - compile 'mysql:mysql-connector-java:5.1.39' + compile 'mysql:mysql-connector-java:8.0.21' compile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.158' compile 'commons-lang:commons-lang:2.6' -} \ No newline at end of file + + compile 'org.jsonschema2pojo:jsonschema2pojo-core:1.0.1' + + compile "gradle.plugin.org.hidetake:gradle-swagger-generator-plugin:2.18.1" +} diff --git a/buildSrc/src/main/groovy/ComponentTestsPlugin.groovy b/buildSrc/src/main/groovy/ComponentTestsPlugin.groovy index 4a07c81b..d4f5d728 100644 --- a/buildSrc/src/main/groovy/ComponentTestsPlugin.groovy +++ b/buildSrc/src/main/groovy/ComponentTestsPlugin.groovy @@ -6,9 +6,9 @@ class ComponentTestsPlugin implements Plugin { @Override void apply(Project project) { - + project.apply(plugin: 'eclipse') - + project.sourceSets { componentTest { java { @@ -28,7 +28,7 @@ class ComponentTestsPlugin implements Plugin { project.eclipse.classpath.plusConfigurations << project.configurations.componentTestCompile project.task("componentTest", type: Test) { - testClassesDir = project.sourceSets.componentTest.output.classesDir + testClassesDirs = project.sourceSets.componentTest.output.classesDirs classpath = project.sourceSets.componentTest.runtimeClasspath } diff --git a/buildSrc/src/main/groovy/FtgoApiDependencyResolverPlugin.groovy b/buildSrc/src/main/groovy/FtgoApiDependencyResolverPlugin.groovy new file mode 100644 index 00000000..783a3ba6 --- /dev/null +++ b/buildSrc/src/main/groovy/FtgoApiDependencyResolverPlugin.groovy @@ -0,0 +1,31 @@ +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.internal.artifacts.dependencies.DefaultProjectDependency + +class FtgoApiDependencyResolverPlugin implements Plugin { + + @Override + void apply(Project project) { + + project.ext.ftgoApiSpecsDir = "${project.buildDir}/ftgo-api-specs" + + def c = project.configurations.create('ftgoApiSpecification') + + project.configurations.implementation.extendsFrom(c) + + def resolveTask = project.task("ftgoResolveAPIDependencies", + type: FtgoResolveAPIDependencies, + group: 'build setup', + description: "fetch API dependencies") + + project.afterEvaluate { + project.configurations.ftgoApiSpecification.allDependencies.each { + if (it instanceof DefaultProjectDependency) { + def buildTask = it.dependencyProject.tasks.getByPath("build") + resolveTask.dependsOn(buildTask) + } + } + } + } + +} \ No newline at end of file diff --git a/buildSrc/src/main/groovy/FtgoJSONSchema2PojoPlugin.groovy b/buildSrc/src/main/groovy/FtgoJSONSchema2PojoPlugin.groovy new file mode 100644 index 00000000..21ada148 --- /dev/null +++ b/buildSrc/src/main/groovy/FtgoJSONSchema2PojoPlugin.groovy @@ -0,0 +1,36 @@ +import org.gradle.api.Plugin +import org.gradle.api.Project + +class FtgoJSONSchema2PojoPlugin implements Plugin { + + @Override + void apply(Project project) { + + project.apply(plugin: FtgoApiDependencyResolverPlugin) + + def jsonSchemaSources = project.container(JSONSchemaSource) + project.extensions.add('ftgoJsonSchema2Pojo', jsonSchemaSources) + + jsonSchemaSources.all { + def source = delegate as JSONSchemaSource + + source.codeGen = project.task("ftgoJSONSchemaToPojoCodeGen${source.name.capitalize()}", + type: FtgoJSONSchemaToPojoCodeGen, + group: 'build', + description: "Code generator") as FtgoJSONSchemaToPojoCodeGen + + + source.codeGen.targetDirectory = new File(project.buildDir, "generated-js2p-code-${source.name}") + + source.codeGen.dependsOn(project.tasks.ftgoResolveAPIDependencies) + + project.afterEvaluate { + project.sourceSets[source.sourceSet].java.srcDirs += [source.codeGen.targetDirectory] + project.tasks.compileJava.dependsOn(source.codeGen) + } + + + } + } + +} \ No newline at end of file diff --git a/buildSrc/src/main/groovy/FtgoJSONSchemaToPojoCodeGen.groovy b/buildSrc/src/main/groovy/FtgoJSONSchemaToPojoCodeGen.groovy new file mode 100644 index 00000000..6652e773 --- /dev/null +++ b/buildSrc/src/main/groovy/FtgoJSONSchemaToPojoCodeGen.groovy @@ -0,0 +1,57 @@ +import org.gradle.api.DefaultTask +import org.gradle.api.file.FileCollection +import org.gradle.api.tasks.TaskAction +import org.jsonschema2pojo.GenerationConfig +import org.jsonschema2pojo.Jsonschema2Pojo +import org.jsonschema2pojo.DefaultGenerationConfig + +class FtgoJSONSchemaToPojoCodeGen extends DefaultTask { + + FileCollection source + String targetPackage; + boolean includeAdditionalProperties; + boolean generateBuilders + boolean useLongIntegers + File targetDirectory + + @TaskAction + def generate() { + GenerationConfig configuration = new DefaultGenerationConfig() { + + @Override + Iterator getSource() { + FtgoJSONSchemaToPojoCodeGen.this.source.collect { it.toURL()}.iterator() + } + + @Override + String getTargetPackage() { + FtgoJSONSchemaToPojoCodeGen.this.targetPackage + } + + @Override + boolean isIncludeAdditionalProperties() { + FtgoJSONSchemaToPojoCodeGen.this.includeAdditionalProperties + } + + @Override + boolean isGenerateBuilders() { + FtgoJSONSchemaToPojoCodeGen.this.generateBuilders + } + + @Override + boolean isUseLongIntegers() { + FtgoJSONSchemaToPojoCodeGen.this.useLongIntegers + } + + @Override + File getTargetDirectory() { + return FtgoJSONSchemaToPojoCodeGen.this.targetDirectory + } + } + + + Jsonschema2Pojo.generate(configuration) + } + + +} diff --git a/buildSrc/src/main/groovy/FtgoOpenApiCodeGenPlugin.groovy b/buildSrc/src/main/groovy/FtgoOpenApiCodeGenPlugin.groovy new file mode 100644 index 00000000..56c3aa84 --- /dev/null +++ b/buildSrc/src/main/groovy/FtgoOpenApiCodeGenPlugin.groovy @@ -0,0 +1,22 @@ +import org.gradle.api.Plugin +import org.gradle.api.Project + +class FtgoOpenApiCodeGenPlugin implements Plugin { + + @Override + void apply(Project project) { + project.pluginManager.apply("org.hidetake.swagger.generator") + project.apply(plugin: FtgoApiDependencyResolverPlugin) + + project.afterEvaluate { + project.swaggerSources.each { + def name = it.name + project.swaggerSources[name].code.dependsOn(project.tasks.getByPath("ftgoResolveAPIDependencies")) + project.compileJava.dependsOn(project.swaggerSources[name].code) + project.sourceSets.main.java.srcDir "${project.swaggerSources[name].code.outputDir}/src/main/java" + project.sourceSets.main.resources.srcDir "${project.swaggerSources[name].code.outputDir}/src/main/resources" + } + } + + } +} diff --git a/buildSrc/src/main/groovy/FtgoResolveAPIDependencies.groovy b/buildSrc/src/main/groovy/FtgoResolveAPIDependencies.groovy new file mode 100644 index 00000000..b0b023a4 --- /dev/null +++ b/buildSrc/src/main/groovy/FtgoResolveAPIDependencies.groovy @@ -0,0 +1,21 @@ +import org.gradle.api.tasks.Sync + +class FtgoResolveAPIDependencies extends Sync { + + FtgoResolveAPIDependencies() { + + + from { + project.configurations.ftgoApiSpecification.resolve().collect { + project.zipTree(it) + } + } + include("**/*.json") + exclude("**/META-INF/**") + into(project.ext.ftgoApiSpecsDir ) + + includeEmptyDirs = false + + } + +} diff --git a/buildSrc/src/main/groovy/FtgoServicePlugin.groovy b/buildSrc/src/main/groovy/FtgoServicePlugin.groovy index 453f2ea1..7f8dfbc9 100644 --- a/buildSrc/src/main/groovy/FtgoServicePlugin.groovy +++ b/buildSrc/src/main/groovy/FtgoServicePlugin.groovy @@ -14,17 +14,24 @@ class FtgoServicePlugin implements Plugin { imports { mavenBom "org.springframework.cloud:spring-cloud-contract-dependencies:${project.ext.springCloudContractDependenciesVersion}" mavenBom "org.springframework.cloud:spring-cloud-sleuth:${project.ext.springCloudSleuthVersion}" + mavenBom "io.eventuate.platform:eventuate-platform-dependencies:${project.ext.eventuatePlatformVersion}" } } + project.configurations.all { + exclude group: 'org.apache.logging.log4j' + exclude group: 'log4j' + } project.dependencies { - compile 'org.springframework.cloud:spring-cloud-starter-sleuth' compile 'org.springframework.cloud:spring-cloud-starter-zipkin' - compile 'io.zipkin.brave:brave-bom:4.17.1' + compile "io.zipkin.brave:brave-bom:4.17.1" + + // Temporarily disable + //compile "io.eventuate.tram.core:eventuate-tram-spring-cloud-sleuth-integration" - compile "io.eventuate.tram.core:eventuate-tram-spring-cloud-sleuth-integration:${project.ext.eventuateTramVersion}" + implementation(platform("io.eventuate.platform:eventuate-platform-dependencies:${project.ext.eventuatePlatformVersion}")) } } diff --git a/buildSrc/src/main/groovy/IntegrationTestsPlugin.groovy b/buildSrc/src/main/groovy/IntegrationTestsPlugin.groovy index 9f5bce54..83f110c8 100644 --- a/buildSrc/src/main/groovy/IntegrationTestsPlugin.groovy +++ b/buildSrc/src/main/groovy/IntegrationTestsPlugin.groovy @@ -6,6 +6,9 @@ class IntegrationTestsPlugin implements Plugin { @Override void apply(Project project) { + + project.apply(plugin: 'eclipse') + project.sourceSets { integrationTest { java { @@ -22,8 +25,10 @@ class IntegrationTestsPlugin implements Plugin { integrationTestRuntime.extendsFrom testRuntime } + project.eclipse.classpath.plusConfigurations << project.configurations.integrationTestCompile + project.task("integrationTest", type: Test) { - testClassesDir = project.sourceSets.integrationTest.output.classesDir + testClassesDirs = project.sourceSets.integrationTest.output.classesDirs classpath = project.sourceSets.integrationTest.runtimeClasspath } diff --git a/buildSrc/src/main/groovy/JSONSchemaSource.groovy b/buildSrc/src/main/groovy/JSONSchemaSource.groovy new file mode 100644 index 00000000..72003abc --- /dev/null +++ b/buildSrc/src/main/groovy/JSONSchemaSource.groovy @@ -0,0 +1,44 @@ +import groovy.transform.ToString +import org.gradle.api.file.FileCollection + +@ToString(includes = 'name', includePackage = false) +class JSONSchemaSource { + + final String name + + def JSONSchemaSource(String name) { + this.name = name + } + + FtgoJSONSchemaToPojoCodeGen codeGen + + String sourceSet = "main" + + void setSource(FileCollection source) { + this.codeGen.source = source + } + + void setTargetPackage(String targetPackage) { + this.codeGen.targetPackage = targetPackage + } + + void setIncludeAdditionalProperties(boolean includeAdditionalProperties) { + this.codeGen.includeAdditionalProperties = includeAdditionalProperties + } + + void setGenerateBuilders(boolean generateBuilders) { + this.codeGen.generateBuilders = generateBuilders + } + + void setUseLongIntegers(boolean useLongIntegers) { + this.codeGen.useLongIntegers = useLongIntegers + } + + File getTargetDirectory() { + return this.codeGen.targetDirectory + } + + void setSourceSet(String sourceSet) { + this.sourceSet = sourceSet + } +} diff --git a/common-swagger/build.gradle b/common-swagger/build.gradle index fd0dde92..add6de46 100644 --- a/common-swagger/build.gradle +++ b/common-swagger/build.gradle @@ -1,10 +1,3 @@ dependencies { - compile ("io.springfox:springfox-swagger2:2.8.0") { - exclude group: "org.springframework" - } - compile ("io.springfox:springfox-swagger-ui:2.8.0"){ - exclude group: "org.springframework" - } - compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion" - + compile "io.eventuate.util:eventuate-util-spring-swagger" } diff --git a/common-swagger/src/main/java/net/chrisrichardson/eventstore/examples/customersandorders/commonswagger/CommonSwaggerConfiguration.java b/common-swagger/src/main/java/net/chrisrichardson/eventstore/examples/customersandorders/commonswagger/CommonSwaggerConfiguration.java index 7c5036a4..b872cfcb 100644 --- a/common-swagger/src/main/java/net/chrisrichardson/eventstore/examples/customersandorders/commonswagger/CommonSwaggerConfiguration.java +++ b/common-swagger/src/main/java/net/chrisrichardson/eventstore/examples/customersandorders/commonswagger/CommonSwaggerConfiguration.java @@ -1,43 +1,14 @@ package net.chrisrichardson.eventstore.examples.customersandorders.commonswagger; -import com.fasterxml.classmate.TypeResolver; -import org.springframework.beans.factory.annotation.Autowired; +import io.eventuate.util.spring.swagger.EventuateSwaggerConfig; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.http.ResponseEntity; -import org.springframework.web.context.request.async.DeferredResult; -import springfox.documentation.builders.RequestHandlerSelectors; -import springfox.documentation.schema.WildcardType; -import springfox.documentation.spi.DocumentationType; -import springfox.documentation.spring.web.plugins.Docket; -import springfox.documentation.swagger2.annotations.EnableSwagger2; - -import java.util.concurrent.CompletableFuture; - -import static springfox.documentation.schema.AlternateTypeRules.newRule; @Configuration -@EnableSwagger2 public class CommonSwaggerConfiguration { @Bean - public Docket api() { - return new Docket(DocumentationType.SWAGGER_2) - .select() - .apis(RequestHandlerSelectors.basePackage("net.chrisrichardson.ftgo")) - .build() - .pathMapping("/") - .genericModelSubstitutes(ResponseEntity.class, CompletableFuture.class) - .alternateTypeRules( - newRule(typeResolver.resolve(DeferredResult.class, - typeResolver.resolve(ResponseEntity.class, WildcardType.class)), - typeResolver.resolve(WildcardType.class)) - ) - .useDefaultResponseMessages(false) - ; + public EventuateSwaggerConfig eventuateSwaggerConfig() { + return () -> "net.chrisrichardson.ftgo"; } - - @Autowired - private TypeResolver typeResolver; - } diff --git a/deployment/kubernetes/cdc-service/ftgo-cdc-service.yml b/deployment/kubernetes/cdc-service/ftgo-cdc-service.yml new file mode 100644 index 00000000..449edef3 --- /dev/null +++ b/deployment/kubernetes/cdc-service/ftgo-cdc-service.yml @@ -0,0 +1,121 @@ +apiVersion: v1 +kind: Service +metadata: + name: ftgo-cdc-service +spec: + ports: + - port: 8080 + targetPort: 8080 + selector: + svc: ftgo-cdc-service +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: ftgo-cdc-service + labels: + application: ftgo +spec: + replicas: 1 + strategy: + rollingUpdate: + maxUnavailable: 0 + template: + metadata: + labels: + svc: ftgo-cdc-service + spec: + containers: + - name: ftgo-cdc-service + image: eventuateio/eventuate-cdc-service:0.4.0.RELEASE + imagePullPolicy: Always + ports: + - containerPort: 8080 + name: httpport + command: ["bash", "-c", "java -Dsun.net.inetaddr.ttl=30 -jar *.jar" ] + env: + - name: JAVA_OPTS + value: "-Dsun.net.inetaddr.ttl=30" + - name: EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS + value: ftgo-kafka:29092 + - name: EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING + value: ftgo-zookeeper:2181 + - name: EVENTUATE_CDC_PIPELINE_PIPELINE1_TYPE + value: eventuate-tram + - name: EVENTUATE_CDC_PIPELINE_PIPELINE1_READER + value: reader1 + - name: EVENTUATE_CDC_PIPELINE_PIPELINE1_EVENTUATEDATABASESCHEMA + value: ftgo_consumer_service + - name: EVENTUATE_CDC_PIPELINE_PIPELINE2_TYPE + value: eventuate-tram + - name: EVENTUATE_CDC_PIPELINE_PIPELINE2_READER + value: reader1 + - name: EVENTUATE_CDC_PIPELINE_PIPELINE2_EVENTUATEDATABASESCHEMA + value: ftgo_order_service + - name: EVENTUATE_CDC_PIPELINE_PIPELINE3_TYPE + value: eventuate-tram + - name: EVENTUATE_CDC_PIPELINE_PIPELINE3_READER + value: reader1 + - name: EVENTUATE_CDC_PIPELINE_PIPELINE3_EVENTUATEDATABASESCHEMA + value: ftgo_kitchen_service + - name: EVENTUATE_CDC_PIPELINE_PIPELINE4_TYPE + value: eventuate-tram + - name: EVENTUATE_CDC_PIPELINE_PIPELINE4_READER + value: reader1 + - name: EVENTUATE_CDC_PIPELINE_PIPELINE4_EVENTUATEDATABASESCHEMA + value: ftgo_restaurant_service + - name: EVENTUATE_CDC_PIPELINE_PIPELINE5_TYPE + value: eventuate-tram + - name: EVENTUATE_CDC_PIPELINE_PIPELINE5_READER + value: reader1 + - name: EVENTUATE_CDC_PIPELINE_PIPELINE5_EVENTUATEDATABASESCHEMA + value: ftgo_accounting_service + - name: EVENTUATE_CDC_PIPELINE_PIPELINE6_TYPE + value: eventuate-tram + - name: EVENTUATE_CDC_PIPELINE_PIPELINE6_READER + value: reader1 + - name: EVENTUATE_CDC_PIPELINE_PIPELINE6_EVENTUATEDATABASESCHEMA + value: ftgoorderhistoryservice + - name: EVENTUATE_CDC_PIPELINE_PIPELINE7_TYPE + value: eventuate-local + - name: EVENTUATE_CDC_PIPELINE_PIPELINE7_READER + value: reader1 + - name: EVENTUATE_CDC_PIPELINE_PIPELINE7_EVENTUATEDATABASESCHEMA + value: ftgo_accounting_service + - name: EVENTUATE_CDC_READER_READER1_TYPE + value: mysql-binlog + - name: EVENTUATE_CDC_READER_READER1_DATASOURCEURL + value: jdbc:mysql://ftgo-mysql:3306/eventuate + - name: EVENTUATE_CDC_READER_READER1_DATASOURCEUSERNAME + value: root + - name: EVENTUATE_CDC_READER_READER1_DATASOURCEPASSWORD + value: rootpassword + - name: EVENTUATE_CDC_READER_READER1_DATASOURCEDRIVERCLASSNAME + value: com.mysql.jdbc.Driver + - name: EVENTUATE_CDC_READER_READER1_LEADERSHIPLOCKPATH + value: /eventuate/cdc/leader/common + - name: EVENTUATE_CDC_READER_READER1_CDCDBUSERNAME + value: root + - name: EVENTUATE_CDC_READER_READER1_CDCDBPASSWORD + value: rootpassword + - name: EVENTUATE_CDC_READER_READER1_READOLDDEBEZIUMDBOFFSETSTORAGETOPIC + value: "false" + - name: EVENTUATE_CDC_READER_READER1_MYSQLBINLOGCLIENTUNIQUEID + value: "1" + - name: EVENTUATE_CDC_READER_READER1_OFFSETSTOREKEY + value: MySqlBinlog + - name: EVENTUATE_CDC_READER_READER1_OFFSETSTORAGETOPICNAME + value: db.history.common + livenessProbe: + httpGet: + path: /actuator + port: 8080 + initialDelaySeconds: 60 + periodSeconds: 20 + readinessProbe: + httpGet: + path: /actuator + port: 8080 + initialDelaySeconds: 60 + periodSeconds: 20 +--- diff --git a/deployment/kubernetes/cdc-services/eventuate-local-cdc-service.yml b/deployment/kubernetes/cdc-services/eventuate-local-cdc-service.yml deleted file mode 100644 index 6a397943..00000000 --- a/deployment/kubernetes/cdc-services/eventuate-local-cdc-service.yml +++ /dev/null @@ -1,75 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: eventuate-local-cdc-service -spec: - ports: - - port: 8080 - targetPort: 8080 - selector: - svc: eventuate-local-cdc-service ---- -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: eventuate-local-cdc-service - labels: - application: ftgo -spec: - replicas: 1 - strategy: - rollingUpdate: - maxUnavailable: 1 - template: - metadata: - labels: - svc: eventuate-local-cdc-service - spec: - containers: - - name: eventuate-local-cdc-service - image: eventuateio/eventuateio-local-cdc-service:0.17.0.RELEASE - imagePullPolicy: Always - ports: - - containerPort: 8080 - name: httpport - command: ["bash", "-c", "java -Dsun.net.inetaddr.ttl=30 -jar *.jar" ] - env: - - name: JAVA_OPTS - value: "-Dsun.net.inetaddr.ttl=30" - - name: SPRING_DATASOURCE_URL - value: jdbc:mysql://ftgo-mysql/eventuate - - name: SPRING_DATASOURCE_USERNAME - valueFrom: - secretKeyRef: - name: ftgo-db-secret - key: username - - name: SPRING_DATASOURCE_PASSWORD - valueFrom: - secretKeyRef: - name: ftgo-db-secret - key: password - - name: SPRING_DATASOURCE_DRIVER_CLASS_NAME - value: com.mysql.jdbc.Driver - - name: EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS - value: ftgo-kafka:9092 - - name: EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING - value: ftgo-zookeeper:2181 - - name: EVENTUATELOCAL_CDC_DB_USER_NAME - value: root - - name: EVENTUATELOCAL_CDC_DB_PASSWORD - value: rootpassword - - name: EVENTUATELOCAL_CDC_LEADERSHIP_LOCK_PATH - value: /eventuatelocal/cdc/leader1 - livenessProbe: - httpGet: - path: /health - port: 8080 - initialDelaySeconds: 60 - periodSeconds: 20 - readinessProbe: - httpGet: - path: /health - port: 8080 - initialDelaySeconds: 60 - periodSeconds: 20 ---- diff --git a/deployment/kubernetes/cdc-services/ftgo-tram-cdc-service.yml b/deployment/kubernetes/cdc-services/ftgo-tram-cdc-service.yml deleted file mode 100644 index 16396453..00000000 --- a/deployment/kubernetes/cdc-services/ftgo-tram-cdc-service.yml +++ /dev/null @@ -1,77 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: ftgo-tram-cdc-service -spec: - ports: - - port: 8080 - targetPort: 8080 - selector: - svc: ftgo-tram-cdc-service ---- -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: ftgo-tram-cdc-service - labels: - application: ftgo -spec: - replicas: 1 - strategy: - rollingUpdate: - maxUnavailable: 0 - template: - metadata: - labels: - svc: ftgo-tram-cdc-service - spec: - containers: - - name: ftgo-tram-cdc-service - image: eventuateio/eventuate-tram-cdc-mysql-service:0.6.0.RELEASE - imagePullPolicy: Always - ports: - - containerPort: 8080 - name: httpport - command: ["bash", "-c", "java -Dsun.net.inetaddr.ttl=30 -jar *.jar" ] - env: - - name: JAVA_OPTS - value: "-Dsun.net.inetaddr.ttl=30" - - name: SPRING_DATASOURCE_URL - value: jdbc:mysql://ftgo-mysql/eventuate - - name: SPRING_DATASOURCE_USERNAME - valueFrom: - secretKeyRef: - name: ftgo-db-secret - key: username - - name: SPRING_DATASOURCE_PASSWORD - valueFrom: - secretKeyRef: - name: ftgo-db-secret - key: password - - name: SPRING_DATASOURCE_DRIVER_CLASS_NAME - value: com.mysql.jdbc.Driver - - name: EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS - value: ftgo-kafka:9092 - - name: EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING - value: ftgo-zookeeper:2181 - - name: EVENTUATELOCAL_CDC_DB_USER_NAME - value: root - - name: EVENTUATELOCAL_CDC_DB_PASSWORD - value: rootpassword - - name: EVENTUATELOCAL_CDC_BINLOG_CLIENT_ID - value: "1234567890" - - name: EVENTUATELOCAL_CDC_SOURCE_TABLE_NAME - value: message - livenessProbe: - httpGet: - path: /health - port: 8080 - initialDelaySeconds: 60 - periodSeconds: 20 - readinessProbe: - httpGet: - path: /health - port: 8080 - initialDelaySeconds: 60 - periodSeconds: 20 ---- diff --git a/deployment/kubernetes/stateful-services/ftgo-kafka-config.yml b/deployment/kubernetes/stateful-services/ftgo-kafka-config.yml deleted file mode 100644 index 7181292e..00000000 --- a/deployment/kubernetes/stateful-services/ftgo-kafka-config.yml +++ /dev/null @@ -1,125 +0,0 @@ ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: ftgo-kafka-config -data: - server.properties: | - # Licensed to the Apache Software Foundation (ASF) under one or more - # contributor license agreements. See the NOTICE file distributed with - # this work for additional information regarding copyright ownership. - # The ASF licenses this file to You under the Apache License, Version 2.0 - # (the "License"); you may not use this file except in compliance with - # the License. You may obtain a copy of the License at - # - # http://www.apache.org/licenses/LICENSE-2.0 - # - # Unless required by applicable law or agreed to in writing, software - # distributed under the License is distributed on an "AS IS" BASIS, - # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - # See the License for the specific language governing permissions and - # limitations under the License. - # see kafka.server.KafkaConfig for additional details and defaults - - ############################# Server Basics ############################# - - # The id of the broker. This must be set to a unique integer for each broker. - broker.id=0 - - ############################# Socket Server Settings ############################# - - # The address the socket server listens on. It will get the value returned from - # java.net.InetAddress.getCanonicalHostName() if not configured. - # FORMAT: - # listeners = security_protocol://host_name:port - # EXAMPLE: - # listeners = PLAINTEXT://your.host.name:9092 - #listeners=PLAINTEXT://:9092 - - # Hostname and port the broker will advertise to producers and consumers. If not set, - # it uses the value for "listeners" if configured. Otherwise, it will use the value - # returned from java.net.InetAddress.getCanonicalHostName(). - - advertised.listeners=PLAINTEXT://ADVERTISED_HOST_NAME:9092 - - # The number of threads handling network requests - num.network.threads=3 - - # The number of threads doing disk I/O - num.io.threads=8 - - # The send buffer (SO_SNDBUF) used by the socket server - socket.send.buffer.bytes=102400 - - # The receive buffer (SO_RCVBUF) used by the socket server - socket.receive.buffer.bytes=102400 - - # The maximum size of a request that the socket server will accept (protection against OOM) - socket.request.max.bytes=104857600 - - - ############################# Log Basics ############################# - - # A comma seperated list of directories under which to store log files - log.dirs=/usr/local/kafka-data/logs - - # The default number of log partitions per topic. More partitions allow greater - # parallelism for consumption, but this will also result in more files across - # the brokers. - num.partitions=2 - - # The number of threads per data directory to be used for log recovery at startup and flushing at shutdown. - # This value is recommended to be increased for installations with data dirs located in RAID array. - num.recovery.threads.per.data.dir=1 - - ############################# Log Flush Policy ############################# - - # Messages are immediately written to the filesystem but by default we only fsync() to sync - # the OS cache lazily. The following configurations control the flush of data to disk. - # There are a few important trade-offs here: - # 1. Durability: Unflushed data may be lost if you are not using replication. - # 2. Latency: Very large flush intervals may lead to latency spikes when the flush does occur as there will be a lot of data to flush. - # 3. Throughput: The flush is generally the most expensive operation, and a small flush interval may lead to exceessive seeks. - # The settings below allow one to configure the flush policy to flush data after a period of time or - # every N messages (or both). This can be done globally and overridden on a per-topic basis. - - # The number of messages to accept before forcing a flush of data to disk - #log.flush.interval.messages=10000 - - # The maximum amount of time a message can sit in a log before we force a flush - #log.flush.interval.ms=1000 - - ############################# Log Retention Policy ############################# - - # The following configurations control the disposal of log segments. The policy can - # be set to delete segments after a period of time, or after a given size has accumulated. - # A segment will be deleted whenever *either* of these criteria are met. Deletion always happens - # from the end of the log. - - # The minimum age of a log file to be eligible for deletion - log.retention.hours=168 - - # A size-based retention policy for logs. Segments are pruned from the log as long as the remaining - # segments don't drop below log.retention.bytes. - #log.retention.bytes=1073741824 - - # The maximum size of a log segment file. When this size is reached a new log segment will be created. - log.segment.bytes=1073741824 - - # The interval at which log segments are checked to see if they can be deleted according - # to the retention policies - log.retention.check.interval.ms=300000 - - ############################# Zookeeper ############################# - - # Zookeeper connection string (see zookeeper docs for details). - # This is a comma separated host:port pairs, each corresponding to a zk - # server. e.g. "127.0.0.1:3000,127.0.0.1:3001,127.0.0.1:3002". - # You can also append an optional chroot string to the urls to specify the - # root directory for all kafka znodes. - zookeeper.connect=ZOOKEEPER_SERVERS - - - # Timeout in ms for connecting to zookeeper - zookeeper.connection.timeout.ms=6000 ---- diff --git a/deployment/kubernetes/stateful-services/ftgo-kafka-deployment.yml b/deployment/kubernetes/stateful-services/ftgo-kafka-deployment.yml index e1bb7863..0190b53e 100644 --- a/deployment/kubernetes/stateful-services/ftgo-kafka-deployment.yml +++ b/deployment/kubernetes/stateful-services/ftgo-kafka-deployment.yml @@ -26,23 +26,19 @@ spec: terminationGracePeriodSeconds: 10 containers: - name: ftgo-kafka - image: eventuateio/eventuateio-local-kafka:0.14.0 + image: confluentinc/cp-kafka:5.2.4 env: - - name: KAFKA_HEAP_OPTS - value: -Xmx320m -Xms320m - - name: ZOOKEEPER_SERVERS + - name: KAFKA_ADVERTISED_LISTENERS + value: PLAINTEXT://ftgo-kafka:9092 + - name: KAFKA_ZOOKEEPER_CONNECT value: ftgo-zookeeper:2181 + - name: KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR + value: "1" ports: - containerPort: 9092 volumeMounts: - name: ftgo-kafka-persistent-storage - mountPath: /usr/local/kafka-data - - name: ftgo-kafka-config-volume - mountPath: /usr/local/kafka-config - volumes: - - name: ftgo-kafka-config-volume - configMap: - name: ftgo-kafka-config + mountPath: /var/lib/zookeeper/data volumeClaimTemplates: - metadata: name: ftgo-kafka-persistent-storage diff --git a/deployment/kubernetes/stateful-services/ftgo-zookeeper-deployment.yml b/deployment/kubernetes/stateful-services/ftgo-zookeeper-deployment.yml index d300ab39..4bfadd5d 100644 --- a/deployment/kubernetes/stateful-services/ftgo-zookeeper-deployment.yml +++ b/deployment/kubernetes/stateful-services/ftgo-zookeeper-deployment.yml @@ -27,12 +27,15 @@ spec: terminationGracePeriodSeconds: 10 containers: - name: ftgo-zookeeper - image: eventuateio/eventuateio-local-zookeeper:0.14.0 + image: confluentinc/cp-zookeeper:5.2.4 ports: - containerPort: 2181 + env: + - name: ZOOKEEPER_CLIENT_PORT + value: "2181" volumeMounts: - name: ftgo-zookeeper-persistent-storage - mountPath: /usr/local/zookeeper-data + mountPath: /var/lib/kafka/data volumeClaimTemplates: - metadata: name: ftgo-zookeeper-persistent-storage diff --git a/docker-compose-api-gateway-graphql.yml b/docker-compose-api-gateway-graphql.yml new file mode 100644 index 00000000..ce99bd13 --- /dev/null +++ b/docker-compose-api-gateway-graphql.yml @@ -0,0 +1,10 @@ +version: '3' +services: + ftgo-api-gateway-graphql: + build: ./ftgo-api-gateway-graphql + ports: + - "8088:3000" + environment: + ORDER_HISTORY_SERVICE_URL: http://ftgo-order-history-service:8080 + CONSUMER_SERVICE_URL: http://ftgo-consumer-service:8080 + RESTAURANT_SERVICE_URL: http://ftgo-restaurant-service:8080 diff --git a/docker-compose-mariadb.yml b/docker-compose-mariadb.yml deleted file mode 100644 index 214e262c..00000000 --- a/docker-compose-mariadb.yml +++ /dev/null @@ -1,10 +0,0 @@ -version: '3' -services: - mysql: - build: ./mariadb - ports: - - 3306:3306 - environment: - - MYSQL_ROOT_PASSWORD=rootpassword - - MYSQL_USER=mysqluser - - MYSQL_PASSWORD=mysqlpw diff --git a/docker-compose.yml b/docker-compose.yml index 72603dd4..a9e55b0d 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,218 +1,257 @@ version: '3' services: zookeeper: - image: eventuateio/eventuateio-local-zookeeper:0.22.0.RELEASE + image: eventuateio/eventuate-zookeeper:$EVENTUATE_COMMON_VERSION ports: - 2181:2181 - - 2888:2888 - - 3888:3888 + environment: + ZOOKEEPER_CLIENT_PORT: 2181 + KAFKA_HEAP_OPTS: -Xmx64m + kafka: - image: eventuateio/eventuateio-local-kafka:0.22.0.RELEASE + image: "eventuateio/eventuate-kafka:$EVENTUATE_MESSAGING_KAFKA_IMAGE_VERSION" ports: - 9092:9092 depends_on: - zookeeper environment: - - ADVERTISED_HOST_NAME=${DOCKER_HOST_IP?DOCKER_HOST_IP must be set} - - KAFKA_HEAP_OPTS=-Xmx192m -Xms192m - - ZOOKEEPER_SERVERS=zookeeper:2181 + KAFKA_LISTENERS: LC://kafka:29092,LX://kafka:9092 + KAFKA_ADVERTISED_LISTENERS: LC://kafka:29092,LX://${DOCKER_HOST_IP:-localhost}:9092 + KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: LC:PLAINTEXT,LX:PLAINTEXT + KAFKA_INTER_BROKER_LISTENER_NAME: LC + KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1 + KAFKA_HEAP_OPTS: -Xmx192m mysql: - build: ./mysql + build: + context: ./mysql + args: + EVENTUATE_COMMON_VERSION: ${EVENTUATE_COMMON_VERSION?} + EVENTUATE_SAGA_VERSION: ${EVENTUATE_SAGA_VERSION?} ports: - 3306:3306 environment: - MYSQL_ROOT_PASSWORD=rootpassword - MYSQL_USER=mysqluser - MYSQL_PASSWORD=mysqlpw - tram-cdc-service: - image: eventuateio/eventuate-tram-cdc-mysql-service:0.11.1.RELEASE + cdc-service: + image: eventuateio/eventuate-cdc-service:$EVENTUATE_CDC_VERSION ports: - "8099:8080" depends_on: - mysql - kafka environment: - SPRING_DATASOURCE_URL: jdbc:mysql://mysql/eventuate - SPRING_DATASOURCE_USERNAME: mysqluser - SPRING_DATASOURCE_PASSWORD: mysqlpw - SPRING_DATASOURCE_DRIVER_CLASS_NAME: com.mysql.jdbc.Driver - EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS: kafka:9092 + EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS: kafka:29092 EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING: zookeeper:2181 - EVENTUATELOCAL_CDC_DB_USER_NAME: root - EVENTUATELOCAL_CDC_DB_PASSWORD: rootpassword - EVENTUATELOCAL_CDC_SOURCE_TABLE_NAME: message - EVENTUATELOCAL_CDC_LEADERSHIP_LOCK_PATH: /eventuate/cdc/leader/eventuatetram - EVENTUATELOCAL_CDC_BINLOG_CLIENT_ID: 2 - EVENTUATELOCAL_CDC_MY_SQL_BIN_LOG_CLIENT_NAME: ClientEventuateTram - EVENTUATELOCAL_CDC_DB_HISTORY_TOPIC_NAME: db.history.eventuate.tram - eventuate-local-cdc-service: - image: eventuateio/eventuateio-local-new-cdc-service:0.22.1.RELEASE - ports: - - "8098:8080" - depends_on: - - mysql - - kafka - environment: - SPRING_DATASOURCE_URL: jdbc:mysql://mysql/eventuate - SPRING_DATASOURCE_USERNAME: mysqluser - SPRING_DATASOURCE_PASSWORD: mysqlpw - SPRING_DATASOURCE_DRIVER_CLASS_NAME: com.mysql.jdbc.Driver - EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS: kafka:9092 - EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING: zookeeper:2181 - EVENTUATELOCAL_CDC_DB_USER_NAME: root - EVENTUATELOCAL_CDC_DB_PASSWORD: rootpassword - EVENTUATELOCAL_CDC_LEADERSHIP_LOCK_PATH: /eventuate/cdc/leader/eventuatelocal - EVENTUATELOCAL_CDC_BINLOG_CLIENT_ID: 1 - EVENTUATELOCAL_CDC_MY_SQL_BIN_LOG_CLIENT_NAME: ClientEventuateLocal - EVENTUATELOCAL_CDC_DB_HISTORY_TOPIC_NAME: db.history.eventuate.local + EVENTUATE_CDC_PIPELINE_PIPELINE1_TYPE: eventuate-tram + EVENTUATE_CDC_PIPELINE_PIPELINE1_READER: reader1 + EVENTUATE_CDC_PIPELINE_PIPELINE1_EVENTUATEDATABASESCHEMA: ftgo_consumer_service + + EVENTUATE_CDC_PIPELINE_PIPELINE2_TYPE: eventuate-tram + EVENTUATE_CDC_PIPELINE_PIPELINE2_READER: reader1 + EVENTUATE_CDC_PIPELINE_PIPELINE2_EVENTUATEDATABASESCHEMA: ftgo_order_service + + EVENTUATE_CDC_PIPELINE_PIPELINE3_TYPE: eventuate-tram + EVENTUATE_CDC_PIPELINE_PIPELINE3_READER: reader1 + EVENTUATE_CDC_PIPELINE_PIPELINE3_EVENTUATEDATABASESCHEMA: ftgo_kitchen_service + + EVENTUATE_CDC_PIPELINE_PIPELINE4_TYPE: eventuate-tram + EVENTUATE_CDC_PIPELINE_PIPELINE4_READER: reader1 + EVENTUATE_CDC_PIPELINE_PIPELINE4_EVENTUATEDATABASESCHEMA: ftgo_restaurant_service + + EVENTUATE_CDC_PIPELINE_PIPELINE5_TYPE: eventuate-tram + EVENTUATE_CDC_PIPELINE_PIPELINE5_READER: reader1 + EVENTUATE_CDC_PIPELINE_PIPELINE5_EVENTUATEDATABASESCHEMA: ftgo_accounting_service + + EVENTUATE_CDC_PIPELINE_PIPELINE6_TYPE: eventuate-tram + EVENTUATE_CDC_PIPELINE_PIPELINE6_READER: reader1 + EVENTUATE_CDC_PIPELINE_PIPELINE6_EVENTUATEDATABASESCHEMA: ftgoorderhistoryservice + + EVENTUATE_CDC_PIPELINE_PIPELINE7_TYPE: eventuate-local + EVENTUATE_CDC_PIPELINE_PIPELINE7_READER: reader1 + EVENTUATE_CDC_PIPELINE_PIPELINE7_EVENTUATEDATABASESCHEMA: ftgo_accounting_service + + EVENTUATE_CDC_PIPELINE_PIPELINE8_TYPE: eventuate-tram + EVENTUATE_CDC_PIPELINE_PIPELINE8_READER: reader1 + EVENTUATE_CDC_PIPELINE_PIPELINE8_EVENTUATEDATABASESCHEMA: ftgo_delivery_service + + EVENTUATE_CDC_READER_READER1_TYPE: mysql-binlog + EVENTUATE_CDC_READER_READER1_DATASOURCEURL: jdbc:mysql://mysql:3306/eventuate + EVENTUATE_CDC_READER_READER1_DATASOURCEUSERNAME: root + EVENTUATE_CDC_READER_READER1_DATASOURCEPASSWORD: rootpassword + EVENTUATE_CDC_READER_READER1_DATASOURCEDRIVERCLASSNAME: com.mysql.jdbc.Driver + EVENTUATE_CDC_READER_READER1_LEADERSHIPLOCKPATH: /eventuate/cdc/leader/common + EVENTUATE_CDC_READER_READER1_CDCDBUSERNAME: root + EVENTUATE_CDC_READER_READER1_CDCDBPASSWORD: rootpassword + EVENTUATE_CDC_READER_READER1_READOLDDEBEZIUMDBOFFSETSTORAGETOPIC: "false" + EVENTUATE_CDC_READER_READER1_MYSQLBINLOGCLIENTUNIQUEID: 1 + EVENTUATE_CDC_READER_READER1_OFFSETSTOREKEY: MySqlBinlog + EVENTUATE_CDC_READER_READER1_OFFSETSTORAGETOPICNAME: db.history.common + EVENTUATE_CDC_READER_READER1_OUTBOXID: 1 + ftgo-consumer-service: - build: ./ftgo-consumer-service + build: + context: ./ftgo-consumer-service + args: + baseImageVersion: ${EVENTUATE_JAVA_BASE_IMAGE_VERSION} ports: - "8081:8080" depends_on: - mysql - kafka - - tram-cdc-service + - cdc-service environment: - SPRING_DATASOURCE_URL: jdbc:mysql://mysql/ftgoconsumerservice - SPRING_DATASOURCE_USERNAME: mysqluser - SPRING_DATASOURCE_PASSWORD: mysqlpw + SPRING_DATASOURCE_URL: jdbc:mysql://mysql/ftgo_consumer_service + SPRING_DATASOURCE_USERNAME: ftgo_consumer_service_user + SPRING_DATASOURCE_PASSWORD: ftgo_consumer_service_password SPRING_DATASOURCE_DRIVER_CLASS_NAME: com.mysql.jdbc.Driver - EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS: kafka:9092 + EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS: kafka:29092 EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING: zookeeper:2181 - JAVA_OPTS: -Xmx192m - SPRING_SLEUTH_ENABLED: "true" - SPRING_SLEUTH_SAMPLER_PROBABILITY: 1 - SPRING_ZIPKIN_BASE_URL: http://zipkin:9411/ + EVENTUATE_DATABASE_SCHEMA: ftgo_consumer_service ftgo-order-service: - build: ./ftgo-order-service + build: + context: ./ftgo-order-service + args: + baseImageVersion: ${EVENTUATE_JAVA_BASE_IMAGE_VERSION} ports: - "8082:8080" depends_on: - mysql - kafka - - tram-cdc-service + - cdc-service - zipkin environment: - SPRING_DATASOURCE_URL: jdbc:mysql://mysql/ftgoorderservice - SPRING_DATASOURCE_USERNAME: mysqluser - SPRING_DATASOURCE_PASSWORD: mysqlpw + SPRING_DATASOURCE_URL: jdbc:mysql://mysql/ftgo_order_service + SPRING_DATASOURCE_USERNAME: ftgo_order_service_user + SPRING_DATASOURCE_PASSWORD: ftgo_order_service_password SPRING_DATASOURCE_DRIVER_CLASS_NAME: com.mysql.jdbc.Driver - EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS: kafka:9092 + EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS: kafka:29092 EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING: zookeeper:2181 - JAVA_OPTS: -Xmx192m SPRING_SLEUTH_ENABLED: "true" SPRING_SLEUTH_SAMPLER_PROBABILITY: 1 SPRING_ZIPKIN_BASE_URL: http://zipkin:9411/ + EVENTUATE_DATABASE_SCHEMA: ftgo_order_service ftgo-kitchen-service: - build: ./ftgo-kitchen-service + build: + context: ./ftgo-kitchen-service + args: + baseImageVersion: ${EVENTUATE_JAVA_BASE_IMAGE_VERSION} ports: - "8083:8080" depends_on: - mysql - kafka - zookeeper - - tram-cdc-service + - cdc-service environment: - SPRING_DATASOURCE_URL: jdbc:mysql://mysql/ftgokitchenservice - SPRING_DATASOURCE_USERNAME: mysqluser - SPRING_DATASOURCE_PASSWORD: mysqlpw + SPRING_DATASOURCE_URL: jdbc:mysql://mysql/ftgo_kitchen_service + SPRING_DATASOURCE_USERNAME: ftgo_kitchen_service_user + SPRING_DATASOURCE_PASSWORD: ftgo_kitchen_service_password SPRING_DATASOURCE_DRIVER_CLASS_NAME: com.mysql.jdbc.Driver - EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS: kafka:9092 + EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS: kafka:29092 EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING: zookeeper:2181 - JAVA_OPTS: -Xmx192m - SPRING_SLEUTH_ENABLED: "true" - SPRING_SLEUTH_SAMPLER_PROBABILITY: 1 - SPRING_ZIPKIN_BASE_URL: http://zipkin:9411/ + EVENTUATE_DATABASE_SCHEMA: ftgo_kitchen_service ftgo-restaurant-service: - build: ./ftgo-restaurant-service + build: + context: ./ftgo-restaurant-service + args: + baseImageVersion: ${EVENTUATE_JAVA_BASE_IMAGE_VERSION} ports: - "8084:8080" depends_on: - mysql - kafka - zookeeper - - tram-cdc-service + - cdc-service environment: - SPRING_DATASOURCE_URL: jdbc:mysql://mysql/ftgorestaurantservice - SPRING_DATASOURCE_USERNAME: mysqluser - SPRING_DATASOURCE_PASSWORD: mysqlpw + SPRING_DATASOURCE_URL: jdbc:mysql://mysql/ftgo_restaurant_service + SPRING_DATASOURCE_USERNAME: ftgo_restaurant_service_user + SPRING_DATASOURCE_PASSWORD: ftgo_restaurant_service_password SPRING_DATASOURCE_DRIVER_CLASS_NAME: com.mysql.jdbc.Driver - EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS: kafka:9092 + EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS: kafka:29092 EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING: zookeeper:2181 - JAVA_OPTS: -Xmx192m - SPRING_SLEUTH_ENABLED: "true" - SPRING_SLEUTH_SAMPLER_PROBABILITY: 1 - SPRING_ZIPKIN_BASE_URL: http://zipkin:9411/ + EVENTUATE_DATABASE_SCHEMA: ftgo_restaurant_service ftgo-accounting-service: - build: ./ftgo-accounting-service + build: + context: ./ftgo-accounting-service + args: + baseImageVersion: ${EVENTUATE_JAVA_BASE_IMAGE_VERSION} ports: - "8085:8080" depends_on: - mysql - kafka - zookeeper - - eventuate-local-cdc-service + - cdc-service environment: - SPRING_DATASOURCE_URL: jdbc:mysql://mysql/ftgoaccountingservice - SPRING_DATASOURCE_USERNAME: mysqluser - SPRING_DATASOURCE_PASSWORD: mysqlpw + SPRING_DATASOURCE_URL: jdbc:mysql://mysql/ftgo_accounting_service + SPRING_DATASOURCE_USERNAME: ftgo_accounting_service_user + SPRING_DATASOURCE_PASSWORD: ftgo_accounting_service_password SPRING_DATASOURCE_DRIVER_CLASS_NAME: com.mysql.jdbc.Driver - EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS: kafka:9092 + EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS: kafka:29092 EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING: zookeeper:2181 - JAVA_OPTS: -Xmx192m - SPRING_SLEUTH_ENABLED: "true" - SPRING_SLEUTH_SAMPLER_PROBABILITY: 1 - SPRING_ZIPKIN_BASE_URL: http://zipkin:9411/ + EVENTUATE_DATABASE_SCHEMA: ftgo_accounting_service + ftgo-delivery-service: + build: + context: ./ftgo-delivery-service + args: + baseImageVersion: ${EVENTUATE_JAVA_BASE_IMAGE_VERSION} + ports: + - "8089:8080" + depends_on: + - mysql + - kafka + - cdc-service + - zipkin + environment: + SPRING_DATASOURCE_URL: jdbc:mysql://mysql/ftgo_delivery_service + SPRING_DATASOURCE_USERNAME: ftgo_delivery_service_user + SPRING_DATASOURCE_PASSWORD: ftgo_delivery_service_password + SPRING_DATASOURCE_DRIVER_CLASS_NAME: com.mysql.jdbc.Driver + EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS: kafka:29092 + EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING: zookeeper:2181 + SPRING_SLEUTH_ENABLED: "true" + SPRING_SLEUTH_SAMPLER_PROBABILITY: 1 + SPRING_ZIPKIN_BASE_URL: http://zipkin:9411/ + EVENTUATE_DATABASE_SCHEMA: ftgo_delivery_service ftgo-order-history-service: - build: ./ftgo-order-history-service - ports: - - "8086:8080" - depends_on: - - kafka - - zookeeper - - tram-cdc-service - - dynamodblocal - - dynamodblocal-init - environment: - SPRING_DATASOURCE_URL: jdbc:mysql://mysql/ftgoorderhistoryservice - SPRING_DATASOURCE_USERNAME: mysqluser - SPRING_DATASOURCE_PASSWORD: mysqlpw - SPRING_DATASOURCE_DRIVER_CLASS_NAME: com.mysql.jdbc.Driver - EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS: kafka:9092 - EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING: zookeeper:2181 - AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID:-id_key} - AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY:-access_key} - AWS_DYNAMODB_ENDPOINT_URL: http://dynamodblocal:8000 - AWS_REGION: ${AWS_REGION:-us-west-2} - JAVA_OPTS: -Xmx192m - SPRING_SLEUTH_ENABLED: "true" - SPRING_SLEUTH_SAMPLER_PROBABILITY: 1 - SPRING_ZIPKIN_BASE_URL: http://zipkin:9411/ + build: + context: ./ftgo-order-history-service + args: + baseImageVersion: ${EVENTUATE_JAVA_BASE_IMAGE_VERSION} + ports: + - "8086:8080" + depends_on: + - kafka + - zookeeper + - cdc-service + - dynamodblocal + - dynamodblocal-init + environment: + EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS: kafka:29092 + EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING: zookeeper:2181 + AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID:-id_key} + AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY:-access_key} + AWS_DYNAMODB_ENDPOINT_URL: http://dynamodblocal:8000 + AWS_REGION: ${AWS_REGION:-us-west-2} ftgo-api-gateway: - build: ./ftgo-api-gateway + build: + context: ./ftgo-api-gateway + args: + baseImageVersion: ${EVENTUATE_JAVA_BASE_IMAGE_VERSION} ports: - "8087:8080" environment: ORDER_DESTINATIONS_ORDERSERVICEURL: http://ftgo-order-service:8080 ORDER_DESTINATIONS_ORDERHISTORYSERVICEURL: http://ftgo-order-history-service:8080 CONSUMER_DESTINATIONS_CONSUMERSERVICEURL: http://ftgo-consumer-service:8080 - JAVA_OPTS: -Xmx192m SPRING_SLEUTH_ENABLED: "true" SPRING_SLEUTH_SAMPLER_PROBABILITY: 1 SPRING_ZIPKIN_BASE_URL: http://zipkin:9411/ - ftgo-api-gateway-graphql: - build: ./ftgo-api-gateway-graphql - ports: - - "8088:3000" - environment: - ORDER_HISTORY_SERVICE_URL: http://ftgo-order-history-service:8080 - CONSUMER_SERVICE_URL: http://ftgo-consumer-service:8080 - RESTAURANT_SERVICE_URL: http://ftgo-restaurant-service:8080 - zipkin: - image: openzipkin/zipkin:2.5.0 + image: openzipkin/zipkin:2.21 ports: - "9411:9411" environment: @@ -235,3 +274,12 @@ services: AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID:-id_key} AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY:-access_key} AWS_REGION: ${AWS_REGION:-us-west-2} + + kafka-gui: + image: quay.io/cloudhut/kowl:master-1d07695 + ports: + - 9088:8080 + depends_on: + - kafka + environment: + KAFKA_BROKERS: kafka:29092 \ No newline at end of file diff --git a/dynamodblocal-init/Dockerfile b/dynamodblocal-init/Dockerfile index 6c133289..0b242f5a 100644 --- a/dynamodblocal-init/Dockerfile +++ b/dynamodblocal-init/Dockerfile @@ -1,7 +1,9 @@ -FROM ubuntu:18.04 -RUN apt-get update && apt-get install -y python && apt-get install -y wget && wget https://bootstrap.pypa.io/get-pip.py && python get-pip.py +FROM python:2.7.16-alpine3.9 RUN pip install awscli --upgrade COPY create-dynamodb-tables.sh . COPY ftgo-order-history.json . COPY wait-for-dynamodblocal.sh . +RUN chmod +x *.sh +HEALTHCHECK --interval=10s --retries=10 --timeout=3s CMD [[ -f /tables-created ]] + CMD ./wait-for-dynamodblocal.sh && ./create-dynamodb-tables.sh diff --git a/dynamodblocal-init/create-dynamodb-tables.sh b/dynamodblocal-init/create-dynamodb-tables.sh index 8f7c22d1..e46f8899 100755 --- a/dynamodblocal-init/create-dynamodb-tables.sh +++ b/dynamodblocal-init/create-dynamodb-tables.sh @@ -1,4 +1,4 @@ -#! /bin/bash -e +#! /bin/sh -e echo Initializing DynamoDB at endpoint ${AWS_DYNAMODB_ENDPOINT_URL} @@ -10,3 +10,10 @@ echo creating table aws dynamodb $* create-table --region us-west-2 --endpoint-url ${AWS_DYNAMODB_ENDPOINT_URL?} --cli-input-json file://ftgo-order-history.json fi + +touch /tables-created + +while [[ true ]] ; do + echo sleeping... + sleep 3600 +done \ No newline at end of file diff --git a/dynamodblocal-init/wait-for-dynamodblocal.sh b/dynamodblocal-init/wait-for-dynamodblocal.sh index eaf231fd..7e94d1e2 100755 --- a/dynamodblocal-init/wait-for-dynamodblocal.sh +++ b/dynamodblocal-init/wait-for-dynamodblocal.sh @@ -1,4 +1,4 @@ -#! /bin/bash +#! /bin/sh done=false diff --git a/dynamodblocal/Dockerfile b/dynamodblocal/Dockerfile index 2cd8bc1b..40d7452c 100644 --- a/dynamodblocal/Dockerfile +++ b/dynamodblocal/Dockerfile @@ -1,8 +1,3 @@ -FROM openjdk:7-jre - -# Default port for DynamoDB Local -EXPOSE 8000 -RUN mkdir /var/dynamodb_local && (wget -q -O - https://s3-us-west-2.amazonaws.com/dynamodb-local/dynamodb_local_latest.tar.gz | tar -xzf - ) - -# Default command for image -CMD /usr/bin/java ${JAVA_OPTS} -Djava.library.path=. -jar DynamoDBLocal.jar -dbPath /var/dynamodb_local -sharedDb -port 8000 +FROM amazon/dynamodb-local:1.18.0 +ENTRYPOINT java -jar DynamoDBLocal.jar -inMemory -sharedDb -port 8000 +HEALTHCHECK --start-period=5s --interval=5s CMD curl http://localhost:8000 || exit 1 diff --git a/ftgo-accounting-service-api-spec/src/main/resources/messages/AuthorizeCommand.json b/ftgo-accounting-service-api-spec/src/main/resources/messages/AuthorizeCommand.json new file mode 100644 index 00000000..ad483669 --- /dev/null +++ b/ftgo-accounting-service-api-spec/src/main/resources/messages/AuthorizeCommand.json @@ -0,0 +1,22 @@ +{ + "type": "object", + "properties": { + "consumerId": { + "type": "integer", + "format": "int64" + }, + "orderId": { + "type": "integer", + "format": "int64" + }, + "orderTotal": { + "type": "string" + } + }, + "required": [ + "consumerId", + "orderId", + "orderTotal" + ], + "javaInterfaces": ["io.eventuate.tram.commands.common.Command"] +} diff --git a/ftgo-accounting-service-api/build.gradle b/ftgo-accounting-service-api/build.gradle index 93b7c8bb..95508518 100644 --- a/ftgo-accounting-service-api/build.gradle +++ b/ftgo-accounting-service-api/build.gradle @@ -1,6 +1,6 @@ dependencies { - compile "io.eventuate.tram.sagas:eventuate-jpa-sagas-framework:$eventuateTramSagasVersion" + compile "io.eventuate.tram.sagas:eventuate-tram-sagas-spring-participant" compile project(":ftgo-common") -} \ No newline at end of file +} diff --git a/ftgo-accounting-service-contracts/build.gradle b/ftgo-accounting-service-contracts/build.gradle index 8eaad933..8e4e6077 100644 --- a/ftgo-accounting-service-contracts/build.gradle +++ b/ftgo-accounting-service-contracts/build.gradle @@ -1,15 +1,3 @@ -buildscript { - dependencies { - // if using Stub Runner (consumer side) only remove this dependency - classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:$springCloudContractDependenciesVersion" - } - repositories { - mavenCentral() - maven { - url 'https://repo.spring.io/libs-milestone' - } - } -} apply plugin: 'spring-cloud-contract' apply plugin: 'maven-publish' @@ -31,3 +19,7 @@ generateContractTests.enabled = false build.finalizedBy(publish) + +dependencies { + compile 'org.codehaus.groovy:groovy-all:2.4.6' +} diff --git a/ftgo-accounting-service/Dockerfile b/ftgo-accounting-service/Dockerfile index 9d04ce4a..86c4e858 100644 --- a/ftgo-accounting-service/Dockerfile +++ b/ftgo-accounting-service/Dockerfile @@ -1,5 +1,3 @@ -FROM openjdk:8u171-jre-alpine -RUN apk --no-cache add curl -CMD java ${JAVA_OPTS} -jar ftgo-accounting-service.jar -HEALTHCHECK --start-period=30s --interval=5s CMD curl -f http://localhost:8080/actuator/health || exit 1 -COPY build/libs/ftgo-accounting-service.jar . +ARG baseImageVersion +FROM eventuateio/eventuate-examples-docker-images-spring-example-base-image:$baseImageVersion +COPY build/libs/ftgo-accounting-service.jar service.jar diff --git a/ftgo-accounting-service/build.gradle b/ftgo-accounting-service/build.gradle index 4c827831..3a2e768d 100644 --- a/ftgo-accounting-service/build.gradle +++ b/ftgo-accounting-service/build.gradle @@ -1,4 +1,3 @@ - apply plugin: FtgoServicePlugin @@ -6,11 +5,12 @@ dependencies { compile project(":ftgo-accounting-service-api") compile project(":common-swagger") - compile "io.eventuate.tram.sagas:eventuate-tram-sagas-event-sourcing-support:$eventuateTramSagasVersion" + compile "io.eventuate.tram.sagas:eventuate-tram-sagas-event-sourcing-support" - compile "io.eventuate.tram.core:eventuate-tram-jdbc-kafka:$eventuateTramVersion" - compile "io.eventuate.tram.core:eventuate-tram-events:$eventuateTramVersion" - compile "io.eventuate.tram.sagas:eventuate-tram-sagas-simple-dsl:$eventuateTramSagasVersion" + compile "io.eventuate.tram.core:eventuate-tram-spring-jdbc-kafka" + compile "io.eventuate.tram.core:eventuate-tram-spring-events" + compile "io.eventuate.tram.core:eventuate-tram-spring-messaging" + compile "io.eventuate.tram.sagas:eventuate-tram-sagas-spring-orchestration-simple-dsl" compile "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion" compile "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion" @@ -18,16 +18,21 @@ dependencies { compile 'javax.el:javax.el-api:2.2.5' - compile "io.eventuate.client.java:eventuate-client-java-spring:$eventuateClientVersion" - compile "io.eventuate.local.java:eventuate-local-java-jdbc:${eventuateLocalVersion}" - - testCompile "io.eventuate.client.java:eventuate-client-java-jdbc:$eventuateClientVersion" - testCompile "io.eventuate.util:eventuate-util-test:$eventuateUtilVersion" - testCompile "io.eventuate.tram.core:eventuate-tram-test-util:$eventuateTramVersion" + compile "io.eventuate.local.java:eventuate-client-java-spring" + compile "io.eventuate.local.java:eventuate-client-java-spring-jdbc" + compile "io.eventuate.local.java:eventuate-local-java-spring-jdbc" + + compile('org.apache.kafka:kafka-clients:2.3.0') { + force = true + } + + testCompile "io.eventuate.tram.core:eventuate-tram-spring-in-memory" + testCompile "io.eventuate.util:eventuate-util-test" + testCompile "io.eventuate.tram.core:eventuate-tram-test-util" - testCompile "io.eventuate.tram.sagas:eventuate-tram-sagas-in-memory:$eventuateTramSagasVersion" + testCompile "io.eventuate.tram.sagas:eventuate-tram-sagas-spring-in-memory" testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion" testCompile "com.jayway.restassured:rest-assured:$restAssuredVersion" testCompile "com.jayway.jsonpath:json-path:2.3.0" -} \ No newline at end of file +} diff --git a/ftgo-accounting-service/src/main/java/net/chrisrichardson/ftgo/accountingservice/domain/AccountServiceConfiguration.java b/ftgo-accounting-service/src/main/java/net/chrisrichardson/ftgo/accountingservice/domain/AccountServiceConfiguration.java index e5771930..94eb40f3 100644 --- a/ftgo-accounting-service/src/main/java/net/chrisrichardson/ftgo/accountingservice/domain/AccountServiceConfiguration.java +++ b/ftgo-accounting-service/src/main/java/net/chrisrichardson/ftgo/accountingservice/domain/AccountServiceConfiguration.java @@ -2,7 +2,7 @@ import io.eventuate.sync.AggregateRepository; import io.eventuate.sync.EventuateAggregateStore; -import io.eventuate.tram.commands.producer.TramCommandProducerConfiguration; +import io.eventuate.tram.spring.commands.producer.TramCommandProducerConfiguration; import net.chrisrichardson.ftgo.common.CommonConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/ftgo-accounting-service/src/main/java/net/chrisrichardson/ftgo/accountingservice/main/AccountingServiceMain.java b/ftgo-accounting-service/src/main/java/net/chrisrichardson/ftgo/accountingservice/main/AccountingServiceMain.java index f3a0b67d..cb1ece2a 100644 --- a/ftgo-accounting-service/src/main/java/net/chrisrichardson/ftgo/accountingservice/main/AccountingServiceMain.java +++ b/ftgo-accounting-service/src/main/java/net/chrisrichardson/ftgo/accountingservice/main/AccountingServiceMain.java @@ -1,15 +1,13 @@ package net.chrisrichardson.ftgo.accountingservice.main; -import io.eventuate.javaclient.driver.EventuateDriverConfiguration; -import io.eventuate.jdbckafka.TramJdbcKafkaConfiguration; -import io.eventuate.tram.commands.common.ChannelMapping; -import io.eventuate.tram.commands.common.DefaultChannelMapping; -import io.eventuate.tram.commands.producer.TramCommandProducerConfiguration; +import io.eventuate.local.java.spring.javaclient.driver.EventuateDriverConfiguration; +import io.eventuate.tram.spring.commands.producer.TramCommandProducerConfiguration; +import io.eventuate.tram.spring.jdbckafka.TramJdbcKafkaConfiguration; +import net.chrisrichardson.eventstore.examples.customersandorders.commonswagger.CommonSwaggerConfiguration; import net.chrisrichardson.ftgo.accountingservice.messaging.AccountingMessagingConfiguration; import net.chrisrichardson.ftgo.accountingservice.web.AccountingWebConfiguration; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -18,14 +16,10 @@ @Import({AccountingMessagingConfiguration.class, AccountingWebConfiguration.class, TramCommandProducerConfiguration.class, EventuateDriverConfiguration.class, - TramJdbcKafkaConfiguration.class}) + TramJdbcKafkaConfiguration.class, + CommonSwaggerConfiguration.class}) public class AccountingServiceMain { - @Bean - public ChannelMapping channelMapping() { - return new DefaultChannelMapping.DefaultChannelMappingBuilder().build(); - } - public static void main(String[] args) { SpringApplication.run(AccountingServiceMain.class, args); } diff --git a/ftgo-accounting-service/src/main/java/net/chrisrichardson/ftgo/accountingservice/messaging/AccountingMessagingConfiguration.java b/ftgo-accounting-service/src/main/java/net/chrisrichardson/ftgo/accountingservice/messaging/AccountingMessagingConfiguration.java index 003dca33..53b6c49a 100644 --- a/ftgo-accounting-service/src/main/java/net/chrisrichardson/ftgo/accountingservice/messaging/AccountingMessagingConfiguration.java +++ b/ftgo-accounting-service/src/main/java/net/chrisrichardson/ftgo/accountingservice/messaging/AccountingMessagingConfiguration.java @@ -2,9 +2,12 @@ import io.eventuate.javaclient.spring.EnableEventHandlers; import io.eventuate.tram.commands.consumer.CommandDispatcher; -import io.eventuate.tram.consumer.kafka.DuplicateMessageDetector; +import io.eventuate.tram.commands.consumer.CommandDispatcherFactory; +import io.eventuate.tram.spring.commands.consumer.TramCommandConsumerConfiguration; +import io.eventuate.tram.spring.consumer.jdbc.TransactionalNoopDuplicateMessageDetectorConfiguration; +import io.eventuate.tram.spring.events.subscriber.TramEventSubscriberConfiguration; import io.eventuate.tram.events.subscriber.DomainEventDispatcher; -import io.eventuate.tram.messaging.consumer.MessageConsumer; +import io.eventuate.tram.events.subscriber.DomainEventDispatcherFactory; import io.eventuate.tram.sagas.eventsourcingsupport.SagaReplyRequestedEventSubscriber; import net.chrisrichardson.ftgo.accountingservice.domain.Account; import net.chrisrichardson.ftgo.accountingservice.domain.AccountServiceConfiguration; @@ -17,7 +20,7 @@ @Configuration @EnableEventHandlers -@Import({AccountServiceConfiguration.class, CommonConfiguration.class}) +@Import({AccountServiceConfiguration.class, CommonConfiguration.class, TramEventSubscriberConfiguration.class, TramCommandConsumerConfiguration.class, TransactionalNoopDuplicateMessageDetectorConfiguration.class}) public class AccountingMessagingConfiguration { @Bean @@ -26,8 +29,8 @@ public AccountingEventConsumer accountingEventConsumer() { } @Bean - public DomainEventDispatcher domainEventDispatcher(AccountingEventConsumer accountingEventConsumer, MessageConsumer messageConsumer) { - return new DomainEventDispatcher("accountingServiceDomainEventDispatcher", accountingEventConsumer.domainEventHandlers(), messageConsumer); + public DomainEventDispatcher domainEventDispatcher(AccountingEventConsumer accountingEventConsumer, DomainEventDispatcherFactory domainEventDispatcherFactory) { + return domainEventDispatcherFactory.make("accountingServiceDomainEventDispatcher", accountingEventConsumer.domainEventHandlers()); } @Bean @@ -38,13 +41,8 @@ public AccountingServiceCommandHandler accountCommandHandler() { @Bean public CommandDispatcher commandDispatcher(AccountingServiceCommandHandler target, - AccountServiceChannelConfiguration data) { - return new CommandDispatcher(data.getCommandDispatcherId(), target.commandHandlers()); - } - - @Bean - public DuplicateMessageDetector duplicateMessageDetector() { - return new NoopDuplicateMessageDetector(); + AccountServiceChannelConfiguration data, CommandDispatcherFactory commandDispatcherFactory) { + return commandDispatcherFactory.make(data.getCommandDispatcherId(), target.commandHandlers()); } @Bean diff --git a/ftgo-accounting-service/src/main/java/net/chrisrichardson/ftgo/accountingservice/messaging/NoopDuplicateMessageDetector.java b/ftgo-accounting-service/src/main/java/net/chrisrichardson/ftgo/accountingservice/messaging/NoopDuplicateMessageDetector.java deleted file mode 100644 index f0b98a71..00000000 --- a/ftgo-accounting-service/src/main/java/net/chrisrichardson/ftgo/accountingservice/messaging/NoopDuplicateMessageDetector.java +++ /dev/null @@ -1,11 +0,0 @@ -package net.chrisrichardson.ftgo.accountingservice.messaging; - -import io.eventuate.tram.consumer.kafka.DuplicateMessageDetector; - -public class NoopDuplicateMessageDetector implements DuplicateMessageDetector { - - @Override - public boolean isDuplicate(String consumerId, String messageId) { - return false; - } -} diff --git a/ftgo-accounting-service-api/src/main/java/net/chrisrichardson/ftgo/accountservice/api/AuthorizeCommand.java b/ftgo-accounting-service/src/main/java/net/chrisrichardson/ftgo/accountservice/api/AuthorizeCommand.java similarity index 86% rename from ftgo-accounting-service-api/src/main/java/net/chrisrichardson/ftgo/accountservice/api/AuthorizeCommand.java rename to ftgo-accounting-service/src/main/java/net/chrisrichardson/ftgo/accountservice/api/AuthorizeCommand.java index a9f7f38c..d7a16af8 100644 --- a/ftgo-accounting-service-api/src/main/java/net/chrisrichardson/ftgo/accountservice/api/AuthorizeCommand.java +++ b/ftgo-accounting-service/src/main/java/net/chrisrichardson/ftgo/accountservice/api/AuthorizeCommand.java @@ -7,7 +7,6 @@ public class AuthorizeCommand implements Command { private long consumerId; private Long orderId; private Money orderTotal; - private Money amount; private AuthorizeCommand() { } @@ -18,14 +17,6 @@ public AuthorizeCommand(long consumerId, Long orderId, Money orderTotal) { this.orderTotal = orderTotal; } - public Money getAmount() { - return amount; - } - - public void setAmount(Money amount) { - this.amount = amount; - } - public long getConsumerId() { return consumerId; } diff --git a/ftgo-accounting-service/src/main/resources/application.properties b/ftgo-accounting-service/src/main/resources/application.properties index 7c65792a..70c4dcd6 100644 --- a/ftgo-accounting-service/src/main/resources/application.properties +++ b/ftgo-accounting-service/src/main/resources/application.properties @@ -1,23 +1,17 @@ spring.application.name=ftgo-accounting-service +management.endpoint.health.show-details=always + spring.jpa.generate-ddl=true logging.level.org.springframework.orm.jpa=INFO logging.level.org.hibernate.SQL=DEBUG logging.level.io.eventuate=DEBUG logging.level.net.chrisrichardson.ftgo=DEBUG -logging.level.io.eventuate.tram=TRACE -spring.datasource.url=jdbc:mysql://${DOCKER_HOST_IP:localhost}/eventuate -spring.datasource.username=mysqluser -spring.datasource.password=mysqlpw -spring.datasource.driver.class.name=com.mysql.jdbc.Driver -spring.data.mongodb.uri=mongodb://${DOCKER_HOST_IP:localhost}/bankingexampledb +logging.level.io.eventuate.tram=DEBUG +spring.datasource.url=jdbc:mysql://${DOCKER_HOST_IP:localhost}/ftgo_accounting_service +spring.datasource.username=ftgo_accounting_service_user +spring.datasource.password=ftgo_accounting_service_password +spring.datasource.driver-class-name=com.mysql.jdbc.Driver eventuatelocal.kafka.bootstrap.servers=${DOCKER_HOST_IP:localhost}:9092 -eventuatelocal.cdc.db.user.name=root -eventuatelocal.cdc.db.password=rootpassword eventuatelocal.zookeeper.connection.string=${DOCKER_HOST_IP:localhost}:2181 - -aws.access.key_id=id_key -aws.secret.access.key=access_key -aws.dynamodb.endpoint.url=http://${DOCKER_HOST_IP:localhost}:8000 -aws.region=us-west-2 diff --git a/ftgo-accounting-service/src/test/java/net/chrisrichardson/ftgo/accountingservice/messaging/AccountingServiceCommandHandlerTest.java b/ftgo-accounting-service/src/test/java/net/chrisrichardson/ftgo/accountingservice/messaging/AccountingServiceCommandHandlerTest.java index ec94d188..702b2f06 100644 --- a/ftgo-accounting-service/src/test/java/net/chrisrichardson/ftgo/accountingservice/messaging/AccountingServiceCommandHandlerTest.java +++ b/ftgo-accounting-service/src/test/java/net/chrisrichardson/ftgo/accountingservice/messaging/AccountingServiceCommandHandlerTest.java @@ -1,25 +1,22 @@ package net.chrisrichardson.ftgo.accountingservice.messaging; -import io.eventuate.sync.AggregateRepository; import io.eventuate.javaclient.spring.jdbc.EmbeddedTestAggregateStoreConfiguration; -import io.eventuate.tram.commands.common.ChannelMapping; -import io.eventuate.tram.commands.common.DefaultChannelMapping; +import io.eventuate.sync.AggregateRepository; import io.eventuate.tram.commands.producer.CommandProducer; -import io.eventuate.tram.commands.producer.TramCommandProducerConfiguration; +import io.eventuate.tram.spring.commands.producer.TramCommandProducerConfiguration; import io.eventuate.tram.events.publisher.DomainEventPublisher; -import io.eventuate.tram.events.publisher.TramEventsPublisherConfiguration; -import io.eventuate.tram.inmemory.TramInMemoryConfiguration; +import io.eventuate.tram.spring.events.publisher.TramEventsPublisherConfiguration; import io.eventuate.tram.sagas.common.SagaCommandHeaders; +import io.eventuate.tram.sagas.spring.inmemory.TramSagaInMemoryConfiguration; import io.eventuate.tram.testutil.TestMessageConsumer; import io.eventuate.tram.testutil.TestMessageConsumerFactory; +import io.eventuate.util.test.async.Eventually; import net.chrisrichardson.ftgo.accountingservice.domain.Account; import net.chrisrichardson.ftgo.accountingservice.domain.AccountCommand; -import net.chrisrichardson.ftgo.accountingservice.domain.AuthorizeCommandInternal; import net.chrisrichardson.ftgo.accountservice.api.AccountingServiceChannels; import net.chrisrichardson.ftgo.accountservice.api.AuthorizeCommand; import net.chrisrichardson.ftgo.common.Money; import net.chrisrichardson.ftgo.consumerservice.domain.ConsumerCreated; -import io.eventuate.util.test.async.Eventually; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -28,17 +25,11 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; -import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; import org.springframework.test.context.junit4.SpringRunner; -import javax.sql.DataSource; import java.util.Collections; import java.util.HashMap; import java.util.Map; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; @RunWith(SpringRunner.class) @SpringBootTest(classes = AccountingServiceCommandHandlerTest.AccountingServiceCommandHandlerTestConfiguration.class) @@ -50,30 +41,13 @@ public class AccountingServiceCommandHandlerTest { TramCommandProducerConfiguration.class, EmbeddedTestAggregateStoreConfiguration.class, TramEventsPublisherConfiguration.class, // TODO - TramInMemoryConfiguration.class}) + TramSagaInMemoryConfiguration.class}) static public class AccountingServiceCommandHandlerTestConfiguration { - @Bean public TestMessageConsumerFactory testMessageConsumerFactory() { return new TestMessageConsumerFactory(); } - @Bean - public ChannelMapping channelMapping() { - return new DefaultChannelMapping.DefaultChannelMappingBuilder().build(); - } - - @Bean - public DataSource dataSource() { - EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); - return builder.setType(EmbeddedDatabaseType.H2) - .addScript("eventuate-tram-embedded-schema.sql") - .addScript("eventuate-tram-sagas-embedded.sql") - .addScript("eventuate-embedded-schema.sql") - .build(); - } - - } @Autowired @@ -90,7 +64,7 @@ public DataSource dataSource() { private AggregateRepository accountRepository; @Test - public void shouldReply() throws InterruptedException, ExecutionException, TimeoutException { + public void shouldReply() { TestMessageConsumer testMessageConsumer = testMessageConsumerFactory.make(); diff --git a/ftgo-api-gateway-graphql/Dockerfile b/ftgo-api-gateway-graphql/Dockerfile index d3c0f875..175a5b95 100644 --- a/ftgo-api-gateway-graphql/Dockerfile +++ b/ftgo-api-gateway-graphql/Dockerfile @@ -1,6 +1,8 @@ FROM node:9.11.2-alpine COPY package.json . +COPY package-lock.json . RUN npm install +RUN npm config set unsafe-perm true && npm install -g typescript COPY tsconfig.json . ADD src ./src RUN npm run build diff --git a/ftgo-api-gateway-graphql/package-lock.json b/ftgo-api-gateway-graphql/package-lock.json new file mode 100644 index 00000000..9bbb919a --- /dev/null +++ b/ftgo-api-gateway-graphql/package-lock.json @@ -0,0 +1,5898 @@ +{ + "requires": true, + "lockfileVersion": 1, + "dependencies": { + "@babel/code-frame": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz", + "integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/core": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.6.2.tgz", + "integrity": "sha512-l8zto/fuoZIbncm+01p8zPSDZu/VuuJhAfA7d/AbzM09WR7iVhavvfNDYCNpo1VvLk6E6xgAoP9P+/EMJHuRkQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.5.5", + "@babel/generator": "^7.6.2", + "@babel/helpers": "^7.6.2", + "@babel/parser": "^7.6.2", + "@babel/template": "^7.6.0", + "@babel/traverse": "^7.6.2", + "@babel/types": "^7.6.0", + "convert-source-map": "^1.1.0", + "debug": "^4.1.0", + "json5": "^2.1.0", + "lodash": "^4.17.13", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.6.2.tgz", + "integrity": "sha512-j8iHaIW4gGPnViaIHI7e9t/Hl8qLjERI6DcV9kEpAIDJsAOrcnXqRS7t+QbhL76pwbtqP+QCQLL0z1CyVmtjjQ==", + "dev": true, + "requires": { + "@babel/types": "^7.6.0", + "jsesc": "^2.5.1", + "lodash": "^4.17.13", + "source-map": "^0.5.0" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "@babel/helper-function-name": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", + "integrity": "sha512-A95XEoCpb3TO+KZzJ4S/5uW5fNe26DjBGqf1o9ucyLyCmi1dXq/B3c8iaWTfBk3VvetUxl16e8tIrd5teOCfGw==", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", + "integrity": "sha512-r2DbJeg4svYvt3HOS74U4eWKsUAMRH01Z1ds1zx8KNTPtpTL5JAsdFv8BNyOpVqdFhHkkRDIg5B4AsxmkjAlmQ==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", + "integrity": "sha512-CYAOUCARwExnEixLdB6sDm2dIJ/YgEAKDM1MOeMeZu9Ld/bDgVo8aiWrXwcY7OBh+1Ea2uUcVRcxKk0GJvW7QA==", + "dev": true + }, + "@babel/helper-split-export-declaration": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.4.tgz", + "integrity": "sha512-Ro/XkzLf3JFITkW6b+hNxzZ1n5OQ80NvIUdmHspih1XAhtN3vPTuUFT4eQnela+2MaZ5ulH+iyP513KJrxbN7Q==", + "dev": true, + "requires": { + "@babel/types": "^7.4.4" + } + }, + "@babel/helpers": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.6.2.tgz", + "integrity": "sha512-3/bAUL8zZxYs1cdX2ilEE0WobqbCmKWr/889lf2SS0PpDcpEIY8pb1CCyz0pEcX3pEb+MCbks1jIokz2xLtGTA==", + "dev": true, + "requires": { + "@babel/template": "^7.6.0", + "@babel/traverse": "^7.6.2", + "@babel/types": "^7.6.0" + } + }, + "@babel/highlight": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.5.0.tgz", + "integrity": "sha512-7dV4eu9gBxoM0dAnj/BCFDW9LFU0zvTrkq0ugM7pnHEgguOEeOz1so2ZghEdzviYzQEED0r4EAgpsBChKy1TRQ==", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.6.2.tgz", + "integrity": "sha512-mdFqWrSPCmikBoaBYMuBulzTIKuXVPtEISFbRRVNwMWpCms/hmE2kRq0bblUHaNRKrjRlmVbx1sDHmjmRgD2Xg==", + "dev": true + }, + "@babel/plugin-syntax-object-rest-spread": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.2.0.tgz", + "integrity": "sha512-t0JKGgqk2We+9may3t0xDdmneaXmyxq0xieYcKHxIsrJO64n1OiMWNUtc5gQK1PA0NpdCRrtZp4z+IUaKugrSA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/template": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.6.0.tgz", + "integrity": "sha512-5AEH2EXD8euCk446b7edmgFdub/qfH1SN6Nii3+fyXP807QRx9Q73A2N5hNwRRslC2H9sNzaFhsPubkS4L8oNQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.6.0", + "@babel/types": "^7.6.0" + } + }, + "@babel/traverse": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.6.2.tgz", + "integrity": "sha512-8fRE76xNwNttVEF2TwxJDGBLWthUkHWSldmfuBzVRmEDWOtu4XdINTgN7TDWzuLg4bbeIMLvfMFD9we5YcWkRQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.5.5", + "@babel/generator": "^7.6.2", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.4.4", + "@babel/parser": "^7.6.2", + "@babel/types": "^7.6.0", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.13" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.6.1.tgz", + "integrity": "sha512-X7gdiuaCmA0uRjCmRtYJNAVCc/q+5xSgsfKJHqMN4iNLILX39677fJE1O40arPMh0TTtS9ItH67yre6c7k6t0g==", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.13", + "to-fast-properties": "^2.0.0" + } + }, + "@cnakazawa/watch": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.3.tgz", + "integrity": "sha512-r5160ogAvGyHsal38Kux7YYtodEKOj89RGb28ht1jh3SJb08VwRwAKKJL0bGb04Zd/3r9FL3BFIc3bBidYffCA==", + "dev": true, + "requires": { + "exec-sh": "^0.3.2", + "minimist": "^1.2.0" + } + }, + "@jest/console": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-24.9.0.tgz", + "integrity": "sha512-Zuj6b8TnKXi3q4ymac8EQfc3ea/uhLeCGThFqXeC8H9/raaH8ARPUTdId+XyGd03Z4In0/VjD2OYFcBF09fNLQ==", + "dev": true, + "requires": { + "@jest/source-map": "^24.9.0", + "chalk": "^2.0.1", + "slash": "^2.0.0" + } + }, + "@jest/core": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.9.0.tgz", + "integrity": "sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/reporters": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-changed-files": "^24.9.0", + "jest-config": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-resolve-dependencies": "^24.9.0", + "jest-runner": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "jest-watcher": "^24.9.0", + "micromatch": "^3.1.10", + "p-each-series": "^1.0.0", + "realpath-native": "^1.1.0", + "rimraf": "^2.5.4", + "slash": "^2.0.0", + "strip-ansi": "^5.0.0" + } + }, + "@jest/environment": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.9.0.tgz", + "integrity": "sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ==", + "dev": true, + "requires": { + "@jest/fake-timers": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/fake-timers": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-24.9.0.tgz", + "integrity": "sha512-eWQcNa2YSwzXWIMC5KufBh3oWRIijrQFROsIqt6v/NS9Io/gknw1jsAC9c+ih/RQX4A3O7SeWAhQeN0goKhT9A==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0" + } + }, + "@jest/reporters": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.9.0.tgz", + "integrity": "sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw==", + "dev": true, + "requires": { + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.2", + "istanbul-lib-coverage": "^2.0.2", + "istanbul-lib-instrument": "^3.0.1", + "istanbul-lib-report": "^2.0.4", + "istanbul-lib-source-maps": "^3.0.1", + "istanbul-reports": "^2.2.6", + "jest-haste-map": "^24.9.0", + "jest-resolve": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.6.0", + "node-notifier": "^5.4.2", + "slash": "^2.0.0", + "source-map": "^0.6.0", + "string-length": "^2.0.0" + } + }, + "@jest/source-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-24.9.0.tgz", + "integrity": "sha512-/Xw7xGlsZb4MJzNDgB7PW5crou5JqWiBQaz6xyPd3ArOg2nfn/PunV8+olXbbEZzNl591o5rWKE9BRDaFAuIBg==", + "dev": true, + "requires": { + "callsites": "^3.0.0", + "graceful-fs": "^4.1.15", + "source-map": "^0.6.0" + } + }, + "@jest/test-result": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-24.9.0.tgz", + "integrity": "sha512-XEFrHbBonBJ8dGp2JmF8kP/nQI/ImPpygKHwQ/SY+es59Z3L5PI4Qb9TQQMAEeYsThG1xF0k6tmG0tIKATNiiA==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/istanbul-lib-coverage": "^2.0.0" + } + }, + "@jest/test-sequencer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-24.9.0.tgz", + "integrity": "sha512-6qqsU4o0kW1dvA95qfNog8v8gkRN9ph6Lz7r96IvZpHdNipP2cBcb07J1Z45mz/VIS01OHJ3pY8T5fUY38tg4A==", + "dev": true, + "requires": { + "@jest/test-result": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-runner": "^24.9.0", + "jest-runtime": "^24.9.0" + } + }, + "@jest/transform": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-24.9.0.tgz", + "integrity": "sha512-TcQUmyNRxV94S0QpMOnZl0++6RMiqpbH/ZMccFB/amku6Uwvyb1cjYX7xkp5nGNkbX4QPH/FcB6q1HBTHynLmQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/types": "^24.9.0", + "babel-plugin-istanbul": "^5.1.0", + "chalk": "^2.0.1", + "convert-source-map": "^1.4.0", + "fast-json-stable-stringify": "^2.0.0", + "graceful-fs": "^4.1.15", + "jest-haste-map": "^24.9.0", + "jest-regex-util": "^24.9.0", + "jest-util": "^24.9.0", + "micromatch": "^3.1.10", + "pirates": "^4.0.1", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "source-map": "^0.6.1", + "write-file-atomic": "2.4.1" + } + }, + "@jest/types": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-24.9.0.tgz", + "integrity": "sha512-XKK7ze1apu5JWQ5eZjHITP66AX+QsLlbaJRBGYr8pNzwcAE2JVkwnf0yqjHTsDRcjR0mujy/NmZMXw5kl+kGBw==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^1.1.1", + "@types/yargs": "^13.0.0" + } + }, + "@types/accepts": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/accepts/-/accepts-1.3.5.tgz", + "integrity": "sha512-jOdnI/3qTpHABjM5cx1Hc0sKsPoYCp+DP/GJRGtDlPd7fiV9oXGGIcjW/ZOxLIvjGz8MA+uMZI9metHlgqbgwQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/babel__core": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.3.tgz", + "integrity": "sha512-8fBo0UR2CcwWxeX7WIIgJ7lXjasFxoYgRnFHUj+hRvKkpiBJbxhdAPTCY6/ZKM0uxANFVzt4yObSLuTiTnazDA==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "@types/babel__generator": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.0.tgz", + "integrity": "sha512-c1mZUu4up5cp9KROs/QAw0gTeHrw/x7m52LcnvMxxOZ03DmLwPV0MlGmlgzV3cnSdjhJOZsj7E7FHeioai+egw==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@types/babel__template": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz", + "integrity": "sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==", + "dev": true, + "requires": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@types/babel__traverse": { + "version": "7.0.7", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.7.tgz", + "integrity": "sha512-CeBpmX1J8kWLcDEnI3Cl2Eo6RfbGvzUctA+CjZUhOKDFbLfcr7fc4usEqLNWetrlJd7RhAkyYe2czXop4fICpw==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/body-parser": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.1.tgz", + "integrity": "sha512-RoX2EZjMiFMjZh9lmYrwgoP9RTpAjSHiJxdp4oidAQVO02T7HER3xj9UKue5534ULWeqVEkujhWcyvUce+d68w==", + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/bunyan": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@types/bunyan/-/bunyan-1.8.6.tgz", + "integrity": "sha512-YiozPOOsS6bIuz31ilYqR5SlLif4TBWsousN2aCWLi5233nZSX19tFbcQUPdR7xJ8ypPyxkCGNxg0CIV5n9qxQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.32", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.32.tgz", + "integrity": "sha512-4r8qa0quOvh7lGD0pre62CAb1oni1OO6ecJLGCezTmhQ8Fz50Arx9RUszryR8KlgK6avuSXvviL6yWyViQABOg==", + "requires": { + "@types/node": "*" + } + }, + "@types/cookies": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/@types/cookies/-/cookies-0.7.4.tgz", + "integrity": "sha512-oTGtMzZZAVuEjTwCjIh8T8FrC8n/uwy+PG0yTvQcdZ7etoel7C7/3MSd7qrukENTgQtotG7gvBlBojuVs7X5rw==", + "requires": { + "@types/connect": "*", + "@types/express": "*", + "@types/keygrip": "*", + "@types/node": "*" + } + }, + "@types/express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.1.tgz", + "integrity": "sha512-VfH/XCP0QbQk5B5puLqTLEeFgR8lfCJHZJKkInZ9mkYd+u8byX0kztXEQxEk4wZXJs8HI+7km2ALXjn4YKcX9w==", + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.16.9", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.16.9.tgz", + "integrity": "sha512-GqpaVWR0DM8FnRUJYKlWgyARoBUAVfRIeVDZQKOttLFp5SmhhF9YFIYeTPwMd/AXfxlP7xVO2dj1fGu0Q+krKQ==", + "requires": { + "@types/node": "*", + "@types/range-parser": "*" + } + }, + "@types/graphql": { + "version": "14.5.0", + "resolved": "https://registry.npmjs.org/@types/graphql/-/graphql-14.5.0.tgz", + "integrity": "sha512-MOkzsEp1Jk5bXuAsHsUi6BVv0zCO+7/2PTiZMXWDSsMXvNU6w/PLMQT2vHn8hy2i0JqojPz1Sz6rsFjHtsU0lA==", + "requires": { + "graphql": "*" + } + }, + "@types/http-assert": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.1.tgz", + "integrity": "sha512-PGAK759pxyfXE78NbKxyfRcWYA/KwW17X290cNev/qAsn9eQIxkH4shoNBafH37wewhDG/0p1cHPbK6+SzZjWQ==" + }, + "@types/istanbul-lib-coverage": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", + "integrity": "sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg==", + "dev": true + }, + "@types/istanbul-lib-report": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-1.1.1.tgz", + "integrity": "sha512-3BUTyMzbZa2DtDI2BkERNC6jJw2Mr2Y0oGI7mRxYNBPxppbtEK1F66u3bKwU2g+wxwWI7PAoRpJnOY1grJqzHg==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*" + } + }, + "@types/istanbul-reports": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.1.tgz", + "integrity": "sha512-UpYjBi8xefVChsCoBpKShdxTllC9pwISirfoZsUa2AAdQg/Jd2KQGtSbw+ya7GPo7x/wAPlH6JBhKhAsXUEZNA==", + "dev": true, + "requires": { + "@types/istanbul-lib-coverage": "*", + "@types/istanbul-lib-report": "*" + } + }, + "@types/keygrip": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/keygrip/-/keygrip-1.0.1.tgz", + "integrity": "sha1-/1QEYtL7TQqIRBzq8n0oewHD2Hg=" + }, + "@types/koa": { + "version": "2.0.50", + "resolved": "https://registry.npmjs.org/@types/koa/-/koa-2.0.50.tgz", + "integrity": "sha512-TcgOD2lh0EISSadAk1DOBYw7kNoY9XdeB3vEMOKiDDaTMYm+V54nyPsU7Ulb/htb5OBIR79RgTeCWntCcophLw==", + "requires": { + "@types/accepts": "*", + "@types/cookies": "*", + "@types/http-assert": "*", + "@types/keygrip": "*", + "@types/koa-compose": "*", + "@types/node": "*" + } + }, + "@types/koa-bodyparser": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@types/koa-bodyparser/-/koa-bodyparser-4.3.0.tgz", + "integrity": "sha512-aB/vwwq4G9FAtKzqZ2p8UHTscXxZvICFKVjuckqxCtkX1Ro7F5KHkTCUqTRZFBgDoEkmeca+bFLI1bIsdPPZTA==", + "requires": { + "@types/koa": "*" + } + }, + "@types/koa-compose": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@types/koa-compose/-/koa-compose-3.2.4.tgz", + "integrity": "sha512-ioou0rxkuWL+yBQYsHUQAzRTfVxAg8Y2VfMftU+Y3RA03/MzuFL0x/M2sXXj3PkfnENbHsjeHR1aMdezLYpTeA==", + "requires": { + "@types/koa": "*" + } + }, + "@types/koa-router": { + "version": "7.0.42", + "resolved": "https://registry.npmjs.org/@types/koa-router/-/koa-router-7.0.42.tgz", + "integrity": "sha512-mggrNY7Ywwjt7QjaMAlbb1ixE+v7AFskOeyKdmZT/NvPVEAo48gYUxIcF8ILlMc3eg1bo6SxNoUcbxhTv7edrA==", + "requires": { + "@types/koa": "*" + } + }, + "@types/mime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz", + "integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==" + }, + "@types/mocha": { + "version": "5.2.7", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-5.2.7.tgz", + "integrity": "sha512-NYrtPht0wGzhwe9+/idPaBB+TqkY9AhTvOLMkThm0IoEfLaiVQZwBwyJ5puCkO3AUCWrmcoePjp2mbFocKy4SQ==", + "dev": true + }, + "@types/node": { + "version": "12.7.11", + "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.11.tgz", + "integrity": "sha512-Otxmr2rrZLKRYIybtdG/sgeO+tHY20GxeDjcGmUnmmlCWyEnv2a2x1ZXBo3BTec4OiTXMQCiazB8NMBf0iRlFw==" + }, + "@types/range-parser": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", + "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" + }, + "@types/restify": { + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/@types/restify/-/restify-5.0.11.tgz", + "integrity": "sha512-UQposjIQewSD4c4gvvW/3kyhAX4RF/rIcoivFYRrRF4km9phHK6PaiST+Ueau5Lq/4b3XYt6iGgzwTxn645PQw==", + "requires": { + "@types/bunyan": "*", + "@types/node": "*", + "@types/spdy": "*" + } + }, + "@types/serve-static": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.3.tgz", + "integrity": "sha512-oprSwp094zOglVrXdlo/4bAHtKTAxX6VT8FOZlBKrmyLbNvE1zxZyJ6yikMVtHIvwP45+ZQGJn+FdXGKTozq0g==", + "requires": { + "@types/express-serve-static-core": "*", + "@types/mime": "*" + } + }, + "@types/spdy": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/@types/spdy/-/spdy-3.4.4.tgz", + "integrity": "sha512-N9LBlbVRRYq6HgYpPkqQc3a9HJ/iEtVZToW6xlTtJiMhmRJ7jJdV7TaZQJw/Ve/1ePUsQiCTDc4JMuzzag94GA==", + "requires": { + "@types/node": "*" + } + }, + "@types/stack-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", + "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", + "dev": true + }, + "@types/yargs": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.3.tgz", + "integrity": "sha512-K8/LfZq2duW33XW/tFwEAfnZlqIfVsoyRB3kfXdPXYhl0nfM8mmh7GS0jg7WrX2Dgq/0Ha/pR1PaR+BvmWwjiQ==", + "dev": true, + "requires": { + "@types/yargs-parser": "*" + } + }, + "@types/yargs-parser": { + "version": "13.1.0", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-13.1.0.tgz", + "integrity": "sha512-gCubfBUZ6KxzoibJ+SCUc/57Ms1jz5NjHe4+dI2krNmU5zCPAphyLJYyTOg06ueIyfj+SaCUqmzun7ImlxDcKg==", + "dev": true + }, + "@types/zen-observable": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.0.tgz", + "integrity": "sha512-te5lMAWii1uEJ4FwLjzdlbw3+n0FZNOvFXHxQDKeT0dilh7HOzdMzV2TrJVUzq8ep7J4Na8OUYPRLSQkJHAlrg==" + }, + "@wry/context": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@wry/context/-/context-0.4.4.tgz", + "integrity": "sha512-LrKVLove/zw6h2Md/KZyWxIkFM6AoyKp71OqpH9Hiip1csjPVoD3tPxlbQUNxEnHENks3UGgNpSBCAfq9KWuag==", + "requires": { + "@types/node": ">=6", + "tslib": "^1.9.3" + } + }, + "@wry/equality": { + "version": "0.1.9", + "resolved": "https://registry.npmjs.org/@wry/equality/-/equality-0.1.9.tgz", + "integrity": "sha512-mB6ceGjpMGz1ZTza8HYnrPGos2mC6So4NhS1PtZ8s4Qt0K7fBiIGhpSxUbQmhwcSWE3no+bYxmI2OL6KuXYmoQ==", + "requires": { + "tslib": "^1.9.3" + } + }, + "abab": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.2.tgz", + "integrity": "sha512-2scffjvioEmNz0OyDSLGWDfKCVwaKc6l9Pm9kOIREU13ClXZvHpg/nRL5xyjSSSLhOnXqft2HpsAzNEEA8cFFg==", + "dev": true + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + }, + "dependencies": { + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "requires": { + "mime-db": "1.40.0" + } + } + } + }, + "acorn": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "dev": true + }, + "acorn-globals": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz", + "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==", + "dev": true, + "requires": { + "acorn": "^6.0.1", + "acorn-walk": "^6.0.1" + }, + "dependencies": { + "acorn": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.3.0.tgz", + "integrity": "sha512-/czfa8BwS88b9gWQVhc8eknunSA2DoJpJyTQkhheIf5E48u1N0R4q/YxxsAeqRrmK9TQ/uYfgLDfZo91UlANIA==", + "dev": true + } + } + }, + "acorn-walk": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz", + "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==", + "dev": true + }, + "ajv": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", + "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "dev": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + } + }, + "apollo-boost": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/apollo-boost/-/apollo-boost-0.1.6.tgz", + "integrity": "sha512-z29fxgaNV7HLCcjgS7MgqPKdMc9is1pf4gaVk2ma9JvMZEwq7d5mAc1sBSq+u6de/oUdElES6f1wc1v1HruU+A==", + "dev": true, + "requires": { + "apollo-cache-inmemory": "^1.2.1", + "apollo-client": "^2.3.1", + "apollo-link": "^1.0.6", + "apollo-link-error": "^1.0.3", + "apollo-link-http": "^1.3.1", + "apollo-link-state": "^0.4.0", + "graphql-tag": "^2.4.2" + } + }, + "apollo-cache": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/apollo-cache/-/apollo-cache-1.3.2.tgz", + "integrity": "sha512-+KA685AV5ETEJfjZuviRTEImGA11uNBp/MJGnaCvkgr+BYRrGLruVKBv6WvyFod27WEB2sp7SsG8cNBKANhGLg==", + "requires": { + "apollo-utilities": "^1.3.2", + "tslib": "^1.9.3" + }, + "dependencies": { + "apollo-utilities": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.3.2.tgz", + "integrity": "sha512-JWNHj8XChz7S4OZghV6yc9FNnzEXj285QYp/nLNh943iObycI5GTDO3NGR9Dth12LRrSFMeDOConPfPln+WGfg==", + "requires": { + "@wry/equality": "^0.1.2", + "fast-json-stable-stringify": "^2.0.0", + "ts-invariant": "^0.4.0", + "tslib": "^1.9.3" + } + } + } + }, + "apollo-cache-control": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/apollo-cache-control/-/apollo-cache-control-0.1.1.tgz", + "integrity": "sha512-XJQs167e9u+e5ybSi51nGYr70NPBbswdvTEHtbtXbwkZ+n9t0SLPvUcoqceayOSwjK1XYOdU/EKPawNdb3rLQA==", + "requires": { + "graphql-extensions": "^0.0.x" + } + }, + "apollo-cache-inmemory": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/apollo-cache-inmemory/-/apollo-cache-inmemory-1.6.3.tgz", + "integrity": "sha512-S4B/zQNSuYc0M/1Wq8dJDTIO9yRgU0ZwDGnmlqxGGmFombOZb9mLjylewSfQKmjNpciZ7iUIBbJ0mHlPJTzdXg==", + "requires": { + "apollo-cache": "^1.3.2", + "apollo-utilities": "^1.3.2", + "optimism": "^0.10.0", + "ts-invariant": "^0.4.0", + "tslib": "^1.9.3" + }, + "dependencies": { + "apollo-utilities": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.3.2.tgz", + "integrity": "sha512-JWNHj8XChz7S4OZghV6yc9FNnzEXj285QYp/nLNh943iObycI5GTDO3NGR9Dth12LRrSFMeDOConPfPln+WGfg==", + "requires": { + "@wry/equality": "^0.1.2", + "fast-json-stable-stringify": "^2.0.0", + "ts-invariant": "^0.4.0", + "tslib": "^1.9.3" + } + } + } + }, + "apollo-client": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/apollo-client/-/apollo-client-2.6.4.tgz", + "integrity": "sha512-oWOwEOxQ9neHHVZrQhHDbI6bIibp9SHgxaLRVPoGvOFy7OH5XUykZE7hBQAVxq99tQjBzgytaZffQkeWo1B4VQ==", + "requires": { + "@types/zen-observable": "^0.8.0", + "apollo-cache": "1.3.2", + "apollo-link": "^1.0.0", + "apollo-utilities": "1.3.2", + "symbol-observable": "^1.0.2", + "ts-invariant": "^0.4.0", + "tslib": "^1.9.3", + "zen-observable": "^0.8.0" + }, + "dependencies": { + "apollo-utilities": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.3.2.tgz", + "integrity": "sha512-JWNHj8XChz7S4OZghV6yc9FNnzEXj285QYp/nLNh943iObycI5GTDO3NGR9Dth12LRrSFMeDOConPfPln+WGfg==", + "requires": { + "@wry/equality": "^0.1.2", + "fast-json-stable-stringify": "^2.0.0", + "ts-invariant": "^0.4.0", + "tslib": "^1.9.3" + } + } + } + }, + "apollo-engine": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/apollo-engine/-/apollo-engine-1.1.2.tgz", + "integrity": "sha512-XALQo4VjyMaOkCeuO5z0j68eSvOxh7KhZkWW3Vqj8ufxk2WbJGCACU7XQEvD6HyGBwtDDXxV8KvZyU0CDisi1Q==", + "requires": { + "@types/connect": "^3.4.31", + "@types/express": "^4.0.36", + "@types/koa": "^2.0.39", + "@types/koa-bodyparser": "^4.2.0", + "@types/koa-router": "^7.0.27", + "@types/restify": "^5.0.7", + "apollo-engine-binary-darwin": "0.2018.6-20-gc0e4bb519", + "apollo-engine-binary-linux": "0.2018.6-20-gc0e4bb519", + "apollo-engine-binary-windows": "0.2018.6-20-gc0e4bb519" + } + }, + "apollo-engine-binary-darwin": { + "version": "0.2018.6-20-gc0e4bb519", + "resolved": "https://registry.npmjs.org/apollo-engine-binary-darwin/-/apollo-engine-binary-darwin-0.2018.6-20-gc0e4bb519.tgz", + "integrity": "sha512-yQB26z3s/lMCBOpoKjL0IiJPuk+EHwoY6zd9nZz0A99/461ZayE02LjV8MwB4HDvSPmvtaanrhQhTaLH6sLHfA==", + "optional": true + }, + "apollo-engine-binary-linux": { + "version": "0.2018.6-20-gc0e4bb519", + "resolved": "https://registry.npmjs.org/apollo-engine-binary-linux/-/apollo-engine-binary-linux-0.2018.6-20-gc0e4bb519.tgz", + "integrity": "sha512-HTUysRxRmwYQmbztuvytq5I2dgbPT/Z0J5HNz0Hx5/Ej19EHIb0v82mFMdMZKGRKHvOfao8XGG3/++C1vSQGiw==", + "optional": true + }, + "apollo-engine-binary-windows": { + "version": "0.2018.6-20-gc0e4bb519", + "resolved": "https://registry.npmjs.org/apollo-engine-binary-windows/-/apollo-engine-binary-windows-0.2018.6-20-gc0e4bb519.tgz", + "integrity": "sha512-Cj0FSI8MDHcDc8EknzcqBP57R5S6gLIdD4/lpQaOtZureS/Ijx3mltZ0csmn5v8S0dDaMM6zjElBMDfwl6aEDg==", + "optional": true + }, + "apollo-link": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/apollo-link/-/apollo-link-1.2.2.tgz", + "integrity": "sha512-Uk/BC09dm61DZRDSu52nGq0nFhq7mcBPTjy5EEH1eunJndtCaNXQhQz/BjkI2NdrfGI+B+i5he6YSoRBhYizdw==", + "requires": { + "@types/graphql": "0.12.6", + "apollo-utilities": "^1.0.0", + "zen-observable-ts": "^0.8.9" + }, + "dependencies": { + "@types/graphql": { + "version": "0.12.6", + "resolved": "http://registry.npmjs.org/@types/graphql/-/graphql-0.12.6.tgz", + "integrity": "sha512-wXAVyLfkG1UMkKOdMijVWFky39+OD/41KftzqfX1Oejd0Gm6dOIKjCihSVECg6X7PHjftxXmfOKA/d1H79ZfvQ==" + } + } + }, + "apollo-link-error": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/apollo-link-error/-/apollo-link-error-1.0.9.tgz", + "integrity": "sha512-82qgIH0UC039kcvcFDJkY8GuUOgPFrkYe/0zNFkA0AXvuvL4RHxWb8SWRiPwtP8jKaKEOedyjopfyH84aLiu7Q==", + "dev": true, + "requires": { + "apollo-link": "^1.2.2" + } + }, + "apollo-link-http": { + "version": "1.5.16", + "resolved": "https://registry.npmjs.org/apollo-link-http/-/apollo-link-http-1.5.16.tgz", + "integrity": "sha512-IA3xA/OcrOzINRZEECI6IdhRp/Twom5X5L9jMehfzEo2AXdeRwAMlH5LuvTZHgKD8V1MBnXdM6YXawXkTDSmJw==", + "requires": { + "apollo-link": "^1.2.13", + "apollo-link-http-common": "^0.2.15", + "tslib": "^1.9.3" + }, + "dependencies": { + "apollo-link": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/apollo-link/-/apollo-link-1.2.13.tgz", + "integrity": "sha512-+iBMcYeevMm1JpYgwDEIDt/y0BB7VWyvlm/7x+TIPNLHCTCMgcEgDuW5kH86iQZWo0I7mNwQiTOz+/3ShPFmBw==", + "requires": { + "apollo-utilities": "^1.3.0", + "ts-invariant": "^0.4.0", + "tslib": "^1.9.3", + "zen-observable-ts": "^0.8.20" + } + }, + "apollo-utilities": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.3.2.tgz", + "integrity": "sha512-JWNHj8XChz7S4OZghV6yc9FNnzEXj285QYp/nLNh943iObycI5GTDO3NGR9Dth12LRrSFMeDOConPfPln+WGfg==", + "requires": { + "@wry/equality": "^0.1.2", + "fast-json-stable-stringify": "^2.0.0", + "ts-invariant": "^0.4.0", + "tslib": "^1.9.3" + } + }, + "zen-observable-ts": { + "version": "0.8.20", + "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.20.tgz", + "integrity": "sha512-2rkjiPALhOtRaDX6pWyNqK1fnP5KkJJybYebopNSn6wDG1lxBoFs2+nwwXKoA6glHIrtwrfBBy6da0stkKtTAA==", + "requires": { + "tslib": "^1.9.3", + "zen-observable": "^0.8.0" + } + } + } + }, + "apollo-link-http-common": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/apollo-link-http-common/-/apollo-link-http-common-0.2.15.tgz", + "integrity": "sha512-+Heey4S2IPsPyTf8Ag3PugUupASJMW894iVps6hXbvwtg1aHSNMXUYO5VG7iRHkPzqpuzT4HMBanCTXPjtGzxg==", + "requires": { + "apollo-link": "^1.2.13", + "ts-invariant": "^0.4.0", + "tslib": "^1.9.3" + }, + "dependencies": { + "apollo-link": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/apollo-link/-/apollo-link-1.2.13.tgz", + "integrity": "sha512-+iBMcYeevMm1JpYgwDEIDt/y0BB7VWyvlm/7x+TIPNLHCTCMgcEgDuW5kH86iQZWo0I7mNwQiTOz+/3ShPFmBw==", + "requires": { + "apollo-utilities": "^1.3.0", + "ts-invariant": "^0.4.0", + "tslib": "^1.9.3", + "zen-observable-ts": "^0.8.20" + } + }, + "apollo-utilities": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.3.2.tgz", + "integrity": "sha512-JWNHj8XChz7S4OZghV6yc9FNnzEXj285QYp/nLNh943iObycI5GTDO3NGR9Dth12LRrSFMeDOConPfPln+WGfg==", + "requires": { + "@wry/equality": "^0.1.2", + "fast-json-stable-stringify": "^2.0.0", + "ts-invariant": "^0.4.0", + "tslib": "^1.9.3" + } + }, + "zen-observable-ts": { + "version": "0.8.20", + "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.20.tgz", + "integrity": "sha512-2rkjiPALhOtRaDX6pWyNqK1fnP5KkJJybYebopNSn6wDG1lxBoFs2+nwwXKoA6glHIrtwrfBBy6da0stkKtTAA==", + "requires": { + "tslib": "^1.9.3", + "zen-observable": "^0.8.0" + } + } + } + }, + "apollo-link-persisted-queries": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/apollo-link-persisted-queries/-/apollo-link-persisted-queries-0.2.0.tgz", + "integrity": "sha512-MPUABSeLDoIMt+fOxkKJw6LiVLfUEu1hnVvvYUUMCMxprOwSQxviVNTTl2ZdawYS3hO2eBS04CAR26FgLxBX3A==", + "dev": true, + "requires": { + "apollo-link": "^1.2.1", + "hash.js": "^1.1.3" + } + }, + "apollo-link-schema": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/apollo-link-schema/-/apollo-link-schema-1.1.0.tgz", + "integrity": "sha512-sqWjse5RfrMAhrXecv0WdSLLdF1R5lI4YpbfkioIeJAkB7VB2o+mgA/+onATYKp214MSjloCDWzkvnVpRPFoBw==", + "dev": true, + "requires": { + "apollo-link": "^1.2.2" + } + }, + "apollo-link-state": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/apollo-link-state/-/apollo-link-state-0.4.1.tgz", + "integrity": "sha512-69/til4ENfl/Fvf7br2xSsLSBcxcXPbOHVNkzLLejvUZickl93HLO4/fO+uvoBi4dCYRgN17Zr8FwI41ueRx0g==", + "dev": true, + "requires": { + "apollo-utilities": "^1.0.8", + "graphql-anywhere": "^4.1.0-alpha.0" + } + }, + "apollo-server-core": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/apollo-server-core/-/apollo-server-core-1.4.0.tgz", + "integrity": "sha512-BP1Vh39krgEjkQxbjTdBURUjLHbFq1zeOChDJgaRsMxGtlhzuLWwwC6lLdPatN8jEPbeHq8Tndp9QZ3iQZOKKA==", + "requires": { + "apollo-cache-control": "^0.1.0", + "apollo-tracing": "^0.1.0", + "graphql-extensions": "^0.0.x" + } + }, + "apollo-server-express": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/apollo-server-express/-/apollo-server-express-1.3.2.tgz", + "integrity": "sha1-D/ggHAvzYoBKFR4TmXZ9rmq34wk=", + "requires": { + "apollo-server-core": "^1.3.2", + "apollo-server-module-graphiql": "^1.3.0" + } + }, + "apollo-server-module-graphiql": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/apollo-server-module-graphiql/-/apollo-server-module-graphiql-1.4.0.tgz", + "integrity": "sha512-GmkOcb5he2x5gat+TuiTvabnBf1m4jzdecal3XbXBh/Jg+kx4hcvO3TTDFQ9CuTprtzdcVyA11iqG7iOMOt7vA==" + }, + "apollo-tracing": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/apollo-tracing/-/apollo-tracing-0.1.4.tgz", + "integrity": "sha512-Uv+1nh5AsNmC3m130i2u3IqbS+nrxyVV3KYimH5QKsdPjxxIQB3JAT+jJmpeDxBel8gDVstNmCh82QSLxLSIdQ==", + "requires": { + "graphql-extensions": "~0.0.9" + } + }, + "apollo-utilities": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.0.12.tgz", + "integrity": "sha512-3mSen+NLouRwhmzCSHbMICfLBa6J+QJOc+M8zzLyo10jAYsOK+A2VgR63q4mcQJmAp8LumC5VAyah1zw6enMcg==" + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-equal": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", + "dev": true + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==", + "dev": true + }, + "async-limiter": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "dev": true + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", + "dev": true + }, + "babel-jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-24.9.0.tgz", + "integrity": "sha512-ntuddfyiN+EhMw58PTNL1ph4C9rECiQXjI4nMMBKBaNjXvqLdkXpPRcMSr4iyBrJg/+wz9brFUD6RhOAT6r4Iw==", + "dev": true, + "requires": { + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/babel__core": "^7.1.0", + "babel-plugin-istanbul": "^5.1.0", + "babel-preset-jest": "^24.9.0", + "chalk": "^2.4.2", + "slash": "^2.0.0" + } + }, + "babel-plugin-istanbul": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-5.2.0.tgz", + "integrity": "sha512-5LphC0USA8t4i1zCtjbbNb6jJj/9+X6P37Qfirc/70EQ34xKlMW+a1RHGwxGI+SwWpNwZ27HqvzAobeqaXwiZw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0", + "find-up": "^3.0.0", + "istanbul-lib-instrument": "^3.3.0", + "test-exclude": "^5.2.3" + } + }, + "babel-plugin-jest-hoist": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-24.9.0.tgz", + "integrity": "sha512-2EMA2P8Vp7lG0RAzr4HXqtYwacfMErOuv1U3wrvxHX6rD1sV6xS3WXG3r8TRQ2r6w8OhvSdWt+z41hQNwNm3Xw==", + "dev": true, + "requires": { + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-preset-jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-24.9.0.tgz", + "integrity": "sha512-izTUuhE4TMfTRPF92fFwD2QfdXaZW08qvWTFCI51V8rW5x00UuPgc3ajRoWofXOuxjfcOM5zzSYsQS3H8KGCAg==", + "dev": true, + "requires": { + "@babel/plugin-syntax-object-rest-spread": "^7.0.0", + "babel-plugin-jest-hoist": "^24.9.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + }, + "dependencies": { + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + } + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "browser-process-hrtime": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz", + "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==", + "dev": true + }, + "browser-resolve": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", + "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", + "dev": true, + "requires": { + "resolve": "1.1.7" + }, + "dependencies": { + "resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", + "dev": true + } + } + }, + "bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "dev": true, + "requires": { + "fast-json-stable-stringify": "2.x" + } + }, + "bser": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.0.tgz", + "integrity": "sha512-8zsjWrQkkBoLK6uxASk1nJ2SKv97ltiGDo6A3wA0/yRPz+CwmEyDo0hUrhIuukG2JHpAl3bvFIixw2/3Hi0DOg==", + "dev": true, + "requires": { + "node-int64": "^0.4.0" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "dev": true + }, + "capture-exit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", + "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "dev": true, + "requires": { + "rsvp": "^4.8.4" + } + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "dev": true + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "dev": true, + "requires": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.1.tgz", + "integrity": "sha512-cCuLsMhJeWQ/ZpsFTbE765kvVfoeSddc4nU3up4fV+fDBcfUXnbITJ+JzhkdjzOqhURjZgujxaioam4RM9yGUg==", + "dev": true, + "optional": true + }, + "component-emitter": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", + "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha512-eFu7XigvxdZ1ETfbgPBohgyQ/Z++C0eEhTor0qRwBw9unw+L0/6V8wkSuGgzdThkiS5lSpdptOQPD8Ak40a+7A==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "core-js": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.9.tgz", + "integrity": "sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true + }, + "cssstyle": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.4.0.tgz", + "integrity": "sha512-GBrLZYZ4X4x6/QEoBnIrqb8B/f5l4+8me2dkom/j1Gtbxy0kBv6OGzKuAsGM75bkGwGAFkt56Iwg28S3XTZgSA==", + "dev": true, + "requires": { + "cssom": "0.3.x" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "data-urls": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", + "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", + "dev": true, + "requires": { + "abab": "^2.0.0", + "whatwg-mimetype": "^2.2.0", + "whatwg-url": "^7.0.0" + }, + "dependencies": { + "whatwg-url": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", + "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + } + } + }, + "dataloader": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/dataloader/-/dataloader-1.4.0.tgz", + "integrity": "sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==" + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "define-properties": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", + "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", + "dev": true, + "requires": { + "foreach": "^2.0.5", + "object-keys": "^1.0.8" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "deprecated-decorator": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/deprecated-decorator/-/deprecated-decorator-0.1.6.tgz", + "integrity": "sha1-AJZjF7ehL+kvPMgx91g68ym4bDc=" + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "detect-newline": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-2.1.0.tgz", + "integrity": "sha1-9B8cEL5LAOh7XxPaaAdZ8sW/0+I=", + "dev": true + }, + "diff-sequences": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-24.9.0.tgz", + "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==", + "dev": true + }, + "domexception": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", + "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "dev": true, + "requires": { + "webidl-conversions": "^4.0.2" + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "requires": { + "is-arrayish": "^0.2.1" + } + }, + "es-abstract": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.11.0.tgz", + "integrity": "sha512-ZnQrE/lXTTQ39ulXZ+J1DTFazV9qBy61x2bY071B+qGco8Z8q1QddsLdt/EF8Ai9hcWH72dWS0kFqXLxOxqslA==", + "dev": true, + "requires": { + "es-to-primitive": "^1.1.1", + "function-bind": "^1.1.1", + "has": "^1.0.1", + "is-callable": "^1.1.3", + "is-regex": "^1.0.4" + } + }, + "es-to-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", + "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", + "dev": true, + "requires": { + "is-callable": "^1.1.1", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.1" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "escodegen": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.12.0.tgz", + "integrity": "sha512-TuA+EhsanGcme5T3R0L80u4t8CpbXQjegRmf7+FPTJrtCTErXFeelblRgHQa1FofEzqYYJmJ/OqjTwREp9qgmg==", + "dev": true, + "requires": { + "esprima": "^3.1.3", + "estraverse": "^4.2.0", + "esutils": "^2.0.2", + "optionator": "^0.8.1", + "source-map": "~0.6.1" + } + }, + "esprima": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", + "dev": true + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "exec-sh": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.2.tgz", + "integrity": "sha512-9sLAvzhI5nc8TpuQUh4ahMdCrWT00wPWz7j47/emR5+2qEfoZP5zzUXvx+vdx+H6ohhnsYC31iX04QLYJK8zTg==", + "dev": true + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expect": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-24.9.0.tgz", + "integrity": "sha512-wvVAx8XIol3Z5m9zvZXiyZOQ+sRJqNTIm6sGjdWlaZIeupQGO3WbYI+15D/AmEwZywL6wtJkbAbJtzkOfBuR0Q==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-styles": "^3.2.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-regex-util": "^24.9.0" + } + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "fb-watchman": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.0.tgz", + "integrity": "sha1-VOmr99+i8mzZsWNsWIwa/AXeXVg=", + "dev": true, + "requires": { + "bser": "^2.0.0" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "foreach": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", + "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", + "dev": true + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz", + "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.12.1", + "node-pre-gyp": "^0.12.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "^2.1.1" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true + }, + "minipass": { + "version": "2.3.5", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.3.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "^4.1.0", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.12.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.4.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.7.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true + }, + "yallist": { + "version": "3.0.3", + "bundled": true, + "dev": true + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.4.tgz", + "integrity": "sha512-hkLPepehmnKk41pUGm3sYxoFs/umurYfYJCerbXEyFIWcAzvpipAgVkBqqT9RBKMGjnq6kMuyYwha6csxbiM1A==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.2.tgz", + "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==", + "dev": true + }, + "graphql": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-0.13.2.tgz", + "integrity": "sha512-QZ5BL8ZO/B20VA8APauGBg3GyEgZ19eduvpLWoq5x7gMmWnHoy8rlQWPLmWgFvo1yNgjSEFMesmS4R6pPr7xog==", + "requires": { + "iterall": "^1.2.1" + } + }, + "graphql-anywhere": { + "version": "4.1.10", + "resolved": "https://registry.npmjs.org/graphql-anywhere/-/graphql-anywhere-4.1.10.tgz", + "integrity": "sha512-ab1O4mOkjlk+17ye5a7DxUuWafKP8aoH+xmc6SsPNZlrQ7spbArqyLIf2JdbwpzsVLXOyRvQpD6tcc0GtnxBfw==", + "dev": true, + "requires": { + "apollo-utilities": "^1.0.12" + } + }, + "graphql-extensions": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/graphql-extensions/-/graphql-extensions-0.0.10.tgz", + "integrity": "sha512-TnQueqUDCYzOSrpQb3q1ngDSP2otJSF+9yNLrQGPzkMsvnQ+v6e2d5tl+B35D4y+XpmvVnAn4T3ZK28mkILveA==", + "requires": { + "core-js": "^2.5.3", + "source-map-support": "^0.5.1" + } + }, + "graphql-tag": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.9.2.tgz", + "integrity": "sha512-qnNmof9pAqj/LUzs3lStP0Gw1qhdVCUS7Ab7+SUB6KD5aX1uqxWQRwMnOGTkhKuLvLNIs1TvNz+iS9kUGl1MhA==", + "dev": true + }, + "graphql-tools": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/graphql-tools/-/graphql-tools-4.0.5.tgz", + "integrity": "sha512-kQCh3IZsMqquDx7zfIGWBau42xe46gmqabwYkpPlCLIjcEY1XK+auP7iGRD9/205BPyoQdY8hT96MPpgERdC9Q==", + "requires": { + "apollo-link": "^1.2.3", + "apollo-utilities": "^1.0.1", + "deprecated-decorator": "^0.1.6", + "iterall": "^1.1.3", + "uuid": "^3.1.0" + }, + "dependencies": { + "apollo-link": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/apollo-link/-/apollo-link-1.2.13.tgz", + "integrity": "sha512-+iBMcYeevMm1JpYgwDEIDt/y0BB7VWyvlm/7x+TIPNLHCTCMgcEgDuW5kH86iQZWo0I7mNwQiTOz+/3ShPFmBw==", + "requires": { + "apollo-utilities": "^1.3.0", + "ts-invariant": "^0.4.0", + "tslib": "^1.9.3", + "zen-observable-ts": "^0.8.20" + }, + "dependencies": { + "apollo-utilities": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/apollo-utilities/-/apollo-utilities-1.3.2.tgz", + "integrity": "sha512-JWNHj8XChz7S4OZghV6yc9FNnzEXj285QYp/nLNh943iObycI5GTDO3NGR9Dth12LRrSFMeDOConPfPln+WGfg==", + "requires": { + "@wry/equality": "^0.1.2", + "fast-json-stable-stringify": "^2.0.0", + "ts-invariant": "^0.4.0", + "tslib": "^1.9.3" + } + } + } + }, + "zen-observable-ts": { + "version": "0.8.20", + "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.20.tgz", + "integrity": "sha512-2rkjiPALhOtRaDX6pWyNqK1fnP5KkJJybYebopNSn6wDG1lxBoFs2+nwwXKoA6glHIrtwrfBBy6da0stkKtTAA==", + "requires": { + "tslib": "^1.9.3", + "zen-observable": "^0.8.0" + } + } + } + }, + "growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", + "dev": true + }, + "handlebars": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.4.2.tgz", + "integrity": "sha512-cIv17+GhL8pHHnRJzGu2wwcthL5sb8uDKBHvZ2Dtu5s1YNt0ljbzKbamnc+gr69y7bzwQiBdr5+hOpRd5pnOdg==", + "dev": true, + "requires": { + "neo-async": "^2.6.0", + "optimist": "^0.6.1", + "source-map": "^0.6.1", + "uglify-js": "^3.1.4" + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true + }, + "har-validator": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", + "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, + "requires": { + "ajv": "^6.5.5", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.1.tgz", + "integrity": "sha1-hGFzP1OLCDfJNh45qauelwTcLyg=", + "dev": true, + "requires": { + "function-bind": "^1.0.2" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "hash.js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.4.tgz", + "integrity": "sha512-pzXIvANXEFrc5oFFXRMkbLPQ2rXRoDERwDLyrcUxGhaZhgP54BBSl9Oheh7Vv0T090cszWBxPjkQQ5Sq1PbBRQ==", + "dev": true + }, + "html-encoding-sniffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", + "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", + "dev": true, + "requires": { + "whatwg-encoding": "^1.0.1" + } + }, + "http-errors": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.3.tgz", + "integrity": "sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.4", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + } + } + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "import-local": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz", + "integrity": "sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ==", + "dev": true, + "requires": { + "pkg-dir": "^3.0.0", + "resolve-cwd": "^2.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "dev": true, + "requires": { + "loose-envify": "^1.0.0" + } + }, + "ipaddr.js": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", + "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "dev": true + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", + "dev": true + }, + "is-callable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", + "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=", + "dev": true + }, + "is-ci": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", + "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "dev": true, + "requires": { + "ci-info": "^2.0.0" + } + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "dev": true + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", + "dev": true + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-symbol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", + "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", + "dev": true + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", + "dev": true + }, + "is-wsl": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", + "integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true + }, + "istanbul-lib-coverage": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz", + "integrity": "sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA==", + "dev": true + }, + "istanbul-lib-instrument": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-3.3.0.tgz", + "integrity": "sha512-5nnIN4vo5xQZHdXno/YDXJ0G+I3dAm4XgzfSVTPLQpj/zAV2dV6Juy0yaf10/zrJOJeHoN3fraFe+XRq2bFVZA==", + "dev": true, + "requires": { + "@babel/generator": "^7.4.0", + "@babel/parser": "^7.4.3", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.3", + "@babel/types": "^7.4.0", + "istanbul-lib-coverage": "^2.0.5", + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "istanbul-lib-report": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-2.0.8.tgz", + "integrity": "sha512-fHBeG573EIihhAblwgxrSenp0Dby6tJMFR/HvlerBsrCTD5bkUuoNtn3gVh29ZCS824cGGBPn7Sg7cNk+2xUsQ==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "istanbul-lib-source-maps": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz", + "integrity": "sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw==", + "dev": true, + "requires": { + "debug": "^4.1.1", + "istanbul-lib-coverage": "^2.0.5", + "make-dir": "^2.1.0", + "rimraf": "^2.6.3", + "source-map": "^0.6.1" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "istanbul-reports": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-2.2.6.tgz", + "integrity": "sha512-SKi4rnMyLBKe0Jy2uUdx28h8oG7ph2PPuQPvIAh31d+Ci+lSiEu4C+h3oBPuJ9+mPKhOyW0M8gY4U5NM1WLeXA==", + "dev": true, + "requires": { + "handlebars": "^4.1.2" + } + }, + "iterall": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.2.2.tgz", + "integrity": "sha512-yynBb1g+RFUPY64fTrFv7nsjRrENBQJaX2UL+2Szc9REFrSNm1rpSXHGzhmAy7a9uv3vlvgBlXnf9RqmPH1/DA==" + }, + "jest": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-24.9.0.tgz", + "integrity": "sha512-YvkBL1Zm7d2B1+h5fHEOdyjCG+sGMz4f8D86/0HiqJ6MB4MnDc8FgP5vdWsGnemOQro7lnYo8UakZ3+5A0jxGw==", + "dev": true, + "requires": { + "import-local": "^2.0.0", + "jest-cli": "^24.9.0" + }, + "dependencies": { + "jest-cli": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.9.0.tgz", + "integrity": "sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg==", + "dev": true, + "requires": { + "@jest/core": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "import-local": "^2.0.0", + "is-ci": "^2.0.0", + "jest-config": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "prompts": "^2.0.1", + "realpath-native": "^1.1.0", + "yargs": "^13.3.0" + } + } + } + }, + "jest-changed-files": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-24.9.0.tgz", + "integrity": "sha512-6aTWpe2mHF0DhL28WjdkO8LyGjs3zItPET4bMSeXU6T3ub4FPMw+mcOcbdGXQOAfmLcxofD23/5Bl9Z4AkFwqg==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "execa": "^1.0.0", + "throat": "^4.0.0" + } + }, + "jest-config": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.9.0.tgz", + "integrity": "sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==", + "dev": true, + "requires": { + "@babel/core": "^7.1.0", + "@jest/test-sequencer": "^24.9.0", + "@jest/types": "^24.9.0", + "babel-jest": "^24.9.0", + "chalk": "^2.0.1", + "glob": "^7.1.1", + "jest-environment-jsdom": "^24.9.0", + "jest-environment-node": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "micromatch": "^3.1.10", + "pretty-format": "^24.9.0", + "realpath-native": "^1.1.0" + } + }, + "jest-diff": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-24.9.0.tgz", + "integrity": "sha512-qMfrTs8AdJE2iqrTp0hzh7kTd2PQWrsFyj9tORoKmu32xjPjeE4NyjVRDz8ybYwqS2ik8N4hsIpiVTyFeo2lBQ==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "diff-sequences": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-docblock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.9.0.tgz", + "integrity": "sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA==", + "dev": true, + "requires": { + "detect-newline": "^2.1.0" + } + }, + "jest-each": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-24.9.0.tgz", + "integrity": "sha512-ONi0R4BvW45cw8s2Lrx8YgbeXL1oCQ/wIDwmsM3CqM/nlblNCPmnC3IPQlMbRFZu3wKdQ2U8BqM6lh3LJ5Bsog==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-environment-jsdom": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-24.9.0.tgz", + "integrity": "sha512-Zv9FV9NBRzLuALXjvRijO2351DRQeLYXtpD4xNvfoVFw21IOKNhZAEUKcbiEtjTkm2GsJ3boMVgkaR7rN8qetA==", + "dev": true, + "requires": { + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0", + "jsdom": "^11.5.1" + } + }, + "jest-environment-node": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-24.9.0.tgz", + "integrity": "sha512-6d4V2f4nxzIzwendo27Tr0aFm+IXWa0XEUnaH6nU0FMaozxovt+sfRvh4J47wL1OvF83I3SSTu0XK+i4Bqe7uA==", + "dev": true, + "requires": { + "@jest/environment": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/types": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-util": "^24.9.0" + } + }, + "jest-get-type": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-24.9.0.tgz", + "integrity": "sha512-lUseMzAley4LhIcpSP9Jf+fTrQ4a1yHQwLNeeVa2cEmbCGeoZAtYPOIv8JaxLD/sUpKxetKGP+gsHl8f8TSj8Q==", + "dev": true + }, + "jest-haste-map": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-24.9.0.tgz", + "integrity": "sha512-kfVFmsuWui2Sj1Rp1AJ4D9HqJwE4uwTlS/vO+eRUaMmd54BFpli2XhMQnPC2k4cHFVbB2Q2C+jtI1AGLgEnCjQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "anymatch": "^2.0.0", + "fb-watchman": "^2.0.0", + "fsevents": "^1.2.7", + "graceful-fs": "^4.1.15", + "invariant": "^2.2.4", + "jest-serializer": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.9.0", + "micromatch": "^3.1.10", + "sane": "^4.0.3", + "walker": "^1.0.7" + } + }, + "jest-jasmine2": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-24.9.0.tgz", + "integrity": "sha512-Cq7vkAgaYKp+PsX+2/JbTarrk0DmNhsEtqBXNwUHkdlbrTBLtMJINADf2mf5FkowNsq8evbPc07/qFO0AdKTzw==", + "dev": true, + "requires": { + "@babel/traverse": "^7.1.0", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "co": "^4.6.0", + "expect": "^24.9.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "pretty-format": "^24.9.0", + "throat": "^4.0.0" + } + }, + "jest-leak-detector": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-24.9.0.tgz", + "integrity": "sha512-tYkFIDsiKTGwb2FG1w8hX9V0aUb2ot8zY/2nFg087dUageonw1zrLMP4W6zsRO59dPkTSKie+D4rhMuP9nRmrA==", + "dev": true, + "requires": { + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-matcher-utils": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-24.9.0.tgz", + "integrity": "sha512-OZz2IXsu6eaiMAwe67c1T+5tUAtQyQx27/EMEkbFAGiw52tB9em+uGbzpcgYVpA8wl0hlxKPZxrly4CXU/GjHA==", + "dev": true, + "requires": { + "chalk": "^2.0.1", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "pretty-format": "^24.9.0" + } + }, + "jest-message-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-24.9.0.tgz", + "integrity": "sha512-oCj8FiZ3U0hTP4aSui87P4L4jC37BtQwUMqk+zk/b11FR19BJDeZsZAvIHutWnmtw7r85UmR3CEWZ0HWU2mAlw==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/stack-utils": "^1.0.1", + "chalk": "^2.0.1", + "micromatch": "^3.1.10", + "slash": "^2.0.0", + "stack-utils": "^1.0.1" + } + }, + "jest-mock": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-24.9.0.tgz", + "integrity": "sha512-3BEYN5WbSq9wd+SyLDES7AHnjH9A/ROBwmz7l2y+ol+NtSFO8DYiEBzoO1CeFc9a8DYy10EO4dDFVv/wN3zl1w==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0" + } + }, + "jest-pnp-resolver": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz", + "integrity": "sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ==", + "dev": true + }, + "jest-regex-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-24.9.0.tgz", + "integrity": "sha512-05Cmb6CuxaA+Ys6fjr3PhvV3bGQmO+2p2La4hFbU+W5uOc479f7FdLXUWXw4pYMAhhSZIuKHwSXSu6CsSBAXQA==", + "dev": true + }, + "jest-resolve": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-24.9.0.tgz", + "integrity": "sha512-TaLeLVL1l08YFZAt3zaPtjiVvyy4oSA6CRe+0AFPPVX3Q/VI0giIWWoAvoS5L96vj9Dqxj4fB5p2qrHCmTU/MQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "browser-resolve": "^1.11.3", + "chalk": "^2.0.1", + "jest-pnp-resolver": "^1.2.1", + "realpath-native": "^1.1.0" + } + }, + "jest-resolve-dependencies": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-24.9.0.tgz", + "integrity": "sha512-Fm7b6AlWnYhT0BXy4hXpactHIqER7erNgIsIozDXWl5dVm+k8XdGVe1oTg1JyaFnOxarMEbax3wyRJqGP2Pq+g==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-snapshot": "^24.9.0" + } + }, + "jest-runner": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-24.9.0.tgz", + "integrity": "sha512-KksJQyI3/0mhcfspnxxEOBueGrd5E4vV7ADQLT9ESaCzz02WnbdbKWIf5Mkaucoaj7obQckYPVX6JJhgUcoWWg==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "chalk": "^2.4.2", + "exit": "^0.1.2", + "graceful-fs": "^4.1.15", + "jest-config": "^24.9.0", + "jest-docblock": "^24.3.0", + "jest-haste-map": "^24.9.0", + "jest-jasmine2": "^24.9.0", + "jest-leak-detector": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "jest-runtime": "^24.9.0", + "jest-util": "^24.9.0", + "jest-worker": "^24.6.0", + "source-map-support": "^0.5.6", + "throat": "^4.0.0" + } + }, + "jest-runtime": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-24.9.0.tgz", + "integrity": "sha512-8oNqgnmF3v2J6PVRM2Jfuj8oX3syKmaynlDMMKQ4iyzbQzIG6th5ub/lM2bCMTmoTKM3ykcUYI2Pw9xwNtjMnw==", + "dev": true, + "requires": { + "@jest/console": "^24.7.1", + "@jest/environment": "^24.9.0", + "@jest/source-map": "^24.3.0", + "@jest/transform": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/yargs": "^13.0.0", + "chalk": "^2.0.1", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.1.15", + "jest-config": "^24.9.0", + "jest-haste-map": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-mock": "^24.9.0", + "jest-regex-util": "^24.3.0", + "jest-resolve": "^24.9.0", + "jest-snapshot": "^24.9.0", + "jest-util": "^24.9.0", + "jest-validate": "^24.9.0", + "realpath-native": "^1.1.0", + "slash": "^2.0.0", + "strip-bom": "^3.0.0", + "yargs": "^13.3.0" + } + }, + "jest-serializer": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz", + "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==", + "dev": true + }, + "jest-snapshot": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-24.9.0.tgz", + "integrity": "sha512-uI/rszGSs73xCM0l+up7O7a40o90cnrk429LOiK3aeTvfC0HHmldbd81/B7Ix81KSFe1lwkbl7GnBGG4UfuDew==", + "dev": true, + "requires": { + "@babel/types": "^7.0.0", + "@jest/types": "^24.9.0", + "chalk": "^2.0.1", + "expect": "^24.9.0", + "jest-diff": "^24.9.0", + "jest-get-type": "^24.9.0", + "jest-matcher-utils": "^24.9.0", + "jest-message-util": "^24.9.0", + "jest-resolve": "^24.9.0", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "pretty-format": "^24.9.0", + "semver": "^6.2.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true + } + } + }, + "jest-util": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-24.9.0.tgz", + "integrity": "sha512-x+cZU8VRmOJxbA1K5oDBdxQmdq0OIdADarLxk0Mq+3XS4jgvhG/oKGWcIDCtPG0HgjxOYvF+ilPJQsAyXfbNOg==", + "dev": true, + "requires": { + "@jest/console": "^24.9.0", + "@jest/fake-timers": "^24.9.0", + "@jest/source-map": "^24.9.0", + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "callsites": "^3.0.0", + "chalk": "^2.0.1", + "graceful-fs": "^4.1.15", + "is-ci": "^2.0.0", + "mkdirp": "^0.5.1", + "slash": "^2.0.0", + "source-map": "^0.6.0" + } + }, + "jest-validate": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-24.9.0.tgz", + "integrity": "sha512-HPIt6C5ACwiqSiwi+OfSSHbK8sG7akG8eATl+IPKaeIjtPOeBUd/g3J7DghugzxrGjI93qS/+RPKe1H6PqvhRQ==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "camelcase": "^5.3.1", + "chalk": "^2.0.1", + "jest-get-type": "^24.9.0", + "leven": "^3.1.0", + "pretty-format": "^24.9.0" + } + }, + "jest-watcher": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-24.9.0.tgz", + "integrity": "sha512-+/fLOfKPXXYJDYlks62/4R4GoT+GU1tYZed99JSCOsmzkkF7727RqKrjNAxtfO4YpGv11wybgRvCjR73lK2GZw==", + "dev": true, + "requires": { + "@jest/test-result": "^24.9.0", + "@jest/types": "^24.9.0", + "@types/yargs": "^13.0.0", + "ansi-escapes": "^3.0.0", + "chalk": "^2.0.1", + "jest-util": "^24.9.0", + "string-length": "^2.0.0" + } + }, + "jest-worker": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-24.9.0.tgz", + "integrity": "sha512-51PE4haMSXcHohnSMdM42anbvZANYTqMrr52tVKPqqsPJMzoP6FYYDVqahX/HrAoKEKz3uUPzSvKs9A3qR4iVw==", + "dev": true, + "requires": { + "merge-stream": "^2.0.0", + "supports-color": "^6.1.0" + }, + "dependencies": { + "supports-color": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.1.0.tgz", + "integrity": "sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true + }, + "jsdom": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", + "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", + "dev": true, + "requires": { + "abab": "^2.0.0", + "acorn": "^5.5.3", + "acorn-globals": "^4.1.0", + "array-equal": "^1.0.0", + "cssom": ">= 0.3.2 < 0.4.0", + "cssstyle": "^1.0.0", + "data-urls": "^1.0.0", + "domexception": "^1.0.1", + "escodegen": "^1.9.1", + "html-encoding-sniffer": "^1.0.2", + "left-pad": "^1.3.0", + "nwsapi": "^2.0.7", + "parse5": "4.0.0", + "pn": "^1.1.0", + "request": "^2.87.0", + "request-promise-native": "^1.0.5", + "sax": "^1.2.4", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.3.4", + "w3c-hr-time": "^1.0.1", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.3", + "whatwg-mimetype": "^2.1.0", + "whatwg-url": "^6.4.1", + "ws": "^5.2.0", + "xml-name-validator": "^3.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "dev": true + }, + "json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true + }, + "json5": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz", + "integrity": "sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", + "dev": true + }, + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "dev": true + }, + "left-pad": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", + "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", + "dev": true + }, + "leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha1-L19Fq5HjMhYjT9U62rZo607AmTs=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.15", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", + "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==", + "dev": true + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", + "dev": true + }, + "lodash.sortby": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", + "dev": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + } + } + }, + "make-error": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.5.tgz", + "integrity": "sha512-c3sIjNUow0+8swNwVpqoH4YCShKNFkMaw6oH1mNS2haDZQqkeZFlHS3dhoeEbKKmJB4vXpJucU6oH75aDYeE9g==", + "dev": true + }, + "makeerror": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", + "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", + "dev": true, + "requires": { + "tmpl": "1.0.x" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==", + "dev": true + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "dev": true, + "requires": { + "mime-db": "1.40.0" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + }, + "mixin-deep": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", + "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "nan": { + "version": "2.14.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.0.tgz", + "integrity": "sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "neo-async": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", + "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true + }, + "node-fetch": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz", + "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==" + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "dev": true + }, + "node-modules-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", + "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", + "dev": true + }, + "node-notifier": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-5.4.3.tgz", + "integrity": "sha512-M4UBGcs4jeOK9CjTsYwkvH6/MzuUmGCyTW+kCY7uO+1ZVr0+FHGdPdIf5CCLqAaxnRrWidyoQlNkMIIVwbKB8Q==", + "dev": true, + "requires": { + "growly": "^1.3.0", + "is-wsl": "^1.1.0", + "semver": "^5.5.0", + "shellwords": "^0.1.1", + "which": "^1.3.0" + } + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "nwsapi": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.1.4.tgz", + "integrity": "sha512-iGfd9Y6SFdTNldEy2L0GUhcarIutFmk+MPWIn9dmj8NMIup03G08uUF2KGbbmv/Ux4RT0VZJoP/sVbWA6d/VIw==", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-keys": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.11.tgz", + "integrity": "sha1-xUYBd4rVYPEULODgG8yotW0TQm0=", + "dev": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "optimism": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/optimism/-/optimism-0.10.3.tgz", + "integrity": "sha512-9A5pqGoQk49H6Vhjb9kPgAeeECfUDF6aIICbMDL23kDLStBn1MWk3YvcZ4xWF9CsSf6XEgvRLkXy4xof/56vVw==", + "requires": { + "@wry/context": "^0.4.0" + } + }, + "optimist": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", + "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", + "dev": true, + "requires": { + "minimist": "~0.0.1", + "wordwrap": "~0.0.2" + }, + "dependencies": { + "minimist": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", + "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=", + "dev": true + } + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + }, + "dependencies": { + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + } + } + }, + "p-each-series": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-1.0.0.tgz", + "integrity": "sha1-kw89Et0fUOdDRFeiLNbwSsatf3E=", + "dev": true, + "requires": { + "p-reduce": "^1.0.0" + } + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-limit": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.1.tgz", + "integrity": "sha512-85Tk+90UCVWvbDavCLKPOLC9vvY8OwEX/RtKF+/1OADJMVlFfEHOiMTPVyxg7mk/dKa+ipdHm0OUkTvCpMTuwg==", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-reduce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-reduce/-/p-reduce-1.0.0.tgz", + "integrity": "sha1-GMKw3ZNqRpClKfgjH1ig/bakffo=", + "dev": true + }, + "p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true + }, + "parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=", + "dev": true, + "requires": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + } + }, + "parse5": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", + "dev": true + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "requires": { + "pify": "^3.0.0" + } + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", + "dev": true + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=", + "dev": true + }, + "pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", + "dev": true, + "requires": { + "node-modules-regexp": "^1.0.0" + } + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "pn": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", + "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", + "dev": true + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "pretty-format": { + "version": "24.9.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-24.9.0.tgz", + "integrity": "sha512-00ZMZUiHaJrNfk33guavqgvfJS30sLYf0f8+Srklv0AMPodGGHcoHgksZ3OThYnIvOd+8yMCn0YiEOogjlgsnA==", + "dev": true, + "requires": { + "@jest/types": "^24.9.0", + "ansi-regex": "^4.0.0", + "ansi-styles": "^3.2.0", + "react-is": "^16.8.4" + } + }, + "prompts": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.2.1.tgz", + "integrity": "sha512-VObPvJiWPhpZI6C5m60XOzTfnYg/xc/an+r9VYymj9WJW3B/DIH+REzjpAACPf8brwPeP+7vz3bIim3S+AaMjw==", + "dev": true, + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.3" + } + }, + "proxy-addr": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz", + "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.0" + } + }, + "psl": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", + "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "dependencies": { + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + } + } + }, + "react-is": { + "version": "16.10.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.10.2.tgz", + "integrity": "sha512-INBT1QEgtcCCgvccr5/86CfD71fw9EPmDxgiJX4I2Ddr6ZsV6iFXsuby+qWJPtmNuMY0zByTsG4468P7nHuNWA==", + "dev": true + }, + "read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha1-nLxoaXj+5l0WwA4rGcI3/Pbjg4k=", + "dev": true, + "requires": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + } + }, + "read-pkg-up": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-4.0.0.tgz", + "integrity": "sha512-6etQSH7nJGsK0RbG/2TeDzZFa8shjQ1um+SwQQ5cwKy0dhSXdOncEhb1CPpvQG4h7FyOV6EB6YlV0yJvZQNAkA==", + "dev": true, + "requires": { + "find-up": "^3.0.0", + "read-pkg": "^3.0.0" + } + }, + "realpath-native": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-1.1.0.tgz", + "integrity": "sha512-wlgPA6cCIIg9gKz0fgAPjnzh4yR/LnXovwuo9hvyGvx3h8nX4+/iLZplfUWasXpqD8BdnGnP5njOFjkUwPzvjA==", + "dev": true, + "requires": { + "util.promisify": "^1.0.0" + } + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true + }, + "safe-buffer": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz", + "integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg==", + "dev": true + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "dev": true, + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + }, + "uuid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", + "dev": true + } + } + }, + "request-promise-core": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.2.tgz", + "integrity": "sha512-UHYyq1MO8GsefGEt7EprS8UrXsm1TxEvFUX1IMTuSLU2Rh7fTIdFtl8xD7JiEYiWU2dl+NYAjCTksTehQUxPag==", + "dev": true, + "requires": { + "lodash": "^4.17.11" + } + }, + "request-promise-native": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.7.tgz", + "integrity": "sha512-rIMnbBdgNViL37nZ1b3L/VfPOpSi0TqVDQPAvO6U14lMzOLrt5nilxCQqtDKhZeDiW0/hkCXGoQjhgJd/tCh6w==", + "dev": true, + "requires": { + "request-promise-core": "1.1.2", + "stealthy-require": "^1.1.1", + "tough-cookie": "^2.3.3" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "dev": true + }, + "resolve": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", + "integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", + "integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", + "dev": true, + "requires": { + "resolve-from": "^3.0.0" + } + }, + "resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "dev": true + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "rsvp": { + "version": "4.8.5", + "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", + "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", + "dev": true + }, + "safe-buffer": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", + "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sane": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", + "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", + "dev": true, + "requires": { + "@cnakazawa/watch": "^1.0.3", + "anymatch": "^2.0.0", + "capture-exit": "^2.0.0", + "exec-sh": "^0.3.2", + "execa": "^1.0.0", + "fb-watchman": "^2.0.0", + "micromatch": "^3.1.4", + "minimist": "^1.1.1", + "walker": "~1.0.5" + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-value": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", + "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "sisteransi": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.3.tgz", + "integrity": "sha512-SbEG75TzH8G7eVXFSN5f9EExILKfly7SUvVY5DhhYLvfhKqhDFY0OzevWa/zwak0RLRfWS5AvfMWpd9gJvr5Yg==", + "dev": true + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", + "dev": true, + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "spdx-correct": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", + "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", + "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", + "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", + "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", + "dev": true + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "stack-utils": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.2.tgz", + "integrity": "sha512-MTX+MeG5U994cazkjd/9KNAapsHnibjMLnfXodlkXw76JEea0UiNzrqidzo1emMwk7w5Qhc9jd4Bn9TBb1MFwA==", + "dev": true + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "stealthy-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", + "dev": true + }, + "string-length": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-2.0.0.tgz", + "integrity": "sha1-1A27aGo6zpYMHP/KVivyxF+DY+0=", + "dev": true, + "requires": { + "astral-regex": "^1.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", + "dev": true + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "symbol-observable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz", + "integrity": "sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==" + }, + "symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true + }, + "test-exclude": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-5.2.3.tgz", + "integrity": "sha512-M+oxtseCFO3EDtAaGH7iiej3CBkzXqFMbzqYAACdzKui4eZA+pq3tZEwChvOdNfa7xxy8BfbmgJSIr43cC/+2g==", + "dev": true, + "requires": { + "glob": "^7.1.3", + "minimatch": "^3.0.4", + "read-pkg-up": "^4.0.0", + "require-main-filename": "^2.0.0" + } + }, + "throat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-4.1.0.tgz", + "integrity": "sha1-iQN8vJLFarGJJua6TLsgDhVnKmo=", + "dev": true + }, + "tmpl": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", + "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "dev": true + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tr46": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", + "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "ts-invariant": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.4.4.tgz", + "integrity": "sha512-uEtWkFM/sdZvRNNDL3Ehu4WVpwaulhwQszV8mrtcdeE8nN00BV9mAmQ88RkrBhFgl9gMgvjJLAQcZbnPXI9mlA==", + "requires": { + "tslib": "^1.9.3" + } + }, + "ts-jest": { + "version": "24.1.0", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-24.1.0.tgz", + "integrity": "sha512-HEGfrIEAZKfu1pkaxB9au17b1d9b56YZSqz5eCVE8mX68+5reOvlM93xGOzzCREIov9mdH7JBG+s0UyNAqr0tQ==", + "dev": true, + "requires": { + "bs-logger": "0.x", + "buffer-from": "1.x", + "fast-json-stable-stringify": "2.x", + "json5": "2.x", + "lodash.memoize": "4.x", + "make-error": "1.x", + "mkdirp": "0.x", + "resolve": "1.x", + "semver": "^5.5", + "yargs-parser": "10.x" + }, + "dependencies": { + "camelcase": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", + "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=", + "dev": true + }, + "yargs-parser": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", + "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", + "dev": true, + "requires": { + "camelcase": "^4.1.0" + } + } + } + }, + "tslib": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz", + "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "dependencies": { + "mime-db": { + "version": "1.40.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", + "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" + }, + "mime-types": { + "version": "2.1.24", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", + "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", + "requires": { + "mime-db": "1.40.0" + } + } + } + }, + "typescript": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.3.tgz", + "integrity": "sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==", + "dev": true + }, + "uglify-js": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz", + "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==", + "dev": true, + "optional": true, + "requires": { + "commander": "~2.20.0", + "source-map": "~0.6.1" + } + }, + "union-value": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", + "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^2.0.1" + } + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", + "dev": true + }, + "util.promisify": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/util.promisify/-/util.promisify-1.0.0.tgz", + "integrity": "sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", + "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==" + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "w3c-hr-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", + "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", + "dev": true, + "requires": { + "browser-process-hrtime": "^0.1.2" + } + }, + "walker": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", + "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "dev": true, + "requires": { + "makeerror": "1.0.x" + } + }, + "webidl-conversions": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "dev": true + }, + "whatwg-encoding": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", + "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, + "requires": { + "iconv-lite": "0.4.24" + } + }, + "whatwg-mimetype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true + }, + "whatwg-url": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", + "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wordwrap": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", + "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=", + "dev": true + }, + "wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + }, + "write-file-atomic": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.1.tgz", + "integrity": "sha512-TGHFeZEZMnv+gBFRfjAcxL5bPHrsGKtnb4qsFAws7/vlh+QfwAaySIw4AXP9ZskTTh5GWu3FLuJhsWVdiJPGvg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.2" + } + }, + "ws": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + }, + "xml-name-validator": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", + "dev": true + }, + "yargs": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz", + "integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==", + "dev": true, + "requires": { + "cliui": "^5.0.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^13.1.1" + } + }, + "yargs-parser": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.1.tgz", + "integrity": "sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "zen-observable": { + "version": "0.8.8", + "resolved": "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.8.tgz", + "integrity": "sha512-HnhhyNnwTFzS48nihkCZIJGsWGFcYUz+XPDlPK5W84Ifji8SksC6m7sQWOf8zdCGhzQ4tDYuMYGu5B0N1dXTtg==" + }, + "zen-observable-ts": { + "version": "0.8.9", + "resolved": "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-0.8.9.tgz", + "integrity": "sha512-KJz2O8FxbAdAU5CSc8qZ1K2WYEJb1HxS6XDRF+hOJ1rOYcg6eTMmS9xYHCXzqZZzKw6BbXWyF4UpwSsBQnHJeA==", + "requires": { + "zen-observable": "^0.8.0" + } + } + } +} diff --git a/ftgo-api-gateway-graphql/package.json b/ftgo-api-gateway-graphql/package.json index 5b90cac9..ebb2b015 100644 --- a/ftgo-api-gateway-graphql/package.json +++ b/ftgo-api-gateway-graphql/package.json @@ -12,29 +12,28 @@ "clean": "rm -fr dist" }, "dependencies": { - "@types/graphql": "^14.0.0", - "apollo-cache-inmemory": "^1.2.1", - "apollo-client": "^2.3.1", - "apollo-engine": "^1.0.1", - "apollo-link-http": "^1.5.4", + "@types/graphql": "^14.5.0", + "apollo-cache-inmemory": "^1.6.3", + "apollo-client": "^2.6.4", + "apollo-engine": "^1.1.2", + "apollo-link-http": "^1.5.6", "apollo-server-express": "1.3.2", - "body-parser": "^1.17.1", + "body-parser": "^1.19.0", "dataloader": "^1.4.0", - "express": "^4.15.2", - "graphql-tools": "2.20.2", - "node-fetch": "^2.0.0" + "express": "^4.17.1", + "graphql": "^0.13.2", + "graphql-tools": "4.0.5", + "node-fetch": "^2.6.0" }, "devDependencies": { - "@types/mocha": "^5.2.0", - "@types/node": "^10.1.4", + "@types/mocha": "^5.2.7", + "@types/node": "^12.7.11", "apollo-boost": "^0.1.6", "apollo-link-persisted-queries": "^0.2.0", "apollo-link-schema": "^1.1.0", - "graphql": "^0.13.2", "graphql-tag": "^2.9.2", - "jest": "^23.0.1", - "ts-jest": "^22.4.6", - "tsc": "^1.20150623.0", - "typescript": "^2.8.4" + "jest": "^24.9.0", + "ts-jest": "^24.1.0", + "typescript": "^3.6.3" } } diff --git a/ftgo-api-gateway-graphql/src/server.ts b/ftgo-api-gateway-graphql/src/server.ts index 5e717951..56250423 100644 --- a/ftgo-api-gateway-graphql/src/server.ts +++ b/ftgo-api-gateway-graphql/src/server.ts @@ -30,4 +30,3 @@ app.post('/graphql', bodyParser.json(), makeGraphQLHandler()); app.get('/graphql', makeGraphQLHandler()); exports.app = app; - diff --git a/ftgo-api-gateway-graphql/tests/end-to-end/client.end2end.test.js b/ftgo-api-gateway-graphql/tests/end-to-end/client.end2end.test.js index 37956d98..5eb5baf2 100644 --- a/ftgo-api-gateway-graphql/tests/end-to-end/client.end2end.test.js +++ b/ftgo-api-gateway-graphql/tests/end-to-end/client.end2end.test.js @@ -19,7 +19,7 @@ afterAll(() => { */ test('findConsumerWithOrders', () => { - const client = new FtgoGraphQLClient({baseUrl: `http://${process.env.DOCKER_HOST_IP}:8088`}); + const client = new FtgoGraphQLClient({baseUrl: `http://${process.env.DOCKER_HOST_IP || "localhost"}:8088`}); return client.findConsumerWithOrders("1") .then(result => { @@ -29,4 +29,4 @@ test('findConsumerWithOrders', () => { console.log("result.data", JSON.stringify(result.data)); }); -}); \ No newline at end of file +}); diff --git a/ftgo-api-gateway-graphql/tsconfig.json b/ftgo-api-gateway-graphql/tsconfig.json index 46ccb9b3..c0a6fefb 100644 --- a/ftgo-api-gateway-graphql/tsconfig.json +++ b/ftgo-api-gateway-graphql/tsconfig.json @@ -7,12 +7,11 @@ "moduleResolution": "node", "noImplicitAny": true, "allowJs": true, - + "noImplicitAny": false, "lib": ["esnext.asynciterable", "es2015"], "baseUrl" : "./src", "paths": { "*": [ - "node_modules/*", "src/types/*" ] } diff --git a/ftgo-api-gateway/Dockerfile b/ftgo-api-gateway/Dockerfile index 02adbc0e..eb07a8d4 100644 --- a/ftgo-api-gateway/Dockerfile +++ b/ftgo-api-gateway/Dockerfile @@ -1,5 +1,3 @@ -FROM openjdk:8u171-jre-alpine -RUN apk --no-cache add curl -CMD java ${JAVA_OPTS} -jar ftgo-api-gateway.jar -HEALTHCHECK --start-period=30s --interval=5s CMD curl -f http://localhost:8080/actuator/health || exit 1 -COPY build/libs/ftgo-api-gateway.jar . +ARG baseImageVersion +FROM eventuateio/eventuate-examples-docker-images-spring-example-base-image:$baseImageVersion +COPY build/libs/ftgo-api-gateway.jar service.jar diff --git a/ftgo-api-gateway/build.gradle b/ftgo-api-gateway/build.gradle index 8ab78d91..c06f9911 100755 --- a/ftgo-api-gateway/build.gradle +++ b/ftgo-api-gateway/build.gradle @@ -1,14 +1,3 @@ -buildscript { - repositories { - maven { - url 'https://repo.spring.io/libs-milestone' - } - dependencies { - classpath "io.spring.gradle:dependency-management-plugin:$springDependencyManagementPluginVersion" - } - } -} - apply plugin: "io.spring.dependency-management" @@ -29,9 +18,6 @@ configurations.all { repositories { mavenCentral() - maven { - url 'https://repo.spring.io/libs-milestone' - } maven { url 'http://oss.jfrog.org/oss-snapshot-local/' } @@ -49,7 +35,7 @@ dependencies { compile 'org.springframework.cloud:spring-cloud-starter-sleuth' compile 'org.springframework.cloud:spring-cloud-starter-zipkin' - compile 'io.zipkin.brave:brave-bom:4.17.1' + compile "io.zipkin.brave:brave-bom:4.17.1" compile "io.micrometer:micrometer-registry-prometheus:$micrometerVersion" compile "org.springframework.boot:spring-boot-starter-actuator" diff --git a/ftgo-api-gateway/src/main/resources/application.properties b/ftgo-api-gateway/src/main/resources/application.properties index e43501db..235ad958 100644 --- a/ftgo-api-gateway/src/main/resources/application.properties +++ b/ftgo-api-gateway/src/main/resources/application.properties @@ -7,6 +7,5 @@ logging.level.com.github.tomakehurst.wiremock=TRACE management.endpoints.web.exposure.include=health,prometheus spring.sleuth.sampler.probability=1.0 -spring.sleuth.web.skipPattern=(^health.*) # routes diff --git a/ftgo-api-gateway/src/test/java/net/chrisrichardson/ftgo/apiagateway/ApiGatewayIntegrationTest.java b/ftgo-api-gateway/src/test/java/net/chrisrichardson/ftgo/apiagateway/ApiGatewayIntegrationTest.java index 0b125a6d..a356715a 100644 --- a/ftgo-api-gateway/src/test/java/net/chrisrichardson/ftgo/apiagateway/ApiGatewayIntegrationTest.java +++ b/ftgo-api-gateway/src/test/java/net/chrisrichardson/ftgo/apiagateway/ApiGatewayIntegrationTest.java @@ -9,8 +9,10 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -30,31 +32,29 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -//import static org.springframework.web.reactive.function.server.RequestPredicates.GET; -//import static org.springframework.web.reactive.function.server.RouterFunctions.route; - @RunWith(SpringRunner.class) @SpringBootTest(classes = ApiGatewayIntegrationTestConfiguration.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, - properties={"order.destinations.orderServiceUrl=http://localhost:8082", + properties={"order.destinations.orderServiceUrl=http://localhost:${wiremock.server.port}", "order.destinations.orderHistoryServiceUrl=http://localhost:8083", "consumer.destinations.consumerServiceUrl=http://localhost:9999" }) +@AutoConfigureWireMock(port=0) public class ApiGatewayIntegrationTest { + @LocalServerPort private int port; - @Rule - public WireMockRule wireMockRule = new WireMockRule(8082); // No-args constructor defaults to port 8080 - @Test public void shouldProxyCreateOrder() { + String expectedResponse = "{}"; + stubFor(post(urlEqualTo("/orders")) .willReturn(aResponse() .withStatus(200) - .withHeader("Content-Type", "text/xml") - .withBody("Some content"))); + .withHeader("Content-Type", "application/json") + .withBody(expectedResponse))); WebClient client = WebClient.create("http://localhost:" + port + "/orders"); @@ -68,7 +68,7 @@ public void shouldProxyCreateOrder() { assertNotNull(z); assertEquals(HttpStatus.OK, z.getStatusCode()); - assertEquals("Some content", z.getBody()); + assertEquals(expectedResponse, z.getBody()); verify(postRequestedFor(urlMatching("/orders"))); diff --git a/ftgo-api-gateway/src/test/java/net/chrisrichardson/ftgo/apiagateway/contract/OrderServiceProxyIntegrationTest.java b/ftgo-api-gateway/src/test/java/net/chrisrichardson/ftgo/apiagateway/contract/OrderServiceProxyIntegrationTest.java index 665f0931..fdfc13b7 100644 --- a/ftgo-api-gateway/src/test/java/net/chrisrichardson/ftgo/apiagateway/contract/OrderServiceProxyIntegrationTest.java +++ b/ftgo-api-gateway/src/test/java/net/chrisrichardson/ftgo/apiagateway/contract/OrderServiceProxyIntegrationTest.java @@ -34,7 +34,6 @@ public class OrderServiceProxyIntegrationTest { public void setUp() throws Exception { orderDestinations = new OrderDestinations(); String orderServiceUrl = "http://localhost:" + port; - System.out.println("orderServiceUrl=" + orderServiceUrl); orderDestinations.setOrderServiceUrl(orderServiceUrl); orderService = new OrderServiceProxy(orderDestinations, WebClient.create()); } diff --git a/ftgo-common-jpa/build.gradle b/ftgo-common-jpa/build.gradle index a822ece3..4c17c4bf 100644 --- a/ftgo-common-jpa/build.gradle +++ b/ftgo-common-jpa/build.gradle @@ -2,4 +2,4 @@ dependencies { compile "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion" -} \ No newline at end of file +} diff --git a/ftgo-common-jpa/src/main/resources/META-INF/orm.xml b/ftgo-common-jpa/src/main/resources/META-INF/orm.xml index bd621400..1a2487cc 100644 --- a/ftgo-common-jpa/src/main/resources/META-INF/orm.xml +++ b/ftgo-common-jpa/src/main/resources/META-INF/orm.xml @@ -10,4 +10,28 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ftgo-common/build.gradle b/ftgo-common/build.gradle index ee43bbfc..51d5ccc1 100644 --- a/ftgo-common/build.gradle +++ b/ftgo-common/build.gradle @@ -2,12 +2,17 @@ dependencies { // TODO eliminate this - problem is value objects like Money need to be embeddable. // TODO https://en.wikibooks.org/wiki/Java_Persistence/Embeddables#Example_of_an_Embeddable_object_XML - compile "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion" +// compile "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion" + compile "org.springframework.boot:spring-boot-starter:$springBootVersion" - compile "io.eventuate.client.java:eventuate-client-java-common-impl:$eventuateClientVersion" + // compile "org.springframework.boot:spring-boot-starter:$springBootVersion" + + compile "io.eventuate.common:eventuate-common-json-mapper:$eventuateCommonVersion" testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion" + compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.7" + runtime "javax.xml.bind:jaxb-api:2.2.11" runtime "com.sun.xml.bind:jaxb-core:2.2.11" runtime "com.sun.xml.bind:jaxb-impl:2.2.11" diff --git a/ftgo-common/src/main/java/net/chrisrichardson/ftgo/common/Address.java b/ftgo-common/src/main/java/net/chrisrichardson/ftgo/common/Address.java new file mode 100644 index 00000000..c9badc65 --- /dev/null +++ b/ftgo-common/src/main/java/net/chrisrichardson/ftgo/common/Address.java @@ -0,0 +1,80 @@ +package net.chrisrichardson.ftgo.common; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; + +public class Address { + + private String street1; + private String street2; + private String city; + private String state; + private String zip; + + private Address() { + } + + public Address(String street1, String street2, String city, String state, String zip) { + this.street1 = street1; + this.street2 = street2; + this.city = city; + this.state = state; + this.zip = zip; + } + + @Override + public boolean equals(Object o) { + return EqualsBuilder.reflectionEquals(this, o); + } + + @Override + public int hashCode() { + return HashCodeBuilder.reflectionHashCode(this); + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + + public String getStreet1() { + return street1; + } + + public void setStreet1(String street1) { + this.street1 = street1; + } + + public String getStreet2() { + return street2; + } + + public void setStreet2(String street2) { + this.street2 = street2; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public String getZip() { + return zip; + } + + public void setZip(String zip) { + this.zip = zip; + } +} diff --git a/ftgo-common/src/main/java/net/chrisrichardson/ftgo/common/CommonJsonMapperInitializer.java b/ftgo-common/src/main/java/net/chrisrichardson/ftgo/common/CommonJsonMapperInitializer.java index 119f7ff7..aeef514d 100644 --- a/ftgo-common/src/main/java/net/chrisrichardson/ftgo/common/CommonJsonMapperInitializer.java +++ b/ftgo-common/src/main/java/net/chrisrichardson/ftgo/common/CommonJsonMapperInitializer.java @@ -1,6 +1,8 @@ package net.chrisrichardson.ftgo.common; -import io.eventuate.javaclient.commonimpl.JSonMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import io.eventuate.common.json.mapper.JSonMapper; import javax.annotation.PostConstruct; @@ -13,5 +15,8 @@ public void initialize() { public static void registerMoneyModule() { JSonMapper.objectMapper.registerModule(new MoneyModule()); + JSonMapper.objectMapper.registerModule(new JavaTimeModule()); + JSonMapper.objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); + } } diff --git a/ftgo-common/src/main/java/net/chrisrichardson/ftgo/common/Money.java b/ftgo-common/src/main/java/net/chrisrichardson/ftgo/common/Money.java index 02d69d8d..0f75ec53 100644 --- a/ftgo-common/src/main/java/net/chrisrichardson/ftgo/common/Money.java +++ b/ftgo-common/src/main/java/net/chrisrichardson/ftgo/common/Money.java @@ -73,4 +73,7 @@ public Money multiply(int x) { return new Money(amount.multiply(new BigDecimal(x))); } + public Long asLong() { + return multiply(100).amount.longValue(); + } } diff --git a/ftgo-common/src/main/java/net/chrisrichardson/ftgo/common/PersonName.java b/ftgo-common/src/main/java/net/chrisrichardson/ftgo/common/PersonName.java index 59a95721..d99c6dd0 100644 --- a/ftgo-common/src/main/java/net/chrisrichardson/ftgo/common/PersonName.java +++ b/ftgo-common/src/main/java/net/chrisrichardson/ftgo/common/PersonName.java @@ -1,8 +1,5 @@ package net.chrisrichardson.ftgo.common; -import javax.persistence.Embeddable; - -@Embeddable public class PersonName { private String firstName; private String lastName; diff --git a/ftgo-common/src/main/java/net/chrisrichardson/ftgo/common/RevisedOrderLineItem.java b/ftgo-common/src/main/java/net/chrisrichardson/ftgo/common/RevisedOrderLineItem.java new file mode 100644 index 00000000..3c5d9dce --- /dev/null +++ b/ftgo-common/src/main/java/net/chrisrichardson/ftgo/common/RevisedOrderLineItem.java @@ -0,0 +1,50 @@ +package net.chrisrichardson.ftgo.common; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; + +import java.util.Objects; + +public class RevisedOrderLineItem { + private int quantity; + private String menuItemId; + + public RevisedOrderLineItem() { + } + + public RevisedOrderLineItem(int quantity, String menuItemId) { + this.quantity = quantity; + this.menuItemId = menuItemId; + } + + public int getQuantity() { + return quantity; + } + + public void setQuantity(int quantity) { + this.quantity = quantity; + } + + public String getMenuItemId() { + return menuItemId; + } + + public void setMenuItemId(String menuItemId) { + this.menuItemId = menuItemId; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + + @Override + public boolean equals(Object o) { + return EqualsBuilder.reflectionEquals(this, o); + } + + @Override + public int hashCode() { + return Objects.hash(quantity, menuItemId); + } +} diff --git a/ftgo-common/src/main/resources/application.properties b/ftgo-common/src/main/resources/application.properties deleted file mode 100644 index 8a316287..00000000 --- a/ftgo-common/src/main/resources/application.properties +++ /dev/null @@ -1,21 +0,0 @@ -spring.jpa.generate-ddl=true -logging.level.org.springframework.orm.jpa=INFO -logging.level.org.hibernate.SQL=DEBUG -logging.level.io.eventuate=DEBUG -logging.level.net.chrisrichardson.ftgo=DEBUG -logging.level.io.eventuate.tram=TRACE -spring.datasource.url=jdbc:mysql://${DOCKER_HOST_IP:localhost}/eventuate -spring.datasource.username=mysqluser -spring.datasource.password=mysqlpw -spring.datasource.driver.class.name=com.mysql.jdbc.Driver -spring.data.mongodb.uri=mongodb://${DOCKER_HOST_IP:localhost}/bankingexampledb - -eventuatelocal.kafka.bootstrap.servers=${DOCKER_HOST_IP:localhost}:9092 -eventuatelocal.cdc.db.user.name=root -eventuatelocal.cdc.db.password=rootpassword -eventuatelocal.zookeeper.connection.string=${DOCKER_HOST_IP:localhost}:2181 - -aws.access.key_id=id_key -aws.secret.access.key=access_key -aws.dynamodb.endpoint.url=http://${DOCKER_HOST_IP:localhost}:8000 -aws.region=us-west-2 diff --git a/ftgo-common/src/test/java/net/chrisrichardson/ftgo/common/MoneySerializationTest.java b/ftgo-common/src/test/java/net/chrisrichardson/ftgo/common/MoneySerializationTest.java index 9a2741c7..47adaefa 100644 --- a/ftgo-common/src/test/java/net/chrisrichardson/ftgo/common/MoneySerializationTest.java +++ b/ftgo-common/src/test/java/net/chrisrichardson/ftgo/common/MoneySerializationTest.java @@ -1,8 +1,7 @@ package net.chrisrichardson.ftgo.common; import com.fasterxml.jackson.databind.JsonMappingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import io.eventuate.javaclient.commonimpl.JSonMapper; +import io.eventuate.common.json.mapper.JSonMapper; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import org.apache.commons.lang.builder.ToStringBuilder; diff --git a/ftgo-consumer-service-api-spec/src/main/resources/ValidateOrderByConsumer.json b/ftgo-consumer-service-api-spec/src/main/resources/ValidateOrderByConsumer.json new file mode 100644 index 00000000..ad483669 --- /dev/null +++ b/ftgo-consumer-service-api-spec/src/main/resources/ValidateOrderByConsumer.json @@ -0,0 +1,22 @@ +{ + "type": "object", + "properties": { + "consumerId": { + "type": "integer", + "format": "int64" + }, + "orderId": { + "type": "integer", + "format": "int64" + }, + "orderTotal": { + "type": "string" + } + }, + "required": [ + "consumerId", + "orderId", + "orderTotal" + ], + "javaInterfaces": ["io.eventuate.tram.commands.common.Command"] +} diff --git a/ftgo-consumer-service-api-spec/src/main/resources/ftgo-consumer-service-swagger.json b/ftgo-consumer-service-api-spec/src/main/resources/ftgo-consumer-service-swagger.json new file mode 100644 index 00000000..6649f86d --- /dev/null +++ b/ftgo-consumer-service-api-spec/src/main/resources/ftgo-consumer-service-swagger.json @@ -0,0 +1,136 @@ +{ + "swagger": "2.0", + "info": { + "description": "Api Documentation", + "version": "1.0", + "title": "Api Documentation", + "termsOfService": "urn:tos", + "contact": {}, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0" + } + }, + "host": "localhost:8081", + "basePath": "/", + "tags": [ + { + "name": "consumer-controller", + "description": "Consumer Controller" + } + ], + "paths": { + "/consumers": { + "post": { + "tags": [ + "consumer-controller" + ], + "summary": "create", + "operationId": "createUsingPOST", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "in": "body", + "name": "request", + "description": "request", + "required": true, + "schema": { + "$ref": "#/definitions/CreateConsumerRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/CreateConsumerResponse" + } + } + } + } + }, + "/consumers/{consumerId}": { + "get": { + "tags": [ + "consumer-controller" + ], + "summary": "get", + "operationId": "getUsingGET", + "produces": [ + "*/*" + ], + "parameters": [ + { + "name": "consumerId", + "in": "path", + "description": "consumerId", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/GetConsumerResponse" + } + } + } + } + } + }, + "definitions": { + "CreateConsumerRequest": { + "type": "object", + "properties": { + "name": { + "$ref": "#/definitions/PersonName" + } + }, + "title": "CreateConsumerRequest" + }, + "CreateConsumerResponse": { + "type": "object", + "properties": { + "consumerId": { + "type": "integer", + "format": "int64" + } + }, + "title": "CreateConsumerResponse" + }, + "GetConsumerResponse": { + "type": "object", + "properties": { + "consumerId": { + "type": "integer", + "format": "int64" + }, + "name": { + "$ref": "#/definitions/PersonName" + } + }, + "title": "GetConsumerResponse" + }, + "PersonName": { + "type": "object", + "properties": { + "firstName": { + "type": "string" + }, + "lastName": { + "type": "string" + } + }, + "required" : ["lastName", "firstName"], + "additionalProperties": true, + "title": "PersonName" + } + } +} diff --git a/ftgo-consumer-service-api/build.gradle b/ftgo-consumer-service-api/build.gradle index 93b7c8bb..95508518 100644 --- a/ftgo-consumer-service-api/build.gradle +++ b/ftgo-consumer-service-api/build.gradle @@ -1,6 +1,6 @@ dependencies { - compile "io.eventuate.tram.sagas:eventuate-jpa-sagas-framework:$eventuateTramSagasVersion" + compile "io.eventuate.tram.sagas:eventuate-tram-sagas-spring-participant" compile project(":ftgo-common") -} \ No newline at end of file +} diff --git a/ftgo-consumer-service-contracts/build.gradle b/ftgo-consumer-service-contracts/build.gradle index 8eaad933..8e4e6077 100644 --- a/ftgo-consumer-service-contracts/build.gradle +++ b/ftgo-consumer-service-contracts/build.gradle @@ -1,15 +1,3 @@ -buildscript { - dependencies { - // if using Stub Runner (consumer side) only remove this dependency - classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:$springCloudContractDependenciesVersion" - } - repositories { - mavenCentral() - maven { - url 'https://repo.spring.io/libs-milestone' - } - } -} apply plugin: 'spring-cloud-contract' apply plugin: 'maven-publish' @@ -31,3 +19,7 @@ generateContractTests.enabled = false build.finalizedBy(publish) + +dependencies { + compile 'org.codehaus.groovy:groovy-all:2.4.6' +} diff --git a/ftgo-consumer-service/Dockerfile b/ftgo-consumer-service/Dockerfile index 888004ef..c729320a 100644 --- a/ftgo-consumer-service/Dockerfile +++ b/ftgo-consumer-service/Dockerfile @@ -1,5 +1,3 @@ -FROM openjdk:8u171-jre-alpine -RUN apk --no-cache add curl -CMD java ${JAVA_OPTS} -jar ftgo-consumer-service.jar -HEALTHCHECK --start-period=30s --interval=5s CMD curl -f http://localhost:8080/actuator/health || exit 1 -COPY build/libs/ftgo-consumer-service.jar . +ARG baseImageVersion +FROM eventuateio/eventuate-examples-docker-images-spring-example-base-image:$baseImageVersion +COPY build/libs/ftgo-consumer-service.jar service.jar diff --git a/ftgo-consumer-service/build.gradle b/ftgo-consumer-service/build.gradle index 5baff76f..1c44c6b4 100644 --- a/ftgo-consumer-service/build.gradle +++ b/ftgo-consumer-service/build.gradle @@ -2,15 +2,21 @@ apply plugin: FtgoServicePlugin +configurations.all { + // Out of date, conflicting JSON library + exclude group: "com.vaadin.external.google" +} dependencies { - compile "io.eventuate.tram.core:eventuate-tram-jdbc-kafka:$eventuateTramVersion" - compile "io.eventuate.tram.core:eventuate-tram-events:$eventuateTramVersion" - compile "io.eventuate.tram.sagas:eventuate-tram-sagas-simple-dsl:$eventuateTramSagasVersion" + compile "io.eventuate.tram.core:eventuate-tram-spring-jdbc-kafka" + compile "io.eventuate.tram.core:eventuate-tram-spring-events" + compile "io.eventuate.tram.core:eventuate-tram-spring-messaging" + compile "io.eventuate.tram.sagas:eventuate-tram-sagas-spring-orchestration-simple-dsl" compile project(":common-swagger") compile project(":ftgo-consumer-service-api") + compile project(":ftgo-consumer-service-api-spec") compile "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion" compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion" @@ -18,12 +24,22 @@ dependencies { compile 'javax.el:javax.el-api:2.2.5' - testCompile "io.eventuate.util:eventuate-util-test:$eventuateUtilVersion" - testCompile "io.eventuate.tram.core:eventuate-tram-test-util:$eventuateTramVersion" + compile('org.apache.kafka:kafka-clients:2.3.0') { + force = true + } + + testCompile "io.eventuate.util:eventuate-util-test" + testCompile "io.eventuate.tram.core:eventuate-tram-test-util" - testCompile "io.eventuate.tram.sagas:eventuate-tram-sagas-in-memory:$eventuateTramSagasVersion" + testCompile "io.eventuate.tram.core:eventuate-tram-spring-in-memory" + testCompile "io.eventuate.tram.sagas:eventuate-tram-sagas-spring-in-memory" testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion" testCompile "com.jayway.restassured:rest-assured:$restAssuredVersion" testCompile "com.jayway.jsonpath:json-path:2.3.0" + testCompile "org.hamcrest:hamcrest:2.1" + + testCompile "com.atlassian.oai:swagger-request-validator-springmvc:${swaggerRequestValidatorVersion}" + testCompile 'io.rest-assured:spring-mock-mvc:3.0.6' + testCompile project(":ftgo-test-util-json-schema") } diff --git a/ftgo-consumer-service/src/deployment/kubernetes/ftgo-consumer-service.yml b/ftgo-consumer-service/src/deployment/kubernetes/ftgo-consumer-service.yml index e5caa055..28a7e8a4 100644 --- a/ftgo-consumer-service/src/deployment/kubernetes/ftgo-consumer-service.yml +++ b/ftgo-consumer-service/src/deployment/kubernetes/ftgo-consumer-service.yml @@ -29,31 +29,27 @@ spec: containers: - name: ftgo-consumer-service image: msapatterns/ftgo-consumer-service:latest - imagePullPolicy: Always + imagePullPolicy: IfNotPresent ports: - containerPort: 8080 name: httpport env: - name: JAVA_OPTS - value: "-Dsun.net.inetaddr.ttl=30" + value: "-Dsun.net.inetaddr.ttl=30 -Xmx192m" - name: SPRING_DATASOURCE_URL - value: jdbc:mysql://ftgo-mysql/eventuate + value: jdbc:mysql://ftgo-mysql/ftgo_consumer_service - name: SPRING_DATASOURCE_USERNAME - valueFrom: - secretKeyRef: - name: ftgo-db-secret - key: username + value: ftgo_consumer_service_user - name: SPRING_DATASOURCE_PASSWORD - valueFrom: - secretKeyRef: - name: ftgo-db-secret - key: password + value: ftgo_consumer_service_password - name: SPRING_DATASOURCE_DRIVER_CLASS_NAME value: com.mysql.jdbc.Driver - name: EVENTUATELOCAL_KAFKA_BOOTSTRAP_SERVERS value: ftgo-kafka:9092 - name: EVENTUATELOCAL_ZOOKEEPER_CONNECTION_STRING value: ftgo-zookeeper:2181 + - name: EVENTUATE_DATABASE_SCHEMA + value: ftgo_consumer_service livenessProbe: httpGet: path: /actuator/health diff --git a/ftgo-consumer-service-api/src/main/java/net/chrisrichardson/ftgo/consumerservice/api/ValidateOrderByConsumer.java b/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/api/ValidateOrderByConsumer.java similarity index 97% rename from ftgo-consumer-service-api/src/main/java/net/chrisrichardson/ftgo/consumerservice/api/ValidateOrderByConsumer.java rename to ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/api/ValidateOrderByConsumer.java index 3019df99..e5daf8ea 100644 --- a/ftgo-consumer-service-api/src/main/java/net/chrisrichardson/ftgo/consumerservice/api/ValidateOrderByConsumer.java +++ b/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/api/ValidateOrderByConsumer.java @@ -36,7 +36,7 @@ public ValidateOrderByConsumer(long consumerId, long orderId, Money orderTotal) this.orderTotal = orderTotal; } - public Long getOrderId() { + public long getOrderId() { return orderId; } diff --git a/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/domain/Consumer.java b/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/domain/Consumer.java index 2aac94e7..5206e643 100644 --- a/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/domain/Consumer.java +++ b/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/domain/Consumer.java @@ -1,6 +1,6 @@ package net.chrisrichardson.ftgo.consumerservice.domain; -import io.eventuate.tram.events.ResultWithEvents; +import io.eventuate.tram.events.publisher.ResultWithEvents; import net.chrisrichardson.ftgo.common.Money; import net.chrisrichardson.ftgo.common.PersonName; diff --git a/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/domain/ConsumerService.java b/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/domain/ConsumerService.java index d9288591..50e1bdfc 100644 --- a/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/domain/ConsumerService.java +++ b/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/domain/ConsumerService.java @@ -1,7 +1,7 @@ package net.chrisrichardson.ftgo.consumerservice.domain; -import io.eventuate.tram.events.ResultWithEvents; import io.eventuate.tram.events.publisher.DomainEventPublisher; +import io.eventuate.tram.events.publisher.ResultWithEvents; import net.chrisrichardson.ftgo.common.Money; import net.chrisrichardson.ftgo.common.PersonName; import org.springframework.beans.factory.annotation.Autowired; @@ -9,7 +9,6 @@ import java.util.Optional; -@Transactional public class ConsumerService { @Autowired @@ -23,6 +22,7 @@ public void validateOrderForConsumer(long consumerId, Money orderTotal) { consumer.orElseThrow(ConsumerNotFoundException::new).validateOrderByConsumer(orderTotal); } + @Transactional public ResultWithEvents create(PersonName name) { ResultWithEvents rwe = Consumer.create(name); consumerRepository.save(rwe.result); diff --git a/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/domain/ConsumerServiceConfiguration.java b/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/domain/ConsumerServiceConfiguration.java index bfcabff5..29dda689 100644 --- a/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/domain/ConsumerServiceConfiguration.java +++ b/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/domain/ConsumerServiceConfiguration.java @@ -1,11 +1,9 @@ package net.chrisrichardson.ftgo.consumerservice.domain; -import io.eventuate.tram.commands.common.ChannelMapping; -import io.eventuate.tram.commands.common.DefaultChannelMapping; import io.eventuate.tram.commands.consumer.CommandDispatcher; -import io.eventuate.tram.events.publisher.TramEventsPublisherConfiguration; -import io.eventuate.tram.sagas.participant.SagaCommandDispatcher; -import io.eventuate.tram.sagas.participant.SagaParticipantConfiguration; +import io.eventuate.tram.spring.events.publisher.TramEventsPublisherConfiguration; +import io.eventuate.tram.sagas.participant.SagaCommandDispatcherFactory; +import io.eventuate.tram.sagas.spring.participant.SagaParticipantConfiguration; import net.chrisrichardson.ftgo.common.CommonConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.annotation.Bean; @@ -18,7 +16,7 @@ @Configuration @EnableJpaRepositories @EnableAutoConfiguration -@Import({SagaParticipantConfiguration.class, TramEventsPublisherConfiguration.class, CommonConfiguration.class}) +@Import({SagaParticipantConfiguration.class, TramEventsPublisherConfiguration.class, CommonConfiguration.class, SagaParticipantConfiguration.class}) @EnableTransactionManagement @ComponentScan public class ConsumerServiceConfiguration { @@ -34,13 +32,9 @@ public ConsumerService consumerService() { } @Bean - public CommandDispatcher commandDispatcher(ConsumerServiceCommandHandlers consumerServiceCommandHandlers) { - return new SagaCommandDispatcher("consumerServiceDispatcher", consumerServiceCommandHandlers.commandHandlers()); + public CommandDispatcher commandDispatcher(ConsumerServiceCommandHandlers consumerServiceCommandHandlers, SagaCommandDispatcherFactory sagaCommandDispatcherFactory) { + return sagaCommandDispatcherFactory.make("consumerServiceDispatcher", consumerServiceCommandHandlers.commandHandlers()); } - @Bean - public ChannelMapping channelMapping() { - return new DefaultChannelMapping.DefaultChannelMappingBuilder().build(); - } } diff --git a/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/main/ConsumerServiceMain.java b/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/main/ConsumerServiceMain.java index 0a558ac9..d5b85881 100644 --- a/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/main/ConsumerServiceMain.java +++ b/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/main/ConsumerServiceMain.java @@ -1,6 +1,6 @@ package net.chrisrichardson.ftgo.consumerservice.main; -import io.eventuate.jdbckafka.TramJdbcKafkaConfiguration; +import io.eventuate.tram.spring.jdbckafka.TramJdbcKafkaConfiguration; import net.chrisrichardson.eventstore.examples.customersandorders.commonswagger.CommonSwaggerConfiguration; import net.chrisrichardson.ftgo.consumerservice.web.ConsumerWebConfiguration; import org.springframework.boot.SpringApplication; diff --git a/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/web/ConsumerController.java b/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/web/ConsumerController.java index afaf069e..76b2761c 100644 --- a/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/web/ConsumerController.java +++ b/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/web/ConsumerController.java @@ -1,11 +1,8 @@ package net.chrisrichardson.ftgo.consumerservice.web; -import io.eventuate.tram.events.ResultWithEvents; -import net.chrisrichardson.ftgo.consumerservice.api.web.CreateConsumerRequest; -import net.chrisrichardson.ftgo.consumerservice.api.web.CreateConsumerResponse; +import io.eventuate.tram.events.publisher.ResultWithEvents; import net.chrisrichardson.ftgo.consumerservice.domain.Consumer; import net.chrisrichardson.ftgo.consumerservice.domain.ConsumerService; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -14,9 +11,12 @@ @RequestMapping(path="/consumers") public class ConsumerController { - @Autowired private ConsumerService consumerService; + public ConsumerController(ConsumerService consumerService) { + this.consumerService = consumerService; + } + @RequestMapping(method= RequestMethod.POST) public CreateConsumerResponse create(@RequestBody CreateConsumerRequest request) { ResultWithEvents result = consumerService.create(request.getName()); diff --git a/ftgo-consumer-service-api/src/main/java/net/chrisrichardson/ftgo/consumerservice/api/web/CreateConsumerRequest.java b/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/web/CreateConsumerRequest.java similarity index 86% rename from ftgo-consumer-service-api/src/main/java/net/chrisrichardson/ftgo/consumerservice/api/web/CreateConsumerRequest.java rename to ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/web/CreateConsumerRequest.java index 09a0eb2c..43f17c95 100644 --- a/ftgo-consumer-service-api/src/main/java/net/chrisrichardson/ftgo/consumerservice/api/web/CreateConsumerRequest.java +++ b/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/web/CreateConsumerRequest.java @@ -1,4 +1,4 @@ -package net.chrisrichardson.ftgo.consumerservice.api.web; +package net.chrisrichardson.ftgo.consumerservice.web; import net.chrisrichardson.ftgo.common.PersonName; diff --git a/ftgo-consumer-service-api/src/main/java/net/chrisrichardson/ftgo/consumerservice/api/web/CreateConsumerResponse.java b/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/web/CreateConsumerResponse.java similarity index 85% rename from ftgo-consumer-service-api/src/main/java/net/chrisrichardson/ftgo/consumerservice/api/web/CreateConsumerResponse.java rename to ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/web/CreateConsumerResponse.java index c666063b..4a98ec1b 100644 --- a/ftgo-consumer-service-api/src/main/java/net/chrisrichardson/ftgo/consumerservice/api/web/CreateConsumerResponse.java +++ b/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/web/CreateConsumerResponse.java @@ -1,4 +1,4 @@ -package net.chrisrichardson.ftgo.consumerservice.api.web; +package net.chrisrichardson.ftgo.consumerservice.web; public class CreateConsumerResponse { private long consumerId; diff --git a/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/web/GetConsumerResponse.java b/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/web/GetConsumerResponse.java index e996d396..0185bb91 100644 --- a/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/web/GetConsumerResponse.java +++ b/ftgo-consumer-service/src/main/java/net/chrisrichardson/ftgo/consumerservice/web/GetConsumerResponse.java @@ -1,8 +1,6 @@ package net.chrisrichardson.ftgo.consumerservice.web; import net.chrisrichardson.ftgo.common.PersonName; -import net.chrisrichardson.ftgo.consumerservice.api.web.CreateConsumerResponse; -import net.chrisrichardson.ftgo.consumerservice.domain.Consumer; public class GetConsumerResponse extends CreateConsumerResponse { private PersonName name; diff --git a/ftgo-consumer-service/src/main/resources/application.properties b/ftgo-consumer-service/src/main/resources/application.properties index a0502e52..1afc72aa 100644 --- a/ftgo-consumer-service/src/main/resources/application.properties +++ b/ftgo-consumer-service/src/main/resources/application.properties @@ -1,17 +1,17 @@ spring.application.name=ftgo-consumer-service +management.endpoint.health.show-details=always + spring.jpa.generate-ddl=true logging.level.org.springframework.orm.jpa=INFO logging.level.org.hibernate.SQL=DEBUG logging.level.io.eventuate=DEBUG logging.level.net.chrisrichardson.ftgo=DEBUG -logging.level.io.eventuate.tram=TRACE -spring.datasource.url=jdbc:mysql://${DOCKER_HOST_IP:localhost}/eventuate -spring.datasource.username=mysqluser -spring.datasource.password=mysqlpw -spring.datasource.driver.class.name=com.mysql.jdbc.Driver +logging.level.io.eventuate.tram=DEBUG +spring.datasource.url=jdbc:mysql://${DOCKER_HOST_IP:localhost}/ftgo_consumer_service +spring.datasource.username=ftgo_consumer_service_user +spring.datasource.password=ftgo_consumer_service_password +spring.datasource.driver-class-name=com.mysql.jdbc.Driver eventuatelocal.kafka.bootstrap.servers=${DOCKER_HOST_IP:localhost}:9092 -eventuatelocal.cdc.db.user.name=root -eventuatelocal.cdc.db.password=rootpassword eventuatelocal.zookeeper.connection.string=${DOCKER_HOST_IP:localhost}:2181 diff --git a/ftgo-consumer-service/src/test/java/net/chrisrichardson/ftgo/consumerservice/ConsumerServiceInMemoryIntegrationTest.java b/ftgo-consumer-service/src/test/java/net/chrisrichardson/ftgo/consumerservice/ConsumerServiceInMemoryIntegrationTest.java index a2f35839..7012a163 100644 --- a/ftgo-consumer-service/src/test/java/net/chrisrichardson/ftgo/consumerservice/ConsumerServiceInMemoryIntegrationTest.java +++ b/ftgo-consumer-service/src/test/java/net/chrisrichardson/ftgo/consumerservice/ConsumerServiceInMemoryIntegrationTest.java @@ -2,21 +2,22 @@ import io.eventuate.tram.commands.producer.CommandProducer; -import io.eventuate.tram.commands.producer.TramCommandProducerConfiguration; -import io.eventuate.tram.inmemory.TramInMemoryConfiguration; +import io.eventuate.tram.spring.commands.producer.TramCommandProducerConfiguration; +import io.eventuate.tram.spring.inmemory.TramInMemoryConfiguration; import io.eventuate.tram.testutil.TestMessageConsumer; import io.eventuate.tram.testutil.TestMessageConsumerFactory; import net.chrisrichardson.ftgo.common.Money; import net.chrisrichardson.ftgo.common.PersonName; import net.chrisrichardson.ftgo.consumerservice.api.ValidateOrderByConsumer; +import net.chrisrichardson.ftgo.consumerservice.web.CreateConsumerRequest; import net.chrisrichardson.ftgo.consumerservice.web.ConsumerWebConfiguration; -import net.chrisrichardson.ftgo.consumerservice.api.web.CreateConsumerRequest; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -42,6 +43,7 @@ public class ConsumerServiceInMemoryIntegrationTest { @Import({ConsumerWebConfiguration.class, TramCommandProducerConfiguration.class, TramInMemoryConfiguration.class}) + @EnableAutoConfiguration public static class TestConfiguration { @Bean diff --git a/ftgo-consumer-service/src/test/java/net/chrisrichardson/ftgo/consumerservice/api/ValidateOrderByConsumerTest.java b/ftgo-consumer-service/src/test/java/net/chrisrichardson/ftgo/consumerservice/api/ValidateOrderByConsumerTest.java new file mode 100644 index 00000000..aeb3aedb --- /dev/null +++ b/ftgo-consumer-service/src/test/java/net/chrisrichardson/ftgo/consumerservice/api/ValidateOrderByConsumerTest.java @@ -0,0 +1,32 @@ +package net.chrisrichardson.ftgo.consumerservice.api; + +import net.chrisrichardson.ftgo.common.CommonJsonMapperInitializer; +import net.chrisrichardson.ftgo.common.Money; +import net.chrisrichardson.ftgo.testutil.jsonschema.ValidatingJSONMapper; +import org.json.JSONObject; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class ValidateOrderByConsumerTest { + + static { + CommonJsonMapperInitializer.registerMoneyModule(); + } + + @Test + public void shouldDeserialize() { + + ValidatingJSONMapper mapper = ValidatingJSONMapper.forSchema("/ValidateOrderByConsumer.json"); + + JSONObject jsonObject = new JSONObject().put("consumerId", 1).put("orderId", 2).put("orderTotal", "12.34"); + + ValidateOrderByConsumer cmd = mapper.fromJSON(jsonObject, ValidateOrderByConsumer.class); + + assertEquals(1, cmd.getConsumerId()); + assertEquals(2, cmd.getOrderId()); + assertEquals(new Money("12.34"), cmd.getOrderTotal()); + } + + +} \ No newline at end of file diff --git a/ftgo-consumer-service/src/test/java/net/chrisrichardson/ftgo/consumerservice/web/ConsumerControllerTest.java b/ftgo-consumer-service/src/test/java/net/chrisrichardson/ftgo/consumerservice/web/ConsumerControllerTest.java new file mode 100644 index 00000000..9ede24d0 --- /dev/null +++ b/ftgo-consumer-service/src/test/java/net/chrisrichardson/ftgo/consumerservice/web/ConsumerControllerTest.java @@ -0,0 +1,80 @@ +package net.chrisrichardson.ftgo.consumerservice.web; + +import com.atlassian.oai.validator.OpenApiInteractionValidator; +import com.atlassian.oai.validator.springmvc.OpenApiValidationFilter; +import com.atlassian.oai.validator.springmvc.OpenApiValidationInterceptor; +import com.atlassian.oai.validator.springmvc.SpringMVCLevelResolverFactory; +import io.eventuate.common.json.mapper.JSonMapper; +import net.chrisrichardson.ftgo.common.CommonJsonMapperInitializer; +import net.chrisrichardson.ftgo.common.PersonName; +import net.chrisrichardson.ftgo.consumerservice.domain.Consumer; +import net.chrisrichardson.ftgo.consumerservice.domain.ConsumerService; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; +import org.springframework.core.io.ClassPathResource; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.test.web.servlet.setup.StandaloneMockMvcBuilder; +import org.springframework.util.StreamUtils; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.util.Optional; + +import static io.restassured.module.mockmvc.RestAssuredMockMvc.given; +import static org.mockito.Mockito.mock; + +public class ConsumerControllerTest { + + private ConsumerController consumerController; + private ConsumerService consumerService; + private Consumer consumer; + + @Before + public void setUp() { + consumerService = mock(ConsumerService.class); + consumerController = new ConsumerController(consumerService); + consumer = new Consumer(new PersonName("x", "y")); + } + + @Test + public void shouldGetConsumer() throws IOException { + Mockito.when(consumerService.findById(1)).thenReturn(Optional.of(consumer)); + given(). + standaloneSetup(configureControllers(consumerController)). + when(). + get("/consumers/1"). + then(). + statusCode(200) + ; + + } + + private StandaloneMockMvcBuilder configureControllers(Object... controllers) throws IOException { + CommonJsonMapperInitializer.registerMoneyModule(); + MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter(JSonMapper.objectMapper); + + ClassPathResource swaggerResource = new ClassPathResource("ftgo-consumer-service-swagger.json"); + + String specification = StreamUtils.copyToString(swaggerResource.getInputStream(), Charset.forName("UTF-8")); + + OpenApiInteractionValidator validator = OpenApiInteractionValidator + .createForInlineApiSpecification(specification) + .withLevelResolver(SpringMVCLevelResolverFactory.create()) + .build(); + + OpenApiValidationInterceptor validationInterceptor = new OpenApiValidationInterceptor(validator); + + OpenApiValidationFilter openApiValidationFilter = new OpenApiValidationFilter( + true, // enable request validation + true // enable response validation + ); + + return MockMvcBuilders.standaloneSetup(controllers) + .setMessageConverters(converter) + .addFilters(openApiValidationFilter) + .addInterceptors(validationInterceptor); + } + +} \ No newline at end of file diff --git a/ftgo-delivery-service-api/build.gradle b/ftgo-delivery-service-api/build.gradle new file mode 100644 index 00000000..2b5a07b9 --- /dev/null +++ b/ftgo-delivery-service-api/build.gradle @@ -0,0 +1,3 @@ +dependencies { + compile project(":ftgo-common") +} diff --git a/ftgo-delivery-service-api/src/main/java/net/chrisrichardson/ftgo/deliveryservice/api/web/ActionInfo.java b/ftgo-delivery-service-api/src/main/java/net/chrisrichardson/ftgo/deliveryservice/api/web/ActionInfo.java new file mode 100644 index 00000000..eee61c86 --- /dev/null +++ b/ftgo-delivery-service-api/src/main/java/net/chrisrichardson/ftgo/deliveryservice/api/web/ActionInfo.java @@ -0,0 +1,20 @@ +package net.chrisrichardson.ftgo.deliveryservice.api.web; + +public class ActionInfo { + private DeliveryActionType type; + + public ActionInfo() { + } + + public ActionInfo(DeliveryActionType type) { + this.type = type; + } + + public DeliveryActionType getType() { + return type; + } + + public void setType(DeliveryActionType type) { + this.type = type; + } +} diff --git a/ftgo-delivery-service-api/src/main/java/net/chrisrichardson/ftgo/deliveryservice/api/web/CourierAvailability.java b/ftgo-delivery-service-api/src/main/java/net/chrisrichardson/ftgo/deliveryservice/api/web/CourierAvailability.java new file mode 100644 index 00000000..7e9620e5 --- /dev/null +++ b/ftgo-delivery-service-api/src/main/java/net/chrisrichardson/ftgo/deliveryservice/api/web/CourierAvailability.java @@ -0,0 +1,21 @@ +package net.chrisrichardson.ftgo.deliveryservice.api.web; + +public class CourierAvailability { + + private boolean available; + + public CourierAvailability() { + } + + public CourierAvailability(boolean available) { + this.available = available; + } + + public boolean isAvailable() { + return available; + } + + public void setAvailable(boolean available) { + this.available = available; + } +} diff --git a/ftgo-delivery-service-api/src/main/java/net/chrisrichardson/ftgo/deliveryservice/api/web/DeliveryActionType.java b/ftgo-delivery-service-api/src/main/java/net/chrisrichardson/ftgo/deliveryservice/api/web/DeliveryActionType.java new file mode 100644 index 00000000..a13f276f --- /dev/null +++ b/ftgo-delivery-service-api/src/main/java/net/chrisrichardson/ftgo/deliveryservice/api/web/DeliveryActionType.java @@ -0,0 +1,4 @@ +package net.chrisrichardson.ftgo.deliveryservice.api.web; + +public enum DeliveryActionType { PICKUP, DROPOFF +} diff --git a/ftgo-delivery-service-api/src/main/java/net/chrisrichardson/ftgo/deliveryservice/api/web/DeliveryInfo.java b/ftgo-delivery-service-api/src/main/java/net/chrisrichardson/ftgo/deliveryservice/api/web/DeliveryInfo.java new file mode 100644 index 00000000..6e33ad3a --- /dev/null +++ b/ftgo-delivery-service-api/src/main/java/net/chrisrichardson/ftgo/deliveryservice/api/web/DeliveryInfo.java @@ -0,0 +1,32 @@ +package net.chrisrichardson.ftgo.deliveryservice.api.web; + +public class DeliveryInfo { + + private long id; + private DeliveryState state; + + public DeliveryInfo() { + } + + public DeliveryInfo(long id, DeliveryState state) { + + this.id = id; + this.state = state; + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public DeliveryState getState() { + return state; + } + + public void setState(DeliveryState state) { + this.state = state; + } +} diff --git a/ftgo-delivery-service-api/src/main/java/net/chrisrichardson/ftgo/deliveryservice/api/web/DeliveryState.java b/ftgo-delivery-service-api/src/main/java/net/chrisrichardson/ftgo/deliveryservice/api/web/DeliveryState.java new file mode 100644 index 00000000..29d4a245 --- /dev/null +++ b/ftgo-delivery-service-api/src/main/java/net/chrisrichardson/ftgo/deliveryservice/api/web/DeliveryState.java @@ -0,0 +1,5 @@ +package net.chrisrichardson.ftgo.deliveryservice.api.web; + +public enum DeliveryState { + CANCELLED, SCHEDULED, PENDING +} diff --git a/ftgo-delivery-service-api/src/main/java/net/chrisrichardson/ftgo/deliveryservice/api/web/DeliveryStatus.java b/ftgo-delivery-service-api/src/main/java/net/chrisrichardson/ftgo/deliveryservice/api/web/DeliveryStatus.java new file mode 100644 index 00000000..b07065c9 --- /dev/null +++ b/ftgo-delivery-service-api/src/main/java/net/chrisrichardson/ftgo/deliveryservice/api/web/DeliveryStatus.java @@ -0,0 +1,42 @@ +package net.chrisrichardson.ftgo.deliveryservice.api.web; + +import java.util.List; + +public class DeliveryStatus { + private DeliveryInfo deliveryInfo; + private Long assignedCourier; + private List courierActions; + + public DeliveryStatus() { + } + + public DeliveryInfo getDeliveryInfo() { + return deliveryInfo; + } + + public void setDeliveryInfo(DeliveryInfo deliveryInfo) { + this.deliveryInfo = deliveryInfo; + } + + public Long getAssignedCourier() { + return assignedCourier; + } + + public void setAssignedCourier(Long assignedCourier) { + this.assignedCourier = assignedCourier; + } + + public List getCourierActions() { + return courierActions; + } + + public void setCourierActions(List courierActions) { + this.courierActions = courierActions; + } + + public DeliveryStatus(DeliveryInfo deliveryInfo, Long assignedCourier, List courierActions) { + this.deliveryInfo = deliveryInfo; + this.assignedCourier = assignedCourier; + this.courierActions = courierActions; + } +} diff --git a/ftgo-delivery-service/Dockerfile b/ftgo-delivery-service/Dockerfile new file mode 100644 index 00000000..0e6725f5 --- /dev/null +++ b/ftgo-delivery-service/Dockerfile @@ -0,0 +1,3 @@ +ARG baseImageVersion +FROM eventuateio/eventuate-examples-docker-images-spring-example-base-image:$baseImageVersion +COPY build/libs/ftgo-delivery-service.jar service.jar diff --git a/ftgo-delivery-service/build.gradle b/ftgo-delivery-service/build.gradle new file mode 100644 index 00000000..c3c493ca --- /dev/null +++ b/ftgo-delivery-service/build.gradle @@ -0,0 +1,79 @@ + +apply plugin: FtgoServicePlugin + +// apply plugin: 'spring-cloud-contract' + +apply plugin: 'docker-compose' + +apply plugin: IntegrationTestsPlugin +apply plugin: ComponentTestsPlugin +apply plugin: FtgoJSONSchema2PojoPlugin + +dependencies { + ftgoApiSpecification project(":ftgo-restaurant-service-api-spec") + + compile project(":ftgo-delivery-service-api") + compile project(":ftgo-kitchen-service-api") + compile project(":ftgo-restaurant-service-api") + compile project(":ftgo-order-service-api") + compile project(":ftgo-common") + compile project(":ftgo-common-jpa") + compile project(":common-swagger") + + compile "io.eventuate.tram.core:eventuate-tram-spring-events" + compile "org.springframework.boot:spring-boot-starter-data-jpa" + compile "io.eventuate.tram.core:eventuate-tram-spring-jdbc-kafka" + + compile "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion" + compile "io.micrometer:micrometer-registry-prometheus:$micrometerVersion" + + compile('org.apache.kafka:kafka-clients:2.3.0') { + force = true + } + + testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion" + testCompile "io.eventuate.tram.core:eventuate-tram-spring-in-memory" + testCompile "io.eventuate.util:eventuate-util-test" + + componentTestCompile "com.jayway.restassured:rest-assured:$restAssuredVersion" + componentTestCompile "com.jayway.jsonpath:json-path:2.3.0" + componentTestCompile "org.hamcrest:hamcrest:2.1" + componentTestCompile project(":ftgo-test-util") + +} + +dockerCompose { + environment.put "EVENTUATE_COMMON_VERSION", eventuateCommonImageVersion + environment.put "EVENTUATE_CDC_VERSION", eventuateCdcImageVersion + environment.put "EVENTUATE_SAGA_VERSION", eventuateTramSagasImageVersion + environment.put "EVENTUATE_JAVA_BASE_IMAGE_VERSION", eventuateExamplesBaseImageVersion + environment.put "EVENTUATE_MESSAGING_KAFKA_IMAGE_VERSION", eventuateMessagingKafkaImageVersion + + projectName = null + + componentTests { + projectName = null + removeOrphans = true + retainContainersOnStartupFailure = true + + startedServices = [ 'ftgo-delivery-service'] + stopContainers = true + } + +} + +componentTestsComposeUp.dependsOn(assemble) +dockerCompose.componentTests.isRequiredBy(componentTest) + +ftgoJsonSchema2Pojo { + + ftgoRestaurantService { + source = files("${ftgoApiSpecsDir}/ftgo-restaurant-service-api-spec/messages") + targetPackage = "net.chrisrichardson.ftgo.restaurantservice.events" + includeAdditionalProperties = false + generateBuilders = true + useLongIntegers = true + } + +} + diff --git a/ftgo-delivery-service/src/component-test/java/net/chrisrichardson/ftgo/deliveryservice/DeliveryServiceInProcessComponentTest.java b/ftgo-delivery-service/src/component-test/java/net/chrisrichardson/ftgo/deliveryservice/DeliveryServiceInProcessComponentTest.java new file mode 100644 index 00000000..4dcdc7e9 --- /dev/null +++ b/ftgo-delivery-service/src/component-test/java/net/chrisrichardson/ftgo/deliveryservice/DeliveryServiceInProcessComponentTest.java @@ -0,0 +1,129 @@ +package net.chrisrichardson.ftgo.deliveryservice; + +import io.eventuate.common.spring.jdbc.EventuateTransactionTemplateConfiguration; +import io.eventuate.tram.events.publisher.DomainEventPublisher; +import io.eventuate.tram.spring.events.publisher.TramEventsPublisherConfiguration; +import io.eventuate.tram.spring.inmemory.TramInMemoryConfiguration; +import net.chrisrichardson.ftgo.deliveryservice.domain.DeliveryRepository; +import net.chrisrichardson.ftgo.deliveryservice.domain.DeliveryServiceTestData; +import net.chrisrichardson.ftgo.deliveryservice.domain.RestaurantRepository; +import net.chrisrichardson.ftgo.deliveryservice.messaging.DeliveryServiceMessagingConfiguration; +import net.chrisrichardson.ftgo.deliveryservice.web.DeliveryServiceWebConfiguration; +import net.chrisrichardson.ftgo.orderservice.api.OrderServiceChannels; +import net.chrisrichardson.ftgo.orderservice.api.events.OrderCreatedEvent; +import net.chrisrichardson.ftgo.orderservice.api.events.OrderDetails; +import net.chrisrichardson.ftgo.restaurantservice.RestaurantServiceChannels; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.Collections; + +import static com.jayway.restassured.RestAssured.given; +import static io.eventuate.util.test.async.Eventually.eventually; +import static org.junit.Assert.*; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = DeliveryServiceInProcessComponentTest.Config.class, + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class DeliveryServiceInProcessComponentTest { + + private long restaurantId; + private long orderId; + + @Configuration + @EnableAutoConfiguration + @Import({DeliveryServiceMessagingConfiguration.class, + DeliveryServiceWebConfiguration.class, + TramInMemoryConfiguration.class, + TramEventsPublisherConfiguration.class, + EventuateTransactionTemplateConfiguration.class + }) + public static class Config { + } + + @LocalServerPort + private int port; + + private String host = "localhost"; + + @Autowired + private DomainEventPublisher domainEventPublisher; + + @Autowired + private RestaurantRepository restaurantRepository; + + @Autowired + private DeliveryRepository deliveryRepository; + + @Test + public void shouldScheduleDelivery() { + + createRestaurant(); + + createOrder(); + + assertDeliveryCreated(); + + // createCourier + // acceptTicket + // TicketCancelled + } + + private String baseUrl(int port, String path, String... pathElements) { + assertNotNull("host", host); + + StringBuilder sb = new StringBuilder("http://"); + sb.append(host); + sb.append(":"); + sb.append(port); + sb.append("/"); + sb.append(path); + + for (String pe : pathElements) { + sb.append("/"); + sb.append(pe); + } + String s = sb.toString(); + System.out.println("url=" + s); + return s; + } + + + private void assertDeliveryCreated() { + + String state = given(). + when(). + get(baseUrl(port, "deliveries", Long.toString(orderId))). + then(). + statusCode(200).extract().path("deliveryInfo.state"); + + assertEquals("PENDING", state); + } + + private void createOrder() { + orderId = System.currentTimeMillis(); + domainEventPublisher.publish(OrderServiceChannels.ORDER_EVENT_CHANNEL, orderId, Collections.singletonList( + new OrderCreatedEvent(new OrderDetails(0L, restaurantId, null, null), + DeliveryServiceTestData.DELIVERY_ADDRESS, null))); + eventually(() -> assertTrue(deliveryRepository.findById(orderId).isPresent())); + + + } + + private void createRestaurant() { + restaurantId = System.currentTimeMillis(); + + domainEventPublisher.publish(RestaurantServiceChannels.RESTAURANT_EVENT_CHANNEL, restaurantId, + Collections.singletonList(RestaurantEventMother.makeRestaurantCreated())); + + eventually(() -> assertTrue(restaurantRepository.findById(restaurantId).isPresent())); + } + +} diff --git a/ftgo-delivery-service/src/component-test/java/net/chrisrichardson/ftgo/deliveryservice/DeliveryServiceOutOfProcessComponentTest.java b/ftgo-delivery-service/src/component-test/java/net/chrisrichardson/ftgo/deliveryservice/DeliveryServiceOutOfProcessComponentTest.java new file mode 100644 index 00000000..bfcd6ba8 --- /dev/null +++ b/ftgo-delivery-service/src/component-test/java/net/chrisrichardson/ftgo/deliveryservice/DeliveryServiceOutOfProcessComponentTest.java @@ -0,0 +1,123 @@ +package net.chrisrichardson.ftgo.deliveryservice; + +import io.eventuate.tram.events.publisher.DomainEventPublisher; +import io.eventuate.tram.spring.events.publisher.TramEventsPublisherConfiguration; +import io.eventuate.tram.spring.jdbckafka.TramJdbcKafkaConfiguration; +import net.chrisrichardson.ftgo.deliveryservice.domain.DeliveryServiceTestData; +import net.chrisrichardson.ftgo.orderservice.api.OrderServiceChannels; +import net.chrisrichardson.ftgo.orderservice.api.events.OrderCreatedEvent; +import net.chrisrichardson.ftgo.orderservice.api.events.OrderDetails; +import net.chrisrichardson.ftgo.restaurantservice.RestaurantServiceChannels; +import net.chrisrichardson.ftgo.testutil.FtgoTestUtil; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.Collections; +import java.util.concurrent.TimeUnit; + +import static com.jayway.restassured.RestAssured.given; +import static io.eventuate.util.test.async.Eventually.eventually; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = DeliveryServiceOutOfProcessComponentTest.Config.class, webEnvironment = SpringBootTest.WebEnvironment.NONE) +public class DeliveryServiceOutOfProcessComponentTest { + + @Configuration + @EnableJpaRepositories + @EnableAutoConfiguration + @Import({TramJdbcKafkaConfiguration.class, TramEventsPublisherConfiguration.class + }) + public static class Config { + } + + private String host = FtgoTestUtil.getDockerHostIp(); + private int port = 8089; + private long restaurantId; + private long orderId; + + @Autowired + private DomainEventPublisher domainEventPublisher; + + // Duplication + + private String baseUrl(int port, String path, String... pathElements) { + assertNotNull("host", host); + + StringBuilder sb = new StringBuilder("http://"); + sb.append(host); + sb.append(":"); + sb.append(port); + sb.append("/"); + sb.append(path); + + for (String pe : pathElements) { + sb.append("/"); + sb.append(pe); + } + String s = sb.toString(); + System.out.println("url=" + s); + return s; + } + + @Test + public void shouldScheduleDelivery() { + + createRestaurant(); + + createOrder(); + + assertDeliveryCreated(); + + // createCourier + // acceptTicket + // TicketCancelled + } + + private void assertDeliveryCreated() { + + eventually(() -> { + String state = given(). + when(). + get(baseUrl(port, "deliveries", Long.toString(orderId))). + then(). + statusCode(200).extract().path("deliveryInfo.state"); + + assertEquals("PENDING", state); + }); + } + + private void createOrder() { + orderId = System.currentTimeMillis(); + domainEventPublisher.publish(OrderServiceChannels.ORDER_EVENT_CHANNEL, orderId, Collections.singletonList( + new OrderCreatedEvent(new OrderDetails(0L, restaurantId, null, null), + DeliveryServiceTestData.DELIVERY_ADDRESS, null))); + + + } + + private void createRestaurant() { + restaurantId = System.currentTimeMillis(); + + domainEventPublisher.publish(RestaurantServiceChannels.RESTAURANT_EVENT_CHANNEL, restaurantId, Collections.singletonList(RestaurantEventMother.makeRestaurantCreated())); + + sleep(); + } + + private void sleep() { + try { + TimeUnit.SECONDS.sleep(5); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/ftgo-delivery-service/src/component-test/java/net/chrisrichardson/ftgo/deliveryservice/RestaurantEventMother.java b/ftgo-delivery-service/src/component-test/java/net/chrisrichardson/ftgo/deliveryservice/RestaurantEventMother.java new file mode 100644 index 00000000..b2176072 --- /dev/null +++ b/ftgo-delivery-service/src/component-test/java/net/chrisrichardson/ftgo/deliveryservice/RestaurantEventMother.java @@ -0,0 +1,36 @@ +package net.chrisrichardson.ftgo.deliveryservice; + +import net.chrisrichardson.ftgo.deliveryservice.domain.DeliveryServiceTestData; +import net.chrisrichardson.ftgo.deliveryservice.messaging.RestaurantEventMapper; +import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantCreated; +import net.chrisrichardson.ftgo.restaurantservice.events.Menu; +import net.chrisrichardson.ftgo.restaurantservice.events.MenuItem; + +import java.util.Collections; +import java.util.List; + +import net.chrisrichardson.ftgo.common.Address; +import net.chrisrichardson.ftgo.common.Money; + +public class RestaurantEventMother { + + public static final String CHICKEN_VINDALOO = "Chicken Vindaloo"; + public static final String CHICKEN_VINDALOO_MENU_ITEM_ID = "1"; + public static final String CHICKEN_VINDALOO_PRICE = "12.34"; + public static final Address RESTAURANT_ADDRESS = new Address("1 Main Street", "Unit 99", "Oakland", "CA", "94611"); + + public static MenuItem CHICKEN_VINDALOO_MENU_ITEM = new MenuItem() + .withId(CHICKEN_VINDALOO_MENU_ITEM_ID) + .withName(CHICKEN_VINDALOO) + .withPrice(CHICKEN_VINDALOO_PRICE); + + public static final List AJANTA_RESTAURANT_MENU_ITEMS = Collections.singletonList(CHICKEN_VINDALOO_MENU_ITEM); + + + static RestaurantCreated makeRestaurantCreated() { + return new RestaurantCreated() + .withName("Delicious Indian") + .withAddress(RestaurantEventMapper.fromAddress(DeliveryServiceTestData.PICKUP_ADDRESS)) + .withMenu(new Menu().withMenuItems(AJANTA_RESTAURANT_MENU_ITEMS)); + } +} diff --git a/ftgo-delivery-service/src/integration-test/java/net/chrisrichardson/ftgo/deliveryservice/domain/CourierJpaTest.java b/ftgo-delivery-service/src/integration-test/java/net/chrisrichardson/ftgo/deliveryservice/domain/CourierJpaTest.java new file mode 100644 index 00000000..8392e424 --- /dev/null +++ b/ftgo-delivery-service/src/integration-test/java/net/chrisrichardson/ftgo/deliveryservice/domain/CourierJpaTest.java @@ -0,0 +1,86 @@ +package net.chrisrichardson.ftgo.deliveryservice.domain; + +import io.eventuate.tram.spring.consumer.jdbc.TramConsumerJdbcAutoConfiguration; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.transaction.support.TransactionTemplate; + +import java.time.LocalDateTime; +import java.util.List; + +import static org.junit.Assert.*; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = CourierJpaTest.Config.class) +public class CourierJpaTest { + + @Configuration + @EnableJpaRepositories + @EnableAutoConfiguration(exclude = TramConsumerJdbcAutoConfiguration.class) + public static class Config { + } + + @Autowired + private CourierRepository courierRepository; + + + @Autowired + private TransactionTemplate transactionTemplate; + + @Test + public void shouldSaveAndLoad() { + long courierId = System.currentTimeMillis(); + Courier courier = Courier.create(courierId); + long deliveryId = 103L; + courier.addAction(Action.makePickup(deliveryId, DeliveryServiceTestData.PICKUP_ADDRESS, LocalDateTime.now())); + + Courier savedCourier = courierRepository.save(courier); + + transactionTemplate.execute((ts) -> { + Courier loadedCourier = courierRepository.findById(courierId).get(); + assertEquals(1, loadedCourier.getPlan().getActions().size()); + return null; + }); + } + + @Test + public void shouldFindAllAvailable() { + long courierId1 = System.currentTimeMillis() * 10; + long courierId2 = System.currentTimeMillis() * 10 + 1; + Courier courier1 = Courier.create(courierId1); + Courier courier2 = Courier.create(courierId2); + + courier1.noteAvailable(); + courier2.noteUnavailable(); + + courierRepository.save(courier1); + courierRepository.save(courier2); + + List availableCouriers = courierRepository.findAllAvailable(); + + assertTrue(availableCouriers.stream().anyMatch(c -> c.getId() == courierId1)); + assertFalse(availableCouriers.stream().anyMatch(c -> c.getId() == courierId2)); + } + + @Test + public void shouldFindOrCreate() { + long courierId = System.currentTimeMillis(); + transactionTemplate.execute((ts) -> { + Courier courier = courierRepository.findOrCreateCourier(courierId); + assertNotNull(courier); + return null; + }); + transactionTemplate.execute((ts) -> { + Courier courier2 = courierRepository.findOrCreateCourier(courierId); + assertNotNull(courier2); + return null; + }); + } + +} diff --git a/ftgo-delivery-service/src/integration-test/java/net/chrisrichardson/ftgo/deliveryservice/domain/DeliveryJpaTest.java b/ftgo-delivery-service/src/integration-test/java/net/chrisrichardson/ftgo/deliveryservice/domain/DeliveryJpaTest.java new file mode 100644 index 00000000..c6acb2a8 --- /dev/null +++ b/ftgo-delivery-service/src/integration-test/java/net/chrisrichardson/ftgo/deliveryservice/domain/DeliveryJpaTest.java @@ -0,0 +1,40 @@ +package net.chrisrichardson.ftgo.deliveryservice.domain; + +import io.eventuate.tram.spring.consumer.jdbc.TramConsumerJdbcAutoConfiguration; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.junit.Assert.assertNull; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = DeliveryJpaTest.Config.class) +public class DeliveryJpaTest { + + @Configuration + @EnableJpaRepositories + @EnableAutoConfiguration(exclude = TramConsumerJdbcAutoConfiguration.class) + public static class Config { + } + + @Autowired + private DeliveryRepository deliveryRepository; + + @Test + public void shouldSaveAndLoadDelivery() { + long restaurantId = 102L; + long orderId = System.currentTimeMillis(); + Delivery delivery = Delivery.create(orderId, + restaurantId, DeliveryServiceTestData.PICKUP_ADDRESS, DeliveryServiceTestData.PICKUP_ADDRESS ); + Delivery savedDelivery = deliveryRepository.save(delivery); + + Delivery loadedDelivery = deliveryRepository.findById(orderId).get(); + assertNull(loadedDelivery.getAssignedCourier()); + } + +} diff --git a/ftgo-delivery-service/src/integration-test/java/net/chrisrichardson/ftgo/deliveryservice/domain/RestaurantJpaTest.java b/ftgo-delivery-service/src/integration-test/java/net/chrisrichardson/ftgo/deliveryservice/domain/RestaurantJpaTest.java new file mode 100644 index 00000000..5bce256f --- /dev/null +++ b/ftgo-delivery-service/src/integration-test/java/net/chrisrichardson/ftgo/deliveryservice/domain/RestaurantJpaTest.java @@ -0,0 +1,46 @@ +package net.chrisrichardson.ftgo.deliveryservice.domain; + +import io.eventuate.tram.spring.consumer.jdbc.TramConsumerJdbcAutoConfiguration; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.transaction.support.TransactionTemplate; + +import static org.junit.Assert.assertEquals; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = RestaurantJpaTest.Config.class) +public class RestaurantJpaTest { + + @Configuration + @EnableJpaRepositories + @EnableAutoConfiguration(exclude = TramConsumerJdbcAutoConfiguration.class) + public static class Config { + } + + + @Autowired + private RestaurantRepository restaurantRepository; + + @Autowired + private TransactionTemplate transactionTemplate; + + @Test + public void shouldSaveAndLoad() { + long restaurantId = System.currentTimeMillis(); + Restaurant restaurant = Restaurant.create(restaurantId, "Delicious Indian", DeliveryServiceTestData.PICKUP_ADDRESS); + restaurantRepository.save(restaurant); + + transactionTemplate.execute((ts) -> { + Restaurant loadedCourier = restaurantRepository.findById(restaurantId).get(); + assertEquals(DeliveryServiceTestData.PICKUP_ADDRESS, loadedCourier.getAddress()); + return null; + }); + + } +} diff --git a/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/Action.java b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/Action.java new file mode 100644 index 00000000..1338745c --- /dev/null +++ b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/Action.java @@ -0,0 +1,51 @@ +package net.chrisrichardson.ftgo.deliveryservice.domain; + +import net.chrisrichardson.ftgo.deliveryservice.api.web.DeliveryActionType; +import net.chrisrichardson.ftgo.common.Address; + +import javax.persistence.Embeddable; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import java.time.LocalDateTime; + +@Embeddable +public class Action { + + @Enumerated(EnumType.STRING) + private DeliveryActionType type; + private Address address; + private LocalDateTime time; + + protected long deliveryId; + + private Action() { + } + + public Action(DeliveryActionType type, long deliveryId, Address address, LocalDateTime time) { + this.type = type; + this.deliveryId = deliveryId; + this.address = address; + this.time = time; + } + + public boolean actionFor(long deliveryId) { + return this.deliveryId == deliveryId; + } + + public static Action makePickup(long deliveryId, Address pickupAddress, LocalDateTime pickupTime) { + return new Action(DeliveryActionType.PICKUP, deliveryId, pickupAddress, pickupTime); + } + + public static Action makeDropoff(long deliveryId, Address deliveryAddress, LocalDateTime deliveryTime) { + return new Action(DeliveryActionType.DROPOFF, deliveryId, deliveryAddress, deliveryTime); + } + + + public DeliveryActionType getType() { + return type; + } + + public Address getAddress() { + return address; + } +} diff --git a/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/Courier.java b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/Courier.java new file mode 100644 index 00000000..42b4c063 --- /dev/null +++ b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/Courier.java @@ -0,0 +1,62 @@ +package net.chrisrichardson.ftgo.deliveryservice.domain; + +import javax.persistence.*; +import java.util.List; + +@Entity +@Access(AccessType.FIELD) +public class Courier { + + @Id + private long id; + + @Embedded + private Plan plan; + + private Boolean available; + + private Courier() { + } + + public Courier(long courierId) { + this.id = courierId; + this.plan = new Plan(); + } + + public static Courier create(long courierId) { + return new Courier(courierId); + } + + public void noteAvailable() { + this.available = true; + + } + + public void addAction(Action action) { + plan.add(action); + } + + public void cancelDelivery(long deliveryId) { + plan.removeDelivery(deliveryId); + } + + public boolean isAvailable() { + return available; + } + + public Plan getPlan() { + return plan; + } + + public long getId() { + return id; + } + + public void noteUnavailable() { + this.available = false; + } + + public List actionsForDelivery(long deliveryId) { + return plan.actionsForDelivery(deliveryId); + } +} diff --git a/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/CourierRepository.java b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/CourierRepository.java new file mode 100644 index 00000000..f7fa7b75 --- /dev/null +++ b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/CourierRepository.java @@ -0,0 +1,13 @@ +package net.chrisrichardson.ftgo.deliveryservice.domain; + +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.CrudRepository; + +import java.util.List; + +public interface CourierRepository extends CrudRepository, CustomCourierRepository { + + @Query("SELECT c FROM Courier c WHERE c.available = true") + List findAllAvailable(); + +} diff --git a/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/CustomCourierRepository.java b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/CustomCourierRepository.java new file mode 100644 index 00000000..e655d2f8 --- /dev/null +++ b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/CustomCourierRepository.java @@ -0,0 +1,11 @@ +package net.chrisrichardson.ftgo.deliveryservice.domain; + +import org.springframework.data.jpa.repository.Query; + +import java.util.List; + +public interface CustomCourierRepository { + + Courier findOrCreateCourier(long courierId); + +} diff --git a/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/CustomCourierRepositoryImpl.java b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/CustomCourierRepositoryImpl.java new file mode 100644 index 00000000..9a2be39e --- /dev/null +++ b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/CustomCourierRepositoryImpl.java @@ -0,0 +1,27 @@ +package net.chrisrichardson.ftgo.deliveryservice.domain; + +import org.springframework.beans.factory.annotation.Autowired; + +import javax.persistence.EntityManager; +import java.util.List; + +public class CustomCourierRepositoryImpl implements CustomCourierRepository { + + @Autowired + private EntityManager entityManager; + +// @Override +// public List findAllAvailable() { +// return entityManager.createQuery("").getResultList(); +// } + + @Override + public Courier findOrCreateCourier(long courierId) { + Courier courier = entityManager.find(Courier.class, courierId); + if (courier == null) { + courier = Courier.create(courierId); + entityManager.persist(courier); + } + return courier; + } +} diff --git a/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/Delivery.java b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/Delivery.java new file mode 100644 index 00000000..9f8f8741 --- /dev/null +++ b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/Delivery.java @@ -0,0 +1,100 @@ +package net.chrisrichardson.ftgo.deliveryservice.domain; + +import net.chrisrichardson.ftgo.common.Address; +import net.chrisrichardson.ftgo.deliveryservice.api.web.DeliveryState; + +import javax.persistence.*; +import java.time.LocalDateTime; + +@Entity +@Access(AccessType.FIELD) +public class Delivery { + + @Id + private Long id; + + @Embedded + @AttributeOverrides({ + @AttributeOverride(name="street1", column = @Column(name="pickup_street1")), + @AttributeOverride(name="street2", column = @Column(name="pickup_street2")), + @AttributeOverride(name="city", column = @Column(name="pickup_city")), + @AttributeOverride(name="state", column = @Column(name="pickup_state")), + @AttributeOverride(name="zip", column = @Column(name="pickup_zip")), + } + ) + private Address pickupAddress; + + @Enumerated(EnumType.STRING) + private DeliveryState state; + + private long restaurantId; + private LocalDateTime pickUpTime; + + @Embedded + @AttributeOverrides({ + @AttributeOverride(name="street1", column = @Column(name="delivery_street1")), + @AttributeOverride(name="street2", column = @Column(name="delivery_street2")), + @AttributeOverride(name="city", column = @Column(name="delivery_city")), + @AttributeOverride(name="state", column = @Column(name="delivery_state")), + @AttributeOverride(name="zip", column = @Column(name="delivery_zip")), + } + ) + + private Address deliveryAddress; + private LocalDateTime deliveryTime; + + private Long assignedCourier; + private LocalDateTime readyBy; + + private Delivery() { + } + + public Delivery(long orderId, long restaurantId, Address pickupAddress, Address deliveryAddress) { + this.id = orderId; + this.pickupAddress = pickupAddress; + this.state = DeliveryState.PENDING; + this.restaurantId = restaurantId; + this.deliveryAddress = deliveryAddress; + } + + public static Delivery create(long orderId, long restaurantId, Address pickupAddress, Address deliveryAddress) { + return new Delivery(orderId, restaurantId, pickupAddress, deliveryAddress); + } + + public void schedule(LocalDateTime readyBy, long assignedCourier) { + this.readyBy = readyBy; + this.assignedCourier = assignedCourier; + this.state = DeliveryState.SCHEDULED; + + } + + public void cancel() { + this.state = DeliveryState.CANCELLED; + this.assignedCourier = null; + } + + + public long getId() { + return id; + } + + public long getRestaurantId() { + return restaurantId; + } + + public Address getDeliveryAddress() { + return deliveryAddress; + } + + public Address getPickupAddress() { + return pickupAddress; + } + + public DeliveryState getState() { + return state; + } + + public Long getAssignedCourier() { + return assignedCourier; + } +} diff --git a/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/DeliveryRepository.java b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/DeliveryRepository.java new file mode 100644 index 00000000..14af96c8 --- /dev/null +++ b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/DeliveryRepository.java @@ -0,0 +1,6 @@ +package net.chrisrichardson.ftgo.deliveryservice.domain; + +import org.springframework.data.repository.CrudRepository; + +public interface DeliveryRepository extends CrudRepository { +} diff --git a/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/DeliveryService.java b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/DeliveryService.java new file mode 100644 index 00000000..b1299286 --- /dev/null +++ b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/DeliveryService.java @@ -0,0 +1,122 @@ +package net.chrisrichardson.ftgo.deliveryservice.domain; + +import net.chrisrichardson.ftgo.common.Address; +import net.chrisrichardson.ftgo.deliveryservice.api.web.ActionInfo; +import net.chrisrichardson.ftgo.deliveryservice.api.web.DeliveryInfo; +import net.chrisrichardson.ftgo.deliveryservice.api.web.DeliveryStatus; +import org.springframework.dao.DuplicateKeyException; +import org.springframework.transaction.annotation.Transactional; + +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Random; +import java.util.stream.Collectors; + +public class DeliveryService { + + private RestaurantRepository restaurantRepository; + private DeliveryRepository deliveryRepository; + private CourierRepository courierRepository; + private Random random = new Random(); + + public DeliveryService(RestaurantRepository restaurantRepository, DeliveryRepository deliveryRepository, CourierRepository courierRepository) { + this.restaurantRepository = restaurantRepository; + this.deliveryRepository = deliveryRepository; + this.courierRepository = courierRepository; + } + + public void createRestaurant(long restaurantId, String restaurantName, Address address) { + restaurantRepository.save(Restaurant.create(restaurantId, restaurantName, address)); + } + + public void createDelivery(long orderId, long restaurantId, Address deliveryAddress) { + Restaurant restaurant = restaurantRepository.findById(restaurantId).get(); + deliveryRepository.save(Delivery.create(orderId, restaurantId, restaurant.getAddress(), deliveryAddress)); + } + + public void scheduleDelivery(long orderId, LocalDateTime readyBy) { + Delivery delivery = deliveryRepository.findById(orderId).get(); + + // Stupid implementation + + List couriers = courierRepository.findAllAvailable(); + Courier courier = couriers.get(random.nextInt(couriers.size())); + courier.addAction(Action.makePickup(delivery.getId(), delivery.getPickupAddress(), readyBy)); + courier.addAction(Action.makeDropoff(delivery.getId(), delivery.getDeliveryAddress(), readyBy.plusMinutes(30))); + + delivery.schedule(readyBy, courier.getId()); + + } + + public void cancelDelivery(long orderId) { + Delivery delivery = deliveryRepository.findById(orderId).get(); + Long assignedCourierId = delivery.getAssignedCourier(); + delivery.cancel(); + if (assignedCourierId != null) { + Courier courier = courierRepository.findById(assignedCourierId).get(); + courier.cancelDelivery(delivery.getId()); + } + + } + + + + // notePickedUp + // noteDelivered + // noteLocation + + void noteAvailable(long courierId) { + courierRepository.findOrCreateCourier(courierId).noteAvailable(); + } + + void noteUnavailable(long courierId) { + courierRepository.findOrCreateCourier(courierId).noteUnavailable(); + } + + private Courier findOrCreateCourier(long courierId) { + Courier courier = Courier.create(courierId); + try { + return courierRepository.save(courier); + } catch (DuplicateKeyException e) { + return courierRepository.findById(courierId).get(); + } + } + + @Transactional + public void updateAvailability(long courierId, boolean available) { + if (available) + noteAvailable(courierId); + else + noteUnavailable(courierId); + } + + + // getCourierRoute() + + @Transactional + public Optional getDeliveryInfo(long deliveryId) { + return deliveryRepository.findById(deliveryId).map(delivery -> { + Long assignedCourier = delivery.getAssignedCourier(); + List courierActions = Collections.EMPTY_LIST; + if (assignedCourier != null) { + Courier courier = courierRepository.findById(assignedCourier).get(); + courierActions = courier.actionsForDelivery(deliveryId); + } + return makeDeliveryStatus(delivery, assignedCourier, courierActions); + }); + } + + private DeliveryStatus makeDeliveryStatus(Delivery delivery, Long assignedCourier, List courierActions) { + return new DeliveryStatus(makeDeliveryInfo(delivery), assignedCourier, courierActions.stream().map(action -> makeActionInfo(action)).collect(Collectors.toList())); + } + + private DeliveryInfo makeDeliveryInfo(Delivery delivery) { + return new DeliveryInfo(delivery.getId(), delivery.getState()); + } + + private ActionInfo makeActionInfo(Action action) { + return new ActionInfo(action.getType()); + } +} diff --git a/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/DeliveryServiceDomainConfiguration.java b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/DeliveryServiceDomainConfiguration.java new file mode 100644 index 00000000..d02b2a29 --- /dev/null +++ b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/DeliveryServiceDomainConfiguration.java @@ -0,0 +1,19 @@ +package net.chrisrichardson.ftgo.deliveryservice.domain; + +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.transaction.annotation.EnableTransactionManagement; + +@Configuration +@EntityScan +@EnableJpaRepositories +@EnableTransactionManagement +public class DeliveryServiceDomainConfiguration { + + @Bean + public DeliveryService deliveryService(RestaurantRepository restaurantRepository, DeliveryRepository deliveryRepository, CourierRepository courierRepository) { + return new DeliveryService(restaurantRepository, deliveryRepository, courierRepository); + } +} diff --git a/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/Plan.java b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/Plan.java new file mode 100644 index 00000000..4351cbe8 --- /dev/null +++ b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/Plan.java @@ -0,0 +1,28 @@ +package net.chrisrichardson.ftgo.deliveryservice.domain; + +import javax.persistence.ElementCollection; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +public class Plan { + + @ElementCollection + private List actions = new LinkedList<>(); + + public void add(Action action) { + actions.add(action); + } + + public void removeDelivery(long deliveryId) { + actions = actions.stream().filter(action -> !action.actionFor(deliveryId)).collect(Collectors.toList()); + } + + public List getActions() { + return actions; + } + + public List actionsForDelivery(long deliveryId) { + return actions.stream().filter(action -> action.actionFor(deliveryId)).collect(Collectors.toList()); + } +} diff --git a/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/Restaurant.java b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/Restaurant.java new file mode 100644 index 00000000..00c4da79 --- /dev/null +++ b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/Restaurant.java @@ -0,0 +1,34 @@ +package net.chrisrichardson.ftgo.deliveryservice.domain; + +import net.chrisrichardson.ftgo.common.Address; + +import javax.persistence.*; + +@Entity +@Access(AccessType.FIELD) +public class Restaurant { + + @Id + private Long id; + + private String restaurantName; + private Address address; + + private Restaurant() { + } + + public Restaurant(long restaurantId, String restaurantName, Address address) { + this.id = restaurantId; + this.restaurantName = restaurantName; + this.address = address; + } + + public static Restaurant create(long restaurantId, String restaurantName, Address address) { + return new Restaurant(restaurantId, restaurantName, address); + } + + public Address getAddress() { + return address; + } +} + diff --git a/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/RestaurantRepository.java b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/RestaurantRepository.java new file mode 100644 index 00000000..ae551818 --- /dev/null +++ b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/domain/RestaurantRepository.java @@ -0,0 +1,6 @@ +package net.chrisrichardson.ftgo.deliveryservice.domain; + +import org.springframework.data.repository.CrudRepository; + +public interface RestaurantRepository extends CrudRepository { +} diff --git a/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/main/DeliveryServiceMain.java b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/main/DeliveryServiceMain.java new file mode 100644 index 00000000..0a468e7d --- /dev/null +++ b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/main/DeliveryServiceMain.java @@ -0,0 +1,22 @@ +package net.chrisrichardson.ftgo.deliveryservice.main; + +import io.eventuate.tram.spring.jdbckafka.TramJdbcKafkaConfiguration; +import net.chrisrichardson.eventstore.examples.customersandorders.commonswagger.CommonSwaggerConfiguration; +import net.chrisrichardson.ftgo.deliveryservice.messaging.DeliveryServiceMessagingConfiguration; +import net.chrisrichardson.ftgo.deliveryservice.web.DeliveryServiceWebConfiguration; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +@Configuration +@EnableAutoConfiguration +@Import({DeliveryServiceMessagingConfiguration.class, DeliveryServiceWebConfiguration.class, + TramJdbcKafkaConfiguration.class, CommonSwaggerConfiguration.class +}) +public class DeliveryServiceMain { + + public static void main(String[] args) { + SpringApplication.run(DeliveryServiceMain.class, args); + } +} diff --git a/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/messaging/DeliveryMessageHandlers.java b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/messaging/DeliveryMessageHandlers.java new file mode 100644 index 00000000..5e375964 --- /dev/null +++ b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/messaging/DeliveryMessageHandlers.java @@ -0,0 +1,59 @@ +package net.chrisrichardson.ftgo.deliveryservice.messaging; + +import io.eventuate.tram.events.subscriber.DomainEventEnvelope; +import io.eventuate.tram.events.subscriber.DomainEventHandlers; +import io.eventuate.tram.events.subscriber.DomainEventHandlersBuilder; +import net.chrisrichardson.ftgo.common.Address; +import net.chrisrichardson.ftgo.deliveryservice.domain.DeliveryService; +import net.chrisrichardson.ftgo.kitchenservice.api.KitchenServiceChannels; +import net.chrisrichardson.ftgo.kitchenservice.api.events.TicketAcceptedEvent; +import net.chrisrichardson.ftgo.kitchenservice.api.events.TicketCancelled; +import net.chrisrichardson.ftgo.orderservice.api.OrderServiceChannels; +import net.chrisrichardson.ftgo.orderservice.api.events.OrderCreatedEvent; +import net.chrisrichardson.ftgo.restaurantservice.RestaurantServiceChannels; +import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantCreated; + +import java.time.LocalDateTime; + +public class DeliveryMessageHandlers { + + private DeliveryService deliveryService; + + public DeliveryMessageHandlers(DeliveryService deliveryService) { + this.deliveryService = deliveryService; + } + + public DomainEventHandlers domainEventHandlers() { + return DomainEventHandlersBuilder + .forAggregateType(KitchenServiceChannels.TICKET_EVENT_CHANNEL) + .onEvent(TicketAcceptedEvent.class, this::handleTicketAcceptedEvent) + .onEvent(TicketCancelled.class, this::handleTicketCancelledEvent) + .andForAggregateType(OrderServiceChannels.ORDER_EVENT_CHANNEL) + .onEvent(OrderCreatedEvent.class, this::handleOrderCreatedEvent) + .andForAggregateType(RestaurantServiceChannels.RESTAURANT_EVENT_CHANNEL) + .onEvent(RestaurantCreated.class, this::handleRestaurantCreated) + .build(); + } + + public void handleRestaurantCreated(DomainEventEnvelope dee) { + Address address = RestaurantEventMapper.toAddress(dee.getEvent().getAddress()); + deliveryService.createRestaurant(Long.parseLong(dee.getAggregateId()), dee.getEvent().getName(), address); + } + + public void handleOrderCreatedEvent(DomainEventEnvelope dee) { + Address address = dee.getEvent().getDeliveryAddress(); + deliveryService.createDelivery(Long.parseLong(dee.getAggregateId()), + dee.getEvent().getOrderDetails().getRestaurantId(), address); + } + + public void handleTicketAcceptedEvent(DomainEventEnvelope dee) { + LocalDateTime readyBy = dee.getEvent().getReadyBy(); + deliveryService.scheduleDelivery(Long.parseLong(dee.getAggregateId()), readyBy); + } + + public void handleTicketCancelledEvent(DomainEventEnvelope dee) { + deliveryService.cancelDelivery(Long.parseLong(dee.getAggregateId())); + } + + +} diff --git a/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/messaging/DeliveryServiceMessagingConfiguration.java b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/messaging/DeliveryServiceMessagingConfiguration.java new file mode 100644 index 00000000..38547924 --- /dev/null +++ b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/messaging/DeliveryServiceMessagingConfiguration.java @@ -0,0 +1,26 @@ +package net.chrisrichardson.ftgo.deliveryservice.messaging; + +import io.eventuate.tram.spring.events.subscriber.TramEventSubscriberConfiguration; +import io.eventuate.tram.events.subscriber.DomainEventDispatcher; +import io.eventuate.tram.events.subscriber.DomainEventDispatcherFactory; +import net.chrisrichardson.ftgo.common.CommonConfiguration; +import net.chrisrichardson.ftgo.deliveryservice.domain.DeliveryService; +import net.chrisrichardson.ftgo.deliveryservice.domain.DeliveryServiceDomainConfiguration; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +@Configuration +@Import({DeliveryServiceDomainConfiguration.class, TramEventSubscriberConfiguration.class, CommonConfiguration.class}) +public class DeliveryServiceMessagingConfiguration { + + @Bean + public DeliveryMessageHandlers deliveryMessageHandlers(DeliveryService deliveryService) { + return new DeliveryMessageHandlers(deliveryService); + } + + @Bean + public DomainEventDispatcher domainEventDispatcher(DeliveryMessageHandlers deliveryMessageHandlers, DomainEventDispatcherFactory domainEventDispatcherFactory) { + return domainEventDispatcherFactory.make("deliveryService-deliveryMessageHandlers", deliveryMessageHandlers.domainEventHandlers()); + } +} diff --git a/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/messaging/RestaurantEventMapper.java b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/messaging/RestaurantEventMapper.java new file mode 100644 index 00000000..d26c11f5 --- /dev/null +++ b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/messaging/RestaurantEventMapper.java @@ -0,0 +1,15 @@ +package net.chrisrichardson.ftgo.deliveryservice.messaging; + +import net.chrisrichardson.ftgo.common.Address; + +public class RestaurantEventMapper { + + public static Address toAddress(net.chrisrichardson.ftgo.restaurantservice.events.Address address) { + return new Address(address.getStreet1(), address.getStreet2(), address.getCity(), address.getState(), address.getZip()); + } + + public static net.chrisrichardson.ftgo.restaurantservice.events.Address fromAddress(net.chrisrichardson.ftgo.common.Address a) { + return new net.chrisrichardson.ftgo.restaurantservice.events.Address().withStreet1(a.getStreet1()).withStreet2(a.getStreet2()).withCity(a.getCity()).withZip(a.getZip()); + } + +} diff --git a/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/web/DeliveryServiceController.java b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/web/DeliveryServiceController.java new file mode 100644 index 00000000..c9a619ec --- /dev/null +++ b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/web/DeliveryServiceController.java @@ -0,0 +1,30 @@ +package net.chrisrichardson.ftgo.deliveryservice.web; + +import net.chrisrichardson.ftgo.deliveryservice.api.web.CourierAvailability; +import net.chrisrichardson.ftgo.deliveryservice.domain.DeliveryService; +import net.chrisrichardson.ftgo.deliveryservice.api.web.DeliveryStatus; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +public class DeliveryServiceController { + + private DeliveryService deliveryService; + + public DeliveryServiceController(DeliveryService deliveryService) { + this.deliveryService = deliveryService; + } + + @RequestMapping(path="/couriers/{courierId}/availability", method= RequestMethod.POST) + public void updateCourierLocation(@PathVariable long courierId, @RequestBody CourierAvailability availability) { + deliveryService.updateAvailability(courierId, availability.isAvailable()); + } + + @RequestMapping(path="/deliveries/{deliveryId}", method= RequestMethod.GET) + public ResponseEntity getDeliveryStatus(@PathVariable long deliveryId) { + return deliveryService.getDeliveryInfo(deliveryId).map(ds -> new ResponseEntity<>(ds, HttpStatus.OK)).orElseGet(() -> new ResponseEntity<>(HttpStatus.NOT_FOUND)); + } + + +} diff --git a/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/web/DeliveryServiceWebConfiguration.java b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/web/DeliveryServiceWebConfiguration.java new file mode 100644 index 00000000..2ed15dfa --- /dev/null +++ b/ftgo-delivery-service/src/main/java/net/chrisrichardson/ftgo/deliveryservice/web/DeliveryServiceWebConfiguration.java @@ -0,0 +1,13 @@ +package net.chrisrichardson.ftgo.deliveryservice.web; + +import net.chrisrichardson.ftgo.common.CommonConfiguration; +import net.chrisrichardson.ftgo.deliveryservice.domain.DeliveryServiceDomainConfiguration; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +@Configuration +@ComponentScan +@Import({DeliveryServiceDomainConfiguration.class, CommonConfiguration.class}) +public class DeliveryServiceWebConfiguration { +} diff --git a/ftgo-delivery-service/src/main/resources/application.properties b/ftgo-delivery-service/src/main/resources/application.properties new file mode 100644 index 00000000..1ad5dce5 --- /dev/null +++ b/ftgo-delivery-service/src/main/resources/application.properties @@ -0,0 +1,23 @@ +spring.application.name=ftgo-order-service +management.endpoint.health.show-details=always +spring.sleuth.sampler.probability=1.0 + +management.endpoints.web.exposure.include=health,prometheus,beans,endpoints + +logging.level.org.springframework.cloud=INFO + +spring.jpa.generate-ddl=true +logging.level.org.springframework.orm.jpa=INFO +logging.level.org.hibernate.SQL=DEBUG +logging.level.io.eventuate=DEBUG +logging.level.net.chrisrichardson.ftgo=DEBUG +logging.level.io.eventuate.tram=DEBUG + +eventuate.database.schema=none +spring.datasource.url=jdbc:mysql://${DOCKER_HOST_IP:localhost}/ftgo_delivery_service +spring.datasource.username=ftgo_delivery_service_user +spring.datasource.password=ftgo_delivery_service_password +spring.datasource.driver-class-name=com.mysql.jdbc.Driver + +eventuatelocal.kafka.bootstrap.servers=${DOCKER_HOST_IP:localhost}:9092 +eventuatelocal.zookeeper.connection.string=${DOCKER_HOST_IP:localhost}:2181 diff --git a/ftgo-delivery-service/src/test/java/net/chrisrichardson/ftgo/deliveryservice/domain/DeliveryServiceTest.java b/ftgo-delivery-service/src/test/java/net/chrisrichardson/ftgo/deliveryservice/domain/DeliveryServiceTest.java new file mode 100644 index 00000000..742a5583 --- /dev/null +++ b/ftgo-delivery-service/src/test/java/net/chrisrichardson/ftgo/deliveryservice/domain/DeliveryServiceTest.java @@ -0,0 +1,97 @@ +package net.chrisrichardson.ftgo.deliveryservice.domain; + +import net.chrisrichardson.ftgo.deliveryservice.api.web.DeliveryActionType; +import net.chrisrichardson.ftgo.deliveryservice.api.web.DeliveryState; +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; + +import java.time.LocalDateTime; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import static org.mockito.Mockito.verify; + +public class DeliveryServiceTest { + + private static final long COURIER_ID = 101L; + private static final long ORDER_ID = 102L; + private static final long RESTAURANT_ID = 103L; + private static final LocalDateTime READY_BY = LocalDateTime.now(); + + private Courier courier; + + private RestaurantRepository restaurantRepository; + private DeliveryRepository deliveryRepository; + private CourierRepository courierRepository; + private DeliveryService deliveryService; + private Restaurant restaurant; + + @Before + public void setUp() { + this.restaurantRepository = mock(RestaurantRepository.class); + this.deliveryRepository = mock(DeliveryRepository.class); + this.courierRepository = mock(CourierRepository.class); + this.courier = Courier.create(COURIER_ID); + this.restaurant = mock(Restaurant.class); + + this.deliveryService = new DeliveryService(restaurantRepository, deliveryRepository, courierRepository); + + } + + @Test + public void shouldCreateCourier() { + when(courierRepository.findOrCreateCourier(COURIER_ID)).thenReturn(courier); + deliveryService.noteAvailable(COURIER_ID); + assertTrue(courier.isAvailable()); + } + + // should Create Restaurant + + @Test + public void shouldCreateDelivery() { + + when(restaurantRepository.findById(RESTAURANT_ID)).thenReturn(Optional.of(restaurant)); + when(restaurant.getAddress()).thenReturn(DeliveryServiceTestData.PICKUP_ADDRESS); + deliveryService.createDelivery(ORDER_ID, RESTAURANT_ID, DeliveryServiceTestData.DELIVERY_ADDRESS); + + ArgumentCaptor arg = ArgumentCaptor.forClass(Delivery.class); + verify(deliveryRepository).save(arg.capture()); + + Delivery delivery = arg.getValue(); + assertNotNull(delivery); + + assertEquals(ORDER_ID, delivery.getId()); + assertEquals(DeliveryState.PENDING, delivery.getState()); + assertEquals(RESTAURANT_ID, delivery.getRestaurantId()); + assertEquals(DeliveryServiceTestData.PICKUP_ADDRESS, delivery.getPickupAddress()); + assertEquals(DeliveryServiceTestData.DELIVERY_ADDRESS, delivery.getDeliveryAddress()); + + } + + @Test + public void shouldScheduleDelivery() { + Delivery delivery = Delivery.create(ORDER_ID, RESTAURANT_ID, DeliveryServiceTestData.PICKUP_ADDRESS, DeliveryServiceTestData.DELIVERY_ADDRESS); + + when(deliveryRepository.findById(ORDER_ID)).thenReturn(Optional.of(delivery)); + when(courierRepository.findAllAvailable()).thenReturn(Collections.singletonList(courier)); + + deliveryService.scheduleDelivery(ORDER_ID, READY_BY); + + assertEquals(DeliveryState.SCHEDULED, delivery.getState()); + assertSame(courier.getId(), delivery.getAssignedCourier()); + + List actions = courier.getPlan().getActions(); + assertEquals(2, actions.size()); + assertEquals(DeliveryActionType.PICKUP, actions.get(0).getType()); + assertEquals(DeliveryServiceTestData.PICKUP_ADDRESS, actions.get(0).getAddress()); + assertEquals(DeliveryActionType.DROPOFF, actions.get(1).getType()); + assertEquals(DeliveryServiceTestData.DELIVERY_ADDRESS, actions.get(1).getAddress()); + } + +} \ No newline at end of file diff --git a/ftgo-delivery-service/src/test/java/net/chrisrichardson/ftgo/deliveryservice/domain/DeliveryServiceTestData.java b/ftgo-delivery-service/src/test/java/net/chrisrichardson/ftgo/deliveryservice/domain/DeliveryServiceTestData.java new file mode 100644 index 00000000..73e54b9e --- /dev/null +++ b/ftgo-delivery-service/src/test/java/net/chrisrichardson/ftgo/deliveryservice/domain/DeliveryServiceTestData.java @@ -0,0 +1,10 @@ +package net.chrisrichardson.ftgo.deliveryservice.domain; + +import net.chrisrichardson.ftgo.common.Address; + +public class DeliveryServiceTestData { + public static final Address PICKUP_ADDRESS = + new Address("1 Main Street", "Suite 501", "Oakland", "CA", "94612"); + public static final Address DELIVERY_ADDRESS = + new Address("1 Quiet Street", "Apartment 101", "Oakland", "CA", "94612"); +} diff --git a/ftgo-end-to-end-tests/build.gradle b/ftgo-end-to-end-tests/build.gradle index beed10a2..ba7db15d 100644 --- a/ftgo-end-to-end-tests/build.gradle +++ b/ftgo-end-to-end-tests/build.gradle @@ -1,15 +1,68 @@ +apply plugin: FtgoOpenApiCodeGenPlugin + +/* +plugins { + id 'org.hidetake.swagger.generator' version '2.18.1' +} +*/ + +apply plugin: 'docker-compose' + dependencies { + swaggerCodegen 'org.openapitools:openapi-generator-cli:3.3.4' // or OpenAPI Generator +} + +swaggerSources { + consumerService { + inputFile = file("${ftgoApiSpecsDir}/ftgo-consumer-service-swagger.json") + code { + language = 'java' + configFile = file('swagger-codegen-config/consumer-service.json') + components = ['models'] + // Supports additionalProperties: https://github.com/swagger-api/swagger-codegen#to-generate-a-sample-client-library + } + } + restaurantService { + inputFile = file("${ftgoApiSpecsDir}/ftgo-restaurant-service-api-spec/web/ftgo-restaurant-service-swagger.json") + code { + language = 'java' + configFile = file('swagger-codegen-config/restaurant-service.json') + components = ['models'] + } + } +} + +dependencies { + ftgoApiSpecification project(":ftgo-consumer-service-api-spec") + ftgoApiSpecification project(":ftgo-restaurant-service-api-spec") + compile project(":ftgo-accounting-service-api") compile project(":ftgo-consumer-service-api") compile project(":ftgo-kitchen-service-api") compile project(":ftgo-restaurant-service-api") compile project(":ftgo-order-service-api") - compile "io.eventuate.util:eventuate-util-test:$eventuateUtilVersion" + compile project(":ftgo-delivery-service-api") + compile "io.eventuate.util:eventuate-util-test" + + compile 'io.swagger:swagger-annotations:1.5.24' testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion" testCompile "com.jayway.restassured:rest-assured:$restAssuredVersion" testCompile "com.jayway.jsonpath:json-path:2.3.0" + testCompile project(":ftgo-test-util") } + +dockerCompose { + environment.put "EVENTUATE_COMMON_VERSION", eventuateCommonImageVersion + environment.put "EVENTUATE_CDC_VERSION", eventuateCdcImageVersion + environment.put "EVENTUATE_SAGA_VERSION", eventuateTramSagasImageVersion + environment.put "EVENTUATE_JAVA_BASE_IMAGE_VERSION", eventuateExamplesBaseImageVersion + environment.put "EVENTUATE_MESSAGING_KAFKA_IMAGE_VERSION", eventuateMessagingKafkaImageVersion + + projectName = null +} + +test.dependsOn(composeUp) diff --git a/ftgo-end-to-end-tests/src/test/java/net/chrisrichardson/ftgo/endtoendtests/EndToEndTests.java b/ftgo-end-to-end-tests/src/test/java/net/chrisrichardson/ftgo/endtoendtests/EndToEndTests.java index 8f113e03..4e2133d4 100644 --- a/ftgo-end-to-end-tests/src/test/java/net/chrisrichardson/ftgo/endtoendtests/EndToEndTests.java +++ b/ftgo-end-to-end-tests/src/test/java/net/chrisrichardson/ftgo/endtoendtests/EndToEndTests.java @@ -3,23 +3,33 @@ import com.jayway.restassured.RestAssured; import com.jayway.restassured.config.ObjectMapperConfig; import com.jayway.restassured.config.RestAssuredConfig; -import io.eventuate.javaclient.commonimpl.JSonMapper; +import io.eventuate.common.json.mapper.JSonMapper; +import io.eventuate.util.test.async.UrlTesting; +import net.chrisrichardson.ftgo.apis.model.consumerservice.CreateConsumerRequest; +import net.chrisrichardson.ftgo.apis.model.consumerservice.PersonName; +import net.chrisrichardson.ftgo.apis.model.restaurantservice.CreateRestaurantRequest; +import net.chrisrichardson.ftgo.apis.model.restaurantservice.MenuItem; +import net.chrisrichardson.ftgo.apis.model.restaurantservice.RestaurantMenu; +import net.chrisrichardson.ftgo.common.Address; import net.chrisrichardson.ftgo.common.CommonJsonMapperInitializer; import net.chrisrichardson.ftgo.common.Money; -import net.chrisrichardson.ftgo.common.PersonName; -import net.chrisrichardson.ftgo.consumerservice.api.web.CreateConsumerRequest; +import net.chrisrichardson.ftgo.common.RevisedOrderLineItem; +import net.chrisrichardson.ftgo.deliveryservice.api.web.CourierAvailability; +import net.chrisrichardson.ftgo.kitchenservice.api.web.TicketAcceptance; +import net.chrisrichardson.ftgo.orderservice.api.events.OrderState; import net.chrisrichardson.ftgo.orderservice.api.web.CreateOrderRequest; import net.chrisrichardson.ftgo.orderservice.api.web.ReviseOrderRequest; -import net.chrisrichardson.ftgo.restaurantservice.events.CreateRestaurantRequest; -import net.chrisrichardson.ftgo.restaurantservice.events.MenuItem; -import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantMenu; import io.eventuate.util.test.async.Eventually; +import net.chrisrichardson.ftgo.testutil.FtgoTestUtil; import org.junit.BeforeClass; import org.junit.Test; +import java.io.IOException; +import java.time.LocalDateTime; import java.util.Collections; import static com.jayway.restassured.RestAssured.given; +import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.CoreMatchers.equalTo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; @@ -32,13 +42,17 @@ public class EndToEndTests { public static final String RESTAURANT_NAME = "My Restaurant"; private final int revisedQuantityOfChickenVindaloo = 10; - private String host = System.getenv("DOCKER_HOST_IP"); + private String host = FtgoTestUtil.getDockerHostIp(); + private int consumerId; private int restaurantId; private int orderId; private final Money priceOfChickenVindaloo = new Money("12.34"); + private long courierId; private String baseUrl(int port, String path, String... pathElements) { + assertNotNull("host", host); + StringBuilder sb = new StringBuilder("http://"); sb.append(host); sb.append(":"); @@ -61,6 +75,7 @@ private String baseUrl(int port, String path, String... pathElements) { private int restaurantsPort = 8084; private int kitchenPort = 8083; private int apiGatewayPort = 8087; + private int deliveryServicePort = 8089; private String consumerBaseUrl(String... pathElements) { @@ -76,13 +91,21 @@ private String restaurantBaseUrl(String... pathElements) { } private String kitchenRestaurantBaseUrl(String... pathElements) { - return baseUrl(kitchenPort, "restaurants", pathElements); + return kitchenServiceBaseUrl("restaurants", pathElements); + } + + private String kitchenServiceBaseUrl(String first, String... pathElements) { + return baseUrl(kitchenPort, first, pathElements); } private String orderBaseUrl(String... pathElements) { return baseUrl(apiGatewayPort, "orders", pathElements); } + private String deliveryServiceBaseUrl(String first, String... pathElements) { + return baseUrl(deliveryServicePort, first, pathElements); + } + private String orderRestaurantBaseUrl(String... pathElements) { return baseUrl(orderPort, "restaurants", pathElements); } @@ -102,7 +125,7 @@ public static void initialize() { } @Test - public void shouldCreateOrder() { + public void shouldCreateReviseAndCancelOrder() { createOrder(); @@ -112,6 +135,31 @@ public void shouldCreateOrder() { } + @Test + public void shouldDeliverOrder() { + + createOrder(); + + noteCourierAvailable(); + + acceptTicket(); + + assertOrderAssignedToCourier(); + + } + + @Test + public void testSwaggerUiUrls() throws IOException { + testSwaggerUiUrl(8081); + testSwaggerUiUrl(8082); + testSwaggerUiUrl(8084); + testSwaggerUiUrl(8086); + } + + private void testSwaggerUiUrl(int port) throws IOException { + UrlTesting.assertUrlStatusIsOk("localhost", port, "/swagger-ui/index.html"); + } + private void reviseOrder() { reviseOrder(orderId); verifyOrderRevised(orderId); @@ -142,7 +190,7 @@ private void verifyOrderRevised(int orderId) { private void reviseOrder(int orderId) { given(). - body(new ReviseOrderRequest(Collections.singletonMap(CHICKED_VINDALOO_MENU_ITEM_ID, revisedQuantityOfChickenVindaloo))) + body(new ReviseOrderRequest(Collections.singletonList(new RevisedOrderLineItem(revisedQuantityOfChickenVindaloo, CHICKED_VINDALOO_MENU_ITEM_ID)))) .contentType("application/json"). when(). post(orderBaseUrl(Integer.toString(orderId), "revise")). @@ -166,13 +214,16 @@ private void createOrder() { verifyOrderAuthorized(orderId); - verifyOrderHistoryUpdated(orderId, consumerId); + verifyOrderHistoryUpdated(orderId, consumerId, OrderState.APPROVED.name()); } private void cancelOrder() { cancelOrder(orderId); verifyOrderCancelled(orderId); + + verifyOrderHistoryUpdated(orderId, consumerId, OrderState.CANCELLED.name()); + } private void verifyOrderCancelled(int orderId) { @@ -203,7 +254,7 @@ private void cancelOrder(int orderId) { private Integer createConsumer() { Integer consumerId = given(). - body(new CreateConsumerRequest(new PersonName("John", "Doe"))). + body(new CreateConsumerRequest().name(new PersonName().firstName("John").lastName("Doe"))). contentType("application/json"). when(). post(consumerBaseUrl()). @@ -227,10 +278,17 @@ private void verifyAccountCreatedForConsumer(int consumerId) { } private int createRestaurant() { + CreateRestaurantRequest request = new CreateRestaurantRequest().name(RESTAURANT_NAME) + .address(new net.chrisrichardson.ftgo.apis.model.restaurantservice.Address() + .street1("1 Main Street").street2("Unit 99").city("Oakland").state("CA").zip("94611")) + .menu( + new RestaurantMenu().addMenuItemsItem( + new MenuItem().id(CHICKED_VINDALOO_MENU_ITEM_ID) + .name("Chicken Vindaloo") + .price(priceOfChickenVindaloo.asString()))); Integer restaurantId = given(). - body(new CreateRestaurantRequest(RESTAURANT_NAME, - new RestaurantMenu(Collections.singletonList(new MenuItem(CHICKED_VINDALOO_MENU_ITEM_ID, "Chicken Vindaloo", priceOfChickenVindaloo))))). + body(request). contentType("application/json"). when(). post(restaurantBaseUrl()). @@ -264,7 +322,10 @@ private void verifyRestaurantCreatedInOrderService(int restaurantId) { private int createOrder(int consumerId, int restaurantId) { Integer orderId = given(). - body(new CreateOrderRequest(consumerId, restaurantId, Collections.singletonList(new CreateOrderRequest.LineItem(CHICKED_VINDALOO_MENU_ITEM_ID, 5)))). + body(new CreateOrderRequest(consumerId, restaurantId, + new Address("9 Amazing View", null, "Oakland", "CA", "94612"), + LocalDateTime.now(), + Collections.singletonList(new CreateOrderRequest.LineItem(CHICKED_VINDALOO_MENU_ITEM_ID, 5)))). contentType("application/json"). when(). post(orderBaseUrl()). @@ -291,7 +352,7 @@ private void verifyOrderAuthorized(int orderId) { } - private void verifyOrderHistoryUpdated(int orderId, int consumerId) { + private void verifyOrderHistoryUpdated(int orderId, int consumerId, String expectedState) { Eventually.eventually(String.format("verifyOrderHistoryUpdated %s", orderId), () -> { String state = given(). when(). @@ -301,8 +362,44 @@ private void verifyOrderHistoryUpdated(int orderId, int consumerId) { .body("orders[0].restaurantName", equalTo(RESTAURANT_NAME)) .extract(). path("orders[0].status"); // TODO state? - assertNotNull(state); + assertEquals(expectedState, state); }); } + private void noteCourierAvailable() { + courierId = System.currentTimeMillis(); + given(). + body(new CourierAvailability(true)). + contentType("application/json"). + when(). + post(deliveryServiceBaseUrl("couriers", Long.toString(courierId), "availability")). + then(). + statusCode(200); + } + + private void acceptTicket() { + courierId = System.currentTimeMillis(); + given(). + body(new TicketAcceptance(LocalDateTime.now().plusHours(9))). + contentType("application/json"). + when(). + post(kitchenServiceBaseUrl("tickets", Long.toString(orderId), "accept")). + then(). + statusCode(200); + } + + private void assertOrderAssignedToCourier() { + Eventually.eventually(() -> { + long assignedCourier = given(). + when(). + get(deliveryServiceBaseUrl("deliveries", Long.toString(orderId))). + then(). + statusCode(200) + .extract() + .path("assignedCourier"); + assertThat(assignedCourier).isGreaterThan(0); + }); + + } + } diff --git a/ftgo-end-to-end-tests/swagger-codegen-config/consumer-service.json b/ftgo-end-to-end-tests/swagger-codegen-config/consumer-service.json new file mode 100644 index 00000000..9642250e --- /dev/null +++ b/ftgo-end-to-end-tests/swagger-codegen-config/consumer-service.json @@ -0,0 +1,4 @@ +{ + "modelPackage": "net.chrisrichardson.ftgo.apis.model.consumerservice", + "library" : "jersey2" +} diff --git a/ftgo-end-to-end-tests/swagger-codegen-config/restaurant-service.json b/ftgo-end-to-end-tests/swagger-codegen-config/restaurant-service.json new file mode 100644 index 00000000..634deced --- /dev/null +++ b/ftgo-end-to-end-tests/swagger-codegen-config/restaurant-service.json @@ -0,0 +1,4 @@ +{ + "modelPackage": "net.chrisrichardson.ftgo.apis.model.restaurantservice", + "library" : "jersey2" +} diff --git a/ftgo-kitchen-service-api/build.gradle b/ftgo-kitchen-service-api/build.gradle index 93b7c8bb..85308768 100644 --- a/ftgo-kitchen-service-api/build.gradle +++ b/ftgo-kitchen-service-api/build.gradle @@ -1,6 +1,7 @@ dependencies { - - compile "io.eventuate.tram.sagas:eventuate-jpa-sagas-framework:$eventuateTramSagasVersion" + compile "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion" + compile "io.eventuate.tram.sagas:eventuate-tram-sagas-spring-participant" + compile "io.eventuate.tram.core:eventuate-tram-spring-events" compile project(":ftgo-common") -} \ No newline at end of file +} diff --git a/ftgo-kitchen-service-api/src/main/java/net/chrisrichardson/ftgo/kitchenservice/api/BeginReviseTicketCommand.java b/ftgo-kitchen-service-api/src/main/java/net/chrisrichardson/ftgo/kitchenservice/api/BeginReviseTicketCommand.java index 54ef9e0e..bf9df3f9 100644 --- a/ftgo-kitchen-service-api/src/main/java/net/chrisrichardson/ftgo/kitchenservice/api/BeginReviseTicketCommand.java +++ b/ftgo-kitchen-service-api/src/main/java/net/chrisrichardson/ftgo/kitchenservice/api/BeginReviseTicketCommand.java @@ -1,21 +1,23 @@ package net.chrisrichardson.ftgo.kitchenservice.api; import io.eventuate.tram.commands.common.Command; +import net.chrisrichardson.ftgo.common.RevisedOrderLineItem; +import java.util.List; import java.util.Map; public class BeginReviseTicketCommand implements Command { private long restaurantId; private Long orderId; - private Map revisedLineItemQuantities; + private List revisedOrderLineItems; private BeginReviseTicketCommand() { } - public BeginReviseTicketCommand(long restaurantId, Long orderId, Map revisedLineItemQuantities) { + public BeginReviseTicketCommand(long restaurantId, Long orderId, List revisedOrderLineItems) { this.restaurantId = restaurantId; this.orderId = orderId; - this.revisedLineItemQuantities = revisedLineItemQuantities; + this.revisedOrderLineItems = revisedOrderLineItems; } public long getRestaurantId() { @@ -34,11 +36,11 @@ public void setOrderId(Long orderId) { this.orderId = orderId; } - public Map getRevisedLineItemQuantities() { - return revisedLineItemQuantities; + public List getRevisedOrderLineItems() { + return revisedOrderLineItems; } - public void setRevisedLineItemQuantities(Map revisedLineItemQuantities) { - this.revisedLineItemQuantities = revisedLineItemQuantities; + public void setRevisedOrderLineItems(List revisedOrderLineItems) { + this.revisedOrderLineItems = revisedOrderLineItems; } } diff --git a/ftgo-kitchen-service-api/src/main/java/net/chrisrichardson/ftgo/kitchenservice/api/ConfirmReviseTicketCommand.java b/ftgo-kitchen-service-api/src/main/java/net/chrisrichardson/ftgo/kitchenservice/api/ConfirmReviseTicketCommand.java index 64c63962..ba768880 100644 --- a/ftgo-kitchen-service-api/src/main/java/net/chrisrichardson/ftgo/kitchenservice/api/ConfirmReviseTicketCommand.java +++ b/ftgo-kitchen-service-api/src/main/java/net/chrisrichardson/ftgo/kitchenservice/api/ConfirmReviseTicketCommand.java @@ -1,22 +1,23 @@ package net.chrisrichardson.ftgo.kitchenservice.api; import io.eventuate.tram.commands.common.Command; +import net.chrisrichardson.ftgo.common.RevisedOrderLineItem; -import java.util.Map; +import java.util.List; public class ConfirmReviseTicketCommand implements Command { private long restaurantId; private long orderId; - private Map revisedLineItemQuantities; + private List revisedOrderLineItems; private ConfirmReviseTicketCommand() { } - public ConfirmReviseTicketCommand(long restaurantId, Long orderId, Map revisedLineItemQuantities) { + public ConfirmReviseTicketCommand(long restaurantId, Long orderId, List revisedOrderLineItems) { this.restaurantId = restaurantId; this.orderId = orderId; - this.revisedLineItemQuantities = revisedLineItemQuantities; + this.revisedOrderLineItems = revisedOrderLineItems; } public long getOrderId() { @@ -35,11 +36,11 @@ public void setRestaurantId(long restaurantId) { this.restaurantId = restaurantId; } - public Map getRevisedLineItemQuantities() { - return revisedLineItemQuantities; + public List getRevisedOrderLineItems() { + return revisedOrderLineItems; } - public void setRevisedLineItemQuantities(Map revisedLineItemQuantities) { - this.revisedLineItemQuantities = revisedLineItemQuantities; + public void setRevisedOrderLineItems(List revisedOrderLineItems) { + this.revisedOrderLineItems = revisedOrderLineItems; } } diff --git a/ftgo-kitchen-service-api/src/main/java/net/chrisrichardson/ftgo/kitchenservice/api/KitchenServiceChannels.java b/ftgo-kitchen-service-api/src/main/java/net/chrisrichardson/ftgo/kitchenservice/api/KitchenServiceChannels.java index 85d50396..5c84373b 100644 --- a/ftgo-kitchen-service-api/src/main/java/net/chrisrichardson/ftgo/kitchenservice/api/KitchenServiceChannels.java +++ b/ftgo-kitchen-service-api/src/main/java/net/chrisrichardson/ftgo/kitchenservice/api/KitchenServiceChannels.java @@ -1,5 +1,7 @@ package net.chrisrichardson.ftgo.kitchenservice.api; public class KitchenServiceChannels { - public static final String kitchenServiceChannel = "kitchenService"; + public static final String COMMAND_CHANNEL = "kitchenService"; + public static final String TICKET_EVENT_CHANNEL = "net.chrisrichardson.ftgo.kitchenservice.domain.Ticket"; + } diff --git a/ftgo-kitchen-service-api/src/main/java/net/chrisrichardson/ftgo/kitchenservice/api/events/TicketAcceptedEvent.java b/ftgo-kitchen-service-api/src/main/java/net/chrisrichardson/ftgo/kitchenservice/api/events/TicketAcceptedEvent.java new file mode 100644 index 00000000..c592afb1 --- /dev/null +++ b/ftgo-kitchen-service-api/src/main/java/net/chrisrichardson/ftgo/kitchenservice/api/events/TicketAcceptedEvent.java @@ -0,0 +1,22 @@ +package net.chrisrichardson.ftgo.kitchenservice.api.events; + +import java.time.LocalDateTime; + +public class TicketAcceptedEvent implements TicketDomainEvent { + private LocalDateTime readyBy; + + public TicketAcceptedEvent() { + } + + public TicketAcceptedEvent(LocalDateTime readyBy) { + this.readyBy = readyBy; + } + + public LocalDateTime getReadyBy() { + return readyBy; + } + + public void setReadyBy(LocalDateTime readyBy) { + this.readyBy = readyBy; + } +} diff --git a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketCancelled.java b/ftgo-kitchen-service-api/src/main/java/net/chrisrichardson/ftgo/kitchenservice/api/events/TicketCancelled.java similarity index 51% rename from ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketCancelled.java rename to ftgo-kitchen-service-api/src/main/java/net/chrisrichardson/ftgo/kitchenservice/api/events/TicketCancelled.java index 99719b1a..89a9d47f 100644 --- a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketCancelled.java +++ b/ftgo-kitchen-service-api/src/main/java/net/chrisrichardson/ftgo/kitchenservice/api/events/TicketCancelled.java @@ -1,4 +1,4 @@ -package net.chrisrichardson.ftgo.kitchenservice.domain; +package net.chrisrichardson.ftgo.kitchenservice.api.events; public class TicketCancelled implements TicketDomainEvent { } diff --git a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketDomainEvent.java b/ftgo-kitchen-service-api/src/main/java/net/chrisrichardson/ftgo/kitchenservice/api/events/TicketDomainEvent.java similarity index 65% rename from ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketDomainEvent.java rename to ftgo-kitchen-service-api/src/main/java/net/chrisrichardson/ftgo/kitchenservice/api/events/TicketDomainEvent.java index 7df51351..b1424c58 100644 --- a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketDomainEvent.java +++ b/ftgo-kitchen-service-api/src/main/java/net/chrisrichardson/ftgo/kitchenservice/api/events/TicketDomainEvent.java @@ -1,4 +1,4 @@ -package net.chrisrichardson.ftgo.kitchenservice.domain; +package net.chrisrichardson.ftgo.kitchenservice.api.events; import io.eventuate.tram.events.common.DomainEvent; diff --git a/ftgo-kitchen-service-api/src/main/java/net/chrisrichardson/ftgo/kitchenservice/api/web/TicketAcceptance.java b/ftgo-kitchen-service-api/src/main/java/net/chrisrichardson/ftgo/kitchenservice/api/web/TicketAcceptance.java new file mode 100644 index 00000000..96bc505e --- /dev/null +++ b/ftgo-kitchen-service-api/src/main/java/net/chrisrichardson/ftgo/kitchenservice/api/web/TicketAcceptance.java @@ -0,0 +1,22 @@ +package net.chrisrichardson.ftgo.kitchenservice.api.web; + +import java.time.LocalDateTime; + +public class TicketAcceptance { + private LocalDateTime readyBy; + + public TicketAcceptance() { + } + + public TicketAcceptance(LocalDateTime readyBy) { + this.readyBy = readyBy; + } + + public LocalDateTime getReadyBy() { + return readyBy; + } + + public void setReadyBy(LocalDateTime readyBy) { + this.readyBy = readyBy; + } +} diff --git a/ftgo-kitchen-service-contracts/build.gradle b/ftgo-kitchen-service-contracts/build.gradle index c9597a0c..427a7c8b 100644 --- a/ftgo-kitchen-service-contracts/build.gradle +++ b/ftgo-kitchen-service-contracts/build.gradle @@ -1,15 +1,3 @@ -buildscript { - dependencies { - // if using Stub Runner (consumer side) only remove this dependency - classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:$springCloudContractDependenciesVersion" - } - repositories { - mavenCentral() - maven { - url 'https://repo.spring.io/libs-milestone' - } - } -} apply plugin: 'spring-cloud-contract' apply plugin: 'maven-publish' @@ -30,3 +18,7 @@ contracts { generateContractTests.enabled = false build.finalizedBy(publish) + +dependencies { + compile 'org.codehaus.groovy:groovy-all:2.4.6' +} diff --git a/ftgo-kitchen-service-contracts/src/main/resources/contracts/deliveryservice/messaging/TicketAcceptedEvent.groovy b/ftgo-kitchen-service-contracts/src/main/resources/contracts/deliveryservice/messaging/TicketAcceptedEvent.groovy new file mode 100644 index 00000000..69432dea --- /dev/null +++ b/ftgo-kitchen-service-contracts/src/main/resources/contracts/deliveryservice/messaging/TicketAcceptedEvent.groovy @@ -0,0 +1,20 @@ +package contracts.messaging; + +org.springframework.cloud.contract.spec.Contract.make { + label 'ticketAcceptedEvent' + input { + triggeredBy('ticketAcceptedEvent()') + } + + outputMessage { + sentTo('net.chrisrichardson.ftgo.kitchenservice.domain.Ticket') + body( + readyBy: $(consumer('2019-08-20T14:20:00.979'), producer(regex('20[0-9]{2}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}.[0-9]+'))) + ) + headers { + header('event-aggregate-type', 'net.chrisrichardson.ftgo.kitchenservice.domain.Ticket') + header('event-type', 'net.chrisrichardson.ftgo.kitchenservice.api.events.TicketAcceptedEvent') + header('event-aggregate-id', '99') + } + } +} \ No newline at end of file diff --git a/ftgo-kitchen-service-contracts/src/main/resources/contracts/ConfirmCreateTicket.groovy b/ftgo-kitchen-service-contracts/src/main/resources/contracts/messaging/ConfirmCreateTicket.groovy similarity index 96% rename from ftgo-kitchen-service-contracts/src/main/resources/contracts/ConfirmCreateTicket.groovy rename to ftgo-kitchen-service-contracts/src/main/resources/contracts/messaging/ConfirmCreateTicket.groovy index f9c8deea..43ad0e85 100644 --- a/ftgo-kitchen-service-contracts/src/main/resources/contracts/ConfirmCreateTicket.groovy +++ b/ftgo-kitchen-service-contracts/src/main/resources/contracts/messaging/ConfirmCreateTicket.groovy @@ -1,4 +1,4 @@ -package contracts; +package contracts.messaging; org.springframework.cloud.contract.spec.Contract.make { label 'confirmCreateTicket' diff --git a/ftgo-kitchen-service-contracts/src/main/resources/contracts/CreateTicket.groovy b/ftgo-kitchen-service-contracts/src/main/resources/contracts/messaging/CreateTicket.groovy similarity index 97% rename from ftgo-kitchen-service-contracts/src/main/resources/contracts/CreateTicket.groovy rename to ftgo-kitchen-service-contracts/src/main/resources/contracts/messaging/CreateTicket.groovy index 51dbd669..b5126a51 100644 --- a/ftgo-kitchen-service-contracts/src/main/resources/contracts/CreateTicket.groovy +++ b/ftgo-kitchen-service-contracts/src/main/resources/contracts/messaging/CreateTicket.groovy @@ -1,4 +1,4 @@ -package contracts; +package contracts.messaging; org.springframework.cloud.contract.spec.Contract.make { label 'createTicket' diff --git a/ftgo-kitchen-service/Dockerfile b/ftgo-kitchen-service/Dockerfile index 81420dfa..cdc5893d 100644 --- a/ftgo-kitchen-service/Dockerfile +++ b/ftgo-kitchen-service/Dockerfile @@ -1,5 +1,3 @@ -FROM openjdk:8u171-jre-alpine -RUN apk --no-cache add curl -CMD java ${JAVA_OPTS} -jar ftgo-kitchen-service.jar -HEALTHCHECK --start-period=30s --interval=5s CMD curl -f http://localhost:8080/actuator/health || exit 1 -COPY build/libs/ftgo-kitchen-service.jar . +ARG baseImageVersion +FROM eventuateio/eventuate-examples-docker-images-spring-example-base-image:$baseImageVersion +COPY build/libs/ftgo-kitchen-service.jar service.jar diff --git a/ftgo-kitchen-service/build.gradle b/ftgo-kitchen-service/build.gradle index 591ff9e2..d044168f 100644 --- a/ftgo-kitchen-service/build.gradle +++ b/ftgo-kitchen-service/build.gradle @@ -11,6 +11,8 @@ buildscript { apply plugin: "io.spring.dependency-management" apply plugin: 'spring-cloud-contract' +apply plugin: IntegrationTestsPlugin +apply plugin: FtgoJSONSchema2PojoPlugin dependencyManagement { imports { @@ -18,42 +20,71 @@ dependencyManagement { } } -dependencies { - testCompile 'org.springframework.cloud:spring-cloud-starter-contract-verifier' -} - contracts { contractsDslDir = new File("../ftgo-kitchen-service-contracts/src/main/resources/contracts") - baseClassForTests = 'net.chrisrichardson.ftgo.kitchenservice.contract.AbstractKitchenServiceConsumerContractTest' + packageWithBaseClasses = 'net.chrisrichardson.ftgo.kitchenservice.contract' + generatedTestSourcesDir = project.file("${project.buildDir}/generated-integration-test-sources/contracts") + sourceSet = "integrationTest" } +sourceSets { + integrationTest { + java { + srcDir project.file("${project.buildDir}/generated-integration-test-sources/contracts") + } + } +} + +compileTestGroovy.enabled=false + apply plugin: FtgoServicePlugin dependencies { + ftgoApiSpecification project(":ftgo-restaurant-service-api-spec") - compile "io.eventuate.tram.core:eventuate-tram-jdbc-kafka:$eventuateTramVersion" - compile "io.eventuate.tram.core:eventuate-tram-events:$eventuateTramVersion" - compile "io.eventuate.tram.sagas:eventuate-tram-sagas-simple-dsl:$eventuateTramSagasVersion" + compile "io.eventuate.tram.core:eventuate-tram-spring-jdbc-kafka" + compile "io.eventuate.tram.core:eventuate-tram-spring-events" + compile "io.eventuate.tram.core:eventuate-tram-spring-messaging" + compile "io.eventuate.tram.sagas:eventuate-tram-sagas-spring-orchestration-simple-dsl" compile project(":common-swagger") compile project(":ftgo-common-jpa") compile project(":ftgo-kitchen-service-api") compile project(":ftgo-restaurant-service-api") - compile "io.eventuate.tram.core:eventuate-tram-aggregate-domain-events:$eventuateTramVersion" + compile "io.eventuate.tram.core:eventuate-tram-aggregate-domain-events" compile "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion" compile "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion" compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion" compile 'javax.el:javax.el-api:2.2.5' - testCompile "io.eventuate.util:eventuate-util-test:$eventuateUtilVersion" - testCompile "io.eventuate.tram.core:eventuate-tram-test-util:$eventuateTramVersion" + compile('org.apache.kafka:kafka-clients:2.3.0') { + force = true + } - testCompile "io.eventuate.tram.sagas:eventuate-tram-sagas-in-memory:$eventuateTramSagasVersion" + testCompile "io.eventuate.util:eventuate-util-test" + testCompile "io.eventuate.tram.core:eventuate-tram-test-util" + + testCompile "io.eventuate.tram.sagas:eventuate-tram-sagas-spring-in-memory" + testCompile "io.eventuate.tram.core:eventuate-tram-spring-in-memory" testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion" testCompile "com.jayway.restassured:rest-assured:$restAssuredVersion" testCompile "com.jayway.jsonpath:json-path:2.3.0" - testCompile "io.eventuate.tram.core:eventuate-tram-testing-support-spring-cloud-contract:$eventuateTramVersion" + testCompile "io.eventuate.tram.core:eventuate-tram-spring-testing-support-cloud-contract" + + testCompile 'org.springframework.cloud:spring-cloud-starter-contract-verifier' +} + +ftgoJsonSchema2Pojo { + + ftgoRestaurantService { + source = files("${ftgoApiSpecsDir}/ftgo-restaurant-service-api-spec/messages") + targetPackage = "net.chrisrichardson.ftgo.restaurantservice.events" + includeAdditionalProperties = false + generateBuilders = true + useLongIntegers = true + } + } diff --git a/ftgo-kitchen-service/src/integration-test/java/net/chrisrichardson/ftgo/kitchenservice/contract/DeliveryserviceMessagingBase.java b/ftgo-kitchen-service/src/integration-test/java/net/chrisrichardson/ftgo/kitchenservice/contract/DeliveryserviceMessagingBase.java new file mode 100644 index 00000000..03e750c1 --- /dev/null +++ b/ftgo-kitchen-service/src/integration-test/java/net/chrisrichardson/ftgo/kitchenservice/contract/DeliveryserviceMessagingBase.java @@ -0,0 +1,57 @@ +package net.chrisrichardson.ftgo.kitchenservice.contract; + +import io.eventuate.common.spring.jdbc.EventuateTransactionTemplateConfiguration; +import io.eventuate.tram.events.publisher.DomainEventPublisher; +import io.eventuate.tram.spring.events.publisher.TramEventsPublisherConfiguration; +import io.eventuate.tram.spring.inmemory.TramInMemoryConfiguration; +import io.eventuate.tram.spring.cloudcontractsupport.EventuateContractVerifierConfiguration; +import net.chrisrichardson.ftgo.common.CommonJsonMapperInitializer; +import net.chrisrichardson.ftgo.kitchenservice.api.TicketDetails; +import net.chrisrichardson.ftgo.kitchenservice.api.events.TicketAcceptedEvent; +import net.chrisrichardson.ftgo.kitchenservice.domain.Ticket; +import net.chrisrichardson.ftgo.kitchenservice.domain.TicketDomainEventPublisher; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.contract.verifier.messaging.boot.AutoConfigureMessageVerifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.junit4.SpringRunner; + +import java.time.LocalDateTime; +import java.util.Collections; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = DeliveryserviceMessagingBase.TestConfiguration.class, webEnvironment = SpringBootTest.WebEnvironment.NONE) +@AutoConfigureMessageVerifier +public abstract class DeliveryserviceMessagingBase { + + static { + CommonJsonMapperInitializer.registerMoneyModule(); + } + + @Configuration + @EnableAutoConfiguration + @Import({EventuateContractVerifierConfiguration.class, TramEventsPublisherConfiguration.class, TramInMemoryConfiguration.class, EventuateTransactionTemplateConfiguration.class}) + public static class TestConfiguration { + + @Bean + public TicketDomainEventPublisher orderAggregateEventPublisher(DomainEventPublisher eventPublisher) { + return new TicketDomainEventPublisher(eventPublisher); + } + } + + + @Autowired + private TicketDomainEventPublisher ticketDomainEventPublisher; + + protected void ticketAcceptedEvent() { + Ticket ticket = new Ticket(101L, 99L, new TicketDetails(Collections.emptyList())); + ticketDomainEventPublisher.publish(ticket, + Collections.singletonList(new TicketAcceptedEvent(LocalDateTime.now()))); + } + +} + diff --git a/ftgo-kitchen-service/src/test/java/net/chrisrichardson/ftgo/kitchenservice/contract/AbstractKitchenServiceConsumerContractTest.java b/ftgo-kitchen-service/src/integration-test/java/net/chrisrichardson/ftgo/kitchenservice/contract/MessagingBase.java similarity index 71% rename from ftgo-kitchen-service/src/test/java/net/chrisrichardson/ftgo/kitchenservice/contract/AbstractKitchenServiceConsumerContractTest.java rename to ftgo-kitchen-service/src/integration-test/java/net/chrisrichardson/ftgo/kitchenservice/contract/MessagingBase.java index 6281231f..2223bed0 100644 --- a/ftgo-kitchen-service/src/test/java/net/chrisrichardson/ftgo/kitchenservice/contract/AbstractKitchenServiceConsumerContractTest.java +++ b/ftgo-kitchen-service/src/integration-test/java/net/chrisrichardson/ftgo/kitchenservice/contract/MessagingBase.java @@ -1,16 +1,16 @@ package net.chrisrichardson.ftgo.kitchenservice.contract; -import io.eventuate.tram.springcloudcontractsupport.EventuateContractVerifierConfiguration; +import io.eventuate.tram.spring.cloudcontractsupport.EventuateContractVerifierConfiguration; import net.chrisrichardson.ftgo.kitchenservice.api.TicketDetails; import net.chrisrichardson.ftgo.kitchenservice.domain.KitchenService; import net.chrisrichardson.ftgo.kitchenservice.domain.Ticket; import net.chrisrichardson.ftgo.kitchenservice.messagehandlers.KitchenServiceMessageHandlersConfiguration; import org.junit.Before; import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.cloud.contract.verifier.messaging.boot.AutoConfigureMessageVerifier; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.test.context.junit4.SpringRunner; @@ -19,27 +19,22 @@ import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.when; @RunWith(SpringRunner.class) -@SpringBootTest(classes = AbstractKitchenServiceConsumerContractTest.TestConfiguration.class, webEnvironment = SpringBootTest.WebEnvironment.NONE) +@SpringBootTest(classes = MessagingBase.TestConfiguration.class, webEnvironment = SpringBootTest.WebEnvironment.NONE) @AutoConfigureMessageVerifier -public abstract class AbstractKitchenServiceConsumerContractTest { +public abstract class MessagingBase { @Configuration + @EnableAutoConfiguration @Import({KitchenServiceMessageHandlersConfiguration.class, EventuateContractVerifierConfiguration.class}) public static class TestConfiguration { - @Bean - public KitchenService kitchenService() { - return mock(KitchenService.class); - } - } - @Autowired + @MockBean private KitchenService kitchenService; @Before diff --git a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/KitchenDomainConfiguration.java b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/KitchenDomainConfiguration.java index 47f991de..9dd51fcd 100644 --- a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/KitchenDomainConfiguration.java +++ b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/KitchenDomainConfiguration.java @@ -1,7 +1,7 @@ package net.chrisrichardson.ftgo.kitchenservice.domain; import io.eventuate.tram.events.publisher.DomainEventPublisher; -import io.eventuate.tram.events.publisher.TramEventsPublisherConfiguration; +import io.eventuate.tram.spring.events.publisher.TramEventsPublisherConfiguration; import net.chrisrichardson.ftgo.common.CommonConfiguration; import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.context.annotation.Bean; diff --git a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/KitchenService.java b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/KitchenService.java index aa1982e9..f28a98b3 100644 --- a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/KitchenService.java +++ b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/KitchenService.java @@ -1,8 +1,9 @@ package net.chrisrichardson.ftgo.kitchenservice.domain; import io.eventuate.tram.events.aggregates.ResultWithDomainEvents; +import net.chrisrichardson.ftgo.common.RevisedOrderLineItem; import net.chrisrichardson.ftgo.kitchenservice.api.TicketDetails; -import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantMenu; +import net.chrisrichardson.ftgo.kitchenservice.api.events.TicketDomainEvent; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; @@ -10,7 +11,6 @@ import java.util.List; import java.util.Map; -@Transactional public class KitchenService { @Autowired @@ -40,6 +40,7 @@ public Ticket createTicket(long restaurantId, Long ticketId, TicketDetails ticke return rwe.result; } + @Transactional public void accept(long ticketId, LocalDateTime readyBy) { Ticket ticket = ticketRepository.findById(ticketId) .orElseThrow(() -> new TicketNotFoundException(ticketId)); @@ -88,11 +89,11 @@ public void undoCancel(long restaurantId, long ticketId) { } - public void beginReviseOrder(long restaurantId, Long ticketId, Map revisedLineItemQuantities) { + public void beginReviseOrder(long restaurantId, Long ticketId, List revisedOrderLineItems) { Ticket ticket = ticketRepository.findById(ticketId) .orElseThrow(() -> new TicketNotFoundException(ticketId)); // TODO - verify restaurant id - List events = ticket.beginReviseOrder(revisedLineItemQuantities); + List events = ticket.beginReviseOrder(revisedOrderLineItems); domainEventPublisher.publish(ticket, events); } @@ -105,11 +106,11 @@ public void undoBeginReviseOrder(long restaurantId, Long ticketId) { domainEventPublisher.publish(ticket, events); } - public void confirmReviseTicket(long restaurantId, long ticketId, Map revisedLineItemQuantities) { + public void confirmReviseTicket(long restaurantId, long ticketId, List revisedOrderLineItems) { Ticket ticket = ticketRepository.findById(ticketId) .orElseThrow(() -> new TicketNotFoundException(ticketId)); // TODO - verify restaurant id - List events = ticket.confirmReviseTicket(revisedLineItemQuantities); + List events = ticket.confirmReviseTicket(revisedOrderLineItems); domainEventPublisher.publish(ticket, events); } diff --git a/ftgo-restaurant-service-api/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/MenuItem.java b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/MenuItem.java similarity index 95% rename from ftgo-restaurant-service-api/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/MenuItem.java rename to ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/MenuItem.java index cf6c3e7d..729b9a36 100644 --- a/ftgo-restaurant-service-api/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/MenuItem.java +++ b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/MenuItem.java @@ -1,4 +1,4 @@ -package net.chrisrichardson.ftgo.restaurantservice.events; +package net.chrisrichardson.ftgo.kitchenservice.domain; import net.chrisrichardson.ftgo.common.Money; import org.apache.commons.lang.builder.EqualsBuilder; diff --git a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/Restaurant.java b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/Restaurant.java index 056d1105..a1786f55 100644 --- a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/Restaurant.java +++ b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/Restaurant.java @@ -2,8 +2,6 @@ import io.eventuate.tram.events.common.DomainEvent; import net.chrisrichardson.ftgo.kitchenservice.api.TicketDetails; -import net.chrisrichardson.ftgo.restaurantservice.events.MenuItem; -import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantMenu; import javax.persistence.Access; import javax.persistence.AccessType; diff --git a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/RestaurantMenu.java b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/RestaurantMenu.java new file mode 100644 index 00000000..28e7718d --- /dev/null +++ b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/RestaurantMenu.java @@ -0,0 +1,15 @@ +package net.chrisrichardson.ftgo.kitchenservice.domain; + +import java.util.List; + +public class RestaurantMenu { + private List menuItems; + + public RestaurantMenu(List menuItems) { + this.menuItems = menuItems; + } + + public List getMenuItems() { + return menuItems; + } +} diff --git a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/Ticket.java b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/Ticket.java index 70304a14..f52eeefa 100644 --- a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/Ticket.java +++ b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/Ticket.java @@ -2,9 +2,13 @@ import io.eventuate.tram.events.aggregates.ResultWithDomainEvents; import net.chrisrichardson.ftgo.common.NotYetImplementedException; +import net.chrisrichardson.ftgo.common.RevisedOrderLineItem; import net.chrisrichardson.ftgo.common.UnsupportedStateTransitionException; import net.chrisrichardson.ftgo.kitchenservice.api.TicketDetails; import net.chrisrichardson.ftgo.kitchenservice.api.TicketLineItem; +import net.chrisrichardson.ftgo.kitchenservice.api.events.TicketAcceptedEvent; +import net.chrisrichardson.ftgo.kitchenservice.api.events.TicketCancelled; +import net.chrisrichardson.ftgo.kitchenservice.api.events.TicketDomainEvent; import javax.persistence.*; import java.time.LocalDateTime; @@ -74,7 +78,7 @@ public List accept(LocalDateTime readyBy) { // Verify that readyBy is in the futurestate = TicketState.ACCEPTED; this.acceptTime = LocalDateTime.now(); if (!acceptTime.isBefore(readyBy)) - throw new IllegalArgumentException("readyBy is not in the future"); + throw new IllegalArgumentException(String.format("readyBy %s is not after now %s", readyBy, acceptTime)); this.readyBy = readyBy; return singletonList(new TicketAcceptedEvent(readyBy)); default: @@ -170,7 +174,7 @@ public List undoCancel() { } } - public List beginReviseOrder(Map revisedLineItemQuantities) { + public List beginReviseOrder(List revisedOrderLineItems) { switch (state) { case AWAITING_ACCEPTANCE: case ACCEPTED: @@ -192,7 +196,7 @@ public List undoBeginReviseOrder() { } } - public List confirmReviseTicket(Map revisedLineItemQuantities) { + public List confirmReviseTicket(List revisedOrderLineItems) { switch (state) { case REVISION_PENDING: this.state = this.previousState; diff --git a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketAcceptedEvent.java b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketAcceptedEvent.java deleted file mode 100644 index c767a810..00000000 --- a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketAcceptedEvent.java +++ /dev/null @@ -1,9 +0,0 @@ -package net.chrisrichardson.ftgo.kitchenservice.domain; - -import java.time.LocalDateTime; - -public class TicketAcceptedEvent implements TicketDomainEvent { - public TicketAcceptedEvent(LocalDateTime readyBy) { - - } -} diff --git a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketCreatedEvent.java b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketCreatedEvent.java index 028f03ec..4f593d81 100644 --- a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketCreatedEvent.java +++ b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketCreatedEvent.java @@ -2,6 +2,7 @@ import net.chrisrichardson.ftgo.kitchenservice.api.TicketDetails; +import net.chrisrichardson.ftgo.kitchenservice.api.events.TicketDomainEvent; public class TicketCreatedEvent implements TicketDomainEvent { public TicketCreatedEvent(Long id, TicketDetails details) { diff --git a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketDomainEventPublisher.java b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketDomainEventPublisher.java index 47483427..0e8af89a 100644 --- a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketDomainEventPublisher.java +++ b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketDomainEventPublisher.java @@ -2,6 +2,7 @@ import io.eventuate.tram.events.aggregates.AbstractAggregateDomainEventPublisher; import io.eventuate.tram.events.publisher.DomainEventPublisher; +import net.chrisrichardson.ftgo.kitchenservice.api.events.TicketDomainEvent; public class TicketDomainEventPublisher extends AbstractAggregateDomainEventPublisher { diff --git a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketPickedUpEvent.java b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketPickedUpEvent.java index 2e845630..36c6a7f2 100644 --- a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketPickedUpEvent.java +++ b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketPickedUpEvent.java @@ -1,4 +1,6 @@ package net.chrisrichardson.ftgo.kitchenservice.domain; +import net.chrisrichardson.ftgo.kitchenservice.api.events.TicketDomainEvent; + public class TicketPickedUpEvent implements TicketDomainEvent { } diff --git a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketPreparationCompletedEvent.java b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketPreparationCompletedEvent.java index 3967b715..4e199bbb 100644 --- a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketPreparationCompletedEvent.java +++ b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketPreparationCompletedEvent.java @@ -1,4 +1,6 @@ package net.chrisrichardson.ftgo.kitchenservice.domain; +import net.chrisrichardson.ftgo.kitchenservice.api.events.TicketDomainEvent; + public class TicketPreparationCompletedEvent implements TicketDomainEvent { } diff --git a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketPreparationStartedEvent.java b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketPreparationStartedEvent.java index 08ebf080..0121c079 100644 --- a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketPreparationStartedEvent.java +++ b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketPreparationStartedEvent.java @@ -1,4 +1,6 @@ package net.chrisrichardson.ftgo.kitchenservice.domain; +import net.chrisrichardson.ftgo.kitchenservice.api.events.TicketDomainEvent; + public class TicketPreparationStartedEvent implements TicketDomainEvent { } diff --git a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketRevised.java b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketRevised.java index 4c013fac..3adfcc3d 100644 --- a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketRevised.java +++ b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketRevised.java @@ -1,5 +1,7 @@ package net.chrisrichardson.ftgo.kitchenservice.domain; +import net.chrisrichardson.ftgo.kitchenservice.api.events.TicketDomainEvent; + public class TicketRevised implements TicketDomainEvent { } diff --git a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/main/KitchenServiceMain.java b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/main/KitchenServiceMain.java index 5b37aea7..ee00bfd7 100644 --- a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/main/KitchenServiceMain.java +++ b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/main/KitchenServiceMain.java @@ -1,27 +1,20 @@ package net.chrisrichardson.ftgo.kitchenservice.main; -import io.eventuate.jdbckafka.TramJdbcKafkaConfiguration; -import io.eventuate.tram.commands.common.ChannelMapping; -import io.eventuate.tram.commands.common.DefaultChannelMapping; +import io.eventuate.tram.spring.jdbckafka.TramJdbcKafkaConfiguration; import net.chrisrichardson.eventstore.examples.customersandorders.commonswagger.CommonSwaggerConfiguration; import net.chrisrichardson.ftgo.kitchenservice.messagehandlers.KitchenServiceMessageHandlersConfiguration; import net.chrisrichardson.ftgo.kitchenservice.web.KitchenServiceWebConfiguration; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; @SpringBootApplication -@Import({KitchenServiceWebConfiguration.class, KitchenServiceMessageHandlersConfiguration.class, +@Import({KitchenServiceWebConfiguration.class, + KitchenServiceMessageHandlersConfiguration.class, TramJdbcKafkaConfiguration.class, CommonSwaggerConfiguration.class}) public class KitchenServiceMain { - @Bean - public ChannelMapping channelMapping() { - return new DefaultChannelMapping.DefaultChannelMappingBuilder().build(); - } - public static void main(String[] args) { SpringApplication.run(KitchenServiceMain.class, args); } diff --git a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/messagehandlers/KitchenServiceCommandHandler.java b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/messagehandlers/KitchenServiceCommandHandler.java index e86ac42e..f5172f1f 100644 --- a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/messagehandlers/KitchenServiceCommandHandler.java +++ b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/messagehandlers/KitchenServiceCommandHandler.java @@ -21,7 +21,7 @@ public class KitchenServiceCommandHandler { public CommandHandlers commandHandlers() { return SagaCommandHandlersBuilder - .fromChannel(KitchenServiceChannels.kitchenServiceChannel) + .fromChannel(KitchenServiceChannels.COMMAND_CHANNEL) .onMessage(CreateTicket.class, this::createTicket) .onMessage(ConfirmCreateTicket.class, this::confirmCreateTicket) .onMessage(CancelCreateTicket.class, this::cancelCreateTicket) @@ -83,7 +83,7 @@ private Message undoBeginCancelTicket(CommandMessage cm) { - kitchenService.beginReviseOrder(cm.getCommand().getRestaurantId(), cm.getCommand().getOrderId(), cm.getCommand().getRevisedLineItemQuantities()); + kitchenService.beginReviseOrder(cm.getCommand().getRestaurantId(), cm.getCommand().getOrderId(), cm.getCommand().getRevisedOrderLineItems()); return withSuccess(); } @@ -93,7 +93,7 @@ public Message undoBeginReviseTicket(CommandMessage cm) { - kitchenService.confirmReviseTicket(cm.getCommand().getRestaurantId(), cm.getCommand().getOrderId(), cm.getCommand().getRevisedLineItemQuantities()); + kitchenService.confirmReviseTicket(cm.getCommand().getRestaurantId(), cm.getCommand().getOrderId(), cm.getCommand().getRevisedOrderLineItems()); return withSuccess(); } diff --git a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/messagehandlers/KitchenServiceEventConsumer.java b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/messagehandlers/KitchenServiceEventConsumer.java index 49444094..4c916ea4 100644 --- a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/messagehandlers/KitchenServiceEventConsumer.java +++ b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/messagehandlers/KitchenServiceEventConsumer.java @@ -4,8 +4,8 @@ import io.eventuate.tram.events.subscriber.DomainEventHandlers; import io.eventuate.tram.events.subscriber.DomainEventHandlersBuilder; import net.chrisrichardson.ftgo.kitchenservice.domain.KitchenService; +import net.chrisrichardson.ftgo.kitchenservice.domain.RestaurantMenu; import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantCreated; -import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantMenu; import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantMenuRevised; import org.springframework.beans.factory.annotation.Autowired; @@ -26,15 +26,14 @@ public DomainEventHandlers domainEventHandlers() { private void createMenu(DomainEventEnvelope de) { String restaurantIds = de.getAggregateId(); long id = Long.parseLong(restaurantIds); - RestaurantMenu menu = de.getEvent().getMenu(); + RestaurantMenu menu = new RestaurantMenu(RestaurantEventMapper.toMenuItems(de.getEvent().getMenu().getMenuItems())); kitchenService.createMenu(id, menu); } public void reviseMenu(DomainEventEnvelope de) { - long id = Long.parseLong(de.getAggregateId()); - RestaurantMenu revisedMenu = de.getEvent().getRevisedMenu(); - kitchenService.reviseMenu(id, revisedMenu); + RestaurantMenu menu = new RestaurantMenu(RestaurantEventMapper.toMenuItems(de.getEvent().getMenu().getMenuItems())); + kitchenService.reviseMenu(id, menu); } } diff --git a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/messagehandlers/KitchenServiceMessageHandlersConfiguration.java b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/messagehandlers/KitchenServiceMessageHandlersConfiguration.java index f09a8ae4..34e23bbf 100644 --- a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/messagehandlers/KitchenServiceMessageHandlersConfiguration.java +++ b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/messagehandlers/KitchenServiceMessageHandlersConfiguration.java @@ -1,9 +1,11 @@ package net.chrisrichardson.ftgo.kitchenservice.messagehandlers; +import io.eventuate.tram.spring.events.subscriber.TramEventSubscriberConfiguration; import io.eventuate.tram.events.subscriber.DomainEventDispatcher; -import io.eventuate.tram.messaging.consumer.MessageConsumer; +import io.eventuate.tram.events.subscriber.DomainEventDispatcherFactory; import io.eventuate.tram.sagas.participant.SagaCommandDispatcher; -import io.eventuate.tram.sagas.participant.SagaParticipantConfiguration; +import io.eventuate.tram.sagas.participant.SagaCommandDispatcherFactory; +import io.eventuate.tram.sagas.spring.participant.SagaParticipantConfiguration; import net.chrisrichardson.ftgo.common.CommonConfiguration; import net.chrisrichardson.ftgo.kitchenservice.domain.KitchenDomainConfiguration; import org.springframework.context.annotation.Bean; @@ -11,7 +13,7 @@ import org.springframework.context.annotation.Import; @Configuration -@Import({KitchenDomainConfiguration.class, SagaParticipantConfiguration.class, CommonConfiguration.class}) +@Import({KitchenDomainConfiguration.class, SagaParticipantConfiguration.class, CommonConfiguration.class, TramEventSubscriberConfiguration.class, SagaParticipantConfiguration.class}) public class KitchenServiceMessageHandlersConfiguration { @Bean @@ -25,12 +27,12 @@ public KitchenServiceCommandHandler kitchenServiceCommandHandler() { } @Bean - public SagaCommandDispatcher kitchenServiceSagaCommandDispatcher(KitchenServiceCommandHandler kitchenServiceCommandHandler) { - return new SagaCommandDispatcher("kitchenServiceCommands", kitchenServiceCommandHandler.commandHandlers()); + public SagaCommandDispatcher kitchenServiceSagaCommandDispatcher(KitchenServiceCommandHandler kitchenServiceCommandHandler, SagaCommandDispatcherFactory sagaCommandDispatcherFactory) { + return sagaCommandDispatcherFactory.make("kitchenServiceCommands", kitchenServiceCommandHandler.commandHandlers()); } @Bean - public DomainEventDispatcher domainEventDispatcher(KitchenServiceEventConsumer kitchenServiceEventConsumer, MessageConsumer messageConsumer) { - return new DomainEventDispatcher("kitchenServiceEvents", kitchenServiceEventConsumer.domainEventHandlers(), messageConsumer); // @Autowire + public DomainEventDispatcher domainEventDispatcher(KitchenServiceEventConsumer kitchenServiceEventConsumer, DomainEventDispatcherFactory domainEventDispatcherFactory) { + return domainEventDispatcherFactory.make("kitchenServiceEvents", kitchenServiceEventConsumer.domainEventHandlers()); } } diff --git a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/messagehandlers/RestaurantEventMapper.java b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/messagehandlers/RestaurantEventMapper.java new file mode 100644 index 00000000..6705b2e5 --- /dev/null +++ b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/messagehandlers/RestaurantEventMapper.java @@ -0,0 +1,15 @@ +package net.chrisrichardson.ftgo.kitchenservice.messagehandlers; + +import net.chrisrichardson.ftgo.common.Money; +import net.chrisrichardson.ftgo.restaurantservice.events.MenuItem; + +import java.util.List; +import java.util.stream.Collectors; + +public class RestaurantEventMapper { + + public static List toMenuItems(List menuItems) { + return menuItems.stream().map(mi -> new net.chrisrichardson.ftgo.kitchenservice.domain.MenuItem(mi.getId(), mi.getName(), new Money(mi.getPrice()))).collect(Collectors.toList()); + } + +} diff --git a/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/web/KitchenController.java b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/web/KitchenController.java new file mode 100644 index 00000000..af1fe6bb --- /dev/null +++ b/ftgo-kitchen-service/src/main/java/net/chrisrichardson/ftgo/kitchenservice/web/KitchenController.java @@ -0,0 +1,22 @@ +package net.chrisrichardson.ftgo.kitchenservice.web; + +import net.chrisrichardson.ftgo.kitchenservice.api.web.TicketAcceptance; +import net.chrisrichardson.ftgo.kitchenservice.domain.KitchenService; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +public class KitchenController { + + private KitchenService kitchenService; + + public KitchenController(KitchenService kitchenService) { + this.kitchenService = kitchenService; + } + + @RequestMapping(path="/tickets/{ticketId}/accept", method= RequestMethod.POST) + public void acceptTicket(@PathVariable long ticketId, @RequestBody TicketAcceptance ticketAcceptance) { + kitchenService.accept(ticketId, ticketAcceptance.getReadyBy()); + } +} diff --git a/ftgo-kitchen-service/src/main/resources/application.properties b/ftgo-kitchen-service/src/main/resources/application.properties index 1b8bfc3f..09c3fd86 100644 --- a/ftgo-kitchen-service/src/main/resources/application.properties +++ b/ftgo-kitchen-service/src/main/resources/application.properties @@ -1,23 +1,18 @@ spring.application.name=ftgo-kitchen-service +management.endpoint.health.show-details=always + spring.jpa.generate-ddl=true logging.level.org.springframework.orm.jpa=INFO logging.level.org.hibernate.SQL=DEBUG logging.level.io.eventuate=DEBUG logging.level.net.chrisrichardson.ftgo=DEBUG -logging.level.io.eventuate.tram=TRACE -spring.datasource.url=jdbc:mysql://${DOCKER_HOST_IP:localhost}/eventuate -spring.datasource.username=mysqluser -spring.datasource.password=mysqlpw -spring.datasource.driver.class.name=com.mysql.jdbc.Driver -spring.data.mongodb.uri=mongodb://${DOCKER_HOST_IP:localhost}/bankingexampledb +logging.level.io.eventuate.tram=DEBUG + +spring.datasource.url=jdbc:mysql://${DOCKER_HOST_IP:localhost}/ftgo_kitchen_service +spring.datasource.username=ftgo_kitchen_service_user +spring.datasource.password=ftgo_kitchen_service_password +spring.datasource.driver-class-name=com.mysql.jdbc.Driver eventuatelocal.kafka.bootstrap.servers=${DOCKER_HOST_IP:localhost}:9092 -eventuatelocal.cdc.db.user.name=root -eventuatelocal.cdc.db.password=rootpassword eventuatelocal.zookeeper.connection.string=${DOCKER_HOST_IP:localhost}:2181 - -aws.access.key_id=id_key -aws.secret.access.key=access_key -aws.dynamodb.endpoint.url=http://${DOCKER_HOST_IP:localhost}:8000 -aws.region=us-west-2 diff --git a/ftgo-kitchen-service/src/test/java/net/chrisrichardson/ftgo/kitchenservice/domain/KitchenServiceInMemoryIntegrationTest.java b/ftgo-kitchen-service/src/test/java/net/chrisrichardson/ftgo/kitchenservice/domain/KitchenServiceInMemoryIntegrationTest.java index 2617bae9..edb12e4b 100644 --- a/ftgo-kitchen-service/src/test/java/net/chrisrichardson/ftgo/kitchenservice/domain/KitchenServiceInMemoryIntegrationTest.java +++ b/ftgo-kitchen-service/src/test/java/net/chrisrichardson/ftgo/kitchenservice/domain/KitchenServiceInMemoryIntegrationTest.java @@ -1,12 +1,10 @@ package net.chrisrichardson.ftgo.kitchenservice.domain; -import io.eventuate.tram.commands.common.ChannelMapping; -import io.eventuate.tram.commands.common.DefaultChannelMapping; import io.eventuate.tram.commands.producer.CommandProducer; -import io.eventuate.tram.commands.producer.TramCommandProducerConfiguration; -import io.eventuate.tram.inmemory.TramInMemoryConfiguration; +import io.eventuate.tram.spring.commands.producer.TramCommandProducerConfiguration; import io.eventuate.tram.sagas.common.SagaCommandHeaders; +import io.eventuate.tram.sagas.spring.inmemory.TramSagaInMemoryConfiguration; import io.eventuate.tram.testutil.TestMessageConsumer; import io.eventuate.tram.testutil.TestMessageConsumerFactory; import net.chrisrichardson.ftgo.common.Money; @@ -25,11 +23,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; -import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; import org.springframework.test.context.junit4.SpringRunner; -import javax.sql.DataSource; import java.util.Collections; import java.util.HashMap; import java.util.Map; @@ -48,30 +43,15 @@ public class KitchenServiceInMemoryIntegrationTest { @EnableAutoConfiguration @Import({KitchenServiceWebConfiguration.class, KitchenServiceMessageHandlersConfiguration.class, TramCommandProducerConfiguration.class, - TramInMemoryConfiguration.class}) + TramSagaInMemoryConfiguration.class}) public static class TestConfiguration { - @Bean - public ChannelMapping channelMapping() { - return new DefaultChannelMapping.DefaultChannelMappingBuilder().build(); - } - @Bean public TestMessageConsumerFactory testMessageConsumerFactory() { return new TestMessageConsumerFactory(); } - @Bean - public DataSource dataSource() { - EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); - return builder.setType(EmbeddedDatabaseType.H2) - .addScript("eventuate-tram-embedded-schema.sql") - .addScript("eventuate-tram-sagas-embedded.sql") - .build(); - } - - } private String baseUrl(String path) { diff --git a/ftgo-kitchen-service/src/test/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketDomainEventPublisherTest.java b/ftgo-kitchen-service/src/test/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketDomainEventPublisherTest.java new file mode 100644 index 00000000..eae6f04c --- /dev/null +++ b/ftgo-kitchen-service/src/test/java/net/chrisrichardson/ftgo/kitchenservice/domain/TicketDomainEventPublisherTest.java @@ -0,0 +1,16 @@ +package net.chrisrichardson.ftgo.kitchenservice.domain; + +import net.chrisrichardson.ftgo.kitchenservice.api.KitchenServiceChannels; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class TicketDomainEventPublisherTest { + + @Test + public void verifyTicketEventChannel() { + assertEquals(KitchenServiceChannels.TICKET_EVENT_CHANNEL, new TicketDomainEventPublisher(null).getAggregateType().getName()); + } + + +} \ No newline at end of file diff --git a/ftgo-kitchen-service/src/test/resources/application.properties b/ftgo-kitchen-service/src/test/resources/application.properties index 8e16f137..86a97ad3 100644 --- a/ftgo-kitchen-service/src/test/resources/application.properties +++ b/ftgo-kitchen-service/src/test/resources/application.properties @@ -8,12 +8,10 @@ stubrunner.integration.enabled=false spring.datasource.url=jdbc:mysql://${DOCKER_HOST_IP:localhost}/eventuate spring.datasource.username=mysqluser spring.datasource.password=mysqlpw -spring.datasource.driver.class.name=com.mysql.jdbc.Driver +spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.data.mongodb.uri=mongodb://${DOCKER_HOST_IP:localhost}/bankingexampledb eventuatelocal.kafka.bootstrap.servers=${DOCKER_HOST_IP:localhost}:9092 -eventuatelocal.cdc.db.user.name=root -eventuatelocal.cdc.db.password=rootpassword eventuatelocal.zookeeper.connection.string=${DOCKER_HOST_IP:localhost}:2181 aws.access.key_id=id_key diff --git a/ftgo-order-history-service/Dockerfile b/ftgo-order-history-service/Dockerfile index ba5ec0c5..7b9b891a 100644 --- a/ftgo-order-history-service/Dockerfile +++ b/ftgo-order-history-service/Dockerfile @@ -1,5 +1,3 @@ -FROM openjdk:8u171-jre-alpine -RUN apk --no-cache add curl -CMD java ${JAVA_OPTS} -jar ftgo-order-history-service.jar -HEALTHCHECK --start-period=30s --interval=5s CMD curl -f http://localhost:8080/actuator/health || exit 1 -COPY build/libs/ftgo-order-history-service.jar . +ARG baseImageVersion +FROM eventuateio/eventuate-examples-docker-images-spring-example-base-image:$baseImageVersion +COPY build/libs/ftgo-order-history-service.jar service.jar diff --git a/ftgo-order-history-service/build.gradle b/ftgo-order-history-service/build.gradle index b5dc6e03..00035a1c 100644 --- a/ftgo-order-history-service/build.gradle +++ b/ftgo-order-history-service/build.gradle @@ -3,6 +3,7 @@ buildscript { dependencies { classpath "io.spring.gradle:dependency-management-plugin:$springDependencyManagementPluginVersion" classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:$springCloudContractDependenciesVersion" + classpath "com.avast.gradle:gradle-docker-compose-plugin:$dockerComposePluginVersion" } repositories { mavenCentral() @@ -12,7 +13,9 @@ buildscript { apply plugin: FtgoServicePlugin apply plugin: "io.spring.dependency-management" -apply plugin: 'spring-cloud-contract' +// apply plugin: 'spring-cloud-contract' +apply plugin: IntegrationTestsPlugin +apply plugin: 'docker-compose' dependencyManagement { imports { @@ -20,39 +23,63 @@ dependencyManagement { } } - dependencies { compile 'com.amazonaws:aws-java-sdk-dynamodb:1.11.158' compile project(":common-swagger") - compile project(":ftgo-order-service-api") - compile "io.eventuate.tram.core:eventuate-tram-jdbc-kafka:$eventuateTramVersion" - compile "io.eventuate.tram.core:eventuate-tram-events:$eventuateTramVersion" - compile "io.eventuate.tram.sagas:eventuate-tram-sagas-simple-dsl:$eventuateTramSagasVersion" + compile (project(":ftgo-order-service-api")) { + exclude module: "spring-boot-starter-data-jpa" + } + + compile "io.eventuate.tram.core:eventuate-tram-spring-events" + compile "io.eventuate.tram.core:eventuate-tram-spring-messaging" + compile "io.eventuate.tram.core:eventuate-tram-spring-consumer-kafka" compile "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion" compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion" compile 'javax.el:javax.el-api:2.2.5' - compile "io.eventuate.client.java:eventuate-client-java-spring:$eventuateClientVersion" - compile "io.eventuate.local.java:eventuate-local-java-jdbc:${eventuateLocalVersion}" + compile('org.apache.kafka:kafka-clients:2.3.0') { + force = true + } - testCompile "io.eventuate.client.java:eventuate-client-java-jdbc:$eventuateClientVersion" - testCompile "io.eventuate.util:eventuate-util-test:$eventuateUtilVersion" - testCompile "io.eventuate.tram.core:eventuate-tram-test-util:$eventuateTramVersion" + testCompile "io.eventuate.tram.core:eventuate-tram-spring-in-memory" + testCompile "io.eventuate.util:eventuate-util-test" + testCompile "io.eventuate.tram.core:eventuate-tram-test-util" - testCompile "io.eventuate.tram.sagas:eventuate-tram-sagas-in-memory:$eventuateTramSagasVersion" testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion" testCompile "com.jayway.restassured:rest-assured:$restAssuredVersion" testCompile "com.jayway.jsonpath:json-path:2.3.0" - // Added this stuff - testCompile "net.chrisrichardson.ftgo:ftgo-order-service-contracts:unspecified:stubs" testCompile "org.springframework.cloud:spring-cloud-contract-wiremock" testCompile "org.springframework.cloud:spring-cloud-starter-contract-stub-runner" - testCompile "io.eventuate.tram.core:eventuate-tram-testing-support-spring-cloud-contract:$eventuateTramVersion" - + testCompile "io.eventuate.tram.core:eventuate-tram-spring-testing-support-cloud-contract" + + testCompile "org.hamcrest:hamcrest:2.1" + +} + +dockerCompose { + + environment.put "EVENTUATE_COMMON_VERSION", eventuateCommonImageVersion + environment.put "EVENTUATE_CDC_VERSION", eventuateCdcImageVersion + environment.put "EVENTUATE_SAGA_VERSION", eventuateTramSagasImageVersion + environment.put "EVENTUATE_JAVA_BASE_IMAGE_VERSION", eventuateExamplesBaseImageVersion + environment.put "EVENTUATE_MESSAGING_KAFKA_IMAGE_VERSION", eventuateMessagingKafkaImageVersion + + projectName = null + + integrationTests { + projectName = null + removeOrphans = true + retainContainersOnStartupFailure = true + startedServices = ["dynamodblocal", "dynamodblocal-init"] + stopContainers = false + if (System.getenv("FTGO_DOCKER_COMPOSE_FILES") != null) + useComposeFiles = System.getenv("FTGO_DOCKER_COMPOSE_FILES").split(",").collect { "../" + it } + } +} -} \ No newline at end of file +integrationTest.dependsOn(integrationTestsComposeUp) diff --git a/ftgo-order-history-service/src/test/java/net/chrisrichardson/ftgo/cqrs/orderhistory/dynamodb/OrderHistoryDaoDynamoDbTest.java b/ftgo-order-history-service/src/integration-test/java/net/chrisrichardson/ftgo/cqrs/orderhistory/dynamodb/OrderHistoryDaoDynamoDbTest.java similarity index 86% rename from ftgo-order-history-service/src/test/java/net/chrisrichardson/ftgo/cqrs/orderhistory/dynamodb/OrderHistoryDaoDynamoDbTest.java rename to ftgo-order-history-service/src/integration-test/java/net/chrisrichardson/ftgo/cqrs/orderhistory/dynamodb/OrderHistoryDaoDynamoDbTest.java index 87338297..f1650de9 100644 --- a/ftgo-order-history-service/src/test/java/net/chrisrichardson/ftgo/cqrs/orderhistory/dynamodb/OrderHistoryDaoDynamoDbTest.java +++ b/ftgo-order-history-service/src/integration-test/java/net/chrisrichardson/ftgo/cqrs/orderhistory/dynamodb/OrderHistoryDaoDynamoDbTest.java @@ -1,6 +1,8 @@ package net.chrisrichardson.ftgo.cqrs.orderhistory.dynamodb; -import io.eventuate.javaclient.commonimpl.JSonMapper; +import io.eventuate.common.json.mapper.JSonMapper; +import io.eventuate.common.spring.jdbc.EventuateTransactionTemplateConfiguration; +import io.eventuate.tram.spring.inmemory.TramInMemoryConfiguration; import net.chrisrichardson.ftgo.common.Money; import net.chrisrichardson.ftgo.cqrs.orderhistory.OrderHistory; import net.chrisrichardson.ftgo.cqrs.orderhistory.OrderHistoryDao; @@ -15,6 +17,8 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import java.util.List; @@ -28,11 +32,17 @@ import static org.junit.Assert.assertTrue; @RunWith(SpringJUnit4ClassRunner.class) -@SpringBootTest(classes = {OrderHistoryDynamoDBConfiguration.class}) -@EnableAutoConfiguration -@ComponentScan +@SpringBootTest(classes = {OrderHistoryDaoDynamoDbTest.OrderHistoryDaoDynamoDbTestConfiguration.class}) public class OrderHistoryDaoDynamoDbTest { + @Configuration + @EnableAutoConfiguration + @ComponentScan + @Import({OrderHistoryDynamoDBConfiguration.class, TramInMemoryConfiguration.class, EventuateTransactionTemplateConfiguration.class}) + static public class OrderHistoryDaoDynamoDbTestConfiguration { + + } + private String consumerId; private Order order1; private String orderId; @@ -66,7 +76,7 @@ public void shouldFindOrder() { @Test public void shouldIgnoreDuplicateAdd() { - dao.cancelOrder(orderId, Optional.empty()); + dao.updateOrderState(orderId, OrderState.CANCELLED, Optional.empty()); assertFalse(dao.addOrder(order1, eventSource)); Optional order = dao.findOrder(orderId); assertEquals(OrderState.CANCELLED, order.get().getStatus()); @@ -102,15 +112,15 @@ public void shouldFindOrdersWithStatus() throws InterruptedException { @Test public void shouldCancel() throws InterruptedException { - dao.cancelOrder(orderId, Optional.of(new SourceEvent("a", "b", "c"))); + dao.updateOrderState(orderId, OrderState.CANCELLED, Optional.of(new SourceEvent("a", "b", "c"))); Order order = dao.findOrder(orderId).get(); assertEquals(OrderState.CANCELLED, order.getStatus()); } @Test public void shouldHandleCancel() throws InterruptedException { - assertTrue(dao.cancelOrder(orderId, Optional.of(new SourceEvent("a", "b", "c")))); - assertFalse(dao.cancelOrder(orderId, Optional.of(new SourceEvent("a", "b", "c")))); + assertTrue(dao.updateOrderState(orderId, OrderState.CANCELLED, Optional.of(new SourceEvent("a", "b", "c")))); + assertFalse(dao.updateOrderState(orderId, OrderState.CANCELLED, Optional.of(new SourceEvent("a", "b", "c")))); } @Test diff --git a/ftgo-order-history-service/src/main/java/net/chrisrichardson/ftgo/cqrs/orderhistory/OrderHistoryDao.java b/ftgo-order-history-service/src/main/java/net/chrisrichardson/ftgo/cqrs/orderhistory/OrderHistoryDao.java index c3f5323c..9e678220 100644 --- a/ftgo-order-history-service/src/main/java/net/chrisrichardson/ftgo/cqrs/orderhistory/OrderHistoryDao.java +++ b/ftgo-order-history-service/src/main/java/net/chrisrichardson/ftgo/cqrs/orderhistory/OrderHistoryDao.java @@ -3,6 +3,7 @@ import net.chrisrichardson.ftgo.cqrs.orderhistory.dynamodb.SourceEvent; import net.chrisrichardson.ftgo.cqrs.orderhistory.dynamodb.Order; +import net.chrisrichardson.ftgo.orderservice.api.events.OrderState; import java.util.Optional; @@ -12,7 +13,7 @@ public interface OrderHistoryDao { OrderHistory findOrderHistory(String consumerId, OrderHistoryFilter filter); - public boolean cancelOrder(String orderId, Optional eventSource); + boolean updateOrderState(String orderId, OrderState newState, Optional eventSource); void noteTicketPreparationStarted(String orderId); @@ -25,4 +26,5 @@ public interface OrderHistoryDao { void noteDelivered(String orderId); Optional findOrder(String orderId); + } diff --git a/ftgo-order-history-service/src/main/java/net/chrisrichardson/ftgo/cqrs/orderhistory/dynamodb/OrderHistoryDaoDynamoDb.java b/ftgo-order-history-service/src/main/java/net/chrisrichardson/ftgo/cqrs/orderhistory/dynamodb/OrderHistoryDaoDynamoDb.java index 4366c1e2..afb23645 100644 --- a/ftgo-order-history-service/src/main/java/net/chrisrichardson/ftgo/cqrs/orderhistory/dynamodb/OrderHistoryDaoDynamoDb.java +++ b/ftgo-order-history-service/src/main/java/net/chrisrichardson/ftgo/cqrs/orderhistory/dynamodb/OrderHistoryDaoDynamoDb.java @@ -343,15 +343,13 @@ private String keywordFilterExpression(Map // } @Override - public boolean cancelOrder(String orderId, Optional - eventSource) { + public boolean updateOrderState(String orderId, OrderState newState, Optional eventSource) { UpdateItemSpec spec = new UpdateItemSpec() .withPrimaryKey("orderId", orderId) .withUpdateExpression("SET #orderStatus = :orderStatus") .withNameMap(Collections.singletonMap("#orderStatus", ORDER_STATUS_FIELD)) - .withValueMap(Collections.singletonMap(":orderStatus", OrderState - .CANCELLED.toString())) + .withValueMap(Collections.singletonMap(":orderStatus", newState.toString())) .withReturnValues(ReturnValue.NONE); return idempotentUpdate(spec, eventSource); } @@ -405,6 +403,7 @@ public Optional findOrder(String orderId) { return Optional.ofNullable(item).map(this::toOrder); } + private Order toOrder(Item avs) { Order order = new Order(avs.getString("orderId"), avs.getString("consumerId"), diff --git a/ftgo-order-history-service/src/main/java/net/chrisrichardson/ftgo/cqrs/orderhistory/main/OrderHistoryServiceMain.java b/ftgo-order-history-service/src/main/java/net/chrisrichardson/ftgo/cqrs/orderhistory/main/OrderHistoryServiceMain.java index 8d584a09..5ae1eed0 100644 --- a/ftgo-order-history-service/src/main/java/net/chrisrichardson/ftgo/cqrs/orderhistory/main/OrderHistoryServiceMain.java +++ b/ftgo-order-history-service/src/main/java/net/chrisrichardson/ftgo/cqrs/orderhistory/main/OrderHistoryServiceMain.java @@ -1,6 +1,7 @@ package net.chrisrichardson.ftgo.cqrs.orderhistory.main; -import io.eventuate.jdbckafka.TramJdbcKafkaConfiguration; +import io.eventuate.tram.spring.consumer.common.TramConsumerCommonConfiguration; +import io.eventuate.tram.spring.consumer.kafka.EventuateTramKafkaMessageConsumerConfiguration; import net.chrisrichardson.eventstore.examples.customersandorders.commonswagger.CommonSwaggerConfiguration; import net.chrisrichardson.ftgo.cqrs.orderhistory.messaging.OrderHistoryServiceMessagingConfiguration; import net.chrisrichardson.ftgo.cqrs.orderhistory.web.OrderHistoryWebConfiguration; @@ -9,8 +10,11 @@ import org.springframework.context.annotation.Import; @SpringBootApplication -@Import({OrderHistoryWebConfiguration.class, OrderHistoryServiceMessagingConfiguration.class, - TramJdbcKafkaConfiguration.class, CommonSwaggerConfiguration.class}) +@Import({OrderHistoryWebConfiguration.class, + OrderHistoryServiceMessagingConfiguration.class, + CommonSwaggerConfiguration.class, + TramConsumerCommonConfiguration.class, + EventuateTramKafkaMessageConsumerConfiguration.class}) public class OrderHistoryServiceMain { public static void main(String[] args) { diff --git a/ftgo-order-history-service/src/main/java/net/chrisrichardson/ftgo/cqrs/orderhistory/messaging/NoopDuplicateMessageDetector.java b/ftgo-order-history-service/src/main/java/net/chrisrichardson/ftgo/cqrs/orderhistory/messaging/NoopDuplicateMessageDetector.java deleted file mode 100644 index b66dfb36..00000000 --- a/ftgo-order-history-service/src/main/java/net/chrisrichardson/ftgo/cqrs/orderhistory/messaging/NoopDuplicateMessageDetector.java +++ /dev/null @@ -1,12 +0,0 @@ -package net.chrisrichardson.ftgo.cqrs.orderhistory.messaging; - -import io.eventuate.tram.consumer.kafka.DuplicateMessageDetector; - -// TODO Duplicate -public class NoopDuplicateMessageDetector implements DuplicateMessageDetector { - - @Override - public boolean isDuplicate(String consumerId, String messageId) { - return false; - } -} diff --git a/ftgo-order-history-service/src/main/java/net/chrisrichardson/ftgo/cqrs/orderhistory/messaging/OrderHistoryEventHandlers.java b/ftgo-order-history-service/src/main/java/net/chrisrichardson/ftgo/cqrs/orderhistory/messaging/OrderHistoryEventHandlers.java index d2332999..96b7d046 100644 --- a/ftgo-order-history-service/src/main/java/net/chrisrichardson/ftgo/cqrs/orderhistory/messaging/OrderHistoryEventHandlers.java +++ b/ftgo-order-history-service/src/main/java/net/chrisrichardson/ftgo/cqrs/orderhistory/messaging/OrderHistoryEventHandlers.java @@ -3,14 +3,12 @@ import io.eventuate.tram.events.subscriber.DomainEventEnvelope; import io.eventuate.tram.events.subscriber.DomainEventHandlers; import io.eventuate.tram.events.subscriber.DomainEventHandlersBuilder; -import io.eventuate.tram.messaging.common.Message; import net.chrisrichardson.ftgo.cqrs.orderhistory.DeliveryPickedUp; import net.chrisrichardson.ftgo.cqrs.orderhistory.Location; import net.chrisrichardson.ftgo.cqrs.orderhistory.OrderHistoryDao; import net.chrisrichardson.ftgo.cqrs.orderhistory.dynamodb.Order; import net.chrisrichardson.ftgo.cqrs.orderhistory.dynamodb.SourceEvent; -import net.chrisrichardson.ftgo.orderservice.api.events.OrderCreatedEvent; -import net.chrisrichardson.ftgo.orderservice.api.events.OrderState; +import net.chrisrichardson.ftgo.orderservice.api.events.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,6 +34,9 @@ public DomainEventHandlers domainEventHandlers() { return DomainEventHandlersBuilder .forAggregateType("net.chrisrichardson.ftgo.orderservice.domain.Order") .onEvent(OrderCreatedEvent.class, this::handleOrderCreated) + .onEvent(OrderAuthorized.class, this::handleOrderAuthorized) + .onEvent(OrderCancelled.class, this::handleOrderCancelled) + .onEvent(OrderRejected.class, this::handleOrderRejected) // .onEvent(DeliveryPickedUp.class, this::handleDeliveryPickedUp) .build(); } @@ -51,6 +52,24 @@ public void handleOrderCreated(DomainEventEnvelope dee) { logger.debug("handleOrderCreated result {} {}", dee, result); } + public void handleOrderAuthorized(DomainEventEnvelope dee) { + logger.debug("handleOrderAuthorized called {}", dee); + boolean result = orderHistoryDao.updateOrderState(dee.getAggregateId(), OrderState.APPROVED, makeSourceEvent(dee)); + logger.debug("handleOrderAuthorized result {} {}", dee, result); + } + + public void handleOrderCancelled(DomainEventEnvelope dee) { + logger.debug("handleOrderCancelled called {}", dee); + boolean result = orderHistoryDao.updateOrderState(dee.getAggregateId(), OrderState.CANCELLED, makeSourceEvent(dee)); + logger.debug("handleOrderCancelled result {} {}", dee, result); + } + + public void handleOrderRejected(DomainEventEnvelope dee) { + logger.debug("handleOrderRejected called {}", dee); + boolean result = orderHistoryDao.updateOrderState(dee.getAggregateId(), OrderState.REJECTED, makeSourceEvent(dee)); + logger.debug("handleOrderRejected result {} {}", dee, result); + } + private Order makeOrder(String orderId, OrderCreatedEvent event) { return new Order(orderId, Long.toString(event.getOrderDetails().getConsumerId()), diff --git a/ftgo-order-history-service/src/main/java/net/chrisrichardson/ftgo/cqrs/orderhistory/messaging/OrderHistoryServiceMessagingConfiguration.java b/ftgo-order-history-service/src/main/java/net/chrisrichardson/ftgo/cqrs/orderhistory/messaging/OrderHistoryServiceMessagingConfiguration.java index c164cdd7..204be123 100644 --- a/ftgo-order-history-service/src/main/java/net/chrisrichardson/ftgo/cqrs/orderhistory/messaging/OrderHistoryServiceMessagingConfiguration.java +++ b/ftgo-order-history-service/src/main/java/net/chrisrichardson/ftgo/cqrs/orderhistory/messaging/OrderHistoryServiceMessagingConfiguration.java @@ -1,17 +1,17 @@ package net.chrisrichardson.ftgo.cqrs.orderhistory.messaging; -import io.eventuate.tram.consumer.kafka.DuplicateMessageDetector; +import io.eventuate.tram.spring.consumer.common.TramNoopDuplicateMessageDetectorConfiguration; +import io.eventuate.tram.spring.events.subscriber.TramEventSubscriberConfiguration; import io.eventuate.tram.events.subscriber.DomainEventDispatcher; -import io.eventuate.tram.messaging.consumer.MessageConsumer; +import io.eventuate.tram.events.subscriber.DomainEventDispatcherFactory; import net.chrisrichardson.ftgo.common.CommonConfiguration; import net.chrisrichardson.ftgo.cqrs.orderhistory.OrderHistoryDao; -import net.chrisrichardson.ftgo.cqrs.orderhistory.dynamodb.OrderHistoryDynamoDBConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @Configuration -@Import(CommonConfiguration.class) +@Import({CommonConfiguration.class, TramNoopDuplicateMessageDetectorConfiguration.class, TramEventSubscriberConfiguration.class}) public class OrderHistoryServiceMessagingConfiguration { @Bean @@ -20,12 +20,8 @@ public OrderHistoryEventHandlers orderHistoryEventHandlers(OrderHistoryDao order } @Bean - public DomainEventDispatcher orderHistoryDomainEventDispatcher(OrderHistoryEventHandlers orderHistoryEventHandlers, MessageConsumer messageConsumer) { - return new DomainEventDispatcher("orderHistoryDomainEventDispatcher", orderHistoryEventHandlers.domainEventHandlers(), messageConsumer); + public DomainEventDispatcher orderHistoryDomainEventDispatcher(OrderHistoryEventHandlers orderHistoryEventHandlers, DomainEventDispatcherFactory domainEventDispatcherFactory) { + return domainEventDispatcherFactory.make("orderHistoryDomainEventDispatcher", orderHistoryEventHandlers.domainEventHandlers()); } - @Bean - public DuplicateMessageDetector duplicateMessageDetector() { - return new NoopDuplicateMessageDetector(); - } } diff --git a/ftgo-order-history-service/src/main/resources/application.properties b/ftgo-order-history-service/src/main/resources/application.properties index f3259469..da78bbf7 100644 --- a/ftgo-order-history-service/src/main/resources/application.properties +++ b/ftgo-order-history-service/src/main/resources/application.properties @@ -1,20 +1,13 @@ spring.application.name=ftgo-order-history-service -spring.jpa.generate-ddl=true -logging.level.org.springframework.orm.jpa=INFO -logging.level.org.hibernate.SQL=DEBUG +management.endpoint.health.show-details=always + logging.level.io.eventuate=DEBUG logging.level.net.chrisrichardson.ftgo=DEBUG -logging.level.io.eventuate.tram=TRACE -spring.datasource.url=jdbc:mysql://${DOCKER_HOST_IP:localhost}/eventuate -spring.datasource.username=mysqluser -spring.datasource.password=mysqlpw -spring.datasource.driver.class.name=com.mysql.jdbc.Driver -spring.data.mongodb.uri=mongodb://${DOCKER_HOST_IP:localhost}/bankingexampledb +logging.level.io.eventuate.tram=DEBUG +logging.level.root=INFO eventuatelocal.kafka.bootstrap.servers=${DOCKER_HOST_IP:localhost}:9092 -eventuatelocal.cdc.db.user.name=root -eventuatelocal.cdc.db.password=rootpassword eventuatelocal.zookeeper.connection.string=${DOCKER_HOST_IP:localhost}:2181 aws.access.key_id=id_key diff --git a/ftgo-order-history-service/src/test/java/net/chrisrichardson/ftgo/cqrs/orderhistory/web/OrderHistoryControllerTest.java b/ftgo-order-history-service/src/test/java/net/chrisrichardson/ftgo/cqrs/orderhistory/web/OrderHistoryControllerTest.java index 92a603fc..36ae93dd 100644 --- a/ftgo-order-history-service/src/test/java/net/chrisrichardson/ftgo/cqrs/orderhistory/web/OrderHistoryControllerTest.java +++ b/ftgo-order-history-service/src/test/java/net/chrisrichardson/ftgo/cqrs/orderhistory/web/OrderHistoryControllerTest.java @@ -1,6 +1,6 @@ package net.chrisrichardson.ftgo.cqrs.orderhistory.web; -import io.eventuate.javaclient.commonimpl.JSonMapper; +import io.eventuate.common.json.mapper.JSonMapper; import net.chrisrichardson.ftgo.common.CommonJsonMapperInitializer; import net.chrisrichardson.ftgo.cqrs.orderhistory.OrderHistoryDao; import net.chrisrichardson.ftgo.cqrs.orderhistory.dynamodb.Order; diff --git a/ftgo-order-history-service/src/test/java/net/chrisrichardson/ftgo/orderhistory/contracts/OrderHistoryEventHandlersTest.java b/ftgo-order-history-service/src/test/java/net/chrisrichardson/ftgo/orderhistory/contracts/OrderHistoryEventHandlersTest.java index a8bca9c5..08c94b90 100644 --- a/ftgo-order-history-service/src/test/java/net/chrisrichardson/ftgo/orderhistory/contracts/OrderHistoryEventHandlersTest.java +++ b/ftgo-order-history-service/src/test/java/net/chrisrichardson/ftgo/orderhistory/contracts/OrderHistoryEventHandlersTest.java @@ -1,10 +1,11 @@ package net.chrisrichardson.ftgo.orderhistory.contracts; -import io.eventuate.tram.commands.common.ChannelMapping; -import io.eventuate.tram.commands.common.DefaultChannelMapping; -import io.eventuate.tram.commands.producer.TramCommandProducerConfiguration; -import io.eventuate.tram.inmemory.TramInMemoryConfiguration; -import io.eventuate.tram.springcloudcontractsupport.EventuateContractVerifierConfiguration; +import io.eventuate.tram.spring.commands.producer.TramCommandProducerConfiguration; +import io.eventuate.tram.spring.consumer.common.TramNoopDuplicateMessageDetectorConfiguration; +import io.eventuate.tram.spring.inmemory.TramInMemoryCommonConfiguration; +import io.eventuate.tram.messaging.common.ChannelMapping; +import io.eventuate.tram.messaging.common.DefaultChannelMapping; +import io.eventuate.tram.spring.cloudcontractsupport.EventuateContractVerifierConfiguration; import net.chrisrichardson.ftgo.cqrs.orderhistory.OrderHistoryDao; import net.chrisrichardson.ftgo.cqrs.orderhistory.dynamodb.Order; import net.chrisrichardson.ftgo.cqrs.orderhistory.dynamodb.SourceEvent; @@ -45,7 +46,9 @@ public class OrderHistoryEventHandlersTest { @EnableAutoConfiguration @Import({OrderHistoryServiceMessagingConfiguration.class, TramCommandProducerConfiguration.class, - TramInMemoryConfiguration.class, EventuateContractVerifierConfiguration.class}) + TramInMemoryCommonConfiguration.class, + TramNoopDuplicateMessageDetectorConfiguration.class, + EventuateContractVerifierConfiguration.class}) public static class TestConfiguration { @Bean diff --git a/ftgo-order-history-service/src/test/resources/application.properties b/ftgo-order-history-service/src/test/resources/application.properties index 8e16f137..86a97ad3 100644 --- a/ftgo-order-history-service/src/test/resources/application.properties +++ b/ftgo-order-history-service/src/test/resources/application.properties @@ -8,12 +8,10 @@ stubrunner.integration.enabled=false spring.datasource.url=jdbc:mysql://${DOCKER_HOST_IP:localhost}/eventuate spring.datasource.username=mysqluser spring.datasource.password=mysqlpw -spring.datasource.driver.class.name=com.mysql.jdbc.Driver +spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.data.mongodb.uri=mongodb://${DOCKER_HOST_IP:localhost}/bankingexampledb eventuatelocal.kafka.bootstrap.servers=${DOCKER_HOST_IP:localhost}:9092 -eventuatelocal.cdc.db.user.name=root -eventuatelocal.cdc.db.password=rootpassword eventuatelocal.zookeeper.connection.string=${DOCKER_HOST_IP:localhost}:2181 aws.access.key_id=id_key diff --git a/ftgo-order-service-api/build.gradle b/ftgo-order-service-api/build.gradle index 2dc958aa..f71c7d39 100644 --- a/ftgo-order-service-api/build.gradle +++ b/ftgo-order-service-api/build.gradle @@ -1,5 +1,5 @@ dependencies { - - compile "io.eventuate.tram.sagas:eventuate-jpa-sagas-framework:$eventuateTramSagasVersion" + compile "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion" + compile "io.eventuate.tram.core:eventuate-tram-spring-events" compile project(":ftgo-common") -} \ No newline at end of file +} diff --git a/ftgo-order-service-api/src/main/java/net/chrisrichardson/ftgo/orderservice/api/OrderServiceChannels.java b/ftgo-order-service-api/src/main/java/net/chrisrichardson/ftgo/orderservice/api/OrderServiceChannels.java index 580d03f1..43c335aa 100644 --- a/ftgo-order-service-api/src/main/java/net/chrisrichardson/ftgo/orderservice/api/OrderServiceChannels.java +++ b/ftgo-order-service-api/src/main/java/net/chrisrichardson/ftgo/orderservice/api/OrderServiceChannels.java @@ -1,5 +1,7 @@ package net.chrisrichardson.ftgo.orderservice.api; public class OrderServiceChannels { - public static final String orderServiceChannel = "orderService"; + public static final String COMMAND_CHANNEL = "orderService"; + public static final String ORDER_EVENT_CHANNEL = "net.chrisrichardson.ftgo.orderservice.domain.Order"; + } diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderAuthorized.java b/ftgo-order-service-api/src/main/java/net/chrisrichardson/ftgo/orderservice/api/events/OrderAuthorized.java similarity index 67% rename from ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderAuthorized.java rename to ftgo-order-service-api/src/main/java/net/chrisrichardson/ftgo/orderservice/api/events/OrderAuthorized.java index 1cab1c38..efb04943 100644 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderAuthorized.java +++ b/ftgo-order-service-api/src/main/java/net/chrisrichardson/ftgo/orderservice/api/events/OrderAuthorized.java @@ -1,7 +1,5 @@ -package net.chrisrichardson.ftgo.orderservice.domain; +package net.chrisrichardson.ftgo.orderservice.api.events; -import io.eventuate.tram.events.common.DomainEvent; -import net.chrisrichardson.ftgo.orderservice.api.events.OrderDomainEvent; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderCancelled.java b/ftgo-order-service-api/src/main/java/net/chrisrichardson/ftgo/orderservice/api/events/OrderCancelled.java similarity index 76% rename from ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderCancelled.java rename to ftgo-order-service-api/src/main/java/net/chrisrichardson/ftgo/orderservice/api/events/OrderCancelled.java index 8113eb67..94ffb8a2 100644 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderCancelled.java +++ b/ftgo-order-service-api/src/main/java/net/chrisrichardson/ftgo/orderservice/api/events/OrderCancelled.java @@ -1,4 +1,4 @@ -package net.chrisrichardson.ftgo.orderservice.domain; +package net.chrisrichardson.ftgo.orderservice.api.events; import io.eventuate.tram.events.common.DomainEvent; import net.chrisrichardson.ftgo.orderservice.api.events.OrderDomainEvent; diff --git a/ftgo-order-service-api/src/main/java/net/chrisrichardson/ftgo/orderservice/api/events/OrderCreatedEvent.java b/ftgo-order-service-api/src/main/java/net/chrisrichardson/ftgo/orderservice/api/events/OrderCreatedEvent.java index 7a53e858..646fa119 100644 --- a/ftgo-order-service-api/src/main/java/net/chrisrichardson/ftgo/orderservice/api/events/OrderCreatedEvent.java +++ b/ftgo-order-service-api/src/main/java/net/chrisrichardson/ftgo/orderservice/api/events/OrderCreatedEvent.java @@ -1,19 +1,22 @@ package net.chrisrichardson.ftgo.orderservice.api.events; +import net.chrisrichardson.ftgo.common.Address; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; import org.apache.commons.lang.builder.ToStringBuilder; public class OrderCreatedEvent implements OrderDomainEvent { private OrderDetails orderDetails; + private Address deliveryAddress; private String restaurantName; private OrderCreatedEvent() { } - public OrderCreatedEvent(OrderDetails orderDetails, String restaurantName) { + public OrderCreatedEvent(OrderDetails orderDetails, Address deliveryAddress, String restaurantName) { this.orderDetails = orderDetails; + this.deliveryAddress = deliveryAddress; this.restaurantName = restaurantName; } @@ -33,6 +36,14 @@ public void setRestaurantName(String restaurantName) { this.restaurantName = restaurantName; } + public Address getDeliveryAddress() { + return deliveryAddress; + } + + public void setDeliveryAddress(Address deliveryAddress) { + this.deliveryAddress = deliveryAddress; + } + @Override public String toString() { return ToStringBuilder.reflectionToString(this); diff --git a/ftgo-order-service-api/src/main/java/net/chrisrichardson/ftgo/orderservice/api/events/OrderRejected.java b/ftgo-order-service-api/src/main/java/net/chrisrichardson/ftgo/orderservice/api/events/OrderRejected.java new file mode 100644 index 00000000..696a5c9d --- /dev/null +++ b/ftgo-order-service-api/src/main/java/net/chrisrichardson/ftgo/orderservice/api/events/OrderRejected.java @@ -0,0 +1,4 @@ +package net.chrisrichardson.ftgo.orderservice.api.events; + +public class OrderRejected implements OrderDomainEvent { +} diff --git a/ftgo-order-service-api/src/main/java/net/chrisrichardson/ftgo/orderservice/api/web/CreateOrderRequest.java b/ftgo-order-service-api/src/main/java/net/chrisrichardson/ftgo/orderservice/api/web/CreateOrderRequest.java index e735f903..706ce6b3 100644 --- a/ftgo-order-service-api/src/main/java/net/chrisrichardson/ftgo/orderservice/api/web/CreateOrderRequest.java +++ b/ftgo-order-service-api/src/main/java/net/chrisrichardson/ftgo/orderservice/api/web/CreateOrderRequest.java @@ -1,16 +1,23 @@ package net.chrisrichardson.ftgo.orderservice.api.web; +import net.chrisrichardson.ftgo.common.Address; + +import java.time.LocalDateTime; import java.util.List; public class CreateOrderRequest { private long restaurantId; private long consumerId; + private LocalDateTime deliveryTime; private List lineItems; + private Address deliveryAddress; - public CreateOrderRequest(long consumerId, long restaurantId, List lineItems) { + public CreateOrderRequest(long consumerId, long restaurantId, Address deliveryAddress, LocalDateTime deliveryTime, List lineItems) { this.restaurantId = restaurantId; this.consumerId = consumerId; + this.deliveryAddress = deliveryAddress; + this.deliveryTime = deliveryTime; this.lineItems = lineItems; } @@ -42,6 +49,22 @@ public void setLineItems(List lineItems) { this.lineItems = lineItems; } + public Address getDeliveryAddress() { + return deliveryAddress; + } + + public void setDeliveryAddress(Address deliveryAddress) { + this.deliveryAddress = deliveryAddress; + } + + public LocalDateTime getDeliveryTime() { + return deliveryTime; + } + + public void setDeliveryTime(LocalDateTime deliveryTime) { + this.deliveryTime = deliveryTime; + } + public static class LineItem { private String menuItemId; diff --git a/ftgo-order-service-api/src/main/java/net/chrisrichardson/ftgo/orderservice/api/web/ReviseOrderRequest.java b/ftgo-order-service-api/src/main/java/net/chrisrichardson/ftgo/orderservice/api/web/ReviseOrderRequest.java index 99d89c82..77f15691 100644 --- a/ftgo-order-service-api/src/main/java/net/chrisrichardson/ftgo/orderservice/api/web/ReviseOrderRequest.java +++ b/ftgo-order-service-api/src/main/java/net/chrisrichardson/ftgo/orderservice/api/web/ReviseOrderRequest.java @@ -1,22 +1,24 @@ package net.chrisrichardson.ftgo.orderservice.api.web; -import java.util.Map; +import net.chrisrichardson.ftgo.common.RevisedOrderLineItem; + +import java.util.List; public class ReviseOrderRequest { - private Map revisedLineItemQuantities; + private List revisedOrderLineItems; private ReviseOrderRequest() { } - public ReviseOrderRequest(Map revisedLineItemQuantities) { - this.revisedLineItemQuantities = revisedLineItemQuantities; + public ReviseOrderRequest(List revisedOrderLineItems) { + this.revisedOrderLineItems = revisedOrderLineItems; } - public Map getRevisedLineItemQuantities() { - return revisedLineItemQuantities; + public List getRevisedOrderLineItems() { + return revisedOrderLineItems; } - public void setRevisedLineItemQuantities(Map revisedLineItemQuantities) { - this.revisedLineItemQuantities = revisedLineItemQuantities; + public void setRevisedOrderLineItems(List revisedOrderLineItems) { + this.revisedOrderLineItems = revisedOrderLineItems; } } diff --git a/ftgo-order-service-contracts/build.gradle b/ftgo-order-service-contracts/build.gradle index 31921037..8e4e6077 100644 --- a/ftgo-order-service-contracts/build.gradle +++ b/ftgo-order-service-contracts/build.gradle @@ -1,15 +1,3 @@ -buildscript { - dependencies { - // if using Stub Runner (consumer side) only remove this dependency - classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:$springCloudContractDependenciesVersion" - } - repositories { - mavenCentral() - maven { - url 'https://repo.spring.io/libs-milestone' - } - } -} apply plugin: 'spring-cloud-contract' apply plugin: 'maven-publish' @@ -30,4 +18,8 @@ contracts { generateContractTests.enabled = false -build.finalizedBy(publish) \ No newline at end of file +build.finalizedBy(publish) + +dependencies { + compile 'org.codehaus.groovy:groovy-all:2.4.6' +} diff --git a/ftgo-order-service-contracts/src/main/resources/contracts/deliveryservice/messaging/OrderCreatedEvent.groovy b/ftgo-order-service-contracts/src/main/resources/contracts/deliveryservice/messaging/OrderCreatedEvent.groovy new file mode 100644 index 00000000..9ed0625a --- /dev/null +++ b/ftgo-order-service-contracts/src/main/resources/contracts/deliveryservice/messaging/OrderCreatedEvent.groovy @@ -0,0 +1,18 @@ +package deliveryservice.messaging; + +org.springframework.cloud.contract.spec.Contract.make { + label 'orderCreatedForDeliveryService' + input { + triggeredBy('orderCreatedEvent()') + } + + outputMessage { + sentTo('net.chrisrichardson.ftgo.orderservice.domain.Order') + body('''{"orderDetails":{"lineItems":[{"quantity":5,"menuItemId":"1","name":"Chicken Vindaloo","price":"12.34","total":"61.70"}],"orderTotal":"61.70","restaurantId":1, "consumerId":1511300065921}, "deliveryAddress":{ "street1" : "9 Amazing View", "city" : "Oakland", "state" : "CA", "zip" : "94612", }, "restaurantName" : "Ajanta"}''') + headers { + header('event-aggregate-type', 'net.chrisrichardson.ftgo.orderservice.domain.Order') + header('event-type', 'net.chrisrichardson.ftgo.orderservice.api.events.OrderCreatedEvent') + header('event-aggregate-id', '99') // Matches OrderDetailsMother.ORDER_ID + } + } +} \ No newline at end of file diff --git a/ftgo-order-service-contracts/src/main/resources/contracts/http/GetOrder.groovy b/ftgo-order-service-contracts/src/main/resources/contracts/http/GetOrder.groovy index 65db5352..5da9763c 100644 --- a/ftgo-order-service-contracts/src/main/resources/contracts/http/GetOrder.groovy +++ b/ftgo-order-service-contracts/src/main/resources/contracts/http/GetOrder.groovy @@ -8,7 +8,7 @@ org.springframework.cloud.contract.spec.Contract.make { response { status 200 headers { - header('Content-Type': 'application/json;charset=UTF-8') + header('Content-Type': 'application/json') } body('''{"orderId" : "99", "state" : "APPROVAL_PENDING"}''') } diff --git a/ftgo-order-service/Dockerfile b/ftgo-order-service/Dockerfile index 0e9bd7ac..e6018afb 100644 --- a/ftgo-order-service/Dockerfile +++ b/ftgo-order-service/Dockerfile @@ -1,5 +1,3 @@ -FROM openjdk:8u171-jre-alpine -RUN apk --no-cache add curl -CMD java ${JAVA_OPTS} -jar ftgo-order-service.jar -HEALTHCHECK --start-period=30s --interval=5s CMD curl -f http://localhost:8080/actuator/health || exit 1 -COPY build/libs/ftgo-order-service.jar . +ARG baseImageVersion +FROM eventuateio/eventuate-examples-docker-images-spring-example-base-image:$baseImageVersion +COPY build/libs/ftgo-order-service.jar service.jar diff --git a/ftgo-order-service/build.gradle b/ftgo-order-service/build.gradle index e64b3cf4..94734f88 100644 --- a/ftgo-order-service/build.gradle +++ b/ftgo-order-service/build.gradle @@ -6,11 +6,19 @@ buildscript { maven { url 'https://repo.spring.io/libs-milestone' } + eventuateMavenRepoUrl.split(',').each { repoUrl -> maven { url repoUrl } } } dependencies { classpath "org.springframework.cloud:spring-cloud-contract-gradle-plugin:$springCloudContractDependenciesVersion" classpath "com.avast.gradle:gradle-docker-compose-plugin:$dockerComposePluginVersion" - classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.0' + classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.10' + + classpath "org.jsonschema2pojo:jsonschema2pojo-gradle-plugin:${js2pVersion}" + classpath "io.eventuate.tram.core:eventuate-tram-spring-commands" + classpath "io.eventuate.tram.core:eventuate-tram-spring-events" + + classpath(platform("io.eventuate.platform:eventuate-platform-dependencies:$eventuatePlatformVersion")) + } } @@ -23,6 +31,8 @@ apply plugin: 'com.google.protobuf' apply plugin: IntegrationTestsPlugin apply plugin: ComponentTestsPlugin +apply plugin: FtgoJSONSchema2PojoPlugin + dependencyManagement { imports { mavenBom "org.springframework.cloud:spring-cloud-contract-dependencies:$springCloudContractDependenciesVersion" @@ -33,9 +43,9 @@ contracts { contractsDslDir = new File("../ftgo-order-service-contracts/src/main/resources/contracts") packageWithBaseClasses = 'net.chrisrichardson.ftgo.orderservice.contract' generatedTestSourcesDir = project.file("${project.buildDir}/generated-integration-test-sources/contracts") + sourceSet = "integrationTest" } - sourceSets { integrationTest { java { @@ -51,31 +61,42 @@ componentTest.dependsOn(assemble) dockerCompose { - startedServices = [ 'not-used'] + environment.put "EVENTUATE_COMMON_VERSION", eventuateCommonImageVersion + environment.put "EVENTUATE_CDC_VERSION", eventuateCdcImageVersion + environment.put "EVENTUATE_SAGA_VERSION", eventuateTramSagasImageVersion + environment.put "EVENTUATE_JAVA_BASE_IMAGE_VERSION", eventuateExamplesBaseImageVersion + environment.put "EVENTUATE_MESSAGING_KAFKA_IMAGE_VERSION", eventuateMessagingKafkaImageVersion - componentTests { - if (System.getenv("FTGO_DOCKER_COMPOSE_FILES") != null) - useComposeFiles = System.getenv("FTGO_DOCKER_COMPOSE_FILES").split(",").collect { "../" + it } - startedServices = [ 'ftgo-order-service'] - stopContainers = false - } + projectName = null integrationTests { - if (System.getenv("FTGO_DOCKER_COMPOSE_FILES") != null) - useComposeFiles = System.getenv("FTGO_DOCKER_COMPOSE_FILES").split(",").collect { "../" + it } - + projectName = null + removeOrphans = true + retainContainersOnStartupFailure = true startedServices = [ 'mysql'] - stopContainers = false + stopContainers = true } + + componentTests { + projectName = null + removeOrphans = true + retainContainersOnStartupFailure = true + startedServices = [ 'ftgo-order-service'] + //forceRecreate = true + stopContainers = true + } + } -componentTest.dependsOn(componentTestsComposeUp) integrationTest.dependsOn(integrationTestsComposeUp) +componentTestsComposeUp.dependsOn(integrationTestsComposeUp) +componentTest.dependsOn(componentTestsComposeUp) + protobuf { protoc { // Download from repositories - artifact = 'com.google.protobuf:protoc:3.0.0' + artifact = "com.google.protobuf:protoc:$protocVersion" } plugins { grpc { @@ -102,18 +123,40 @@ idea { } } +// This is already done by ComponentTestsPlugin +// apply plugin: 'eclipse' + +eclipse { + sourceSets { + main { + java { + srcDirs += ["build/generated/source/proto/main/java", "build/generated/source/proto/main/grpc"] + } + } + } +} dependencies { + compile ("io.netty:netty-codec-http2:4.1.72.Final") + compile ("io.netty:netty-codec:4.1.72.Final") + compile ("io.netty:netty-transport:4.1.72.Final") + compile ("io.netty:netty-common:4.1.72.Final") + compile ("io.netty:netty-buffer:4.1.72.Final") + compile ("io.netty:netty-resolver:4.1.72.Final") + + ftgoApiSpecification project(":ftgo-consumer-service-api-spec") + ftgoApiSpecification project(":ftgo-accounting-service-api-spec") + ftgoApiSpecification project(":ftgo-restaurant-service-api-spec") compile project(":common-swagger") compile project(":ftgo-common-jpa") - compile "io.eventuate.tram.core:eventuate-tram-aggregate-domain-events:$eventuateTramVersion" - compile "io.eventuate.tram.core:eventuate-tram-jdbc-kafka:$eventuateTramVersion" - compile "io.eventuate.tram.core:eventuate-tram-commands:$eventuateTramVersion" + compile "io.eventuate.tram.core:eventuate-tram-aggregate-domain-events" + compile "io.eventuate.tram.core:eventuate-tram-spring-jdbc-kafka" + compile "io.eventuate.tram.core:eventuate-tram-spring-commands" - compile "io.eventuate.tram.sagas:eventuate-tram-sagas-simple-dsl:$eventuateTramSagasVersion" + compile "io.eventuate.tram.sagas:eventuate-tram-sagas-spring-orchestration-simple-dsl" compile project(":ftgo-accounting-service-api") compile project(":ftgo-consumer-service-api") @@ -121,6 +164,7 @@ dependencies { compile project(":ftgo-restaurant-service-api") compile project(":ftgo-order-service-api") + compile "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion" compile "org.springframework.boot:spring-boot-starter-actuator:$springBootVersion" compile "io.micrometer:micrometer-registry-prometheus:$micrometerVersion" @@ -131,10 +175,19 @@ dependencies { compile "io.grpc:grpc-protobuf:${grpcVersion}" compile "io.grpc:grpc-stub:${grpcVersion}" - testCompile "io.eventuate.util:eventuate-util-test:$eventuateUtilVersion" - testCompile "io.eventuate.tram.core:eventuate-tram-test-util:$eventuateTramVersion" + compile "io.microservices.tools.canvas:microservice-canvas-springmvc:$microserviceCanvasVersion" + compile "io.microservices.tools.canvas:microservice-canvas-extractor-tram-sagas:$microserviceCanvasVersion" + compile "io.eventuate.tram.core:eventuate-tram-spring-messaging" + compile "io.eventuate.tram.sagas:eventuate-tram-sagas-spring-orchestration" + + compile('org.apache.kafka:kafka-clients:2.3.0') { + force = true + } - testCompile "io.eventuate.tram.sagas:eventuate-tram-sagas-in-memory:$eventuateTramSagasVersion" + testCompile "io.eventuate.util:eventuate-util-test" + testCompile "io.eventuate.tram.core:eventuate-tram-test-util" + + testCompile "io.eventuate.tram.sagas:eventuate-tram-sagas-spring-in-memory" testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion" // Added this stuff @@ -148,13 +201,44 @@ dependencies { testCompile 'io.rest-assured:spring-mock-mvc:3.0.6' testCompile "io.rest-assured:json-path:3.0.6" - testCompile "io.eventuate.tram.core:eventuate-tram-testing-support-spring-cloud-contract:$eventuateTramVersion" + testCompile "io.eventuate.tram.core:eventuate-tram-spring-testing-support-cloud-contract" - testCompile "io.eventuate.tram.sagas:eventuate-tram-sagas-testing-support:$eventuateTramSagasVersion" + testCompile "io.eventuate.tram.sagas:eventuate-tram-sagas-spring-testing-support" componentTestCompile 'info.cukes:cucumber-java:1.2.5' componentTestCompile 'info.cukes:cucumber-junit:1.2.5' componentTestCompile 'info.cukes:cucumber-spring:1.2.5' + testCompile project(":ftgo-test-util") + testCompile "org.hamcrest:hamcrest:2.1" + + integrationTestCompile "io.eventuate.tram.sagas:eventuate-tram-sagas-spring-testing-support-cloud-contract" + integrationTestCompile "io.eventuate.tram.core:eventuate-tram-spring-in-memory" + +} + +ftgoJsonSchema2Pojo { + + ftgoConsumerService { + source = files("${ftgoApiSpecsDir}/ValidateOrderByConsumer.json") + targetPackage = "net.chrisrichardson.ftgo.consumerservice.api" + includeAdditionalProperties = false + generateBuilders = true + useLongIntegers = true + } + ftgoAccountingService { + source = files("${ftgoApiSpecsDir}/messages") + targetPackage = "net.chrisrichardson.ftgo.accountservice.api" + includeAdditionalProperties = false + generateBuilders = true + useLongIntegers = true + } + ftgoRestaurantService { + source = files("${ftgoApiSpecsDir}/ftgo-restaurant-service-api-spec/messages") + targetPackage = "net.chrisrichardson.ftgo.restaurantservice.events" + includeAdditionalProperties = false + generateBuilders = true + useLongIntegers = true + } } diff --git a/ftgo-order-service/src/attic/AbstractOrderServiceComponentTest.java b/ftgo-order-service/src/attic/AbstractOrderServiceComponentTest.java index dbb6cb2a..677e40e5 100644 --- a/ftgo-order-service/src/attic/AbstractOrderServiceComponentTest.java +++ b/ftgo-order-service/src/attic/AbstractOrderServiceComponentTest.java @@ -1,21 +1,20 @@ package net.chrisrichardson.ftgo.orderservice; -import io.eventuate.tram.commands.producer.TramCommandProducerConfiguration; +import io.eventuate.tram.spring.commands.producer.TramCommandProducerConfiguration; import io.eventuate.tram.events.publisher.DomainEventPublisher; import io.eventuate.tram.messaging.consumer.MessageConsumer; import io.eventuate.util.test.async.Eventually; import net.chrisrichardson.ftgo.accountservice.api.AuthorizeCommand; import net.chrisrichardson.ftgo.consumerservice.api.ValidateOrderByConsumer; +import net.chrisrichardson.ftgo.kitchenservice.api.ConfirmCreateTicket; +import net.chrisrichardson.ftgo.kitchenservice.api.CreateTicket; +import net.chrisrichardson.ftgo.kitchenservice.api.CreateTicketReply; import net.chrisrichardson.ftgo.orderservice.api.web.CreateOrderRequest; import net.chrisrichardson.ftgo.orderservice.domain.RestaurantRepository; import net.chrisrichardson.ftgo.orderservice.messaging.OrderServiceMessagingConfiguration; import net.chrisrichardson.ftgo.orderservice.sagaparticipants.ApproveOrderCommand; import net.chrisrichardson.ftgo.orderservice.service.OrderCommandHandlersConfiguration; import net.chrisrichardson.ftgo.orderservice.web.OrderWebConfiguration; -import net.chrisrichardson.ftgo.kitchenservice.api.ConfirmCreateTicket; -import net.chrisrichardson.ftgo.kitchenservice.api.CreateTicket; -import net.chrisrichardson.ftgo.kitchenservice.api.CreateTicketReply; -import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantCreated; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -27,7 +26,6 @@ import static io.eventuate.tram.commands.consumer.CommandHandlerReplyBuilder.withSuccess; import static io.restassured.RestAssured.given; -import static net.chrisrichardson.ftgo.orderservice.RestaurantMother.AJANTA_RESTAURANT_MENU; import static org.junit.Assert.assertNotNull; public abstract class AbstractOrderServiceComponentTest { @@ -92,7 +90,7 @@ public void shouldCreateOrder() { domainEventPublisher.publish("net.chrisrichardson.ftgo.restaurantservice.domain.Restaurant", RestaurantMother.AJANTA_ID, - Collections.singletonList(new RestaurantCreated(RestaurantMother.AJANTA_RESTAURANT_NAME, AJANTA_RESTAURANT_MENU))); + Collections.singletonList(RestaurantMother.makeAjantaRestaurantCreatedEvent())); Eventually.eventually(() -> { diff --git a/ftgo-order-service/src/attic/OrderServiceExternalComponentTest.java b/ftgo-order-service/src/attic/OrderServiceExternalComponentTest.java index 995f0fb8..b3a28dec 100644 --- a/ftgo-order-service/src/attic/OrderServiceExternalComponentTest.java +++ b/ftgo-order-service/src/attic/OrderServiceExternalComponentTest.java @@ -1,8 +1,8 @@ package net.chrisrichardson.ftgo.orderservice; -import io.eventuate.jdbckafka.TramJdbcKafkaConfiguration; -import io.eventuate.tram.commands.producer.TramCommandProducerConfiguration; -import io.eventuate.tram.events.publisher.TramEventsPublisherConfiguration; +import io.eventuate.tram.spring.jdbckafka.TramJdbcKafkaConfiguration; +import io.eventuate.tram.spring.commands.producer.TramCommandProducerConfiguration; +import io.eventuate.tram.spring.events.publisher.TramEventsPublisherConfiguration; import net.chrisrichardson.ftgo.common.CommonJsonMapperInitializer; import org.junit.runner.RunWith; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -24,7 +24,7 @@ public class OrderServiceExternalComponentTest extends AbstractOrderServiceCompo } private int port = 8082; - private String host = System.getenv("DOCKER_HOST_IP"); + private String host = FtgoTestUtil.getDockerHostIp(); @Override protected String baseUrl(String path) { diff --git a/ftgo-order-service/src/attic/OrderServiceInProcessComponentTest.java b/ftgo-order-service/src/attic/OrderServiceInProcessComponentTest.java index 5ffc3806..8d3f4d7b 100644 --- a/ftgo-order-service/src/attic/OrderServiceInProcessComponentTest.java +++ b/ftgo-order-service/src/attic/OrderServiceInProcessComponentTest.java @@ -1,6 +1,6 @@ package net.chrisrichardson.ftgo.orderservice; -import io.eventuate.tram.inmemory.TramInMemoryConfiguration; +import io.eventuate.tram.spring.inmemory.TramInMemoryConfiguration; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; diff --git a/ftgo-order-service/src/attic/OrderServiceOutOfProcessComponentTest.java b/ftgo-order-service/src/attic/OrderServiceOutOfProcessComponentTest.java index 12baf44f..71a25091 100644 --- a/ftgo-order-service/src/attic/OrderServiceOutOfProcessComponentTest.java +++ b/ftgo-order-service/src/attic/OrderServiceOutOfProcessComponentTest.java @@ -1,6 +1,6 @@ package net.chrisrichardson.ftgo.orderservice; -import io.eventuate.jdbckafka.TramJdbcKafkaConfiguration; +import io.eventuate.tram.spring.jdbckafka.TramJdbcKafkaConfiguration; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; diff --git a/ftgo-order-service/src/attic/OrderServiceOutOfProcessComponentV0Test.java b/ftgo-order-service/src/attic/OrderServiceOutOfProcessComponentV0Test.java index cbedce7e..bafa7e0d 100644 --- a/ftgo-order-service/src/attic/OrderServiceOutOfProcessComponentV0Test.java +++ b/ftgo-order-service/src/attic/OrderServiceOutOfProcessComponentV0Test.java @@ -2,9 +2,9 @@ import io.eventuate.tram.commands.common.ChannelMapping; import io.eventuate.tram.commands.common.DefaultChannelMapping; -import io.eventuate.tram.commands.producer.TramCommandProducerConfiguration; +import io.eventuate.tram.spring.commands.producer.TramCommandProducerConfiguration; import io.eventuate.tram.events.publisher.DomainEventPublisher; -import io.eventuate.tram.inmemory.TramInMemoryConfiguration; +import io.eventuate.tram.spring.inmemory.TramInMemoryConfiguration; import io.eventuate.util.test.async.Eventually; import net.chrisrichardson.ftgo.orderservice.api.events.OrderState; import net.chrisrichardson.ftgo.orderservice.domain.Order; @@ -14,8 +14,6 @@ import net.chrisrichardson.ftgo.orderservice.messaging.OrderServiceMessagingConfiguration; import net.chrisrichardson.ftgo.orderservice.service.OrderCommandHandlersConfiguration; import net.chrisrichardson.ftgo.orderservice.web.OrderWebConfiguration; -import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantCreated; -import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantMenu; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -105,8 +103,7 @@ private String baseUrl(String path) { @Test public void shouldCreateOrder() throws InterruptedException { domainEventPublisher.publish("net.chrisrichardson.ftgo.restaurantservice.domain.Restaurant", RestaurantMother.AJANTA_ID, - Collections.singletonList(new RestaurantCreated(RestaurantMother.AJANTA_RESTAURANT_NAME, - new RestaurantMenu(Collections.singletonList(RestaurantMother.CHICKEN_VINDALOO_MENU_ITEM))))); + Collections.singletonList(RestaurantMother.makeAjantaRestaurantCreatedEvent())); Eventually.eventually(() -> { FtgoTestUtil.assertPresent(restaurantRepository.findById(RestaurantMother.AJANTA_ID)); diff --git a/ftgo-order-service/src/component-test/java/net/chrisrichardson/ftgo/orderservice/cucumber/OrderServiceComponentTestStepDefinitions.java b/ftgo-order-service/src/component-test/java/net/chrisrichardson/ftgo/orderservice/cucumber/OrderServiceComponentTestStepDefinitions.java index 7758c088..803e969c 100644 --- a/ftgo-order-service/src/component-test/java/net/chrisrichardson/ftgo/orderservice/cucumber/OrderServiceComponentTestStepDefinitions.java +++ b/ftgo-order-service/src/component-test/java/net/chrisrichardson/ftgo/orderservice/cucumber/OrderServiceComponentTestStepDefinitions.java @@ -5,30 +5,26 @@ import cucumber.api.java.en.Given; import cucumber.api.java.en.Then; import cucumber.api.java.en.When; -import io.eventuate.jdbckafka.TramJdbcKafkaConfiguration; -import io.eventuate.tram.commands.common.ChannelMapping; -import io.eventuate.tram.commands.common.DefaultChannelMapping; -import io.eventuate.tram.events.common.DomainEvent; +import io.eventuate.tram.spring.jdbckafka.TramJdbcKafkaConfiguration; import io.eventuate.tram.events.publisher.DomainEventPublisher; import io.eventuate.tram.messaging.consumer.MessageConsumer; -import io.eventuate.tram.sagas.testing.SagaParticipantStubManagerConfiguration; +import io.eventuate.tram.sagas.testing.SagaParticipantChannels; import io.eventuate.tram.sagas.testing.SagaParticipantStubManager; +import io.eventuate.tram.sagas.spring.testing.SagaParticipantStubManagerConfiguration; import io.eventuate.tram.testing.MessageTracker; -import io.eventuate.tram.sagas.testing.SagaParticipantChannels; import io.restassured.response.Response; import net.chrisrichardson.ftgo.accountservice.api.AuthorizeCommand; import net.chrisrichardson.ftgo.common.CommonJsonMapperInitializer; import net.chrisrichardson.ftgo.consumerservice.api.ValidateOrderByConsumer; +import net.chrisrichardson.ftgo.kitchenservice.api.CancelCreateTicket; +import net.chrisrichardson.ftgo.kitchenservice.api.ConfirmCreateTicket; +import net.chrisrichardson.ftgo.kitchenservice.api.CreateTicket; +import net.chrisrichardson.ftgo.kitchenservice.api.CreateTicketReply; import net.chrisrichardson.ftgo.orderservice.OrderDetailsMother; import net.chrisrichardson.ftgo.orderservice.RestaurantMother; import net.chrisrichardson.ftgo.orderservice.api.web.CreateOrderRequest; import net.chrisrichardson.ftgo.orderservice.domain.Order; import net.chrisrichardson.ftgo.orderservice.domain.RestaurantRepository; -import net.chrisrichardson.ftgo.kitchenservice.api.CancelCreateTicket; -import net.chrisrichardson.ftgo.kitchenservice.api.ConfirmCreateTicket; -import net.chrisrichardson.ftgo.kitchenservice.api.CreateTicket; -import net.chrisrichardson.ftgo.kitchenservice.api.CreateTicketReply; -import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantCreated; import net.chrisrichardson.ftgo.testutil.FtgoTestUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -40,16 +36,14 @@ import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.test.context.ContextConfiguration; +import java.util.Arrays; import java.util.Collections; import static io.eventuate.tram.commands.consumer.CommandHandlerReplyBuilder.withSuccess; import static io.eventuate.util.test.async.Eventually.eventually; import static io.restassured.RestAssured.given; import static java.util.Collections.singleton; -import static net.chrisrichardson.ftgo.orderservice.RestaurantMother.AJANTA_RESTAURANT_MENU; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; +import static org.junit.Assert.*; @SpringBootTest(classes = OrderServiceComponentTestStepDefinitions.TestConfiguration.class, webEnvironment = SpringBootTest.WebEnvironment.NONE) @@ -66,7 +60,7 @@ public class OrderServiceComponentTestStepDefinitions { } private int port = 8082; - private String host = System.getenv("DOCKER_HOST_IP"); + private String host = FtgoTestUtil.getDockerHostIp(); protected String baseUrl(String path) { return String.format("http://%s:%s%s", host, port, path); @@ -89,11 +83,6 @@ public MessageTracker messageTracker(MessageConsumer messageConsumer) { return new MessageTracker(singleton("net.chrisrichardson.ftgo.orderservice.domain.Order"), messageConsumer) ; } - @Bean - public ChannelMapping channelMapping() { - return new DefaultChannelMapping.DefaultChannelMappingBuilder().build(); - } - } @Autowired @@ -151,7 +140,7 @@ public void restaurantAcceptsOrder() { if (!restaurantRepository.findById(RestaurantMother.AJANTA_ID).isPresent()) { domainEventPublisher.publish("net.chrisrichardson.ftgo.restaurantservice.domain.Restaurant", RestaurantMother.AJANTA_ID, - Collections.singletonList(new RestaurantCreated(RestaurantMother.AJANTA_RESTAURANT_NAME, AJANTA_RESTAURANT_MENU))); + Collections.singletonList(RestaurantMother.makeAjantaRestaurantCreatedEvent())); eventually(() -> { FtgoTestUtil.assertPresent(restaurantRepository.findById(RestaurantMother.AJANTA_ID)); @@ -164,7 +153,7 @@ public void placeOrder() { response = given(). body(new CreateOrderRequest(consumerId, - RestaurantMother.AJANTA_ID, Collections.singletonList( + RestaurantMother.AJANTA_ID, OrderDetailsMother.DELIVERY_ADDRESS, OrderDetailsMother.DELIVERY_TIME, Collections.singletonList( new CreateOrderRequest.LineItem(RestaurantMother.CHICKEN_VINDALOO_MENU_ITEM_ID, OrderDetailsMother.CHICKEN_VINDALOO_QUANTITY)))). contentType("application/json"). @@ -208,7 +197,18 @@ public void theOrderShouldBeInState(String desiredOrderState) { @And("an (.*) event should be published") public void verifyEventPublished(String expectedEventClass) { messageTracker.assertDomainEventPublished("net.chrisrichardson.ftgo.orderservice.domain.Order", - "net.chrisrichardson.ftgo.orderservice.domain." + expectedEventClass); + findEventClass(expectedEventClass, "net.chrisrichardson.ftgo.orderservice.domain", "net.chrisrichardson.ftgo.orderservice.api.events")); + } + + private String findEventClass(String expectedEventClass, String... packages) { + return Arrays.stream(packages).map(p -> p + "." + expectedEventClass).filter(className -> { + try { + Class.forName(className); + return true; + } catch (ClassNotFoundException e) { + return false; + } + }).findFirst().orElseThrow(() -> new RuntimeException(String.format("Cannot find class %s in packages %s", expectedEventClass, String.join(",", packages)))); } } diff --git a/ftgo-order-service/src/component-test/resources/application.properties b/ftgo-order-service/src/component-test/resources/application.properties deleted file mode 100644 index 28449a1e..00000000 --- a/ftgo-order-service/src/component-test/resources/application.properties +++ /dev/null @@ -1,27 +0,0 @@ -spring.application.name=ftgo-order-service - -logging.level.root=INFO -logging.level.org.hibernate.SQL=DEBUG -logging.level.org.springframework.cloud.contract=DEBUG -logging.level.io.eventuate.tram=TRACE -#logging.level.org.springframework.orm.jpa.JpaTransactionManager=TRACE - -spring.jpa.generate-ddl=true - -stubrunner.stream.enabled=false -stubrunner.integration.enabled=false -spring.datasource.url=jdbc:mysql://${DOCKER_HOST_IP:localhost}/eventuate -spring.datasource.username=mysqluser -spring.datasource.password=mysqlpw -spring.datasource.driver.class.name=com.mysql.jdbc.Driver -spring.data.mongodb.uri=mongodb://${DOCKER_HOST_IP:localhost}/bankingexampledb - -eventuatelocal.kafka.bootstrap.servers=${DOCKER_HOST_IP:localhost}:9092 -eventuatelocal.cdc.db.user.name=root -eventuatelocal.cdc.db.password=rootpassword -eventuatelocal.zookeeper.connection.string=${DOCKER_HOST_IP:localhost}:2181 - -aws.access.key_id=id_key -aws.secret.access.key=access_key -aws.dynamodb.endpoint.url=http://${DOCKER_HOST_IP:localhost}:8000 -aws.region=us-west-2 diff --git a/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/contract/DeliveryserviceMessagingBase.java b/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/contract/DeliveryserviceMessagingBase.java new file mode 100644 index 00000000..f9bb4626 --- /dev/null +++ b/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/contract/DeliveryserviceMessagingBase.java @@ -0,0 +1,55 @@ +package net.chrisrichardson.ftgo.orderservice.contract; + +import io.eventuate.common.spring.jdbc.EventuateTransactionTemplateConfiguration; +import io.eventuate.tram.events.publisher.DomainEventPublisher; +import io.eventuate.tram.spring.events.publisher.TramEventsPublisherConfiguration; +import io.eventuate.tram.spring.inmemory.TramInMemoryConfiguration; +import io.eventuate.tram.spring.cloudcontractsupport.EventuateContractVerifierConfiguration; +import net.chrisrichardson.ftgo.common.CommonJsonMapperInitializer; +import net.chrisrichardson.ftgo.orderservice.OrderDetailsMother; +import net.chrisrichardson.ftgo.orderservice.api.events.OrderCreatedEvent; +import net.chrisrichardson.ftgo.orderservice.domain.OrderDomainEventPublisher; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.contract.verifier.messaging.boot.AutoConfigureMessageVerifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.Collections; + +import static net.chrisrichardson.ftgo.orderservice.OrderDetailsMother.CHICKEN_VINDALOO_ORDER; +import static net.chrisrichardson.ftgo.orderservice.OrderDetailsMother.CHICKEN_VINDALOO_ORDER_DETAILS; +import static net.chrisrichardson.ftgo.orderservice.RestaurantMother.AJANTA_RESTAURANT_NAME; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = DeliveryserviceMessagingBase.TestConfiguration.class, webEnvironment = SpringBootTest.WebEnvironment.NONE) +@AutoConfigureMessageVerifier +public abstract class DeliveryserviceMessagingBase { + + static { + CommonJsonMapperInitializer.registerMoneyModule(); + } + + @Configuration + @EnableAutoConfiguration + @Import({EventuateContractVerifierConfiguration.class, TramEventsPublisherConfiguration.class, TramInMemoryConfiguration.class, EventuateTransactionTemplateConfiguration.class}) + public static class TestConfiguration { + + @Bean + public OrderDomainEventPublisher orderAggregateEventPublisher(DomainEventPublisher eventPublisher) { + return new OrderDomainEventPublisher(eventPublisher); + } + } + + @Autowired + private OrderDomainEventPublisher orderAggregateEventPublisher; + + protected void orderCreatedEvent() { + orderAggregateEventPublisher.publish(CHICKEN_VINDALOO_ORDER, + Collections.singletonList(new OrderCreatedEvent(CHICKEN_VINDALOO_ORDER_DETAILS, OrderDetailsMother.DELIVERY_ADDRESS, AJANTA_RESTAURANT_NAME))); + } +} diff --git a/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/contract/HttpBase.java b/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/contract/HttpBase.java index 807d94b6..625fb5e0 100644 --- a/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/contract/HttpBase.java +++ b/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/contract/HttpBase.java @@ -1,6 +1,6 @@ package net.chrisrichardson.ftgo.orderservice.contract; -import io.eventuate.javaclient.commonimpl.JSonMapper; +import io.eventuate.common.json.mapper.JSonMapper; import io.restassured.module.mockmvc.RestAssuredMockMvc; import net.chrisrichardson.ftgo.common.CommonJsonMapperInitializer; import net.chrisrichardson.ftgo.orderservice.OrderDetailsMother; diff --git a/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/contract/MessagingBase.java b/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/contract/MessagingBase.java index 08cd06ab..7a151290 100644 --- a/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/contract/MessagingBase.java +++ b/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/contract/MessagingBase.java @@ -1,11 +1,12 @@ package net.chrisrichardson.ftgo.orderservice.contract; -import io.eventuate.tram.commands.common.ChannelMapping; -import io.eventuate.tram.commands.common.DefaultChannelMapping; +import io.eventuate.common.spring.jdbc.EventuateTransactionTemplateConfiguration; import io.eventuate.tram.events.publisher.DomainEventPublisher; -import io.eventuate.tram.events.publisher.TramEventsPublisherConfiguration; -import io.eventuate.tram.inmemory.TramInMemoryConfiguration; -import io.eventuate.tram.springcloudcontractsupport.EventuateContractVerifierConfiguration; +import io.eventuate.tram.spring.events.publisher.TramEventsPublisherConfiguration; +import io.eventuate.tram.spring.inmemory.TramInMemoryConfiguration; +import io.eventuate.tram.spring.cloudcontractsupport.EventuateContractVerifierConfiguration; +import net.chrisrichardson.ftgo.common.CommonJsonMapperInitializer; +import net.chrisrichardson.ftgo.orderservice.OrderDetailsMother; import net.chrisrichardson.ftgo.orderservice.api.events.OrderCreatedEvent; import net.chrisrichardson.ftgo.orderservice.domain.OrderDomainEventPublisher; import org.junit.runner.RunWith; @@ -29,16 +30,15 @@ @AutoConfigureMessageVerifier public abstract class MessagingBase { + static { + CommonJsonMapperInitializer.registerMoneyModule(); + } + @Configuration @EnableAutoConfiguration - @Import({EventuateContractVerifierConfiguration.class, TramEventsPublisherConfiguration.class, TramInMemoryConfiguration.class}) + @Import({EventuateContractVerifierConfiguration.class, TramEventsPublisherConfiguration.class, TramInMemoryConfiguration.class, EventuateTransactionTemplateConfiguration.class}) public static class TestConfiguration { - @Bean - public ChannelMapping channelMapping() { - return new DefaultChannelMapping.DefaultChannelMappingBuilder().build(); - } - @Bean public OrderDomainEventPublisher orderAggregateEventPublisher(DomainEventPublisher eventPublisher) { return new OrderDomainEventPublisher(eventPublisher); @@ -51,7 +51,7 @@ public OrderDomainEventPublisher orderAggregateEventPublisher(DomainEventPublish protected void orderCreated() { orderAggregateEventPublisher.publish(CHICKEN_VINDALOO_ORDER, - Collections.singletonList(new OrderCreatedEvent(CHICKEN_VINDALOO_ORDER_DETAILS, AJANTA_RESTAURANT_NAME))); + Collections.singletonList(new OrderCreatedEvent(CHICKEN_VINDALOO_ORDER_DETAILS, OrderDetailsMother.DELIVERY_ADDRESS, AJANTA_RESTAURANT_NAME))); } } diff --git a/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/domain/OrderJpaTest.java b/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/domain/OrderJpaTest.java index 4f989d8f..5e9632f3 100644 --- a/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/domain/OrderJpaTest.java +++ b/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/domain/OrderJpaTest.java @@ -1,5 +1,6 @@ package net.chrisrichardson.ftgo.orderservice.domain; +import net.chrisrichardson.ftgo.orderservice.OrderDetailsMother; import net.chrisrichardson.ftgo.orderservice.api.events.OrderState; import org.junit.Test; import org.junit.runner.RunWith; @@ -28,7 +29,7 @@ public class OrderJpaTest { public void shouldSaveAndLoadOrder() { long orderId = transactionTemplate.execute((ts) -> { - Order order = new Order(CONSUMER_ID, AJANTA_ID, chickenVindalooLineItems()); + Order order = new Order(CONSUMER_ID, AJANTA_ID, OrderDetailsMother.DELIVERY_INFORMATION, chickenVindalooLineItems()); orderRepository.save(order); return order.getId(); }); diff --git a/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/domain/OrderJpaTestConfiguration.java b/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/domain/OrderJpaTestConfiguration.java index ab182663..2504e1f2 100644 --- a/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/domain/OrderJpaTestConfiguration.java +++ b/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/domain/OrderJpaTestConfiguration.java @@ -1,11 +1,12 @@ package net.chrisrichardson.ftgo.orderservice.domain; +import io.eventuate.tram.spring.consumer.jdbc.TramConsumerJdbcAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.annotation.Configuration; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; @Configuration @EnableJpaRepositories -@EnableAutoConfiguration +@EnableAutoConfiguration(exclude = TramConsumerJdbcAutoConfiguration.class) public class OrderJpaTestConfiguration { } diff --git a/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/domain/OrderServiceIntegrationTest.java b/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/domain/OrderServiceIntegrationTest.java index 6d8bd573..c7ff381d 100644 --- a/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/domain/OrderServiceIntegrationTest.java +++ b/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/domain/OrderServiceIntegrationTest.java @@ -1,25 +1,21 @@ package net.chrisrichardson.ftgo.orderservice.domain; import com.jayway.jsonpath.JsonPath; -import io.eventuate.tram.commands.common.ChannelMapping; import io.eventuate.tram.commands.common.CommandMessageHeaders; -import io.eventuate.tram.commands.common.DefaultChannelMapping; -import io.eventuate.tram.commands.producer.TramCommandProducerConfiguration; +import io.eventuate.tram.spring.commands.producer.TramCommandProducerConfiguration; import io.eventuate.tram.events.publisher.DomainEventPublisher; -import io.eventuate.tram.inmemory.TramInMemoryConfiguration; import io.eventuate.tram.messaging.common.Message; +import io.eventuate.tram.sagas.spring.inmemory.TramSagaInMemoryConfiguration; import io.eventuate.tram.testutil.TestMessageConsumerFactory; import io.eventuate.util.test.async.Eventually; -import net.chrisrichardson.ftgo.common.Money; import net.chrisrichardson.ftgo.consumerservice.api.ConsumerServiceChannels; import net.chrisrichardson.ftgo.consumerservice.api.ValidateOrderByConsumer; +import net.chrisrichardson.ftgo.orderservice.OrderDetailsMother; +import net.chrisrichardson.ftgo.orderservice.RestaurantMother; import net.chrisrichardson.ftgo.orderservice.messaging.OrderServiceMessagingConfiguration; import net.chrisrichardson.ftgo.orderservice.service.OrderCommandHandlersConfiguration; import net.chrisrichardson.ftgo.orderservice.web.MenuItemIdAndQuantity; import net.chrisrichardson.ftgo.orderservice.web.OrderWebConfiguration; -import net.chrisrichardson.ftgo.restaurantservice.events.MenuItem; -import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantCreated; -import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantMenu; import net.chrisrichardson.ftgo.testutil.FtgoTestUtil; import org.junit.Test; import org.junit.runner.RunWith; @@ -31,19 +27,16 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; -import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; import org.springframework.test.context.junit4.SpringRunner; -import javax.sql.DataSource; import java.util.Collections; import java.util.function.Predicate; -import static org.junit.Assert.assertNotNull; - @RunWith(SpringRunner.class) @SpringBootTest(classes = OrderServiceIntegrationTest.TestConfiguration.class, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) + webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, + properties="eventuate.database.schema=eventuate" +) public class OrderServiceIntegrationTest { @@ -59,30 +52,15 @@ private String baseUrl(String path) { @EnableAutoConfiguration @Import({OrderWebConfiguration.class, OrderServiceMessagingConfiguration.class, OrderCommandHandlersConfiguration.class, TramCommandProducerConfiguration.class, - TramInMemoryConfiguration.class}) + TramSagaInMemoryConfiguration.class}) public static class TestConfiguration { - @Bean - public ChannelMapping channelMapping() { - return new DefaultChannelMapping.DefaultChannelMappingBuilder().build(); - } - @Bean public TestMessageConsumerFactory testMessageConsumerFactory() { return new TestMessageConsumerFactory(); } - @Bean - public DataSource dataSource() { - EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); - return builder.setType(EmbeddedDatabaseType.H2) - .addScript("eventuate-tram-embedded-schema.sql") - .addScript("eventuate-tram-sagas-embedded.sql") - .build(); - } - - @Bean public TestMessageConsumer2 mockConsumerService() { return new TestMessageConsumer2("mockConsumerService", ConsumerServiceChannels.consumerServiceChannel); @@ -110,8 +88,7 @@ public TestMessageConsumer2 mockConsumerService() { @Test public void shouldCreateOrder() { domainEventPublisher.publish("net.chrisrichardson.ftgo.restaurantservice.domain.Restaurant", RESTAURANT_ID, - Collections.singletonList(new RestaurantCreated("Ajanta", - new RestaurantMenu(Collections.singletonList(new MenuItem(CHICKED_VINDALOO_MENU_ITEM_ID, "Chicken Vindaloo", new Money("12.34"))))))); + Collections.singletonList(RestaurantMother.makeAjantaRestaurantCreatedEvent())); Eventually.eventually(() -> { FtgoTestUtil.assertPresent(restaurantRepository.findById(Long.parseLong(RESTAURANT_ID))); @@ -119,7 +96,7 @@ public void shouldCreateOrder() { long consumerId = 1511300065921L; - Order order = orderService.createOrder(consumerId, Long.parseLong(RESTAURANT_ID), Collections.singletonList(new MenuItemIdAndQuantity(CHICKED_VINDALOO_MENU_ITEM_ID, 5))); + Order order = orderService.createOrder(consumerId, Long.parseLong(RESTAURANT_ID), OrderDetailsMother.DELIVERY_INFORMATION, Collections.singletonList(new MenuItemIdAndQuantity(CHICKED_VINDALOO_MENU_ITEM_ID, 5))); FtgoTestUtil.assertPresent(orderRepository.findById(order.getId())); diff --git a/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/domain/RestaurantJpaTest.java b/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/domain/RestaurantJpaTest.java index ffffa5d2..a5143a4e 100644 --- a/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/domain/RestaurantJpaTest.java +++ b/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/domain/RestaurantJpaTest.java @@ -1,6 +1,5 @@ package net.chrisrichardson.ftgo.orderservice.domain; -import net.chrisrichardson.ftgo.orderservice.api.events.OrderState; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -8,13 +7,8 @@ import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.support.TransactionTemplate; -import static net.chrisrichardson.ftgo.orderservice.OrderDetailsMother.CONSUMER_ID; -import static net.chrisrichardson.ftgo.orderservice.OrderDetailsMother.chickenVindalooLineItems; -import static net.chrisrichardson.ftgo.orderservice.RestaurantMother.AJANTA_ID; -import static net.chrisrichardson.ftgo.orderservice.RestaurantMother.AJANTA_RESTAURANT_MENU_ITEMS; -import static net.chrisrichardson.ftgo.orderservice.RestaurantMother.AJANTA_RESTAURANT_NAME; +import static net.chrisrichardson.ftgo.orderservice.RestaurantMother.*; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; @RunWith(SpringRunner.class) @SpringBootTest(classes = OrderJpaTestConfiguration.class) @@ -27,21 +21,37 @@ public class RestaurantJpaTest { private TransactionTemplate transactionTemplate; @Test - public void shouldSaveRestaurant() { + public void shouldSaveAndLoadRestaurant() { + long restaurantId = saveRestaurant(); + assertEquals(AJANTA_ID, restaurantId); + loadRestaurant(restaurantId); + } - transactionTemplate.execute((ts) -> { - Restaurant restaurant = new Restaurant(AJANTA_ID, AJANTA_RESTAURANT_NAME, AJANTA_RESTAURANT_MENU_ITEMS); - restaurantRepository.save(restaurant); - return null; - }); - transactionTemplate.execute((ts) -> { - Restaurant restaurant = new Restaurant(AJANTA_ID, AJANTA_RESTAURANT_NAME, AJANTA_RESTAURANT_MENU_ITEMS); - restaurantRepository.save(restaurant); + @Test + public void shouldSaveRestaurantTwice() { + long restaurantId1 = saveRestaurant(); + long restaurantId2 = saveRestaurant(); + assertEquals(AJANTA_ID, restaurantId1); + assertEquals(restaurantId1, restaurantId2); + loadRestaurant(restaurantId1); + } + + private void loadRestaurant(long restaurantId) { + transactionTemplate.execute(ts -> { + Restaurant restaurant = restaurantRepository.findById(restaurantId).get(); + assertEquals(AJANTA_RESTAURANT_NAME, restaurant.getName()); + assertEquals(AJANTA_RESTAURANT_MENU_ITEMS, restaurant.getMenuItems()); return null; }); + } - + private long saveRestaurant() { + return transactionTemplate.execute((ts) -> { + Restaurant restaurant = new Restaurant(AJANTA_ID, AJANTA_RESTAURANT_NAME, AJANTA_RESTAURANT_MENU_ITEMS); + restaurantRepository.save(restaurant); + return restaurant.getId(); + }); } } diff --git a/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/grpc/OrderServiceClient.java b/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/grpc/OrderServiceClient.java index ce51a369..0b4215da 100644 --- a/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/grpc/OrderServiceClient.java +++ b/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/grpc/OrderServiceClient.java @@ -3,6 +3,8 @@ import io.grpc.*; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; import java.util.List; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; @@ -27,20 +29,28 @@ public void shutdown() throws InterruptedException { channel.shutdown().awaitTermination(5, TimeUnit.SECONDS); } - public long createOrder(long consumerId, long restaurantId, List lineItems) { + public long createOrder(long consumerId, long restaurantId, List lineItems, net.chrisrichardson.ftgo.common.Address deliveryAddress, LocalDateTime deliveryTime) { CreateOrderRequest.Builder builder = CreateOrderRequest.newBuilder() .setConsumerId(consumerId) - .setRestaurantId(restaurantId); - CreateOrderRequest request = builder - .build(); - IntStream.range(0, lineItems.size()).forEach(idx -> { - MenuItemIdAndQuantity li = lineItems.get(idx); - builder.setLineItems(idx, LineItem.newBuilder().setQuantity(li.getQuantity()).setMenuItemId(li.getMenuItemId()).build()); - }); - CreateOrderReply response; - response = clientStub.createOrder(request); + .setRestaurantId(restaurantId) + .setDeliveryAddress(makeAddress(deliveryAddress)) + .setDeliveryTime(DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(deliveryTime)); + lineItems.forEach(li -> builder.addLineItems(LineItem.newBuilder().setQuantity(li.getQuantity()).setMenuItemId(li.getMenuItemId()))); + CreateOrderReply response = clientStub.createOrder(builder.build()); return response.getOrderId(); } + private Address makeAddress(net.chrisrichardson.ftgo.common.Address address) { + Address.Builder builder = Address.newBuilder() + .setStreet1(address.getStreet1()); + if (address.getStreet2() != null) + builder.setStreet2(address.getStreet2()); + builder + .setCity(address.getCity()) + .setState(address.getState()) + .setZip(address.getZip()); + return builder.build(); + } + } diff --git a/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/grpc/OrderServiceGrpIntegrationTest.java b/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/grpc/OrderServiceGrpIntegrationTest.java index dc693a39..970894ba 100644 --- a/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/grpc/OrderServiceGrpIntegrationTest.java +++ b/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/grpc/OrderServiceGrpIntegrationTest.java @@ -1,9 +1,11 @@ package net.chrisrichardson.ftgo.orderservice.grpc; +import net.chrisrichardson.ftgo.orderservice.OrderDetailsMother; +import net.chrisrichardson.ftgo.orderservice.domain.DeliveryInformation; import net.chrisrichardson.ftgo.orderservice.domain.Order; -import net.chrisrichardson.ftgo.orderservice.domain.OrderJpaTestConfiguration; import net.chrisrichardson.ftgo.orderservice.domain.OrderService; +import net.chrisrichardson.ftgo.orderservice.web.MenuItemIdAndQuantity; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -11,8 +13,12 @@ import org.springframework.test.context.junit4.SpringRunner; import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @RunWith(SpringRunner.class) @@ -25,15 +31,19 @@ public class OrderServiceGrpIntegrationTest { @Test public void shouldCreateOrder() { - Order order = new Order(1, 2, Collections.emptyList()); - order.setId(101L); + Order order = OrderDetailsMother.CHICKEN_VINDALOO_ORDER; + + when(orderService.createOrder(any(Long.class), any(Long.class), any(DeliveryInformation.class), any(List.class))).thenReturn(order); - when(orderService.createOrder(1, 2, Collections.emptyList())).thenReturn(order); OrderServiceClient client = new OrderServiceClient("localhost", 50051); - long orderId = client.createOrder(1, 2, Collections.emptyList()); + List expectedLineItems = order.getLineItems().stream().map(li -> new MenuItemIdAndQuantity(li.getMenuItemId(), li.getQuantity())).collect(Collectors.toList()); + + long orderId = client.createOrder(order.getConsumerId(), order.getRestaurantId(), expectedLineItems, order.getDeliveryInformation().getDeliveryAddress(), order.getDeliveryInformation().getDeliveryTime()); + + assertEquals((long)order.getId(), orderId); - assertEquals(101L, orderId); + verify(orderService).createOrder(order.getConsumerId(), order.getRestaurantId(), order.getDeliveryInformation(), expectedLineItems); } } diff --git a/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/sagaparticipants/KitchenServiceProxyIntegrationTest.java b/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/sagaparticipants/KitchenServiceProxyIntegrationTest.java index 3919f320..bbd38890 100644 --- a/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/sagaparticipants/KitchenServiceProxyIntegrationTest.java +++ b/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/sagaparticipants/KitchenServiceProxyIntegrationTest.java @@ -1,18 +1,15 @@ package net.chrisrichardson.ftgo.orderservice.sagaparticipants; -import io.eventuate.tram.commands.common.ChannelMapping; -import io.eventuate.tram.commands.common.DefaultChannelMapping; -import io.eventuate.tram.commands.producer.TramCommandProducerConfiguration; -import io.eventuate.tram.inmemory.TramInMemoryConfiguration; -import io.eventuate.tram.sagas.orchestration.SagaCommandProducer; -import io.eventuate.tram.springcloudcontractsupport.EventuateContractVerifierConfiguration; -import io.eventuate.tram.springcloudcontractsupport.EventuateTramRoutesConfigurer; -import net.chrisrichardson.ftgo.orderservice.OrderDetailsMother; -import net.chrisrichardson.ftgo.orderservice.sagas.createorder.CreateOrderSaga; +import io.eventuate.tram.sagas.spring.inmemory.TramSagaInMemoryConfiguration; +import io.eventuate.tram.sagas.spring.testing.contract.EventuateTramSagasSpringCloudContractSupportConfiguration; +import io.eventuate.tram.sagas.spring.testing.contract.SagaMessagingTestHelper; +import io.eventuate.tram.spring.cloudcontractsupport.EventuateTramRoutesConfigurer; import net.chrisrichardson.ftgo.kitchenservice.api.CreateTicket; import net.chrisrichardson.ftgo.kitchenservice.api.CreateTicketReply; import net.chrisrichardson.ftgo.kitchenservice.api.TicketDetails; import net.chrisrichardson.ftgo.kitchenservice.api.TicketLineItem; +import net.chrisrichardson.ftgo.orderservice.OrderDetailsMother; +import net.chrisrichardson.ftgo.orderservice.sagas.createorder.CreateOrderSaga; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; @@ -23,18 +20,13 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder; -import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; -import javax.sql.DataSource; import java.util.Collections; import static net.chrisrichardson.ftgo.orderservice.OrderDetailsMother.CHICKEN_VINDALOO_QUANTITY; -import static net.chrisrichardson.ftgo.orderservice.RestaurantMother.AJANTA_ID; -import static net.chrisrichardson.ftgo.orderservice.RestaurantMother.CHICKEN_VINDALOO; -import static net.chrisrichardson.ftgo.orderservice.RestaurantMother.CHICKEN_VINDALOO_MENU_ITEM_ID; +import static net.chrisrichardson.ftgo.orderservice.RestaurantMother.*; import static org.junit.Assert.assertEquals; @RunWith(SpringRunner.class) @@ -49,43 +41,15 @@ public class KitchenServiceProxyIntegrationTest { @Configuration @EnableAutoConfiguration - @Import({TramCommandProducerConfiguration.class, - TramInMemoryConfiguration.class, EventuateContractVerifierConfiguration.class}) + @Import({TramSagaInMemoryConfiguration.class, EventuateTramSagasSpringCloudContractSupportConfiguration.class}) public static class TestConfiguration { - @Bean - public ChannelMapping channelMapping() { - return new DefaultChannelMapping.DefaultChannelMappingBuilder().build(); - } - - - /// TramSagaInMemoryConfiguration - - @Bean - public DataSource dataSource() { - EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder(); - return builder.setType(EmbeddedDatabaseType.H2) - .addScript("eventuate-tram-embedded-schema.sql") - .addScript("eventuate-tram-sagas-embedded.sql") - .build(); - } - @Bean public EventuateTramRoutesConfigurer eventuateTramRoutesConfigurer(BatchStubRunner batchStubRunner) { return new EventuateTramRoutesConfigurer(batchStubRunner); } - @Bean - public SagaMessagingTestHelper sagaMessagingTestHelper() { - return new SagaMessagingTestHelper(); - } - - @Bean - public SagaCommandProducer sagaCommandProducer() { - return new SagaCommandProducer(); - } - @Bean public KitchenServiceProxy kitchenServiceProxy() { return new KitchenServiceProxy(); diff --git a/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/sagaparticipants/SagaMessagingTestHelper.java b/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/sagaparticipants/SagaMessagingTestHelper.java deleted file mode 100644 index 80cf87a5..00000000 --- a/ftgo-order-service/src/integration-test/java/net/chrisrichardson/ftgo/orderservice/sagaparticipants/SagaMessagingTestHelper.java +++ /dev/null @@ -1,39 +0,0 @@ -package net.chrisrichardson.ftgo.orderservice.sagaparticipants; - -import io.eventuate.javaclient.commonimpl.JSonMapper; -import io.eventuate.javaclient.spring.jdbc.IdGenerator; -import io.eventuate.tram.commands.common.Command; -import io.eventuate.tram.sagas.orchestration.SagaCommandProducer; -import io.eventuate.tram.sagas.simpledsl.CommandEndpoint; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessage; -import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierMessaging; - -import javax.inject.Inject; - -public class SagaMessagingTestHelper { - - @Inject - ContractVerifierMessaging contractVerifierMessaging; - - @Autowired - private SagaCommandProducer sagaCommandProducer; - - @Autowired - private IdGenerator idGenerator; - - - public R sendAndReceiveCommand(CommandEndpoint commandEndpoint, C command, Class replyClass, String sagaType) { - // TODO verify that replyClass is allowed - - String sagaId = idGenerator.genId().asString(); - - String replyTo = sagaType + "-reply"; - sagaCommandProducer.sendCommand(sagaType, sagaId, commandEndpoint.getCommandChannel(), null, command, replyTo); - - ContractVerifierMessage response = contractVerifierMessaging.receive(replyTo); - - String payload = (String) response.getPayload(); - return (R) JSonMapper.fromJson(payload, replyClass); - } -} diff --git a/ftgo-order-service/src/integration-test/resources/application.properties b/ftgo-order-service/src/integration-test/resources/application.properties deleted file mode 100644 index 294b7725..00000000 --- a/ftgo-order-service/src/integration-test/resources/application.properties +++ /dev/null @@ -1,27 +0,0 @@ -spring.application.name=ftgo-order-service - -logging.level.root=INFO -logging.level.org.hibernate.SQL=DEBUG -logging.level.org.springframework.cloud.contract=DEBUG -logging.level.io.eventuate=DEBUG -#logging.level.org.springframework.orm.jpa.JpaTransactionManager=TRACE - -spring.jpa.generate-ddl=true - -stubrunner.stream.enabled=false -stubrunner.integration.enabled=false -spring.datasource.url=jdbc:mysql://${DOCKER_HOST_IP:localhost}/eventuate -spring.datasource.username=mysqluser -spring.datasource.password=mysqlpw -spring.datasource.driver.class.name=com.mysql.jdbc.Driver -spring.data.mongodb.uri=mongodb://${DOCKER_HOST_IP:localhost}/bankingexampledb - -eventuatelocal.kafka.bootstrap.servers=${DOCKER_HOST_IP:localhost}:9092 -eventuatelocal.cdc.db.user.name=root -eventuatelocal.cdc.db.password=rootpassword -eventuatelocal.zookeeper.connection.string=${DOCKER_HOST_IP:localhost}:2181 - -aws.access.key_id=id_key -aws.secret.access.key=access_key -aws.dynamodb.endpoint.url=http://${DOCKER_HOST_IP:localhost}:8000 -aws.region=us-west-2 diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/Address.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/Address.java deleted file mode 100644 index 2fc955c5..00000000 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/Address.java +++ /dev/null @@ -1,11 +0,0 @@ -package net.chrisrichardson.ftgo.orderservice.domain; - -public class Address { - - private String street1; - private String street2; - private String city; - private String state; - private String zip; - -} diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/DeliveryInformation.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/DeliveryInformation.java index 9e8dbc0f..57e1712b 100644 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/DeliveryInformation.java +++ b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/DeliveryInformation.java @@ -1,6 +1,11 @@ package net.chrisrichardson.ftgo.orderservice.domain; +import net.chrisrichardson.ftgo.common.Address; +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; + import javax.persistence.Access; import javax.persistence.AccessType; import javax.persistence.AttributeOverride; @@ -19,4 +24,43 @@ public class DeliveryInformation { @AttributeOverride(name="state", column=@Column(name="delivery_state")) }) private Address deliveryAddress; + + public DeliveryInformation() { + } + + @Override + public boolean equals(Object o) { + return EqualsBuilder.reflectionEquals(this, o); + } + + @Override + public int hashCode() { + return HashCodeBuilder.reflectionHashCode(this); + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + + public DeliveryInformation(LocalDateTime deliveryTime, Address deliveryAddress) { + this.deliveryTime = deliveryTime; + this.deliveryAddress = deliveryAddress; + } + + public LocalDateTime getDeliveryTime() { + return deliveryTime; + } + + public void setDeliveryTime(LocalDateTime deliveryTime) { + this.deliveryTime = deliveryTime; + } + + public Address getDeliveryAddress() { + return deliveryAddress; + } + + public void setDeliveryAddress(Address deliveryAddress) { + this.deliveryAddress = deliveryAddress; + } } diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/MenuItem.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/MenuItem.java new file mode 100644 index 00000000..359de68c --- /dev/null +++ b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/MenuItem.java @@ -0,0 +1,67 @@ +package net.chrisrichardson.ftgo.orderservice.domain; + +import net.chrisrichardson.ftgo.common.Money; +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; + +import javax.persistence.Access; +import javax.persistence.AccessType; +import javax.persistence.Embeddable; + +@Embeddable +@Access(AccessType.FIELD) +public class MenuItem { + + private String id; + private String name; + private Money price; + + private MenuItem() { + } + + public MenuItem(String id, String name, Money price) { + this.id = id; + this.name = name; + this.price = price; + } + + @Override + public boolean equals(Object o) { + return EqualsBuilder.reflectionEquals(this, o); + } + + @Override + public int hashCode() { + return HashCodeBuilder.reflectionHashCode(this); + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Money getPrice() { + return price; + } + + public void setPrice(Money price) { + this.price = price; + } +} diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/Order.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/Order.java index 71032645..84acc95d 100644 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/Order.java +++ b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/Order.java @@ -1,6 +1,7 @@ package net.chrisrichardson.ftgo.orderservice.domain; import io.eventuate.tram.events.aggregates.ResultWithDomainEvents; +import net.chrisrichardson.ftgo.common.Address; import net.chrisrichardson.ftgo.common.Money; import net.chrisrichardson.ftgo.common.UnsupportedStateTransitionException; import net.chrisrichardson.ftgo.orderservice.api.events.*; @@ -21,11 +22,12 @@ public class Order { public static ResultWithDomainEvents - createOrder(long consumerId, Restaurant restaurant, List orderLineItems) { - Order order = new Order(consumerId, restaurant.getId(), orderLineItems); + createOrder(long consumerId, Restaurant restaurant, DeliveryInformation deliveryInformation, List orderLineItems) { + Order order = new Order(consumerId, restaurant.getId(), deliveryInformation, orderLineItems); List events = singletonList(new OrderCreatedEvent( new OrderDetails(consumerId, restaurant.getId(), orderLineItems, order.getOrderTotal()), + deliveryInformation.getDeliveryAddress(), restaurant.getName())); return new ResultWithDomainEvents<>(order, events); } @@ -58,9 +60,10 @@ public class Order { private Order() { } - public Order(long consumerId, long restaurantId, List orderLineItems) { + public Order(long consumerId, long restaurantId, DeliveryInformation deliveryInformation, List orderLineItems) { this.consumerId = consumerId; this.restaurantId = restaurantId; + this.deliveryInformation = deliveryInformation; this.orderLineItems = new OrderLineItems(orderLineItems); this.state = APPROVAL_PENDING; } @@ -73,7 +76,9 @@ public void setId(Long id) { this.id = id; } - + public DeliveryInformation getDeliveryInformation() { + return deliveryInformation; + } public Money getOrderTotal() { return orderLineItems.orderTotal(); @@ -170,7 +175,7 @@ public List confirmRevision(OrderRevision orderRevision) { orderRevision.getDeliveryInformation().ifPresent(newDi -> this.deliveryInformation = newDi); - if (!orderRevision.getRevisedLineItemQuantities().isEmpty()) { + if (orderRevision.getRevisedOrderLineItems() != null && orderRevision.getRevisedOrderLineItems().size() > 0) { orderLineItems.updateLineItems(orderRevision); } diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderDomainEventPublisher.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderDomainEventPublisher.java index fbdcc7e0..232951a7 100644 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderDomainEventPublisher.java +++ b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderDomainEventPublisher.java @@ -8,8 +8,7 @@ public class OrderDomainEventPublisher extends AbstractAggregateDomainEventPubli public OrderDomainEventPublisher(DomainEventPublisher eventPublisher) { - super(eventPublisher, Order.class, Order::getId - ); + super(eventPublisher, Order.class, Order::getId); } } diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderLineItems.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderLineItems.java index a1f2fdfd..3d4d05d8 100644 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderLineItems.java +++ b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderLineItems.java @@ -2,12 +2,14 @@ import net.chrisrichardson.ftgo.common.Money; import net.chrisrichardson.ftgo.orderservice.api.events.OrderLineItem; +import net.chrisrichardson.ftgo.common.RevisedOrderLineItem; import javax.persistence.CollectionTable; import javax.persistence.ElementCollection; import javax.persistence.Embeddable; import java.util.List; -import java.util.concurrent.atomic.AtomicReference; +import java.util.Objects; +import java.util.Optional; @Embeddable public class OrderLineItems { @@ -36,19 +38,27 @@ OrderLineItem findOrderLineItem(String lineItemId) { } Money changeToOrderTotal(OrderRevision orderRevision) { - AtomicReference delta = new AtomicReference<>(Money.ZERO); - - orderRevision.getRevisedLineItemQuantities().forEach((lineItemId, newQuantity) -> { - OrderLineItem lineItem = findOrderLineItem(lineItemId); - delta.set(delta.get().add(lineItem.deltaForChangedQuantity(newQuantity))); - }); - return delta.get(); + return orderRevision + .getRevisedOrderLineItems() + .stream() + .map(item -> { + OrderLineItem lineItem = findOrderLineItem(item.getMenuItemId()); + return lineItem.deltaForChangedQuantity(item.getQuantity()); + }) + .reduce(Money.ZERO, Money::add); } void updateLineItems(OrderRevision orderRevision) { getLineItems().stream().forEach(li -> { - Integer revised = orderRevision.getRevisedLineItemQuantities().get(li.getMenuItemId()); - li.setQuantity(revised); + + Optional revised = orderRevision.getRevisedOrderLineItems() + .stream() + .filter(item -> Objects.equals(li.getMenuItemId(), item.getMenuItemId())) + .map(RevisedOrderLineItem::getQuantity) + .findFirst(); + + li.setQuantity(revised.orElseThrow(() -> + new IllegalArgumentException(String.format("menu item id not found.", li.getMenuItemId())))); }); } diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderRejected.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderRejected.java deleted file mode 100644 index f4f3594c..00000000 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderRejected.java +++ /dev/null @@ -1,7 +0,0 @@ -package net.chrisrichardson.ftgo.orderservice.domain; - -import io.eventuate.tram.events.common.DomainEvent; -import net.chrisrichardson.ftgo.orderservice.api.events.OrderDomainEvent; - -public class OrderRejected implements OrderDomainEvent { -} diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderRevision.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderRevision.java index c2eca4b0..999961ef 100644 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderRevision.java +++ b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderRevision.java @@ -1,35 +1,36 @@ package net.chrisrichardson.ftgo.orderservice.domain; -import java.util.Map; +import net.chrisrichardson.ftgo.common.RevisedOrderLineItem; + +import java.util.List; import java.util.Optional; public class OrderRevision { private Optional deliveryInformation = Optional.empty(); - private Map revisedLineItemQuantities; + private List revisedOrderLineItems; private OrderRevision() { } - public OrderRevision(Optional deliveryInformation, Map revisedLineItemQuantities) { + public OrderRevision(Optional deliveryInformation, List revisedOrderLineItems) { this.deliveryInformation = deliveryInformation; - this.revisedLineItemQuantities = revisedLineItemQuantities; + this.revisedOrderLineItems = revisedOrderLineItems; } public void setDeliveryInformation(Optional deliveryInformation) { this.deliveryInformation = deliveryInformation; } - public void setRevisedLineItemQuantities(Map revisedLineItemQuantities) { - this.revisedLineItemQuantities = revisedLineItemQuantities; - } - public Optional getDeliveryInformation() { return deliveryInformation; } + public List getRevisedOrderLineItems() { + return revisedOrderLineItems; + } - public Map getRevisedLineItemQuantities() { - return revisedLineItemQuantities; + public void setRevisedOrderLineItems(List revisedOrderLineItems) { + this.revisedOrderLineItems = revisedOrderLineItems; } } diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderService.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderService.java index f0272a17..94f2d686 100644 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderService.java +++ b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderService.java @@ -2,17 +2,18 @@ import io.eventuate.tram.events.aggregates.ResultWithDomainEvents; import io.eventuate.tram.events.publisher.DomainEventPublisher; -import io.eventuate.tram.sagas.orchestration.SagaManager; +import io.eventuate.tram.sagas.orchestration.SagaInstanceFactory; import io.micrometer.core.instrument.MeterRegistry; import net.chrisrichardson.ftgo.orderservice.api.events.OrderDetails; import net.chrisrichardson.ftgo.orderservice.api.events.OrderDomainEvent; import net.chrisrichardson.ftgo.orderservice.api.events.OrderLineItem; +import net.chrisrichardson.ftgo.orderservice.sagas.cancelorder.CancelOrderSaga; import net.chrisrichardson.ftgo.orderservice.sagas.cancelorder.CancelOrderSagaData; +import net.chrisrichardson.ftgo.orderservice.sagas.createorder.CreateOrderSaga; import net.chrisrichardson.ftgo.orderservice.sagas.createorder.CreateOrderSagaState; +import net.chrisrichardson.ftgo.orderservice.sagas.reviseorder.ReviseOrderSaga; import net.chrisrichardson.ftgo.orderservice.sagas.reviseorder.ReviseOrderSagaData; import net.chrisrichardson.ftgo.orderservice.web.MenuItemIdAndQuantity; -import net.chrisrichardson.ftgo.restaurantservice.events.MenuItem; -import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantMenu; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.transaction.annotation.Propagation; @@ -24,36 +25,48 @@ import static java.util.stream.Collectors.toList; -@Transactional public class OrderService { private Logger logger = LoggerFactory.getLogger(getClass()); + private SagaInstanceFactory sagaInstanceFactory; + private OrderRepository orderRepository; private RestaurantRepository restaurantRepository; - private SagaManager createOrderSagaManager; + private CreateOrderSaga createOrderSaga; - private SagaManager cancelOrderSagaManager; + private CancelOrderSaga cancelOrderSaga; - private SagaManager reviseOrderSagaManager; + private ReviseOrderSaga reviseOrderSaga; private OrderDomainEventPublisher orderAggregateEventPublisher; private Optional meterRegistry; - public OrderService(OrderRepository orderRepository, DomainEventPublisher eventPublisher, RestaurantRepository restaurantRepository, SagaManager createOrderSagaManager, SagaManager cancelOrderSagaManager, SagaManager reviseOrderSagaManager, OrderDomainEventPublisher orderAggregateEventPublisher, Optional meterRegistry) { + public OrderService(SagaInstanceFactory sagaInstanceFactory, + OrderRepository orderRepository, + DomainEventPublisher eventPublisher, + RestaurantRepository restaurantRepository, + CreateOrderSaga createOrderSaga, + CancelOrderSaga cancelOrderSaga, + ReviseOrderSaga reviseOrderSaga, + OrderDomainEventPublisher orderAggregateEventPublisher, + Optional meterRegistry) { + + this.sagaInstanceFactory = sagaInstanceFactory; this.orderRepository = orderRepository; this.restaurantRepository = restaurantRepository; - this.createOrderSagaManager = createOrderSagaManager; - this.cancelOrderSagaManager = cancelOrderSagaManager; - this.reviseOrderSagaManager = reviseOrderSagaManager; + this.createOrderSaga = createOrderSaga; + this.cancelOrderSaga = cancelOrderSaga; + this.reviseOrderSaga = reviseOrderSaga; this.orderAggregateEventPublisher = orderAggregateEventPublisher; this.meterRegistry = meterRegistry; } - public Order createOrder(long consumerId, long restaurantId, + @Transactional + public Order createOrder(long consumerId, long restaurantId, DeliveryInformation deliveryInformation, List lineItems) { Restaurant restaurant = restaurantRepository.findById(restaurantId) .orElseThrow(() -> new RestaurantNotFoundException(restaurantId)); @@ -61,7 +74,7 @@ public Order createOrder(long consumerId, long restaurantId, List orderLineItems = makeOrderLineItems(lineItems, restaurant); ResultWithDomainEvents orderAndEvents = - Order.createOrder(consumerId, restaurant, orderLineItems); + Order.createOrder(consumerId, restaurant, deliveryInformation, orderLineItems); Order order = orderAndEvents.result; orderRepository.save(order); @@ -71,7 +84,7 @@ public Order createOrder(long consumerId, long restaurantId, OrderDetails orderDetails = new OrderDetails(consumerId, restaurantId, orderLineItems, order.getOrderTotal()); CreateOrderSagaState data = new CreateOrderSagaState(order.getId(), orderDetails); - createOrderSagaManager.create(data, Order.class, order.getId()); + sagaInstanceFactory.create(createOrderSaga, data); meterRegistry.ifPresent(mr -> mr.counter("placed_orders").increment()); @@ -99,11 +112,12 @@ public void noteReversingAuthorization(Long orderId) { throw new UnsupportedOperationException(); } + @Transactional public Order cancel(Long orderId) { Order order = orderRepository.findById(orderId) .orElseThrow(() -> new OrderNotFoundException(orderId)); CancelOrderSagaData sagaData = new CancelOrderSagaData(order.getConsumerId(), orderId, order.getOrderTotal()); - cancelOrderSagaManager.create(sagaData); + sagaInstanceFactory.create(cancelOrderSaga, sagaData); return order; } @@ -136,10 +150,11 @@ public void confirmCancelled(long orderId) { updateOrder(orderId, Order::noteCancelled); } + @Transactional public Order reviseOrder(long orderId, OrderRevision orderRevision) { Order order = orderRepository.findById(orderId).orElseThrow(() -> new OrderNotFoundException(orderId)); ReviseOrderSagaData sagaData = new ReviseOrderSagaData(order.getConsumerId(), orderId, null, orderRevision); - reviseOrderSagaManager.create(sagaData); + sagaInstanceFactory.create(reviseOrderSaga, sagaData); return order; } @@ -159,16 +174,14 @@ public void confirmRevision(long orderId, OrderRevision revision) { updateOrder(orderId, order -> order.confirmRevision(revision)); } - @Transactional(propagation = Propagation.MANDATORY) - public void createMenu(long id, String name, RestaurantMenu menu) { - Restaurant restaurant = new Restaurant(id, name, menu.getMenuItems()); + public void createMenu(long id, String name, List menuItems) { + Restaurant restaurant = new Restaurant(id, name, menuItems); restaurantRepository.save(restaurant); } - @Transactional(propagation = Propagation.MANDATORY) - public void reviseMenu(long id, RestaurantMenu revisedMenu) { + public void reviseMenu(long id, List menuItems) { restaurantRepository.findById(id).map(restaurant -> { - List events = restaurant.reviseMenu(revisedMenu); + List events = restaurant.reviseMenu(menuItems); return restaurant; }).orElseThrow(RuntimeException::new); } diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderServiceConfiguration.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderServiceConfiguration.java index 1b716413..b80d3556 100644 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderServiceConfiguration.java +++ b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/OrderServiceConfiguration.java @@ -1,11 +1,9 @@ package net.chrisrichardson.ftgo.orderservice.domain; import io.eventuate.tram.events.publisher.DomainEventPublisher; -import io.eventuate.tram.events.publisher.TramEventsPublisherConfiguration; -import io.eventuate.tram.sagas.orchestration.SagaCommandProducer; -import io.eventuate.tram.sagas.orchestration.SagaManager; -import io.eventuate.tram.sagas.orchestration.SagaManagerImpl; -import io.eventuate.tram.sagas.orchestration.SagaOrchestratorConfiguration; +import io.eventuate.tram.spring.events.publisher.TramEventsPublisherConfiguration; +import io.eventuate.tram.sagas.orchestration.*; +import io.eventuate.tram.sagas.spring.orchestration.SagaOrchestratorConfiguration; import io.micrometer.core.instrument.MeterRegistry; import net.chrisrichardson.ftgo.common.CommonConfiguration; import net.chrisrichardson.ftgo.orderservice.sagaparticipants.AccountingServiceProxy; @@ -13,41 +11,33 @@ import net.chrisrichardson.ftgo.orderservice.sagaparticipants.KitchenServiceProxy; import net.chrisrichardson.ftgo.orderservice.sagaparticipants.OrderServiceProxy; import net.chrisrichardson.ftgo.orderservice.sagas.cancelorder.CancelOrderSaga; -import net.chrisrichardson.ftgo.orderservice.sagas.cancelorder.CancelOrderSagaData; import net.chrisrichardson.ftgo.orderservice.sagas.createorder.CreateOrderSaga; -import net.chrisrichardson.ftgo.orderservice.sagas.createorder.CreateOrderSagaState; import net.chrisrichardson.ftgo.orderservice.sagas.reviseorder.ReviseOrderSaga; -import net.chrisrichardson.ftgo.orderservice.sagas.reviseorder.ReviseOrderSagaData; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.actuate.autoconfigure.metrics.MeterRegistryCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import org.springframework.transaction.annotation.EnableTransactionManagement; import java.util.Optional; @Configuration @Import({TramEventsPublisherConfiguration.class, SagaOrchestratorConfiguration.class, CommonConfiguration.class}) public class OrderServiceConfiguration { - // TODO move to framework @Bean - public SagaCommandProducer sagaCommandProducer() { - return new SagaCommandProducer(); - } - - @Bean - public OrderService orderService(RestaurantRepository restaurantRepository, OrderRepository orderRepository, DomainEventPublisher eventPublisher, - SagaManager createOrderSagaManager, - SagaManager cancelOrderSagaManager, SagaManager reviseOrderSagaManager, OrderDomainEventPublisher orderAggregateEventPublisher, Optional meterRegistry) { - return new OrderService(orderRepository, eventPublisher, restaurantRepository, - createOrderSagaManager, cancelOrderSagaManager, reviseOrderSagaManager, orderAggregateEventPublisher, meterRegistry); - } - - @Bean - public SagaManager createOrderSagaManager(CreateOrderSaga saga) { - return new SagaManagerImpl<>(saga); + public OrderService orderService(SagaInstanceFactory sagaInstanceFactory, + RestaurantRepository restaurantRepository, + OrderRepository orderRepository, + DomainEventPublisher eventPublisher, + CreateOrderSaga createOrderSaga, + CancelOrderSaga cancelOrderSaga, + ReviseOrderSaga reviseOrderSaga, + OrderDomainEventPublisher orderAggregateEventPublisher, + Optional meterRegistry) { + + return new OrderService(sagaInstanceFactory, orderRepository, eventPublisher, restaurantRepository, + createOrderSaga, cancelOrderSaga, reviseOrderSaga, orderAggregateEventPublisher, meterRegistry); } @Bean @@ -55,21 +45,11 @@ public CreateOrderSaga createOrderSaga(OrderServiceProxy orderService, ConsumerS return new CreateOrderSaga(orderService, consumerService, kitchenServiceProxy, accountingService); } - @Bean - public SagaManager CancelOrderSagaManager(CancelOrderSaga saga) { - return new SagaManagerImpl<>(saga); - } - @Bean public CancelOrderSaga cancelOrderSaga() { return new CancelOrderSaga(); } - @Bean - public SagaManager reviseOrderSagaManager(ReviseOrderSaga saga) { - return new SagaManagerImpl<>(saga); - } - @Bean public ReviseOrderSaga reviseOrderSaga() { return new ReviseOrderSaga(); diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/Restaurant.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/Restaurant.java index 20c1d3ee..f534df2e 100644 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/Restaurant.java +++ b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/domain/Restaurant.java @@ -1,18 +1,9 @@ package net.chrisrichardson.ftgo.orderservice.domain; -import net.chrisrichardson.ftgo.orderservice.api.events.OrderDomainEvent; import net.chrisrichardson.ftgo.kitchenservice.api.TicketDetails; -import net.chrisrichardson.ftgo.restaurantservice.events.MenuItem; -import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantMenu; +import net.chrisrichardson.ftgo.orderservice.api.events.OrderDomainEvent; -import javax.persistence.Access; -import javax.persistence.AccessType; -import javax.persistence.CollectionTable; -import javax.persistence.ElementCollection; -import javax.persistence.Embedded; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Table; +import javax.persistence.*; import java.util.List; import java.util.Optional; @@ -39,7 +30,7 @@ public Restaurant(long id, String name, List menuItems) { this.menuItems = menuItems; } - public List reviseMenu(RestaurantMenu revisedMenu) { + public List reviseMenu(List revisedMenu) { throw new UnsupportedOperationException(); } diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/grpc/OrderServiceServer.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/grpc/OrderServiceServer.java index 6303e941..6448f981 100644 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/grpc/OrderServiceServer.java +++ b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/grpc/OrderServiceServer.java @@ -3,15 +3,21 @@ import io.grpc.Server; import io.grpc.ServerBuilder; import io.grpc.stub.StreamObserver; +import net.chrisrichardson.ftgo.common.Address; +import net.chrisrichardson.ftgo.orderservice.domain.DeliveryInformation; import net.chrisrichardson.ftgo.orderservice.domain.Order; import net.chrisrichardson.ftgo.orderservice.domain.OrderService; import net.chrisrichardson.ftgo.orderservice.web.MenuItemIdAndQuantity; +import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.annotation.PostConstruct; import javax.annotation.PreDestroy; import java.io.IOException; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; import static java.util.stream.Collectors.toList; @@ -49,15 +55,21 @@ private class OrderServiceImpl extends OrderServiceGrpc.OrderServiceImplBase { @Override public void createOrder(CreateOrderRequest req, StreamObserver responseObserver) { + List lineItemsList = req.getLineItemsList(); Order order = orderService.createOrder(req.getConsumerId(), req.getRestaurantId(), - req.getLineItemsList().stream().map(x -> new MenuItemIdAndQuantity(x.getMenuItemId(), x.getQuantity())).collect(toList()) + new DeliveryInformation(LocalDateTime.parse(req.getDeliveryTime(), DateTimeFormatter.ISO_LOCAL_DATE_TIME), makeAddress(req.getDeliveryAddress())), + lineItemsList.stream().map(x -> new MenuItemIdAndQuantity(x.getMenuItemId(), x.getQuantity())).collect(toList()) ); CreateOrderReply reply = CreateOrderReply.newBuilder().setOrderId(order.getId()).build(); responseObserver.onNext(reply); responseObserver.onCompleted(); } + private Address makeAddress(net.chrisrichardson.ftgo.orderservice.grpc.Address address) { + return new Address(address.getStreet1(), nullIfBlank(address.getStreet2()), address.getCity(), address.getState(), address.getZip()); + } + @Override public void cancelOrder(CancelOrderRequest req, StreamObserver responseObserver) { CancelOrderReply reply = CancelOrderReply.newBuilder().setMessage("Hello " + req.getName()).build(); @@ -72,4 +84,8 @@ public void reviseOrder(ReviseOrderRequest req, StreamObserver responseObserver.onCompleted(); } } + + private String nullIfBlank(String s) { + return StringUtils.isBlank(s) ? null : s; + } } diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/main/OrderServiceMain.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/main/OrderServiceMain.java index 49a084ae..9249122c 100644 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/main/OrderServiceMain.java +++ b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/main/OrderServiceMain.java @@ -1,8 +1,8 @@ package net.chrisrichardson.ftgo.orderservice.main; -import io.eventuate.jdbckafka.TramJdbcKafkaConfiguration; -import io.eventuate.tram.commands.common.ChannelMapping; -import io.eventuate.tram.commands.common.DefaultChannelMapping; +import io.eventuate.tram.spring.jdbckafka.TramJdbcKafkaConfiguration; +import io.microservices.canvas.extractor.spring.annotations.ServiceDescription; +import io.microservices.canvas.springmvc.MicroserviceCanvasWebConfiguration; import net.chrisrichardson.eventstore.examples.customersandorders.commonswagger.CommonSwaggerConfiguration; import net.chrisrichardson.ftgo.orderservice.grpc.GrpcConfiguration; import net.chrisrichardson.ftgo.orderservice.messaging.OrderServiceMessagingConfiguration; @@ -10,19 +10,15 @@ import net.chrisrichardson.ftgo.orderservice.web.OrderWebConfiguration; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Import; @SpringBootApplication @Import({OrderWebConfiguration.class, OrderCommandHandlersConfiguration.class, OrderServiceMessagingConfiguration.class, - TramJdbcKafkaConfiguration.class, CommonSwaggerConfiguration.class, GrpcConfiguration.class}) + TramJdbcKafkaConfiguration.class, CommonSwaggerConfiguration.class, GrpcConfiguration.class, + MicroserviceCanvasWebConfiguration.class}) +@ServiceDescription(description="Manages Orders", capabilities = "Order Management") public class OrderServiceMain { - @Bean - public ChannelMapping channelMapping() { - return new DefaultChannelMapping.DefaultChannelMappingBuilder().build(); - } - public static void main(String[] args) { SpringApplication.run(OrderServiceMain.class, args); } diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/messaging/OrderEventConsumer.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/messaging/OrderEventConsumer.java index 5d7b39d7..c09e8c4f 100644 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/messaging/OrderEventConsumer.java +++ b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/messaging/OrderEventConsumer.java @@ -5,10 +5,7 @@ import io.eventuate.tram.events.subscriber.DomainEventHandlersBuilder; import net.chrisrichardson.ftgo.orderservice.domain.OrderService; import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantCreated; -import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantMenu; import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantMenuRevised; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; public class OrderEventConsumer { @@ -30,17 +27,13 @@ public DomainEventHandlers domainEventHandlers() { private void createMenu(DomainEventEnvelope de) { String restaurantIds = de.getAggregateId(); long id = Long.parseLong(restaurantIds); - RestaurantMenu menu = de.getEvent().getMenu(); - orderService.createMenu(id, de.getEvent().getName(), menu); + orderService.createMenu(id, de.getEvent().getName(), RestaurantEventMapper.toMenuItems(de.getEvent().getMenu().getMenuItems())); } public void reviseMenu(DomainEventEnvelope de) { - String restaurantIds = de.getAggregateId(); long id = Long.parseLong(restaurantIds); - RestaurantMenu revisedMenu = de.getEvent().getRevisedMenu(); - - orderService.reviseMenu(id, revisedMenu); + orderService.reviseMenu(id, RestaurantEventMapper.toMenuItems(de.getEvent().getMenu().getMenuItems())); } } diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/messaging/OrderServiceMessagingConfiguration.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/messaging/OrderServiceMessagingConfiguration.java index 85021c80..d25e0105 100644 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/messaging/OrderServiceMessagingConfiguration.java +++ b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/messaging/OrderServiceMessagingConfiguration.java @@ -1,7 +1,8 @@ package net.chrisrichardson.ftgo.orderservice.messaging; +import io.eventuate.tram.spring.events.subscriber.TramEventSubscriberConfiguration; import io.eventuate.tram.events.subscriber.DomainEventDispatcher; -import io.eventuate.tram.messaging.consumer.MessageConsumer; +import io.eventuate.tram.events.subscriber.DomainEventDispatcherFactory; import net.chrisrichardson.ftgo.orderservice.domain.OrderService; import net.chrisrichardson.ftgo.orderservice.domain.OrderServiceWithRepositoriesConfiguration; import org.springframework.context.annotation.Bean; @@ -9,7 +10,7 @@ import org.springframework.context.annotation.Import; @Configuration -@Import({OrderServiceWithRepositoriesConfiguration.class}) +@Import({OrderServiceWithRepositoriesConfiguration.class, TramEventSubscriberConfiguration.class}) public class OrderServiceMessagingConfiguration { @Bean @@ -18,8 +19,8 @@ public OrderEventConsumer orderEventConsumer(OrderService orderService) { } @Bean - public DomainEventDispatcher domainEventDispatcher(OrderEventConsumer orderEventConsumer, MessageConsumer messageConsumer) { - return new DomainEventDispatcher("orderServiceEvents", orderEventConsumer.domainEventHandlers(), messageConsumer); // @Autowire + public DomainEventDispatcher domainEventDispatcher(OrderEventConsumer orderEventConsumer, DomainEventDispatcherFactory domainEventDispatcherFactory) { + return domainEventDispatcherFactory.make("orderServiceEvents", orderEventConsumer.domainEventHandlers()); } } diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/messaging/RestaurantEventMapper.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/messaging/RestaurantEventMapper.java new file mode 100644 index 00000000..b158df15 --- /dev/null +++ b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/messaging/RestaurantEventMapper.java @@ -0,0 +1,26 @@ +package net.chrisrichardson.ftgo.orderservice.messaging; + +import net.chrisrichardson.ftgo.common.Money; +import net.chrisrichardson.ftgo.restaurantservice.events.Address; +import net.chrisrichardson.ftgo.restaurantservice.events.MenuItem; +import org.jetbrains.annotations.NotNull; + +import java.util.List; +import java.util.stream.Collectors; + +public class RestaurantEventMapper { + + @NotNull + public static List fromMenuItems(List menuItems) { + return menuItems.stream().map(mi -> new MenuItem().withId(mi.getId()).withName(mi.getName()).withPrice(mi.getPrice().asString())).collect(Collectors.toList()); + } + + public static Address fromAddress(net.chrisrichardson.ftgo.common.Address a) { + return new Address().withStreet1(a.getStreet1()).withStreet2(a.getStreet2()).withCity(a.getCity()).withZip(a.getZip()); + } + + public static List toMenuItems(List menuItems) { + return menuItems.stream().map(mi -> new net.chrisrichardson.ftgo.orderservice.domain.MenuItem(mi.getId(), mi.getName(), new Money(mi.getPrice()))).collect(Collectors.toList()); + } + +} diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagaparticipants/AccountingServiceProxy.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagaparticipants/AccountingServiceProxy.java index 814f0f5f..f360939b 100644 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagaparticipants/AccountingServiceProxy.java +++ b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagaparticipants/AccountingServiceProxy.java @@ -5,8 +5,6 @@ import io.eventuate.tram.sagas.simpledsl.CommandEndpointBuilder; import net.chrisrichardson.ftgo.accountservice.api.AccountingServiceChannels; import net.chrisrichardson.ftgo.accountservice.api.AuthorizeCommand; -import net.chrisrichardson.ftgo.consumerservice.api.ConsumerServiceChannels; -import net.chrisrichardson.ftgo.consumerservice.api.ValidateOrderByConsumer; public class AccountingServiceProxy { diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagaparticipants/ConsumerServiceProxy.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagaparticipants/ConsumerServiceProxy.java index 1746edf8..8c50dcf6 100644 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagaparticipants/ConsumerServiceProxy.java +++ b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagaparticipants/ConsumerServiceProxy.java @@ -5,7 +5,6 @@ import io.eventuate.tram.sagas.simpledsl.CommandEndpointBuilder; import net.chrisrichardson.ftgo.consumerservice.api.ConsumerServiceChannels; import net.chrisrichardson.ftgo.consumerservice.api.ValidateOrderByConsumer; -import net.chrisrichardson.ftgo.orderservice.api.OrderServiceChannels; public class ConsumerServiceProxy { diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagaparticipants/KitchenServiceProxy.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagaparticipants/KitchenServiceProxy.java index d50ba938..19a653df 100644 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagaparticipants/KitchenServiceProxy.java +++ b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagaparticipants/KitchenServiceProxy.java @@ -9,18 +9,18 @@ public class KitchenServiceProxy { public final CommandEndpoint create = CommandEndpointBuilder .forCommand(CreateTicket.class) - .withChannel(KitchenServiceChannels.kitchenServiceChannel) + .withChannel(KitchenServiceChannels.COMMAND_CHANNEL) .withReply(CreateTicketReply.class) .build(); public final CommandEndpoint confirmCreate = CommandEndpointBuilder .forCommand(ConfirmCreateTicket.class) - .withChannel(KitchenServiceChannels.kitchenServiceChannel) + .withChannel(KitchenServiceChannels.COMMAND_CHANNEL) .withReply(Success.class) .build(); public final CommandEndpoint cancel = CommandEndpointBuilder .forCommand(CancelCreateTicket.class) - .withChannel(KitchenServiceChannels.kitchenServiceChannel) + .withChannel(KitchenServiceChannels.COMMAND_CHANNEL) .withReply(Success.class) .build(); diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagaparticipants/OrderServiceProxy.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagaparticipants/OrderServiceProxy.java index 876943c2..ea3e6182 100644 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagaparticipants/OrderServiceProxy.java +++ b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagaparticipants/OrderServiceProxy.java @@ -9,13 +9,13 @@ public class OrderServiceProxy { public final CommandEndpoint reject = CommandEndpointBuilder .forCommand(RejectOrderCommand.class) - .withChannel(OrderServiceChannels.orderServiceChannel) + .withChannel(OrderServiceChannels.COMMAND_CHANNEL) .withReply(Success.class) .build(); public final CommandEndpoint approve = CommandEndpointBuilder .forCommand(ApproveOrderCommand.class) - .withChannel(OrderServiceChannels.orderServiceChannel) + .withChannel(OrderServiceChannels.COMMAND_CHANNEL) .withReply(Success.class) .build(); diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagas/cancelorder/CancelOrderSaga.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagas/cancelorder/CancelOrderSaga.java index 588e739c..6a12e1ec 100644 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagas/cancelorder/CancelOrderSaga.java +++ b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagas/cancelorder/CancelOrderSaga.java @@ -46,14 +46,14 @@ public void initializeSagaDefinition() { private CommandWithDestination confirmOrderCancel(CancelOrderSagaData data) { return send(new ConfirmCancelOrderCommand(data.getOrderId())) - .to(OrderServiceChannels.orderServiceChannel) + .to(OrderServiceChannels.COMMAND_CHANNEL) .build(); } private CommandWithDestination confirmTicketCancel(CancelOrderSagaData data) { return send(new ConfirmCancelTicketCommand(data.getRestaurantId(), data.getOrderId())) - .to(KitchenServiceChannels.kitchenServiceChannel) + .to(KitchenServiceChannels.COMMAND_CHANNEL) .build(); } @@ -67,27 +67,27 @@ private CommandWithDestination reverseAuthorization(CancelOrderSagaData data) { private CommandWithDestination undoBeginCancelTicket(CancelOrderSagaData data) { return send(new UndoBeginCancelTicketCommand(data.getRestaurantId(), data.getOrderId())) - .to(KitchenServiceChannels.kitchenServiceChannel) + .to(KitchenServiceChannels.COMMAND_CHANNEL) .build(); } private CommandWithDestination beginCancelTicket(CancelOrderSagaData data) { return send(new BeginCancelTicketCommand(data.getRestaurantId(), (long) data.getOrderId())) - .to(KitchenServiceChannels.kitchenServiceChannel) + .to(KitchenServiceChannels.COMMAND_CHANNEL) .build(); } private CommandWithDestination undoBeginCancel(CancelOrderSagaData data) { return send(new UndoBeginCancelCommand(data.getOrderId())) - .to(OrderServiceChannels.orderServiceChannel) + .to(OrderServiceChannels.COMMAND_CHANNEL) .build(); } private CommandWithDestination beginCancel(CancelOrderSagaData data) { return send(new BeginCancelCommand(data.getOrderId())) - .to(OrderServiceChannels.orderServiceChannel) + .to(OrderServiceChannels.COMMAND_CHANNEL) .build(); } diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagas/createorder/CreateOrderSaga.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagas/createorder/CreateOrderSaga.java index 69898e58..b7944eeb 100644 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagas/createorder/CreateOrderSaga.java +++ b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagas/createorder/CreateOrderSaga.java @@ -26,7 +26,7 @@ public CreateOrderSaga(OrderServiceProxy orderService, ConsumerServiceProxy cons .onReply(CreateTicketReply.class, CreateOrderSagaState::handleCreateTicketReply) .withCompensation(kitchenService.cancel, CreateOrderSagaState::makeCancelCreateTicketCommand) .step() - .invokeParticipant(accountingService.authorize, CreateOrderSagaState::makeAuthorizeCommand) + .invokeParticipant(accountingService.authorize, CreateOrderSagaState::makeAuthorizeCommand) .step() .invokeParticipant(kitchenService.confirmCreate, CreateOrderSagaState::makeConfirmCreateTicketCommand) .step() diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagas/createorder/CreateOrderSagaState.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagas/createorder/CreateOrderSagaState.java index 677d52ea..274cea75 100644 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagas/createorder/CreateOrderSagaState.java +++ b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagas/createorder/CreateOrderSagaState.java @@ -94,11 +94,15 @@ RejectOrderCommand makeRejectOrderCommand() { } ValidateOrderByConsumer makeValidateOrderByConsumerCommand() { - return new ValidateOrderByConsumer(getOrderDetails().getConsumerId(), getOrderId(), getOrderDetails().getOrderTotal()); + ValidateOrderByConsumer x = new ValidateOrderByConsumer(); + x.setConsumerId(getOrderDetails().getConsumerId()); + x.setOrderId(getOrderId()); + x.setOrderTotal(getOrderDetails().getOrderTotal().asString()); + return x; } AuthorizeCommand makeAuthorizeCommand() { - return new AuthorizeCommand(getOrderDetails().getConsumerId(), getOrderId(), getOrderDetails().getOrderTotal()); + return new AuthorizeCommand().withConsumerId(getOrderDetails().getConsumerId()).withOrderId(getOrderId()).withOrderTotal(getOrderDetails().getOrderTotal().asString()); } ApproveOrderCommand makeApproveOrderCommand() { diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagas/reviseorder/ReviseOrderSaga.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagas/reviseorder/ReviseOrderSaga.java index a1f1ecd9..28e409fb 100644 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagas/reviseorder/ReviseOrderSaga.java +++ b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/sagas/reviseorder/ReviseOrderSaga.java @@ -57,14 +57,14 @@ public SagaDefinition getSagaDefinition() { private CommandWithDestination confirmOrderRevision(ReviseOrderSagaData data) { return send(new ConfirmReviseOrderCommand(data.getOrderId(), data.getOrderRevision())) - .to(OrderServiceChannels.orderServiceChannel) + .to(OrderServiceChannels.COMMAND_CHANNEL) .build(); } private CommandWithDestination confirmTicketRevision(ReviseOrderSagaData data) { - return send(new ConfirmReviseTicketCommand(data.getRestaurantId(), data.getOrderId(), data.getOrderRevision().getRevisedLineItemQuantities())) - .to(KitchenServiceChannels.kitchenServiceChannel) + return send(new ConfirmReviseTicketCommand(data.getRestaurantId(), data.getOrderId(), data.getOrderRevision().getRevisedOrderLineItems())) + .to(KitchenServiceChannels.COMMAND_CHANNEL) .build(); } @@ -78,27 +78,27 @@ private CommandWithDestination reviseAuthorization(ReviseOrderSagaData data) { private CommandWithDestination undoBeginReviseTicket(ReviseOrderSagaData data) { return send(new UndoBeginReviseTicketCommand(data.getRestaurantId(), data.getOrderId())) - .to(KitchenServiceChannels.kitchenServiceChannel) + .to(KitchenServiceChannels.COMMAND_CHANNEL) .build(); } private CommandWithDestination beginReviseTicket(ReviseOrderSagaData data) { - return send(new BeginReviseTicketCommand(data.getRestaurantId(), data.getOrderId(), data.getOrderRevision().getRevisedLineItemQuantities())) - .to(KitchenServiceChannels.kitchenServiceChannel) + return send(new BeginReviseTicketCommand(data.getRestaurantId(), data.getOrderId(), data.getOrderRevision().getRevisedOrderLineItems())) + .to(KitchenServiceChannels.COMMAND_CHANNEL) .build(); } private CommandWithDestination undoBeginReviseOrder(ReviseOrderSagaData data) { return send(new UndoBeginReviseOrderCommand(data.getOrderId())) - .to(OrderServiceChannels.orderServiceChannel) + .to(OrderServiceChannels.COMMAND_CHANNEL) .build(); } private CommandWithDestination beginReviseOrder(ReviseOrderSagaData data) { return send(new BeginReviseOrderCommand(data.getOrderId(), data.getOrderRevision())) - .to(OrderServiceChannels.orderServiceChannel) + .to(OrderServiceChannels.COMMAND_CHANNEL) .build(); } diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/service/OrderCommandHandlersConfiguration.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/service/OrderCommandHandlersConfiguration.java index 7c86c7f0..75df4339 100644 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/service/OrderCommandHandlersConfiguration.java +++ b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/service/OrderCommandHandlersConfiguration.java @@ -1,15 +1,16 @@ package net.chrisrichardson.ftgo.orderservice.service; -import io.eventuate.tram.events.publisher.TramEventsPublisherConfiguration; +import io.eventuate.tram.spring.events.publisher.TramEventsPublisherConfiguration; import io.eventuate.tram.sagas.participant.SagaCommandDispatcher; -import io.eventuate.tram.sagas.participant.SagaParticipantConfiguration; +import io.eventuate.tram.sagas.participant.SagaCommandDispatcherFactory; +import io.eventuate.tram.sagas.spring.participant.SagaParticipantConfiguration; import net.chrisrichardson.ftgo.common.CommonConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @Configuration -@Import({SagaParticipantConfiguration.class, TramEventsPublisherConfiguration.class, CommonConfiguration.class}) +@Import({SagaParticipantConfiguration.class, TramEventsPublisherConfiguration.class, CommonConfiguration.class, SagaParticipantConfiguration.class}) public class OrderCommandHandlersConfiguration { @Bean @@ -18,8 +19,8 @@ public OrderCommandHandlers orderCommandHandlers() { } @Bean - public SagaCommandDispatcher orderCommandHandlersDispatcher(OrderCommandHandlers orderCommandHandlers) { - return new SagaCommandDispatcher("orderService", orderCommandHandlers.commandHandlers()); + public SagaCommandDispatcher orderCommandHandlersDispatcher(OrderCommandHandlers orderCommandHandlers, SagaCommandDispatcherFactory sagaCommandDispatcherFactory) { + return sagaCommandDispatcherFactory.make("orderService", orderCommandHandlers.commandHandlers()); } } diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/web/GetOrderResponse.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/web/GetOrderResponse.java index a6956488..70a3537b 100644 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/web/GetOrderResponse.java +++ b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/web/GetOrderResponse.java @@ -1,16 +1,17 @@ package net.chrisrichardson.ftgo.orderservice.web; import net.chrisrichardson.ftgo.common.Money; +import net.chrisrichardson.ftgo.orderservice.api.events.OrderState; public class GetOrderResponse { private long orderId; - private String state; + private OrderState state; private Money orderTotal; private GetOrderResponse() { } - public GetOrderResponse(long orderId, String state, Money orderTotal) { + public GetOrderResponse(long orderId, OrderState state, Money orderTotal) { this.orderId = orderId; this.state = state; this.orderTotal = orderTotal; @@ -32,11 +33,11 @@ public void setOrderId(long orderId) { this.orderId = orderId; } - public String getState() { + public OrderState getState() { return state; } - public void setState(String state) { + public void setState(OrderState state) { this.state = state; } } diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/web/MenuItemIdAndQuantity.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/web/MenuItemIdAndQuantity.java index d52c85cd..a3392959 100644 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/web/MenuItemIdAndQuantity.java +++ b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/web/MenuItemIdAndQuantity.java @@ -1,5 +1,9 @@ package net.chrisrichardson.ftgo.orderservice.web; +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; + public class MenuItemIdAndQuantity { private String menuItemId; @@ -23,4 +27,20 @@ public MenuItemIdAndQuantity(String menuItemId, int quantity) { this.quantity = quantity; } + + @Override + public boolean equals(Object o) { + return EqualsBuilder.reflectionEquals(this, o); + } + + @Override + public int hashCode() { + return HashCodeBuilder.reflectionHashCode(this); + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + } diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/web/OrderController.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/web/OrderController.java index 699de9df..1a3c5d60 100644 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/web/OrderController.java +++ b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/web/OrderController.java @@ -34,6 +34,7 @@ public OrderController(OrderService orderService, OrderRepository orderRepositor public CreateOrderResponse create(@RequestBody CreateOrderRequest request) { Order order = orderService.createOrder(request.getConsumerId(), request.getRestaurantId(), + new DeliveryInformation(request.getDeliveryTime(), request.getDeliveryAddress()), request.getLineItems().stream().map(x -> new MenuItemIdAndQuantity(x.getMenuItemId(), x.getQuantity())).collect(toList()) ); return new CreateOrderResponse(order.getId()); @@ -47,7 +48,7 @@ public ResponseEntity getOrder(@PathVariable long orderId) { } private GetOrderResponse makeGetOrderResponse(Order order) { - return new GetOrderResponse(order.getId(), order.getState().name(), order.getOrderTotal()); + return new GetOrderResponse(order.getId(), order.getState(), order.getOrderTotal()); } @RequestMapping(path = "/{orderId}/cancel", method = RequestMethod.POST) @@ -63,7 +64,7 @@ public ResponseEntity cancel(@PathVariable long orderId) { @RequestMapping(path = "/{orderId}/revise", method = RequestMethod.POST) public ResponseEntity revise(@PathVariable long orderId, @RequestBody ReviseOrderRequest request) { try { - Order order = orderService.reviseOrder(orderId, new OrderRevision(Optional.empty(), request.getRevisedLineItemQuantities())); + Order order = orderService.reviseOrder(orderId, new OrderRevision(Optional.empty(), request.getRevisedOrderLineItems())); return new ResponseEntity<>(makeGetOrderResponse(order), HttpStatus.OK); } catch (OrderNotFoundException e) { return new ResponseEntity<>(HttpStatus.NOT_FOUND); diff --git a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/web/OrderWebConfiguration.java b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/web/OrderWebConfiguration.java index c4a7de19..63c6b081 100644 --- a/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/web/OrderWebConfiguration.java +++ b/ftgo-order-service/src/main/java/net/chrisrichardson/ftgo/orderservice/web/OrderWebConfiguration.java @@ -2,7 +2,7 @@ import brave.sampler.Sampler; import com.fasterxml.jackson.databind.ObjectMapper; -import io.eventuate.javaclient.commonimpl.JSonMapper; +import io.eventuate.common.json.mapper.JSonMapper; import net.chrisrichardson.ftgo.orderservice.domain.OrderServiceWithRepositoriesConfiguration; import org.springframework.context.annotation.*; diff --git a/ftgo-order-service/src/main/proto/OrderService.proto b/ftgo-order-service/src/main/proto/OrderService.proto index c0f85fbc..77a110d4 100644 --- a/ftgo-order-service/src/main/proto/OrderService.proto +++ b/ftgo-order-service/src/main/proto/OrderService.proto @@ -17,6 +17,16 @@ message CreateOrderRequest { int64 restaurantId = 1; int64 consumerId = 2; repeated LineItem lineItems = 3; + Address deliveryAddress = 4; + string deliveryTime = 5; +} + +message Address { + string street1 = 1; + string street2 = 2; + string city = 3; + string state = 4; + string zip = 5; } message LineItem { diff --git a/ftgo-order-service/src/main/resources/application.properties b/ftgo-order-service/src/main/resources/application.properties index a5233806..89121df7 100644 --- a/ftgo-order-service/src/main/resources/application.properties +++ b/ftgo-order-service/src/main/resources/application.properties @@ -1,8 +1,8 @@ spring.application.name=ftgo-order-service - +management.endpoint.health.show-details=always spring.sleuth.sampler.probability=1.0 -management.endpoints.web.exposure.include=health,prometheus +management.endpoints.web.exposure.include=health,prometheus,beans,endpoints logging.level.org.springframework.cloud=INFO @@ -11,19 +11,13 @@ logging.level.org.springframework.orm.jpa=INFO logging.level.org.hibernate.SQL=DEBUG logging.level.io.eventuate=DEBUG logging.level.net.chrisrichardson.ftgo=DEBUG -logging.level.io.eventuate.tram=TRACE -spring.datasource.url=jdbc:mysql://${DOCKER_HOST_IP:localhost}/eventuate -spring.datasource.username=mysqluser -spring.datasource.password=mysqlpw -spring.datasource.driver.class.name=com.mysql.jdbc.Driver -spring.data.mongodb.uri=mongodb://${DOCKER_HOST_IP:localhost}/bankingexampledb +logging.level.io.eventuate.tram=DEBUG + +eventuate.database.schema=none +spring.datasource.url=jdbc:mysql://${DOCKER_HOST_IP:localhost}/ftgo_order_service +spring.datasource.username=ftgo_order_service_user +spring.datasource.password=ftgo_order_service_password +spring.datasource.driver-class-name=com.mysql.jdbc.Driver eventuatelocal.kafka.bootstrap.servers=${DOCKER_HOST_IP:localhost}:9092 -eventuatelocal.cdc.db.user.name=root -eventuatelocal.cdc.db.password=rootpassword eventuatelocal.zookeeper.connection.string=${DOCKER_HOST_IP:localhost}:2181 - -aws.access.key_id=id_key -aws.secret.access.key=access_key -aws.dynamodb.endpoint.url=http://${DOCKER_HOST_IP:localhost}:8000 -aws.region=us-west-2 diff --git a/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/OrderDetailsMother.java b/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/OrderDetailsMother.java index 6ca279d6..d6bd2869 100644 --- a/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/OrderDetailsMother.java +++ b/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/OrderDetailsMother.java @@ -1,12 +1,15 @@ package net.chrisrichardson.ftgo.orderservice; +import net.chrisrichardson.ftgo.common.Address; import net.chrisrichardson.ftgo.common.Money; import net.chrisrichardson.ftgo.orderservice.api.events.OrderDetails; import net.chrisrichardson.ftgo.orderservice.api.events.OrderLineItem; import net.chrisrichardson.ftgo.orderservice.api.events.OrderState; +import net.chrisrichardson.ftgo.orderservice.domain.DeliveryInformation; import net.chrisrichardson.ftgo.orderservice.domain.Order; import net.chrisrichardson.ftgo.orderservice.web.MenuItemIdAndQuantity; +import java.time.LocalDateTime; import java.util.Collections; import java.util.List; @@ -35,13 +38,18 @@ public static List chickenVindalooLineItems() { public static long ORDER_ID = 99L; - public static Order CHICKEN_VINDALOO_ORDER = makeAjantaOrder(); - public static final OrderState CHICKEN_VINDALOO_ORDER_STATE = OrderState.APPROVAL_PENDING; + public static final Address DELIVERY_ADDRESS = new Address("9 Amazing View", null, "Oakland", "CA", "94612"); + public static final LocalDateTime DELIVERY_TIME = LocalDateTime.now(); + public static final DeliveryInformation DELIVERY_INFORMATION = new DeliveryInformation(DELIVERY_TIME, DELIVERY_ADDRESS); + private static Order makeAjantaOrder() { - Order order = new Order(CONSUMER_ID, AJANTA_ID, chickenVindalooLineItems()); + Order order = new Order(CONSUMER_ID, AJANTA_ID, new DeliveryInformation(DELIVERY_TIME, DELIVERY_ADDRESS), chickenVindalooLineItems()); order.setId(ORDER_ID); return order; } + + public static Order CHICKEN_VINDALOO_ORDER = makeAjantaOrder(); + } diff --git a/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/RestaurantMother.java b/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/RestaurantMother.java index 988da66b..cd45d0e6 100644 --- a/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/RestaurantMother.java +++ b/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/RestaurantMother.java @@ -1,9 +1,12 @@ package net.chrisrichardson.ftgo.orderservice; +import net.chrisrichardson.ftgo.common.Address; import net.chrisrichardson.ftgo.common.Money; +import net.chrisrichardson.ftgo.orderservice.domain.MenuItem; import net.chrisrichardson.ftgo.orderservice.domain.Restaurant; -import net.chrisrichardson.ftgo.restaurantservice.events.MenuItem; -import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantMenu; +import net.chrisrichardson.ftgo.orderservice.messaging.RestaurantEventMapper; +import net.chrisrichardson.ftgo.restaurantservice.events.Menu; +import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantCreated; import java.util.Collections; import java.util.List; @@ -15,11 +18,18 @@ public class RestaurantMother { public static final String CHICKEN_VINDALOO = "Chicken Vindaloo"; public static final String CHICKEN_VINDALOO_MENU_ITEM_ID = "1"; public static final Money CHICKEN_VINDALOO_PRICE = new Money("12.34"); + public static final Address RESTAURANT_ADDRESS = new Address("1 Main Street", "Unit 99", "Oakland", "CA", "94611"); public static MenuItem CHICKEN_VINDALOO_MENU_ITEM = new MenuItem(CHICKEN_VINDALOO_MENU_ITEM_ID, CHICKEN_VINDALOO, CHICKEN_VINDALOO_PRICE); - public static final List AJANTA_RESTAURANT_MENU_ITEMS = Collections.singletonList(new MenuItem(CHICKEN_VINDALOO_MENU_ITEM_ID, CHICKEN_VINDALOO, CHICKEN_VINDALOO_PRICE)); - public static final RestaurantMenu AJANTA_RESTAURANT_MENU = new RestaurantMenu(AJANTA_RESTAURANT_MENU_ITEMS); + public static final List AJANTA_RESTAURANT_MENU_ITEMS = Collections.singletonList(CHICKEN_VINDALOO_MENU_ITEM); + public static final Restaurant AJANTA_RESTAURANT = new Restaurant(AJANTA_ID, AJANTA_RESTAURANT_NAME, AJANTA_RESTAURANT_MENU_ITEMS); + + public static RestaurantCreated makeAjantaRestaurantCreatedEvent() { + return new RestaurantCreated().withName(AJANTA_RESTAURANT_NAME) + .withAddress(RestaurantEventMapper.fromAddress(RESTAURANT_ADDRESS)) + .withMenu(new Menu().withMenuItems(RestaurantEventMapper.fromMenuItems(AJANTA_RESTAURANT_MENU_ITEMS))); + } } diff --git a/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/domain/OrderDomainEventPublisherTest.java b/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/domain/OrderDomainEventPublisherTest.java new file mode 100644 index 00000000..55ea8375 --- /dev/null +++ b/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/domain/OrderDomainEventPublisherTest.java @@ -0,0 +1,15 @@ +package net.chrisrichardson.ftgo.orderservice.domain; + +import net.chrisrichardson.ftgo.orderservice.api.OrderServiceChannels; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class OrderDomainEventPublisherTest { + + @Test + public void verifyOrderEventChannel() { + assertEquals(OrderServiceChannels.ORDER_EVENT_CHANNEL, new OrderDomainEventPublisher(null).getAggregateType().getName()); + } + +} \ No newline at end of file diff --git a/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/domain/OrderServiceTest.java b/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/domain/OrderServiceTest.java index 3793f841..0a85e1eb 100644 --- a/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/domain/OrderServiceTest.java +++ b/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/domain/OrderServiceTest.java @@ -1,12 +1,14 @@ package net.chrisrichardson.ftgo.orderservice.domain; import io.eventuate.tram.events.publisher.DomainEventPublisher; -import io.eventuate.tram.sagas.orchestration.SagaManager; +import io.eventuate.tram.sagas.orchestration.SagaInstanceFactory; +import net.chrisrichardson.ftgo.orderservice.OrderDetailsMother; import net.chrisrichardson.ftgo.orderservice.RestaurantMother; import net.chrisrichardson.ftgo.orderservice.api.events.OrderCreatedEvent; -import net.chrisrichardson.ftgo.orderservice.sagas.cancelorder.CancelOrderSagaData; +import net.chrisrichardson.ftgo.orderservice.sagas.cancelorder.CancelOrderSaga; +import net.chrisrichardson.ftgo.orderservice.sagas.createorder.CreateOrderSaga; import net.chrisrichardson.ftgo.orderservice.sagas.createorder.CreateOrderSagaState; -import net.chrisrichardson.ftgo.orderservice.sagas.reviseorder.ReviseOrderSagaData; +import net.chrisrichardson.ftgo.orderservice.sagas.reviseorder.ReviseOrderSaga; import org.junit.Before; import org.junit.Test; @@ -31,26 +33,28 @@ public class OrderServiceTest { private OrderRepository orderRepository; private DomainEventPublisher eventPublisher; private RestaurantRepository restaurantRepository; - private SagaManager createOrderSagaManager; - private SagaManager cancelOrderSagaManager; - private SagaManager reviseOrderSagaManager; + private SagaInstanceFactory sagaInstanceFactory; + private CreateOrderSaga createOrderSaga; + private CancelOrderSaga cancelOrderSaga; + private ReviseOrderSaga reviseOrderSaga; private OrderDomainEventPublisher orderAggregateEventPublisher; @Before public void setup() { + sagaInstanceFactory = mock(SagaInstanceFactory.class); orderRepository = mock(OrderRepository.class); eventPublisher = mock(DomainEventPublisher.class); restaurantRepository = mock(RestaurantRepository.class); - createOrderSagaManager = mock(SagaManager.class); - cancelOrderSagaManager = mock(SagaManager.class); - reviseOrderSagaManager = mock(SagaManager.class); + createOrderSaga = mock(CreateOrderSaga.class); + cancelOrderSaga = mock(CancelOrderSaga.class); + reviseOrderSaga = mock(ReviseOrderSaga.class); // Mock DomainEventPublisher AND use the real OrderDomainEventPublisher orderAggregateEventPublisher = mock(OrderDomainEventPublisher.class); - orderService = new OrderService(orderRepository, eventPublisher, restaurantRepository, - createOrderSagaManager, cancelOrderSagaManager, reviseOrderSagaManager, orderAggregateEventPublisher, Optional.empty()); + orderService = new OrderService(sagaInstanceFactory, orderRepository, eventPublisher, restaurantRepository, + createOrderSaga, cancelOrderSaga, reviseOrderSaga, orderAggregateEventPublisher, Optional.empty()); } @@ -63,14 +67,14 @@ public void shouldCreateOrder() { return order; }); - Order order = orderService.createOrder(CONSUMER_ID, AJANTA_ID, CHICKEN_VINDALOO_MENU_ITEMS_AND_QUANTITIES); + Order order = orderService.createOrder(CONSUMER_ID, AJANTA_ID, OrderDetailsMother.DELIVERY_INFORMATION, CHICKEN_VINDALOO_MENU_ITEMS_AND_QUANTITIES); verify(orderRepository).save(same(order)); verify(orderAggregateEventPublisher).publish(order, - Collections.singletonList(new OrderCreatedEvent(CHICKEN_VINDALOO_ORDER_DETAILS, RestaurantMother.AJANTA_RESTAURANT_NAME))); + Collections.singletonList(new OrderCreatedEvent(CHICKEN_VINDALOO_ORDER_DETAILS, OrderDetailsMother.DELIVERY_ADDRESS, RestaurantMother.AJANTA_RESTAURANT_NAME))); - verify(createOrderSagaManager).create(new CreateOrderSagaState(ORDER_ID, CHICKEN_VINDALOO_ORDER_DETAILS), Order.class, ORDER_ID); + verify(sagaInstanceFactory).create(createOrderSaga, new CreateOrderSagaState(ORDER_ID, CHICKEN_VINDALOO_ORDER_DETAILS)); } // TODO write tests for other methods diff --git a/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/domain/OrderTest.java b/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/domain/OrderTest.java index fc690521..ef3f93d1 100644 --- a/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/domain/OrderTest.java +++ b/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/domain/OrderTest.java @@ -1,7 +1,10 @@ package net.chrisrichardson.ftgo.orderservice.domain; import io.eventuate.tram.events.aggregates.ResultWithDomainEvents; +import net.chrisrichardson.ftgo.common.RevisedOrderLineItem; +import net.chrisrichardson.ftgo.orderservice.OrderDetailsMother; import net.chrisrichardson.ftgo.orderservice.RestaurantMother; +import net.chrisrichardson.ftgo.orderservice.api.events.OrderAuthorized; import net.chrisrichardson.ftgo.orderservice.api.events.OrderCreatedEvent; import net.chrisrichardson.ftgo.orderservice.api.events.OrderDomainEvent; import net.chrisrichardson.ftgo.orderservice.api.events.OrderState; @@ -14,7 +17,6 @@ import static java.util.Collections.singletonList; import static net.chrisrichardson.ftgo.orderservice.OrderDetailsMother.*; -import static net.chrisrichardson.ftgo.orderservice.RestaurantMother.AJANTA_ID; import static net.chrisrichardson.ftgo.orderservice.RestaurantMother.AJANTA_RESTAURANT; import static net.chrisrichardson.ftgo.orderservice.RestaurantMother.CHICKEN_VINDALOO_PRICE; import static org.junit.Assert.assertEquals; @@ -26,13 +28,13 @@ public class OrderTest { @Before public void setUp() throws Exception { - createResult = Order.createOrder(CONSUMER_ID, AJANTA_RESTAURANT, chickenVindalooLineItems()); + createResult = Order.createOrder(CONSUMER_ID, AJANTA_RESTAURANT, OrderDetailsMother.DELIVERY_INFORMATION, chickenVindalooLineItems()); order = createResult.result; } @Test public void shouldCreateOrder() { - assertEquals(singletonList(new OrderCreatedEvent(CHICKEN_VINDALOO_ORDER_DETAILS, RestaurantMother.AJANTA_RESTAURANT_NAME)), createResult.events); + assertEquals(singletonList(new OrderCreatedEvent(CHICKEN_VINDALOO_ORDER_DETAILS, OrderDetailsMother.DELIVERY_ADDRESS, RestaurantMother.AJANTA_RESTAURANT_NAME)), createResult.events); assertEquals(OrderState.APPROVAL_PENDING, order.getState()); // ... @@ -55,7 +57,7 @@ public void shouldReviseOrder() { order.noteApproved(); - OrderRevision orderRevision = new OrderRevision(Optional.empty(), Collections.singletonMap("1", 10)); + OrderRevision orderRevision = new OrderRevision(Optional.empty(), Collections.singletonList(new RevisedOrderLineItem(10, "1"))); ResultWithDomainEvents result = order.revise(orderRevision); diff --git a/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/messaging/OrderEventConsumerTest.java b/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/messaging/OrderEventConsumerTest.java index 3b3d3fd2..75b0b204 100644 --- a/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/messaging/OrderEventConsumerTest.java +++ b/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/messaging/OrderEventConsumerTest.java @@ -3,8 +3,6 @@ import net.chrisrichardson.ftgo.common.CommonJsonMapperInitializer; import net.chrisrichardson.ftgo.orderservice.RestaurantMother; import net.chrisrichardson.ftgo.orderservice.domain.OrderService; -import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantCreated; -import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantMenu; import org.junit.Before; import org.junit.Test; @@ -20,7 +18,7 @@ public class OrderEventConsumerTest { private OrderEventConsumer orderEventConsumer; @Before - public void setUp() throws Exception { + public void setUp() { orderService = mock(OrderService.class); orderEventConsumer = new OrderEventConsumer(orderService); } @@ -34,10 +32,10 @@ public void shouldCreateMenu() { eventHandlers(orderEventConsumer.domainEventHandlers()). when(). aggregate("net.chrisrichardson.ftgo.restaurantservice.domain.Restaurant", AJANTA_ID). - publishes(new RestaurantCreated(AJANTA_RESTAURANT_NAME, RestaurantMother.AJANTA_RESTAURANT_MENU)). + publishes(RestaurantMother.makeAjantaRestaurantCreatedEvent()). then(). verify(() -> { - verify(orderService).createMenu(AJANTA_ID, AJANTA_RESTAURANT_NAME, new RestaurantMenu(RestaurantMother.AJANTA_RESTAURANT_MENU_ITEMS)); + verify(orderService).createMenu(AJANTA_ID, AJANTA_RESTAURANT_NAME, RestaurantMother.AJANTA_RESTAURANT_MENU_ITEMS); }) ; diff --git a/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/sagas/createorder/CreateOrderSagaTest.java b/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/sagas/createorder/CreateOrderSagaTest.java index 26d1ce13..8bb400f1 100644 --- a/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/sagas/createorder/CreateOrderSagaTest.java +++ b/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/sagas/createorder/CreateOrderSagaTest.java @@ -11,6 +11,7 @@ import net.chrisrichardson.ftgo.kitchenservice.api.KitchenServiceChannels; import net.chrisrichardson.ftgo.orderservice.api.OrderServiceChannels; import net.chrisrichardson.ftgo.orderservice.sagaparticipants.*; +import org.jetbrains.annotations.NotNull; import org.junit.BeforeClass; import org.junit.Test; @@ -40,46 +41,49 @@ public void shouldCreateOrder() { .saga(makeCreateOrderSaga(), new CreateOrderSagaState(ORDER_ID, CHICKEN_VINDALOO_ORDER_DETAILS)). expect(). - command(new ValidateOrderByConsumer(CONSUMER_ID, ORDER_ID, - CHICKEN_VINDALOO_ORDER_TOTAL)). + command(makeValidateOrderByConsumer()). to(ConsumerServiceChannels.consumerServiceChannel). andGiven(). successReply(). expect(). command(new CreateTicket(AJANTA_ID, ORDER_ID, null /* FIXME */)). - to(KitchenServiceChannels.kitchenServiceChannel). + to(KitchenServiceChannels.COMMAND_CHANNEL). andGiven(). successReply(). expect(). - command(new AuthorizeCommand(CONSUMER_ID, ORDER_ID, CHICKEN_VINDALOO_ORDER_TOTAL)). + command(new AuthorizeCommand().withConsumerId(CONSUMER_ID).withOrderId(ORDER_ID).withOrderTotal(CHICKEN_VINDALOO_ORDER_TOTAL.asString())). to(AccountingServiceChannels.accountingServiceChannel). andGiven(). successReply(). expect(). command(new ConfirmCreateTicket(ORDER_ID)). - to(KitchenServiceChannels.kitchenServiceChannel). + to(KitchenServiceChannels.COMMAND_CHANNEL). andGiven(). successReply(). expect(). command(new ApproveOrderCommand(ORDER_ID)). - to(OrderServiceChannels.orderServiceChannel) + to(OrderServiceChannels.COMMAND_CHANNEL) ; } + @NotNull + private ValidateOrderByConsumer makeValidateOrderByConsumer() { + return new ValidateOrderByConsumer().withConsumerId(CONSUMER_ID).withOrderId(ORDER_ID).withOrderTotal(CHICKEN_VINDALOO_ORDER_TOTAL.asString()); + } + @Test public void shouldRejectOrderDueToConsumerVerificationFailed() { given() .saga(makeCreateOrderSaga(), new CreateOrderSagaState(ORDER_ID, CHICKEN_VINDALOO_ORDER_DETAILS)). expect(). - command(new ValidateOrderByConsumer(CONSUMER_ID, ORDER_ID, - CHICKEN_VINDALOO_ORDER_TOTAL)). + command(makeValidateOrderByConsumer()). to(ConsumerServiceChannels.consumerServiceChannel). andGiven(). failureReply(). expect(). command(new RejectOrderCommand(ORDER_ID)). - to(OrderServiceChannels.orderServiceChannel); + to(OrderServiceChannels.COMMAND_CHANNEL); } @Test @@ -88,29 +92,28 @@ public void shouldRejectDueToFailedAuthorizxation() { .saga(makeCreateOrderSaga(), new CreateOrderSagaState(ORDER_ID, CHICKEN_VINDALOO_ORDER_DETAILS)). expect(). - command(new ValidateOrderByConsumer(CONSUMER_ID, ORDER_ID, - CHICKEN_VINDALOO_ORDER_TOTAL)). + command(makeValidateOrderByConsumer()). to(ConsumerServiceChannels.consumerServiceChannel). andGiven(). successReply(). expect(). command(new CreateTicket(AJANTA_ID, ORDER_ID, null /* FIXME */)). - to(KitchenServiceChannels.kitchenServiceChannel). + to(KitchenServiceChannels.COMMAND_CHANNEL). andGiven(). successReply(). expect(). - command(new AuthorizeCommand(CONSUMER_ID, ORDER_ID, CHICKEN_VINDALOO_ORDER_TOTAL)). + command(new AuthorizeCommand().withConsumerId(CONSUMER_ID).withOrderId(ORDER_ID).withOrderTotal(CHICKEN_VINDALOO_ORDER_TOTAL.asString())). to(AccountingServiceChannels.accountingServiceChannel). andGiven(). failureReply(). expect(). command(new CancelCreateTicket(ORDER_ID)). - to(KitchenServiceChannels.kitchenServiceChannel). + to(KitchenServiceChannels.COMMAND_CHANNEL). andGiven(). successReply(). expect(). command(new RejectOrderCommand(ORDER_ID)). - to(OrderServiceChannels.orderServiceChannel) + to(OrderServiceChannels.COMMAND_CHANNEL) ; } } \ No newline at end of file diff --git a/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/web/OrderControllerTest.java b/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/web/OrderControllerTest.java index 2c1831b3..f4a3f483 100644 --- a/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/web/OrderControllerTest.java +++ b/ftgo-order-service/src/test/java/net/chrisrichardson/ftgo/orderservice/web/OrderControllerTest.java @@ -1,6 +1,6 @@ package net.chrisrichardson.ftgo.orderservice.web; -import io.eventuate.javaclient.commonimpl.JSonMapper; +import io.eventuate.common.json.mapper.JSonMapper; import net.chrisrichardson.ftgo.common.CommonJsonMapperInitializer; import net.chrisrichardson.ftgo.orderservice.OrderDetailsMother; import net.chrisrichardson.ftgo.orderservice.domain.OrderRepository; @@ -27,7 +27,7 @@ public class OrderControllerTest { private OrderController orderController; @Before - public void setUp() throws Exception { + public void setUp() { orderService = mock(OrderService.class); orderRepository = mock(OrderRepository.class); orderController = new OrderController(orderService, orderRepository); diff --git a/ftgo-order-service/src/test/resources/application.properties b/ftgo-order-service/src/test/resources/application.properties deleted file mode 100644 index d9630432..00000000 --- a/ftgo-order-service/src/test/resources/application.properties +++ /dev/null @@ -1,23 +0,0 @@ -spring.application.name=ftgo-order-service - -logging.level.org.hibernate.SQL=DEBUG -logging.level.org.springframework.cloud.contract=DEBUG -logging.level.io.eventuate=DEBUG -spring.jpa.generate-ddl=true -stubrunner.stream.enabled=false -stubrunner.integration.enabled=false -spring.datasource.url=jdbc:mysql://${DOCKER_HOST_IP:localhost}/eventuate -spring.datasource.username=mysqluser -spring.datasource.password=mysqlpw -spring.datasource.driver.class.name=com.mysql.jdbc.Driver -spring.data.mongodb.uri=mongodb://${DOCKER_HOST_IP:localhost}/bankingexampledb - -eventuatelocal.kafka.bootstrap.servers=${DOCKER_HOST_IP:localhost}:9092 -eventuatelocal.cdc.db.user.name=root -eventuatelocal.cdc.db.password=rootpassword -eventuatelocal.zookeeper.connection.string=${DOCKER_HOST_IP:localhost}:2181 - -aws.access.key_id=id_key -aws.secret.access.key=access_key -aws.dynamodb.endpoint.url=http://${DOCKER_HOST_IP:localhost}:8000 -aws.region=us-west-2 diff --git a/ftgo-restaurant-service-api-spec/src/main/resources/ftgo-restaurant-service-api-spec/messages/MenuItem.json b/ftgo-restaurant-service-api-spec/src/main/resources/ftgo-restaurant-service-api-spec/messages/MenuItem.json new file mode 100644 index 00000000..fa540ec7 --- /dev/null +++ b/ftgo-restaurant-service-api-spec/src/main/resources/ftgo-restaurant-service-api-spec/messages/MenuItem.json @@ -0,0 +1,20 @@ +{ + "type" : "object", + "javaType" : "net.chrisrichardson.ftgo.restaurantservice.events.MenuItem", + "properties" : { + "id" : { + "type" : "string" + }, + "name" : { + "type" : "string" + }, + "price" : { + "type" : "string" + } + }, + "required": [ + "id", + "name", + "price" + ] +} diff --git a/ftgo-restaurant-service-api-spec/src/main/resources/ftgo-restaurant-service-api-spec/messages/RestaurantCreated.json b/ftgo-restaurant-service-api-spec/src/main/resources/ftgo-restaurant-service-api-spec/messages/RestaurantCreated.json new file mode 100644 index 00000000..7670229d --- /dev/null +++ b/ftgo-restaurant-service-api-spec/src/main/resources/ftgo-restaurant-service-api-spec/messages/RestaurantCreated.json @@ -0,0 +1,53 @@ +{ + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "address": { + "type": "object", + "properties": { + "street1": { + "type": "string" + }, + "street2": { + "type": "string" + }, + "city": { + "type": "string" + }, + "state": { + "type": "string" + }, + "zip": { + "type": "string" + } + }, + "required": [ + "street1", + "city", + "state", + "zip" + ] + }, + "menu": { + "type": "object", + "properties": { + "menuItems": { + "type": "array", + "items": { + "$ref": "MenuItem.json" + } + } + } + } + }, + "required": [ + "name", + "menu", + "address" + ], + "javaInterfaces": [ + "io.eventuate.tram.events.common.DomainEvent" + ] +} \ No newline at end of file diff --git a/ftgo-restaurant-service-api-spec/src/main/resources/ftgo-restaurant-service-api-spec/messages/RestaurantMenuRevised.json b/ftgo-restaurant-service-api-spec/src/main/resources/ftgo-restaurant-service-api-spec/messages/RestaurantMenuRevised.json new file mode 100644 index 00000000..daf71b57 --- /dev/null +++ b/ftgo-restaurant-service-api-spec/src/main/resources/ftgo-restaurant-service-api-spec/messages/RestaurantMenuRevised.json @@ -0,0 +1,21 @@ +{ + "type": "object", + "properties": { + "menu": { + "type": "object", + "properties": { + "menuItems": { + "type": "array", + "items": { + "$ref": "MenuItem.json" + } + } + } + } + }, + "required": [ "menu" + ], + "javaInterfaces": [ + "io.eventuate.tram.events.common.DomainEvent" + ] +} \ No newline at end of file diff --git a/ftgo-restaurant-service-api-spec/src/main/resources/ftgo-restaurant-service-api-spec/web/ftgo-restaurant-service-swagger.json b/ftgo-restaurant-service-api-spec/src/main/resources/ftgo-restaurant-service-api-spec/web/ftgo-restaurant-service-swagger.json new file mode 100644 index 00000000..09b65e3b --- /dev/null +++ b/ftgo-restaurant-service-api-spec/src/main/resources/ftgo-restaurant-service-api-spec/web/ftgo-restaurant-service-swagger.json @@ -0,0 +1,180 @@ +{ + "swagger": "2.0", + "info": { + "description": "Api Documentation", + "version": "1.0", + "title": "Api Documentation", + "termsOfService": "urn:tos", + "contact": {}, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0" + } + }, + "host": "localhost:8084", + "basePath": "/", + "tags": [ + { + "name": "restaurant-controller", + "description": "Restaurant Controller" + } + ], + "paths": { + "/restaurants": { + "post": { + "tags": [ + "restaurant-controller" + ], + "summary": "create", + "operationId": "createUsingPOST", + "consumes": [ + "application/json" + ], + "produces": [ + "*/*" + ], + "parameters": [ + { + "in": "body", + "name": "request", + "description": "request", + "required": true, + "schema": { + "$ref": "#/definitions/CreateRestaurantRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/CreateRestaurantResponse" + } + } + } + } + }, + "/restaurants/{restaurantId}": { + "get": { + "tags": [ + "restaurant-controller" + ], + "summary": "get", + "operationId": "getUsingGET", + "produces": [ + "*/*" + ], + "parameters": [ + { + "name": "restaurantId", + "in": "path", + "description": "restaurantId", + "required": true, + "type": "integer", + "format": "int64" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/GetRestaurantResponse" + } + } + } + } + } + }, + "definitions": { + "Address": { + "type": "object", + "properties": { + "city": { + "type": "string" + }, + "state": { + "type": "string" + }, + "street1": { + "type": "string" + }, + "street2": { + "type": "string" + }, + "zip": { + "type": "string" + } + }, + "title": "Address" + }, + "CreateRestaurantRequest": { + "type": "object", + "properties": { + "address": { + "$ref": "#/definitions/Address" + }, + "menu": { + "$ref": "#/definitions/RestaurantMenu" + }, + "name": { + "type": "string" + } + }, + "title": "CreateRestaurantRequest" + }, + "CreateRestaurantResponse": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + } + }, + "title": "CreateRestaurantResponse" + }, + "GetRestaurantResponse": { + "type": "object", + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + } + }, + "title": "GetRestaurantResponse" + }, + "MenuItem": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "price": { + "type": "string" + } + }, + "title": "MenuItem" + }, + "Money": { + "type": "object", + "title": "Money" + }, + "RestaurantMenu": { + "type": "object", + "properties": { + "menuItems": { + "type": "array", + "items": { + "$ref": "#/definitions/MenuItem" + } + } + }, + "title": "RestaurantMenu" + } + } +} diff --git a/ftgo-restaurant-service-api/build.gradle b/ftgo-restaurant-service-api/build.gradle index 2714367c..e0b96968 100644 --- a/ftgo-restaurant-service-api/build.gradle +++ b/ftgo-restaurant-service-api/build.gradle @@ -1,8 +1,5 @@ dependencies { - - - compile "io.eventuate.tram.core:eventuate-tram-events:$eventuateTramVersion" + compile "io.eventuate.tram.core:eventuate-tram-spring-events" compile project(":ftgo-common") - } diff --git a/ftgo-restaurant-service-api/src/main/java/net/chrisrichardson/ftgo/restaurantservice/RestaurantServiceChannels.java b/ftgo-restaurant-service-api/src/main/java/net/chrisrichardson/ftgo/restaurantservice/RestaurantServiceChannels.java new file mode 100644 index 00000000..800bd0ac --- /dev/null +++ b/ftgo-restaurant-service-api/src/main/java/net/chrisrichardson/ftgo/restaurantservice/RestaurantServiceChannels.java @@ -0,0 +1,6 @@ +package net.chrisrichardson.ftgo.restaurantservice; + +public class RestaurantServiceChannels { + + public static final String RESTAURANT_EVENT_CHANNEL = "net.chrisrichardson.ftgo.restaurantservice.domain.Restaurant"; +} diff --git a/ftgo-restaurant-service-api/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantCreated.java b/ftgo-restaurant-service-api/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantCreated.java deleted file mode 100644 index a878c818..00000000 --- a/ftgo-restaurant-service-api/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantCreated.java +++ /dev/null @@ -1,32 +0,0 @@ -package net.chrisrichardson.ftgo.restaurantservice.events; - -import io.eventuate.tram.events.common.DomainEvent; - -public class RestaurantCreated implements DomainEvent { - private String name; - private RestaurantMenu menu; - - public String getName() { - return name; - } - - private RestaurantCreated() { - } - - public RestaurantCreated(String name, RestaurantMenu menu) { - this.name = name; - this.menu = menu; - } - - public RestaurantMenu getMenu() { - return menu; - } - - public void setMenu(RestaurantMenu menu) { - this.menu = menu; - } - - public void setName(String name) { - this.name = name; - } -} diff --git a/ftgo-restaurant-service-aws-lambda/build.gradle b/ftgo-restaurant-service-aws-lambda/build.gradle index 29d56677..d5c2b9c6 100644 --- a/ftgo-restaurant-service-aws-lambda/build.gradle +++ b/ftgo-restaurant-service-aws-lambda/build.gradle @@ -1,28 +1,31 @@ - +apply plugin: IntegrationTestsPlugin +apply plugin: 'docker-compose' dependencies { - compile "com.amazonaws:aws-lambda-java-core:1.1.0" compile "com.amazonaws:aws-lambda-java-events:2.0.1" - compile ("io.eventuate.tram.core:eventuate-tram-producer-jdbc:$eventuateTramVersion") { + compile ("io.eventuate.tram.core:eventuate-tram-spring-producer-jdbc") { exclude module: "org.springframework" } - compile "io.eventuate.tram.core:eventuate-tram-events:$eventuateTramVersion" + compile "io.eventuate.tram.core:eventuate-tram-spring-events" + compile "io.eventuate.tram.core:eventuate-tram-spring-messaging" compile project(":ftgo-restaurant-service-api") compile project(":ftgo-common-jpa") + compile project(":ftgo-common") compile "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion" compile 'javax.el:javax.el-api:2.2.5' - testCompile "io.eventuate.util:eventuate-util-test:$eventuateUtilVersion" - testCompile "io.eventuate.tram.core:eventuate-tram-test-util:$eventuateTramVersion" + testCompile "io.eventuate.util:eventuate-util-test" + testCompile "io.eventuate.tram.core:eventuate-tram-test-util" testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion" testCompile 'com.jayway.restassured:rest-assured:2.3.0' testCompile "com.jayway.jsonpath:json-path:2.3.0" + testCompile(platform("io.eventuate.platform:eventuate-platform-dependencies:$eventuatePlatformVersion")) } task buildZip(type: Zip) { @@ -33,5 +36,26 @@ task buildZip(type: Zip) { } } +dockerCompose { + environment.put "EVENTUATE_COMMON_VERSION", eventuateCommonImageVersion + environment.put "EVENTUATE_CDC_VERSION", eventuateCdcImageVersion + environment.put "EVENTUATE_SAGA_VERSION", eventuateTramSagasImageVersion + environment.put "EVENTUATE_JAVA_BASE_IMAGE_VERSION", eventuateExamplesBaseImageVersion + environment.put "EVENTUATE_MESSAGING_KAFKA_IMAGE_VERSION", eventuateMessagingKafkaImageVersion + + projectName = null + + integrationTests { + projectName = null + removeOrphans = true + retainContainersOnStartupFailure = true + + startedServices = ['mysql'] + stopContainers = true + } +} + +integrationTest.dependsOn(integrationTestsComposeUp) + build.dependsOn buildZip diff --git a/ftgo-restaurant-service-aws-lambda/src/test/java/net/chrisrichardson/ftgo/restaurantservice/lambda/RestaurantServiceLambdaConfigurationTest.java b/ftgo-restaurant-service-aws-lambda/src/integration-test/java/net/chrisrichardson/ftgo/restaurantservice/lambda/RestaurantServiceLambdaConfigurationTest.java similarity index 100% rename from ftgo-restaurant-service-aws-lambda/src/test/java/net/chrisrichardson/ftgo/restaurantservice/lambda/RestaurantServiceLambdaConfigurationTest.java rename to ftgo-restaurant-service-aws-lambda/src/integration-test/java/net/chrisrichardson/ftgo/restaurantservice/lambda/RestaurantServiceLambdaConfigurationTest.java diff --git a/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/aws/AbstractHttpHandler.java b/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/aws/AbstractHttpHandler.java index 14afd6c2..c71c6cbc 100644 --- a/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/aws/AbstractHttpHandler.java +++ b/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/aws/AbstractHttpHandler.java @@ -10,7 +10,8 @@ import static net.chrisrichardson.ftgo.restaurantservice.aws.ApiGatewayResponse.buildErrorResponse; -public abstract class AbstractHttpHandler implements RequestHandler { +public abstract class AbstractHttpHandler + implements RequestHandler { private Logger log = LoggerFactory.getLogger(this.getClass()); diff --git a/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/aws/ApiGatewayResponse.java b/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/aws/ApiGatewayResponse.java index 8ae97cc4..61c1df75 100644 --- a/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/aws/ApiGatewayResponse.java +++ b/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/aws/ApiGatewayResponse.java @@ -1,7 +1,7 @@ package net.chrisrichardson.ftgo.restaurantservice.aws; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; -import io.eventuate.javaclient.commonimpl.JSonMapper; +import io.eventuate.common.json.mapper.JSonMapper; import java.nio.charset.StandardCharsets; import java.util.Base64; diff --git a/ftgo-restaurant-service-api/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/CreateRestaurantRequest.java b/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/CreateRestaurantRequest.java similarity index 58% rename from ftgo-restaurant-service-api/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/CreateRestaurantRequest.java rename to ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/CreateRestaurantRequest.java index c3bb0be4..7e8b7c0c 100644 --- a/ftgo-restaurant-service-api/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/CreateRestaurantRequest.java +++ b/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/CreateRestaurantRequest.java @@ -1,12 +1,20 @@ -package net.chrisrichardson.ftgo.restaurantservice.events; +package net.chrisrichardson.ftgo.restaurantservice.domain; + +import net.chrisrichardson.ftgo.common.Address; public class CreateRestaurantRequest { private String name; + private Address address; private RestaurantMenu menu; - public CreateRestaurantRequest(String name, RestaurantMenu menu) { + private CreateRestaurantRequest() { + + } + + public CreateRestaurantRequest(String name, Address address, RestaurantMenu menu) { this.name = name; + this.address = address; this.menu = menu; } @@ -26,7 +34,7 @@ public void setMenu(RestaurantMenu menu) { this.menu = menu; } - private CreateRestaurantRequest() { - + public Address getAddress() { + return address; } } diff --git a/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/MenuItem.java b/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/MenuItem.java new file mode 100644 index 00000000..12834325 --- /dev/null +++ b/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/MenuItem.java @@ -0,0 +1,67 @@ +package net.chrisrichardson.ftgo.restaurantservice.domain; + +import net.chrisrichardson.ftgo.common.Money; +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; + +import javax.persistence.Access; +import javax.persistence.AccessType; +import javax.persistence.Embeddable; + +@Embeddable +@Access(AccessType.FIELD) +public class MenuItem { + + private String id; + private String name; + private Money price; + + private MenuItem() { + } + + public MenuItem(String id, String name, Money price) { + this.id = id; + this.name = name; + this.price = price; + } + + @Override + public boolean equals(Object o) { + return EqualsBuilder.reflectionEquals(this, o); + } + + @Override + public int hashCode() { + return HashCodeBuilder.reflectionHashCode(this); + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Money getPrice() { + return price; + } + + public void setPrice(Money price) { + this.price = price; + } +} diff --git a/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/Restaurant.java b/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/Restaurant.java index f5bc9d6c..52a67034 100644 --- a/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/Restaurant.java +++ b/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/Restaurant.java @@ -1,7 +1,5 @@ package net.chrisrichardson.ftgo.restaurantservice.domain; -import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantMenu; - import javax.persistence.Access; import javax.persistence.AccessType; import javax.persistence.Embedded; diff --git a/ftgo-restaurant-service-api/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantMenu.java b/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/RestaurantMenu.java similarity index 94% rename from ftgo-restaurant-service-api/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantMenu.java rename to ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/RestaurantMenu.java index e6adbd2c..975de0ed 100644 --- a/ftgo-restaurant-service-api/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantMenu.java +++ b/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/RestaurantMenu.java @@ -1,4 +1,4 @@ -package net.chrisrichardson.ftgo.restaurantservice.events; +package net.chrisrichardson.ftgo.restaurantservice.domain; import org.apache.commons.lang.builder.EqualsBuilder; import org.apache.commons.lang.builder.HashCodeBuilder; diff --git a/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/RestaurantService.java b/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/RestaurantService.java index 08b50b8e..92c5a6fc 100644 --- a/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/RestaurantService.java +++ b/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/RestaurantService.java @@ -1,7 +1,6 @@ package net.chrisrichardson.ftgo.restaurantservice.domain; import io.eventuate.tram.events.publisher.DomainEventPublisher; -import net.chrisrichardson.ftgo.restaurantservice.events.CreateRestaurantRequest; import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantCreated; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; @@ -31,7 +30,7 @@ public RestaurantService(RestaurantRepository restaurantRepository) { public Restaurant create(CreateRestaurantRequest request) { Restaurant restaurant = new Restaurant(request.getName(), request.getMenu()); restaurantRepository.save(restaurant); - domainEventPublisher.publish(Restaurant.class, restaurant.getId(), Collections.singletonList(new RestaurantCreated(request.getName(), request.getMenu()))); + domainEventPublisher.publish(Restaurant.class, restaurant.getId(), Collections.singletonList(new RestaurantCreated(request.getName(), request.getAddress(), request.getMenu()))); return restaurant; } diff --git a/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/RestaurantServiceDomainConfiguration.java b/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/RestaurantServiceDomainConfiguration.java index f3efe706..2b53922a 100644 --- a/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/RestaurantServiceDomainConfiguration.java +++ b/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/RestaurantServiceDomainConfiguration.java @@ -1,13 +1,12 @@ package net.chrisrichardson.ftgo.restaurantservice.domain; -import io.eventuate.tram.events.publisher.TramEventsPublisherConfiguration; +import io.eventuate.tram.spring.events.publisher.TramEventsPublisherConfiguration; import net.chrisrichardson.ftgo.common.CommonConfiguration; import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean; -import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; @Configuration @EntityScan diff --git a/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantCreated.java b/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantCreated.java new file mode 100644 index 00000000..6746dfd3 --- /dev/null +++ b/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantCreated.java @@ -0,0 +1,49 @@ +package net.chrisrichardson.ftgo.restaurantservice.events; + +import net.chrisrichardson.ftgo.common.Address; +import net.chrisrichardson.ftgo.restaurantservice.domain.RestaurantMenu; + +public class RestaurantCreated implements RestaurantDomainEvent { + private String name; + private Address address; + private RestaurantMenu menu; + + public String getName() { + return name; + } + + private RestaurantCreated() { + } + + public RestaurantCreated(String name, Address address, RestaurantMenu menu) { + this.name = name; + this.address = address; + this.menu = menu; + + + if (menu == null) + throw new NullPointerException("Null Menu"); + if (address == null) + throw new NullPointerException("Null address"); + } + + public RestaurantMenu getMenu() { + return menu; + } + + public void setMenu(RestaurantMenu menu) { + this.menu = menu; + } + + public void setName(String name) { + this.name = name; + } + + public Address getAddress() { + return address; + } + + public void setAddress(Address address) { + this.address = address; + } +} diff --git a/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantDomainEvent.java b/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantDomainEvent.java new file mode 100644 index 00000000..6efebc8b --- /dev/null +++ b/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantDomainEvent.java @@ -0,0 +1,6 @@ +package net.chrisrichardson.ftgo.restaurantservice.events; + +import io.eventuate.tram.events.common.DomainEvent; + +public interface RestaurantDomainEvent extends DomainEvent { +} diff --git a/ftgo-restaurant-service-api/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantMenuRevised.java b/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantMenuRevised.java similarity index 52% rename from ftgo-restaurant-service-api/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantMenuRevised.java rename to ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantMenuRevised.java index 5390c1e1..b1cfd68e 100644 --- a/ftgo-restaurant-service-api/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantMenuRevised.java +++ b/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantMenuRevised.java @@ -1,8 +1,8 @@ package net.chrisrichardson.ftgo.restaurantservice.events; -import io.eventuate.tram.events.common.DomainEvent; +import net.chrisrichardson.ftgo.restaurantservice.domain.RestaurantMenu; -public class RestaurantMenuRevised implements DomainEvent { +public class RestaurantMenuRevised implements RestaurantDomainEvent { private RestaurantMenu menu; diff --git a/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/lambda/CreateRestaurantRequestHandler.java b/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/lambda/CreateRestaurantRequestHandler.java index 5dcd0767..4fb0cfe7 100644 --- a/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/lambda/CreateRestaurantRequestHandler.java +++ b/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/lambda/CreateRestaurantRequestHandler.java @@ -3,11 +3,11 @@ import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyRequestEvent; import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent; -import io.eventuate.javaclient.commonimpl.JSonMapper; +import io.eventuate.common.json.mapper.JSonMapper; import net.chrisrichardson.ftgo.restaurantservice.aws.ApiGatewayResponse; import net.chrisrichardson.ftgo.restaurantservice.domain.Restaurant; import net.chrisrichardson.ftgo.restaurantservice.domain.RestaurantService; -import net.chrisrichardson.ftgo.restaurantservice.events.CreateRestaurantRequest; +import net.chrisrichardson.ftgo.restaurantservice.domain.CreateRestaurantRequest; import net.chrisrichardson.ftgo.restaurantservice.web.CreateRestaurantResponse; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; diff --git a/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/lambda/RestaurantServiceLambdaConfiguration.java b/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/lambda/RestaurantServiceLambdaConfiguration.java index 0ea4b001..5662e632 100644 --- a/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/lambda/RestaurantServiceLambdaConfiguration.java +++ b/ftgo-restaurant-service-aws-lambda/src/main/java/net/chrisrichardson/ftgo/restaurantservice/lambda/RestaurantServiceLambdaConfiguration.java @@ -1,6 +1,6 @@ package net.chrisrichardson.ftgo.restaurantservice.lambda; -import io.eventuate.tram.messaging.producer.jdbc.TramMessageProducerJdbcConfiguration; +import io.eventuate.tram.spring.messaging.producer.jdbc.TramMessageProducerJdbcConfiguration; import net.chrisrichardson.ftgo.restaurantservice.domain.RestaurantServiceDomainConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.annotation.Configuration; diff --git a/ftgo-restaurant-service-aws-lambda/src/main/resources/application.properties b/ftgo-restaurant-service-aws-lambda/src/main/resources/application.properties index da5ef2c7..fa0375eb 100644 --- a/ftgo-restaurant-service-aws-lambda/src/main/resources/application.properties +++ b/ftgo-restaurant-service-aws-lambda/src/main/resources/application.properties @@ -4,12 +4,9 @@ logging.level.org.springframework.jdbc.datasource.DataSourceUtils=DEBUG spring.datasource.url=jdbc:mysql://${DOCKER_HOST_IP:localhost}/eventuate spring.datasource.username=mysqluser spring.datasource.password=mysqlpw -spring.datasource.driver.class.name=com.mysql.jdbc.Driver -spring.data.mongodb.uri=mongodb://${DOCKER_HOST_IP:localhost}/bankingexampledb +spring.datasource.driver-class-name=com.mysql.jdbc.Driver eventuatelocal.kafka.bootstrap.servers=${DOCKER_HOST_IP:localhost}:9092 -eventuatelocal.cdc.db.user.name=root -eventuatelocal.cdc.db.password=rootpassword eventuatelocal.zookeeper.connection.string=${DOCKER_HOST_IP:localhost}:2181 aws.access.key_id=id_key diff --git a/ftgo-restaurant-service-contracts/build.gradle b/ftgo-restaurant-service-contracts/build.gradle new file mode 100644 index 00000000..4f2b7bec --- /dev/null +++ b/ftgo-restaurant-service-contracts/build.gradle @@ -0,0 +1,26 @@ + +apply plugin: 'groovy' +apply plugin: 'spring-cloud-contract' +apply plugin: 'maven-publish' + + +publishing { + repositories { + maven { + url "${project.rootDir}/build/repo" + } + } +} + +contracts { + contractsDslDir = file("${projectDir}/src/main/resources/contracts") +} + +generateContractTests.enabled = false + + +build.finalizedBy(publish) + +dependencies { + compile 'org.codehaus.groovy:groovy-all:2.4.6' +} diff --git a/ftgo-restaurant-service-contracts/src/main/resources/contracts/deliveryservice/messaging/RestaurantCreatedEvent.groovy b/ftgo-restaurant-service-contracts/src/main/resources/contracts/deliveryservice/messaging/RestaurantCreatedEvent.groovy new file mode 100644 index 00000000..83a1ec1b --- /dev/null +++ b/ftgo-restaurant-service-contracts/src/main/resources/contracts/deliveryservice/messaging/RestaurantCreatedEvent.groovy @@ -0,0 +1,18 @@ +package contracts.deliveryservice.messaging; + +org.springframework.cloud.contract.spec.Contract.make { + label 'restaurantCreatedEvent' + input { + triggeredBy('restaurantCreated()') + } + + outputMessage { + sentTo('net.chrisrichardson.ftgo.restaurantservice.domain.Restaurant') + body('''{"address":{ "street1" : "1 Main Street", "street2" : "Unit 99", "city" : "Oakland", "state" : "CA", "zip" : "94611", }''') + headers { + header('event-aggregate-type', 'net.chrisrichardson.ftgo.restaurantservice.domain.Restaurant') + header('event-type', 'net.chrisrichardson.ftgo.restaurantservice.events.RestaurantCreated') + header('event-aggregate-id', '99') // Matches RestaurantDetailsMother.RESTAURANT_ID + } + } +} \ No newline at end of file diff --git a/ftgo-restaurant-service/Dockerfile b/ftgo-restaurant-service/Dockerfile index 2647ae74..acfcae92 100644 --- a/ftgo-restaurant-service/Dockerfile +++ b/ftgo-restaurant-service/Dockerfile @@ -1,5 +1,3 @@ -FROM openjdk:8u171-jre-alpine -RUN apk --no-cache add curl -CMD java ${JAVA_OPTS} -jar ftgo-restaurant-service.jar -HEALTHCHECK --start-period=30s --interval=5s CMD curl -f http://localhost:8080/actuator/health || exit 1 -COPY build/libs/ftgo-restaurant-service.jar . +ARG baseImageVersion +FROM eventuateio/eventuate-examples-docker-images-spring-example-base-image:$baseImageVersion +COPY build/libs/ftgo-restaurant-service.jar service.jar diff --git a/ftgo-restaurant-service/build.gradle b/ftgo-restaurant-service/build.gradle index 1de3b9f7..abe09cc6 100644 --- a/ftgo-restaurant-service/build.gradle +++ b/ftgo-restaurant-service/build.gradle @@ -1,17 +1,52 @@ apply plugin: FtgoServicePlugin +apply plugin: IntegrationTestsPlugin + +apply plugin: 'spring-cloud-contract' + +contracts { + contractsDslDir = new File("../ftgo-restaurant-service-contracts/src/main/resources/contracts") + packageWithBaseClasses = 'net.chrisrichardson.ftgo.restaurantservice.contract' + generatedTestSourcesDir = project.file("${project.buildDir}/generated-integration-test-sources/contracts") + sourceSet = "integrationTest" +} + +sourceSets { + integrationTest { + java { + srcDir project.file("${project.buildDir}/generated-integration-test-sources/contracts") + } + } +} + +compileTestGroovy.enabled=false + +dependencyManagement { + imports { + mavenBom "org.springframework.cloud:spring-cloud-contract-dependencies:$springCloudContractDependenciesVersion" + } +} + +configurations.all { + // Out of date, conflicting JSON library + exclude group: "com.vaadin.external.google" +} dependencies { - compile "io.eventuate.tram.core:eventuate-tram-jdbc-kafka:$eventuateTramVersion" - compile "io.eventuate.tram.core:eventuate-tram-events:$eventuateTramVersion" - compile "io.eventuate.tram.sagas:eventuate-tram-sagas-simple-dsl:$eventuateTramSagasVersion" + compile "io.eventuate.tram.core:eventuate-tram-spring-jdbc-kafka" + compile "io.eventuate.tram.core:eventuate-tram-spring-events" + compile "io.eventuate.tram.core:eventuate-tram-spring-messaging" + compile "io.eventuate.tram.sagas:eventuate-tram-sagas-spring-orchestration-simple-dsl" + compile "io.eventuate.tram.core:eventuate-tram-aggregate-domain-events" compile project(":common-swagger") compile project(":ftgo-restaurant-service-api") + compile project(":ftgo-restaurant-service-api-spec") compile project(":ftgo-common-jpa") + compile project(":ftgo-common") compile "org.springframework.boot:spring-boot-starter-data-jpa:$springBootVersion" compile "org.springframework.boot:spring-boot-starter-web:$springBootVersion" @@ -19,13 +54,23 @@ dependencies { compile 'javax.el:javax.el-api:2.2.5' - testCompile "io.eventuate.util:eventuate-util-test:$eventuateUtilVersion" - testCompile "io.eventuate.tram.core:eventuate-tram-test-util:$eventuateTramVersion" + compile('org.apache.kafka:kafka-clients:2.3.0') { + force = true + } + + testCompile "io.eventuate.util:eventuate-util-test" + testCompile "io.eventuate.tram.core:eventuate-tram-test-util" - testCompile "io.eventuate.tram.sagas:eventuate-tram-sagas-in-memory:$eventuateTramSagasVersion" + testCompile "io.eventuate.tram.sagas:eventuate-tram-sagas-spring-in-memory" testCompile "org.springframework.boot:spring-boot-starter-test:$springBootVersion" testCompile "com.jayway.restassured:rest-assured:$restAssuredVersion" testCompile "com.jayway.jsonpath:json-path:2.3.0" - testCompile "io.eventuate.tram.core:eventuate-tram-testing-support-spring-cloud-contract:$eventuateTramVersion" + testCompile "io.eventuate.tram.core:eventuate-tram-spring-testing-support-cloud-contract" + testCompile project(":ftgo-test-util-json-schema") + + integrationTestCompile "org.springframework.cloud:spring-cloud-contract-wiremock" + integrationTestCompile "org.springframework.cloud:spring-cloud-starter-contract-stub-runner" + integrationTestCompile "io.eventuate.tram.sagas:eventuate-tram-sagas-spring-testing-support" + integrationTestCompile "io.eventuate.tram.core:eventuate-tram-spring-in-memory" } diff --git a/ftgo-restaurant-service/src/integration-test/java/net/chrisrichardson/ftgo/restaurantservice/contract/DeliveryserviceMessagingBase.java b/ftgo-restaurant-service/src/integration-test/java/net/chrisrichardson/ftgo/restaurantservice/contract/DeliveryserviceMessagingBase.java new file mode 100644 index 00000000..823d6bc9 --- /dev/null +++ b/ftgo-restaurant-service/src/integration-test/java/net/chrisrichardson/ftgo/restaurantservice/contract/DeliveryserviceMessagingBase.java @@ -0,0 +1,53 @@ +package net.chrisrichardson.ftgo.restaurantservice.contract; + +import io.eventuate.common.spring.jdbc.EventuateTransactionTemplateConfiguration; +import io.eventuate.tram.events.publisher.DomainEventPublisher; +import io.eventuate.tram.spring.events.publisher.TramEventsPublisherConfiguration; +import io.eventuate.tram.spring.inmemory.TramInMemoryConfiguration; +import io.eventuate.tram.spring.cloudcontractsupport.EventuateContractVerifierConfiguration; +import net.chrisrichardson.ftgo.common.Address; +import net.chrisrichardson.ftgo.restaurantservice.domain.Restaurant; +import net.chrisrichardson.ftgo.restaurantservice.domain.RestaurantDomainEventPublisher; +import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantCreated; +import net.chrisrichardson.ftgo.restaurantservice.domain.RestaurantMenu; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.EnableAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.contract.verifier.messaging.boot.AutoConfigureMessageVerifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.Collections; + +@RunWith(SpringRunner.class) +@SpringBootTest(classes = DeliveryserviceMessagingBase.TestConfiguration.class, webEnvironment = SpringBootTest.WebEnvironment.NONE) +@AutoConfigureMessageVerifier +public abstract class DeliveryserviceMessagingBase { + + @Configuration + @EnableAutoConfiguration + @Import({EventuateContractVerifierConfiguration.class, TramEventsPublisherConfiguration.class, TramInMemoryConfiguration.class, EventuateTransactionTemplateConfiguration.class}) + public static class TestConfiguration { + + @Bean + public RestaurantDomainEventPublisher orderAggregateEventPublisher(DomainEventPublisher eventPublisher) { + return new RestaurantDomainEventPublisher(eventPublisher); + } + } + + + @Autowired + private RestaurantDomainEventPublisher restaurantDomainEventPublisher; + + protected void restaurantCreated() { + Restaurant restaurant = new Restaurant("Yummy Indian", new RestaurantMenu(Collections.emptyList())); + restaurant.setId(99L); + restaurantDomainEventPublisher.publish(restaurant, + Collections.singletonList(new RestaurantCreated(restaurant.getName(), new Address("1 Main Street", "Unit 99", "Oakland", "CA", "94611"), + restaurant.getMenu()))); + } + +} diff --git a/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/RestaurantServiceMain.java b/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/RestaurantServiceMain.java index f29eb0fe..557bcab9 100644 --- a/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/RestaurantServiceMain.java +++ b/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/RestaurantServiceMain.java @@ -1,30 +1,18 @@ package net.chrisrichardson.ftgo.restaurantservice; import com.fasterxml.jackson.databind.ObjectMapper; -import io.eventuate.javaclient.commonimpl.JSonMapper; -import io.eventuate.jdbckafka.TramJdbcKafkaConfiguration; -import io.eventuate.tram.commands.common.ChannelMapping; -import io.eventuate.tram.commands.common.DefaultChannelMapping; -import net.chrisrichardson.eventstore.examples.customersandorders.commonswagger.CommonSwaggerConfiguration; +import io.eventuate.common.json.mapper.JSonMapper; +import io.eventuate.tram.spring.jdbckafka.TramJdbcKafkaConfiguration; import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Primary; @SpringBootApplication -@EnableAutoConfiguration -@Import({TramJdbcKafkaConfiguration.class, CommonSwaggerConfiguration.class}) -@ComponentScan +@Import(TramJdbcKafkaConfiguration.class) public class RestaurantServiceMain { - @Bean - public ChannelMapping channelMapping() { - return new DefaultChannelMapping.DefaultChannelMappingBuilder().build(); - } - @Bean @Primary // conflicts with _halObjectMapper public ObjectMapper objectMapper() { diff --git a/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/CreateRestaurantRequest.java b/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/CreateRestaurantRequest.java new file mode 100644 index 00000000..7e8b7c0c --- /dev/null +++ b/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/CreateRestaurantRequest.java @@ -0,0 +1,40 @@ +package net.chrisrichardson.ftgo.restaurantservice.domain; + +import net.chrisrichardson.ftgo.common.Address; + +public class CreateRestaurantRequest { + + private String name; + private Address address; + private RestaurantMenu menu; + + private CreateRestaurantRequest() { + + } + + public CreateRestaurantRequest(String name, Address address, RestaurantMenu menu) { + this.name = name; + this.address = address; + this.menu = menu; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public RestaurantMenu getMenu() { + return menu; + } + + public void setMenu(RestaurantMenu menu) { + this.menu = menu; + } + + public Address getAddress() { + return address; + } +} diff --git a/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/MenuItem.java b/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/MenuItem.java new file mode 100644 index 00000000..12834325 --- /dev/null +++ b/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/MenuItem.java @@ -0,0 +1,67 @@ +package net.chrisrichardson.ftgo.restaurantservice.domain; + +import net.chrisrichardson.ftgo.common.Money; +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; + +import javax.persistence.Access; +import javax.persistence.AccessType; +import javax.persistence.Embeddable; + +@Embeddable +@Access(AccessType.FIELD) +public class MenuItem { + + private String id; + private String name; + private Money price; + + private MenuItem() { + } + + public MenuItem(String id, String name, Money price) { + this.id = id; + this.name = name; + this.price = price; + } + + @Override + public boolean equals(Object o) { + return EqualsBuilder.reflectionEquals(this, o); + } + + @Override + public int hashCode() { + return HashCodeBuilder.reflectionHashCode(this); + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Money getPrice() { + return price; + } + + public void setPrice(Money price) { + this.price = price; + } +} diff --git a/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/Restaurant.java b/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/Restaurant.java index f5bc9d6c..f4f0851b 100644 --- a/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/Restaurant.java +++ b/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/Restaurant.java @@ -1,7 +1,5 @@ package net.chrisrichardson.ftgo.restaurantservice.domain; -import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantMenu; - import javax.persistence.Access; import javax.persistence.AccessType; import javax.persistence.Embedded; @@ -49,4 +47,8 @@ public Restaurant(String name, RestaurantMenu menu) { public Long getId() { return id; } + + public RestaurantMenu getMenu() { + return menu; + } } diff --git a/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/RestaurantDomainEventPublisher.java b/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/RestaurantDomainEventPublisher.java new file mode 100644 index 00000000..89392a19 --- /dev/null +++ b/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/RestaurantDomainEventPublisher.java @@ -0,0 +1,11 @@ +package net.chrisrichardson.ftgo.restaurantservice.domain; + +import io.eventuate.tram.events.aggregates.AbstractAggregateDomainEventPublisher; +import io.eventuate.tram.events.publisher.DomainEventPublisher; +import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantDomainEvent; + +public class RestaurantDomainEventPublisher extends AbstractAggregateDomainEventPublisher { + public RestaurantDomainEventPublisher(DomainEventPublisher eventPublisher) { + super(eventPublisher, Restaurant.class, Restaurant::getId); + } +} diff --git a/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/RestaurantMenu.java b/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/RestaurantMenu.java new file mode 100644 index 00000000..975de0ed --- /dev/null +++ b/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/RestaurantMenu.java @@ -0,0 +1,52 @@ +package net.chrisrichardson.ftgo.restaurantservice.domain; + +import org.apache.commons.lang.builder.EqualsBuilder; +import org.apache.commons.lang.builder.HashCodeBuilder; +import org.apache.commons.lang.builder.ToStringBuilder; + +import javax.persistence.Access; +import javax.persistence.AccessType; +import javax.persistence.ElementCollection; +import javax.persistence.Embeddable; +import java.util.List; + +@Embeddable +@Access(AccessType.FIELD) +public class RestaurantMenu { + + + @ElementCollection + private List menuItems; + + private RestaurantMenu() { + } + + @Override + public boolean equals(Object o) { + return EqualsBuilder.reflectionEquals(this, o); + } + + @Override + public int hashCode() { + return HashCodeBuilder.reflectionHashCode(this); + } + + public List getMenuItems() { + return menuItems; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + + public void setMenuItems(List menuItems) { + this.menuItems = menuItems; + } + + public RestaurantMenu(List menuItems) { + + this.menuItems = menuItems; + } + +} diff --git a/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/RestaurantService.java b/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/RestaurantService.java index 811dba6d..ecd4ae2a 100644 --- a/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/RestaurantService.java +++ b/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/RestaurantService.java @@ -1,7 +1,5 @@ package net.chrisrichardson.ftgo.restaurantservice.domain; -import io.eventuate.tram.events.publisher.DomainEventPublisher; -import net.chrisrichardson.ftgo.restaurantservice.events.CreateRestaurantRequest; import net.chrisrichardson.ftgo.restaurantservice.events.RestaurantCreated; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.transaction.annotation.Transactional; @@ -17,12 +15,12 @@ public class RestaurantService { private RestaurantRepository restaurantRepository; @Autowired - private DomainEventPublisher domainEventPublisher; + private RestaurantDomainEventPublisher restaurantDomainEventPublisher; public Restaurant create(CreateRestaurantRequest request) { Restaurant restaurant = new Restaurant(request.getName(), request.getMenu()); restaurantRepository.save(restaurant); - domainEventPublisher.publish(Restaurant.class, restaurant.getId(), Collections.singletonList(new RestaurantCreated(request.getName(), request.getMenu()))); + restaurantDomainEventPublisher.publish(restaurant, Collections.singletonList(new RestaurantCreated(request.getName(), request.getAddress(), request.getMenu()))); return restaurant; } diff --git a/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/RestaurantServiceDomainConfiguration.java b/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/RestaurantServiceDomainConfiguration.java index 5f4383b5..7bf2913b 100644 --- a/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/RestaurantServiceDomainConfiguration.java +++ b/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/domain/RestaurantServiceDomainConfiguration.java @@ -1,6 +1,7 @@ package net.chrisrichardson.ftgo.restaurantservice.domain; -import io.eventuate.tram.events.publisher.TramEventsPublisherConfiguration; +import io.eventuate.tram.spring.events.publisher.TramEventsPublisherConfiguration; +import io.eventuate.tram.events.publisher.DomainEventPublisher; import net.chrisrichardson.ftgo.common.CommonConfiguration; import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.context.annotation.Bean; @@ -20,4 +21,9 @@ public class RestaurantServiceDomainConfiguration { public RestaurantService restaurantService() { return new RestaurantService(); } + + @Bean + public RestaurantDomainEventPublisher restaurantDomainEventPublisher(DomainEventPublisher domainEventPublisher) { + return new RestaurantDomainEventPublisher(domainEventPublisher); + } } diff --git a/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantCreated.java b/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantCreated.java new file mode 100644 index 00000000..49e58bca --- /dev/null +++ b/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantCreated.java @@ -0,0 +1,48 @@ +package net.chrisrichardson.ftgo.restaurantservice.events; + +import net.chrisrichardson.ftgo.common.Address; +import net.chrisrichardson.ftgo.restaurantservice.domain.RestaurantMenu; + +public class RestaurantCreated implements RestaurantDomainEvent { + private String name; + private Address address; + private RestaurantMenu menu; + + public String getName() { + return name; + } + + private RestaurantCreated() { + } + + public RestaurantCreated(String name, Address address, RestaurantMenu menu) { + this.name = name; + this.address = address; + this.menu = menu; + + if (menu == null) + throw new NullPointerException("Null Menu"); + if (address == null) + throw new NullPointerException("Null address"); + } + + public RestaurantMenu getMenu() { + return menu; + } + + public void setMenu(RestaurantMenu menu) { + this.menu = menu; + } + + public void setName(String name) { + this.name = name; + } + + public Address getAddress() { + return address; + } + + public void setAddress(Address address) { + this.address = address; + } +} diff --git a/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantDomainEvent.java b/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantDomainEvent.java new file mode 100644 index 00000000..6efebc8b --- /dev/null +++ b/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantDomainEvent.java @@ -0,0 +1,6 @@ +package net.chrisrichardson.ftgo.restaurantservice.events; + +import io.eventuate.tram.events.common.DomainEvent; + +public interface RestaurantDomainEvent extends DomainEvent { +} diff --git a/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantMenuRevised.java b/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantMenuRevised.java new file mode 100644 index 00000000..b1cfd68e --- /dev/null +++ b/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantMenuRevised.java @@ -0,0 +1,12 @@ +package net.chrisrichardson.ftgo.restaurantservice.events; + +import net.chrisrichardson.ftgo.restaurantservice.domain.RestaurantMenu; + +public class RestaurantMenuRevised implements RestaurantDomainEvent { + + private RestaurantMenu menu; + + public RestaurantMenu getRevisedMenu() { + return menu; + } +} diff --git a/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/web/RestaurantController.java b/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/web/RestaurantController.java index db309335..f07f8042 100644 --- a/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/web/RestaurantController.java +++ b/ftgo-restaurant-service/src/main/java/net/chrisrichardson/ftgo/restaurantservice/web/RestaurantController.java @@ -2,7 +2,7 @@ import net.chrisrichardson.ftgo.restaurantservice.domain.Restaurant; import net.chrisrichardson.ftgo.restaurantservice.domain.RestaurantService; -import net.chrisrichardson.ftgo.restaurantservice.events.CreateRestaurantRequest; +import net.chrisrichardson.ftgo.restaurantservice.domain.CreateRestaurantRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; diff --git a/ftgo-restaurant-service/src/main/resources/application.properties b/ftgo-restaurant-service/src/main/resources/application.properties index f8cab89d..b1085fc2 100644 --- a/ftgo-restaurant-service/src/main/resources/application.properties +++ b/ftgo-restaurant-service/src/main/resources/application.properties @@ -1,23 +1,18 @@ spring.application.name=ftgo-restaurant-service +management.endpoint.health.show-details=always + spring.jpa.generate-ddl=true logging.level.org.springframework.orm.jpa=INFO logging.level.org.hibernate.SQL=DEBUG logging.level.io.eventuate=DEBUG logging.level.net.chrisrichardson.ftgo=DEBUG -logging.level.io.eventuate.tram=TRACE -spring.datasource.url=jdbc:mysql://${DOCKER_HOST_IP:localhost}/eventuate -spring.datasource.username=mysqluser -spring.datasource.password=mysqlpw -spring.datasource.driver.class.name=com.mysql.jdbc.Driver -spring.data.mongodb.uri=mongodb://${DOCKER_HOST_IP:localhost}/bankingexampledb +logging.level.io.eventuate.tram=DEBUG + +spring.datasource.url=jdbc:mysql://${DOCKER_HOST_IP:localhost}/ftgo_restaurant_service +spring.datasource.username=ftgo_restaurant_service_user +spring.datasource.password=ftgo_restaurant_service_password +spring.datasource.driver-class-name=com.mysql.jdbc.Driver eventuatelocal.kafka.bootstrap.servers=${DOCKER_HOST_IP:localhost}:9092 -eventuatelocal.cdc.db.user.name=root -eventuatelocal.cdc.db.password=rootpassword eventuatelocal.zookeeper.connection.string=${DOCKER_HOST_IP:localhost}:2181 - -aws.access.key_id=id_key -aws.secret.access.key=access_key -aws.dynamodb.endpoint.url=http://${DOCKER_HOST_IP:localhost}:8000 -aws.region=us-west-2 diff --git a/ftgo-restaurant-service/src/test/java/net/chrisrichardson/ftgo/restaurantservice/domain/RestaurantDomainEventPublisherTest.java b/ftgo-restaurant-service/src/test/java/net/chrisrichardson/ftgo/restaurantservice/domain/RestaurantDomainEventPublisherTest.java new file mode 100644 index 00000000..a75a52dd --- /dev/null +++ b/ftgo-restaurant-service/src/test/java/net/chrisrichardson/ftgo/restaurantservice/domain/RestaurantDomainEventPublisherTest.java @@ -0,0 +1,16 @@ +package net.chrisrichardson.ftgo.restaurantservice.domain; + +import net.chrisrichardson.ftgo.restaurantservice.RestaurantServiceChannels; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class RestaurantDomainEventPublisherTest { + + @Test + public void verifyRestaurantEventChannel() { + assertEquals(RestaurantServiceChannels.RESTAURANT_EVENT_CHANNEL, new RestaurantDomainEventPublisher(null).getAggregateType().getName()); + } + + +} \ No newline at end of file diff --git a/ftgo-restaurant-service/src/test/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantCreatedSerializationTest.java b/ftgo-restaurant-service/src/test/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantCreatedSerializationTest.java new file mode 100644 index 00000000..a531d230 --- /dev/null +++ b/ftgo-restaurant-service/src/test/java/net/chrisrichardson/ftgo/restaurantservice/events/RestaurantCreatedSerializationTest.java @@ -0,0 +1,43 @@ +package net.chrisrichardson.ftgo.restaurantservice.events; + +import net.chrisrichardson.ftgo.common.Address; +import net.chrisrichardson.ftgo.common.CommonJsonMapperInitializer; +import net.chrisrichardson.ftgo.common.Money; +import net.chrisrichardson.ftgo.restaurantservice.domain.MenuItem; +import net.chrisrichardson.ftgo.restaurantservice.domain.RestaurantMenu; +import net.chrisrichardson.ftgo.testutil.jsonschema.ValidatingJSONMapper; +import org.json.JSONException; +import org.junit.Test; + +import java.util.Collections; + +import static org.junit.Assert.assertNotNull; + +public class RestaurantCreatedSerializationTest { + + static { + CommonJsonMapperInitializer.registerMoneyModule(); + } + + public static final String AJANTA_RESTAURANT_NAME = "Ajanta"; + public static final long AJANTA_ID = 1L; + public static final String CHICKEN_VINDALOO = "Chicken Vindaloo"; + public static final String CHICKEN_VINDALOO_MENU_ITEM_ID = "1"; + public static final Money CHICKEN_VINDALOO_PRICE = new Money("12.34"); + public static final Address RESTAURANT_ADDRESS = new Address("1 Main Street", "Unit 99", "Oakland", "CA", "94611"); + + public static MenuItem CHICKEN_VINDALOO_MENU_ITEM = new MenuItem(CHICKEN_VINDALOO_MENU_ITEM_ID, CHICKEN_VINDALOO, CHICKEN_VINDALOO_PRICE); + + @Test + public void shouldSerialize() throws JSONException { + + ValidatingJSONMapper mapper = ValidatingJSONMapper.forSchema("/ftgo-restaurant-service-api-spec/messages/RestaurantCreated.json"); + + RestaurantCreated event = new RestaurantCreated(AJANTA_RESTAURANT_NAME, RESTAURANT_ADDRESS, + new RestaurantMenu(Collections.singletonList(CHICKEN_VINDALOO_MENU_ITEM))); + String json = mapper.toJSON(event); + assertNotNull(json); + } + + +} \ No newline at end of file diff --git a/ftgo-test-util-json-schema/build.gradle b/ftgo-test-util-json-schema/build.gradle new file mode 100644 index 00000000..5dfc2843 --- /dev/null +++ b/ftgo-test-util-json-schema/build.gradle @@ -0,0 +1,6 @@ +dependencies { + compile "com.github.everit-org.json-schema:org.everit.json.schema:1.12.0" + compile "junit:junit:4.12" + compile "io.eventuate.common:eventuate-common-json-mapper:$eventuateCommonVersion" + +} diff --git a/ftgo-test-util-json-schema/src/main/java/net/chrisrichardson/ftgo/testutil/jsonschema/ValidatingJSONMapper.java b/ftgo-test-util-json-schema/src/main/java/net/chrisrichardson/ftgo/testutil/jsonschema/ValidatingJSONMapper.java new file mode 100644 index 00000000..b2d3d456 --- /dev/null +++ b/ftgo-test-util-json-schema/src/main/java/net/chrisrichardson/ftgo/testutil/jsonschema/ValidatingJSONMapper.java @@ -0,0 +1,72 @@ +package net.chrisrichardson.ftgo.testutil.jsonschema; + +import io.eventuate.common.json.mapper.JSonMapper; +import org.apache.commons.lang.StringUtils; +import org.everit.json.schema.Schema; +import org.everit.json.schema.ValidationException; +import org.everit.json.schema.loader.SchemaClient; +import org.everit.json.schema.loader.SchemaLoader; +import org.json.JSONException; +import org.json.JSONObject; +import org.json.JSONTokener; +import org.junit.Assert; + +import java.io.IOException; +import java.io.InputStream; + +import static org.junit.Assert.fail; + +public class ValidatingJSONMapper { + + private Schema schema; + + public ValidatingJSONMapper(Schema schema) { + this.schema = schema; + } + + public static ValidatingJSONMapper forSchema(String schemaPath) { + Schema schema; + try (InputStream inputStream = ValidatingJSONMapper.class.getResourceAsStream(schemaPath)) { + if (inputStream == null) + fail("Can't find schema: " + schemaPath); + JSONObject rawSchema = new JSONObject(new JSONTokener(inputStream)); + schema = SchemaLoader.load(rawSchema, new SchemaClient() { + @Override + public InputStream get(String url) { + String path = StringUtils.substringBeforeLast(schemaPath, "/") + "/" + url; + InputStream is = ValidatingJSONMapper.class.getResourceAsStream(path); + Assert.assertNotNull(path, is); + return is; + } + }); + } catch (IOException | JSONException e) { + throw new RuntimeException(e); + } + return new ValidatingJSONMapper(schema); + } + + public void validate(JSONObject jsonObject) { + try { + schema.validate(jsonObject); + } catch (ValidationException e) { + e.getErrorMessage(); + fail("Schema validation failed: " + String.join(",", e.getAllMessages())); + } + + } + public void validate(String jsonObject) { + JSONObject jo = new JSONObject(new JSONTokener(jsonObject)); + validate(jo); + } + + public T fromJSON(JSONObject jsonObject, Class clasz) { + validate(jsonObject); + return JSonMapper.fromJson(jsonObject.toString(), clasz); + } + + public String toJSON(Object object) { + String json = JSonMapper.toJson(object); + validate(json); + return json; + } +} diff --git a/ftgo-test-util/build.gradle b/ftgo-test-util/build.gradle index ee721aba..9903d133 100644 --- a/ftgo-test-util/build.gradle +++ b/ftgo-test-util/build.gradle @@ -1,3 +1,3 @@ dependencies { compile "junit:junit:4.12" -} \ No newline at end of file +} diff --git a/ftgo-test-util/src/main/java/net/chrisrichardson/ftgo/testutil/FtgoTestUtil.java b/ftgo-test-util/src/main/java/net/chrisrichardson/ftgo/testutil/FtgoTestUtil.java index 68f70df8..5397fc3a 100644 --- a/ftgo-test-util/src/main/java/net/chrisrichardson/ftgo/testutil/FtgoTestUtil.java +++ b/ftgo-test-util/src/main/java/net/chrisrichardson/ftgo/testutil/FtgoTestUtil.java @@ -9,4 +9,8 @@ public class FtgoTestUtil { public static void assertPresent(Optional value) { assertTrue(value.isPresent()); } + + public static String getDockerHostIp() { + return Optional.ofNullable(System.getenv("DOCKER_HOST_IP")).orElse("localhost"); + } } diff --git a/gradle.properties b/gradle.properties index d4f454f0..b7ae0b3e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,25 +1,38 @@ - deployUrl=file:///Users/cer/.m2/testdeploy -eventuateMavenRepoUrl=https://dl.bintray.com/eventuateio-oss/eventuate-maven-release,file:///Users/cer/.m2/testdeploy +eventuateMavenRepoUrl=https://snapshots.repositories.eventuate.io/repository,file:///Users/cer/.m2/testdeploy -springBootVersion=2.0.3.RELEASE +springBootVersion=2.2.6.RELEASE restAssuredVersion=2.9.0 -springCloudContractDependenciesVersion=2.0.1.RELEASE +# TODO Upgrading to 2.0.2.RELEASE requires addressing this issue: https://stackoverflow.com/questions/54268428/spring-cloud-contracts-plugin-change-sourceset + +#springCloudContractDependenciesVersion=2.0.1.RELEASE +springCloudContractDependenciesVersion=2.2.0.RELEASE + springDependencyManagementPluginVersion=1.0.3.RELEASE -# springBootVersion = '1.5.6.RELEASE' +eventuateCommonVersion=0.15.0.RELEASE +eventuatePlatformVersion=2022.0.RELEASE + -eventuateClientVersion=0.20.1.RELEASE -eventuateLocalVersion=0.22.1.RELEASE +eventuateCommonImageVersion=0.15.0.RELEASE +eventuateMessagingKafkaImageVersion=0.15.0.RELEASE +eventuateCdcImageVersion=0.13.0.RELEASE +eventuateTramSagasImageVersion=0.19.0.RELEASE -eventuateTramVersion=0.13.0.RELEASE -eventuateTramSagasVersion=0.7.0.RELEASE -eventuateUtilVersion=0.2.0.RELEASE +protocVersion=3.20.1 +grpcVersion = 1.47.0 -#dockerComposePluginVersion=0.4.5 -dockerComposePluginVersion=0.6.6 -grpcVersion = 1.13.2 -springCloudSleuthVersion=2.0.0.RELEASE +swaggerUiVersion=3.23.11 +eventuateExamplesBaseImageVersion=BUILD-15 + +dockerComposePluginVersion=0.15.2 +springCloudSleuthVersion=2.2.2.RELEASE springCloudGatewayVersion=2.0.0.RELEASE micrometerVersion=1.0.4 + +microserviceCanvasVersion=0.8.0.RELEASE +swaggerRequestValidatorVersion=2.8.3 +js2pVersion=1.0.1 + +eventuatePlatformVersion=2022.0.RELEASE diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e5cade3e..70a89216 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.9.1-all.zip diff --git a/mariadb/0.eventuate-schema.sql b/mariadb/0.eventuate-schema.sql deleted file mode 100644 index 91a91650..00000000 --- a/mariadb/0.eventuate-schema.sql +++ /dev/null @@ -1,42 +0,0 @@ -create database eventuate; -GRANT ALL PRIVILEGES ON eventuate.* TO 'mysqluser'@'%' WITH GRANT OPTION; - -USE eventuate; - -DROP table IF EXISTS events; -DROP table IF EXISTS entities; -DROP table IF EXISTS snapshots; - -create table events ( - event_id varchar(1000) PRIMARY KEY, - event_type varchar(1000), - event_data varchar(1000) NOT NULL, - entity_type VARCHAR(1000) NOT NULL, - entity_id VARCHAR(1000) NOT NULL, - triggering_event VARCHAR(1000), - metadata VARCHAR(1000), - published TINYINT DEFAULT 0 -); - -CREATE INDEX events_idx ON events(entity_type, entity_id, event_id); -CREATE INDEX events_published_idx ON events(published, event_id); - -create table entities ( - entity_type VARCHAR(1000), - entity_id VARCHAR(1000), - entity_version VARCHAR(1000) NOT NULL, - PRIMARY KEY(entity_type, entity_id) -); - -CREATE INDEX entities_idx ON events(entity_type, entity_id); - -create table snapshots ( - entity_type VARCHAR(1000), - entity_id VARCHAR(1000), - entity_version VARCHAR(1000), - snapshot_type VARCHAR(1000) NOT NULL, - snapshot_json VARCHAR(1000) NOT NULL, - triggering_events VARCHAR(1000), - PRIMARY KEY(entity_type, entity_id, entity_version) -); - diff --git a/mariadb/1.eventuate-tram-schema.sql b/mariadb/1.eventuate-tram-schema.sql deleted file mode 100644 index ac5b48fe..00000000 --- a/mariadb/1.eventuate-tram-schema.sql +++ /dev/null @@ -1,21 +0,0 @@ -USE eventuate; - -DROP Table IF Exists message; -DROP Table IF Exists received_messages; - -CREATE TABLE message ( - id VARCHAR(767) PRIMARY KEY, - destination VARCHAR(1000) NOT NULL, - headers VARCHAR(1000) NOT NULL, - payload VARCHAR(1000) NOT NULL, - published SMALLINT DEFAULT 0 -); - -CREATE INDEX message_published_idx ON message(published, id); - -CREATE TABLE received_messages ( - consumer_id VARCHAR(767), - message_id VARCHAR(767), - PRIMARY KEY(consumer_id, message_id) -); - diff --git a/mariadb/2.eventuate-tram-saga-schema.sql b/mariadb/2.eventuate-tram-saga-schema.sql deleted file mode 100644 index 2c10a675..00000000 --- a/mariadb/2.eventuate-tram-saga-schema.sql +++ /dev/null @@ -1,49 +0,0 @@ -USE eventuate; - -DROP Table IF Exists aggregate_instance_subscriptions; -DROP Table IF Exists saga_instance_participants; -DROP Table IF Exists saga_instance; -DROP Table IF Exists saga_lock_table; -DROP Table IF Exists saga_stash_table; - -CREATE TABLE aggregate_instance_subscriptions( - aggregate_type VARCHAR(200) DEFAULT NULL, - aggregate_id VARCHAR(1000) NOT NULL, - event_type VARCHAR(200) NOT NULL, - saga_id VARCHAR(1000) NOT NULL, - saga_type VARCHAR(200) NOT NULL, - PRIMARY KEY(aggregate_id, event_type, saga_id, saga_type) -); - -CREATE TABLE saga_instance_participants ( - saga_type VARCHAR(100) NOT NULL, - saga_id VARCHAR(100) NOT NULL, - destination VARCHAR(100) NOT NULL, - resource VARCHAR(100) NOT NULL, - PRIMARY KEY(saga_type, saga_id, destination, resource) -); - -CREATE TABLE saga_instance( - saga_type VARCHAR(100) NOT NULL, - saga_id VARCHAR(100) NOT NULL, - state_name VARCHAR(100) NOT NULL, - last_request_id VARCHAR(100), - saga_data_type VARCHAR(1000) NOT NULL, - saga_data_json VARCHAR(1000) NOT NULL, - PRIMARY KEY(saga_type, saga_id) -); - -create table saga_lock_table( - target VARCHAR(100) PRIMARY KEY, - saga_type VARCHAR(100) NOT NULL, - saga_Id VARCHAR(100) NOT NULL -); - -create table saga_stash_table( - message_id VARCHAR(100) PRIMARY KEY, - target VARCHAR(100) NOT NULL, - saga_type VARCHAR(100) NOT NULL, - saga_id VARCHAR(100) NOT NULL, - message_headers VARCHAR(1000) NOT NULL, - message_payload VARCHAR(1000) NOT NULL - ); diff --git a/mariadb/3.ftgo-schema.sql b/mariadb/3.ftgo-schema.sql deleted file mode 100644 index 106d5aac..00000000 --- a/mariadb/3.ftgo-schema.sql +++ /dev/null @@ -1,18 +0,0 @@ -create database ftgoconsumerservice; -GRANT ALL PRIVILEGES ON ftgoconsumerservice.* TO 'mysqluser'@'%' WITH GRANT OPTION; - -create database ftgoorderservice; -GRANT ALL PRIVILEGES ON ftgoorderservice.* TO 'mysqluser'@'%' WITH GRANT OPTION; - -create database ftgokitchenservice; -GRANT ALL PRIVILEGES ON ftgokitchenservice.* TO 'mysqluser'@'%' WITH GRANT OPTION; - -create database ftgorestaurantservice; -GRANT ALL PRIVILEGES ON ftgorestaurantservice.* TO 'mysqluser'@'%' WITH GRANT OPTION; - -create database ftgoaccountingservice; -GRANT ALL PRIVILEGES ON ftgoaccountingservice.* TO 'mysqluser'@'%' WITH GRANT OPTION; - -create database ftgoorderhistoryservice; -GRANT ALL PRIVILEGES ON ftgoorderhistoryservice.* TO 'mysqluser'@'%' WITH GRANT OPTION; - diff --git a/mariadb/Dockerfile b/mariadb/Dockerfile deleted file mode 100644 index 766d4079..00000000 --- a/mariadb/Dockerfile +++ /dev/null @@ -1,3 +0,0 @@ -FROM mariadb:10.3.8 -COPY replication.cnf /etc/mysql/conf.d -COPY *.sql /docker-entrypoint-initdb.d/ diff --git a/mariadb/build-docker.sh b/mariadb/build-docker.sh deleted file mode 100755 index 823334d3..00000000 --- a/mariadb/build-docker.sh +++ /dev/null @@ -1,3 +0,0 @@ -#! /bin/bash -e - -docker build -t test-eventuateio-local-mysql . diff --git a/mariadb/replication.cnf b/mariadb/replication.cnf deleted file mode 100644 index 8ba8fa3a..00000000 --- a/mariadb/replication.cnf +++ /dev/null @@ -1,4 +0,0 @@ -[mysqld] -log-bin=mysql-bin -server-id=1 -binlog_format=ROW diff --git a/mysql-cli.sh b/mysql-cli.sh index e64e943b..468a6d51 100755 --- a/mysql-cli.sh +++ b/mysql-cli.sh @@ -1,7 +1,6 @@ #! /bin/bash -e docker run $* \ - --name mysqlterm --rm \ - -e MYSQL_PORT_3306_TCP_ADDR=$DOCKER_HOST_IP -e MYSQL_PORT_3306_TCP_PORT=3306 -e MYSQL_ENV_MYSQL_ROOT_PASSWORD=rootpassword \ - mysql:5.7.13 \ - sh -c 'exec mysql -h"$MYSQL_PORT_3306_TCP_ADDR" -P"$MYSQL_PORT_3306_TCP_PORT" -uroot -p"$MYSQL_ENV_MYSQL_ROOT_PASSWORD" ' + --name mysqlterm --rm --network=${PWD##*/}_default \ + mysql/mysql-server:8.0.27-1.2.6-server \ + mysql -hmysql -P3306 -uroot -prootpassword diff --git a/mysql/Dockerfile b/mysql/Dockerfile index 3bbf8c55..3e91971b 100644 --- a/mysql/Dockerfile +++ b/mysql/Dockerfile @@ -1,2 +1,15 @@ -FROM eventuateio/eventuate-tram-sagas-mysql:0.6.0.RELEASE -COPY eventuate-local-schema-mysql.sql /docker-entrypoint-initdb.d/z-eventuate-local-schema-mysql.sql +ARG EVENTUATE_COMMON_VERSION +FROM eventuateio/eventuate-mysql8:$EVENTUATE_COMMON_VERSION +ARG EVENTUATE_COMMON_VERSION +ARG EVENTUATE_SAGA_VERSION + +COPY compile-schema-per-service.sh /docker-entrypoint-initdb.d/4.compile-schema-per-service.sh + +ADD https://raw.githubusercontent.com/eventuate-foundation/eventuate-common/$EVENTUATE_COMMON_VERSION/mysql/1.initialize-database.sql /docker-entrypoint-initdb.d/template1 +ADD https://raw.githubusercontent.com/eventuate-foundation/eventuate-common/$EVENTUATE_COMMON_VERSION/mysql/2.initialize-database.sql /docker-entrypoint-initdb.d/template2 +ADD https://raw.githubusercontent.com/eventuate-tram/eventuate-tram-sagas/$EVENTUATE_SAGA_VERSION/mysql/tram-saga-schema.sql /docker-entrypoint-initdb.d/template3 + +RUN cat /docker-entrypoint-initdb.d/template? | sed -e 's/eventuate.offset_store/offset_store/' -e /eventuate/d > /docker-entrypoint-initdb.d/template + +RUN touch /docker-entrypoint-initdb.d/5.schema-per-service.sql +RUN chown mysql -R /docker-entrypoint-initdb.d diff --git a/mysql/compile-schema-per-service.sh b/mysql/compile-schema-per-service.sh new file mode 100644 index 00000000..6d1d3697 --- /dev/null +++ b/mysql/compile-schema-per-service.sh @@ -0,0 +1,14 @@ +#! /bin/bash -e + +for schema in ftgo_accounting_service ftgo_consumer_service ftgo_order_service ftgo_kitchen_service ftgo_restaurant_service ftgo_delivery_service; +do + user=${schema}_user + password=${schema}_password + cat >> /docker-entrypoint-initdb.d/5.schema-per-service.sql <> /docker-entrypoint-initdb.d/5.schema-per-service.sql +done diff --git a/mysql/eventuate-local-schema-mysql.sql b/mysql/eventuate-local-schema-mysql.sql deleted file mode 100644 index 779a200d..00000000 --- a/mysql/eventuate-local-schema-mysql.sql +++ /dev/null @@ -1,55 +0,0 @@ -create database ftgoconsumerservice; -GRANT ALL PRIVILEGES ON ftgoconsumerservice.* TO 'mysqluser'@'%' WITH GRANT OPTION; - -create database ftgoorderservice; -GRANT ALL PRIVILEGES ON ftgoorderservice.* TO 'mysqluser'@'%' WITH GRANT OPTION; - -create database ftgokitchenservice; -GRANT ALL PRIVILEGES ON ftgokitchenservice.* TO 'mysqluser'@'%' WITH GRANT OPTION; - -create database ftgorestaurantservice; -GRANT ALL PRIVILEGES ON ftgorestaurantservice.* TO 'mysqluser'@'%' WITH GRANT OPTION; - -create database ftgoaccountingservice; -GRANT ALL PRIVILEGES ON ftgoaccountingservice.* TO 'mysqluser'@'%' WITH GRANT OPTION; - -create database ftgoorderhistoryservice; -GRANT ALL PRIVILEGES ON ftgoorderhistoryservice.* TO 'mysqluser'@'%' WITH GRANT OPTION; - -USE eventuate; -DROP table IF EXISTS events; -DROP table IF EXISTS entities; -DROP table IF EXISTS snapshots; - -create table events ( - event_id varchar(1000) PRIMARY KEY, - event_type varchar(1000), - event_data varchar(1000) NOT NULL, - entity_type VARCHAR(1000) NOT NULL, - entity_id VARCHAR(1000) NOT NULL, - triggering_event VARCHAR(1000), - metadata VARCHAR(1000), - published TINYINT DEFAULT 0 -); - -CREATE INDEX events_idx ON events(entity_type, entity_id, event_id); -CREATE INDEX events_published_idx ON events(published, event_id); - -create table entities ( - entity_type VARCHAR(1000), - entity_id VARCHAR(1000), - entity_version VARCHAR(1000) NOT NULL, - PRIMARY KEY(entity_type, entity_id) -); - -CREATE INDEX entities_idx ON events(entity_type, entity_id); - -create table snapshots ( - entity_type VARCHAR(1000), - entity_id VARCHAR(1000), - entity_version VARCHAR(1000), - snapshot_type VARCHAR(1000) NOT NULL, - snapshot_json VARCHAR(1000) NOT NULL, - triggering_events VARCHAR(1000), - PRIMARY KEY(entity_type, entity_id, entity_version) -); diff --git a/open-swagger-uis.sh b/open-swagger-uis.sh index d0e1d1bc..f29cb3ad 100755 --- a/open-swagger-uis.sh +++ b/open-swagger-uis.sh @@ -2,5 +2,5 @@ for port in 8081 8084 8082 ; do - open http://${DOCKER_HOST_IP?}:$port/swagger-ui.html + open http://${DOCKER_HOST_IP:-localhost}:$port/swagger-ui.html done diff --git a/publish-docker-images.sh b/publish-docker-images.sh index b65dccc4..530cecbe 100755 --- a/publish-docker-images.sh +++ b/publish-docker-images.sh @@ -1,6 +1,6 @@ #! /bin/bash -e -DOCKER_COMPOSE_PREFIX=$(echo ${PWD##*/} | sed -e 's/-//g')_ +DOCKER_COMPOSE_PREFIX="${PWD##*/}_" DOCKER_REPO=msapatterns diff --git a/run-graphql-api-gateway-tests.sh b/run-graphql-api-gateway-tests.sh index 14e28de3..13940204 100755 --- a/run-graphql-api-gateway-tests.sh +++ b/run-graphql-api-gateway-tests.sh @@ -1,4 +1,4 @@ -#!/bin/bash -e +#! /bin/bash -e if which npm ; then echo npm on path. attempting to test GraphQL API gateway @@ -13,6 +13,14 @@ if [ ! -d node_modules ] ; then npm install fi +if which tsc ; then + echo tsc installed +else + npm install -g typescript +fi + npm run unit-test -npm run end-to-end-test +docker-compose -f ../docker-compose.yml -f ../docker-compose-api-gateway-graphql.yml up -d --build + +npm run end-to-end-test diff --git a/scan-order-history-view.sh b/scan-order-history-view.sh new file mode 100755 index 00000000..0f5ad155 --- /dev/null +++ b/scan-order-history-view.sh @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +export AWS_ACCESS_KEY_ID=id_key AWS_SECRET_ACCESS_KEY=access_key +aws --region ignored --endpoint-url http://${DOCKER_HOST_IP:=localhost}:8000 dynamodb scan --table-name ftgo-order-history + diff --git a/set-env.sh b/set-env.sh index 3540c603..31a9f984 100644 --- a/set-env.sh +++ b/set-env.sh @@ -1,11 +1,16 @@ if [ -z "$DOCKER_HOST_IP" ] ; then - if [ -z "$DOCKER_HOST" ] ; then - export DOCKER_HOST_IP=`hostname` - else + if [ ! -z "$DOCKER_HOST" ] ; then echo using ${DOCKER_HOST?} XX=${DOCKER_HOST%\:*} export DOCKER_HOST_IP=${XX#tcp\:\/\/} fi fi -echo DOCKER_HOST_IP is $DOCKER_HOST_IP + +if [ -z "$DOCKER_HOST_IP" ] ; then + echo DOCKER_HOST_IP is not set - localhost will be used +else + echo DOCKER_HOST_IP is ${DOCKER_HOST_IP} +fi + +export COMPOSE_HTTP_TIMEOUT=240 diff --git a/settings.gradle b/settings.gradle index 54c3facf..067490f3 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,11 +1,13 @@ include "common-swagger" include "ftgo-test-util" +include "ftgo-test-util-json-schema" include "ftgo-common" include "ftgo-common-jpa" include "ftgo-order-service" + include "ftgo-order-service-api" include "ftgo-order-service-contracts" @@ -14,18 +16,27 @@ include "ftgo-kitchen-service" include "ftgo-kitchen-service-contracts" include "ftgo-accounting-service-api" +include "ftgo-accounting-service-api-spec" include "ftgo-accounting-service" include "ftgo-accounting-service-contracts" include "ftgo-consumer-service-api" +include "ftgo-consumer-service-api-spec" include "ftgo-consumer-service" include "ftgo-consumer-service-contracts" include "ftgo-restaurant-service-api" +include "ftgo-restaurant-service-api-spec" +include "ftgo-restaurant-service-contracts" include "ftgo-restaurant-service" + include "ftgo-order-history-service" + +include "ftgo-delivery-service" +include "ftgo-delivery-service-api" + + include "ftgo-api-gateway" include "ftgo-end-to-end-tests" include "ftgo-restaurant-service-aws-lambda" - diff --git a/show-swagger-ui-urls.sh b/show-swagger-ui-urls.sh index 85d18a23..9914b7d6 100755 --- a/show-swagger-ui-urls.sh +++ b/show-swagger-ui-urls.sh @@ -2,10 +2,10 @@ ./wait-for-services.sh -echo Create consumer - open http://${DOCKER_HOST_IP?}:8081/swagger-ui.html -echo Create a restaurant - open http://${DOCKER_HOST_IP?}:8084/swagger-ui.html -echo Create an order - open http://${DOCKER_HOST_IP?}:8082/swagger-ui.html -echo View the order - open http://${DOCKER_HOST_IP?}:8082/swagger-ui.html -echo View the order history - open http://${DOCKER_HOST_IP?}:8086/swagger-ui.html -echo Zipkin distributed tracing - open http://${DOCKER_HOST_IP?}:9411 +echo Create consumer - open http://${DOCKER_HOST_IP:-localhost}:8081/swagger-ui/index.html +echo Create a restaurant - open http://${DOCKER_HOST_IP:-localhost}:8084/swagger-ui/index.html +echo Create an order - open http://${DOCKER_HOST_IP:-localhost}:8082/swagger-ui/index.html +echo View the order - open http://${DOCKER_HOST_IP:-localhost}:8082/swagger-ui/index.html +echo View the order history - open http://${DOCKER_HOST_IP:-localhost}:8086/swagger-ui/index.html +echo Zipkin distributed tracing - open http://${DOCKER_HOST_IP:-localhost}:9411 diff --git a/skaffold.yaml b/skaffold.yaml new file mode 100644 index 00000000..c0f566ad --- /dev/null +++ b/skaffold.yaml @@ -0,0 +1,20 @@ +apiVersion: skaffold/v1 +kind: Config +metadata: + name: ftgo-application +build: + artifacts: + - image: msapatterns/ftgo-consumer-service + context: ftgo-consumer-service +deploy: + kubectl: + manifests: + - deployment/kubernetes/cdc-services/eventuate-local-cdc-service.yml + - deployment/kubernetes/cdc-services/ftgo-tram-cdc-service.yml + - deployment/kubernetes/stateful-services/ftgo-db-secret.yml + - deployment/kubernetes/stateful-services/ftgo-dynamodb-local.yml + - deployment/kubernetes/stateful-services/ftgo-kafka-config.yml + - deployment/kubernetes/stateful-services/ftgo-kafka-deployment.yml + - deployment/kubernetes/stateful-services/ftgo-mysql-deployment.yml + - deployment/kubernetes/stateful-services/ftgo-zookeeper-deployment.yml + - ftgo-consumer-service/src/deployment/kubernetes/ftgo-consumer-service.yml diff --git a/start-infrastructure-services.sh b/start-infrastructure-services.sh index 8ba8cfcc..4a702bdb 100755 --- a/start-infrastructure-services.sh +++ b/start-infrastructure-services.sh @@ -1,6 +1,6 @@ #!/bin/bash -e -docker-compose up -d --build $* mysql tram-cdc-service eventuate-local-cdc-service dynamodblocal +docker-compose up -d --build $* mysql cdc-service eventuate-local-cdc-service dynamodblocal ./initialize-dynamodb.sh diff --git a/wait-for-services.sh b/wait-for-services.sh index 1685e1c9..913b7c8f 100755 --- a/wait-for-services.sh +++ b/wait-for-services.sh @@ -1,6 +1,3 @@ #! /bin/bash -./_wait-for-services.sh /actuator/health 8081 8082 8083 8084 8085 8086 - -./_wait-for-services.sh /health 8099 8098 - +./_wait-for-services.sh /actuator/health 8081 8082 8083 8084 8085 8086 8099