diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..6c018781 --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/** +!**/src/test/** + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/gastos/.gitignore b/gastos/.gitignore new file mode 100644 index 00000000..6c018781 --- /dev/null +++ b/gastos/.gitignore @@ -0,0 +1,32 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/** +!**/src/test/** + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/gastos/Dockerfile b/gastos/Dockerfile new file mode 100644 index 00000000..3cba0b98 --- /dev/null +++ b/gastos/Dockerfile @@ -0,0 +1,4 @@ +FROM openjdk:8-jdk-alpine +VOLUME /tmp +COPY build/libs/*.jar app.jar +ENTRYPOINT ["java","-jar","/app.jar"] \ No newline at end of file diff --git a/gastos/README.md b/gastos/README.md new file mode 100644 index 00000000..e62417a9 --- /dev/null +++ b/gastos/README.md @@ -0,0 +1,20 @@ +# Gastos + +App desenvolvido utilizando Spring boot, Mysql para persistência de dados e Redis para cache de requisições. + +## Compilando + +Dentro da pasta do projeto execute o comando + +```$ ./gradlew clean build``` + +Será gerado um arquivo .jar dentro do diretório ```/build/libs```. + +## Executando +Para executar a aplicação execute o seguinte comando: + +```$ docker-compose up --build ``` + +## Link para docmentação API + +[Santander API](https://documenter.getpostman.com/view/7191301/SVSEur2A?version=latest) \ No newline at end of file diff --git a/gastos/build.gradle b/gastos/build.gradle new file mode 100644 index 00000000..a4b7c233 --- /dev/null +++ b/gastos/build.gradle @@ -0,0 +1,44 @@ +plugins { + id 'org.springframework.boot' version '2.1.6.RELEASE' + id 'java' +} + +apply plugin: 'io.spring.dependency-management' + +group = 'io.santander' +version = '0.0.1-SNAPSHOT' +sourceCompatibility = '1.8' + +configurations { + developmentOnly + runtimeClasspath { + extendsFrom developmentOnly + } + compileOnly { + extendsFrom annotationProcessor + } +} + +repositories { + mavenCentral() +} + +ext { + orikaVersion = '1.5.2' + flywayVersion = '5.2.4' +} + +dependencies { + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation "ma.glasnost.orika:orika-core:${orikaVersion}" + implementation "org.flywaydb:flyway-core:${flywayVersion}" + implementation 'org.springframework.boot:spring-boot-starter-data-redis:2.1.2.RELEASE' + compileOnly 'org.projectlombok:lombok' + developmentOnly 'org.springframework.boot:spring-boot-devtools' + runtimeOnly 'com.h2database:h2' + runtimeOnly 'mysql:mysql-connector-java' + annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor' + annotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' +} diff --git a/gastos/docker-compose.yml b/gastos/docker-compose.yml new file mode 100644 index 00000000..44af9147 --- /dev/null +++ b/gastos/docker-compose.yml @@ -0,0 +1,53 @@ +version: '3.3' + +services: + mysql: + image: mysql:5.7 + restart: always + environment: + MYSQL_DATABASE: 'gastos' + MYSQL_USER: 'gastos' + MYSQL_PASSWORD: 'gastos' + MYSQL_ROOT_PASSWORD: 'root' + networks: + - net-spents + ports: + - '3306:3306' + expose: + - '3306' + volumes: + - my-db:/var/lib/mysql + + redis: + image: redis + command: redis-server --requirepass Redis2019! --protected-mode no + networks: + - net-spents + ports: + - "6379:6379" + expose: + - '6379' + + santander-spents: + depends_on: + - mysql + - redis + links: + - mysql + environment: + SPRING_REDIS_HOST: redis + SPRING_DATASOURCE_URL: "jdbc:mysql://mysql:3306/gastos" + networks: + - net-spents + build: + context: . + container_name: spents-app + ports: + - "8080:8080" + +volumes: + my-db: + +networks: + net-spents: + driver: bridge \ No newline at end of file diff --git a/gastos/gradle/wrapper/gradle-wrapper.jar b/gastos/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..87b738cb Binary files /dev/null and b/gastos/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gastos/gradle/wrapper/gradle-wrapper.properties b/gastos/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..f4d7b2bf --- /dev/null +++ b/gastos/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gastos/gradlew b/gastos/gradlew new file mode 100755 index 00000000..af6708ff --- /dev/null +++ b/gastos/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='"-Xmx64m"' + +# 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/gastos/gradlew.bat b/gastos/gradlew.bat new file mode 100644 index 00000000..0f8d5937 --- /dev/null +++ b/gastos/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="-Xmx64m" + +@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/gastos/settings.gradle b/gastos/settings.gradle new file mode 100644 index 00000000..680a2edf --- /dev/null +++ b/gastos/settings.gradle @@ -0,0 +1,6 @@ +pluginManagement { + repositories { + gradlePluginPortal() + } +} +rootProject.name = 'gastos' diff --git a/gastos/src/main/java/io/santander/gastos/SantanderGastosApplication.java b/gastos/src/main/java/io/santander/gastos/SantanderGastosApplication.java new file mode 100644 index 00000000..814c2a3b --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/SantanderGastosApplication.java @@ -0,0 +1,17 @@ +package io.santander.gastos; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; + +@EnableJpaAuditing +@SpringBootApplication +@EnableCaching +public class SantanderGastosApplication { + + public static void main(String[] args) { + SpringApplication.run(SantanderGastosApplication.class, args); + } + +} diff --git a/gastos/src/main/java/io/santander/gastos/commons/AbstractMapper.java b/gastos/src/main/java/io/santander/gastos/commons/AbstractMapper.java new file mode 100644 index 00000000..3a6fe9f3 --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/commons/AbstractMapper.java @@ -0,0 +1,99 @@ +package io.santander.gastos.commons; + +import ma.glasnost.orika.MapperFacade; +import ma.glasnost.orika.MapperFactory; +import ma.glasnost.orika.impl.DefaultMapperFactory; +import ma.glasnost.orika.metadata.ClassMapBuilder; + +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.util.List; + +public abstract class AbstractMapper { + + protected static final MapperFactory factory = createFactory(); + protected static final MapperFacade mapper = factory.getMapperFacade(); + + private final Class entityClass; + private final Class dtoClass; + + public AbstractMapper() { + final Type[] actualTypeArguments = ((ParameterizedType) this.getClass().getGenericSuperclass()) + .getActualTypeArguments(); + this.entityClass = (Class) actualTypeArguments[0]; + this.dtoClass = (Class) actualTypeArguments[1]; + } + + public AbstractMapper(final String... fields) { + this(); + final ClassMapBuilder classMap = factory.classMap(entityClass, + dtoClass); + + for (final String field : fields) { + final String[] split = field.split("="); + classMap.field(split[0], split[1]); + } + + factory.registerClassMap(classMap.byDefault().toClassMap()); + } + + private static MapperFactory createFactory() { + return new DefaultMapperFactory.Builder().build(); + } + + protected D toDTO(final E entity) { + return mapper.map(entity, dtoClass); + } + + protected D toDTO(final E entity, final String... exclusions) { + final MapperFactory instanceFactory = createFactory(); + final ClassMapBuilder classMap = instanceFactory.classMap( + dtoClass, entityClass); + for (final String exc : exclusions) { + classMap.exclude(exc); + } + classMap.byDefault().register(); + return instanceFactory.getMapperFacade().map(entity, dtoClass); + } + + protected List toDTOList(final List entity) { + return mapper.mapAsList(entity, dtoClass); + } + + protected E toEntity(final D dto) { + return mapper.map(dto, entityClass); + } + + protected E toEntity(final D dto, final String... exclusions) { + final MapperFactory instanceFactory = createFactory(); + final ClassMapBuilder classMap = instanceFactory.classMap( + entityClass, dtoClass); + for (final String exc : exclusions) { + classMap.exclude(exc); + } + classMap.byDefault().register(); + return instanceFactory.getMapperFacade().map(dto, entityClass); + } + + protected List toEntityList(final List dto) { + return mapper.mapAsList(dto, entityClass); + } + + /** + * Thread-safe. Create a new MapperFacade for every update + */ + protected void updateEntity(final E entity, final D dto, + final String... exclusions) { + final MapperFactory instanceFactory = createFactory(); + final ClassMapBuilder classMap = instanceFactory.classMap( + entityClass, dtoClass); + for (final String exc : exclusions) { + classMap.exclude(exc); + } + classMap.byDefault().register(); + + instanceFactory.getMapperFacade(entityClass, dtoClass).mapReverse(dto, + entity); + } + +} \ No newline at end of file diff --git a/gastos/src/main/java/io/santander/gastos/domain/CardSpent.java b/gastos/src/main/java/io/santander/gastos/domain/CardSpent.java new file mode 100644 index 00000000..898824b7 --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/domain/CardSpent.java @@ -0,0 +1,42 @@ +package io.santander.gastos.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import javax.persistence.*; +import java.io.Serializable; +import java.util.Date; + +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +@Entity +@Table(name = "card_spent") +@EntityListeners(AuditingEntityListener.class) +public class CardSpent implements Serializable { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne + @JoinColumn(name = "card") + private CreditCard creditCard; + + @OneToOne + @JoinColumn(name = "spent") + private Spent spent; + + @CreatedDate + @Column(name = "dat_creation", updatable = false, nullable = false) + private Date creationTime; + + @LastModifiedDate + @Column(name = "dat_update", nullable = false) + private Date updateTime; +} diff --git a/gastos/src/main/java/io/santander/gastos/domain/Classification.java b/gastos/src/main/java/io/santander/gastos/domain/Classification.java new file mode 100644 index 00000000..6c9e5f5e --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/domain/Classification.java @@ -0,0 +1,41 @@ +package io.santander.gastos.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import javax.persistence.*; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +@Entity +@Table(name = "classification") +@EntityListeners(AuditingEntityListener.class) +public class Classification implements Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + + @OneToMany(mappedBy = "classification", cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE}) + private List clientClassifications; + + @CreatedDate + @Column(name = "dat_creation", updatable = false, nullable = false) + private Date creationTime; + + @LastModifiedDate + @Column(name = "dat_update", nullable = false) + private Date updateTime; +} diff --git a/gastos/src/main/java/io/santander/gastos/domain/Client.java b/gastos/src/main/java/io/santander/gastos/domain/Client.java new file mode 100644 index 00000000..63e2bb51 --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/domain/Client.java @@ -0,0 +1,41 @@ +package io.santander.gastos.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import javax.persistence.*; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +@Entity +@Table(name = "client") +@EntityListeners(AuditingEntityListener.class) +public class Client implements Serializable { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @OneToMany(mappedBy = "client", cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE}) + private List clientCards; + + @OneToMany(mappedBy = "client", cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE}) + private List clientClassifications; + + @CreatedDate + @Column(name = "dat_creation", updatable = false, nullable = false) + private Date creationTime; + + @LastModifiedDate + @Column(name = "dat_update", nullable = false) + private Date updateTime; +} diff --git a/gastos/src/main/java/io/santander/gastos/domain/ClientCard.java b/gastos/src/main/java/io/santander/gastos/domain/ClientCard.java new file mode 100644 index 00000000..b1ef9f75 --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/domain/ClientCard.java @@ -0,0 +1,43 @@ +package io.santander.gastos.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import javax.persistence.*; +import java.io.Serializable; +import java.util.Date; + +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +@Entity +@Table(name = "client_card") +@EntityListeners(AuditingEntityListener.class) +public class ClientCard implements Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne + @JoinColumn(name = "client") + private Client client; + + @OneToOne + @JoinColumn(name = "card") + private CreditCard creditCard; + + @CreatedDate + @Column(name = "dat_creation", updatable = false, nullable = false) + private Date creationTime; + + @LastModifiedDate + @Column(name = "dat_update", nullable = false) + private Date updateTime; +} diff --git a/gastos/src/main/java/io/santander/gastos/domain/ClientClassification.java b/gastos/src/main/java/io/santander/gastos/domain/ClientClassification.java new file mode 100644 index 00000000..23e361d0 --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/domain/ClientClassification.java @@ -0,0 +1,43 @@ +package io.santander.gastos.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import javax.persistence.*; +import java.io.Serializable; +import java.util.Date; + +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +@Entity +@Table(name = "client_classification") +@EntityListeners(AuditingEntityListener.class) +public class ClientClassification implements Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @ManyToOne + @JoinColumn(name = "client") + private Client client; + + @ManyToOne + @JoinColumn(name = "classification") + private Classification classification; + + @CreatedDate + @Column(name = "dat_creation", updatable = false, nullable = false) + private Date creationTime; + + @LastModifiedDate + @Column(name = "dat_update", nullable = false) + private Date updateTime; +} diff --git a/gastos/src/main/java/io/santander/gastos/domain/CreditCard.java b/gastos/src/main/java/io/santander/gastos/domain/CreditCard.java new file mode 100644 index 00000000..64262030 --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/domain/CreditCard.java @@ -0,0 +1,44 @@ +package io.santander.gastos.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import javax.persistence.*; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +@Entity +@Table(name = "card") +@EntityListeners(AuditingEntityListener.class) +public class CreditCard implements Serializable { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(unique = true) + private String cardNumber; + + @OneToMany(mappedBy = "creditCard", cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE}) + private List clientCards; + + @OneToMany(mappedBy = "creditCard", cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REMOVE}) + private List cardSpents; + + @CreatedDate + @Column(name = "dat_creation", updatable = false, nullable = false) + private Date creationTime; + + @LastModifiedDate + @Column(name = "dat_update", nullable = false) + private Date updateTime; +} diff --git a/gastos/src/main/java/io/santander/gastos/domain/Spent.java b/gastos/src/main/java/io/santander/gastos/domain/Spent.java new file mode 100644 index 00000000..b07634cc --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/domain/Spent.java @@ -0,0 +1,40 @@ +package io.santander.gastos.domain; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import javax.persistence.*; +import java.io.Serializable; +import java.util.Date; + +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +@Entity +@Table(name = "spent") +@EntityListeners(AuditingEntityListener.class) +public class Spent implements Serializable { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + @Column(nullable = false) + private String description; + private Double spentValue; + private Date spentDate; + @JoinColumn(name = "classification") + @ManyToOne + private Classification classification; + @CreatedDate + @Column(name = "dat_creation", updatable = false, nullable = false) + private Date creationTime; + + @LastModifiedDate + @Column(name = "dat_update", nullable = false) + private Date updateTime; +} \ No newline at end of file diff --git a/gastos/src/main/java/io/santander/gastos/dto/CardSpentDTO.java b/gastos/src/main/java/io/santander/gastos/dto/CardSpentDTO.java new file mode 100644 index 00000000..e7dd9c01 --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/dto/CardSpentDTO.java @@ -0,0 +1,18 @@ +package io.santander.gastos.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CardSpentDTO { + private Long id; + + private CreditCardDTO creditCard; + + private SpentDTO spent; +} \ No newline at end of file diff --git a/gastos/src/main/java/io/santander/gastos/dto/ClassificationDTO.java b/gastos/src/main/java/io/santander/gastos/dto/ClassificationDTO.java new file mode 100644 index 00000000..ad238ab8 --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/dto/ClassificationDTO.java @@ -0,0 +1,18 @@ +package io.santander.gastos.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ClassificationDTO { + + private Long id; + + private String name; + +} \ No newline at end of file diff --git a/gastos/src/main/java/io/santander/gastos/dto/CreditCardDTO.java b/gastos/src/main/java/io/santander/gastos/dto/CreditCardDTO.java new file mode 100644 index 00000000..1fd5473e --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/dto/CreditCardDTO.java @@ -0,0 +1,15 @@ +package io.santander.gastos.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class CreditCardDTO { + + private Long id; + private String cardNumber; + +} \ No newline at end of file diff --git a/gastos/src/main/java/io/santander/gastos/dto/SpentDTO.java b/gastos/src/main/java/io/santander/gastos/dto/SpentDTO.java new file mode 100644 index 00000000..4ea4a745 --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/dto/SpentDTO.java @@ -0,0 +1,21 @@ +package io.santander.gastos.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Date; + +@Builder +@Data +@AllArgsConstructor +@NoArgsConstructor +public class SpentDTO { + private Long id; + private Long userCode; + private String description; + private Double spentValue; + private Date spentDate; + private ClassificationDTO classification; +} diff --git a/gastos/src/main/java/io/santander/gastos/enumerators/ErrorMessages.java b/gastos/src/main/java/io/santander/gastos/enumerators/ErrorMessages.java new file mode 100644 index 00000000..af8c2788 --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/enumerators/ErrorMessages.java @@ -0,0 +1,47 @@ +package io.santander.gastos.enumerators; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.lang.Nullable; + +@Getter +@NoArgsConstructor +public enum ErrorMessages { + + MISSING_PARAMETER { + @Override + public String getErrorMessage(String error) { + return String.format("The %s parameter was not found.", error); + } + }, + + MISSING_USER_CARD { + @Override + public String getErrorMessage(String error) { + return String.format("There is no card for the customer %s", error); + } + }, + + NONEXISTENT_CARD { + @Override + public String getErrorMessage(@Nullable String error) { + return String.format("This card does not belong to the user %s", error); + } + }, + + INVALID_HOLDER { + @Override + public String getErrorMessage(@Nullable String error) { + return String.format(" card", error); + } + }, + + INTERNAL_SERVER_ERROR { + @Override + public String getErrorMessage(String error) { + return String.format("%s", error); + } + }; + + public abstract String getErrorMessage(String error); +} diff --git a/gastos/src/main/java/io/santander/gastos/exceptions/InvalidHolderException.java b/gastos/src/main/java/io/santander/gastos/exceptions/InvalidHolderException.java new file mode 100644 index 00000000..4c451187 --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/exceptions/InvalidHolderException.java @@ -0,0 +1,7 @@ +package io.santander.gastos.exceptions; + +public class InvalidHolderException extends RuntimeException { + public InvalidHolderException(String message) { + super(message); + } +} diff --git a/gastos/src/main/java/io/santander/gastos/exceptions/MissingCardException.java b/gastos/src/main/java/io/santander/gastos/exceptions/MissingCardException.java new file mode 100644 index 00000000..7cbdfa23 --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/exceptions/MissingCardException.java @@ -0,0 +1,7 @@ +package io.santander.gastos.exceptions; + +public class MissingCardException extends RuntimeException { + public MissingCardException(String message) { + super(message); + } +} diff --git a/gastos/src/main/java/io/santander/gastos/exceptions/MissingFieldException.java b/gastos/src/main/java/io/santander/gastos/exceptions/MissingFieldException.java new file mode 100644 index 00000000..953cb63a --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/exceptions/MissingFieldException.java @@ -0,0 +1,4 @@ +package io.santander.gastos.exceptions; + +public class MissingFieldException extends RuntimeException { +} diff --git a/gastos/src/main/java/io/santander/gastos/exceptions/NonexistentCardException.java b/gastos/src/main/java/io/santander/gastos/exceptions/NonexistentCardException.java new file mode 100644 index 00000000..84bd2a08 --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/exceptions/NonexistentCardException.java @@ -0,0 +1,7 @@ +package io.santander.gastos.exceptions; + +public class NonexistentCardException extends RuntimeException { + public NonexistentCardException(String message) { + super(message); + } +} diff --git a/gastos/src/main/java/io/santander/gastos/mapper/CardMapper.java b/gastos/src/main/java/io/santander/gastos/mapper/CardMapper.java new file mode 100644 index 00000000..7c9936a6 --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/mapper/CardMapper.java @@ -0,0 +1,31 @@ +package io.santander.gastos.mapper; + +import io.santander.gastos.commons.AbstractMapper; +import io.santander.gastos.domain.CreditCard; +import io.santander.gastos.dto.CreditCardDTO; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +public class CardMapper extends AbstractMapper { + @Override + public CreditCardDTO toDTO(CreditCard entity) { + return super.toDTO(entity); + } + + @Override + public CreditCard toEntity(CreditCardDTO dto) { + return super.toEntity(dto); + } + + @Override + public List toDTOList(List entity) { + return super.toDTOList(entity); + } + + @Override + public List toEntityList(List dto) { + return super.toEntityList(dto); + } +} diff --git a/gastos/src/main/java/io/santander/gastos/mapper/CardSpentMapper.java b/gastos/src/main/java/io/santander/gastos/mapper/CardSpentMapper.java new file mode 100644 index 00000000..11b111c5 --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/mapper/CardSpentMapper.java @@ -0,0 +1,31 @@ +package io.santander.gastos.mapper; + +import io.santander.gastos.commons.AbstractMapper; +import io.santander.gastos.domain.CardSpent; +import io.santander.gastos.dto.CardSpentDTO; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +public class CardSpentMapper extends AbstractMapper { + @Override + public CardSpentDTO toDTO(CardSpent entity) { + return super.toDTO(entity); + } + + @Override + public CardSpent toEntity(CardSpentDTO dto) { + return super.toEntity(dto); + } + + @Override + public List toDTOList(List entity) { + return super.toDTOList(entity); + } + + @Override + public List toEntityList(List dto) { + return super.toEntityList(dto); + } +} diff --git a/gastos/src/main/java/io/santander/gastos/mapper/ClassificationMapper.java b/gastos/src/main/java/io/santander/gastos/mapper/ClassificationMapper.java new file mode 100644 index 00000000..f8d47c1f --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/mapper/ClassificationMapper.java @@ -0,0 +1,34 @@ +package io.santander.gastos.mapper; + +import io.santander.gastos.commons.AbstractMapper; +import io.santander.gastos.domain.Classification; +import io.santander.gastos.dto.ClassificationDTO; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +public class ClassificationMapper extends AbstractMapper { + + @Override + public ClassificationDTO toDTO(Classification entity) { + return super.toDTO(entity); + } + + @Override + public Classification toEntity(ClassificationDTO dto) { + return super.toEntity(dto); + } + + @Override + public List toDTOList(List entity) { + return super.toDTOList(entity); + } + + @Override + public List toEntityList(List dto) { + return super.toEntityList(dto); + } + + +} diff --git a/gastos/src/main/java/io/santander/gastos/mapper/SpentMapper.java b/gastos/src/main/java/io/santander/gastos/mapper/SpentMapper.java new file mode 100644 index 00000000..1509e255 --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/mapper/SpentMapper.java @@ -0,0 +1,31 @@ +package io.santander.gastos.mapper; + +import io.santander.gastos.commons.AbstractMapper; +import io.santander.gastos.domain.Spent; +import io.santander.gastos.dto.SpentDTO; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +public class SpentMapper extends AbstractMapper { + @Override + public SpentDTO toDTO(Spent entity) { + return super.toDTO(entity); + } + + @Override + public Spent toEntity(SpentDTO dto) { + return super.toEntity(dto); + } + + @Override + public List toDTOList(List entity) { + return super.toDTOList(entity); + } + + @Override + public List toEntityList(List dto) { + return super.toEntityList(dto); + } +} diff --git a/gastos/src/main/java/io/santander/gastos/repository/CardSpentRepository.java b/gastos/src/main/java/io/santander/gastos/repository/CardSpentRepository.java new file mode 100644 index 00000000..b7345766 --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/repository/CardSpentRepository.java @@ -0,0 +1,9 @@ +package io.santander.gastos.repository; + +import io.santander.gastos.domain.CardSpent; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface CardSpentRepository extends JpaRepository { +} diff --git a/gastos/src/main/java/io/santander/gastos/repository/ClassificationRepository.java b/gastos/src/main/java/io/santander/gastos/repository/ClassificationRepository.java new file mode 100644 index 00000000..dbce2545 --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/repository/ClassificationRepository.java @@ -0,0 +1,22 @@ +package io.santander.gastos.repository; + +import io.santander.gastos.domain.Classification; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface ClassificationRepository extends JpaRepository { + @Query(value = "FROM Classification c WHERE c.name LIKE ?1 ") + List findByText(String text); + + @Query(value = "SELECT cla FROM CardSpent cs " + + "INNER JOIN cs.creditCard cc " + + "INNER JOIN cs.spent s " + + "INNER JOIN s.classification cla " + + "WHERE cc.id IN ?1 " + + "AND s.description = ?2 ") + Classification findSpendClassification(List cards, String description); +} diff --git a/gastos/src/main/java/io/santander/gastos/repository/ClientCardRepository.java b/gastos/src/main/java/io/santander/gastos/repository/ClientCardRepository.java new file mode 100644 index 00000000..121596ac --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/repository/ClientCardRepository.java @@ -0,0 +1,29 @@ +package io.santander.gastos.repository; + +import io.santander.gastos.domain.ClientCard; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.List; + +@Repository +public interface ClientCardRepository extends JpaRepository { + @Query(value = "SELECT CASE WHEN COUNT(cc) > 0 THEN true ELSE false END FROM ClientCard cc " + + "INNER JOIN cc.client c " + + "INNER JOIN cc.creditCard ca " + + "WHERE c.id = ?1 AND ca.id = ?2") + boolean existsByClientAndCard(long client, long card); + + @Query(value = "SELECT cc.id FROM ClientCard clic " + + "INNER JOIN clic.creditCard cc " + + "INNER JOIN clic.client cli " + + "WHERE cli.id = ?1 AND cc.cardNumber = ?2") + List findByClientAndCard(long client, String card); + + @Query(value = "SELECT cc.id FROM ClientCard clic " + + "INNER JOIN clic.creditCard cc " + + "INNER JOIN clic.client cli " + + "WHERE cli.id = ?1") + List findAllCardByClient(Long userCode); +} diff --git a/gastos/src/main/java/io/santander/gastos/repository/ClientRepository.java b/gastos/src/main/java/io/santander/gastos/repository/ClientRepository.java new file mode 100644 index 00000000..bd463784 --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/repository/ClientRepository.java @@ -0,0 +1,9 @@ +package io.santander.gastos.repository; + +import io.santander.gastos.domain.Client; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ClientRepository extends JpaRepository { +} diff --git a/gastos/src/main/java/io/santander/gastos/repository/CreditCardRepository.java b/gastos/src/main/java/io/santander/gastos/repository/CreditCardRepository.java new file mode 100644 index 00000000..b70bd20a --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/repository/CreditCardRepository.java @@ -0,0 +1,10 @@ +package io.santander.gastos.repository; + +import io.santander.gastos.domain.CreditCard; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface CreditCardRepository extends JpaRepository { + CreditCard findByCardNumber(String cardNumber); +} diff --git a/gastos/src/main/java/io/santander/gastos/repository/SpentRepository.java b/gastos/src/main/java/io/santander/gastos/repository/SpentRepository.java new file mode 100644 index 00000000..b6e496f3 --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/repository/SpentRepository.java @@ -0,0 +1,24 @@ +package io.santander.gastos.repository; + +import io.santander.gastos.domain.Spent; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; + +import java.util.Date; +import java.util.List; + +@Repository +public interface SpentRepository extends JpaRepository { + + @Query(value = "SELECT s FROM CardSpent cs " + + "INNER JOIN cs.creditCard cc " + + "INNER JOIN cs.spent s " + + "WHERE cc.id IN ?1 " + + "AND (?2 IS NULL OR s.description = ?2) " + + "AND (?3 IS NULL OR s.spentValue = ?3) " + + "AND (?4 IS NULL OR s.spentDate = ?4) ") + Page findAllWithFilters(List cards, String description, Double value, Date date, Pageable pageable); +} diff --git a/gastos/src/main/java/io/santander/gastos/service/CardService.java b/gastos/src/main/java/io/santander/gastos/service/CardService.java new file mode 100644 index 00000000..231510f3 --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/service/CardService.java @@ -0,0 +1,21 @@ +package io.santander.gastos.service; + +import io.santander.gastos.dto.CreditCardDTO; +import io.santander.gastos.mapper.CardMapper; +import io.santander.gastos.repository.CreditCardRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor(onConstructor = @__(@Lazy)) +public class CardService { + private final CreditCardRepository creditCardRepository; + private final CardMapper cardMapper; + + + public CreditCardDTO findCard(String cardNumber) { + return cardMapper.toDTO(creditCardRepository.findByCardNumber(cardNumber)); + } + +} diff --git a/gastos/src/main/java/io/santander/gastos/service/CardSpentService.java b/gastos/src/main/java/io/santander/gastos/service/CardSpentService.java new file mode 100644 index 00000000..d3c44469 --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/service/CardSpentService.java @@ -0,0 +1,20 @@ +package io.santander.gastos.service; + +import io.santander.gastos.dto.CardSpentDTO; +import io.santander.gastos.mapper.CardSpentMapper; +import io.santander.gastos.repository.CardSpentRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor(onConstructor = @__(@Lazy)) +public class CardSpentService { + private final CardSpentRepository cardSpentRepository; + private final CardSpentMapper cardSpentMapper; + + public void save(CardSpentDTO cardSpentDTO){ + cardSpentRepository.save(cardSpentMapper.toEntity(cardSpentDTO)); + } + +} diff --git a/gastos/src/main/java/io/santander/gastos/service/ClassificationService.java b/gastos/src/main/java/io/santander/gastos/service/ClassificationService.java new file mode 100644 index 00000000..93fbbb1f --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/service/ClassificationService.java @@ -0,0 +1,37 @@ +package io.santander.gastos.service; + +import io.santander.gastos.dto.ClassificationDTO; +import io.santander.gastos.mapper.ClassificationMapper; +import io.santander.gastos.repository.ClassificationRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.util.List; + +@Service +@RequiredArgsConstructor(onConstructor = @__(@Lazy)) +public class ClassificationService { + + private final ClassificationRepository classificationRepository; + private final SpentService spentService; + private final ClassificationMapper classificationMapper; + + public List getAllClassificatios(String text) { + return classificationMapper.toDTOList(classificationRepository.findByText("%" + text + "%")); + } + + @Transactional + public void saveClassificationToClient(final Long userCode, final String description) { + spentService.getSpentClassification(userCode, description); + } + + public ClassificationDTO getClassification(final List cards, final String description) { + return classificationMapper.toDTO(classificationRepository.findSpendClassification(cards, description)); + } + + public ClassificationDTO getById(Long id) { + return classificationMapper.toDTO(classificationRepository.findById(id).get()); + } +} diff --git a/gastos/src/main/java/io/santander/gastos/service/ClientCardService.java b/gastos/src/main/java/io/santander/gastos/service/ClientCardService.java new file mode 100644 index 00000000..93a78941 --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/service/ClientCardService.java @@ -0,0 +1,28 @@ +package io.santander.gastos.service; + +import io.santander.gastos.repository.ClientCardRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor(onConstructor = @__(@Lazy)) +public class ClientCardService { + + private final ClientCardRepository clientCardRepository; + + public boolean verifyCardHolder(long client, long card) { + return clientCardRepository.existsByClientAndCard(client, card); + + } + + public List getClientsCard(long client, String card) { + return clientCardRepository.findByClientAndCard(client, card); + } + + public List getCardsByClient(Long userCode) { + return clientCardRepository.findAllCardByClient(userCode); + } +} diff --git a/gastos/src/main/java/io/santander/gastos/service/ClientService.java b/gastos/src/main/java/io/santander/gastos/service/ClientService.java new file mode 100644 index 00000000..c399d564 --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/service/ClientService.java @@ -0,0 +1,16 @@ +package io.santander.gastos.service; + +import io.santander.gastos.repository.ClientRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor(onConstructor = @__(@Lazy)) +public class ClientService { + private final ClientRepository clientRepository; + + public boolean verifyUser(Long id){ + return clientRepository.existsById(id); + } +} diff --git a/gastos/src/main/java/io/santander/gastos/service/DateUTCParser.java b/gastos/src/main/java/io/santander/gastos/service/DateUTCParser.java new file mode 100644 index 00000000..72320104 --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/service/DateUTCParser.java @@ -0,0 +1,33 @@ +package io.santander.gastos.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Lazy; +import org.springframework.stereotype.Component; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +@Component +@RequiredArgsConstructor(onConstructor = @__(@Lazy)) +public class DateUTCParser extends SimpleDateFormat { + + private String pattern = "yyyy-MM-dd"; + + final SimpleDateFormat sdf = new SimpleDateFormat(pattern); + + public Date toDate(String utcDate) { + if (utcDate != null) + try { + return sdf.parse(utcDate); + } catch (ParseException e) { + e.printStackTrace(); + } + return null; + } + + public String toUtcDate(Date date) { + return new SimpleDateFormat(pattern).format(date); + } + +} diff --git a/gastos/src/main/java/io/santander/gastos/service/SpentService.java b/gastos/src/main/java/io/santander/gastos/service/SpentService.java new file mode 100644 index 00000000..f8110046 --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/service/SpentService.java @@ -0,0 +1,128 @@ +package io.santander.gastos.service; + +import io.santander.gastos.domain.Spent; +import io.santander.gastos.dto.CardSpentDTO; +import io.santander.gastos.dto.ClassificationDTO; +import io.santander.gastos.dto.CreditCardDTO; +import io.santander.gastos.dto.SpentDTO; +import io.santander.gastos.exceptions.InvalidHolderException; +import io.santander.gastos.exceptions.MissingCardException; +import io.santander.gastos.exceptions.NonexistentCardException; +import io.santander.gastos.mapper.ClassificationMapper; +import io.santander.gastos.mapper.SpentMapper; +import io.santander.gastos.repository.SpentRepository; +import io.santander.gastos.vo.GastoVO; +import lombok.RequiredArgsConstructor; +import lombok.extern.log4j.Log4j2; +import org.springframework.context.annotation.Lazy; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.util.List; +import java.util.Optional; + +@Log4j2 +@Service +@RequiredArgsConstructor(onConstructor = @__(@Lazy)) +public class SpentService { + + private final SpentRepository spentRepository; + private final SpentMapper spentMapper; + private final CardService cardService; + private final CardSpentService cardSpentService; + private final ClientService clientService; + private final ClientCardService clientCardService; + private final ClassificationService classificationService; + private final DateUTCParser utcParser; + + private final ClassificationMapper classificationMapper; + + + private List getCarHolder(final Long userCode, final String cardNumber) { + List cards = clientCardService.getClientsCard(userCode, cardNumber); + if (cards.isEmpty()) { + throw new MissingCardException(userCode.toString()); + } else + return cards; + } + + @Transactional + public PageImpl buscaTodosOsGastoPorCliente(final Long userCode, String cardNumber, final GastoVO vo, final Pageable pageable) { + Page spentPage; + + List cards = getCarHolder(userCode, cardNumber); + spentPage = spentRepository.findAllWithFilters( + cards, + Optional.ofNullable(vo.getDescricao()).orElse(null), + Optional.ofNullable(vo.getValor()).orElse(null), + Optional.ofNullable(utcParser.toDate(vo.getData())).orElse(null), + pageable); + return new PageImpl<>(this.spentMapper.toDTOList(spentPage.getContent()), pageable, spentPage.getTotalElements()); + } + + @Transactional + public ClassificationDTO getSpentClassification(final Long userCode, final String decription) { + List cards = clientCardService.getCardsByClient(userCode); + return classificationService.getClassification(cards, decription); + } + + @Transactional + public String saveSpent(String numeroCartao, GastoVO vo) { + + if (clientService.verifyUser(vo.getCodigoUsuario())) { + CreditCardDTO cardDTO = cardService.findCard(numeroCartao); + if (cardDTO == null) { + throw new NonexistentCardException(numeroCartao); + } else { + if (clientCardService.verifyCardHolder(vo.getCodigoUsuario(), cardDTO.getId())) { + cardSpentService.save(CardSpentDTO + .builder() + .creditCard(cardDTO) + .spent(spentMapper.toDTO(spentRepository.save(Spent + .builder() + .description(vo.getDescricao()) + .spentDate(utcParser.toDate(vo.getData())) + .spentValue(vo.getValor()) + .classification(Optional.ofNullable(classificationMapper.toEntity(getSpentClassification(vo.getCodigoUsuario(), vo.getDescricao()))).orElse(null)) + .build()))) + .build()); + log.info("gasto registrado"); + return "gasto registrado"; + } else { + throw new InvalidHolderException(vo.getCodigoUsuario().toString()); + } + } + + } else { + //TODO corrigir exeption + throw new RuntimeException("Usuario inexistente"); + } + + } + + @Transactional + //TODO: salvar classificação que não exista no banco e associar ao usuario + public void updateSpendDatail(final Long userCode, final Long spentId, String cardNumber, final String description, final Long classificationId) { + + SpentDTO spentDTO = getSpentDetail(userCode, spentId, cardNumber); + + Optional.ofNullable(description).ifPresent(s -> spentDTO.setDescription(s)); + Optional.of(classificationId).ifPresent(aLong -> spentDTO.setClassification(classificationService.getById(aLong))); + if (classificationId == null) { + spentDTO.setClassification(Optional.ofNullable(getSpentClassification(userCode, description)).orElse(null)); + } + + spentRepository.save(spentMapper.toEntity(spentDTO)); + + } + + @Transactional + public SpentDTO getSpentDetail(Long userCode, Long codigoGasto, String cardNumber) { + List cards = getCarHolder(userCode, cardNumber); + //TODO: corrigir retorno + return spentMapper.toDTO(spentRepository.findById(codigoGasto).get()); + } +} diff --git a/gastos/src/main/java/io/santander/gastos/vo/ErrorResponse.java b/gastos/src/main/java/io/santander/gastos/vo/ErrorResponse.java new file mode 100644 index 00000000..0c2c966d --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/vo/ErrorResponse.java @@ -0,0 +1,28 @@ +package io.santander.gastos.vo; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.Data; +import lombok.Singular; + +import java.time.Instant; +import java.util.List; + +import static com.fasterxml.jackson.annotation.JsonInclude.Include.NON_EMPTY; + +@Data +@Builder +@JsonInclude(NON_EMPTY) +public class ErrorResponse { + + private Instant timestamp; + + private int status; + + private String error; + + @Singular + @JsonProperty("error_description") + private List messages; +} \ No newline at end of file diff --git a/gastos/src/main/java/io/santander/gastos/vo/GastoVO.java b/gastos/src/main/java/io/santander/gastos/vo/GastoVO.java new file mode 100644 index 00000000..141016fc --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/vo/GastoVO.java @@ -0,0 +1,31 @@ +package io.santander.gastos.vo; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import io.santander.gastos.service.DateUTCParser; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@Builder +@JsonInclude(JsonInclude.Include.NON_EMPTY) +public class GastoVO { + + @Autowired + private DateUTCParser dateParser; + + private Long id; + private String descricao; + private Double valor; + private Long codigoUsuario; + private String data; + @JsonProperty(access = JsonProperty.Access.READ_ONLY) + private String classificacao; + @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + private Long classificacaoId; +} diff --git a/gastos/src/main/java/io/santander/gastos/web/advice/SpertServiceAdvices.java b/gastos/src/main/java/io/santander/gastos/web/advice/SpertServiceAdvices.java new file mode 100644 index 00000000..3f82d1e3 --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/web/advice/SpertServiceAdvices.java @@ -0,0 +1,62 @@ +package io.santander.gastos.web.advice; + +import io.santander.gastos.enumerators.ErrorMessages; +import io.santander.gastos.exceptions.InvalidHolderException; +import io.santander.gastos.exceptions.MissingCardException; +import io.santander.gastos.exceptions.NonexistentCardException; +import io.santander.gastos.vo.ErrorResponse; +import lombok.extern.log4j.Log4j2; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MissingServletRequestParameterException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; + +import java.util.Arrays; + +import static java.time.Instant.now; +import static org.springframework.http.HttpStatus.*; +import static org.springframework.http.ResponseEntity.status; + +@ControllerAdvice +@Log4j2 +public class SpertServiceAdvices { + + @ExceptionHandler(MissingServletRequestParameterException.class) + public ResponseEntity handleMissingFieldException(final MissingServletRequestParameterException e) { + log.error(ErrorMessages.MISSING_PARAMETER.getErrorMessage(e.getParameterName())); + return status(BAD_REQUEST) + .body(this.constructErrorResponse(BAD_REQUEST, ErrorMessages.MISSING_PARAMETER.getErrorMessage(e.getParameterName()))); + } + + @ExceptionHandler(MissingCardException.class) + public ResponseEntity handleMissingCardException(final MissingCardException e) { + log.error(ErrorMessages.MISSING_USER_CARD.getErrorMessage(e.getMessage())); + return status(NOT_FOUND) + .body(this.constructErrorResponse(NOT_FOUND, ErrorMessages.MISSING_USER_CARD.getErrorMessage(e.getMessage()))); + } + + @ExceptionHandler(NonexistentCardException.class) + public ResponseEntity handleInexistentCardException(final NonexistentCardException e) { + log.error(ErrorMessages.NONEXISTENT_CARD.getErrorMessage(e.getMessage())); + return status(NOT_FOUND) + .body(this.constructErrorResponse(NOT_FOUND, ErrorMessages.NONEXISTENT_CARD.getErrorMessage(e.getMessage()))); + } + + @ExceptionHandler(InvalidHolderException.class) + public ResponseEntity handleInvalidHolderException(final InvalidHolderException e) { + log.error(ErrorMessages.INVALID_HOLDER.getErrorMessage(e.getMessage())); + return status(CONFLICT) + .body(this.constructErrorResponse(CONFLICT, ErrorMessages.INVALID_HOLDER.getErrorMessage(e.getMessage()))); + } + + + private ErrorResponse constructErrorResponse(final HttpStatus httpStatus, final String... messages) { + return ErrorResponse.builder() + .timestamp(now()) + .status(httpStatus.value()) + .error(httpStatus.getReasonPhrase()) + .messages(Arrays.asList(messages)) + .build(); + } +} diff --git a/gastos/src/main/java/io/santander/gastos/web/controller/ClassificationController.java b/gastos/src/main/java/io/santander/gastos/web/controller/ClassificationController.java new file mode 100644 index 00000000..6c52a7fa --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/web/controller/ClassificationController.java @@ -0,0 +1,31 @@ +package io.santander.gastos.web.controller; + +import io.santander.gastos.dto.ClassificationDTO; +import io.santander.gastos.service.ClassificationService; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.context.annotation.Lazy; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; +import java.util.List; + +@RestController +@RequestMapping(ClassificationController.ClASSIFICATION_ENDPOINT) +@Validated +@RequiredArgsConstructor(onConstructor = @__(@Lazy)) +public class ClassificationController { + public static final String ClASSIFICATION_ENDPOINT = "/classification"; + + private final ClassificationService classificationService; + + @GetMapping + @Cacheable("classification") + List buscaCassificacao(@RequestParam @Valid String searchText) { + return classificationService.getAllClassificatios(searchText); + } +} diff --git a/gastos/src/main/java/io/santander/gastos/web/controller/SpentController.java b/gastos/src/main/java/io/santander/gastos/web/controller/SpentController.java new file mode 100644 index 00000000..dc2d7015 --- /dev/null +++ b/gastos/src/main/java/io/santander/gastos/web/controller/SpentController.java @@ -0,0 +1,64 @@ +package io.santander.gastos.web.controller; + +import io.santander.gastos.dto.SpentDTO; +import io.santander.gastos.service.DateUTCParser; +import io.santander.gastos.service.SpentService; +import io.santander.gastos.vo.GastoVO; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.context.annotation.Lazy; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.stream.Collectors; + +@RestController +@RequestMapping(SpentController.SPENTS_ENDPOINT) +@Validated +@RequiredArgsConstructor(onConstructor = @__(@Lazy)) +public class SpentController { + + public static final String SPENTS_ENDPOINT = "/spent"; + + private final SpentService spentService; + private final DateUTCParser dateParser; + + @PostMapping("/{numeroCartao}") + String addSpent(@Valid @PathVariable("numeroCartao") final String numeroCartao, final GastoVO vo) { + return spentService.saveSpent(numeroCartao, vo); + } + + @GetMapping("/user/{codigoUsuario}") + @Cacheable("spents") + PageImpl getAllSpents(@Valid @PathVariable final Long codigoUsuario, @RequestParam final String numeroCartão, final GastoVO vo, Pageable pageable) { + Page dtoPage = spentService.buscaTodosOsGastoPorCliente(codigoUsuario, numeroCartão, vo, pageable); + return new PageImpl<>(dtoPage.getContent().stream().map(this::toVo).collect(Collectors.toList()), pageable, dtoPage.getTotalElements()); + } + + @GetMapping("/user/{codigoUsuario}/{codigoGasto}") + @Cacheable("spent-datail") + GastoVO getSpentDatail(@Valid @PathVariable final Long codigoUsuario, @Valid @PathVariable final Long codigoGasto, @RequestParam final String numeroCartão) { + return toVo(spentService.getSpentDetail(codigoUsuario, codigoGasto, numeroCartão)); + } + + @PutMapping("/user/{codigoUsuario}/{codigoGasto}") + GastoVO updateSpentDatail(@Valid @PathVariable final Long codigoUsuario, @Valid @PathVariable final Long codigoGasto, @RequestParam final String numeroCartão, @RequestParam(required = false) final String description, @RequestParam(required = false) final Long classification) { + spentService.updateSpendDatail(codigoUsuario, codigoGasto, numeroCartão, description, classification); + return null; + } + + private GastoVO toVo(SpentDTO spentDTO) { + return GastoVO.builder() + .id(spentDTO.getId()) + .codigoUsuario(spentDTO.getUserCode()) + .descricao(spentDTO.getDescription()) + .data(dateParser.toUtcDate(spentDTO.getSpentDate())) + .valor(spentDTO.getSpentValue()) + .classificacao(spentDTO.getClassification().getName()) + .build(); + } +} diff --git a/gastos/src/main/resources/application.yml b/gastos/src/main/resources/application.yml new file mode 100644 index 00000000..0ce65529 --- /dev/null +++ b/gastos/src/main/resources/application.yml @@ -0,0 +1,24 @@ +spent: + date-format: yyyy-MM-dd + +spring: + cache: + type: REDIS + redis: + time-to-live: 60000 + redis: + host: localhost + password: Redis2019! + port: 6379 + jpa: + show-sql: true + hibernate: + ddl-auto: validate + properties: + hibernate: + dialect: org.hibernate.dialect.MySQL5InnoDBDialect + datasource: + url: jdbc:mysql://localhost:3306/gastos + username: gastos + password: gastos + initialization-mode: EMBEDDED diff --git a/gastos/src/main/resources/db/migration/V1__init_script.sql b/gastos/src/main/resources/db/migration/V1__init_script.sql new file mode 100644 index 00000000..625ee4ab --- /dev/null +++ b/gastos/src/main/resources/db/migration/V1__init_script.sql @@ -0,0 +1,73 @@ +CREATE TABLE card ( + id bigint(20) NOT NULL AUTO_INCREMENT, + card_number varchar(255) DEFAULT NULL, + dat_creation datetime NOT NULL, + dat_update datetime NOT NULL, + PRIMARY KEY (id), + UNIQUE KEY card_UK (card_number) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +CREATE TABLE spent ( + id bigint(20) NOT NULL AUTO_INCREMENT, + dat_creation datetime NOT NULL, + description varchar(255) NOT NULL, + classification bigint(20) DEFAULT NULL, + spent_date datetime DEFAULT NULL, + spent_value double DEFAULT NULL, + dat_update datetime NOT NULL, + PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +CREATE TABLE client ( + id bigint(20) NOT NULL AUTO_INCREMENT, + dat_creation datetime NOT NULL, + dat_update datetime NOT NULL, + PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +CREATE TABLE classification ( + id bigint(20) NOT NULL AUTO_INCREMENT, + name varchar(255) DEFAULT NULL, + dat_creation datetime NOT NULL, + dat_update datetime NOT NULL, + PRIMARY KEY (id) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +CREATE TABLE card_spent ( + id bigint(20) NOT NULL AUTO_INCREMENT, + dat_creation datetime NOT NULL, + dat_update datetime NOT NULL, + card bigint(20) DEFAULT NULL, + spent bigint(20) DEFAULT NULL, + PRIMARY KEY (id), + KEY FK_card (card), + KEY FK_spent (spent), + CONSTRAINT FK_spent FOREIGN KEY (spent) REFERENCES spent (id), + CONSTRAINT FK_card FOREIGN KEY (card) REFERENCES card (id) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +CREATE TABLE client_card ( + id bigint(20) NOT NULL AUTO_INCREMENT, + dat_creation datetime NOT NULL, + dat_update datetime NOT NULL, + client bigint(20) DEFAULT NULL, + card bigint(20) DEFAULT NULL, + PRIMARY KEY (id), + KEY FK_client (client), + KEY FK_card_2 (card), + CONSTRAINT FK_card_2 FOREIGN KEY (card) REFERENCES card (id), + CONSTRAINT FK_client FOREIGN KEY (client) REFERENCES client (id) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; + +CREATE TABLE client_classification ( + id bigint(20) NOT NULL AUTO_INCREMENT, + dat_creation datetime NOT NULL, + dat_update datetime NOT NULL, + client bigint(20) DEFAULT NULL, + classification bigint(20) DEFAULT NULL, + PRIMARY KEY (id), + KEY FK_client_3 (client), + KEY FK_classification (classification), + CONSTRAINT FK_classification FOREIGN KEY (classification) REFERENCES classification (id), + CONSTRAINT FK_client_3 FOREIGN KEY (client) REFERENCES client (id) +) ENGINE=InnoDB DEFAULT CHARSET=latin1; \ No newline at end of file diff --git a/gastos/src/main/resources/db/migration/V2__init_script.sql b/gastos/src/main/resources/db/migration/V2__init_script.sql new file mode 100644 index 00000000..842467ec --- /dev/null +++ b/gastos/src/main/resources/db/migration/V2__init_script.sql @@ -0,0 +1,39 @@ +INSERT INTO gastos.card (card_number,dat_creation,dat_update) VALUES +('1','2013-06-22 20:58:21.000','2006-11-06 13:38:49.000') +,('2','1984-02-07 15:42:40.000','2006-01-17 16:14:02.000') +,('3','2004-07-22 23:07:29.000','2014-02-03 18:56:08.000') +,('4','1983-09-05 00:08:47.000','2014-08-16 05:15:51.000') +,('5','1980-05-26 19:08:51.000','2013-07-24 01:11:33.000') +,('6','1997-08-04 18:55:48.000','1999-03-16 22:38:51.000') +,('7','2019-01-06 08:45:08.000','2001-04-28 10:27:05.000') +,('8','2002-04-30 12:12:36.000','1980-11-30 14:56:37.000') +,('9','2013-06-15 08:35:55.000','1996-03-16 14:40:45.000') +,('10','2009-01-17 03:29:33.000','1992-04-30 22:45:46.000'); + +INSERT INTO gastos.client (dat_creation,dat_update) VALUES +('2010-01-26 03:03:37.000','1995-05-09 18:54:48.000') +,('1985-04-22 08:36:52.000','2006-06-14 03:05:45.000') +,('2005-09-09 15:56:26.000','1998-05-04 09:20:31.000') +,('1991-02-01 03:24:08.000','1994-01-04 06:10:38.000') +,('1996-10-27 06:58:41.000','1986-02-25 04:16:15.000') +,('1999-10-23 18:30:11.000','2017-06-03 23:06:17.000') +,('1992-07-05 15:04:39.000','2017-12-31 12:34:51.000') +,('1985-02-19 09:32:50.000','1993-11-04 15:42:28.000') +,('1992-04-28 15:26:02.000','2012-11-09 23:07:22.000') +,('1992-09-04 00:51:32.000','1984-02-04 04:46:11.000'); + +INSERT INTO gastos.client_card (dat_creation,dat_update,client,card) VALUES +('1995-04-08 23:39:13.000','1995-11-09 12:21:31.000',1,1) +,('2015-03-23 10:51:45.000','1990-04-22 16:11:22.000',2,8); + +INSERT INTO gastos.classification (name,dat_creation,dat_update) VALUES +('laboris nisi','1998-08-22 08:32:33.000','1995-02-18 19:52:51.000') +,('Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pari','2002-04-27 22:42:05.000','2003-09-22 05:19:05.000') +,('cupidatat non proident, sunt in culpa qui ','1999-05-27 06:31:04.000','2013-10-04 22:44:45.000') +,('incididunt ut labore et dolore magna aliqua. Ut enim ad minim v','1999-03-31 10:09:37.000','1998-07-11 20:48:46.000') +,('ex ea commodo consequat. Duis aute irure dolor in reprehenderit in','1980-12-02 23:23:49.000','1992-02-06 16:20:42.000') +,('velit esse ci','2007-08-20 23:20:58.000','1980-12-19 04:56:13.000') +,('in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Exc','2002-03-14 01:08:02.000','1994-12-28 09:17:43.000') +,('magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris ','2003-03-03 00:41:31.000','2018-06-26 20:23:33.000') +,('Excepteur sint occaecat cu','2003-07-22 07:39:24.000','2005-08-10 07:04:08.000') +,('dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa','1997-07-01 01:25:24.000','1983-04-13 10:37:49.000') \ No newline at end of file diff --git a/gastos/src/test/java/io/santander/gastos/SantanderGastosApplicationTests.java b/gastos/src/test/java/io/santander/gastos/SantanderGastosApplicationTests.java new file mode 100644 index 00000000..cc069f7e --- /dev/null +++ b/gastos/src/test/java/io/santander/gastos/SantanderGastosApplicationTests.java @@ -0,0 +1,16 @@ +package io.santander.gastos; + +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 SantanderGastosApplicationTests { + + @Test + public void contextLoads() { + } + +} diff --git a/gastos/src/test/java/io/santander/gastos/base/SpentBaseTest.java b/gastos/src/test/java/io/santander/gastos/base/SpentBaseTest.java new file mode 100644 index 00000000..28118183 --- /dev/null +++ b/gastos/src/test/java/io/santander/gastos/base/SpentBaseTest.java @@ -0,0 +1,64 @@ +package io.santander.gastos.base; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.Before; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.Resource; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.junit4.SpringRunner; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.web.context.WebApplicationContext; + +import javax.servlet.Filter; +import java.io.IOException; +import java.io.InputStream; + +import static com.fasterxml.jackson.core.JsonGenerator.Feature.IGNORE_UNKNOWN; +import static com.fasterxml.jackson.core.JsonParser.Feature.IGNORE_UNDEFINED; +import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES; +import static java.lang.String.format; +import static java.nio.file.Paths.get; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; +import static org.springframework.test.web.servlet.setup.MockMvcBuilders.webAppContextSetup; + +@ActiveProfiles("test") +@RunWith(SpringRunner.class) +@SpringBootTest(webEnvironment = RANDOM_PORT) +public abstract class SpentBaseTest { + + protected static final String PATH_TEMPLATE = "./src/test/resources/json/%s.json"; + + protected static final ObjectMapper MAPPER; + + @Autowired + protected WebApplicationContext context; + + @Autowired + protected Filter[] filters; + + protected MockMvc mockMvc; + + static { + MAPPER = new ObjectMapper() + .configure(FAIL_ON_UNKNOWN_PROPERTIES, false) + .configure(IGNORE_UNDEFINED, true) + .configure(IGNORE_UNKNOWN, true); + } + + @Before + public void setUp() { + this.mockMvc = webAppContextSetup(this.context).addFilters(this.filters).build(); + } + + + public static T createMockFromFile(final String fileName, final Class resultClass) + throws IOException { + final Resource resource = new FileSystemResource(get(format(PATH_TEMPLATE, fileName)).toFile()); + try (final InputStream stream = resource.getInputStream()) { + return MAPPER.readValue(stream, resultClass); + } + } +} \ No newline at end of file diff --git a/gastos/src/test/resources/application.yml b/gastos/src/test/resources/application.yml new file mode 100644 index 00000000..b83bbbf5 --- /dev/null +++ b/gastos/src/test/resources/application.yml @@ -0,0 +1,19 @@ +info: + artifactId: '${project.name}' + version: '${project.version}' + +spring: + cache: + type: SIMPLE + datasource: + initialize: true + url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1 + driver-class-name: org.h2.Driver + username: sa + jpa: + show-sql: true + hibernate: + ddl-auto: create + flyway: + enabled: false + baselineOnMigrate: true \ No newline at end of file diff --git a/gastos/src/test/resources/data.sql b/gastos/src/test/resources/data.sql new file mode 100644 index 00000000..3088e34d --- /dev/null +++ b/gastos/src/test/resources/data.sql @@ -0,0 +1,39 @@ +INSERT INTO card (card_number,dat_creation,dat_update) VALUES +('1','2013-06-22 20:58:21.000','2006-11-06 13:38:49.000') +,('2','1984-02-07 15:42:40.000','2006-01-17 16:14:02.000') +,('3','2004-07-22 23:07:29.000','2014-02-03 18:56:08.000') +,('4','1983-09-05 00:08:47.000','2014-08-16 05:15:51.000') +,('5','1980-05-26 19:08:51.000','2013-07-24 01:11:33.000') +,('6','1997-08-04 18:55:48.000','1999-03-16 22:38:51.000') +,('7','2019-01-06 08:45:08.000','2001-04-28 10:27:05.000') +,('8','2002-04-30 12:12:36.000','1980-11-30 14:56:37.000') +,('9','2013-06-15 08:35:55.000','1996-03-16 14:40:45.000') +,('10','2009-01-17 03:29:33.000','1992-04-30 22:45:46.000'); + +INSERT INTO client (dat_creation,dat_update) VALUES +('2010-01-26 03:03:37.000','1995-05-09 18:54:48.000') +,('1985-04-22 08:36:52.000','2006-06-14 03:05:45.000') +,('2005-09-09 15:56:26.000','1998-05-04 09:20:31.000') +,('1991-02-01 03:24:08.000','1994-01-04 06:10:38.000') +,('1996-10-27 06:58:41.000','1986-02-25 04:16:15.000') +,('1999-10-23 18:30:11.000','2017-06-03 23:06:17.000') +,('1992-07-05 15:04:39.000','2017-12-31 12:34:51.000') +,('1985-02-19 09:32:50.000','1993-11-04 15:42:28.000') +,('1992-04-28 15:26:02.000','2012-11-09 23:07:22.000') +,('1992-09-04 00:51:32.000','1984-02-04 04:46:11.000'); + +INSERT INTO client_card (dat_creation,dat_update,client,card) VALUES +('1995-04-08 23:39:13.000','1995-11-09 12:21:31.000',1,1) +,('2015-03-23 10:51:45.000','1990-04-22 16:11:22.000',2,8); + +INSERT INTO classification (name,dat_creation,dat_update) VALUES +('laboris nisi','1998-08-22 08:32:33.000','1995-02-18 19:52:51.000') +,('Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pari','2002-04-27 22:42:05.000','2003-09-22 05:19:05.000') +,('cupidatat non proident, sunt in culpa qui ','1999-05-27 06:31:04.000','2013-10-04 22:44:45.000') +,('incididunt ut labore et dolore magna aliqua. Ut enim ad minim v','1999-03-31 10:09:37.000','1998-07-11 20:48:46.000') +,('ex ea commodo consequat. Duis aute irure dolor in reprehenderit in','1980-12-02 23:23:49.000','1992-02-06 16:20:42.000') +,('velit esse ci','2007-08-20 23:20:58.000','1980-12-19 04:56:13.000') +,('in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Exc','2002-03-14 01:08:02.000','1994-12-28 09:17:43.000') +,('magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris ','2003-03-03 00:41:31.000','2018-06-26 20:23:33.000') +,('Excepteur sint occaecat cu','2003-07-22 07:39:24.000','2005-08-10 07:04:08.000') +,('dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa','1997-07-01 01:25:24.000','1983-04-13 10:37:49.000') \ No newline at end of file