diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 index 6143e53..f2f2307 --- a/.gitignore +++ b/.gitignore @@ -1,22 +1,34 @@ -# Compiled class file -*.class +target/ +.mvn/ -# Log file -*.log +application-*.yml -# BlueJ files -*.ctxt +### STS ### +.classpath +.factorypath +.project +.settings +.springBeans -# Mobile Tools for Java (J2ME) -.mtj.tmp/ +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +*.DS_Store -# Package Files # -*.jar -*.war -*.ear -*.zip -*.tar.gz -*.rar +### NetBeans ### +nbproject/private/ +build/ +nbbuild/ +dist/ +nbdist/ +.nb-gradle/ -# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml -hs_err_pid* +.gradle/ + +### Application ### +backup-temp/ +mocks-prod +fake-api-data +out diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..a0d3488 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,18 @@ +language: java +jdk: +- oraclejdk8 +before_cache: +- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock +- rm -fr $HOME/.gradle/caches/*/plugin-resolution/ +cache: + directories: + - "$HOME/.gradle/caches/" + - "$HOME/.gradle/wrapper/" +script: +- gradle check +- gradle codeCoverageReport +after_success: +- bash <(curl -s https://codecov.io/bash) -t + +notifications: + email: false diff --git a/README.md b/README.md new file mode 100644 index 0000000..dd6626b --- /dev/null +++ b/README.md @@ -0,0 +1,61 @@ +[![Build Status](https://travis-ci.org/concretesolutions/mock-api.svg?branch=master)](https://travis-ci.org/concretesolutions/mock-api) +[![codecov.io](https://codecov.io/github/concretesolutions/mock-api/coverage.svg?branch=master)](https://codecov.io/github/concretesolutions/mock-api?branch=master) +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/67cdddf44d87495c84e3bddfdb5de074)](https://www.codacy.com/app/concrete/mock-api?utm_source=github.com&utm_medium=referral&utm_content=concretesolutions/mock-api&utm_campaign=Badge_Grade) + +## Coverage History +![codecov.io](https://codecov.io/github/concretesolutions/mock-api/branch.svg?branch=master) + +# Mock API + +App criado para fazer mock com REST utilizando JSON + +## Regras + +Quando uma request é feita é seguido o seguinte fluxo: + +* Existe na pasta do mock (conforme a propriedade `api.fileBase`)? Caso sim, retorna o mock +* A uri se encaixa em algum pattern da lista de `api.uriConfigurations[].pattern`? Caso sim, vai redirecionar conforme a configuração e fazer fazer cache conforme o field `backup` +* Se não entrar nos fluxos anteriores, vai redirecionar para o host padrão `api.host` + +## Requisitos +* Java JDK 8 +* Gradle 4 + +## Run + +## Usando seu arquivo de propriedades +Crie seu arquivo de propriedade `src/main/resources/application-custom.yml` e rode com o argumento `-Dspring.profiles.active=custom`. Exemplo: +``` +gradle bootRun -Dspring.profiles.active=custom +``` + +## Usando imagem Docker +Para gerar a imagem Docker do projeto, execute: +``` +gradle buildDocker +``` + +Por padrão, o nome da imagem será `concretesolutions/mock-api:VERSAO`. + +Para rodar a aplicação, crie dois diretórios: um contendo o arquivo de configuração `application-custom.yml` e o outro contendo os arquivos de mock. Execute então: + +``` +docker run -d --name mock-api \ + -p 9090:9090 \ + -v /path/para/arquivo/application-custom.yml:/config/application.yml \ + -v /path/para/diretorio/dados/:/data \ + concretesolutions/mock-api:VERSAO +``` + +A porta `9090` expõe o serviço enquanto a porta `5000` é utilizada para debug da aplicação. + +Para visualizar os logs da aplicação a partir do container: `docker logs -f mock-api` + +## TODO +* Inseriri exemplo do "arquivo de propriedades" no README +* Separar testes unitários dos testes integrados +* Corrigir os testes ignorados +* Corrigir Code Style +* Adicionar plugin do FindBugs +* Revisar dependências (ver, por exemplo, se é mesmo necessário ter o GSON ou modelmapper) +* Usar objectmapper como component: `compile('com.fasterxml.jackson.datatype:jackson-datatype-jdk8')` diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..4143663 --- /dev/null +++ b/build.gradle @@ -0,0 +1,73 @@ +buildscript { + ext { + springBootVersion = '1.5.4.RELEASE' + } + repositories { + mavenCentral() + } + dependencies { + classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") + classpath('se.transmode.gradle:gradle-docker:1.2') + } +} + +apply plugin: 'java' +apply plugin: 'eclipse' +apply plugin: 'org.springframework.boot' +apply plugin: 'jacoco' +apply plugin: 'docker' + +version = '4.0.0-SNAPSHOT' +sourceCompatibility = 1.8 + +repositories { + mavenCentral() +} + +dependencies { + compile('org.springframework.boot:spring-boot-starter') + compile 'org.springframework.boot:spring-boot-starter-undertow' + compile 'org.springframework.boot:spring-boot-starter-web' + compile 'org.springframework.boot:spring-boot-starter-hateoas' + compile 'com.google.code.gson:gson' + compile 'javax.ws.rs:javax.ws.rs-api:2.0' + compile 'net.minidev:json-smart:1.0.8' + compile 'com.google.guava:guava:22.0' + compile 'org.modelmapper:modelmapper:0.7.5' + compile 'commons-io:commons-io:2.5' + compile 'org.apache.httpcomponents:httpclient:4.4.1' + compile 'com.squareup.okhttp3:okhttp:3.8.1' + + testCompile 'org.springframework.boot:spring-boot-starter-test' + testCompile 'br.com.six2six:fixture-factory:3.1.0' +} + +bootRun { + String activeProfile = System.properties['spring.profiles.active'] + systemProperty "spring.profiles.active", activeProfile +} + +task codeCoverageReport(type: JacocoReport) { + executionData fileTree(project.rootDir.absolutePath).include("**/build/jacoco/*.exec") + + sourceSets sourceSets.main + reports { + xml.enabled true + xml.destination "${buildDir}/reports/jacoco/report.xml" + html.enabled false + csv.enabled false + } +} + + +task buildDocker(type: Docker, dependsOn: build) { + push = false + applicationName = 'concretesolutions/' + jar.baseName + dockerfile = file('src/main/docker/Dockerfile') + doFirst { + copy { + from jar + into stageDir + } + } +} \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..1a958be Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..681fe21 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon Jul 10 15:07:48 BRT 2017 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-3.5.1-all.zip diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..4453cce --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save ( ) { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..e95643d --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/mocks-test/get/guests/132/1.json b/mocks-test/get/guests/132/1.json new file mode 100755 index 0000000..dddbac5 --- /dev/null +++ b/mocks-test/get/guests/132/1.json @@ -0,0 +1,18 @@ +{ + "response": { + "body": [ + { + "cc": "11", + "ca": "12" + }, + { + "cc": "21", + "ca": "22" + }, + { + "cc": "31", + "ca": "32" + } + ] + } +} diff --git a/mocks-test/get/guests/132/users/1.json b/mocks-test/get/guests/132/users/1.json new file mode 100755 index 0000000..614b8ce --- /dev/null +++ b/mocks-test/get/guests/132/users/1.json @@ -0,0 +1,9 @@ +{ + "response": { + "body": [ + { + "move": "3" + } + ] + } +} diff --git a/mocks-test/get/guests/132/users/21/cc/1.json b/mocks-test/get/guests/132/users/21/cc/1.json new file mode 100755 index 0000000..3f8c597 --- /dev/null +++ b/mocks-test/get/guests/132/users/21/cc/1.json @@ -0,0 +1,7 @@ +{ + "response": { + "body": { + "tt": "789" + } + } +} diff --git a/mocks-test/get/guests/132/users/22/cc/1.json b/mocks-test/get/guests/132/users/22/cc/1.json new file mode 100755 index 0000000..462294e --- /dev/null +++ b/mocks-test/get/guests/132/users/22/cc/1.json @@ -0,0 +1,7 @@ +{ + "response": { + "body": { + "tt": "456" + } + } +} diff --git a/mocks-test/get/payments/user/detail/1.json b/mocks-test/get/payments/user/detail/1.json new file mode 100755 index 0000000..e5240de --- /dev/null +++ b/mocks-test/get/payments/user/detail/1.json @@ -0,0 +1,18 @@ +{ + "request": { + "query": { + "payment": "1", + "value": "10" + } + }, + "response": { + "body": [ + { + "total": "1700" + }, + { + "total": "1800" + } + ] + } +} diff --git a/mocks-test/get/payments/user/detail/2.json b/mocks-test/get/payments/user/detail/2.json new file mode 100755 index 0000000..3690a07 --- /dev/null +++ b/mocks-test/get/payments/user/detail/2.json @@ -0,0 +1,18 @@ +{ + "request": { + "query": { + "payment": "2", + "value": "20" + } + }, + "response": { + "body": [ + { + "total": "1702" + }, + { + "total": "1802" + } + ] + } +} diff --git a/mocks-test/get/person/11/1.json b/mocks-test/get/person/11/1.json new file mode 100755 index 0000000..a24e97f --- /dev/null +++ b/mocks-test/get/person/11/1.json @@ -0,0 +1,7 @@ +{ + "response": { + "body": { + "name": "Paul" + } + } +} diff --git a/mocks-test/get/users/123/1.json b/mocks-test/get/users/123/1.json new file mode 100755 index 0000000..a24e97f --- /dev/null +++ b/mocks-test/get/users/123/1.json @@ -0,0 +1,7 @@ +{ + "response": { + "body": { + "name": "Paul" + } + } +} diff --git a/mocks-test/get/users/123/2.json b/mocks-test/get/users/123/2.json new file mode 100755 index 0000000..08e23df --- /dev/null +++ b/mocks-test/get/users/123/2.json @@ -0,0 +1,13 @@ +{ + "request": { + "query": { + "payment": "22" + } + }, + "response": { + "body": { + "name": "John" + }, + "httpStatus": 202 + } +} diff --git a/mocks-test/patch/users/1456/1.json b/mocks-test/patch/users/1456/1.json new file mode 100755 index 0000000..75b53ec --- /dev/null +++ b/mocks-test/patch/users/1456/1.json @@ -0,0 +1,12 @@ +{ + "request": { + "body": { + "result": "done" + } + }, + "response": { + "body": { + "sync": "success" + } + } +} diff --git a/mocks-test/post/move/to/country/13/1.json b/mocks-test/post/move/to/country/13/1.json new file mode 100755 index 0000000..b480e59 --- /dev/null +++ b/mocks-test/post/move/to/country/13/1.json @@ -0,0 +1,12 @@ +{ + "request": { + "body": { + "count": "698" + } + }, + "response": { + "body": { + "street": "USA" + } + } +} diff --git a/mocks-test/post/move/to/country/13/pi/1.json b/mocks-test/post/move/to/country/13/pi/1.json new file mode 100755 index 0000000..4595917 --- /dev/null +++ b/mocks-test/post/move/to/country/13/pi/1.json @@ -0,0 +1,12 @@ +{ + "request": { + "body": { + "pi": "123456" + } + }, + "response": { + "body": { + "street": "NY" + } + } +} diff --git a/mocks-test/post/move/to/country/13/pi/2.json b/mocks-test/post/move/to/country/13/pi/2.json new file mode 100755 index 0000000..b8ccfb3 --- /dev/null +++ b/mocks-test/post/move/to/country/13/pi/2.json @@ -0,0 +1,14 @@ +{ + "request": { + "body": { + "pi": "j12" + } + }, + "response": { + "body": { + "codigo": "123", + "mensagem": "Error :(" + }, + "httpStatus": "422" + } +} diff --git a/src/main/docker/Dockerfile b/src/main/docker/Dockerfile new file mode 100644 index 0000000..b84c47f --- /dev/null +++ b/src/main/docker/Dockerfile @@ -0,0 +1,17 @@ +FROM frolvlad/alpine-oraclejdk8:slim +MAINTAINER "Concrete " + +VOLUME /config +VOLUME /data + +WORKDIR / + +EXPOSE 5000 +EXPOSE 9090 + +COPY mock-api-*.jar app.jar +ENTRYPOINT ["java", \ + "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5000 -Djava.security.egd=file:/dev/./urandom", \ + "-jar", \ + "/app.jar", \ + "--spring.config.location=file:/config/application.yml"] \ No newline at end of file diff --git a/src/main/java/br/com/concrete/mock/ApiApplication.java b/src/main/java/br/com/concrete/mock/ApiApplication.java new file mode 100755 index 0000000..2ce12db --- /dev/null +++ b/src/main/java/br/com/concrete/mock/ApiApplication.java @@ -0,0 +1,34 @@ +package br.com.concrete.mock; + +import okhttp3.OkHttpClient; +import org.apache.http.client.HttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; +import org.springframework.web.client.RestTemplate; + +@SpringBootApplication +@EnableCaching +public class ApiApplication { + + public static void main(String[] args) { + SpringApplication.run(ApiApplication.class, args); + } + + @Bean + public RestTemplate restTemplate() { + HttpClient httpClient = HttpClientBuilder.create().build(); + ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient); + return new RestTemplate(requestFactory); + } + + @Bean + public OkHttpClient okHttpClient(){ + return new OkHttpClient(); + } + +} diff --git a/src/main/java/br/com/concrete/mock/configuration/api/v1/controller/CaptureStateController.java b/src/main/java/br/com/concrete/mock/configuration/api/v1/controller/CaptureStateController.java new file mode 100755 index 0000000..04d0838 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/configuration/api/v1/controller/CaptureStateController.java @@ -0,0 +1,57 @@ +package br.com.concrete.mock.configuration.api.v1.controller; + +import br.com.concrete.mock.configuration.api.v1.mapper.CaptureStateDto; +import br.com.concrete.mock.configuration.service.CaptureStateService; +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.CommandLineRunner; +import org.springframework.context.annotation.Bean; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/configuration/capture-state") +public class CaptureStateController { + + private static final Logger LOGGER = LoggerFactory.getLogger(CaptureStateController.class); + + private final CaptureStateService service; + + @Value("${captureState}") + private boolean captureState =true; + + @Autowired + public CaptureStateController(CaptureStateService service) { + this.service = service; + } + + @Bean + private CommandLineRunner onLoad() { + return args -> { + LOGGER.info("Application capture state: " + captureState); + if(captureState) { + this.service.enable(); + } + }; + } + + @RequestMapping(method = RequestMethod.GET) + public ResponseEntity getCurrent() { + return ResponseEntity.ok().body(new CaptureStateDto(service.getCurrent())); + } + + @RequestMapping(value = "/enable", method = RequestMethod.POST) + public ResponseEntity enable() { + return ResponseEntity.ok().body(new CaptureStateDto(service.enable())); + } + + @RequestMapping(value = "/disable", method = RequestMethod.DELETE) + @ResponseStatus(HttpStatus.NO_CONTENT) + public void disable() { + service.delete(); + } + +} diff --git a/src/main/java/br/com/concrete/mock/configuration/api/v1/mapper/CaptureStateDto.java b/src/main/java/br/com/concrete/mock/configuration/api/v1/mapper/CaptureStateDto.java new file mode 100755 index 0000000..bcd7778 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/configuration/api/v1/mapper/CaptureStateDto.java @@ -0,0 +1,35 @@ +package br.com.concrete.mock.configuration.api.v1.mapper; + +import br.com.concrete.mock.configuration.model.CaptureState; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.io.Serializable; +import java.util.Optional; + +public class CaptureStateDto implements Serializable { + + private final Boolean enabled; + + @JsonCreator + public CaptureStateDto(@JsonProperty("enabled") Boolean enabled) { + this.enabled = enabled; + } + + public CaptureStateDto(Optional captureMode) { + this(captureMode.map(o -> o.isEnabled()).orElse(false)); + } + + public CaptureStateDto(CaptureState captureState) { + this(Optional.ofNullable(captureState)); + } + + public Boolean isEnabled() { + return enabled; + } + + public CaptureState toModel() { + return new CaptureState(enabled); + } + +} diff --git a/src/main/java/br/com/concrete/mock/configuration/model/CaptureState.java b/src/main/java/br/com/concrete/mock/configuration/model/CaptureState.java new file mode 100755 index 0000000..3344213 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/configuration/model/CaptureState.java @@ -0,0 +1,15 @@ +package br.com.concrete.mock.configuration.model; + +public class CaptureState { + + private final Boolean enabled; + + public CaptureState(Boolean enabled) { + this.enabled = enabled; + } + + public Boolean isEnabled() { + return enabled; + } + +} diff --git a/src/main/java/br/com/concrete/mock/configuration/repository/CaptureStateRepository.java b/src/main/java/br/com/concrete/mock/configuration/repository/CaptureStateRepository.java new file mode 100755 index 0000000..a01dac1 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/configuration/repository/CaptureStateRepository.java @@ -0,0 +1,14 @@ +package br.com.concrete.mock.configuration.repository; + +import br.com.concrete.mock.configuration.model.CaptureState; + +import java.util.Optional; + +public interface CaptureStateRepository { + + Optional getCurrent(); + + CaptureState save(CaptureState captureState); + + void delete(); +} diff --git a/src/main/java/br/com/concrete/mock/configuration/repository/impl/CaptureStateRepositoryImpl.java b/src/main/java/br/com/concrete/mock/configuration/repository/impl/CaptureStateRepositoryImpl.java new file mode 100755 index 0000000..62634d6 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/configuration/repository/impl/CaptureStateRepositoryImpl.java @@ -0,0 +1,40 @@ +package br.com.concrete.mock.configuration.repository.impl; + +import br.com.concrete.mock.configuration.model.CaptureState; +import br.com.concrete.mock.configuration.repository.CaptureStateRepository; +import org.springframework.cache.annotation.CacheConfig; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Component; + +import java.util.Optional; + +import static java.util.Optional.empty; + +@Component +@CacheConfig(cacheNames = "captureState") +public class CaptureStateRepositoryImpl implements CaptureStateRepository { + + private Optional captureState; + + public CaptureStateRepositoryImpl() { + delete(); + } + + @Cacheable + public Optional getCurrent() { + return captureState; + } + + @CacheEvict(allEntries = true) + public CaptureState save(CaptureState captureState) { + this.captureState = Optional.ofNullable(captureState); + return captureState; + } + + @CacheEvict(allEntries = true) + public void delete() { + this.captureState = empty(); + } + +} diff --git a/src/main/java/br/com/concrete/mock/configuration/service/CaptureExecutor.java b/src/main/java/br/com/concrete/mock/configuration/service/CaptureExecutor.java new file mode 100755 index 0000000..cb2a9f3 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/configuration/service/CaptureExecutor.java @@ -0,0 +1,11 @@ +package br.com.concrete.mock.configuration.service; + +import br.com.concrete.mock.generic.model.Endpoint; +import br.com.concrete.mock.generic.model.ExternalApiResult; + +@FunctionalInterface +public interface CaptureExecutor { + + void execute(ExternalApiResult apiResult, Endpoint endpoint); + +} diff --git a/src/main/java/br/com/concrete/mock/configuration/service/CaptureStateService.java b/src/main/java/br/com/concrete/mock/configuration/service/CaptureStateService.java new file mode 100755 index 0000000..b908a48 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/configuration/service/CaptureStateService.java @@ -0,0 +1,14 @@ +package br.com.concrete.mock.configuration.service; + +import br.com.concrete.mock.configuration.model.CaptureState; + +import java.util.Optional; + +public interface CaptureStateService { + Optional getCurrent(); + + CaptureState enable(); + + void delete(); + +} diff --git a/src/main/java/br/com/concrete/mock/configuration/service/impl/CaptureExecutorImpl.java b/src/main/java/br/com/concrete/mock/configuration/service/impl/CaptureExecutorImpl.java new file mode 100755 index 0000000..7851524 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/configuration/service/impl/CaptureExecutorImpl.java @@ -0,0 +1,26 @@ +package br.com.concrete.mock.configuration.service.impl; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import br.com.concrete.mock.configuration.service.CaptureExecutor; +import br.com.concrete.mock.generic.model.Endpoint; +import br.com.concrete.mock.generic.model.ExternalApiResult; +import br.com.concrete.mock.generic.service.EndpointBackupService; + +@Service +public class CaptureExecutorImpl implements CaptureExecutor { + + private final EndpointBackupService endpointBackupService; + + @Autowired + public CaptureExecutorImpl(EndpointBackupService endpointBackupService) { + this.endpointBackupService = endpointBackupService; + } + + public void execute(ExternalApiResult apiResult, Endpoint endpoint) { + if (apiResult.getUriConfiguration().isActiveBackup()) { + endpointBackupService.doBackup(endpoint); + } + } +} diff --git a/src/main/java/br/com/concrete/mock/configuration/service/impl/CaptureStateServiceImpl.java b/src/main/java/br/com/concrete/mock/configuration/service/impl/CaptureStateServiceImpl.java new file mode 100755 index 0000000..d4f2e77 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/configuration/service/impl/CaptureStateServiceImpl.java @@ -0,0 +1,46 @@ +package br.com.concrete.mock.configuration.service.impl; + +import br.com.concrete.mock.configuration.model.CaptureState; +import br.com.concrete.mock.configuration.repository.CaptureStateRepository; +import br.com.concrete.mock.configuration.service.CaptureStateService; +import br.com.concrete.mock.infra.exception.impl.ApiApplicationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Optional; + +@Component +public class CaptureStateServiceImpl implements CaptureStateService { + + private static final Logger LOGGER = LoggerFactory.getLogger(CaptureStateServiceImpl.class); + + private final CaptureStateRepository repository; + + @Autowired + public CaptureStateServiceImpl(CaptureStateRepository repository) { + this.repository = repository; + } + + public Optional getCurrent() { + return repository.getCurrent(); + } + + public CaptureState enable() { + return repository.save(Optional + .of(new CaptureState(true)) + .map(old -> { + LOGGER.info("Backup activated"); + return new CaptureState(true); + }) + .orElseThrow(() -> new ApiApplicationException("cannot have an invalid capture state")) + ); + } + + public void delete() { + LOGGER.info("Backup deactivated"); + repository.delete(); + } + +} diff --git a/src/main/java/br/com/concrete/mock/generic/api/v1/controller/GenericApiController.java b/src/main/java/br/com/concrete/mock/generic/api/v1/controller/GenericApiController.java new file mode 100755 index 0000000..a632a2c --- /dev/null +++ b/src/main/java/br/com/concrete/mock/generic/api/v1/controller/GenericApiController.java @@ -0,0 +1,94 @@ +package br.com.concrete.mock.generic.api.v1.controller; + +import br.com.concrete.mock.generic.api.v1.mapper.RequestMapper; +import br.com.concrete.mock.generic.service.GenericApiService; +import br.com.concrete.mock.infra.component.JsonFormatter; +import br.com.concrete.mock.infra.component.RequestFormatter; +import com.google.gson.Gson; +import com.google.gson.JsonParseException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import java.util.Optional; + +@RestController +public class GenericApiController { + + private static final Logger LOGGER = LoggerFactory.getLogger(GenericApiController.class); + + private final GenericApiService genericApiService; + private final RequestMapper requestMapper; + private final JsonFormatter jsonFormatter; + private final RequestFormatter requestFormatter; + + @Autowired + public GenericApiController(GenericApiService genericApiService, RequestMapper requestMapper, + JsonFormatter jsonFormatter, RequestFormatter requestFormatter) { + this.genericApiService = genericApiService; + this.requestMapper = requestMapper; + this.jsonFormatter = jsonFormatter; + this.requestFormatter = requestFormatter; + } + + private void logRequest(final HttpServletRequest request, final Optional requestBody) { + LOGGER.info(requestFormatter.generateLog(request, requestBody)); + LOGGER.info("Headers: " + genericApiService.getHeaders(request)); + requestBody.ifPresent(body -> logJson(new Gson().toJson(body))); + } + + private void logJson(final String jsonString) { + try { + LOGGER.info(jsonFormatter.format(jsonString)); + } catch (JsonParseException e) { + LOGGER.warn("cannot print json: " + jsonString); + } + } + private ResponseEntity interceptRequestAnyMedia(final HttpServletRequest request, + final Optional requestBody) { + logRequest(request, requestBody); + + Optional> responseEntity; + if((request.getMethod().equalsIgnoreCase(HttpMethod.GET.name()))&& + (request.getQueryString() != null)){ + responseEntity = genericApiService + .genericResponseEntityGET(requestMapper.mapper(request, requestBody), request); + }else { + responseEntity = genericApiService + .genericResponseEntity(requestMapper.mapper(request, requestBody)); + } + + final ResponseEntity result = responseEntity + .map(r -> { + logJson(r.getBody()); + return r; + }) + .orElse(null); + + if (result == null) { + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } else { + return result; + } + } + + @RequestMapping(value = "/*/**") + public ResponseEntity interceptRequest(final HttpServletRequest request, + final @RequestBody(required = false) Object requestBodyRaw) { + return interceptRequestAnyMedia(request, Optional.ofNullable(requestBodyRaw)); + } + + @RequestMapping(value = "/*/**", consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + public ResponseEntity interceptRequestForm(final HttpServletRequest request) { + return interceptRequestAnyMedia(request, Optional.empty()); + } + +} diff --git a/src/main/java/br/com/concrete/mock/generic/api/v1/mapper/RequestMapper.java b/src/main/java/br/com/concrete/mock/generic/api/v1/mapper/RequestMapper.java new file mode 100755 index 0000000..bf36d99 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/generic/api/v1/mapper/RequestMapper.java @@ -0,0 +1,48 @@ +package br.com.concrete.mock.generic.api.v1.mapper; + +import br.com.concrete.mock.generic.mapper.HeaderMapper; +import br.com.concrete.mock.generic.mapper.QueryMapper; +import br.com.concrete.mock.generic.model.Request; +import com.google.gson.Gson; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.RequestMethod; + +import javax.servlet.http.HttpServletRequest; +import java.util.Optional; + +@Component +public class RequestMapper { + + private static final Gson GSON = new Gson(); + + private final QueryMapper queryMapper; + private final HeaderMapper headerMapper; + + @Autowired + public RequestMapper(QueryMapper queryMapper, HeaderMapper headerMapper) { + this.queryMapper = queryMapper; + this.headerMapper = headerMapper; + } + + private Request.Builder mapperBuilder(final HttpServletRequest request) { + return new Request + .Builder(RequestMethod.valueOf(request.getMethod().toUpperCase()), request.getRequestURI()) + .withQuery(queryMapper.mapper(request.getQueryString())) + .withHeader(headerMapper.mapper(request)); + } + + public Request mapper(final HttpServletRequest request) { + return mapperBuilder(request).build(); + } + + public Request mapper(final HttpServletRequest request, final Optional requestBody) { + final Request.Builder requestMockBuilder = mapperBuilder(request); + + return requestBody + .map(requestBodyJson -> requestMockBuilder.withBody(Optional.ofNullable(GSON.toJson(requestBodyJson)))) + .orElse(requestMockBuilder) + .build(); + } + +} diff --git a/src/main/java/br/com/concrete/mock/generic/mapper/EndpointDto.java b/src/main/java/br/com/concrete/mock/generic/mapper/EndpointDto.java new file mode 100755 index 0000000..ad45ce0 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/generic/mapper/EndpointDto.java @@ -0,0 +1,55 @@ +package br.com.concrete.mock.generic.mapper; + +import br.com.concrete.mock.generic.model.Endpoint; +import br.com.concrete.mock.generic.model.Request; +import br.com.concrete.mock.generic.model.Response; +import br.com.concrete.mock.infra.component.FromJsonStringToObjectConverter; +import br.com.concrete.mock.infra.exception.impl.ApiApplicationException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import org.springframework.web.bind.annotation.RequestMethod; + +import java.io.Serializable; +import java.util.Optional; + +public class EndpointDto implements Serializable { + + private final RequestDto request; + private final ResponseDto response; + + @JsonCreator + public EndpointDto(@JsonProperty("request") RequestDto request, @JsonProperty("response") ResponseDto response) { + this.request = request; + this.response = response; + } + + public EndpointDto(Endpoint endpoint, FromJsonStringToObjectConverter converter) { + this( + endpoint.getRequest() == null || !endpoint.getRequest().isValid() ? null : new RequestDto(endpoint.getRequest(), converter), + endpoint.getResponse() == null ? null : new ResponseDto(endpoint.getResponse(), converter) + ); + } + + public RequestDto getRequest() { + return request; + } + + public ResponseDto getResponse() { + return response; + } + + public Endpoint toModel(RequestMethod method, String uri) { + final Request request = Optional + .ofNullable(this.request) + .map(requestDto -> requestDto.toModel(method, uri)) + .orElse(new Request.Builder(method, uri).build()); + + final Response response = Optional + .ofNullable(this.response) + .map(ResponseDto::toModel) + .orElseThrow(() -> new ApiApplicationException("Cannot deserialize: response is null")); + + return new Endpoint.Builder(request, response).build(); + } + +} diff --git a/src/main/java/br/com/concrete/mock/generic/mapper/EndpointMapper.java b/src/main/java/br/com/concrete/mock/generic/mapper/EndpointMapper.java new file mode 100755 index 0000000..acafc1e --- /dev/null +++ b/src/main/java/br/com/concrete/mock/generic/mapper/EndpointMapper.java @@ -0,0 +1,40 @@ +package br.com.concrete.mock.generic.mapper; + +import br.com.concrete.mock.generic.model.Endpoint; +import br.com.concrete.mock.infra.component.file.FileJsonReader; +import com.google.gson.Gson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.RequestMethod; + +import java.io.IOException; +import java.util.Optional; + +@Component +public class EndpointMapper { + + private static final Logger LOGGER = LoggerFactory.getLogger(EndpointMapper.class); + + private final FileJsonReader fileJsonReader; + + @Autowired + public EndpointMapper(FileJsonReader fileJsonReader) { + this.fileJsonReader = fileJsonReader; + } + + public Optional mapper(RequestMethod requestMethod, String requestUrl, String fileName) { + try { + return fileJsonReader + .getJsonByFileName(fileName) + .map(endpointDtoJson -> new Gson().fromJson(endpointDtoJson, EndpointDto.class)) + .map(endpointDto -> endpointDto.toModel(requestMethod, requestUrl)) + .map(endpoint -> new Endpoint.Builder(endpoint).withId(fileName).build()); + } catch (IOException e) { + LOGGER.error("Cannot to map endpoint from file", e); + return Optional.empty(); + } + } + +} diff --git a/src/main/java/br/com/concrete/mock/generic/mapper/HeaderMapper.java b/src/main/java/br/com/concrete/mock/generic/mapper/HeaderMapper.java new file mode 100755 index 0000000..a12bbef --- /dev/null +++ b/src/main/java/br/com/concrete/mock/generic/mapper/HeaderMapper.java @@ -0,0 +1,27 @@ +package br.com.concrete.mock.generic.mapper; + +import org.springframework.http.HttpHeaders; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import java.util.Enumeration; +import java.util.Optional; + +@Component +public class HeaderMapper { + + public Optional mapper(HttpServletRequest request) { + final HttpHeaders httpHeaders = new HttpHeaders(); + + Enumeration headerNames = request.getHeaderNames(); + if (headerNames != null) { + while (headerNames.hasMoreElements()) { + final String name = headerNames.nextElement(); + httpHeaders.add(name, request.getHeader(name)); + } + } + + return Optional.of(httpHeaders); + } + +} diff --git a/src/main/java/br/com/concrete/mock/generic/mapper/QueryMapper.java b/src/main/java/br/com/concrete/mock/generic/mapper/QueryMapper.java new file mode 100755 index 0000000..1f858ff --- /dev/null +++ b/src/main/java/br/com/concrete/mock/generic/mapper/QueryMapper.java @@ -0,0 +1,47 @@ +package br.com.concrete.mock.generic.mapper; + +import br.com.concrete.mock.infra.exception.impl.ApiApplicationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.springframework.util.MultiValueMap; +import org.springframework.web.util.UriComponentsBuilder; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Map; +import java.util.Optional; + +@Component +public class QueryMapper { + + private static final Logger LOGGER = LoggerFactory.getLogger(QueryMapper.class); + + public Optional> mapper(final String queryRequest) { + return Optional + .ofNullable(queryRequest) + .map(query -> { + try { + return UriComponentsBuilder + .fromUri(new URI("localhost?" + query)) + .build() + .getQueryParams(); + } catch (URISyntaxException e) { + final String message = new StringBuilder() + .append("Cannot convert queryString # ") + .append(query) + .append(" # ") + .append(e.getMessage()) + .toString(); + + LOGGER.error(message, e); + + throw new ApiApplicationException(message); + } + + }) + .filter(parameters -> !parameters.isEmpty()) + .map(MultiValueMap::toSingleValueMap); + } + +} diff --git a/src/main/java/br/com/concrete/mock/generic/mapper/RequestDto.java b/src/main/java/br/com/concrete/mock/generic/mapper/RequestDto.java new file mode 100755 index 0000000..0ea573c --- /dev/null +++ b/src/main/java/br/com/concrete/mock/generic/mapper/RequestDto.java @@ -0,0 +1,60 @@ +package br.com.concrete.mock.generic.mapper; + +import br.com.concrete.mock.generic.model.Request; +import br.com.concrete.mock.infra.component.FromJsonStringToObjectConverter; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import org.springframework.http.HttpHeaders; +import org.springframework.web.bind.annotation.RequestMethod; + +import java.io.Serializable; +import java.util.Map; +import java.util.Optional; + +public class RequestDto implements Serializable { + + private static final Gson GSON = new Gson(); + + private final HttpHeaders headers; + private final Map query; + private final JsonElement body; + + @JsonCreator + public RequestDto(@JsonProperty("headers") HttpHeaders headers, @JsonProperty("query") Map query, @JsonProperty("body") JsonElement body) { + this.headers = headers; + this.query = query; + this.body = body; + } + + public RequestDto(Request request, FromJsonStringToObjectConverter converter) { + this( + request.getHeaders().orElse(null), + request.getQuery().orElse(null), + converter.apply(request.getBody()) + ); + + } + + public HttpHeaders getHeaders() { + return headers; + } + + public Map getQuery() { + return query; + } + + public Object getBody() { + return body; + } + + public Request toModel(RequestMethod method, String uri) { + return new Request.Builder(method, uri) + .withHeader(headers) + .withQuery(query) + .withBody(Optional.ofNullable(body).map(GSON::toJson)) + .build(); + } + +} diff --git a/src/main/java/br/com/concrete/mock/generic/mapper/ResponseDto.java b/src/main/java/br/com/concrete/mock/generic/mapper/ResponseDto.java new file mode 100755 index 0000000..ac01196 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/generic/mapper/ResponseDto.java @@ -0,0 +1,50 @@ +package br.com.concrete.mock.generic.mapper; + +import br.com.concrete.mock.generic.model.Response; +import br.com.concrete.mock.infra.component.FromJsonStringToObjectConverter; +import br.com.concrete.mock.infra.exception.impl.ApiApplicationException; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.google.gson.Gson; +import com.google.gson.JsonElement; +import org.springframework.http.HttpStatus; + +import java.io.Serializable; +import java.util.Optional; + +public class ResponseDto implements Serializable { + + private static final Gson GSON = new Gson(); + + private final JsonElement body; + private final Integer httpStatus; + + @JsonCreator + public ResponseDto(@JsonProperty("body") JsonElement body, @JsonProperty("httpStatus") Integer httpStatus) { + this.body = body; + this.httpStatus = httpStatus; + } + + public ResponseDto(Response response, FromJsonStringToObjectConverter converter) { + this( + converter.apply(Optional.ofNullable(response.getBody())), + response.getHttpStatus().map(HttpStatus::value).orElse(null) + ); + } + + public Object getBody() { + return body; + } + + public Response toModel() { + final String bodyResponse = Optional + .ofNullable(body) + .map(GSON::toJson) + .orElseThrow(() -> new ApiApplicationException("Cannot have a null body on response")); + final Optional httpStatusResponse = Optional + .ofNullable(this.httpStatus) + .map(HttpStatus::valueOf); + return new Response(bodyResponse, httpStatusResponse); + } + +} diff --git a/src/main/java/br/com/concrete/mock/generic/model/Endpoint.java b/src/main/java/br/com/concrete/mock/generic/model/Endpoint.java new file mode 100755 index 0000000..5837300 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/generic/model/Endpoint.java @@ -0,0 +1,117 @@ +package br.com.concrete.mock.generic.model; + +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestMethod; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; + +public class Endpoint implements Comparable { + + private final Optional id; + private final Request request; + private final Response response; + + @JsonCreator + public Endpoint(@JsonProperty("id") Optional id, @JsonProperty("request") Request request, + @JsonProperty("response") Response response) { + this.id = id; + this.request = request; + this.response = response; + } + + @JsonIgnore + public Optional getId() { + return id; + } + + @JsonProperty + public Request getRequest() { + return request; + } + + @JsonProperty + public Response getResponse() { + return response; + } + + @Override + public int compareTo(Endpoint o) { + return this.request.compareTo(o.request); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Endpoint endpoint = (Endpoint) o; + return Objects.equals(id, endpoint.id) && + Objects.equals(request, endpoint.request) && + Objects.equals(response, endpoint.response); + } + + @Override + public int hashCode() { + return Objects.hash(id, request, response); + } + + public static class Builder { + + private Optional id; + private Request request; + private Response response; + + public Builder(Request request, Response response) { + this.request = request; + this.response = response; + } + + public Builder(final Endpoint endpoint) { + this.id = endpoint.id; + this.request = endpoint.request; + this.response = endpoint.response; + } + + public Builder withId(final Optional id) { + this.id = id; + return this; + } + + public Builder withId(final String id) { + return withId(Optional.ofNullable(id)); + } + + public Builder(Request request, ResponseEntity responseEntity) { + this(request, new Response(responseEntity.getBody(), Optional.ofNullable(responseEntity.getStatusCode()))); + } + + public Builder(RequestMethod method, String url, Response response) { + withEmptyRequest(method, url); + this.response = response; + } + + public Builder withRequest(Optional> requestQuery, Optional requestBody) { + return withRequest(new Request.Builder(this.request).withQuery(requestQuery).withBody(requestBody).build()); + } + + public Builder withRequest(Request request) { + this.request = request; + return this; + } + + public Builder withEmptyRequest(RequestMethod method, String url) { + return withRequest(new Request.Builder(method, url).build()); + } + + public Endpoint build() { + return new Endpoint(id, request, response); + } + + } + +} diff --git a/src/main/java/br/com/concrete/mock/generic/model/Error.java b/src/main/java/br/com/concrete/mock/generic/model/Error.java new file mode 100755 index 0000000..ba4cff4 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/generic/model/Error.java @@ -0,0 +1,27 @@ +package br.com.concrete.mock.generic.model; + +import java.io.Serializable; + +public class Error implements Serializable { + + private int codigo; + + private String mensagem; + + public Error() { + } + + public Error(int codigo, String mensagem) { + this.codigo = codigo; + this.mensagem = mensagem; + } + + public int getCodigo() { + return codigo; + } + + public String getMensagem() { + return mensagem; + } + +} diff --git a/src/main/java/br/com/concrete/mock/generic/model/ExternalApiResult.java b/src/main/java/br/com/concrete/mock/generic/model/ExternalApiResult.java new file mode 100644 index 0000000..3c51e58 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/generic/model/ExternalApiResult.java @@ -0,0 +1,25 @@ +package br.com.concrete.mock.generic.model; + +import org.springframework.http.ResponseEntity; + +import br.com.concrete.mock.infra.model.UriConfiguration; + +public class ExternalApiResult { + + private final ResponseEntity apiResult; + private final UriConfiguration uriConfiguration; + + public ExternalApiResult(ResponseEntity apiResult, UriConfiguration uriConfiguration) { + this.apiResult = apiResult; + this.uriConfiguration = uriConfiguration; + } + + public ResponseEntity getApiResult() { + return apiResult; + } + + public UriConfiguration getUriConfiguration() { + return uriConfiguration; + } + +} diff --git a/src/main/java/br/com/concrete/mock/generic/model/Request.java b/src/main/java/br/com/concrete/mock/generic/model/Request.java new file mode 100755 index 0000000..cd2f786 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/generic/model/Request.java @@ -0,0 +1,165 @@ +package br.com.concrete.mock.generic.model; + +import br.com.concrete.mock.infra.component.ConvertJson; +import br.com.concrete.mock.infra.component.impl.ConvertJsonImpl; +import br.com.concrete.mock.infra.exception.impl.JsonApiApplicationException; +import org.springframework.http.HttpHeaders; +import org.springframework.web.bind.annotation.RequestMethod; + +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +public class Request implements Comparable { + + private static final ConvertJson CONVERT_JSON = new ConvertJsonImpl(); + + private RequestMethod method; + private String uri; + private Optional headers; + private Optional> query; + private Optional body; + + private Request() { + this.headers = Optional.empty(); + this.query = Optional.empty(); + this.body = Optional.empty(); + } + + public Request(RequestMethod method, String uri) { + this(); + this.method = method; + this.uri = uri; + } + + public RequestMethod getMethod() { + return method; + } + + public String getUri() { + return uri; + } + + public Optional getHeaders() { + return headers; + } + + public Optional> getQuery() { + return query; + } + + public Optional getBody() { + return body; + } + + public Integer countQueryFields() { + return getQuery() + .map(Map::size) + .orElse(0); + } + + public Integer countBodyFields() { + return countFields(getBody()); + } + + private Integer countFields(Optional attr) { + return attr + .map(queryStr -> { + try { + return CONVERT_JSON.apply(queryStr).size(); + } catch (JsonApiApplicationException e) { + return 0; + } + }) + .orElse(0); + } + + public Boolean isValid() { + return + (headers.filter(h -> h.size() > 0).map(h -> true).orElse(false)) + || countBodyFields() > 0 + || countQueryFields() > 0; + } + + @Override + public int compareTo(Request o) { + final Integer resultMethod = this.method.compareTo(o.method); + if (resultMethod == 0) { + final Integer resultUrl = this.uri.compareTo(o.uri); + if (resultUrl == 0) { + final Integer resultQueries = o.countQueryFields().compareTo(this.countQueryFields()); + if (resultQueries == 0) { + return o.countBodyFields().compareTo(this.countBodyFields()); + } else { + return resultQueries; + } + } else { + return resultUrl; + } + } else { + return resultMethod; + } + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Request request = (Request) o; + return method == request.method && + Objects.equals(uri, request.uri) && + Objects.equals(headers, request.headers) && + Objects.equals(query, request.query) && + Objects.equals(body, request.body); + } + + @Override + public int hashCode() { + return Objects.hash(method, uri, headers, query, body); + } + + public static class Builder { + + private final Request instance; + + public Builder(RequestMethod method, String uri) { + this.instance = new Request(method, uri); + } + + public Builder(Request request) { + this(request.getMethod(), request.getUri()); + withHeader(request.getHeaders()); + withQuery(request.getQuery()); + withBody(request.getBody()); + } + + public Builder withHeader(HttpHeaders headers) { + return withHeader(Optional.ofNullable(headers)); + } + + public Builder withHeader(Optional headers) { + instance.headers = headers; + return this; + } + + public Builder withQuery(Map query) { + return withQuery(Optional.ofNullable(query)); + } + + public Builder withQuery(Optional> query) { + instance.query = query; + return this; + } + + public Builder withBody(Optional body) { + instance.body = body; + return this; + } + + public Request build() { + return instance; + } + + } + +} diff --git a/src/main/java/br/com/concrete/mock/generic/model/Response.java b/src/main/java/br/com/concrete/mock/generic/model/Response.java new file mode 100755 index 0000000..dc9acf6 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/generic/model/Response.java @@ -0,0 +1,25 @@ +package br.com.concrete.mock.generic.model; + +import org.springframework.http.HttpStatus; + +import java.util.Optional; + +public class Response { + + private final String body; + private final Optional httpStatus; + + public Response(String body, Optional httpStatus) { + this.body = body; + this.httpStatus = httpStatus; + } + + public String getBody() { + return body; + } + + public Optional getHttpStatus() { + return httpStatus; + } + +} diff --git a/src/main/java/br/com/concrete/mock/generic/repository/EndpointRepository.java b/src/main/java/br/com/concrete/mock/generic/repository/EndpointRepository.java new file mode 100755 index 0000000..cf510a0 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/generic/repository/EndpointRepository.java @@ -0,0 +1,16 @@ +package br.com.concrete.mock.generic.repository; + +import br.com.concrete.mock.generic.model.Endpoint; +import br.com.concrete.mock.generic.model.Request; +import org.springframework.web.bind.annotation.RequestMethod; + +import java.util.Collection; +import java.util.Optional; + +public interface EndpointRepository { + + Collection getByMethodAndUri(RequestMethod requestMethod, String pathUri); + + Optional getByMethodAndRequest(Request request); + +} diff --git a/src/main/java/br/com/concrete/mock/generic/repository/impl/EndpointFileFilter.java b/src/main/java/br/com/concrete/mock/generic/repository/impl/EndpointFileFilter.java new file mode 100755 index 0000000..d29282b --- /dev/null +++ b/src/main/java/br/com/concrete/mock/generic/repository/impl/EndpointFileFilter.java @@ -0,0 +1,10 @@ +package br.com.concrete.mock.generic.repository.impl; + +import br.com.concrete.mock.generic.model.Endpoint; + +@FunctionalInterface +interface EndpointFileFilter { + + Boolean apply(Endpoint endpoint, T request); + +} diff --git a/src/main/java/br/com/concrete/mock/generic/repository/impl/EndpointFileFilterBody.java b/src/main/java/br/com/concrete/mock/generic/repository/impl/EndpointFileFilterBody.java new file mode 100755 index 0000000..c1944c0 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/generic/repository/impl/EndpointFileFilterBody.java @@ -0,0 +1,34 @@ +package br.com.concrete.mock.generic.repository.impl; + +import br.com.concrete.mock.infra.component.CompareJson; +import br.com.concrete.mock.generic.model.Endpoint; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Optional; + +@Component +class EndpointFileFilterBody implements EndpointFileFilter> { + + private final CompareJson compareJson; + + @Autowired + public EndpointFileFilterBody(CompareJson compareJson) { + this.compareJson = compareJson; + } + + @Override + public Boolean apply(Endpoint endpoint, Optional request) { + return request + .map(requestBody -> + endpoint + .getRequest() + .getBody() + .map(expetedBody -> compareJson.isEquivalent(expetedBody, requestBody)) + .orElse(true) + ) + .orElse(!endpoint.getRequest().getBody().isPresent()); + } + + +} diff --git a/src/main/java/br/com/concrete/mock/generic/repository/impl/EndpointFileFilterQuery.java b/src/main/java/br/com/concrete/mock/generic/repository/impl/EndpointFileFilterQuery.java new file mode 100755 index 0000000..acb3d05 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/generic/repository/impl/EndpointFileFilterQuery.java @@ -0,0 +1,34 @@ +package br.com.concrete.mock.generic.repository.impl; + +import br.com.concrete.mock.infra.component.CompareMap; +import br.com.concrete.mock.generic.model.Endpoint; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Map; +import java.util.Optional; + +@Component +class EndpointFileFilterQuery implements EndpointFileFilter>> { + + private final CompareMap compareMap; + + @Autowired + public EndpointFileFilterQuery(CompareMap compareMap) { + this.compareMap = compareMap; + } + + @Override + public Boolean apply(Endpoint endpoint, Optional> request) { + return request + .map(requestQuery -> + endpoint + .getRequest() + .getQuery() + .map(expetedQuery -> compareMap.isEquivalent(expetedQuery, requestQuery)) + .orElse(true) + ) + .orElse(endpoint.getRequest() != null && (!endpoint.getRequest().getQuery().isPresent())); + } + +} diff --git a/src/main/java/br/com/concrete/mock/generic/repository/impl/EndpointFileFilterRequest.java b/src/main/java/br/com/concrete/mock/generic/repository/impl/EndpointFileFilterRequest.java new file mode 100755 index 0000000..2f7684d --- /dev/null +++ b/src/main/java/br/com/concrete/mock/generic/repository/impl/EndpointFileFilterRequest.java @@ -0,0 +1,26 @@ +package br.com.concrete.mock.generic.repository.impl; + +import br.com.concrete.mock.generic.model.Endpoint; +import br.com.concrete.mock.generic.model.Request; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +@Component +class EndpointFileFilterRequest implements EndpointFileFilter { + + private final EndpointFileFilterQuery endpointFileFilterQuery; + private final EndpointFileFilterBody endpointMockFileFilterBody; + + @Autowired + public EndpointFileFilterRequest(EndpointFileFilterQuery endpointFileFilterQuery, EndpointFileFilterBody endpointMockFileFilterBody) { + this.endpointFileFilterQuery = endpointFileFilterQuery; + this.endpointMockFileFilterBody = endpointMockFileFilterBody; + } + + @Override + public Boolean apply(Endpoint endpoint, Request request) { + return endpointFileFilterQuery.apply(endpoint, request.getQuery()) && + endpointMockFileFilterBody.apply(endpoint, request.getBody()); + } + +} diff --git a/src/main/java/br/com/concrete/mock/generic/repository/impl/EndpointRepositoryBackup.java b/src/main/java/br/com/concrete/mock/generic/repository/impl/EndpointRepositoryBackup.java new file mode 100755 index 0000000..8fc0483 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/generic/repository/impl/EndpointRepositoryBackup.java @@ -0,0 +1,24 @@ +package br.com.concrete.mock.generic.repository.impl; + +import br.com.concrete.mock.generic.mapper.EndpointMapper; +import br.com.concrete.mock.generic.repository.EndpointRepository; +import br.com.concrete.mock.infra.component.file.BaseFileNameBuilder; +import br.com.concrete.mock.infra.property.FileExtensionProperty; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Repository; + +@Repository("EndpointRepositoryBackup") +public class EndpointRepositoryBackup extends EndpointRepositoryBase implements EndpointRepository { + + @Autowired + public EndpointRepositoryBackup(FileExtensionProperty fileExtensionProperty, EndpointMapper endpointMapper, @Qualifier("BaseFileNameBuilderBackup") BaseFileNameBuilder baseFileNameBuilder, EndpointFileFilterRequest endpointFileFilterRequest) { + super( + fileExtensionProperty, + endpointMapper, + baseFileNameBuilder, + endpointFileFilterRequest + ); + } + +} diff --git a/src/main/java/br/com/concrete/mock/generic/repository/impl/EndpointRepositoryBase.java b/src/main/java/br/com/concrete/mock/generic/repository/impl/EndpointRepositoryBase.java new file mode 100755 index 0000000..4ffc115 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/generic/repository/impl/EndpointRepositoryBase.java @@ -0,0 +1,85 @@ +package br.com.concrete.mock.generic.repository.impl; + +import br.com.concrete.mock.generic.mapper.EndpointMapper; +import br.com.concrete.mock.generic.model.Endpoint; +import br.com.concrete.mock.generic.model.Request; +import br.com.concrete.mock.generic.repository.EndpointRepository; +import br.com.concrete.mock.infra.component.file.BaseFileNameBuilder; +import br.com.concrete.mock.infra.property.FileExtensionProperty; +import com.google.common.collect.ImmutableList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.RequestMethod; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class EndpointRepositoryBase implements EndpointRepository { + + private static final Logger LOGGER = LoggerFactory.getLogger(EndpointRepositoryBase.class); + + private final FileExtensionProperty fileExtensionProperty; + private final EndpointMapper endpointMapper; + private final BaseFileNameBuilder baseFileNameBuilder; + private final EndpointFileFilterRequest endpointFileFilterRequest; + + public EndpointRepositoryBase(FileExtensionProperty fileExtensionProperty, EndpointMapper endpointMapper, BaseFileNameBuilder baseFileNameBuilder, EndpointFileFilterRequest endpointFileFilterRequest) { + this.fileExtensionProperty = fileExtensionProperty; + this.endpointMapper = endpointMapper; + this.baseFileNameBuilder = baseFileNameBuilder; + this.endpointFileFilterRequest = endpointFileFilterRequest; + } + + @Override + public Collection getByMethodAndUri(RequestMethod requestMethod, String requestUrl) { + return ImmutableList.copyOf(getByMethodAndUriMutable(requestMethod, requestUrl)); + } + + @Override + public Optional getByMethodAndRequest(Request request) { + return getByMethodAndUri(request.getMethod(), request.getUri()) + .stream() + .sorted() + .filter(endpointMock -> endpointFileFilterRequest.apply(endpointMock, request)) + .findFirst(); + } + + private List getByMethodAndUriMutable(RequestMethod requestMethod, String requestUrl) { + final String basePath = baseFileNameBuilder.buildPath(requestMethod, requestUrl); + + final Path start = Paths.get(basePath); + if (!Files.exists(start)) { + return new ArrayList<>(); + } + + final Integer maxDepth = 1; + + try (Stream stream = Files.walk(start, maxDepth)) { + return stream + .map(String::valueOf) + .filter(path -> path.endsWith(fileExtensionProperty.getFileExtension())) + .map(fileName -> endpointMapper.mapper(requestMethod, requestUrl, fileName)) + .filter(o -> o != null) + .filter(Optional::isPresent) + .filter(a -> a.get() != null) + .map(Optional::get) + .collect(Collectors.toList()); + } catch (IOException e) { + final String message = new StringBuilder() + .append("Cannot read file from repository: ") + .append(basePath) + .toString(); + LOGGER.error(message, e); + return new ArrayList<>(); + } + } + +} diff --git a/src/main/java/br/com/concrete/mock/generic/repository/impl/EndpointRepositoryModel.java b/src/main/java/br/com/concrete/mock/generic/repository/impl/EndpointRepositoryModel.java new file mode 100755 index 0000000..24216de --- /dev/null +++ b/src/main/java/br/com/concrete/mock/generic/repository/impl/EndpointRepositoryModel.java @@ -0,0 +1,23 @@ +package br.com.concrete.mock.generic.repository.impl; + +import br.com.concrete.mock.generic.mapper.EndpointMapper; +import br.com.concrete.mock.generic.repository.EndpointRepository; +import br.com.concrete.mock.infra.component.file.BaseFileNameBuilder; +import br.com.concrete.mock.infra.property.FileExtensionProperty; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Repository; + +@Repository("EndpointRepositoryModel") +public class EndpointRepositoryModel extends EndpointRepositoryBase implements EndpointRepository { + + @Autowired + public EndpointRepositoryModel(FileExtensionProperty fileExtensionProperty, EndpointMapper endpointMapper, @Qualifier("BaseFileNameBuilderModel") BaseFileNameBuilder baseFileNameBuilder, EndpointFileFilterRequest endpointFileFilterRequest) { + super( + fileExtensionProperty, endpointMapper, + baseFileNameBuilder, + endpointFileFilterRequest + ); + } + +} diff --git a/src/main/java/br/com/concrete/mock/generic/service/EndpointBackupService.java b/src/main/java/br/com/concrete/mock/generic/service/EndpointBackupService.java new file mode 100755 index 0000000..472d51e --- /dev/null +++ b/src/main/java/br/com/concrete/mock/generic/service/EndpointBackupService.java @@ -0,0 +1,11 @@ +package br.com.concrete.mock.generic.service; + +import br.com.concrete.mock.generic.model.Endpoint; + +public interface EndpointBackupService { + + void doBackup(Endpoint endpoint); + + void cleanAllBackupData(); // dangerous + +} diff --git a/src/main/java/br/com/concrete/mock/generic/service/GenericApiService.java b/src/main/java/br/com/concrete/mock/generic/service/GenericApiService.java new file mode 100755 index 0000000..fa712d2 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/generic/service/GenericApiService.java @@ -0,0 +1,20 @@ +package br.com.concrete.mock.generic.service; + +import java.util.Map; +import java.util.Optional; + +import javax.servlet.http.HttpServletRequest; + +import org.springframework.http.ResponseEntity; + +import br.com.concrete.mock.generic.model.Request; + +public interface GenericApiService { + + Optional> genericResponseEntity(Request request); + + Map getHeaders(final HttpServletRequest request); + + Optional> genericResponseEntityGET(Request request, HttpServletRequest httpServletRequest); + +} diff --git a/src/main/java/br/com/concrete/mock/generic/service/impl/EndpointBackupServiceFile.java b/src/main/java/br/com/concrete/mock/generic/service/impl/EndpointBackupServiceFile.java new file mode 100755 index 0000000..0d17f55 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/generic/service/impl/EndpointBackupServiceFile.java @@ -0,0 +1,95 @@ +package br.com.concrete.mock.generic.service.impl; + +import br.com.concrete.mock.generic.mapper.EndpointDto; +import br.com.concrete.mock.generic.model.Endpoint; +import br.com.concrete.mock.generic.model.Request; +import br.com.concrete.mock.generic.repository.EndpointRepository; +import br.com.concrete.mock.generic.service.EndpointBackupService; +import br.com.concrete.mock.infra.component.FromJsonStringToObjectConverter; +import br.com.concrete.mock.infra.component.file.BaseFileNameBuilder; +import br.com.concrete.mock.infra.component.file.FileNameGenerator; +import br.com.concrete.mock.infra.component.impl.JsonFormatterPretty; +import br.com.concrete.mock.infra.property.FileExtensionProperty; +import br.com.concrete.mock.infra.property.FileProperty; +import com.google.gson.Gson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Service; +import org.springframework.util.FileSystemUtils; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +@Service +public class EndpointBackupServiceFile implements EndpointBackupService { + + private static final Logger LOGGER = LoggerFactory.getLogger(EndpointBackupServiceFile.class); + + private final FileProperty fileProperty; + private final FileExtensionProperty fileExtensionProperty; + private final BaseFileNameBuilder baseFileNameBuilder; + private final FileNameGenerator fileNameGenerator; + private final FromJsonStringToObjectConverter fromJsonStringToObjectConverter; + private final JsonFormatterPretty jsonFormatterPretty; + private final EndpointRepository endpointRepository; + + @Autowired + public EndpointBackupServiceFile(@Qualifier("FilePropertyBackup") FileProperty fileProperty, FileExtensionProperty fileExtensionProperty, @Qualifier("BaseFileNameBuilderBackup") BaseFileNameBuilder baseFileNameBuilder, FileNameGenerator fileNameGenerator, FromJsonStringToObjectConverter fromJsonStringToObjectConverter, JsonFormatterPretty jsonFormatterPretty, @Qualifier("EndpointRepositoryBackup") EndpointRepository endpointRepository) { + this.fileProperty = fileProperty; + this.fileExtensionProperty = fileExtensionProperty; + this.baseFileNameBuilder = baseFileNameBuilder; + this.fileNameGenerator = fileNameGenerator; + this.fromJsonStringToObjectConverter = fromJsonStringToObjectConverter; + this.jsonFormatterPretty = jsonFormatterPretty; + this.endpointRepository = endpointRepository; + } + + public void doBackup(Endpoint endpoint) { + final Boolean isNeedToCreateABackup = endpointRepository + .getByMethodAndRequest(endpoint.getRequest()) + .map(e -> { + LOGGER.info("Existent backup not replaced [id=" + e.getId().orElse("no_id") + "]"); + return false; + }) + .orElse(true); + + if (isNeedToCreateABackup) execute(endpoint); + } + + private Boolean execute(final Endpoint endpoint) { + final Request request = endpoint.getRequest(); + + final String pathName = baseFileNameBuilder.buildPath(fileProperty.getFileBase(), request.getMethod().name().toLowerCase(), request.getUri()); + final String fileName = pathName + "/" + fileNameGenerator.fromPath(pathName).concat(fileExtensionProperty.getFileExtension()); + + final EndpointDto endpointDto = new EndpointDto(endpoint, fromJsonStringToObjectConverter); + final String endpointJson = jsonFormatterPretty.format(new Gson().toJson(endpointDto)); + + try { + Files.createDirectories(Paths.get(pathName)); + Files.write(Paths.get(fileName), endpointJson.getBytes()); + + LOGGER.info("Backup into " + fileName); + } catch (IOException e) { + LOGGER.error("Cannot backup endpoint {}", e); + } + return true; + } + + public void cleanAllBackupData() { + try { + final String backupPath = fileProperty.getFileBase(); + Files + .list(Paths.get(backupPath)) + .map(path -> path.getFileName().toFile()) + .filter(file -> !file.getName().startsWith(".")) + .forEach(FileSystemUtils::deleteRecursively); + } catch (IOException e) { + LOGGER.error("Cannot list backup files {}", e); + } + } + +} diff --git a/src/main/java/br/com/concrete/mock/generic/service/impl/GenericApiServiceImpl.java b/src/main/java/br/com/concrete/mock/generic/service/impl/GenericApiServiceImpl.java new file mode 100755 index 0000000..57f2126 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/generic/service/impl/GenericApiServiceImpl.java @@ -0,0 +1,122 @@ +package br.com.concrete.mock.generic.service.impl; + +import br.com.concrete.mock.configuration.service.CaptureExecutor; +import br.com.concrete.mock.generic.model.Endpoint; +import br.com.concrete.mock.generic.model.Request; +import br.com.concrete.mock.generic.repository.EndpointRepository; +import br.com.concrete.mock.generic.service.GenericApiService; +import br.com.concrete.mock.infra.component.ExternalApi; +import br.com.concrete.mock.infra.component.JsonValueCompiler; +import br.com.concrete.mock.infra.property.ApiProperty; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; + +import javax.servlet.http.HttpServletRequest; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; + +@Service +public class GenericApiServiceImpl implements GenericApiService { + + private final EndpointRepository endpointRepository; + private final ExternalApi externalApi; + private final CaptureExecutor captureExecutor; + private final JsonValueCompiler jsonValueCompiler; + private final ApiProperty apiProperty; + + @Autowired + public GenericApiServiceImpl(@Qualifier("EndpointRepositoryModel") EndpointRepository endpointRepository, + ExternalApi externalApi, CaptureExecutor captureExecutor, JsonValueCompiler jsonValueCompiler, ApiProperty apiProperty) { + this.endpointRepository = endpointRepository; + this.externalApi = externalApi; + this.captureExecutor = captureExecutor; + this.jsonValueCompiler = jsonValueCompiler; + this.apiProperty = apiProperty; + } + + private Optional getEndpoint(Request request) { + return endpointRepository.getByMethodAndRequest(request); + } + + // TODO refatorar esse método + @Override + public Optional> genericResponseEntity(Request request) { + final Endpoint endpointNotSafe = getEndpoint(request).orElse(null); + + final Optional> apiResult; + + // TODO refatorar: esta no if porque estava dando problema ao colocar + // dentro do orElse() + if (endpointNotSafe == null) { + apiResult = externalApi.execute(request).map(r -> { + captureExecutor.execute(r, new Endpoint.Builder(request, r.getApiResult()).build()); + return r.getApiResult(); + }); + } else { + final String body = jsonValueCompiler.compile(endpointNotSafe.getResponse().getBody()); + apiResult = Optional.of( + new ResponseEntity<>(body, endpointNotSafe.getResponse().getHttpStatus().orElse(HttpStatus.OK))); + } + + return apiResult.map(responseEntity -> { + final ResponseEntity.BodyBuilder bodyBuilder = ResponseEntity.status(responseEntity.getStatusCode()); + + apiProperty.getDefaultHeaders().forEach(header -> { + final String headerName = header.getHeaderName(); + final String[] headerValues = header.getHeaderValues().stream().toArray(String[]::new); + bodyBuilder.header(headerName, headerValues); + }); + + return bodyBuilder.body(responseEntity.getBody()); + }); + } + + @Override + public Optional> genericResponseEntityGET(Request request, HttpServletRequest httpServletRequest) { + final Endpoint endpointNotSafe = getEndpoint(request).orElse(null); + + final Optional> apiResult; + + // TODO refatorar: esta no if porque estava dando problema ao colocar + // dentro do orElse() + if (endpointNotSafe == null) { + apiResult = externalApi.okHttpClientRequest(httpServletRequest, request).map(r -> { + captureExecutor.execute(r, new Endpoint.Builder(request, r.getApiResult()).build()); + return r.getApiResult(); + }); + } else { + final String body = jsonValueCompiler.compile(endpointNotSafe.getResponse().getBody()); + apiResult = Optional.of( + new ResponseEntity<>(body, endpointNotSafe.getResponse().getHttpStatus().orElse(HttpStatus.OK))); + } + + return apiResult.map(responseEntity -> { + final ResponseEntity.BodyBuilder bodyBuilder = ResponseEntity.status(responseEntity.getStatusCode()); + + apiProperty.getDefaultHeaders().forEach(header -> { + final String headerName = header.getHeaderName(); + final String[] headerValues = header.getHeaderValues().stream().toArray(String[]::new); + bodyBuilder.header(headerName, headerValues); + }); + + return bodyBuilder.body(responseEntity.getBody()); + }); + } + + public Map getHeaders(final HttpServletRequest request) { + Map map = new HashMap<>(); + Enumeration headerNames = request.getHeaderNames(); + while (headerNames.hasMoreElements()) { + String key = headerNames.nextElement(); + String value = request.getHeader(key); + map.put(key, value); + } + return map; + } + +} diff --git a/src/main/java/br/com/concrete/mock/infra/component/CompareJson.java b/src/main/java/br/com/concrete/mock/infra/component/CompareJson.java new file mode 100755 index 0000000..044b899 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/component/CompareJson.java @@ -0,0 +1,40 @@ +package br.com.concrete.mock.infra.component; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +@Component +public class CompareJson { + + private final ConvertJson convertJson; + + @Autowired + public CompareJson(ConvertJson convertJson) { + this.convertJson = convertJson; + } + + public Boolean isEquivalent(String jsonKey, String jsonToCompare) { + final HashMap keys = convertJson.apply(jsonKey); + final HashMap toCompare = convertJson.apply(jsonToCompare); + + for (Object entrySet : keys.entrySet()) { + Map.Entry entry = (Map.Entry) entrySet; + final Object valueCompare = toCompare.get(entry.getKey()); + + if(valueCompare instanceof LinkedHashMap && entry.getValue() instanceof LinkedHashMap){ + return ((LinkedHashMap)valueCompare).size() == ((LinkedHashMap) entry.getValue()).size(); + } + + if (!entry.getValue().equals(valueCompare)) { + return false; + } + } + + return true; + } + +} diff --git a/src/main/java/br/com/concrete/mock/infra/component/CompareMap.java b/src/main/java/br/com/concrete/mock/infra/component/CompareMap.java new file mode 100755 index 0000000..e64de1b --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/component/CompareMap.java @@ -0,0 +1,26 @@ +package br.com.concrete.mock.infra.component; + +import org.springframework.stereotype.Component; + +import java.util.Map; + +@Component +public class CompareMap { + + public Boolean isEquivalent(Map value, Map valueToCompare) { + for (Map.Entry entrySet : value.entrySet()) { + final K key = entrySet.getKey(); + if (!valueToCompare.containsKey(key)) { + return false; + } + + final V valueCompare = valueToCompare.get(key); + if (!entrySet.getValue().equals(valueCompare)) { + return false; + } + } + + return true; + } + +} diff --git a/src/main/java/br/com/concrete/mock/infra/component/ConvertJson.java b/src/main/java/br/com/concrete/mock/infra/component/ConvertJson.java new file mode 100755 index 0000000..a1e4484 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/component/ConvertJson.java @@ -0,0 +1,10 @@ +package br.com.concrete.mock.infra.component; + +import java.util.HashMap; + +@FunctionalInterface +public interface ConvertJson { + + HashMap apply(String jsonKey); + +} diff --git a/src/main/java/br/com/concrete/mock/infra/component/ExternalApi.java b/src/main/java/br/com/concrete/mock/infra/component/ExternalApi.java new file mode 100755 index 0000000..881bde1 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/component/ExternalApi.java @@ -0,0 +1,131 @@ +package br.com.concrete.mock.infra.component; + +import br.com.concrete.mock.configuration.model.CaptureState; +import br.com.concrete.mock.configuration.repository.CaptureStateRepository; +import br.com.concrete.mock.generic.model.ExternalApiResult; +import br.com.concrete.mock.generic.model.Request; +import br.com.concrete.mock.infra.model.UriConfiguration; +import br.com.concrete.mock.infra.property.ApiProperty; +import okhttp3.Headers; +import okhttp3.OkHttpClient; +import okhttp3.Response; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.*; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestTemplate; + +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.util.Enumeration; +import java.util.Optional; +import java.util.regex.Pattern; + +@Component +public class ExternalApi { + + private static final Logger LOGGER = LoggerFactory.getLogger(ExternalApi.class); + + private final ApiProperty apiProperty; + private final QueryStringBuilder queryStringBuilder; + private final RestTemplate restTemplate; + private final HeaderFilter headerFilter; + private final CaptureStateRepository captureStateRepository; + private final OkHttpClient okHttpClient; + + @Autowired + public ExternalApi(ApiProperty apiProperty, QueryStringBuilder queryStringBuilder, RestTemplate restTemplate, + HeaderFilter headerFilter, CaptureStateRepository captureStateRepository, OkHttpClient okHttpClient) { + this.apiProperty = apiProperty; + this.queryStringBuilder = queryStringBuilder; + this.restTemplate = restTemplate; + this.headerFilter = headerFilter; + this.captureStateRepository = captureStateRepository; + this.okHttpClient = okHttpClient; + } + + public Optional execute(final Request request) { + final Boolean state = captureStateRepository + .getCurrent() + .map(CaptureState::isEnabled) + .orElse(true); + + + final UriConfiguration uriConfiguration = apiProperty + .getConfiguration(request.getUri()) + .orElse(new UriConfiguration(apiProperty.getHost(), Pattern.compile(".*"), state)); + final Optional httpHeaders = headerFilter.execute(request.getHeaders()); + + LOGGER.info("### EXTERNAL API ###"); + LOGGER.info("{}", uriConfiguration); + request.getBody().ifPresent(LOGGER::info); + httpHeaders.ifPresent(list -> LOGGER.info(list.toString())); + + String magicalHeaders = apiProperty.getMagicalHeaders(); + String[] split = magicalHeaders.split(","); + HttpHeaders httpHeadersFiltrado = new HttpHeaders(); + + for (String s : split) { + if (httpHeaders.get().containsKey(s)) { + httpHeadersFiltrado.add(s, String.valueOf(httpHeaders.get().get(s))); + } + } + + Optional optionalHttpHeaders = Optional.ofNullable(httpHeadersFiltrado); + + final HttpEntity entity = optionalHttpHeaders + .map(headers -> request.getBody().map(body -> new HttpEntity<>(body, headers)) + .orElse(new HttpEntity<>(headers))) + .orElse(request.getBody().map(HttpEntity::new).orElse(new HttpEntity<>((String) null))); + + final String parameters = request.getQuery().map(queryStringBuilder::fromMap).orElse(""); + final String url = uriConfiguration + .getHost() + .concat(request.getUri()) + .concat(parameters); + + LOGGER.info("URL => {}", url); + + final ResponseEntity apiResult = restTemplate.exchange(url, HttpMethod.valueOf(request.getMethod().name().toUpperCase()), entity, + String.class); + return Optional.of(new ExternalApiResult(apiResult, uriConfiguration)); + } + + public Optional okHttpClientRequest(HttpServletRequest request, Request req){ + + final Boolean state = captureStateRepository + .getCurrent() + .map(CaptureState::isEnabled) + .orElse(true); + + Enumeration headerNames = request.getHeaderNames(); + Headers.Builder builder = new Headers.Builder(); + while (headerNames.hasMoreElements()) { + final String name = headerNames.nextElement(); + builder.add(name, request.getHeader(name)); + } + Headers h = builder.build(); + + final UriConfiguration uriConfiguration = apiProperty + .getConfiguration(req.getUri()) + .orElse(new UriConfiguration(apiProperty.getHost(), Pattern.compile(".*"), state)); + + okhttp3.Request okHttpRequest = new okhttp3.Request.Builder() + .url(uriConfiguration.getHost()+req.getUri()+"?"+request.getQueryString()) + .get() + .headers(h) + .build(); + + ResponseEntity apiResult = null; + try { + Response response = okHttpClient.newCall(okHttpRequest).execute(); + apiResult = new ResponseEntity<>(response.body().string(), new HttpHeaders(), HttpStatus.OK); + } catch (IOException e) { + e.printStackTrace(); + } + + return Optional.of(new ExternalApiResult(apiResult, uriConfiguration)); + } + +} diff --git a/src/main/java/br/com/concrete/mock/infra/component/FromJsonStringToObjectConverter.java b/src/main/java/br/com/concrete/mock/infra/component/FromJsonStringToObjectConverter.java new file mode 100755 index 0000000..a2be2fc --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/component/FromJsonStringToObjectConverter.java @@ -0,0 +1,12 @@ +package br.com.concrete.mock.infra.component; + +import com.google.gson.JsonElement; + +import java.util.Optional; + +@FunctionalInterface +public interface FromJsonStringToObjectConverter { + + JsonElement apply(final Optional json); + +} diff --git a/src/main/java/br/com/concrete/mock/infra/component/HeaderFilter.java b/src/main/java/br/com/concrete/mock/infra/component/HeaderFilter.java new file mode 100755 index 0000000..baeb783 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/component/HeaderFilter.java @@ -0,0 +1,44 @@ +package br.com.concrete.mock.infra.component; + +import br.com.concrete.mock.infra.property.ApiProperty; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpHeaders; +import org.springframework.stereotype.Component; + +import java.util.Objects; +import java.util.Optional; + +@Component +public class HeaderFilter { + private final ApiProperty apiProperty; + + @Autowired + public HeaderFilter(ApiProperty apiProperty) { + this.apiProperty = apiProperty; + } + + public Optional execute(Optional headers) { + return headers + .map(h -> { + final HttpHeaders httpHeaders = new HttpHeaders(); + + h + .entrySet() + .stream() + .filter(value -> apiProperty.isAcceptedHeader(value.getKey())) + .forEach(entry -> { + Optional + .ofNullable(entry.getValue()) + .filter(Objects::nonNull) + .filter(value -> !value.isEmpty()) + .map(value -> value.stream().findFirst().get()) + .ifPresent(value -> httpHeaders.add(entry.getKey(), value)); + }); + + return httpHeaders; + } + ); + } + + +} diff --git a/src/main/java/br/com/concrete/mock/infra/component/JsonFormatter.java b/src/main/java/br/com/concrete/mock/infra/component/JsonFormatter.java new file mode 100755 index 0000000..252e830 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/component/JsonFormatter.java @@ -0,0 +1,7 @@ +package br.com.concrete.mock.infra.component; + +public interface JsonFormatter { + + String format(final String jsonString); + +} diff --git a/src/main/java/br/com/concrete/mock/infra/component/JsonValueCompiler.java b/src/main/java/br/com/concrete/mock/infra/component/JsonValueCompiler.java new file mode 100755 index 0000000..887e53d --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/component/JsonValueCompiler.java @@ -0,0 +1,8 @@ +package br.com.concrete.mock.infra.component; + +@FunctionalInterface +public interface JsonValueCompiler { + + String compile(final String json); + +} diff --git a/src/main/java/br/com/concrete/mock/infra/component/QueryStringBuilder.java b/src/main/java/br/com/concrete/mock/infra/component/QueryStringBuilder.java new file mode 100755 index 0000000..be9a651 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/component/QueryStringBuilder.java @@ -0,0 +1,7 @@ +package br.com.concrete.mock.infra.component; + +import java.util.Map; + +public interface QueryStringBuilder { + String fromMap(Map queryMap); +} diff --git a/src/main/java/br/com/concrete/mock/infra/component/RequestFormatter.java b/src/main/java/br/com/concrete/mock/infra/component/RequestFormatter.java new file mode 100755 index 0000000..431302d --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/component/RequestFormatter.java @@ -0,0 +1,10 @@ +package br.com.concrete.mock.infra.component; + +import javax.servlet.http.HttpServletRequest; +import java.util.Optional; + +public interface RequestFormatter { + + String generateLog(final HttpServletRequest request, final Optional requestBody); + +} diff --git a/src/main/java/br/com/concrete/mock/infra/component/file/BaseFileNameBuilder.java b/src/main/java/br/com/concrete/mock/infra/component/file/BaseFileNameBuilder.java new file mode 100755 index 0000000..ab5952f --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/component/file/BaseFileNameBuilder.java @@ -0,0 +1,11 @@ +package br.com.concrete.mock.infra.component.file; + +import org.springframework.web.bind.annotation.RequestMethod; + +public interface BaseFileNameBuilder { + + String buildPath(RequestMethod requestMethod, String pathUri); + + String buildPath(String fileBaseName, String methodName, String pathUri); + +} diff --git a/src/main/java/br/com/concrete/mock/infra/component/file/BaseFileNameBuilderBackup.java b/src/main/java/br/com/concrete/mock/infra/component/file/BaseFileNameBuilderBackup.java new file mode 100755 index 0000000..ae5d127 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/component/file/BaseFileNameBuilderBackup.java @@ -0,0 +1,16 @@ +package br.com.concrete.mock.infra.component.file; + +import br.com.concrete.mock.infra.property.FileProperty; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +@Component("BaseFileNameBuilderBackup") +public class BaseFileNameBuilderBackup extends BaseFileNameBuilderBase implements BaseFileNameBuilder { + + @Autowired + public BaseFileNameBuilderBackup(@Qualifier("FilePropertyBackup") FileProperty fileProperty) { + super(fileProperty); + } + +} diff --git a/src/main/java/br/com/concrete/mock/infra/component/file/BaseFileNameBuilderBase.java b/src/main/java/br/com/concrete/mock/infra/component/file/BaseFileNameBuilderBase.java new file mode 100755 index 0000000..9a67526 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/component/file/BaseFileNameBuilderBase.java @@ -0,0 +1,27 @@ +package br.com.concrete.mock.infra.component.file; + +import br.com.concrete.mock.infra.property.FileProperty; +import org.springframework.web.bind.annotation.RequestMethod; + +public class BaseFileNameBuilderBase implements BaseFileNameBuilder { + + private final FileProperty fileProperty; + + public BaseFileNameBuilderBase(FileProperty fileProperty) { + this.fileProperty = fileProperty; + } + + public String buildPath(RequestMethod requestMethod, String pathUri) { + return buildPath(fileProperty.getFileBase(), requestMethod.name(), pathUri); + } + + public String buildPath(String fileBaseName, String methodName, String pathUri) { + return new StringBuilder() + .append(fileBaseName) + .append("/") + .append(methodName.toLowerCase()) + .append(pathUri) + .toString(); + } + +} diff --git a/src/main/java/br/com/concrete/mock/infra/component/file/BaseFileNameBuilderModel.java b/src/main/java/br/com/concrete/mock/infra/component/file/BaseFileNameBuilderModel.java new file mode 100755 index 0000000..eb6114e --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/component/file/BaseFileNameBuilderModel.java @@ -0,0 +1,16 @@ +package br.com.concrete.mock.infra.component.file; + +import br.com.concrete.mock.infra.property.FileProperty; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.stereotype.Component; + +@Component("BaseFileNameBuilderModel") +public class BaseFileNameBuilderModel extends BaseFileNameBuilderBase implements BaseFileNameBuilder { + + @Autowired + public BaseFileNameBuilderModel(@Qualifier("FilePropertyModel") FileProperty fileProperty) { + super(fileProperty); + } + +} diff --git a/src/main/java/br/com/concrete/mock/infra/component/file/FileJsonReader.java b/src/main/java/br/com/concrete/mock/infra/component/file/FileJsonReader.java new file mode 100755 index 0000000..edf7c78 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/component/file/FileJsonReader.java @@ -0,0 +1,32 @@ +package br.com.concrete.mock.infra.component.file; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Optional; + +@Component +public class FileJsonReader { + + private static final Logger LOGGER = LoggerFactory.getLogger(FileJsonReader.class); + + public Optional getJsonByFileName(String fileName) throws IOException { + return Optional + .of(Paths.get(fileName)) + .filter(path -> Files.exists(path)) + .map(path -> { + try { + return new String(Files.readAllBytes(path)); + } catch (IOException e) { + LOGGER.error("Cannot to read json from file", e); + return null; + } + }) + .filter(json -> json != null); + } + +} diff --git a/src/main/java/br/com/concrete/mock/infra/component/file/FileNameGenerator.java b/src/main/java/br/com/concrete/mock/infra/component/file/FileNameGenerator.java new file mode 100755 index 0000000..80c511c --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/component/file/FileNameGenerator.java @@ -0,0 +1,7 @@ +package br.com.concrete.mock.infra.component.file; + +public interface FileNameGenerator { + + String fromPath(final String path); + +} diff --git a/src/main/java/br/com/concrete/mock/infra/component/file/FileNameGeneratorImpl.java b/src/main/java/br/com/concrete/mock/infra/component/file/FileNameGeneratorImpl.java new file mode 100755 index 0000000..2bd46cd --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/component/file/FileNameGeneratorImpl.java @@ -0,0 +1,40 @@ +package br.com.concrete.mock.infra.component.file; + +import br.com.concrete.mock.infra.property.FileExtensionProperty; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; + +@Component +public class FileNameGeneratorImpl implements FileNameGenerator { + + private final FileExtensionProperty fileExtensionProperty; + + @Autowired + public FileNameGeneratorImpl(FileExtensionProperty fileExtensionProperty) { + this.fileExtensionProperty = fileExtensionProperty; + } + + public String fromPath(final String pathName) { + try { + final Long count = Files.list(Paths.get(pathName)).count(); + return getNewFileName(pathName, count); + } catch (IOException e) { + return "1"; + } + } + + private String getNewFileName(final String pathName, final Long fileCount) { + final long newFileCount = fileCount + 1; + final String newFileName = getFileName(pathName, newFileCount); + return Files.exists(Paths.get(newFileName)) ? getNewFileName(pathName, newFileCount) : String.valueOf(newFileCount); + } + + private String getFileName(final String pathName, final Long count) { + return Paths.get(pathName, String.valueOf(count) + fileExtensionProperty.getFileExtension()).toFile().getAbsolutePath(); + } + +} diff --git a/src/main/java/br/com/concrete/mock/infra/component/impl/ConvertJsonImpl.java b/src/main/java/br/com/concrete/mock/infra/component/impl/ConvertJsonImpl.java new file mode 100755 index 0000000..9c82e90 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/component/impl/ConvertJsonImpl.java @@ -0,0 +1,28 @@ +package br.com.concrete.mock.infra.component.impl; + +import br.com.concrete.mock.infra.component.ConvertJson; +import br.com.concrete.mock.infra.exception.impl.JsonApiApplicationException; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.io.IOException; +import java.util.HashMap; + +@Component +public class ConvertJsonImpl implements ConvertJson { + + private static final Logger LOGGER = LoggerFactory.getLogger(ConvertJson.class); + private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); + + public HashMap apply(String jsonKey) { + try { + return OBJECT_MAPPER.readValue(jsonKey, HashMap.class); + } catch (IOException e) { + LOGGER.error("Cannot to convert json to HashMap", e); + throw new JsonApiApplicationException(e.getMessage()); + } + } + +} diff --git a/src/main/java/br/com/concrete/mock/infra/component/impl/FromJsonStringToObjectConverterImpl.java b/src/main/java/br/com/concrete/mock/infra/component/impl/FromJsonStringToObjectConverterImpl.java new file mode 100755 index 0000000..b231537 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/component/impl/FromJsonStringToObjectConverterImpl.java @@ -0,0 +1,27 @@ +package br.com.concrete.mock.infra.component.impl; + +import br.com.concrete.mock.infra.component.FromJsonStringToObjectConverter; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import org.springframework.stereotype.Component; + +import java.util.Optional; + +@Component +public class FromJsonStringToObjectConverterImpl implements FromJsonStringToObjectConverter { + + private static final JsonParser JSON_PARSER = new JsonParser(); + + @Override + public JsonElement apply(final Optional json) { + return json + .map(JSON_PARSER::parse) + .filter(jsonElement -> !jsonElement.isJsonNull()) + .map(jsonElement -> jsonElement.isJsonObject() ? + jsonElement.getAsJsonObject() : + jsonElement.getAsJsonArray() + ) + .orElse(JSON_PARSER.parse("")); + } + +} diff --git a/src/main/java/br/com/concrete/mock/infra/component/impl/JsonFormatterPretty.java b/src/main/java/br/com/concrete/mock/infra/component/impl/JsonFormatterPretty.java new file mode 100755 index 0000000..79c5553 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/component/impl/JsonFormatterPretty.java @@ -0,0 +1,22 @@ +package br.com.concrete.mock.infra.component.impl; + +import br.com.concrete.mock.infra.component.JsonFormatter; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import org.springframework.stereotype.Component; + +@Component +public class JsonFormatterPretty implements JsonFormatter { + + @Override + public String format(final String jsonString) { + final JsonParser parser = new JsonParser(); + final Gson gson = new GsonBuilder().setPrettyPrinting().create(); + + final JsonElement el = parser.parse(jsonString); + return gson.toJson(el).concat("\n"); + } + +} diff --git a/src/main/java/br/com/concrete/mock/infra/component/impl/JsonValueCompilerImpl.java b/src/main/java/br/com/concrete/mock/infra/component/impl/JsonValueCompilerImpl.java new file mode 100755 index 0000000..ca6620d --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/component/impl/JsonValueCompilerImpl.java @@ -0,0 +1,33 @@ +package br.com.concrete.mock.infra.component.impl; + +import br.com.concrete.mock.infra.component.JsonValueCompiler; +import org.springframework.stereotype.Component; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@Component +public class JsonValueCompilerImpl implements JsonValueCompiler { + + @Override + public String compile(String json) { + // http://regexr.com/3f7lq + final Pattern p = Pattern.compile("\\#\\{\\{(\\d)+daysAgo:([a-z]+?.*?[a-z]+?)\\}\\}", Pattern.CASE_INSENSITIVE); + final Matcher m = p.matcher(json); + + final StringBuffer sb = new StringBuffer(); + while (m.find()) { + final Integer daysAgo = Integer.valueOf(m.group(1)); + final String format = m.group(2); + + final String dateFormatted = LocalDate.now().minusDays(daysAgo).format(DateTimeFormatter.ofPattern(format)); + + m.appendReplacement(sb, dateFormatted); + } + m.appendTail(sb); + return sb.toString(); + } + +} diff --git a/src/main/java/br/com/concrete/mock/infra/component/impl/QueryStringBuilderImpl.java b/src/main/java/br/com/concrete/mock/infra/component/impl/QueryStringBuilderImpl.java new file mode 100755 index 0000000..c9871e7 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/component/impl/QueryStringBuilderImpl.java @@ -0,0 +1,31 @@ +package br.com.concrete.mock.infra.component.impl; + +import br.com.concrete.mock.infra.component.ConvertJson; +import br.com.concrete.mock.infra.component.QueryStringBuilder; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Map; + +@Component +public class QueryStringBuilderImpl implements QueryStringBuilder { + + private final ConvertJson convertJson; + + @Autowired + public QueryStringBuilderImpl(ConvertJson convertJson) { + this.convertJson = convertJson; + } + + @Override + public String fromMap(Map queryMap) { + return queryMap + .entrySet() + .stream() + .map(entry -> "&".concat(entry.getKey()).concat("=").concat(entry.getValue())) + .reduce(String::concat) + .map(s -> "?" + s.substring(1, s.length())) + .orElse(""); + } + +} diff --git a/src/main/java/br/com/concrete/mock/infra/component/impl/RequestFormatterImpl.java b/src/main/java/br/com/concrete/mock/infra/component/impl/RequestFormatterImpl.java new file mode 100755 index 0000000..ee6f421 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/component/impl/RequestFormatterImpl.java @@ -0,0 +1,21 @@ +package br.com.concrete.mock.infra.component.impl; + +import br.com.concrete.mock.infra.component.RequestFormatter; +import org.springframework.stereotype.Component; + +import javax.servlet.http.HttpServletRequest; +import java.util.Optional; + +@Component +public class RequestFormatterImpl implements RequestFormatter { + + public String generateLog(HttpServletRequest request, Optional requestBody) { + return new StringBuilder() + .append(request.getMethod()) + .append(": ") + .append(request.getRequestURL()) + .append(request.getQueryString() == null ? "" : "?" + request.getQueryString()) + .toString(); + } + +} diff --git a/src/main/java/br/com/concrete/mock/infra/controller/ShutdownController.java b/src/main/java/br/com/concrete/mock/infra/controller/ShutdownController.java new file mode 100644 index 0000000..d7ed3f7 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/controller/ShutdownController.java @@ -0,0 +1,21 @@ +package br.com.concrete.mock.infra.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.SpringApplication; +import org.springframework.context.ApplicationContext; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import static org.springframework.web.bind.annotation.RequestMethod.POST; + +@RestController +public class ShutdownController { + + @Autowired + private ApplicationContext appContext; + + @RequestMapping(value = "/shutdown", method = POST) + public void initiateShutdown() { + SpringApplication.exit(appContext); + } +} diff --git a/src/main/java/br/com/concrete/mock/infra/exception/ApplicationException.java b/src/main/java/br/com/concrete/mock/infra/exception/ApplicationException.java new file mode 100755 index 0000000..197418d --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/exception/ApplicationException.java @@ -0,0 +1,7 @@ +package br.com.concrete.mock.infra.exception; + +public interface ApplicationException { + + ApplicationExceptionMessage buildApplicationExceptionMessage(); + +} diff --git a/src/main/java/br/com/concrete/mock/infra/exception/ApplicationExceptionHandler.java b/src/main/java/br/com/concrete/mock/infra/exception/ApplicationExceptionHandler.java new file mode 100755 index 0000000..c3ac694 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/exception/ApplicationExceptionHandler.java @@ -0,0 +1,66 @@ +package br.com.concrete.mock.infra.exception; + +import br.com.concrete.mock.infra.exception.impl.ApiApplicationException; +import br.com.concrete.mock.infra.exception.impl.ApplicationExceptionImpl; +import br.com.concrete.mock.infra.exception.impl.ApplicationExceptionMessageImpl; +import br.com.concrete.mock.infra.exception.impl.ErrorApplicationException; +import com.google.gson.Gson; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.ResourceAccessException; + +@ControllerAdvice +public class ApplicationExceptionHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationExceptionHandler.class); + private static final Gson GSON = new Gson(); + + @ResponseStatus(HttpStatus.BAD_REQUEST) + @ExceptionHandler(ApplicationExceptionImpl.class) + @ResponseBody + public ApplicationExceptionMessage handleBadRequest(ApplicationException e) { + LOGGER.error("handleBadRequest", e); + return e.buildApplicationExceptionMessage(); + } + + @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY) + @ExceptionHandler(ErrorApplicationException.class) + @ResponseBody + public ApplicationExceptionMessage handleMockError(ErrorApplicationException e) { + LOGGER.error("handleMockApiError", e); + return e.buildApplicationExceptionMessage(); + } + + @ResponseStatus(HttpStatus.EXPECTATION_FAILED) + @ExceptionHandler(ApiApplicationException.class) + @ResponseBody + public ApplicationExceptionMessage handleApiException(ApiApplicationException e) { + LOGGER.error("handleApiException", e); + return e.buildApplicationExceptionMessage(); + } + + @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY) + @ExceptionHandler(ResourceAccessException.class) + @ResponseBody + public ApplicationExceptionMessage handleResourceAccessException(ResourceAccessException e) { + final ApplicationExceptionMessage applicationExceptionMessage = new ApplicationExceptionMessageImpl(String.valueOf(HttpStatus.UNPROCESSABLE_ENTITY.value()), e.getMessage()); + LOGGER.error("handleResourceAccessException", e); + return applicationExceptionMessage; + } + + @ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY) + @ExceptionHandler(HttpClientErrorException.class) + @ResponseBody + public ApplicationExceptionMessage handleHttpClientErrorException(HttpClientErrorException e) { + final ApplicationExceptionMessageImpl applicationExceptionMessage = GSON.fromJson(e.getResponseBodyAsString(), ApplicationExceptionMessageImpl.class); + LOGGER.error("handleHttpClientErrorException", e); + return applicationExceptionMessage; + } + +} diff --git a/src/main/java/br/com/concrete/mock/infra/exception/ApplicationExceptionMessage.java b/src/main/java/br/com/concrete/mock/infra/exception/ApplicationExceptionMessage.java new file mode 100755 index 0000000..4a5d7d9 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/exception/ApplicationExceptionMessage.java @@ -0,0 +1,9 @@ +package br.com.concrete.mock.infra.exception; + +public interface ApplicationExceptionMessage { + + String getCodigo(); + + String getMensagem(); + +} diff --git a/src/main/java/br/com/concrete/mock/infra/exception/impl/ApiApplicationException.java b/src/main/java/br/com/concrete/mock/infra/exception/impl/ApiApplicationException.java new file mode 100755 index 0000000..ff25bb0 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/exception/impl/ApiApplicationException.java @@ -0,0 +1,12 @@ +package br.com.concrete.mock.infra.exception.impl; + +import br.com.concrete.mock.infra.exception.ApplicationException; +import org.springframework.http.HttpStatus; + +public class ApiApplicationException extends ApplicationExceptionImpl implements ApplicationException { + + public ApiApplicationException(String mensagem) { + super(String.valueOf(HttpStatus.EXPECTATION_FAILED.value()), "Internal Mock API exception: ".concat(mensagem)); + } + +} diff --git a/src/main/java/br/com/concrete/mock/infra/exception/impl/ApplicationExceptionImpl.java b/src/main/java/br/com/concrete/mock/infra/exception/impl/ApplicationExceptionImpl.java new file mode 100755 index 0000000..c7c7e16 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/exception/impl/ApplicationExceptionImpl.java @@ -0,0 +1,20 @@ +package br.com.concrete.mock.infra.exception.impl; + +import br.com.concrete.mock.infra.exception.ApplicationException; +import br.com.concrete.mock.infra.exception.ApplicationExceptionMessage; + +public class ApplicationExceptionImpl extends RuntimeException implements ApplicationException { + + private final String codigo; + + public ApplicationExceptionImpl(String codigo, String mensagem) { + super(mensagem); + this.codigo = codigo; + } + + @Override + public ApplicationExceptionMessage buildApplicationExceptionMessage() { + return new ApplicationExceptionMessageImpl(codigo, getMessage()); + } + +} diff --git a/src/main/java/br/com/concrete/mock/infra/exception/impl/ApplicationExceptionMessageImpl.java b/src/main/java/br/com/concrete/mock/infra/exception/impl/ApplicationExceptionMessageImpl.java new file mode 100755 index 0000000..cf7fbe2 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/exception/impl/ApplicationExceptionMessageImpl.java @@ -0,0 +1,27 @@ +package br.com.concrete.mock.infra.exception.impl; + +import br.com.concrete.mock.infra.exception.ApplicationExceptionMessage; + +import java.io.Serializable; + +public class ApplicationExceptionMessageImpl implements ApplicationExceptionMessage, Serializable { + + private final String codigo; + private final String mensagem; + + public ApplicationExceptionMessageImpl(String codigo, String mensagem) { + this.codigo = codigo; + this.mensagem = mensagem; + } + + @Override + public String getCodigo() { + return codigo; + } + + @Override + public String getMensagem() { + return mensagem; + } + +} diff --git a/src/main/java/br/com/concrete/mock/infra/exception/impl/ErrorApplicationException.java b/src/main/java/br/com/concrete/mock/infra/exception/impl/ErrorApplicationException.java new file mode 100755 index 0000000..060e6d9 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/exception/impl/ErrorApplicationException.java @@ -0,0 +1,11 @@ +package br.com.concrete.mock.infra.exception.impl; + +import br.com.concrete.mock.infra.exception.ApplicationException; + +public class ErrorApplicationException extends ApplicationExceptionImpl implements ApplicationException { + + public ErrorApplicationException(String codigo, String mensagem) { + super(codigo, mensagem); + } + +} diff --git a/src/main/java/br/com/concrete/mock/infra/exception/impl/JsonApiApplicationException.java b/src/main/java/br/com/concrete/mock/infra/exception/impl/JsonApiApplicationException.java new file mode 100755 index 0000000..3c355c5 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/exception/impl/JsonApiApplicationException.java @@ -0,0 +1,12 @@ +package br.com.concrete.mock.infra.exception.impl; + +import br.com.concrete.mock.infra.exception.ApplicationException; +import org.springframework.http.HttpStatus; + +public class JsonApiApplicationException extends ApplicationExceptionImpl implements ApplicationException { + + public JsonApiApplicationException(String mensagem) { + super(String.valueOf(HttpStatus.EXPECTATION_FAILED.value()), "Internal Mock API exception on convert json: ".concat(mensagem)); + } + +} diff --git a/src/main/java/br/com/concrete/mock/infra/model/DefaultHeader.java b/src/main/java/br/com/concrete/mock/infra/model/DefaultHeader.java new file mode 100644 index 0000000..3d33c31 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/model/DefaultHeader.java @@ -0,0 +1,54 @@ +package br.com.concrete.mock.infra.model; + +import java.io.Serializable; +import java.util.List; +import java.util.Objects; + +public class DefaultHeader implements Serializable { + + private static final long serialVersionUID = 6319619000946506689L; + + private String headerName; + private List headerValues; + + public DefaultHeader(String headerName, List headerValues) { + this.headerName = headerName; + this.headerValues = headerValues; + } + + public DefaultHeader() { + + } + // String headerName + // String... headerValues; + + public void setHeaderName(String headerName) { + this.headerName = headerName; + } + + public void setHeaderValues(List headerValues) { + this.headerValues = headerValues; + } + + public String getHeaderName() { + return headerName; + } + + public List getHeaderValues() { + return headerValues; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DefaultHeader that = (DefaultHeader) o; + return Objects.equals(headerName, that.headerName) && + Objects.equals(headerValues, that.headerValues); + } + + @Override + public int hashCode() { + return Objects.hash(headerName, headerValues); + } +} diff --git a/src/main/java/br/com/concrete/mock/infra/model/UriConfiguration.java b/src/main/java/br/com/concrete/mock/infra/model/UriConfiguration.java new file mode 100644 index 0000000..dca3278 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/model/UriConfiguration.java @@ -0,0 +1,68 @@ +package br.com.concrete.mock.infra.model; + +import java.io.Serializable; +import java.util.Objects; +import java.util.Optional; +import java.util.regex.Pattern; + +public class UriConfiguration implements Serializable { + + private String host; + private Pattern pattern; + private Boolean backup; + + public UriConfiguration(String host, Pattern pattern, Boolean backup) { + this.host = host; + this.pattern = pattern; + this.backup = backup; + } + + public UriConfiguration() { + this.backup = true; + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public Pattern getPattern() { + return pattern; + } + + public void setPattern(String pattern) { + this.pattern = Pattern.compile(pattern, Pattern.CASE_INSENSITIVE | Pattern.MULTILINE); + } + + public Boolean isActiveBackup() { + return Optional.ofNullable(backup).orElse(true); + } + + public void setBackup(Boolean backup) { + this.backup = backup; + } + + @Override + public String toString() { + return "UriConfiguration [host=" + host + ", pattern=" + pattern + ", backup=" + backup + "]"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + UriConfiguration that = (UriConfiguration) o; + return Objects.equals(host, that.host) && + Objects.equals(pattern.pattern(), that.pattern.pattern()) && + Objects.equals(pattern.flags(), that.pattern.flags()) && + Objects.equals(backup, that.backup); + } + + @Override + public int hashCode() { + return Objects.hash(host, pattern, backup); + } +} diff --git a/src/main/java/br/com/concrete/mock/infra/property/ApiProperty.java b/src/main/java/br/com/concrete/mock/infra/property/ApiProperty.java new file mode 100755 index 0000000..91467ce --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/property/ApiProperty.java @@ -0,0 +1,79 @@ +package br.com.concrete.mock.infra.property; + +import br.com.concrete.mock.infra.model.DefaultHeader; +import br.com.concrete.mock.infra.model.UriConfiguration; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import static java.util.Collections.emptyList; + +@Component +@ConfigurationProperties(prefix = "api") +public class ApiProperty { + + private List acceptedHeaders = new ArrayList<>(); + private String host; + private List uriConfigurations; + private List defaultHeaders; + private String magicalHeaders; + + public List getAcceptedHeaders() { + return acceptedHeaders; + } + + public void setAcceptedHeaders(List acceptedHeaders) { + this.acceptedHeaders = getAcceptedHeaders().stream().map(String::toLowerCase).collect(Collectors.toList()); + } + + public Boolean isAcceptedHeader(final String headerValue) { + return getAcceptedHeaders().isEmpty() || getAcceptedHeaders().contains(headerValue.toLowerCase()); + } + + public void setUriConfigurations(List uriConfigurations) { + this.uriConfigurations = uriConfigurations; + } + + public List getUriConfigurations() { + return this.uriConfigurations; + } + + public Optional getConfiguration(String uri) { + return Optional + .ofNullable(uriConfigurations) + .orElse(emptyList()) + .stream() + .filter(config -> config.getPattern().matcher(uri).find()).findAny(); + } + + public String getHost() { + return host; + } + + public void setHost(String host) { + this.host = host; + } + + public List getDefaultHeaders() { + if (defaultHeaders == null) { + defaultHeaders = new ArrayList<>(); + } + return defaultHeaders; + } + + public void setDefaultHeaders(List defaultHeaders) { + this.defaultHeaders = defaultHeaders; + } + + public String getMagicalHeaders() { + return magicalHeaders; + } + + public void setMagicalHeaders(String magicalHeaders) { + this.magicalHeaders = magicalHeaders; + } +} diff --git a/src/main/java/br/com/concrete/mock/infra/property/FileExtensionProperty.java b/src/main/java/br/com/concrete/mock/infra/property/FileExtensionProperty.java new file mode 100755 index 0000000..ed753d8 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/property/FileExtensionProperty.java @@ -0,0 +1,7 @@ +package br.com.concrete.mock.infra.property; + +public interface FileExtensionProperty { + + String getFileExtension(); + +} diff --git a/src/main/java/br/com/concrete/mock/infra/property/FileProperty.java b/src/main/java/br/com/concrete/mock/infra/property/FileProperty.java new file mode 100755 index 0000000..ffe10e5 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/property/FileProperty.java @@ -0,0 +1,7 @@ +package br.com.concrete.mock.infra.property; + +public interface FileProperty { + + String getFileBase(); + +} diff --git a/src/main/java/br/com/concrete/mock/infra/property/impl/FileExtensionPropertyImpl.java b/src/main/java/br/com/concrete/mock/infra/property/impl/FileExtensionPropertyImpl.java new file mode 100755 index 0000000..47f6dd0 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/property/impl/FileExtensionPropertyImpl.java @@ -0,0 +1,20 @@ +package br.com.concrete.mock.infra.property.impl; + +import br.com.concrete.mock.infra.property.FileExtensionProperty; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component +public class FileExtensionPropertyImpl implements FileExtensionProperty { + + private final String fileExtension; + + public FileExtensionPropertyImpl(@Value("${file.extension}") String fileExtension) { + this.fileExtension = fileExtension; + } + + public String getFileExtension() { + return fileExtension; + } + +} diff --git a/src/main/java/br/com/concrete/mock/infra/property/impl/FilePropertyBackup.java b/src/main/java/br/com/concrete/mock/infra/property/impl/FilePropertyBackup.java new file mode 100755 index 0000000..b4542c9 --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/property/impl/FilePropertyBackup.java @@ -0,0 +1,20 @@ +package br.com.concrete.mock.infra.property.impl; + +import br.com.concrete.mock.infra.property.FileProperty; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component("FilePropertyBackup") +public class FilePropertyBackup implements FileProperty { + + private final String fileBase; + + public FilePropertyBackup(@Value("${file.backup.path}") String fileBase) { + this.fileBase = fileBase; + } + + public String getFileBase() { + return fileBase; + } + +} diff --git a/src/main/java/br/com/concrete/mock/infra/property/impl/FilePropertyModel.java b/src/main/java/br/com/concrete/mock/infra/property/impl/FilePropertyModel.java new file mode 100755 index 0000000..de4ee0b --- /dev/null +++ b/src/main/java/br/com/concrete/mock/infra/property/impl/FilePropertyModel.java @@ -0,0 +1,20 @@ +package br.com.concrete.mock.infra.property.impl; + +import br.com.concrete.mock.infra.property.FileProperty; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Component("FilePropertyModel") +public class FilePropertyModel implements FileProperty { + + private final String fileBase; + + public FilePropertyModel(@Value("${file.base}") String fileBase) { + this.fileBase = fileBase; + } + + public String getFileBase() { + return fileBase; + } + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100755 index 0000000..192a822 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,50 @@ +# vmoptions: -Dspring.profiles.active=test + +server.port: 9090 + +api: + # Requests accept just this headers (optinal property). If property not exists, all headers are accepted +# acceptedHeaders: +# - "accept" +# - "Content-Type" + + # When is not find in repository, then will try to do a request in this host + host: "http://www.mocky.io" + + magicalHeaders: "Accept,User-Agent,Content-Type" + + # Default headers to do a request + defaultHeaders: + - + headerName: "Access-Control-Allow-Origin" + headerValues: + - "Connection" + - "Keep-Alive" + - + headerName: "Content-Type" + headerValues: + - "application/json;charset=UTF-8" + + # Alternative hosts + # When a request uri match with some pattern, than this host will be used + uriConfigurations: + - + host: "http://www.mocky2.io" + pattern: "begin-of-uri/|another-pattern/" + backup: false + - + host: "http://www.mocky3.io" + pattern: "teste/|test/" + +# Paths of json +file: + # Look for this path to find json with a request + base: "${MOCK_API_FILE_BASE}" + extension: ".json" + # Save request (cache) in this path + backup.path: "${MOCK_BACKUP_FILE_BASE}" + +# Initialize app in capture mode. All requests will be saved +captureState: true + +debug: true diff --git a/src/test/java/br/com/concrete/mock/ApiApplicationTests.java b/src/test/java/br/com/concrete/mock/ApiApplicationTests.java new file mode 100755 index 0000000..c29a56b --- /dev/null +++ b/src/test/java/br/com/concrete/mock/ApiApplicationTests.java @@ -0,0 +1,16 @@ +package br.com.concrete.mock; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +@RunWith(SpringRunner.class) +@SpringBootTest +public class ApiApplicationTests { + + @Test + public void contextLoads() { + } + +} diff --git a/src/test/java/br/com/concrete/mock/configuration/api/v1/controller/CaptureStateControllerIntegrationTest.java b/src/test/java/br/com/concrete/mock/configuration/api/v1/controller/CaptureStateControllerIntegrationTest.java new file mode 100755 index 0000000..4085d82 --- /dev/null +++ b/src/test/java/br/com/concrete/mock/configuration/api/v1/controller/CaptureStateControllerIntegrationTest.java @@ -0,0 +1,98 @@ +package br.com.concrete.mock.configuration.api.v1.controller; + +import br.com.concrete.mock.configuration.api.v1.mapper.CaptureStateDto; +import br.com.concrete.mock.configuration.model.CaptureState; +import br.com.concrete.mock.configuration.service.CaptureStateService; +import com.google.gson.Gson; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + +import java.util.Optional; + +import static org.mockito.BDDMockito.given; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@RunWith(SpringRunner.class) +@WebMvcTest(CaptureStateController.class) +public class CaptureStateControllerIntegrationTest { + + @Autowired + private MockMvc mvc; + + @MockBean + private CaptureStateService captureStateService; + + @Test + public void shouldRequestStateDisabledOfCaptureMode() throws Exception { + // given + given(captureStateService.getCurrent()) + .willReturn(Optional.empty()); + + // when + final ResultActions result = this.mvc.perform(get("/configuration/capture-state").accept(MediaType.APPLICATION_JSON_UTF8)); + + // then + result.andExpect(status().isOk()) + .andExpect(content().json("{\"enabled\": false}", false)); + } + + @Test + public void shouldRequestStateEnabledOfCaptureMode() throws Exception { + // given + given(captureStateService.getCurrent()) + .willReturn(Optional.of(new CaptureState(true))); + + // when + final ResultActions result = this.mvc.perform(get("/configuration/capture-state").accept(MediaType.APPLICATION_JSON_UTF8)); + + // then + result.andExpect(status().isOk()) + .andExpect(content().json("{\"enabled\": true}", false)); + } + + @Test + public void shouldBeEnableCaptureMode() throws Exception { + // given + final CaptureState captureState = new CaptureState(true); + final String requestBody = new Gson().toJson(new CaptureStateDto(captureState)); + + given(captureStateService.enable()) + .willReturn(captureState); + + // when + final ResultActions result = this.mvc.perform( + post("/configuration/capture-state/enable") + .content(requestBody) + .contentType(MediaType.APPLICATION_JSON_UTF8) + .accept(MediaType.APPLICATION_JSON_UTF8) + ); + + // then + result.andExpect(status() + .isOk()) + .andExpect(content().json("{\"enabled\": true}", false)); + } + + @Test + public void shouldBeDisableCaptureMode() throws Exception { + // given + given(captureStateService.getCurrent()) + .willReturn(Optional.of(new CaptureState(true))); + + // when + final ResultActions result = this.mvc.perform(delete("/configuration/capture-state/disable").accept(MediaType.APPLICATION_JSON_UTF8)); + + // then + result.andExpect(status().isNoContent()); + } + +} diff --git a/src/test/java/br/com/concrete/mock/generic/api/v1/controller/GenericApiControllerExternalIntegrationTest.java b/src/test/java/br/com/concrete/mock/generic/api/v1/controller/GenericApiControllerExternalIntegrationTest.java new file mode 100755 index 0000000..e66311c --- /dev/null +++ b/src/test/java/br/com/concrete/mock/generic/api/v1/controller/GenericApiControllerExternalIntegrationTest.java @@ -0,0 +1,106 @@ +package br.com.concrete.mock.generic.api.v1.controller; + +import br.com.concrete.mock.generic.mapper.EndpointDto; +import com.google.common.collect.ImmutableMap; +import com.google.gson.Gson; +import org.json.JSONException; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.skyscreamer.jsonassert.JSONAssert; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.*; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Map; +import java.util.Optional; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class GenericApiControllerExternalIntegrationTest { + + @Autowired + private TestRestTemplate restTemplate; + + @Value("${file.base}") + private String fileBase; + @Value("${file.extension}") + private String fileExtension; + + private File resource; + + @Before + public void init() throws URISyntaxException { + this.resource = Paths.get(getClass().getClassLoader().getResource(fileBase).toURI()).toFile(); + } + + @Test + public void shouldFileExistsInTest() { + assertNotNull(resource); + } + + private String getJson(String fileNameExpected) throws IOException { + return new String(Files.readAllBytes(Paths.get(fileNameExpected)), StandardCharsets.UTF_8); + } + + @Test(timeout = 10000) + public void shouldResolvePostWithExternalMock() throws IOException, JSONException { + shouldResolveWithExternalMock(HttpMethod.POST); + } + + @Test(timeout = 5000) + public void shouldResolvePathWithExternalMock() throws IOException, JSONException { + shouldResolveWithExternalMock(HttpMethod.PATCH); + } + + private void shouldResolveWithExternalMock(final HttpMethod httpMethod) throws IOException, JSONException { + final ImmutableMap headers = ImmutableMap.builder() + .put(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE).build(); + + shouldResolveWithExternalMock(httpMethod, Optional.of(headers)); + } + + private void shouldResolveWithExternalMock(final HttpMethod httpMethod, final Optional> headers) + throws IOException, JSONException { + // given + final String url = "/v2/57fbd6280f0000ed154fd470"; + + final HttpStatus httpStatus = HttpStatus.OK; + final String fileName = + Paths.get(resource.getAbsolutePath(), httpMethod.name().toLowerCase(), url, "1" + fileExtension) + .toAbsolutePath().toString(); + + final EndpointDto endpointDto = new Gson().fromJson(getJson(fileName), EndpointDto.class); + final String requestJson = new Gson().toJson(endpointDto.getRequest().getBody()); + final String responseJson = new Gson().toJson(endpointDto.getResponse().getBody()); + + final HttpHeaders httpHeaders = headers.filter(mapHeaders -> !mapHeaders.isEmpty()).map(map -> { + final HttpHeaders result = new HttpHeaders(); + result.setContentType(MediaType.APPLICATION_JSON); + return result; + }).orElse(null); + + final HttpEntity httpEntity = new HttpEntity<>(requestJson, httpHeaders); + + // when + final ResponseEntity response = restTemplate.exchange(url, httpMethod, httpEntity, String.class); + + // then + assertEquals(httpStatus, response.getStatusCode()); + JSONAssert.assertEquals(responseJson, response.getBody(), false); + assertNotNull(response.getHeaders().get("Access-Control-Allow-Origin")); + } + +} diff --git a/src/test/java/br/com/concrete/mock/generic/api/v1/controller/GenericApiControllerIntegrationTest.java b/src/test/java/br/com/concrete/mock/generic/api/v1/controller/GenericApiControllerIntegrationTest.java new file mode 100755 index 0000000..6684b80 --- /dev/null +++ b/src/test/java/br/com/concrete/mock/generic/api/v1/controller/GenericApiControllerIntegrationTest.java @@ -0,0 +1,234 @@ +package br.com.concrete.mock.generic.api.v1.controller; + +import br.com.concrete.mock.infra.component.QueryStringBuilder; +import br.com.concrete.mock.generic.mapper.EndpointDto; +import br.com.concrete.mock.generic.model.Endpoint; +import com.google.gson.Gson; +import org.json.JSONException; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.skyscreamer.jsonassert.JSONAssert; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.*; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.bind.annotation.RequestMethod; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class GenericApiControllerIntegrationTest { + + @Autowired + private TestRestTemplate restTemplate; + + @Autowired + private QueryStringBuilder queryStringBuilder; + + @Value("${file.extension}") + private String fileExtension; + @Value("${file.base}") + private String fileBase; + + private File resource; + + @Before + public void init() throws URISyntaxException { + this.resource = Paths.get(getClass().getClassLoader().getResource(fileBase).toURI()).toFile(); + } + + @Test + public void shouldFileExistsInTest() { + assertNotNull(resource); + } + + private String getJson(String fileNameExpected) throws IOException { + final Path path = Paths.get(fileNameExpected); + return Files.exists(path) ? new String(Files.readAllBytes(path), StandardCharsets.UTF_8) : "{}"; + } + + private void shouldResolveGetWithLocalMockMatchQueryCaseX(String uri, String caseX) throws IOException, JSONException { + // given + final String fileName = + Paths.get(resource.getAbsolutePath(), "get", uri, caseX + fileExtension) + .toAbsolutePath().toString(); + + final String endpointJson = getJson(fileName); + + final EndpointDto endpointDto = new Gson().fromJson(endpointJson, EndpointDto.class); + final Endpoint endpoint = endpointDto.toModel(RequestMethod.GET, uri); + final String parameters = endpoint + .getRequest() + .getQuery() + .filter(queryMap -> !queryMap.isEmpty()) + .map(queryMap -> queryStringBuilder.fromMap(queryMap)) + .map(""::concat) + .orElse(""); + + final String responseJson = new Gson().toJson(endpointDto.getResponse().getBody()); + + // when + final ResponseEntity response = restTemplate.getForEntity(uri.concat(parameters), String.class); + + // then + assertEquals(HttpStatus.OK, response.getStatusCode()); + JSONAssert.assertEquals(responseJson, response.getBody(), false); + } + + @Test(timeout = 2000) + public void shouldResolveGetWithSimpleResponseWithoutRequest() throws IOException, JSONException { + // given + final String uri = "/guests/132/users/21/cc"; + final String fileName = + Paths.get(resource.getAbsolutePath(), "get", uri, "1" + fileExtension) + .toAbsolutePath().toString(); + final String endpointJson = getJson(fileName); + final EndpointDto endpointDto = new Gson().fromJson(endpointJson, EndpointDto.class); + final String responseJson = new Gson().toJson(endpointDto.getResponse().getBody()); + + // when + final ResponseEntity response = restTemplate.getForEntity(uri, String.class); + + // then + assertNotNull(response); + assertEquals(HttpStatus.OK, response.getStatusCode()); + JSONAssert.assertEquals(responseJson, response.getBody(), false); + } + + @Test(timeout = 2000) + public void shouldResolveGetWithSimpleResponseWithRequest() throws IOException, JSONException { + // given + final String uri = "/guests/132/users/22/cc"; + final String fileName = + Paths.get(resource.getAbsolutePath(), "get", uri, "1" + fileExtension) + .toAbsolutePath().toString(); + final String endpointJson = getJson(fileName); + final EndpointDto endpointDto = new Gson().fromJson(endpointJson, EndpointDto.class); + final String responseJson = new Gson().toJson(endpointDto.getResponse().getBody()); + + // when + final ResponseEntity response = restTemplate.getForEntity(uri, String.class); + + // then + assertNotNull(response); + assertEquals(HttpStatus.OK, response.getStatusCode()); + JSONAssert.assertEquals(responseJson, response.getBody(), false); + } + + @Test(timeout = 2000) + public void shouldResolveGetWithLocalMock() throws IOException, JSONException { + // given + final String uri = "/users/123"; + + final String fileName = + Paths.get(resource.getAbsolutePath(), "get", uri, "1" + fileExtension) + .toAbsolutePath().toString(); + final EndpointDto endpointDto = new Gson().fromJson(getJson(fileName), EndpointDto.class); + final String responseJson = new Gson().toJson(endpointDto.getResponse().getBody()); + + // when + final ResponseEntity response = restTemplate.getForEntity(uri, String.class); + + // then + assertEquals(HttpStatus.OK, response.getStatusCode()); + JSONAssert.assertEquals(responseJson, response.getBody(), false); + } + + @Test(timeout = 2000) + public void shouldResolveWithHttpStatusCreated() throws IOException, JSONException { + // given + final String uri = "/users/123"; + + final String fileName = + Paths.get(resource.getAbsolutePath(), "get", uri, "2" + fileExtension) + .toAbsolutePath().toString(); + final EndpointDto endpointDto = new Gson().fromJson(getJson(fileName), EndpointDto.class); + final String responseJson = new Gson().toJson(endpointDto.getResponse().getBody()); + final String query = queryStringBuilder.fromMap(endpointDto.getRequest().getQuery()); + + // when + final ResponseEntity response = restTemplate.getForEntity(uri + query, String.class); + + // then + assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); + JSONAssert.assertEquals(responseJson, response.getBody(), false); + } + + @Test(timeout = 2000) + public void shouldResolvePatchWithLocalMock() throws IOException, JSONException { + shouldResolveWithLocalMockMatcheRequest("/users/1456", "1", HttpStatus.OK, HttpMethod.PATCH); + } + + @Test(timeout = 2000) + public void shouldResolveGetWithLocalMockMatchQueryCase1() throws IOException, JSONException { + shouldResolveGetWithLocalMockMatchQueryCaseX("/payments/user/detail", "1"); + } + + @Test(timeout = 2000) + public void shouldResolveGetWithLocalMockMatchQueryCase2() throws IOException, JSONException { + shouldResolveGetWithLocalMockMatchQueryCaseX("/payments/user/detail", "2"); + } + + // fail + @Test(timeout = 2000) + public void shouldResolveGetWithLocalMockWithSubDirectory() throws IOException, JSONException { + shouldResolveGetWithLocalMockMatchQueryCaseX("/guests/132", "1"); + } + + private void shouldResolvePostWithLocalMockMatcheRequest(final String url, final String caseX, final HttpStatus httpStatus) throws IOException, JSONException { + shouldResolveWithLocalMockMatcheRequest(url, caseX, httpStatus, HttpMethod.POST); + } + + private void shouldResolveWithLocalMockMatcheRequest(final String uri, final String caseX, final HttpStatus httpStatus, HttpMethod httpMethod) throws IOException, JSONException { + // given + final String fileName = + Paths.get(resource.getAbsolutePath(), httpMethod.name().toLowerCase(), uri, caseX + fileExtension) + .toAbsolutePath().toString(); + + final EndpointDto endpointDto = new Gson().fromJson(getJson(fileName), EndpointDto.class); + final String requestJson = new Gson().toJson(endpointDto.getRequest().getBody()); + final String responseJson = new Gson().toJson(endpointDto.getResponse().getBody()); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + + final HttpEntity request = new HttpEntity<>(requestJson, headers); + + // when + final ResponseEntity response = restTemplate.exchange(uri, httpMethod, request, String.class); + + // then + assertEquals(httpStatus, response.getStatusCode()); + JSONAssert.assertEquals(responseJson, response.getBody(), false); + } + + @Test(timeout = 2000) + public void shouldResolvePostWithLocalMockMatcheRequest() throws IOException, JSONException { + shouldResolvePostWithLocalMockMatcheRequest("/move/to/country/13", "1", HttpStatus.OK); + } + + @Test//(timeout = 2000) + public void shouldResolvePostWithLocalMockMatcheRequestCase1() throws IOException, JSONException { + shouldResolvePostWithLocalMockMatcheRequest("/move/to/country/13/pi", "1", HttpStatus.OK); + } + + @Test(timeout = 2000) + public void shouldResolvePostWithLocalMockMatcheRequestCase2() throws IOException, JSONException { + shouldResolvePostWithLocalMockMatcheRequest("/move/to/country/13/pi", "2", HttpStatus.UNPROCESSABLE_ENTITY); + } + +} diff --git a/src/test/java/br/com/concrete/mock/generic/api/v1/mapper/RequestMapperTest.java b/src/test/java/br/com/concrete/mock/generic/api/v1/mapper/RequestMapperTest.java new file mode 100755 index 0000000..ca75061 --- /dev/null +++ b/src/test/java/br/com/concrete/mock/generic/api/v1/mapper/RequestMapperTest.java @@ -0,0 +1,155 @@ +package br.com.concrete.mock.generic.api.v1.mapper; + +import br.com.concrete.mock.generic.mapper.HeaderMapper; +import br.com.concrete.mock.generic.mapper.QueryMapper; +import br.com.concrete.mock.generic.model.Request; +import com.google.common.collect.ImmutableMap; +import org.json.JSONException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.skyscreamer.jsonassert.JSONAssert; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.web.bind.annotation.RequestMethod; + +import java.io.Serializable; +import java.net.URISyntaxException; +import java.util.Map; +import java.util.Optional; + +import static org.junit.Assert.*; + +@RunWith(MockitoJUnitRunner.class) +public class RequestMapperTest { + + @InjectMocks + private RequestMapper requestMapper; + + @Mock(answer = Answers.CALLS_REAL_METHODS) + private QueryMapper queryMapper; + @Mock(answer = Answers.CALLS_REAL_METHODS) + private HeaderMapper headerMapper; + + @Test + public void shouldMapRequest() throws URISyntaxException { + // given + final MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); + + final RequestMethod requestMethod = RequestMethod.GET; + httpServletRequest.setMethod(requestMethod.name().toUpperCase()); + + final String uri = "http://localhost/my-request"; + httpServletRequest.setRequestURI(uri); + + // when + final Request request = requestMapper.mapper(httpServletRequest); + + // then + assertNotNull(request); + assertEquals(requestMethod, request.getMethod()); + assertEquals(uri, request.getUri()); + assertTrue(request.getHeaders().isPresent()); + assertTrue(request.getHeaders().get().isEmpty()); + assertFalse(request.getQuery().isPresent()); + assertFalse(request.getBody().isPresent()); + } + + @Test + public void shouldMapRequestWithHeaders() throws URISyntaxException { + // given + final MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); + + final String key = "keyHeader"; + final String value = "valueHeader"; + httpServletRequest.addHeader(key, value); + + final RequestMethod requestMethod = RequestMethod.GET; + httpServletRequest.setMethod(requestMethod.name().toUpperCase()); + + final String uri = "localhost://my-request"; + httpServletRequest.setRequestURI(uri); + + // whenvalue + final Request request = requestMapper.mapper(httpServletRequest); + + // then + assertNotNull(request); + assertEquals(requestMethod, request.getMethod()); + assertEquals(uri, request.getUri()); + assertTrue(request.getHeaders().isPresent()); + assertTrue(request.getHeaders().get().containsKey(key)); + assertFalse(request.getHeaders().get().get(key).isEmpty()); + assertEquals(value, request.getHeaders().get().get(key).get(0)); + } + + @Test + public void shouldMapRequestWithQueryString() throws URISyntaxException { + // given + final MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); + + final RequestMethod requestMethod = RequestMethod.GET; + httpServletRequest.setMethod(requestMethod.name().toUpperCase()); + + final String uri = "localhost://my-request"; + httpServletRequest.setRequestURI(uri); + + httpServletRequest.setQueryString("name=Paul&age=10"); + + final Map expectedQuery = ImmutableMap.builder() + .put("name", "Paul") + .put("age", "10") + .build(); + + // when + final Request request = requestMapper.mapper(httpServletRequest); + + // then + assertNotNull(request); + assertEquals(requestMethod, request.getMethod()); + assertEquals(uri, request.getUri()); + assertTrue(request.getQuery().isPresent()); + assertEquals(expectedQuery, request.getQuery().get()); + } + + @Test + public void shouldMapRequestWithBody() throws URISyntaxException, JSONException { + // given + final MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); + + final RequestMethod requestMethod = RequestMethod.POST; + httpServletRequest.setMethod(requestMethod.name().toUpperCase()); + + final String uri = "localhost://my-request"; + httpServletRequest.setRequestURI(uri); + + final Optional body = Optional.of(new Person("black")); + + // when + final Request request = requestMapper.mapper(httpServletRequest, body); + + // then + assertNotNull(request); + assertEquals(requestMethod, request.getMethod()); + assertEquals(uri, request.getUri()); + assertTrue(request.getBody().isPresent()); + assertNotNull(request.getBody().get()); + JSONAssert.assertEquals("{\"color\": \"black\"}", request.getBody().get(), false); + } + + private static class Person implements Serializable { + + private final String color; + + public Person(String color) { + this.color = color; + } + + public String getColor() { + return color; + } + } + +} diff --git a/src/test/java/br/com/concrete/mock/generic/mapper/EndpointDtoTest.java b/src/test/java/br/com/concrete/mock/generic/mapper/EndpointDtoTest.java new file mode 100755 index 0000000..b49cedb --- /dev/null +++ b/src/test/java/br/com/concrete/mock/generic/mapper/EndpointDtoTest.java @@ -0,0 +1,163 @@ +package br.com.concrete.mock.generic.mapper; + +import br.com.concrete.mock.generic.model.Endpoint; +import br.com.concrete.mock.generic.model.template.EndpointTemplate; +import br.com.concrete.mock.infra.component.impl.FromJsonStringToObjectConverterImpl; +import br.com.six2six.fixturefactory.Fixture; +import br.com.six2six.fixturefactory.loader.FixtureFactoryLoader; +import com.google.gson.Gson; +import org.json.JSONException; +import org.junit.BeforeClass; +import org.junit.Test; +import org.skyscreamer.jsonassert.JSONAssert; +import org.springframework.web.bind.annotation.RequestMethod; + +import static org.junit.Assert.*; + +public class EndpointDtoTest { + + @BeforeClass + public static void initClass() { + FixtureFactoryLoader.loadTemplates("br.com.concrete.mock.generic.model.template"); + } + + @Test + public void shouldConvertRequest() { + // given + final String json = "{\"request\":{\"body\":[{\"run\":\"7\"}]},\"response\":{\"body\":[{\"age\":8}]}}"; + + // when + final EndpointDto endpointDto = new Gson().fromJson(json, EndpointDto.class); + final Endpoint endpoint = endpointDto.toModel(RequestMethod.GET, "/product"); + + // then + assertNotNull(endpoint); + assertNotNull(endpoint.getRequest()); + assertNotNull(endpoint.getRequest().getBody()); + assertTrue(endpoint.getRequest().getBody().isPresent()); + assertEquals("[{\"run\":\"7\"}]", endpoint.getRequest().getBody().get()); + } + + @Test + public void shouldConvertResponse() { + // given + final String json = "{\"request\":{\"body\":[{\"run\":\"7\"}]},\"response\":{\"body\":[{\"age\":8}]}}"; + + // when + final EndpointDto endpointDto = new Gson().fromJson(json, EndpointDto.class); + final Endpoint endpoint = endpointDto.toModel(RequestMethod.GET, "/product"); + + // then + assertNotNull(endpoint); + assertNotNull(endpoint.getResponse()); + assertNotNull(endpoint.getResponse().getBody()); + assertEquals("[{\"age\":8}]", endpoint.getResponse().getBody()); + } + + @Test + public void shouldConvertFromModel() throws JSONException { + // given + final Endpoint endpoint = Fixture.from(Endpoint.class).gimme(EndpointTemplate.VALID_FULL); + final String expectedJson = "{\n" + + " \"request\": {\n" + + " \"headers\": {\n" + + " \"Accept\": [\"application/json\"]\n" + + " },\n" + + " \"query\": {\n" + + " \"age\": 10,\n" + + " \"text\": \"abc\"\n" + + " },\n" + + " \"body\": {\n" + + " \"id\": 7,\n" + + " \"name\": \"Paul\"\n" + + " }\n" + + " },\n" + + " \"response\": {\n" + + " \"body\": {\n" + + " \"name\": \"Paul\"\n" + + " },\n" + + " \"httpStatus\": 201\n" + + " }\n" + + "}"; + + // when + final EndpointDto endpointDto = new EndpointDto(endpoint, new FromJsonStringToObjectConverterImpl()); + final String json = new Gson().toJson(endpointDto); + + // then + JSONAssert.assertEquals(expectedJson, json, false); + } + + @Test + public void shouldConvertFromModelWithoutHttpStatus() throws JSONException { + // given + final Endpoint endpoint = Fixture.from(Endpoint.class).gimme(EndpointTemplate.VALID_FULL); + final String expectedJson = "{\n" + + " \"request\": {\n" + + " \"headers\": {\n" + + " \"Accept\": [\"application/json\"]\n" + + " },\n" + + " \"query\": {\n" + + " \"age\": 10,\n" + + " \"text\": \"abc\"\n" + + " },\n" + + " \"body\": {\n" + + " \"name\": \"Paul\"\n" + + " }\n" + + " },\n" + + " \"response\": {\n" + + " \"body\": {\n" + + " \"name\": \"Paul\"\n" + + " }\n" + + " }\n" + + "}"; + + // when + final EndpointDto endpointDto = new EndpointDto(endpoint, new FromJsonStringToObjectConverterImpl()); + final String json = new Gson().toJson(endpointDto); + + // then + JSONAssert.assertEquals(expectedJson, json, false); + } + + @Test + public void shouldConvertFromModelWithList() throws JSONException { + // given + final Endpoint endpoint = Fixture.from(Endpoint.class).gimme(EndpointTemplate.VALID_WITH_LIST); + final String expectedJson = "{\n" + + " \"request\": {\n" + + " \"headers\": {\n" + + " \"Accept\": [\n" + + " \"application/json\"\n" + + " ]\n" + + " },\n" + + " \"query\": {\n" + + " \"age\": 10,\n" + + " \"text\": \"abc\"\n" + + " },\n" + + " \"body\": [{\n" + + " \"id\": 7,\n" + + " \"name\": \"Paul\"\n" + + " }, {\n" + + " \"id\": 8,\n" + + " \"name\": \"Peter\"\n" + + " }]\n" + + " },\n" + + " \"response\": {\n" + + " \"body\": [{\n" + + " \"name\": \"Paul\"\n" + + " }, {\n" + + " \"name\": \"Peter\"\n" + + " }]\n" + + " }\n" + + "}"; + + // when + final EndpointDto endpointDto = new EndpointDto(endpoint, new FromJsonStringToObjectConverterImpl()); + final String json = new Gson().toJson(endpointDto); + + // then + JSONAssert.assertEquals(expectedJson, json, false); + } + +} diff --git a/src/test/java/br/com/concrete/mock/generic/mapper/EndpointMapperTest.java b/src/test/java/br/com/concrete/mock/generic/mapper/EndpointMapperTest.java new file mode 100755 index 0000000..7447bcc --- /dev/null +++ b/src/test/java/br/com/concrete/mock/generic/mapper/EndpointMapperTest.java @@ -0,0 +1,69 @@ +package br.com.concrete.mock.generic.mapper; + +import br.com.concrete.mock.infra.component.file.FileJsonReader; +import br.com.concrete.mock.generic.model.Endpoint; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.bind.annotation.RequestMethod; + +import java.io.IOException; +import java.net.URL; +import java.util.Optional; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.when; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class EndpointMapperTest { + + @Value("${file.base}") + private String fileBase; + @Value("${file.extension}") + private String fileExtension; + + @InjectMocks + private EndpointMapper endpointMapper; + + @Mock + private FileJsonReader fileJsonReader; + + private URL resource; + + @Before + public void init() { + this.resource = getClass().getClassLoader().getResource(fileBase.concat("/get/")); + } + + @Test + public void shouldFileExistsInTest() { + assertNotNull(resource); + assertNotNull(resource.getFile()); + } + + @Test + public void shouldMapFromResponse() throws IOException { + // given + final RequestMethod requestMethod = RequestMethod.GET; + final String requestUrl = "person/11"; + final String basePath = resource.getFile() + requestUrl; + final String fileNameResponse = basePath + "/my-mock" + fileExtension; + + // when + when(fileJsonReader.getJsonByFileName(fileNameResponse)).thenReturn(Optional.of("{\"response\":{\"body\":{\"age\": 10}}}")); + + final Optional endpointMock = endpointMapper.mapper(requestMethod, requestUrl, fileNameResponse); + + // then + assertNotNull(endpointMock); + assertTrue(endpointMock.isPresent()); + } + +} diff --git a/src/test/java/br/com/concrete/mock/generic/mapper/HeaderMapperTest.java b/src/test/java/br/com/concrete/mock/generic/mapper/HeaderMapperTest.java new file mode 100755 index 0000000..3ee0c9a --- /dev/null +++ b/src/test/java/br/com/concrete/mock/generic/mapper/HeaderMapperTest.java @@ -0,0 +1,38 @@ +package br.com.concrete.mock.generic.mapper; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.http.HttpHeaders; +import org.springframework.mock.web.MockHttpServletRequest; + +import java.util.Optional; + +import static org.junit.Assert.*; + +@RunWith(MockitoJUnitRunner.class) +public class HeaderMapperTest { + + @InjectMocks + private HeaderMapper headerMapper; + + @Test + public void shouldConvertHeader() { + // given + final String key = "keyHeader"; + final String value = "valueHeader"; + final MockHttpServletRequest httpServletRequest = new MockHttpServletRequest(); + httpServletRequest.addHeader(key, value); + + // when + final Optional result = headerMapper.mapper(httpServletRequest); + + // then + assertTrue(result.isPresent()); + assertTrue(result.get().containsKey(key)); + assertFalse(result.get().get(key).isEmpty()); + assertEquals(value, result.get().get(key).get(0)); + } + +} diff --git a/src/test/java/br/com/concrete/mock/generic/mapper/QueryMapperTest.java b/src/test/java/br/com/concrete/mock/generic/mapper/QueryMapperTest.java new file mode 100755 index 0000000..6a59266 --- /dev/null +++ b/src/test/java/br/com/concrete/mock/generic/mapper/QueryMapperTest.java @@ -0,0 +1,77 @@ +package br.com.concrete.mock.generic.mapper; + +import com.google.common.collect.ImmutableMap; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.runners.MockitoJUnitRunner; + +import java.util.Map; +import java.util.Optional; + +import static org.junit.Assert.*; + +@RunWith(MockitoJUnitRunner.class) +public class QueryMapperTest { + + @InjectMocks + private QueryMapper queryMapper; + + @Test + public void shouldConvertNullQuery() { + // given + final String queryRequest = null; + + // when + final Optional> result = queryMapper.mapper(queryRequest); + + // then + assertFalse(result.isPresent()); + } + + @Test + public void shouldConvertEmptyQuery() { + // given + final String queryRequest = ""; + + // when + final Optional> result = queryMapper.mapper(queryRequest); + + // then + assertFalse(result.isPresent()); + } + + @Test + public void shouldConvertQueryWith1Parameter() { + // given + final String queryRequest = "name=Paul"; + final Map expectedMap = ImmutableMap.builder() + .put("name", "Paul") + .build(); + + // when + final Optional> result = queryMapper.mapper(queryRequest); + + // then + assertTrue(result.isPresent()); + assertEquals(expectedMap, result.get()); + } + + @Test + public void shouldConvertQueryWith2Parameter() { + // given + final String queryRequest = "name=Paul&age=10"; + final Map expectedMap = ImmutableMap.builder() + .put("name", "Paul") + .put("age", "10") + .build(); + + // when + final Optional> result = queryMapper.mapper(queryRequest); + + // then + assertTrue(result.isPresent()); + assertEquals(expectedMap, result.get()); + } + +} diff --git a/src/test/java/br/com/concrete/mock/generic/mapper/ResponseDtoTest.java b/src/test/java/br/com/concrete/mock/generic/mapper/ResponseDtoTest.java new file mode 100755 index 0000000..fbeda82 --- /dev/null +++ b/src/test/java/br/com/concrete/mock/generic/mapper/ResponseDtoTest.java @@ -0,0 +1,82 @@ +package br.com.concrete.mock.generic.mapper; + +import br.com.concrete.mock.generic.model.Response; +import br.com.concrete.mock.generic.model.template.ResponseTemplate; +import br.com.concrete.mock.infra.component.impl.FromJsonStringToObjectConverterImpl; +import br.com.six2six.fixturefactory.Fixture; +import br.com.six2six.fixturefactory.loader.FixtureFactoryLoader; +import com.google.gson.Gson; +import org.json.JSONException; +import org.junit.BeforeClass; +import org.junit.Test; +import org.skyscreamer.jsonassert.JSONAssert; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +public class ResponseDtoTest { + + @BeforeClass + public static void initClass() { + FixtureFactoryLoader.loadTemplates("br.com.concrete.mock.generic.model.template"); + } + + @Test + public void shouldConvertFromJsonAnObject() throws JSONException { + // given + final String json = "{ \"body\": { \"tt\": \"789\" } }"; + + // when + final ResponseDto responseDto = new Gson().fromJson(json, ResponseDto.class); + final Response response = responseDto.toModel(); + + // then + assertNotNull(response); + assertNotNull(response.getBody()); + JSONAssert.assertEquals("{ \"tt\": \"789\" }", response.getBody(), false); + } + + @Test + public void shouldConvertFromJsonAListOfObjects() { + // given + final String json = "{ \"body\": [{ \"age\": 10 }, { \"age\": 11 }] }"; + + // when + final ResponseDto responseDto = new Gson().fromJson(json, ResponseDto.class); + final Response response = responseDto.toModel(); + + // then + assertNotNull(response); + assertNotNull(response.getBody()); + assertEquals("[{\"age\":10},{\"age\":11}]", response.getBody()); + } + + @Test + public void shouldBeSerializableWhenIsObject() throws JSONException { + // given + final Response model = Fixture.from(Response.class).gimme(ResponseTemplate.VALID_FULL); + final String expectedJson = "{ \"body\": {\"name\": \"Paul\"} }"; + + // when + final ResponseDto modelDto = new ResponseDto(model, new FromJsonStringToObjectConverterImpl()); + final String json = new Gson().toJson(modelDto); + + // then + JSONAssert.assertEquals(expectedJson, json, false); + } + + @Test + public void shouldBeSerializableWhenIsArray() throws JSONException { + // given + final Response model = Fixture.from(Response.class).gimme(ResponseTemplate.VALID_WITH_LIST); + final String expectedJson = "{ \"body\": [ {\"name\": \"Paul\"}, {\"name\": \"Peter\"} ] }"; + + // when + final ResponseDto modelDto = new ResponseDto(model, new FromJsonStringToObjectConverterImpl()); + final String json = new Gson().toJson(modelDto); + + // then + JSONAssert.assertEquals(expectedJson, json, false); + } + +} diff --git a/src/test/java/br/com/concrete/mock/generic/model/RequestTest.java b/src/test/java/br/com/concrete/mock/generic/model/RequestTest.java new file mode 100755 index 0000000..b039b0b --- /dev/null +++ b/src/test/java/br/com/concrete/mock/generic/model/RequestTest.java @@ -0,0 +1,79 @@ +package br.com.concrete.mock.generic.model; + +import br.com.concrete.mock.generic.model.template.RequestTemplate; +import br.com.six2six.fixturefactory.Fixture; +import br.com.six2six.fixturefactory.loader.FixtureFactoryLoader; +import org.junit.BeforeClass; +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class RequestTest { + + @BeforeClass + public static void initClass() { + FixtureFactoryLoader.loadTemplates("br.com.concrete.mock.generic.model.template"); + } + + @Test + public void shouldBeValidWhenAllFieldsAreFilled() { + // given + final Request request = Fixture.from(Request.class).gimme(RequestTemplate.VALID_FULL); + + // when + final Boolean valid = request.isValid(); + + // then + assertTrue(valid); + } + + @Test + public void shouldBeValidWhenAllFieldsAreBody() { + // given + final Request request = Fixture.from(Request.class).gimme(RequestTemplate.VALID_WITH_LIST); + + // when + final Boolean valid = request.isValid(); + + // then + assertTrue(valid); + } + + @Test + public void shouldBeValidWhenAllFieldsAreQuery() { + // given + final Request request = Fixture.from(Request.class).gimme(RequestTemplate.VALID_QUERY_AGE25); + + // when + final Boolean valid = request.isValid(); + + // then + assertTrue(valid); + } + + @Test + public void shouldBeValidWhenAllFieldsAreHeaders() { + // given + final Request request = Fixture.from(Request.class).gimme(RequestTemplate.VALID_WITH_HEADERS); + + // when + final Boolean valid = request.isValid(); + + // then + assertTrue(valid); + } + + @Test + public void shouldNotValid() { + // given + final Request request = Fixture.from(Request.class).gimme(RequestTemplate.VALID_EMPTY); + + // when + final Boolean valid = request.isValid(); + + // then + assertFalse(valid); + } + +} diff --git a/src/test/java/br/com/concrete/mock/generic/model/template/EndpointTemplate.java b/src/test/java/br/com/concrete/mock/generic/model/template/EndpointTemplate.java new file mode 100755 index 0000000..d7a5319 --- /dev/null +++ b/src/test/java/br/com/concrete/mock/generic/model/template/EndpointTemplate.java @@ -0,0 +1,60 @@ +package br.com.concrete.mock.generic.model.template; + +import br.com.concrete.mock.generic.model.Endpoint; +import br.com.concrete.mock.generic.model.Request; +import br.com.concrete.mock.generic.model.Response; +import br.com.six2six.fixturefactory.Rule; +import br.com.six2six.fixturefactory.loader.TemplateLoader; + +import static br.com.six2six.fixturefactory.Fixture.of; + +import java.util.Optional; + +public class EndpointTemplate implements TemplateLoader { + + private static final String VALID_WITHOUT_HTTPSTATUS = "validFull"; + private static final String NOT_VALID = "notValid"; + public static final String VALID = "valid"; + public static final String VALID_FULL = "validFull"; + public static final String VALID_WITH_LIST = "validWithList"; + public static final String VALID_WITH_REQUEST_QUERY_AGE10 = "validWithRequestQueryAge10"; + public static final String VALID_WITH_REQUEST_BODY_ID6 = "validWithRequestBodyId6"; + + + @Override + public void load() { + of(Endpoint.class) + .addTemplate(VALID, new Rule() {{ + add("id", Optional.empty()); + add("request", one(Request.class, RequestTemplate.VALID_EMPTY)); + add("response", one(Response.class, ResponseTemplate.VALID)); + }}) + .addTemplate(VALID_FULL) + .inherits(VALID, new Rule() {{ + add("request", one(Request.class, RequestTemplate.VALID_FULL)); + add("response", one(Response.class, ResponseTemplate.VALID_FULL)); + }}) + .addTemplate(VALID_WITHOUT_HTTPSTATUS) + .inherits(VALID_FULL, new Rule() {{ + add("response", one(Response.class, ResponseTemplate.VALID)); + }}) + .addTemplate(VALID_WITH_LIST) + .inherits(VALID_FULL, new Rule() {{ + add("request", one(Request.class, RequestTemplate.VALID_WITH_LIST)); + add("response", one(Response.class, ResponseTemplate.VALID_WITH_LIST)); + }}) + .addTemplate(VALID_WITH_REQUEST_QUERY_AGE10) + .inherits(VALID, new Rule() {{ + add("request", one(Request.class, RequestTemplate.VALID_QUERY_AGE10)); + }}) + .addTemplate(VALID_WITH_REQUEST_BODY_ID6) + .inherits(VALID, new Rule() {{ + add("request", one(Request.class, RequestTemplate.VALID_BODY_ID6)); + }}) + .addTemplate(NOT_VALID) + .inherits(VALID, new Rule() {{ + add("response", one(Response.class, ResponseTemplate.NOT_VALID)); + }}); + } + +} diff --git a/src/test/java/br/com/concrete/mock/generic/model/template/RequestTemplate.java b/src/test/java/br/com/concrete/mock/generic/model/template/RequestTemplate.java new file mode 100755 index 0000000..6dbbfa9 --- /dev/null +++ b/src/test/java/br/com/concrete/mock/generic/model/template/RequestTemplate.java @@ -0,0 +1,71 @@ +package br.com.concrete.mock.generic.model.template; + +import br.com.concrete.mock.generic.model.Request; +import br.com.six2six.fixturefactory.Fixture; +import br.com.six2six.fixturefactory.Rule; +import br.com.six2six.fixturefactory.loader.TemplateLoader; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.RequestMethod; + +import java.util.Optional; + +public class RequestTemplate implements TemplateLoader { + + public static final String VALID_EMPTY = "validEmpty"; + public static final String VALID_WITH_HEADERS = "validWithHeaders"; + public static final String VALID_FULL = "validFull"; + public static final String VALID_WITH_LIST = "validWithList"; + public static final String VALID_QUERY_AGE10 = "validQueryAge10"; + public static final String VALID_QUERY_AGE25 = "validQueryAge25"; + public static final String VALID_BODY_ID6 = "validBodyId6"; + public static final String VALID_BODY_ID7 = "validBodyId7"; + + @Override + public void load() { + Fixture.of(Request.class) + .addTemplate(VALID_EMPTY, new Rule() {{ + add("method", RequestMethod.GET); + add("uri", "/person/11"); + add("headers", Optional.empty()); + add("query", Optional.empty()); + add("body", Optional.empty()); + }}) + .addTemplate(VALID_WITH_HEADERS, new Rule() {{ + final HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setAccept(ImmutableList.builder().add(MediaType.APPLICATION_JSON).build()); + + add("headers", Optional.of(httpHeaders)); + }}) + .addTemplate(VALID_FULL) + .inherits(VALID_WITH_HEADERS, new Rule() {{ + add("method", RequestMethod.POST); + add("uri", "/person/11"); + add("query", Optional.of(ImmutableMap.builder().put("text", "abc").put("age", 10).build())); + add("body", Optional.of("{\"id\": 7, \"name\": \"Paul\" }")); + }}) + .addTemplate(VALID_WITH_LIST) + .inherits(VALID_FULL, new Rule() {{ + add("body", Optional.of(" [{\"id\": 7, \"name\": \"Paul\" },{\"id\": 8, \"name\": \"Peter\" }] ")); + }}) + .addTemplate(VALID_QUERY_AGE10) + .inherits(VALID_EMPTY, new Rule() {{ + add("query", Optional.of(ImmutableMap.builder().put("age", "10").build())); + }}) + .addTemplate(VALID_QUERY_AGE25) + .inherits(VALID_EMPTY, new Rule() {{ + add("query", Optional.of(ImmutableMap.builder().put("age", "25").build())); + }}) + .addTemplate(VALID_BODY_ID6) + .inherits(VALID_EMPTY, new Rule() {{ + add("body", Optional.of("{\"id\": 6 }")); + }}) + .addTemplate(VALID_BODY_ID7) + .inherits(VALID_EMPTY, new Rule() {{ + add("body", Optional.of("{\"id\": 7 }")); + }}); + } + +} diff --git a/src/test/java/br/com/concrete/mock/generic/model/template/ResponseTemplate.java b/src/test/java/br/com/concrete/mock/generic/model/template/ResponseTemplate.java new file mode 100755 index 0000000..b31a8e7 --- /dev/null +++ b/src/test/java/br/com/concrete/mock/generic/model/template/ResponseTemplate.java @@ -0,0 +1,40 @@ +package br.com.concrete.mock.generic.model.template; + +import br.com.concrete.mock.generic.model.Response; +import br.com.six2six.fixturefactory.Fixture; +import br.com.six2six.fixturefactory.Rule; +import br.com.six2six.fixturefactory.loader.TemplateLoader; +import org.springframework.http.HttpStatus; + +import java.util.Optional; + +public class ResponseTemplate implements TemplateLoader { + + public static final String VALID = "valid"; + public static final String VALID_FULL = "valid"; + public static final String VALID_WITH_LIST = "validWithList"; + public static final String NOT_VALID = "notValid"; + + @Override + public void load() { + Fixture.of(Response.class) + .addTemplate(VALID, new Rule() {{ + add("body", "{\"name\": \"Paul\"}"); + add("httpStatus", Optional.empty()); + }}) + .addTemplate(VALID_FULL) + .inherits(VALID, new Rule() {{ + add("body", "{\"name\": \"Paul\"}"); + add("httpStatus", Optional.of(HttpStatus.CREATED)); + }}) + .addTemplate(VALID_WITH_LIST) + .inherits(VALID_FULL, new Rule() {{ + add("body", " [ {\"name\": \"Paul\"}, {\"name\": \"Peter\"} ] "); + add("httpStatus", Optional.empty()); + }}) + .addTemplate(NOT_VALID, new Rule() {{ + add("body", "{\"codigo\": \"422\", \"mensagem\": \"Erro :(\"}"); + add("httpStatus", Optional.empty()); + }}); + } +} diff --git a/src/test/java/br/com/concrete/mock/generic/repository/impl/EndpointFileFilterBodyTest.java b/src/test/java/br/com/concrete/mock/generic/repository/impl/EndpointFileFilterBodyTest.java new file mode 100755 index 0000000..0a98b87 --- /dev/null +++ b/src/test/java/br/com/concrete/mock/generic/repository/impl/EndpointFileFilterBodyTest.java @@ -0,0 +1,70 @@ +package br.com.concrete.mock.generic.repository.impl; + +import br.com.concrete.mock.generic.model.Endpoint; +import br.com.concrete.mock.generic.model.Request; +import br.com.concrete.mock.generic.model.template.EndpointTemplate; +import br.com.concrete.mock.generic.model.template.RequestTemplate; +import br.com.six2six.fixturefactory.Fixture; +import br.com.six2six.fixturefactory.loader.FixtureFactoryLoader; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class EndpointFileFilterBodyTest { + + @Autowired + private EndpointFileFilterBody endpointFileFilter; + + @BeforeClass + public static void initClass() { + FixtureFactoryLoader.loadTemplates("br.com.concrete.mock.generic.model.template"); + } + + @Test + public void shouldBeEquivalentWhenThereIsNoFields() { + // given + final Endpoint endpoint = Fixture.from(Endpoint.class).gimme(EndpointTemplate.VALID); + final Request request = Fixture.from(Request.class).gimme(RequestTemplate.VALID_EMPTY); + + // when + final Boolean result = endpointFileFilter.apply(endpoint, request.getBody()); + + // then + assertTrue(result); + } + + @Test + public void shouldBeEquivalentWhenFieldsAreEqual() { + // given + final Endpoint endpoint = Fixture.from(Endpoint.class).gimme(EndpointTemplate.VALID_WITH_REQUEST_BODY_ID6); + final Request request = Fixture.from(Request.class).gimme(RequestTemplate.VALID_BODY_ID6); + + // when + final Boolean result = endpointFileFilter.apply(endpoint, request.getBody()); + + // then + assertTrue(result); + } + + @Test + public void shouldNotBeEquivalentWhenOneFieldIsNotEqual() { + // given + final Endpoint endpoint = Fixture.from(Endpoint.class).gimme(EndpointTemplate.VALID_WITH_REQUEST_BODY_ID6); + final Request request = Fixture.from(Request.class).gimme(RequestTemplate.VALID_BODY_ID7); + + // when + final Boolean result = endpointFileFilter.apply(endpoint, request.getBody()); + + // then + assertFalse(result); + } + +} diff --git a/src/test/java/br/com/concrete/mock/generic/repository/impl/EndpointFileFilterQueryTest.java b/src/test/java/br/com/concrete/mock/generic/repository/impl/EndpointFileFilterQueryTest.java new file mode 100755 index 0000000..06f0fbc --- /dev/null +++ b/src/test/java/br/com/concrete/mock/generic/repository/impl/EndpointFileFilterQueryTest.java @@ -0,0 +1,70 @@ +package br.com.concrete.mock.generic.repository.impl; + +import br.com.concrete.mock.generic.model.Endpoint; +import br.com.concrete.mock.generic.model.Request; +import br.com.concrete.mock.generic.model.template.EndpointTemplate; +import br.com.concrete.mock.generic.model.template.RequestTemplate; +import br.com.six2six.fixturefactory.Fixture; +import br.com.six2six.fixturefactory.loader.FixtureFactoryLoader; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class EndpointFileFilterQueryTest { + + @Autowired + private EndpointFileFilterQuery endpointMockFileFilter; + + @BeforeClass + public static void initClass() { + FixtureFactoryLoader.loadTemplates("br.com.concrete.mock.generic.model.template"); + } + + @Test + public void shouldBeEquivalentWhenThereIsNoFields() { + // given + final Endpoint endpoint = Fixture.from(Endpoint.class).gimme(EndpointTemplate.VALID); + final Request request = Fixture.from(Request.class).gimme(RequestTemplate.VALID_EMPTY); + + // when + final Boolean result = endpointMockFileFilter.apply(endpoint, request.getQuery()); + + // then + assertTrue(result); + } + + @Test + public void shouldBeEquivalentWhenFieldsAreEqual() { + // given + final Endpoint endpoint = Fixture.from(Endpoint.class).gimme(EndpointTemplate.VALID_WITH_REQUEST_QUERY_AGE10); + final Request request = Fixture.from(Request.class).gimme(RequestTemplate.VALID_QUERY_AGE10); + + // when + final Boolean result = endpointMockFileFilter.apply(endpoint, request.getQuery()); + + // then + assertTrue(result); + } + + @Test + public void shouldNotBeEquivalentWhenOneFieldIsNotEqual() { + // given + final Endpoint endpoint = Fixture.from(Endpoint.class).gimme(EndpointTemplate.VALID_WITH_REQUEST_QUERY_AGE10); + final Request request = Fixture.from(Request.class).gimme(RequestTemplate.VALID_QUERY_AGE25); + + // when + final Boolean result = endpointMockFileFilter.apply(endpoint, request.getQuery()); + + // then + assertFalse(result); + } + +} diff --git a/src/test/java/br/com/concrete/mock/generic/repository/impl/EndpointRepositoryModelTest.java b/src/test/java/br/com/concrete/mock/generic/repository/impl/EndpointRepositoryModelTest.java new file mode 100755 index 0000000..4f882ce --- /dev/null +++ b/src/test/java/br/com/concrete/mock/generic/repository/impl/EndpointRepositoryModelTest.java @@ -0,0 +1,131 @@ +package br.com.concrete.mock.generic.repository.impl; + +import br.com.concrete.mock.generic.mapper.EndpointMapper; +import br.com.concrete.mock.generic.model.Endpoint; +import br.com.concrete.mock.generic.model.Request; +import br.com.concrete.mock.generic.model.template.EndpointTemplate; +import br.com.concrete.mock.infra.component.file.BaseFileNameBuilderModel; +import br.com.concrete.mock.infra.property.FileExtensionProperty; +import br.com.concrete.mock.infra.property.FileProperty; +import br.com.six2six.fixturefactory.Fixture; +import br.com.six2six.fixturefactory.loader.FixtureFactoryLoader; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.bind.annotation.RequestMethod; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Paths; +import java.util.Collection; +import java.util.Optional; + +import static org.junit.Assert.*; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class EndpointRepositoryModelTest { + + private static final String NAME = "/get/"; + @InjectMocks + private EndpointRepositoryModel endpointRepositoryModel; + @Mock + private FileProperty fileProperty; + @Mock + private FileExtensionProperty fileExtensionProperty; + @Mock + private EndpointMapper endpointMapper; + @Mock + private BaseFileNameBuilderModel baseFileNameBuilder; + @Mock + private EndpointFileFilterRequest endpointMockFileFilterRequest; + + private File resource; + + @Value("${file.base}") + private String fileBase; + @Value("${file.extension}") + private String fileExtension; + + @BeforeClass + public static void initClass() { + FixtureFactoryLoader.loadTemplates("br.com.concrete.mock.generic.model.template"); + } + + @Before + public void init() throws URISyntaxException { + this.resource = Paths.get(getClass().getClassLoader().getResource(fileBase).toURI()).toFile(); + } + + @Test + public void shouldFileExistsInTest() { + assertNotNull(resource); + } + + @Test + public void shouldFindSomeResponse() throws IOException { + // given + final RequestMethod requestMethod = RequestMethod.GET; + final String requestUrl = "person/11"; + final String basePath = Paths.get(resource.getAbsolutePath(), requestMethod.toString().toLowerCase(), requestUrl).toAbsolutePath().toString(); + final Optional endpoint = Optional.of(Fixture.from(Endpoint.class).gimme(EndpointTemplate.VALID)); + + // when + when(endpointMapper.mapper(any(), any(), any())).thenReturn(endpoint); + when(fileExtensionProperty.getFileExtension()).thenReturn(fileExtension); + when(baseFileNameBuilder.buildPath(any(), any())).thenReturn(basePath); + + final Collection mocks = endpointRepositoryModel.getByMethodAndUri(requestMethod, requestUrl); + + // then + assertNotNull(mocks); + assertFalse(mocks.isEmpty()); + } + + @Test + public void shouldNotFindResponseWhenDoNotExists() throws IOException { + // given + final RequestMethod requestMethod = RequestMethod.GET; + final String requestUrl = "/person/66"; + final String basePath = Paths.get(resource.getAbsolutePath(), requestUrl).toAbsolutePath().toString(); + + // when + when(baseFileNameBuilder.buildPath(any(), any())).thenReturn(basePath); + + final Collection mocks = endpointRepositoryModel.getByMethodAndUri(requestMethod, requestUrl); + + // then + assertNotNull(mocks); + assertTrue(mocks.isEmpty()); + } + + @Test + public void shouldFilterByMethodAndUriAndQuery() { + // given + final String requestUrl = "person/11"; + final String basePath = Paths.get(resource.getAbsolutePath(), requestUrl).toAbsolutePath().toString(); + final Optional result = Optional.empty(); + + // when + when(endpointMapper.mapper(any(), any(), any())).thenReturn(result); + when(fileExtensionProperty.getFileExtension()).thenReturn(fileExtension); + when(baseFileNameBuilder.buildPath(any(), any())).thenReturn(basePath); + when(endpointMockFileFilterRequest.apply(any(), any())).thenReturn(true); + + final Optional endpointMock = endpointRepositoryModel.getByMethodAndRequest(mock(Request.class)); + + // then + assertEquals(result, endpointMock); + } + +} diff --git a/src/test/java/br/com/concrete/mock/generic/service/impl/EndpointBackupServiceFileIntegrarionTest.java b/src/test/java/br/com/concrete/mock/generic/service/impl/EndpointBackupServiceFileIntegrarionTest.java new file mode 100755 index 0000000..cdb265a --- /dev/null +++ b/src/test/java/br/com/concrete/mock/generic/service/impl/EndpointBackupServiceFileIntegrarionTest.java @@ -0,0 +1,125 @@ +package br.com.concrete.mock.generic.service.impl; + +import br.com.concrete.mock.generic.model.Endpoint; +import br.com.concrete.mock.generic.model.template.EndpointTemplate; +import br.com.concrete.mock.generic.service.EndpointBackupService; +import br.com.concrete.mock.infra.property.FileExtensionProperty; +import br.com.concrete.mock.infra.property.FileProperty; +import br.com.six2six.fixturefactory.Fixture; +import br.com.six2six.fixturefactory.loader.FixtureFactoryLoader; +import org.json.JSONException; +import org.junit.*; +import org.junit.runner.RunWith; +import org.skyscreamer.jsonassert.JSONAssert; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.web.client.TestRestTemplate; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.Paths; + +import static org.junit.Assert.*; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class EndpointBackupServiceFileIntegrarionTest { + + private static final String CONFIGURATION_CAPTURE_STATE = "/configuration/capture-state"; + + @Autowired + private TestRestTemplate restTemplate; + + @Autowired + @Qualifier("FilePropertyBackup") + private FileProperty fileProperty; + + @Autowired + private FileExtensionProperty fileExtensionProperty; + + @Autowired + private EndpointBackupService endpointBackupService; + + @Value("${file.base}") + private String fileBase; + + private String responseJson; + private String uri; + private String baseName; + private String fileName; + private URL resource; + + @BeforeClass + public static void initClass() { + FixtureFactoryLoader.loadTemplates("br.com.concrete.mock.generic.model.template"); + } + + @Before + public void init() throws IOException { + final Endpoint endpoint = Fixture.from(Endpoint.class).gimme(EndpointTemplate.VALID); + responseJson = endpoint.getResponse().getBody(); + + uri = endpoint.getRequest().getUri(); + String backupPathName = new File("").getAbsolutePath() + "/" + fileProperty.getFileBase(); + this.baseName = backupPathName + "/"; + fileName = baseName + endpoint.getRequest().getMethod().name().toLowerCase() + uri + "/1" + fileExtensionProperty.getFileExtension(); + + deleteBackupFolder(); + this.resource = getClass().getClassLoader().getResource(fileBase); + } + + @Test + public void shouldFileExistsInTest() { + assertNotNull(resource); + assertNotNull(resource.getFile()); + } + + public void deleteBackupFolder() throws IOException { + endpointBackupService.cleanAllBackupData(); + } + + @After + public void setupRestore() throws IOException { + restTemplate.delete(CONFIGURATION_CAPTURE_STATE + "/disable"); // change setup + deleteBackupFolder(); + } + + @Test(timeout = 2000) + @Ignore + public void shouldDoARequestAndDoNotCaptureBackupWhenIsNotConfigured() throws IOException, JSONException { + // when + final ResponseEntity response = restTemplate.getForEntity(uri, String.class); + + // then + assertNotNull(response); + assertEquals(HttpStatus.OK, response.getStatusCode()); + JSONAssert.assertEquals(responseJson, response.getBody(), false); + assertFalse("File shouldn't exists in: " + fileName, Files.exists(Paths.get(fileName))); + } + + @Test//(timeout = 2000) + public void shouldDoARequestAndCaptureBackupWhenIsConfigured() throws IOException, JSONException { + // given + final String path = "/v2/5928a3aa0f0000140538834a"; + final String name = baseName + "get" + path; + + // when + restTemplate.postForEntity(CONFIGURATION_CAPTURE_STATE + "/enable", null, Object.class); // change setup + + final ResponseEntity response = restTemplate.getForEntity(path, String.class); + + // then + assertNotNull(response); + assertEquals(HttpStatus.OK, response.getStatusCode()); + JSONAssert.assertEquals("{ \"response\": { \"httpStatus\": 204 }}", response.getBody(), false); + assertTrue("File not exists in: " + name, Files.exists(Paths.get(name))); + } + +} diff --git a/src/test/java/br/com/concrete/mock/generic/service/impl/EndpointBackupServiceFileTest.java b/src/test/java/br/com/concrete/mock/generic/service/impl/EndpointBackupServiceFileTest.java new file mode 100755 index 0000000..ffa5166 --- /dev/null +++ b/src/test/java/br/com/concrete/mock/generic/service/impl/EndpointBackupServiceFileTest.java @@ -0,0 +1,126 @@ +package br.com.concrete.mock.generic.service.impl; + +import br.com.concrete.mock.generic.model.Endpoint; +import br.com.concrete.mock.generic.model.Request; +import br.com.concrete.mock.generic.model.template.EndpointTemplate; +import br.com.concrete.mock.generic.repository.EndpointRepository; +import br.com.concrete.mock.infra.component.FromJsonStringToObjectConverter; +import br.com.concrete.mock.infra.component.file.BaseFileNameBuilderModel; +import br.com.concrete.mock.infra.component.file.FileNameGenerator; +import br.com.concrete.mock.infra.component.impl.JsonFormatterPretty; +import br.com.concrete.mock.infra.property.FileExtensionProperty; +import br.com.concrete.mock.infra.property.FileProperty; +import br.com.six2six.fixturefactory.Fixture; +import br.com.six2six.fixturefactory.loader.FixtureFactoryLoader; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.springframework.util.FileSystemUtils; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Optional; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.any; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class EndpointBackupServiceFileTest { + private static final String BACKUP_TEMP = "backup-temp/"; + + @InjectMocks + private EndpointBackupServiceFile endpointBackupServiceFile; + + @Mock + private FileProperty fileProperty; + @Mock + private FileExtensionProperty fileExtensionProperty; + @Mock + private BaseFileNameBuilderModel baseFileNameBuilder; + @Mock + private FileNameGenerator fileNameGenerator; + @Mock(answer = Answers.CALLS_REAL_METHODS) + private FromJsonStringToObjectConverter fromJsonStringToObjectConverter; + @Mock(answer = Answers.CALLS_REAL_METHODS) + private JsonFormatterPretty jsonFormatterPretty; + @Mock + private EndpointRepository endpointRepository; + + @BeforeClass + public static void initClass() { + FixtureFactoryLoader.loadTemplates("br.com.concrete.mock.generic.model.template"); + } + + @Test + public void shouldDoBackup() throws IOException { + // given + final Endpoint endpoint = Fixture.from(Endpoint.class).gimme(EndpointTemplate.VALID); + final String pathName = BACKUP_TEMP + endpoint.getRequest().getUri().concat("/"); + final String fileName = "1"; + final String fileExtension = ".json"; + final Path path = Paths.get(pathName.concat("/").concat(fileName).concat(fileExtension)); + + // when + if (Files.exists(path)) Files.delete(path); + when(endpointRepository.getByMethodAndRequest(any(Request.class))).thenReturn(Optional.empty()); + when(baseFileNameBuilder.buildPath(anyString(), anyString(), anyString())).thenReturn(pathName); + when(fileExtensionProperty.getFileExtension()).thenReturn(fileExtension); + when(fileNameGenerator.fromPath(anyString())).thenReturn(fileName); + + endpointBackupServiceFile.doBackup(endpoint); + + // then + assertTrue(Files.exists(path)); + } + + @Test + @Ignore + public void shouldRemoveEverithingImportantInsidePath() throws IOException { + // given + final String baseNameFilesToRemove = BACKUP_TEMP + "data/files"; + final Path pathsToRemove = Paths.get(baseNameFilesToRemove + "/file1.json"); + + final String baseNameFilesToNotRemove = BACKUP_TEMP + ".keep-folder"; + final Path pathsDoNotRemove = Paths.get(baseNameFilesToNotRemove + "/fileDotNotRemove1.json"); + + final Path keepFilePath = Paths.get(baseNameFilesToNotRemove + ".keep-file"); + + // before + if (Files.exists(pathsToRemove)) Files.delete(pathsToRemove); + if (Files.exists(pathsDoNotRemove)) Files.delete(pathsDoNotRemove); + Files.deleteIfExists(keepFilePath); + + // when + when(fileProperty.getFileBase()).thenReturn(BACKUP_TEMP); + + Files.createDirectories(Paths.get(baseNameFilesToRemove)); + Files.createFile(pathsToRemove); + + Files.createDirectories(Paths.get(baseNameFilesToNotRemove)); + Files.createFile(pathsDoNotRemove); + + assertTrue(Files.exists(pathsToRemove)); + assertTrue(Files.exists(pathsDoNotRemove)); + + endpointBackupServiceFile.cleanAllBackupData(); + + // then + assertFalse(Files.exists(pathsToRemove)); + assertTrue(Files.exists(pathsDoNotRemove)); + + // after + FileSystemUtils.deleteRecursively(new File(baseNameFilesToNotRemove)); // dangerous + } + +} diff --git a/src/test/java/br/com/concrete/mock/infra/component/ApiPropertyTest.java b/src/test/java/br/com/concrete/mock/infra/component/ApiPropertyTest.java new file mode 100755 index 0000000..c381d1e --- /dev/null +++ b/src/test/java/br/com/concrete/mock/infra/component/ApiPropertyTest.java @@ -0,0 +1,64 @@ +package br.com.concrete.mock.infra.component; + +import br.com.concrete.mock.infra.model.DefaultHeader; +import br.com.concrete.mock.infra.model.UriConfiguration; +import br.com.concrete.mock.infra.property.ApiProperty; +import com.google.common.collect.ImmutableList; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import java.util.List; +import java.util.regex.Pattern; + +import static org.junit.Assert.*; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class ApiPropertyTest { + + @Autowired + private ApiProperty apiProperty; + + @Test + public void shouldLoadAcceptHeaders() { + // when + final List acceptedHeaders = apiProperty.getAcceptedHeaders(); + + // then + assertNotNull(acceptedHeaders); + assertFalse(acceptedHeaders.isEmpty()); + } + + @Test + public void shouldLoadDefaultHeaders() { + // given + final ImmutableList headers = ImmutableList.builder().add("Connection", "Keep-Alive").build(); + final DefaultHeader defaultHeader = new DefaultHeader("Access-Control-Allow-Origin", headers); + + // when + final List defaultHeaders = apiProperty.getDefaultHeaders(); + + // then + assertNotNull(defaultHeaders); + assertFalse(defaultHeaders.isEmpty()); + assertEquals(defaultHeader, defaultHeaders.get(0)); + } + + @Test + public void shouldLoadUriConfigurations() { + // given + final UriConfiguration uriConfiguration = new UriConfiguration("http://www.mocky.io", Pattern.compile("/v2/57fbd6280f0000ed154fd470", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE), false); + + // when + final List uriConfigurations = apiProperty.getUriConfigurations(); + + // then + assertNotNull(uriConfigurations); + assertFalse(uriConfigurations.isEmpty()); + assertEquals(uriConfiguration, uriConfigurations.get(0)); + } + +} diff --git a/src/test/java/br/com/concrete/mock/infra/component/CompareJsonTest.java b/src/test/java/br/com/concrete/mock/infra/component/CompareJsonTest.java new file mode 100755 index 0000000..edb8ca7 --- /dev/null +++ b/src/test/java/br/com/concrete/mock/infra/component/CompareJsonTest.java @@ -0,0 +1,105 @@ +package br.com.concrete.mock.infra.component; + +import br.com.concrete.mock.infra.component.impl.ConvertJsonImpl; +import org.junit.Ignore; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import java.io.IOException; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +@RunWith(MockitoJUnitRunner.class) +public class CompareJsonTest { + + @InjectMocks + private CompareJson compareJson; + + @Mock(answer = Answers.CALLS_REAL_METHODS) + private ConvertJsonImpl convertJson; + + @Test + public void shouldBeEquivalentWhenIsEqual() throws IOException { + // given + final String jsonKey = "{\"id\": 10}"; + final String jsonToCompare = "{ \"id\" : 10 } "; + + // when + final Boolean equivalent = compareJson.isEquivalent(jsonKey, jsonToCompare); + + // then + assertTrue(equivalent); + } + + @Test + public void shouldBeEquivalentWhenExistsFieldsIgnoreds() throws IOException { + // given + final String jsonKey = "{\"id\": 10}"; + final String jsonToCompare = "{ \"id\" : 10, \"age\" : 19 } "; + + // when + final Boolean equivalent = compareJson.isEquivalent(jsonKey, jsonToCompare); + + // then + assertTrue(equivalent); + } + + @Test + public void shouldBeEquivalentWhenExistsSubObjects() throws IOException { + // given + final String jsonKey = "{\"id\": 10, \"anotherObject\": { \"number\" : 2}}"; + final String jsonToCompare = "{\"id\": 10, \"anotherObject\": { \"number\" : 2}}"; + + // when + final Boolean equivalent = compareJson.isEquivalent(jsonKey, jsonToCompare); + + // then + assertTrue(equivalent); + } + + @Test + public void shouldNotBeEquivalentWhenValuesAreDifferents() throws IOException { + // given + final String jsonKey = "{ \"id\" : 10, \"age\" : 19 } "; + final String jsonToCompare = "{\"id\": 10}"; + + // when + final Boolean equivalent = compareJson.isEquivalent(jsonKey, jsonToCompare); + + // then + assertFalse(equivalent); + } + + @Test + public void shouldNotBeEquivalentWhenThereIsNoAllFields() throws IOException { + // given + final String jsonKey = "{\"id\": 10}"; + final String jsonToCompare = "{ \"id\" : 12 } "; + + // when + final Boolean equivalent = compareJson.isEquivalent(jsonKey, jsonToCompare); + + // then + assertFalse(equivalent); + } + + @Ignore + @Test + public void shouldNotBeEquivalentWhenExistsDifferentSubObjects() throws IOException { + // given + final String jsonKey = "{\"id\": 10, \"anotherObject\": { \"number\" : 2}}"; + final String jsonToCompare = "{\"id\": 10, \"anotherObject\": { \"number\" : 3}}"; + + // when + final Boolean equivalent = compareJson.isEquivalent(jsonKey, jsonToCompare); + + // then + assertFalse(equivalent); + } + +} diff --git a/src/test/java/br/com/concrete/mock/infra/component/CompareMapTest.java b/src/test/java/br/com/concrete/mock/infra/component/CompareMapTest.java new file mode 100755 index 0000000..db70515 --- /dev/null +++ b/src/test/java/br/com/concrete/mock/infra/component/CompareMapTest.java @@ -0,0 +1,90 @@ +package br.com.concrete.mock.infra.component; + +import com.google.common.collect.ImmutableMap; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.runners.MockitoJUnitRunner; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +@RunWith(MockitoJUnitRunner.class) +public class CompareMapTest { + + @InjectMocks + private CompareMap compareMap; + + @Test + public void shouldBeEqualsWhenCompareValueMaps() { + // given + final ImmutableMap map = ImmutableMap.builder() + .put("name", "Paul") + .build(); + final ImmutableMap mapToCompare = ImmutableMap.builder() + .put("name", "Paul") + .build(); + + // when + final Boolean isEquivalent = compareMap.isEquivalent(map, mapToCompare); + + // then + assertTrue(isEquivalent); + } + + @Test + public void shouldBeEqualsWhenCompareValueMapsWhereHaveMoreAttributesInComparation() { + // given + final ImmutableMap map = ImmutableMap.builder() + .put("name", "Paul") + .build(); + final ImmutableMap mapToCompare = ImmutableMap.builder() + .put("name", "Paul") + .put("age", "15") + .build(); + + // when + final Boolean isEquivalent = compareMap.isEquivalent(map, mapToCompare); + + // then + assertTrue(isEquivalent); + } + + @Test + public void shouldNotBeEqualsWhenCompareValueMapsWhereHaveLessAttributesInComparation() { + // given + final ImmutableMap map = ImmutableMap.builder() + .put("name", "Paul") + .put("age", "15") + .build(); + final ImmutableMap mapToCompare = ImmutableMap.builder() + .put("name", "Paul") + .build(); + + // when + final Boolean isEquivalent = compareMap.isEquivalent(map, mapToCompare); + + // then + assertFalse(isEquivalent); + } + + @Test + public void shouldNotBeEqualsWhenThereDifferentValues() { + // given + final ImmutableMap map = ImmutableMap.builder() + .put("name", "Paul") + .put("age", "15") + .build(); + final ImmutableMap mapToCompare = ImmutableMap.builder() + .put("name", "Paul") + .put("age", "25") + .build(); + + // when + final Boolean isEquivalent = compareMap.isEquivalent(map, mapToCompare); + + // then + assertFalse(isEquivalent); + } + +} diff --git a/src/test/java/br/com/concrete/mock/infra/component/ExternalApiTest.java b/src/test/java/br/com/concrete/mock/infra/component/ExternalApiTest.java new file mode 100755 index 0000000..b501d05 --- /dev/null +++ b/src/test/java/br/com/concrete/mock/infra/component/ExternalApiTest.java @@ -0,0 +1,95 @@ +package br.com.concrete.mock.infra.component; + +import static org.junit.Assert.assertTrue; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.when; + +import java.util.Optional; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.bind.annotation.RequestMethod; + +import br.com.concrete.mock.generic.model.ExternalApiResult; +import br.com.concrete.mock.generic.model.Request; +import br.com.concrete.mock.infra.property.ApiProperty; +import br.com.concrete.mock.infra.model.UriConfiguration; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class ExternalApiTest { + + private static final String EXTERNAL_HOST = "http://www.mocky.io"; + private static final String URI = "/v2/57fbd6280f0000ed154fd470"; + // private static final String EXTERNAL_URL = "http://localhost:8080" + URI; + + @Autowired + private ExternalApi externalApi; + + @MockBean + private ApiProperty apiProperty; + + @Test(timeout = 5000) + public void shouldDoExternalGet() { + // given + final Request request = new Request.Builder(RequestMethod.GET, URI).build(); + + final UriConfiguration configuration = new UriConfiguration(); + configuration.setHost(EXTERNAL_HOST); + configuration.setBackup(false); + configuration.setPattern(URI); + + // when + when(apiProperty.getConfiguration(anyString())).thenReturn(Optional.of(configuration)); + final Optional responseEntity = externalApi.execute(request); + + // then + assertTrue(responseEntity.isPresent()); + assertTrue(responseEntity.get().getApiResult().getStatusCode().is2xxSuccessful()); + } + + @Test(timeout = 5000) + public void shouldDoExternalPost() { + // given + final Request request = new Request.Builder(RequestMethod.POST, URI) + .withBody(Optional.of("{\"name\": \"Paul\"}")).build(); + + final UriConfiguration configuration = new UriConfiguration(); + configuration.setHost(EXTERNAL_HOST); + configuration.setBackup(false); + configuration.setPattern(URI); + + // when + when(apiProperty.getConfiguration(anyString())).thenReturn(Optional.of(configuration)); + final Optional responseEntity = externalApi.execute(request); + + // then + assertTrue(responseEntity.isPresent()); + assertTrue(responseEntity.get().getApiResult().getStatusCode().is2xxSuccessful()); + } + + @Test(timeout = 8000) + public void shouldDoExternalPatch() { + // given + final Request request = new Request.Builder(RequestMethod.PATCH, URI) + .withBody(Optional.of("{\"name\": \"Paul\"}")).build(); + + final UriConfiguration configuration = new UriConfiguration(); + configuration.setHost(EXTERNAL_HOST); + configuration.setBackup(false); + configuration.setPattern(URI); + + // when + when(apiProperty.getConfiguration(anyString())).thenReturn(Optional.of(configuration)); + final Optional responseEntity = externalApi.execute(request); + + // then + assertTrue(responseEntity.isPresent()); + assertTrue(responseEntity.get().getApiResult().getStatusCode().is2xxSuccessful()); + } + +} diff --git a/src/test/java/br/com/concrete/mock/infra/component/file/BaseFileNameBuilderModelTest.java b/src/test/java/br/com/concrete/mock/infra/component/file/BaseFileNameBuilderModelTest.java new file mode 100755 index 0000000..0af8195 --- /dev/null +++ b/src/test/java/br/com/concrete/mock/infra/component/file/BaseFileNameBuilderModelTest.java @@ -0,0 +1,60 @@ +package br.com.concrete.mock.infra.component.file; + +import br.com.concrete.mock.infra.property.FileProperty; +import org.hamcrest.CoreMatchers; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.web.bind.annotation.RequestMethod; + +import java.net.URL; + +import static org.junit.Assert.*; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class BaseFileNameBuilderModelTest { + + private URL resource; + + @Autowired + @Qualifier("FilePropertyModel") + private FileProperty fileProperty; + + @Autowired + @Qualifier("BaseFileNameBuilderModel") + private BaseFileNameBuilder baseFileNameBuilder; + + @Before + public void init() { + this.resource = getClass().getClassLoader().getResource(fileProperty.getFileBase()); + } + + @Test + public void shouldHavePathToTest() { + assertNotNull(baseFileNameBuilder); + assertNotNull(fileProperty.getFileBase()); + assertFalse(fileProperty.getFileBase().isEmpty()); + assertNotNull(resource); + assertNotNull(resource.getFile()); + } + + @Test + public void shouldBuildPath() { + // given + final RequestMethod requestMethod = RequestMethod.GET; + final String pathUri = "/person"; + + // when + final String path = baseFileNameBuilder.buildPath(requestMethod, pathUri); + + // then + assertNotNull(path); + assertThat(path, CoreMatchers.endsWith(fileProperty.getFileBase() + "/get/person")); + } + +} diff --git a/src/test/java/br/com/concrete/mock/infra/component/file/FileJsonReaderTest.java b/src/test/java/br/com/concrete/mock/infra/component/file/FileJsonReaderTest.java new file mode 100755 index 0000000..76abdaa --- /dev/null +++ b/src/test/java/br/com/concrete/mock/infra/component/file/FileJsonReaderTest.java @@ -0,0 +1,67 @@ +package br.com.concrete.mock.infra.component.file; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.junit4.SpringRunner; + +import java.io.File; +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Paths; +import java.util.Optional; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.junit.Assert.*; + +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) +public class FileJsonReaderTest { + + private static final String NAME = "/get/person/11/my-mock.json"; + + @InjectMocks + private FileJsonReader fileJsonReader; + + private File resource; + + @Value("${file.base}") + private String fileBase; + + @Before + public void init() throws URISyntaxException { + File dir = Paths.get(getClass().getClassLoader().getResource(fileBase).toURI()).toFile(); + this.resource = Paths.get(dir.getAbsolutePath(), NAME).toFile(); + } + + @Test + public void shouldFileExistsInTest() { + assertNotNull(resource); + } + + @Test + public void shouldReadFile() throws IOException { + // when + final Optional jsonFile = fileJsonReader.getJsonByFileName(resource.getAbsolutePath()); + + // then + assertTrue(jsonFile.isPresent()); + assertThat(jsonFile.get(), containsString("name")); + } + + @Test + public void shouldReturnEmptyWhenFileNotFound() throws IOException { + // when + final Optional jsonFile = + fileJsonReader.getJsonByFileName( + Paths.get(resource.getAbsolutePath(), "-file-not-found" + ).toAbsolutePath().toString()); + + // then + assertFalse(jsonFile.isPresent()); + } + +} diff --git a/src/test/java/br/com/concrete/mock/infra/component/file/FileNameGeneratorImplTest.java b/src/test/java/br/com/concrete/mock/infra/component/file/FileNameGeneratorImplTest.java new file mode 100755 index 0000000..9fded17 --- /dev/null +++ b/src/test/java/br/com/concrete/mock/infra/component/file/FileNameGeneratorImplTest.java @@ -0,0 +1,103 @@ +package br.com.concrete.mock.infra.component.file; + +import br.com.concrete.mock.infra.property.FileExtensionProperty; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class FileNameGeneratorImplTest { + + private static final String EXTENSION = ".json"; + + @InjectMocks + private FileNameGeneratorImpl fileNameGenerator; + + @Mock + private FileExtensionProperty fileExtensionProperty; + + private Path path; + + @Before + public void init() throws IOException { + path = + Files.createTempDirectory("filenametest") + .toAbsolutePath(); + } + + @Test + public void shouldHavePathToTest() { + assertNotNull("Something is wrong with base path", path); + } + + @Test + public void shouldGenerateFileWhenFolderIsEmpty() { + // given + // empty folder + + // when + when(fileExtensionProperty.getFileExtension()).thenReturn(EXTENSION); + final String fileName = fileNameGenerator.fromPath(path.toFile().getAbsolutePath()); + + // then + assertEquals("1", fileName); + } + + @Test + public void shouldGenerateFileWhenFolderHave1File() throws IOException { + // given + createFileWithName("1"); + + // when + when(fileExtensionProperty.getFileExtension()).thenReturn(EXTENSION); + final String fileName = fileNameGenerator.fromPath(path.toFile().getAbsolutePath()); + + // then + assertEquals("2", fileName); + } + + @Test + public void shouldGenerateFileWhenFolderHave2File() throws IOException { + // given + createFileWithName("1"); + createFileWithName("2"); + + // when + when(fileExtensionProperty.getFileExtension()).thenReturn(EXTENSION); + final String fileName = fileNameGenerator.fromPath(path.toFile().getAbsolutePath()); + + // then + assertEquals("3", fileName); + } + + @Test + public void shouldGenerateFileWhenFolderHaveFileNameEqualSizeFilesIntoFolder() throws IOException { + // given + createFileWithName("2"); + createFileWithName("3"); + + // when + when(fileExtensionProperty.getFileExtension()).thenReturn(EXTENSION); + final String fileName = fileNameGenerator.fromPath(path.toFile().getAbsolutePath()); + + // then + assertEquals("4", fileName); + } + + private void createFileWithName(final String name) throws IOException { + Files.createFile(Paths.get(path.toFile().getAbsolutePath(), name + EXTENSION)); + } + +} diff --git a/src/test/java/br/com/concrete/mock/infra/component/impl/ConvertJsonImplTest.java b/src/test/java/br/com/concrete/mock/infra/component/impl/ConvertJsonImplTest.java new file mode 100755 index 0000000..f1f7ce8 --- /dev/null +++ b/src/test/java/br/com/concrete/mock/infra/component/impl/ConvertJsonImplTest.java @@ -0,0 +1,34 @@ +package br.com.concrete.mock.infra.component.impl; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.runners.MockitoJUnitRunner; + +import java.util.HashMap; + +import static org.junit.Assert.assertEquals; + +@RunWith(MockitoJUnitRunner.class) +public class ConvertJsonImplTest { + + @InjectMocks + private ConvertJsonImpl convertJsonImpl; + + @Test + public void shouldConvertJsonObject() { + // given + final String json = "{\"name\": \"Paul\"}"; + + final HashMap hashMap = new HashMap<>(); + hashMap.put("name", "Paul"); + + // when + final HashMap result = convertJsonImpl.apply(json); + + // then + assertEquals(hashMap, result); + } + + +} diff --git a/src/test/java/br/com/concrete/mock/infra/component/impl/FromJsonStringToObjectConverterImplTest.java b/src/test/java/br/com/concrete/mock/infra/component/impl/FromJsonStringToObjectConverterImplTest.java new file mode 100755 index 0000000..a08f67a --- /dev/null +++ b/src/test/java/br/com/concrete/mock/infra/component/impl/FromJsonStringToObjectConverterImplTest.java @@ -0,0 +1,62 @@ +package br.com.concrete.mock.infra.component.impl; + + +import com.google.gson.Gson; +import org.json.JSONException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.runners.MockitoJUnitRunner; +import org.skyscreamer.jsonassert.JSONAssert; + +import java.util.Optional; + +@RunWith(MockitoJUnitRunner.class) +public class FromJsonStringToObjectConverterImplTest { + + @InjectMocks + private FromJsonStringToObjectConverterImpl converter; + + @Test + public void shouldConvertObject() throws JSONException { + // given + final String expectedJson = "{ \"name\": \"Peter\", \"age\": 99}"; + final Optional jsonString = Optional.of(expectedJson); + + // when + final Object jsonObject = converter.apply(jsonString); + final String json = new Gson().toJson(jsonObject); + + // then + JSONAssert.assertEquals(expectedJson, json, false); + } + + @Test + public void shouldConvertArray() throws JSONException { + // given + final String expectedJson = " [ { \"name\": \"Peter\", \"age\": 99}, { \"name\": \"Paul\", \"age\": 98} ]"; + final Optional jsonString = Optional.of(expectedJson); + + // when + final Object jsonObject = converter.apply(jsonString); + final String json = new Gson().toJson(jsonObject); + + // then + JSONAssert.assertEquals(expectedJson, json, false); + } + + @Test + public void shouldConvertNull() throws JSONException { + // given + final String expectedJson = "{ }"; + final Optional jsonString = Optional.of(expectedJson); + + // when + final Object jsonObject = converter.apply(jsonString); + final String json = new Gson().toJson(jsonObject); + + // then + JSONAssert.assertEquals(expectedJson, json, false); + } + +} diff --git a/src/test/java/br/com/concrete/mock/infra/component/impl/JsonFormatterPrettyTest.java b/src/test/java/br/com/concrete/mock/infra/component/impl/JsonFormatterPrettyTest.java new file mode 100755 index 0000000..7a166e4 --- /dev/null +++ b/src/test/java/br/com/concrete/mock/infra/component/impl/JsonFormatterPrettyTest.java @@ -0,0 +1,47 @@ +package br.com.concrete.mock.infra.component.impl; + +import com.google.gson.JsonParseException; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.runners.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class JsonFormatterPrettyTest { + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + @InjectMocks + private JsonFormatterPretty jsonFormatterPretty; + + @Test + public void shouldFormatValidJson() { + // given + final String jsonRaw = "{\"age\":10}"; + final String jsonExpected = "{\n" + + " \"age\": 10\n" + + "}\n"; + + // when + final String jsonPretty = jsonFormatterPretty.format(jsonRaw); + + // then + Assert.assertEquals(jsonExpected, jsonPretty); + } + + @Test + public void shouldThrowExceptionWhenIsInvalidJsonFormat() { + // given + final String jsonRaw = "invalid json format"; + + // excpected exception + expectedException.expect(JsonParseException.class); + + // when + jsonFormatterPretty.format(jsonRaw); + } + +} diff --git a/src/test/java/br/com/concrete/mock/infra/component/impl/JsonValueCompilerImplTest.java b/src/test/java/br/com/concrete/mock/infra/component/impl/JsonValueCompilerImplTest.java new file mode 100755 index 0000000..91bee45 --- /dev/null +++ b/src/test/java/br/com/concrete/mock/infra/component/impl/JsonValueCompilerImplTest.java @@ -0,0 +1,80 @@ +package br.com.concrete.mock.infra.component.impl; + +import org.json.JSONException; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.InjectMocks; +import org.mockito.runners.MockitoJUnitRunner; +import org.skyscreamer.jsonassert.JSONAssert; + +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; + +@RunWith(MockitoJUnitRunner.class) +public class JsonValueCompilerImplTest { + + @InjectMocks + private JsonValueCompilerImpl jsonValueCompiler; + + @Test + public void shouldBeEqualWhenNotExistVariables() throws JSONException { + // given + final String jsonWithValue = "{ \"id\": \"first\", \"name\": \"Paul\" }"; + + // when + final String jsonCompiled = jsonValueCompiler.compile(jsonWithValue); + + // then + JSONAssert.assertEquals(jsonWithValue, jsonCompiled, false); + } + + @Test + public void shouldCompileVariable3daysAgo() throws JSONException { + // given + final String format = "YYYY/MM/dd"; + final String jsonWithValue = "{ \"date1\": \"#{{3daysAgo:" + format + "}}\", \"name\": \"Paul\" }"; + final String date = LocalDate.now().minusDays(3).format(DateTimeFormatter.ofPattern(format)); + final String expectedJson = "{ \"date1\": \"" + date + "\", \"name\": \"Paul\" }"; + + // when + final String jsonCompiled = jsonValueCompiler.compile(jsonWithValue); + + // then + JSONAssert.assertEquals(expectedJson, jsonCompiled, false); + } + + @Test + public void shouldCompileVariable3daysAgoRepeated() throws JSONException { + // given + final String format = "YYYY/MM/dd"; + final String jsonWithValue = "{ \"date1\": \"#{{3daysAgo:" + format + "}}\", \"date2\": \"#{{3daysAgo:" + format + "}}\", \"name\": \"Paul\" }"; + final String date = LocalDate.now().minusDays(3).format(DateTimeFormatter.ofPattern(format)); + final String expectedJson = "{ \"date1\": \"" + date + "\", \"date2\": \"" + date + "\", \"name\": \"Paul\" }"; + + // when + final String jsonCompiled = jsonValueCompiler.compile(jsonWithValue); + + // then + JSONAssert.assertEquals(expectedJson, jsonCompiled, false); + } + + @Test + public void shouldCompileVariableNdaysAgo() throws JSONException { + // given + final String format = "YYYY/MM/dd"; + final String jsonWithValue = "{ \"date1\": \"#{{3daysAgo:" + format + "}}\", \"date2\": \"#{{3daysAgo:" + format + "}}\", \"date3\": \"#{{7daysAgo:" + format + "}}\", \"date4\": \"#{{8daysAgo:" + format + "}}\", \"name\": \"Paul\" }"; + final String date3daysAgo = LocalDate.now().minusDays(3).format(DateTimeFormatter.ofPattern(format)); + final String date7daysAgo = LocalDate.now().minusDays(7).format(DateTimeFormatter.ofPattern(format)); + final String date8daysAgo = LocalDate.now().minusDays(8).format(DateTimeFormatter.ofPattern(format)); + final String expectedJson = "{ \"date1\": \"" + date3daysAgo + "\", \"date2\": \"" + date3daysAgo + "\", \"date3\": \"" + date7daysAgo + "\", \"date4\": \"" + date8daysAgo + "\", \"name\": \"Paul\" }"; + + // when + final String jsonCompiled = jsonValueCompiler.compile(jsonWithValue); + + // then + JSONAssert.assertEquals(expectedJson, jsonCompiled, false); + } + + // format error + +} diff --git a/src/test/java/br/com/concrete/mock/infra/component/impl/QueryStringBuilderImplTest.java b/src/test/java/br/com/concrete/mock/infra/component/impl/QueryStringBuilderImplTest.java new file mode 100755 index 0000000..c8223c5 --- /dev/null +++ b/src/test/java/br/com/concrete/mock/infra/component/impl/QueryStringBuilderImplTest.java @@ -0,0 +1,64 @@ +package br.com.concrete.mock.infra.component.impl; + +import br.com.concrete.mock.infra.component.ConvertJson; +import com.google.common.collect.ImmutableMap; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import static org.junit.Assert.*; + +@RunWith(MockitoJUnitRunner.class) +public class QueryStringBuilderImplTest { + + @InjectMocks + private QueryStringBuilderImpl queryStringBuilder; + + @Mock(answer = Answers.CALLS_REAL_METHODS) + private ConvertJson convertJson; + + @Test + public void shouldBeEmpty() { + // given + final ImmutableMap queryMap = ImmutableMap.builder().build(); + + // when + final String queryString = queryStringBuilder.fromMap(queryMap); + + // then + assertNotNull(queryString); + assertTrue(queryString.isEmpty()); + } + + @Test + public void shouldHaveOneParameter() { + // given + final ImmutableMap queryMap = ImmutableMap.builder().put("name", "Paul").build(); + + // when + final String queryString = queryStringBuilder.fromMap(queryMap); + + // then + assertNotNull(queryString); + assertFalse(queryString.isEmpty()); + assertEquals("?name=Paul", queryString); + } + + @Test + public void shouldHaveTwoParameters() { + // given + final ImmutableMap queryMap = ImmutableMap.builder().put("name", "Paul").put("age", "10").build(); + + // when + final String queryString = queryStringBuilder.fromMap(queryMap); + + // then + assertNotNull(queryString); + assertFalse(queryString.isEmpty()); + assertEquals("?name=Paul&age=10", queryString); + } + +} diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml new file mode 100755 index 0000000..e95c4f7 --- /dev/null +++ b/src/test/resources/application.yml @@ -0,0 +1,40 @@ +# vmoptions: -Dspring.profiles.active=test + +server.port: 9090 + +api: + acceptedHeaders: + - "execute" + - "Content-Type" + host: "http://www.mocky.io" + defaultHeaders: + - + headerName: "Access-Control-Allow-Origin" + headerValues: + - "Connection" + - "Keep-Alive" + - + headerName: "Content-Type" + headerValues: + - "application/json;charset=UTF-8" + uriConfigurations: + - + host: "http://www.mocky.io" + pattern: "/v2/57fbd6280f0000ed154fd470" + backup: false + - + host: "http://www.mocky.io" + pattern: "/v2/5928a3aa0f0000140538834a" + backup: true + - + host: "/teste.html" + pattern: "teste/|test/" + +file: + base: "mocks-test" + extension: ".json" + backup.path: "backup-temp" + +captureState: false + +debug: true diff --git a/src/test/resources/mocks-test/get/guests/132/1.json b/src/test/resources/mocks-test/get/guests/132/1.json new file mode 100755 index 0000000..dddbac5 --- /dev/null +++ b/src/test/resources/mocks-test/get/guests/132/1.json @@ -0,0 +1,18 @@ +{ + "response": { + "body": [ + { + "cc": "11", + "ca": "12" + }, + { + "cc": "21", + "ca": "22" + }, + { + "cc": "31", + "ca": "32" + } + ] + } +} diff --git a/src/test/resources/mocks-test/get/guests/132/users/1.json b/src/test/resources/mocks-test/get/guests/132/users/1.json new file mode 100755 index 0000000..614b8ce --- /dev/null +++ b/src/test/resources/mocks-test/get/guests/132/users/1.json @@ -0,0 +1,9 @@ +{ + "response": { + "body": [ + { + "move": "3" + } + ] + } +} diff --git a/src/test/resources/mocks-test/get/guests/132/users/21/cc/1.json b/src/test/resources/mocks-test/get/guests/132/users/21/cc/1.json new file mode 100755 index 0000000..3f8c597 --- /dev/null +++ b/src/test/resources/mocks-test/get/guests/132/users/21/cc/1.json @@ -0,0 +1,7 @@ +{ + "response": { + "body": { + "tt": "789" + } + } +} diff --git a/src/test/resources/mocks-test/get/guests/132/users/22/cc/1.json b/src/test/resources/mocks-test/get/guests/132/users/22/cc/1.json new file mode 100755 index 0000000..462294e --- /dev/null +++ b/src/test/resources/mocks-test/get/guests/132/users/22/cc/1.json @@ -0,0 +1,7 @@ +{ + "response": { + "body": { + "tt": "456" + } + } +} diff --git a/src/test/resources/mocks-test/get/payments/user/detail/1.json b/src/test/resources/mocks-test/get/payments/user/detail/1.json new file mode 100755 index 0000000..e5240de --- /dev/null +++ b/src/test/resources/mocks-test/get/payments/user/detail/1.json @@ -0,0 +1,18 @@ +{ + "request": { + "query": { + "payment": "1", + "value": "10" + } + }, + "response": { + "body": [ + { + "total": "1700" + }, + { + "total": "1800" + } + ] + } +} diff --git a/src/test/resources/mocks-test/get/payments/user/detail/2.json b/src/test/resources/mocks-test/get/payments/user/detail/2.json new file mode 100755 index 0000000..3690a07 --- /dev/null +++ b/src/test/resources/mocks-test/get/payments/user/detail/2.json @@ -0,0 +1,18 @@ +{ + "request": { + "query": { + "payment": "2", + "value": "20" + } + }, + "response": { + "body": [ + { + "total": "1702" + }, + { + "total": "1802" + } + ] + } +} diff --git a/src/test/resources/mocks-test/get/person/11/my-mock.json b/src/test/resources/mocks-test/get/person/11/my-mock.json new file mode 100755 index 0000000..49fb393 --- /dev/null +++ b/src/test/resources/mocks-test/get/person/11/my-mock.json @@ -0,0 +1,8 @@ +{ + "response": { + "body": { + "name": "Paul", + "age": 10 + } + } +} diff --git a/src/test/resources/mocks-test/get/users/123/1.json b/src/test/resources/mocks-test/get/users/123/1.json new file mode 100755 index 0000000..a24e97f --- /dev/null +++ b/src/test/resources/mocks-test/get/users/123/1.json @@ -0,0 +1,7 @@ +{ + "response": { + "body": { + "name": "Paul" + } + } +} diff --git a/src/test/resources/mocks-test/get/users/123/2.json b/src/test/resources/mocks-test/get/users/123/2.json new file mode 100755 index 0000000..08e23df --- /dev/null +++ b/src/test/resources/mocks-test/get/users/123/2.json @@ -0,0 +1,13 @@ +{ + "request": { + "query": { + "payment": "22" + } + }, + "response": { + "body": { + "name": "John" + }, + "httpStatus": 202 + } +} diff --git a/src/test/resources/mocks-test/patch/users/1456/1.json b/src/test/resources/mocks-test/patch/users/1456/1.json new file mode 100755 index 0000000..75b53ec --- /dev/null +++ b/src/test/resources/mocks-test/patch/users/1456/1.json @@ -0,0 +1,12 @@ +{ + "request": { + "body": { + "result": "done" + } + }, + "response": { + "body": { + "sync": "success" + } + } +} diff --git a/src/test/resources/mocks-test/patch/v2/57fbd6280f0000ed154fd470/1.json b/src/test/resources/mocks-test/patch/v2/57fbd6280f0000ed154fd470/1.json new file mode 100755 index 0000000..12ec53e --- /dev/null +++ b/src/test/resources/mocks-test/patch/v2/57fbd6280f0000ed154fd470/1.json @@ -0,0 +1,12 @@ +{ + "request": { + "body": { + "result": "done" + } + }, + "response": { + "body": { + "message": "success" + } + } +} diff --git a/src/test/resources/mocks-test/post/move/to/country/13/1.json b/src/test/resources/mocks-test/post/move/to/country/13/1.json new file mode 100755 index 0000000..b480e59 --- /dev/null +++ b/src/test/resources/mocks-test/post/move/to/country/13/1.json @@ -0,0 +1,12 @@ +{ + "request": { + "body": { + "count": "698" + } + }, + "response": { + "body": { + "street": "USA" + } + } +} diff --git a/src/test/resources/mocks-test/post/move/to/country/13/pi/1.json b/src/test/resources/mocks-test/post/move/to/country/13/pi/1.json new file mode 100755 index 0000000..4595917 --- /dev/null +++ b/src/test/resources/mocks-test/post/move/to/country/13/pi/1.json @@ -0,0 +1,12 @@ +{ + "request": { + "body": { + "pi": "123456" + } + }, + "response": { + "body": { + "street": "NY" + } + } +} diff --git a/src/test/resources/mocks-test/post/move/to/country/13/pi/2.json b/src/test/resources/mocks-test/post/move/to/country/13/pi/2.json new file mode 100755 index 0000000..b8ccfb3 --- /dev/null +++ b/src/test/resources/mocks-test/post/move/to/country/13/pi/2.json @@ -0,0 +1,14 @@ +{ + "request": { + "body": { + "pi": "j12" + } + }, + "response": { + "body": { + "codigo": "123", + "mensagem": "Error :(" + }, + "httpStatus": "422" + } +} diff --git a/src/test/resources/mocks-test/post/v2/57fbd6280f0000ed154fd470/1.json b/src/test/resources/mocks-test/post/v2/57fbd6280f0000ed154fd470/1.json new file mode 100755 index 0000000..12ec53e --- /dev/null +++ b/src/test/resources/mocks-test/post/v2/57fbd6280f0000ed154fd470/1.json @@ -0,0 +1,12 @@ +{ + "request": { + "body": { + "result": "done" + } + }, + "response": { + "body": { + "message": "success" + } + } +}