mirror of
https://github.com/valitydev/liminator.git
synced 2024-11-06 01:15:21 +00:00
TD-955: Add implementation (#1)
This commit is contained in:
parent
23d86bd8ca
commit
44c3657ec2
2
.github/settings.yml
vendored
Normal file
2
.github/settings.yml
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# These settings are synced to GitHub by https://probot.github.io/apps/settings/
|
||||||
|
_extends: .github
|
10
.github/workflows/build.yml
vendored
Normal file
10
.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
name: Build Maven Artifact
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- '**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
uses: valitydev/java-workflow/.github/workflows/maven-service-build.yml@v3
|
15
.github/workflows/deploy.yml
vendored
Normal file
15
.github/workflows/deploy.yml
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
name: Deploy Docker Image
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'master'
|
||||||
|
- 'main'
|
||||||
|
- 'epic/**'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-and-deploy:
|
||||||
|
uses: valitydev/java-workflow/.github/workflows/maven-service-deploy.yml@v3
|
||||||
|
secrets:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
mm-webhook-url: ${{ secrets.MATTERMOST_WEBHOOK_URL }}
|
83
.gitignore
vendored
Normal file
83
.gitignore
vendored
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
# Created by .ignore support plugin (hsz.mobi)
|
||||||
|
### Maven template
|
||||||
|
target/
|
||||||
|
pom.xml.tag
|
||||||
|
pom.xml.releaseBackup
|
||||||
|
pom.xml.versionsBackup
|
||||||
|
pom.xml.next
|
||||||
|
release.properties
|
||||||
|
dependency-reduced-pom.xml
|
||||||
|
buildNumber.properties
|
||||||
|
.mvn/timing.properties
|
||||||
|
### JetBrains template
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff:
|
||||||
|
.idea/
|
||||||
|
.idea/workspace.xml
|
||||||
|
.idea/tasks.xml
|
||||||
|
.idea/dictionaries
|
||||||
|
.idea/vcs.xml
|
||||||
|
.idea/jsLibraryMappings.xml
|
||||||
|
|
||||||
|
# Sensitive or high-churn files:
|
||||||
|
.idea/dataSources.ids
|
||||||
|
.idea/dataSources.xml
|
||||||
|
.idea/dataSources.local.xml
|
||||||
|
.idea/sqlDataSources.xml
|
||||||
|
.idea/dynamic.xml
|
||||||
|
.idea/uiDesigner.xml
|
||||||
|
|
||||||
|
# Gradle:
|
||||||
|
.idea/gradle.xml
|
||||||
|
.idea/libraries
|
||||||
|
|
||||||
|
# Mongo Explorer plugin:
|
||||||
|
.idea/mongoSettings.xml
|
||||||
|
|
||||||
|
## File-based project format:
|
||||||
|
*.iws
|
||||||
|
*.ipr
|
||||||
|
*.iml
|
||||||
|
|
||||||
|
## Plugin-specific files:
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
/out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
### Java template
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Mobile Tools for Java (J2ME)
|
||||||
|
.mtj.tmp/
|
||||||
|
|
||||||
|
# Package Files #
|
||||||
|
*.jar
|
||||||
|
*.war
|
||||||
|
*.ear
|
||||||
|
|
||||||
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
|
hs_err_pid*
|
||||||
|
|
||||||
|
env.list
|
||||||
|
|
||||||
|
|
||||||
|
# OSX
|
||||||
|
*.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# TestContainers
|
||||||
|
.testcontainers-*
|
385
pom.xml
Normal file
385
pom.xml
Normal file
@ -0,0 +1,385 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>dev.vality</groupId>
|
||||||
|
<artifactId>service-parent-pom</artifactId>
|
||||||
|
<version>3.0.2</version>
|
||||||
|
<relativePath/>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>liminator</artifactId>
|
||||||
|
<version>1.0.0</version>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<name>liminator</name>
|
||||||
|
<description>Service for interation with the dominant data</description>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<dockerfile.registry>${env.REGISTRY}</dockerfile.registry>
|
||||||
|
<server.port>8022</server.port>
|
||||||
|
<management.port>8023</management.port>
|
||||||
|
<exposed.ports>${server.port} ${management.port}</exposed.ports>
|
||||||
|
<db.url>jdbc:postgresql://localhost:5432/liminator</db.url>
|
||||||
|
<db.user>postgres</db.user>
|
||||||
|
<db.password>postgres</db.password>
|
||||||
|
<db.name>liminator</db.name>
|
||||||
|
<db.schema>lim</db.schema>
|
||||||
|
<local.pg.url>jdbc:postgresql://localhost:5432/liminator</local.pg.url>
|
||||||
|
<local.pg.port>5432</local.pg.port>
|
||||||
|
<checkstyle.config.suppressions.path>./src/main/resources/checkstyle/checkstyle-suppressions.xml
|
||||||
|
</checkstyle.config.suppressions.path>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!--spring -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.hibernate</groupId>
|
||||||
|
<artifactId>hibernate-validator</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.yaml</groupId>
|
||||||
|
<artifactId>snakeyaml</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-validation</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.kafka</groupId>
|
||||||
|
<artifactId>spring-kafka</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-cache</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-web</artifactId>
|
||||||
|
<version>6.1.6</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-webmvc</artifactId>
|
||||||
|
<version>6.1.6</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!--Thrirdparty libs-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>jakarta.servlet</groupId>
|
||||||
|
<artifactId>jakarta.servlet-api</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.postgresql</groupId>
|
||||||
|
<artifactId>postgresql</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.zaxxer</groupId>
|
||||||
|
<artifactId>HikariCP</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.flywaydb</groupId>
|
||||||
|
<artifactId>flyway-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.flywaydb</groupId>
|
||||||
|
<artifactId>flyway-database-postgresql</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jooq</groupId>
|
||||||
|
<artifactId>jooq</artifactId>
|
||||||
|
<version>3.19.11</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.logstash.logback</groupId>
|
||||||
|
<artifactId>logstash-logback-encoder</artifactId>
|
||||||
|
<version>7.4</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.fasterxml.jackson.core</groupId>
|
||||||
|
<artifactId>jackson-databind</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||||
|
<artifactId>caffeine</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.commons</groupId>
|
||||||
|
<artifactId>commons-lang3</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.javacrumbs.shedlock</groupId>
|
||||||
|
<artifactId>shedlock-provider-jdbc-template</artifactId>
|
||||||
|
<version>4.46.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.javacrumbs.shedlock</groupId>
|
||||||
|
<artifactId>shedlock-spring</artifactId>
|
||||||
|
<version>4.46.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.micrometer</groupId>
|
||||||
|
<artifactId>micrometer-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.micrometer</groupId>
|
||||||
|
<artifactId>micrometer-registry-prometheus</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.yaml</groupId>
|
||||||
|
<artifactId>snakeyaml</artifactId>
|
||||||
|
<version>2.0</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>software.amazon.msk</groupId>
|
||||||
|
<artifactId>aws-msk-iam-auth</artifactId>
|
||||||
|
<version>2.0.3</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!--dev.vality-->
|
||||||
|
<dependency>
|
||||||
|
<groupId>dev.vality</groupId>
|
||||||
|
<artifactId>db-common-lib</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>dev.vality</groupId>
|
||||||
|
<artifactId>liminator-proto</artifactId>
|
||||||
|
<version>1.2-63529e6</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>dev.vality.woody</groupId>
|
||||||
|
<artifactId>woody-thrift</artifactId>
|
||||||
|
<version>${woody.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>dev.vality</groupId>
|
||||||
|
<artifactId>limiter-proto</artifactId>
|
||||||
|
<version>1.33-31de59b</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>dev.vality</groupId>
|
||||||
|
<artifactId>shared-resources</artifactId>
|
||||||
|
<version>${shared-resources.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>dev.vality.geck</groupId>
|
||||||
|
<artifactId>serializer</artifactId>
|
||||||
|
<version>1.0.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>dev.vality.geck</groupId>
|
||||||
|
<artifactId>filter</artifactId>
|
||||||
|
<version>1.0.1</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Test -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-test</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>dev.vality</groupId>
|
||||||
|
<artifactId>testcontainers-annotations</artifactId>
|
||||||
|
<version>2.0.3</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.awaitility</groupId>
|
||||||
|
<artifactId>awaitility</artifactId>
|
||||||
|
<version>4.2.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<resources>
|
||||||
|
<resource>
|
||||||
|
<directory>${project.build.directory}/maven-shared-archive-resources</directory>
|
||||||
|
<targetPath>${project.build.directory}</targetPath>
|
||||||
|
<includes>
|
||||||
|
<include>Dockerfile</include>
|
||||||
|
</includes>
|
||||||
|
<filtering>true</filtering>
|
||||||
|
</resource>
|
||||||
|
<resource>
|
||||||
|
<directory>${project.build.directory}/maven-shared-archive-resources</directory>
|
||||||
|
<filtering>true</filtering>
|
||||||
|
<excludes>
|
||||||
|
<exclude>Dockerfile</exclude>
|
||||||
|
</excludes>
|
||||||
|
</resource>
|
||||||
|
<resource>
|
||||||
|
<directory>src/main/resources</directory>
|
||||||
|
<filtering>true</filtering>
|
||||||
|
</resource>
|
||||||
|
</resources>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>dev.vality.maven.plugins</groupId>
|
||||||
|
<artifactId>pg-embedded-plugin</artifactId>
|
||||||
|
<version>2.0.0</version>
|
||||||
|
<configuration>
|
||||||
|
<port>${local.pg.port}</port>
|
||||||
|
<dbName>${db.name}</dbName>
|
||||||
|
<schemas>
|
||||||
|
<schema>${db.schema}</schema>
|
||||||
|
</schemas>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>PG_server_start</id>
|
||||||
|
<phase>initialize</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>start</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
<execution>
|
||||||
|
<id>PG_server_stop</id>
|
||||||
|
<phase>generate-resources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>stop</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.flywaydb</groupId>
|
||||||
|
<artifactId>flyway-maven-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<url>${local.pg.url}</url>
|
||||||
|
<user>${db.user}</user>
|
||||||
|
<password>${db.password}</password>
|
||||||
|
<schemas>
|
||||||
|
<schema>${db.schema}</schema>
|
||||||
|
</schemas>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>migrate</id>
|
||||||
|
<phase>initialize</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>migrate</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.postgresql</groupId>
|
||||||
|
<artifactId>postgresql</artifactId>
|
||||||
|
<version>42.7.3</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.jooq</groupId>
|
||||||
|
<artifactId>jooq-codegen-maven</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<jdbc>
|
||||||
|
<driver>org.postgresql.Driver</driver>
|
||||||
|
<url>${local.pg.url}</url>
|
||||||
|
<user>${db.user}</user>
|
||||||
|
<password>${db.password}</password>
|
||||||
|
</jdbc>
|
||||||
|
<generator>
|
||||||
|
<generate>
|
||||||
|
<javaTimeTypes>true</javaTimeTypes>
|
||||||
|
<pojos>true</pojos>
|
||||||
|
<pojosEqualsAndHashCode>true</pojosEqualsAndHashCode>
|
||||||
|
<pojosToString>true</pojosToString>
|
||||||
|
</generate>
|
||||||
|
<database>
|
||||||
|
<name>org.jooq.meta.postgres.PostgresDatabase</name>
|
||||||
|
<includes>.*</includes>
|
||||||
|
<excludes>
|
||||||
|
schema_version|.*func
|
||||||
|
</excludes>
|
||||||
|
<inputSchema>${db.schema}</inputSchema>
|
||||||
|
</database>
|
||||||
|
<target>
|
||||||
|
<packageName>com.empayre.liminator.domain</packageName>
|
||||||
|
<directory>target/generated-sources/</directory>
|
||||||
|
</target>
|
||||||
|
</generator>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>gen-src</id>
|
||||||
|
<phase>initialize</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>generate</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-surefire-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<argLine>-Dfile.encoding=UTF-8</argLine>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-remote-resources-plugin</artifactId>
|
||||||
|
<version>3.1.0</version>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.maven.shared</groupId>
|
||||||
|
<artifactId>maven-filtering</artifactId>
|
||||||
|
<version>3.3.1</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
<configuration>
|
||||||
|
<resourceBundles>
|
||||||
|
<resourceBundle>dev.vality:shared-resources:${shared-resources.version}</resourceBundle>
|
||||||
|
</resourceBundles>
|
||||||
|
<attachToMain>false</attachToMain>
|
||||||
|
<attachToTest>false</attachToTest>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<goals>
|
||||||
|
<goal>process</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-resources-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<nonFilteredFileExtensions>
|
||||||
|
<nonFilteredFileExtension>p12</nonFilteredFileExtension>
|
||||||
|
</nonFilteredFileExtensions>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
4
renovate.json
Normal file
4
renovate.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||||
|
"extends": ["local>valitydev/.github:renovate-config"]
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.empayre.liminator;
|
||||||
|
|
||||||
|
import org.springframework.boot.SpringApplication;
|
||||||
|
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||||
|
import org.springframework.boot.web.servlet.ServletComponentScan;
|
||||||
|
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||||
|
|
||||||
|
@EnableScheduling
|
||||||
|
@ServletComponentScan
|
||||||
|
@SpringBootApplication(scanBasePackages = {"com.empayre.liminator"})
|
||||||
|
public class LiminatorApplication {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SpringApplication.run(LiminatorApplication.class, args);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package com.empayre.liminator.config;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class ApplicationConfig {
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public ObjectMapper mapper() {
|
||||||
|
return new ObjectMapper();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,22 @@
|
|||||||
|
package com.empayre.liminator.converter;
|
||||||
|
|
||||||
|
import com.empayre.liminator.domain.tables.pojos.LimitData;
|
||||||
|
import dev.vality.liminator.CreateLimitRequest;
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class CreateLimitRequestToLimitDataConverter implements Converter<CreateLimitRequest, LimitData> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LimitData convert(CreateLimitRequest request) {
|
||||||
|
LimitData data = new LimitData();
|
||||||
|
data.setName(request.getLimitName());
|
||||||
|
data.setCreatedAt(LocalDate.now());
|
||||||
|
data.setWtime(LocalDateTime.now());
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.empayre.liminator.converter;
|
||||||
|
|
||||||
|
import com.empayre.liminator.model.LimitValue;
|
||||||
|
import dev.vality.liminator.LimitResponse;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
public class CurrentLimitValuesToLimitResponseConverter implements Converter<List<LimitValue>, List<LimitResponse>> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<LimitResponse> convert(List<LimitValue> values) {
|
||||||
|
if (values == null) {
|
||||||
|
log.info("Received LimitValues array is empty");
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
return values.stream()
|
||||||
|
.map(limitValue -> new LimitResponse(
|
||||||
|
limitValue.getLimitName(),
|
||||||
|
limitValue.getCommitValue(),
|
||||||
|
limitValue.getHoldValue())
|
||||||
|
)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package com.empayre.liminator.converter;
|
||||||
|
|
||||||
|
import com.empayre.liminator.domain.tables.pojos.Operation;
|
||||||
|
import dev.vality.liminator.LimitRequest;
|
||||||
|
|
||||||
|
public interface OperationConverter {
|
||||||
|
|
||||||
|
Operation convert(LimitRequest request, Long limitId);
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package com.empayre.liminator.converter.impl;
|
||||||
|
|
||||||
|
import com.empayre.liminator.converter.OperationConverter;
|
||||||
|
import com.empayre.liminator.domain.enums.OperationState;
|
||||||
|
import com.empayre.liminator.domain.tables.pojos.Operation;
|
||||||
|
import dev.vality.liminator.LimitRequest;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class OperationConverterImpl implements OperationConverter {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Operation convert(LimitRequest request, Long limitId) {
|
||||||
|
Operation operation = new Operation();
|
||||||
|
operation.setLimitId(limitId);
|
||||||
|
operation.setOperationId(request.getOperationId());
|
||||||
|
operation.setAmount(request.getValue());
|
||||||
|
operation.setCreatedAt(LocalDateTime.now());
|
||||||
|
operation.setState(OperationState.HOLD);
|
||||||
|
return operation;
|
||||||
|
}
|
||||||
|
}
|
23
src/main/java/com/empayre/liminator/dao/AbstractDao.java
Normal file
23
src/main/java/com/empayre/liminator/dao/AbstractDao.java
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package com.empayre.liminator.dao;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import org.jooq.Configuration;
|
||||||
|
import org.jooq.DSLContext;
|
||||||
|
import org.jooq.SQLDialect;
|
||||||
|
import org.jooq.impl.DSL;
|
||||||
|
import org.jooq.impl.DefaultConfiguration;
|
||||||
|
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public abstract class AbstractDao {
|
||||||
|
|
||||||
|
private final DSLContext dslContext;
|
||||||
|
|
||||||
|
public AbstractDao(DataSource dataSource) {
|
||||||
|
Configuration configuration = new DefaultConfiguration();
|
||||||
|
configuration.set(dataSource);
|
||||||
|
configuration.set(SQLDialect.POSTGRES);
|
||||||
|
this.dslContext = DSL.using(configuration);
|
||||||
|
}
|
||||||
|
}
|
8
src/main/java/com/empayre/liminator/dao/CommonDao.java
Normal file
8
src/main/java/com/empayre/liminator/dao/CommonDao.java
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package com.empayre.liminator.dao;
|
||||||
|
|
||||||
|
import com.empayre.liminator.exception.DaoException;
|
||||||
|
|
||||||
|
public interface CommonDao<T> {
|
||||||
|
|
||||||
|
Long save(T domainObject) throws DaoException;
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package com.empayre.liminator.dao;
|
||||||
|
|
||||||
|
import com.empayre.liminator.domain.tables.pojos.LimitContext;
|
||||||
|
|
||||||
|
public interface LimitContextDao extends CommonDao<LimitContext> {
|
||||||
|
|
||||||
|
LimitContext getLimitContext(Long limitId);
|
||||||
|
}
|
13
src/main/java/com/empayre/liminator/dao/LimitDataDao.java
Normal file
13
src/main/java/com/empayre/liminator/dao/LimitDataDao.java
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package com.empayre.liminator.dao;
|
||||||
|
|
||||||
|
import com.empayre.liminator.domain.tables.pojos.LimitData;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface LimitDataDao extends CommonDao<LimitData> {
|
||||||
|
|
||||||
|
LimitData get(String limitName);
|
||||||
|
|
||||||
|
List<LimitData> get(Collection<String> limitNames);
|
||||||
|
}
|
21
src/main/java/com/empayre/liminator/dao/OperationDao.java
Normal file
21
src/main/java/com/empayre/liminator/dao/OperationDao.java
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package com.empayre.liminator.dao;
|
||||||
|
|
||||||
|
import com.empayre.liminator.domain.tables.pojos.Operation;
|
||||||
|
import com.empayre.liminator.model.LimitValue;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface OperationDao extends CommonDao<Operation> {
|
||||||
|
|
||||||
|
void saveBatch(List<Operation> operations);
|
||||||
|
|
||||||
|
Operation get(Long id);
|
||||||
|
|
||||||
|
List<LimitValue> getCurrentLimitValue(List<String> limitNames);
|
||||||
|
|
||||||
|
List<LimitValue> getCurrentLimitValue(List<String> limitNames, String operationId);
|
||||||
|
|
||||||
|
int commit(List<String> operationIds);
|
||||||
|
|
||||||
|
int rollback(List<String> operationIds);
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package com.empayre.liminator.dao.impl;
|
||||||
|
|
||||||
|
import com.empayre.liminator.dao.AbstractDao;
|
||||||
|
import com.empayre.liminator.dao.LimitContextDao;
|
||||||
|
import com.empayre.liminator.domain.tables.pojos.LimitContext;
|
||||||
|
import com.empayre.liminator.exception.DaoException;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
|
import static com.empayre.liminator.domain.Tables.LIMIT_CONTEXT;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class LimitContextDaoImpl extends AbstractDao implements LimitContextDao {
|
||||||
|
|
||||||
|
public LimitContextDaoImpl(DataSource dataSource) {
|
||||||
|
super(dataSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long save(LimitContext limitContext) throws DaoException {
|
||||||
|
return getDslContext()
|
||||||
|
.insertInto(LIMIT_CONTEXT)
|
||||||
|
.set(getDslContext().newRecord(LIMIT_CONTEXT, limitContext))
|
||||||
|
.returning(LIMIT_CONTEXT.ID)
|
||||||
|
.fetchOne()
|
||||||
|
.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LimitContext getLimitContext(Long limitId) {
|
||||||
|
return getDslContext()
|
||||||
|
.selectFrom(LIMIT_CONTEXT)
|
||||||
|
.where(LIMIT_CONTEXT.LIMIT_ID.eq(limitId))
|
||||||
|
.fetchOneInto(LimitContext.class);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,52 @@
|
|||||||
|
package com.empayre.liminator.dao.impl;
|
||||||
|
|
||||||
|
import com.empayre.liminator.dao.AbstractDao;
|
||||||
|
import com.empayre.liminator.dao.LimitDataDao;
|
||||||
|
import com.empayre.liminator.domain.tables.pojos.LimitData;
|
||||||
|
import com.empayre.liminator.exception.DaoException;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static com.empayre.liminator.domain.Tables.LIMIT_DATA;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class LimitDataDaoImpl extends AbstractDao implements LimitDataDao {
|
||||||
|
|
||||||
|
public LimitDataDaoImpl(DataSource dataSource) {
|
||||||
|
super(dataSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long save(LimitData limitData) throws DaoException {
|
||||||
|
return getDslContext()
|
||||||
|
.insertInto(LIMIT_DATA)
|
||||||
|
.set(getDslContext().newRecord(LIMIT_DATA, limitData))
|
||||||
|
.onConflict(LIMIT_DATA.NAME)
|
||||||
|
.doUpdate()
|
||||||
|
.set(LIMIT_DATA.WTIME, LocalDateTime.now())
|
||||||
|
.returning(LIMIT_DATA.ID)
|
||||||
|
.fetchOne()
|
||||||
|
.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LimitData get(String limitName) {
|
||||||
|
return getDslContext()
|
||||||
|
.selectFrom(LIMIT_DATA)
|
||||||
|
.where(LIMIT_DATA.NAME.equal(limitName))
|
||||||
|
.fetchOneInto(LimitData.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<LimitData> get(Collection<String> limitNames) {
|
||||||
|
return getDslContext()
|
||||||
|
.selectFrom(LIMIT_DATA)
|
||||||
|
.where(LIMIT_DATA.NAME.in(limitNames))
|
||||||
|
.fetchInto(LimitData.class);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,143 @@
|
|||||||
|
package com.empayre.liminator.dao.impl;
|
||||||
|
|
||||||
|
import com.empayre.liminator.dao.AbstractDao;
|
||||||
|
import com.empayre.liminator.dao.OperationDao;
|
||||||
|
import com.empayre.liminator.domain.enums.OperationState;
|
||||||
|
import com.empayre.liminator.domain.tables.pojos.Operation;
|
||||||
|
import com.empayre.liminator.domain.tables.records.OperationRecord;
|
||||||
|
import com.empayre.liminator.exception.DaoException;
|
||||||
|
import com.empayre.liminator.model.LimitValue;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import javax.sql.DataSource;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static com.empayre.liminator.domain.Tables.OPERATION;
|
||||||
|
import static org.jooq.impl.DSL.raw;
|
||||||
|
import static org.jooq.impl.DSL.val;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class OperationDaoImpl extends AbstractDao implements OperationDao {
|
||||||
|
|
||||||
|
public OperationDaoImpl(DataSource dataSource) {
|
||||||
|
super(dataSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String DELIMITER = " ,";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long save(Operation operation) throws DaoException {
|
||||||
|
return getDslContext()
|
||||||
|
.insertInto(OPERATION)
|
||||||
|
.set(getDslContext().newRecord(OPERATION, operation))
|
||||||
|
.returning(OPERATION.ID)
|
||||||
|
.fetchOne()
|
||||||
|
.getId();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Operation get(Long id) {
|
||||||
|
return getDslContext()
|
||||||
|
.selectFrom(OPERATION)
|
||||||
|
.where(OPERATION.ID.eq(id))
|
||||||
|
.fetchOneInto(Operation.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void saveBatch(List<Operation> operations) {
|
||||||
|
List<OperationRecord> records = operations.stream()
|
||||||
|
.map(operation -> getDslContext().newRecord(OPERATION, operation))
|
||||||
|
.toList();
|
||||||
|
getDslContext().batchInsert(records).execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int commit(List<String> operationIds) {
|
||||||
|
return updateStateForHoldOperation(operationIds, OperationState.COMMIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int rollback(List<String> operationIds) {
|
||||||
|
return updateStateForHoldOperation(operationIds, OperationState.ROLLBACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
private int updateStateForHoldOperation(List<String> operationIds, OperationState state) {
|
||||||
|
return getDslContext()
|
||||||
|
.update(OPERATION)
|
||||||
|
.set(OPERATION.STATE, state)
|
||||||
|
.where(OPERATION.OPERATION_ID.in(operationIds))
|
||||||
|
.and(OPERATION.STATE.eq(OperationState.HOLD))
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<LimitValue> getCurrentLimitValue(List<String> limitNames) {
|
||||||
|
String sql = """
|
||||||
|
with hold_data as (
|
||||||
|
select ld.id, ld.name, coalesce(sum(ops.amount), 0) as hold_value
|
||||||
|
from lim.limit_data as ld
|
||||||
|
left join lim.operation as ops
|
||||||
|
on ops.limit_id = ld.id and ops.state = 'HOLD'
|
||||||
|
where ld.name in ({0})
|
||||||
|
group by ld.id, ld.name
|
||||||
|
), commit_data as (
|
||||||
|
select ld.id, ld.name, coalesce(sum(ops.amount), 0) as commit_value
|
||||||
|
from lim.limit_data as ld
|
||||||
|
left join lim.operation as ops
|
||||||
|
on ops.limit_id = ld.id and ops.state = 'COMMIT'
|
||||||
|
where ld.name in ({0})
|
||||||
|
group by ld.id, ld.name
|
||||||
|
)
|
||||||
|
|
||||||
|
select cd.name as limit_name, cd.commit_value, hd.hold_value
|
||||||
|
from commit_data as cd
|
||||||
|
join hold_data as hd on cd.id = hd.id;
|
||||||
|
""";
|
||||||
|
return getDslContext()
|
||||||
|
.resultQuery(sql, raw(arrayToString(limitNames)))
|
||||||
|
.fetchInto(LimitValue.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<LimitValue> getCurrentLimitValue(List<String> limitNames, String operationId) {
|
||||||
|
String sql = """
|
||||||
|
with operation_timestamp as (
|
||||||
|
select created_at
|
||||||
|
from lim.operation
|
||||||
|
where operation_id = {0}
|
||||||
|
), hold_data as (
|
||||||
|
select ld.id, ld.name, coalesce(sum(ops.amount), 0) as hold_amount
|
||||||
|
from lim.limit_data as ld
|
||||||
|
left join lim.operation as ops
|
||||||
|
on ops.limit_id = ld.id
|
||||||
|
and ops.created_at <= (select created_at from operation_timestamp limit 1)
|
||||||
|
and ops.state = 'HOLD'
|
||||||
|
where ld.name in ({1})
|
||||||
|
group by ld.id, ld.name
|
||||||
|
), commit_data as (
|
||||||
|
select ld.id, ld.name, coalesce(sum(ops.amount), 0) as commit_amount
|
||||||
|
from lim.limit_data as ld
|
||||||
|
left join lim.operation as ops
|
||||||
|
on ops.limit_id = ld.id
|
||||||
|
and ops.created_at <= (select created_at from operation_timestamp limit 1)
|
||||||
|
and ops.state = 'COMMIT'
|
||||||
|
where ld.name in ({1})
|
||||||
|
group by ld.id, ld.name
|
||||||
|
)
|
||||||
|
|
||||||
|
select cd.name as limit_name, cd.commit_amount, hd.hold_amount
|
||||||
|
from commit_data as cd
|
||||||
|
join hold_data as hd on cd.id = hd.id;
|
||||||
|
""";
|
||||||
|
return getDslContext()
|
||||||
|
.resultQuery(sql, val(operationId), raw(arrayToString(limitNames)))
|
||||||
|
.fetchInto(LimitValue.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String arrayToString(List<String> strings) {
|
||||||
|
return strings.stream()
|
||||||
|
.map(limit -> "'%s'".formatted(limit))
|
||||||
|
.collect(Collectors.joining(DELIMITER));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.empayre.liminator.exception;
|
||||||
|
|
||||||
|
public class BusinessException extends RuntimeException {
|
||||||
|
public BusinessException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,23 @@
|
|||||||
|
package com.empayre.liminator.exception;
|
||||||
|
|
||||||
|
public class DaoException extends RuntimeException {
|
||||||
|
public DaoException() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
|
||||||
|
public DaoException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DaoException(String message, Throwable cause) {
|
||||||
|
super(message, cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public DaoException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected DaoException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||||
|
super(message, cause, enableSuppression, writableStackTrace);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.empayre.liminator.exception;
|
||||||
|
|
||||||
|
public class NotFoundException extends RuntimeException {
|
||||||
|
public NotFoundException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package com.empayre.liminator.exception;
|
||||||
|
|
||||||
|
public class SerializationException extends RuntimeException {
|
||||||
|
public SerializationException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
package com.empayre.liminator.handler;
|
||||||
|
|
||||||
|
import com.empayre.liminator.dao.OperationDao;
|
||||||
|
import com.empayre.liminator.service.LimitsGettingService;
|
||||||
|
import dev.vality.liminator.LimitRequest;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.thrift.TException;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CommitLimitAmountHandler implements Handler<List<LimitRequest>, Boolean> {
|
||||||
|
|
||||||
|
private final OperationDao operationDao;
|
||||||
|
private final LimitsGettingService limitsGettingService;
|
||||||
|
|
||||||
|
private static final String LOG_PREFIX = "COMMIT";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public Boolean handle(List<LimitRequest> requestList) throws TException {
|
||||||
|
if (CollectionUtils.isEmpty(requestList)) {
|
||||||
|
log.warn("[{}] Received LimitRequest list is empty", LOG_PREFIX);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
limitsGettingService.get(requestList, LOG_PREFIX);
|
||||||
|
|
||||||
|
List<String> operationsIds = requestList.stream()
|
||||||
|
.map(request -> request.getOperationId())
|
||||||
|
.toList();
|
||||||
|
int updatedRowsCount = operationDao.commit(operationsIds);
|
||||||
|
if (updatedRowsCount != operationsIds.size()) {
|
||||||
|
log.warn("[{}] Count of updated rows ({}) is not equal to the count of source commit operations ({})",
|
||||||
|
LOG_PREFIX, updatedRowsCount, operationsIds.size());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,68 @@
|
|||||||
|
package com.empayre.liminator.handler;
|
||||||
|
|
||||||
|
import com.empayre.liminator.dao.LimitContextDao;
|
||||||
|
import com.empayre.liminator.dao.LimitDataDao;
|
||||||
|
import com.empayre.liminator.domain.tables.pojos.LimitContext;
|
||||||
|
import com.empayre.liminator.domain.tables.pojos.LimitData;
|
||||||
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import dev.vality.liminator.CreateLimitRequest;
|
||||||
|
import dev.vality.liminator.LimitResponse;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.thrift.TException;
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CreateLimitHandler implements Handler<CreateLimitRequest, LimitResponse> {
|
||||||
|
|
||||||
|
private final ObjectMapper mapper;
|
||||||
|
private final Converter<CreateLimitRequest, LimitData> createLimitRequestToLimitDataConverter;
|
||||||
|
private final LimitDataDao limitDataDao;
|
||||||
|
private final LimitContextDao limitContextDao;
|
||||||
|
|
||||||
|
private static final String LOG_PREFIX = "CREATE";
|
||||||
|
private static final String EMPTY_JSON = "{}";
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public LimitResponse handle(CreateLimitRequest request) throws TException {
|
||||||
|
String limitName = request.getLimitName();
|
||||||
|
LimitData existedLimitData = limitDataDao.get(limitName);
|
||||||
|
if (existedLimitData != null) {
|
||||||
|
log.info("[{}] Limit {} already exists", LOG_PREFIX, limitName);
|
||||||
|
return new LimitResponse(limitName, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
LimitData limitData = createLimitRequestToLimitDataConverter.convert(request);
|
||||||
|
Long limitId = limitDataDao.save(limitData);
|
||||||
|
if (request.context != null) {
|
||||||
|
limitContextDao.save(convertToLimitContext(limitId, request.context));
|
||||||
|
}
|
||||||
|
return new LimitResponse(limitName, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LimitContext convertToLimitContext(Long limitId, Map<String, String> contextMap) {
|
||||||
|
LimitContext context = new LimitContext();
|
||||||
|
context.setLimitId(limitId);
|
||||||
|
context.setContext(getContextString(contextMap));
|
||||||
|
context.setWtime(LocalDateTime.now());
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getContextString(Map<String, String> contextMap) {
|
||||||
|
try {
|
||||||
|
return mapper.writeValueAsString(contextMap);
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
log.error("[{}] ContextJSON processing exception", LOG_PREFIX, e);
|
||||||
|
return EMPTY_JSON;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
package com.empayre.liminator.handler;
|
||||||
|
|
||||||
|
import com.empayre.liminator.dao.OperationDao;
|
||||||
|
import com.empayre.liminator.model.LimitValue;
|
||||||
|
import dev.vality.liminator.LimitResponse;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.thrift.TException;
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class GetLimitAmountHandler implements Handler<List<String>, List<LimitResponse>> {
|
||||||
|
|
||||||
|
private final OperationDao operationDao;
|
||||||
|
private final Converter<List<LimitValue>, List<LimitResponse>> currentLimitValuesToLimitResponseConverter;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public List<LimitResponse> handle(List<String> limitIdNames) throws TException {
|
||||||
|
return currentLimitValuesToLimitResponseConverter.convert(operationDao.getCurrentLimitValue(limitIdNames));
|
||||||
|
}
|
||||||
|
}
|
8
src/main/java/com/empayre/liminator/handler/Handler.java
Normal file
8
src/main/java/com/empayre/liminator/handler/Handler.java
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package com.empayre.liminator.handler;
|
||||||
|
|
||||||
|
import org.apache.thrift.TException;
|
||||||
|
|
||||||
|
public interface Handler<T, R> {
|
||||||
|
|
||||||
|
R handle(T source) throws TException;
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
package com.empayre.liminator.handler;
|
||||||
|
|
||||||
|
import com.empayre.liminator.converter.OperationConverter;
|
||||||
|
import com.empayre.liminator.dao.OperationDao;
|
||||||
|
import com.empayre.liminator.domain.tables.pojos.LimitData;
|
||||||
|
import com.empayre.liminator.domain.tables.pojos.Operation;
|
||||||
|
import com.empayre.liminator.model.LimitValue;
|
||||||
|
import com.empayre.liminator.service.LimitsGettingService;
|
||||||
|
import com.empayre.liminator.util.LimitDataUtils;
|
||||||
|
import dev.vality.liminator.LimitRequest;
|
||||||
|
import dev.vality.liminator.LimitResponse;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.thrift.TException;
|
||||||
|
import org.springframework.core.convert.converter.Converter;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class HoldLimitAmountHandler implements Handler<List<LimitRequest>, List<LimitResponse>> {
|
||||||
|
|
||||||
|
private final OperationDao operationDao;
|
||||||
|
private final LimitsGettingService limitsGettingService;
|
||||||
|
private final Converter<List<LimitValue>, List<LimitResponse>> currentLimitValuesToLimitResponseConverter;
|
||||||
|
private final OperationConverter operationConverter;
|
||||||
|
|
||||||
|
private static final String LOG_PREFIX = "HOLD";
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
@Override
|
||||||
|
public List<LimitResponse> handle(List<LimitRequest> requestList) throws TException {
|
||||||
|
if (CollectionUtils.isEmpty(requestList)) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
List<LimitData> limitData = limitsGettingService.get(requestList, LOG_PREFIX);
|
||||||
|
Map<String, Long> limitNamesMap = LimitDataUtils.createLimitNamesMap(limitData);
|
||||||
|
List<Operation> operations = convertToOperation(requestList, limitNamesMap);
|
||||||
|
operationDao.saveBatch(operations);
|
||||||
|
List<String> limitNames = LimitDataUtils.getLimitNames(requestList);
|
||||||
|
return currentLimitValuesToLimitResponseConverter.convert(operationDao.getCurrentLimitValue(limitNames));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Operation> convertToOperation(List<LimitRequest> requestList, Map<String, Long> limitNamesMap) {
|
||||||
|
return requestList.stream()
|
||||||
|
.map(request -> operationConverter.convert(request, limitNamesMap.get(request.getLimitName())))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,39 @@
|
|||||||
|
package com.empayre.liminator.handler;
|
||||||
|
|
||||||
|
import com.empayre.liminator.dao.OperationDao;
|
||||||
|
import com.empayre.liminator.service.LimitsGettingService;
|
||||||
|
import dev.vality.liminator.LimitRequest;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.thrift.TException;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class RollbackLimitAmountHandler implements Handler<List<LimitRequest>, Boolean> {
|
||||||
|
|
||||||
|
private final OperationDao operationDao;
|
||||||
|
private final LimitsGettingService limitsGettingService;
|
||||||
|
|
||||||
|
private static final String LOG_PREFIX = "ROLLBACK";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public Boolean handle(List<LimitRequest> requestList) throws TException {
|
||||||
|
limitsGettingService.get(requestList, LOG_PREFIX);
|
||||||
|
|
||||||
|
List<String> operationsIds = requestList.stream()
|
||||||
|
.map(request -> request.getOperationId())
|
||||||
|
.toList();
|
||||||
|
int updatedRowsCount = operationDao.rollback(operationsIds);
|
||||||
|
if (updatedRowsCount != operationsIds.size()) {
|
||||||
|
log.warn("[{}] Count of updated rows ({}) is not equal to the count of source rollback operations ({})",
|
||||||
|
LOG_PREFIX, updatedRowsCount, operationsIds.size());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
13
src/main/java/com/empayre/liminator/model/LimitValue.java
Normal file
13
src/main/java/com/empayre/liminator/model/LimitValue.java
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package com.empayre.liminator.model;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class LimitValue {
|
||||||
|
|
||||||
|
private String limitName;
|
||||||
|
private Long commitValue;
|
||||||
|
private Long holdValue;
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
package com.empayre.liminator.service;
|
||||||
|
|
||||||
|
import com.empayre.liminator.handler.Handler;
|
||||||
|
import dev.vality.liminator.*;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.thrift.TException;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class LiminatorService implements LiminatorServiceSrv.Iface {
|
||||||
|
|
||||||
|
private final Handler<CreateLimitRequest, LimitResponse> createLimitHandler;
|
||||||
|
private final Handler<List<LimitRequest>, List<LimitResponse>> holdLimitAmountHandler;
|
||||||
|
private final Handler<List<LimitRequest>, Boolean> commitLimitAmountHandler;
|
||||||
|
private final Handler<List<LimitRequest>, Boolean> rollbackLimitAmountHandler;
|
||||||
|
private final Handler<List<String>, List<LimitResponse>> getLimitAmountHandler;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public LimitResponse create(CreateLimitRequest createLimitRequest) throws DuplicateLimitName, TException {
|
||||||
|
return createLimitHandler.handle(createLimitRequest);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<LimitResponse> hold(List<LimitRequest> list) throws LimitNotFound, TException {
|
||||||
|
return holdLimitAmountHandler.handle(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean commit(List<LimitRequest> list) throws LimitNotFound, TException {
|
||||||
|
try {
|
||||||
|
commitLimitAmountHandler.handle(list);
|
||||||
|
return true;
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.error("Commit execution exception. Request list: {}", list, ex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean rollback(List<LimitRequest> list) throws LimitNotFound, TException {
|
||||||
|
try {
|
||||||
|
rollbackLimitAmountHandler.handle(list);
|
||||||
|
return true;
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.error("Commit execution exception. Request list: {}", list, ex);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<LimitResponse> get(List<String> limitNames) throws LimitNotFound, TException {
|
||||||
|
return getLimitAmountHandler.handle(limitNames);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
package com.empayre.liminator.service;
|
||||||
|
|
||||||
|
import com.empayre.liminator.dao.LimitDataDao;
|
||||||
|
import com.empayre.liminator.domain.tables.pojos.LimitData;
|
||||||
|
import com.empayre.liminator.util.LimitDataUtils;
|
||||||
|
import dev.vality.liminator.LimitNotFound;
|
||||||
|
import dev.vality.liminator.LimitRequest;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.thrift.TException;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.util.CollectionUtils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class LimitsGettingService {
|
||||||
|
|
||||||
|
private final LimitDataDao limitDataDao;
|
||||||
|
|
||||||
|
public List<LimitData> get(List<LimitRequest> requestList, String source) throws TException {
|
||||||
|
List<String> limitNames = LimitDataUtils.getLimitNames(requestList);
|
||||||
|
List<LimitData> limitData = limitDataDao.get(limitNames);
|
||||||
|
if (CollectionUtils.isEmpty(limitData)) {
|
||||||
|
log.error("[{}] Limits not found: {}", source, limitNames);
|
||||||
|
throw new LimitNotFound();
|
||||||
|
}
|
||||||
|
if (limitData.size() != limitNames.size()) {
|
||||||
|
log.error("[{}] Received limit ({}) size is not equal to expected ({}). " +
|
||||||
|
"Probably one of limits doesn't exist", source, limitData.size(), limitNames.size());
|
||||||
|
throw new LimitNotFound();
|
||||||
|
}
|
||||||
|
return limitData;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package com.empayre.liminator.servlet;
|
||||||
|
|
||||||
|
import dev.vality.liminator.LiminatorServiceSrv;
|
||||||
|
import dev.vality.woody.thrift.impl.http.THServiceBuilder;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import jakarta.servlet.*;
|
||||||
|
import jakarta.servlet.annotation.WebServlet;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
@WebServlet("/liminator/v1")
|
||||||
|
public class LiminatorServiceServlet extends GenericServlet {
|
||||||
|
|
||||||
|
private Servlet thriftServlet;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private LiminatorServiceSrv.Iface requestHandler;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void init(ServletConfig config) throws ServletException {
|
||||||
|
super.init(config);
|
||||||
|
thriftServlet = new THServiceBuilder()
|
||||||
|
.build(LiminatorServiceSrv.Iface.class, requestHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
|
||||||
|
thriftServlet.service(req, res);
|
||||||
|
}
|
||||||
|
}
|
29
src/main/java/com/empayre/liminator/util/LimitDataUtils.java
Normal file
29
src/main/java/com/empayre/liminator/util/LimitDataUtils.java
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package com.empayre.liminator.util;
|
||||||
|
|
||||||
|
import com.empayre.liminator.domain.tables.pojos.LimitData;
|
||||||
|
import dev.vality.liminator.LimitRequest;
|
||||||
|
import lombok.AccessLevel;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@NoArgsConstructor(access = AccessLevel.PRIVATE)
|
||||||
|
public class LimitDataUtils {
|
||||||
|
|
||||||
|
public static List<String> getLimitNames(List<LimitRequest> requestList) {
|
||||||
|
return requestList.stream()
|
||||||
|
.map(LimitRequest::getLimitName)
|
||||||
|
.distinct()
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, Long> createLimitNamesMap(List<LimitData> limitData) {
|
||||||
|
Map<String, Long> map = new HashMap<>();
|
||||||
|
for (LimitData data : limitData) {
|
||||||
|
map.put(data.getName(), data.getId());
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
}
|
97
src/main/resources/application.yml
Normal file
97
src/main/resources/application.yml
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
server:
|
||||||
|
port: "${server.port}"
|
||||||
|
|
||||||
|
spring:
|
||||||
|
application:
|
||||||
|
name: "${project.name}"
|
||||||
|
datasource:
|
||||||
|
type: com.zaxxer.hikari.HikariDataSource
|
||||||
|
driver-class-name: org.postgresql.Driver
|
||||||
|
url: "${db.url}"
|
||||||
|
username: '${db.user}'
|
||||||
|
password: "${db.password}"
|
||||||
|
hikari:
|
||||||
|
idle-timeout: 30000
|
||||||
|
maximum-pool-size: 10
|
||||||
|
data-source-properties:
|
||||||
|
reWriteBatchedInserts: true
|
||||||
|
flyway:
|
||||||
|
schemas: lim
|
||||||
|
jooq:
|
||||||
|
sql-dialect: Postgres
|
||||||
|
kafka:
|
||||||
|
bootstrap-servers: localhost:9092
|
||||||
|
client-id: dominant_warehouse
|
||||||
|
consumer:
|
||||||
|
enable-auto-commit: false
|
||||||
|
auto-offset-reset: earliest
|
||||||
|
max-poll-records: 20
|
||||||
|
properties:
|
||||||
|
max.poll.interval.ms: 30000
|
||||||
|
session.timeout.ms: 30000
|
||||||
|
|
||||||
|
service:
|
||||||
|
default:
|
||||||
|
limit: 20
|
||||||
|
|
||||||
|
management:
|
||||||
|
server:
|
||||||
|
port: "${management.port}"
|
||||||
|
metrics:
|
||||||
|
export:
|
||||||
|
prometheus:
|
||||||
|
enabled: false
|
||||||
|
tags:
|
||||||
|
application: "${project.name}"
|
||||||
|
endpoint:
|
||||||
|
health:
|
||||||
|
probes:
|
||||||
|
enabled: true
|
||||||
|
show-details: always
|
||||||
|
metrics:
|
||||||
|
enabled: true
|
||||||
|
prometheus:
|
||||||
|
enabled: true
|
||||||
|
endpoints:
|
||||||
|
web:
|
||||||
|
exposure:
|
||||||
|
include: health,info,prometheus
|
||||||
|
|
||||||
|
kafka:
|
||||||
|
consumer:
|
||||||
|
group-id: "Liminator-Listener"
|
||||||
|
party-management-concurrency: 7
|
||||||
|
wallet-concurrency: 7
|
||||||
|
identity-concurrency: 7
|
||||||
|
topics:
|
||||||
|
party-management:
|
||||||
|
id: mg-events-party
|
||||||
|
enabled: false
|
||||||
|
consumer.group-id: "LiminatorListenerPartyManagement"
|
||||||
|
identity:
|
||||||
|
id: mg-events-ff-identity
|
||||||
|
enabled: false
|
||||||
|
wallet:
|
||||||
|
id: mg-events-ff-wallet
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
dmt:
|
||||||
|
url: http://dominant:8022/v1/domain/repository
|
||||||
|
networkTimeout: 5000
|
||||||
|
polling:
|
||||||
|
delay: 100
|
||||||
|
maxQuerySize: 10
|
||||||
|
enabled: false
|
||||||
|
|
||||||
|
cache:
|
||||||
|
party-shop:
|
||||||
|
size: 10000
|
||||||
|
expire:
|
||||||
|
after:
|
||||||
|
sec: 600
|
||||||
|
|
||||||
|
testcontainers:
|
||||||
|
postgresql:
|
||||||
|
tag: '11.4'
|
||||||
|
kafka:
|
||||||
|
tag: '6.2.0'
|
12
src/main/resources/checkstyle/checkstyle-suppressions.xml
Normal file
12
src/main/resources/checkstyle/checkstyle-suppressions.xml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0"?>
|
||||||
|
|
||||||
|
<!DOCTYPE suppressions PUBLIC
|
||||||
|
"-//Checkstyle//DTD SuppressionFilter Configuration 1.0//EN"
|
||||||
|
"https://checkstyle.org/dtds/suppressions_1_0.dtd">
|
||||||
|
|
||||||
|
<suppressions>
|
||||||
|
<suppress checks="[a-zA-Z0-9]*"
|
||||||
|
files="com[\\/]empayre[\\/]dominant[\\/]warehouse[\\/].*.java"/>
|
||||||
|
<suppress checks="LineLength" files=".*ContractUtil.java"/>
|
||||||
|
<suppress checks="VariableDeclarationUsageDistance" files=".*DaoTests.java"/>
|
||||||
|
</suppressions>
|
44
src/main/resources/db/migration/V1_1__add_base_tables.sql
Normal file
44
src/main/resources/db/migration/V1_1__add_base_tables.sql
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
CREATE SCHEMA IF NOT EXISTS lim;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE lim.limit_data
|
||||||
|
(
|
||||||
|
id bigserial NOT NULL,
|
||||||
|
name character varying NOT NULL,
|
||||||
|
created_at date NOT NULL,
|
||||||
|
wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL,
|
||||||
|
CONSTRAINT limit_data_pkey PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX limit_data_unq_idx ON lim.limit_data USING btree (name);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE lim.limit_context
|
||||||
|
(
|
||||||
|
id bigserial NOT NULL,
|
||||||
|
limit_id bigint NOT NULL,
|
||||||
|
context character varying,
|
||||||
|
wtime timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL,
|
||||||
|
CONSTRAINT limit_context_pkey PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX limit_context_unq_idx ON lim.limit_context USING btree (limit_id);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TYPE lim.operation_state AS ENUM ('HOLD', 'COMMIT', 'ROLLBACK');
|
||||||
|
|
||||||
|
CREATE TABLE lim.operation
|
||||||
|
(
|
||||||
|
id bigserial NOT NULL,
|
||||||
|
limit_id bigint NOT NULL,
|
||||||
|
operation_id character varying NOT NULL,
|
||||||
|
state lim.operation_state NOT NULL,
|
||||||
|
amount bigint NOT NULL,
|
||||||
|
created_at timestamp without time zone DEFAULT timezone('utc'::text, now()) NOT NULL,
|
||||||
|
CONSTRAINT operation_pkey PRIMARY KEY (id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE UNIQUE INDEX operation_unq_idx ON lim.operation USING btree (limit_id, operation_id);
|
||||||
|
CREATE INDEX operation_idx ON lim.operation USING btree (limit_id, state, created_at, operation_id);
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.empayre.liminator.config;
|
||||||
|
|
||||||
|
import dev.vality.testcontainers.annotations.postgresql.PostgresqlTestcontainerSingleton;
|
||||||
|
import org.springframework.boot.test.autoconfigure.jooq.JooqTest;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target({ElementType.TYPE})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@PostgresqlTestcontainerSingleton
|
||||||
|
@JooqTest
|
||||||
|
public @interface PostgresqlJooqSpringBootITest {
|
||||||
|
}
|
@ -0,0 +1,16 @@
|
|||||||
|
package com.empayre.liminator.config;
|
||||||
|
|
||||||
|
import dev.vality.testcontainers.annotations.DefaultSpringBootTest;
|
||||||
|
import dev.vality.testcontainers.annotations.postgresql.PostgresqlTestcontainerSingleton;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Target({ElementType.TYPE})
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@PostgresqlTestcontainerSingleton
|
||||||
|
@DefaultSpringBootTest
|
||||||
|
public @interface PostgresqlSpringBootITest {
|
||||||
|
}
|
143
src/test/java/com/empayre/liminator/dao/DaoTests.java
Normal file
143
src/test/java/com/empayre/liminator/dao/DaoTests.java
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
package com.empayre.liminator.dao;
|
||||||
|
|
||||||
|
import com.empayre.liminator.config.PostgresqlSpringBootITest;
|
||||||
|
import com.empayre.liminator.domain.enums.OperationState;
|
||||||
|
import com.empayre.liminator.domain.tables.pojos.LimitContext;
|
||||||
|
import com.empayre.liminator.domain.tables.pojos.LimitData;
|
||||||
|
import com.empayre.liminator.domain.tables.pojos.Operation;
|
||||||
|
import com.empayre.liminator.model.LimitValue;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@PostgresqlSpringBootITest
|
||||||
|
public class DaoTests {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private LimitDataDao limitDataDao;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private LimitContextDao limitContextDao;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private OperationDao operationDao;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void limitDataDaoTest() {
|
||||||
|
List<String> limitNames = new ArrayList<>();
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
String limitName = "Limit-" + i;
|
||||||
|
limitDataDao.save(new LimitData(null, limitName, LocalDate.now(), LocalDateTime.now()));
|
||||||
|
limitNames.add(limitName);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<LimitData> limitDataList = limitDataDao.get(limitNames);
|
||||||
|
assertEquals(limitNames.size(), limitDataList.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void limitContextDaoTest() {
|
||||||
|
LimitContext limitContext = new LimitContext();
|
||||||
|
long limitId = 123L;
|
||||||
|
limitContext.setLimitId(limitId);
|
||||||
|
limitContext.setContext("{\"provider\":\"test\"}");
|
||||||
|
limitContextDao.save(limitContext);
|
||||||
|
LimitContext result = limitContextDao.getLimitContext(limitId);
|
||||||
|
assertNotNull(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void operationDaoTest() {
|
||||||
|
List<Long> limitIdsList = new ArrayList<>();
|
||||||
|
List<String> limitNamesList = new ArrayList<>();
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
String limitName = "Limit-odc-1-" + i;
|
||||||
|
Long limitId = limitDataDao.save(new LimitData(null, limitName, LocalDate.now(), LocalDateTime.now()));
|
||||||
|
limitIdsList.add(limitId);
|
||||||
|
limitNamesList.add(limitName);
|
||||||
|
}
|
||||||
|
List<Operation> operations = new ArrayList<>();
|
||||||
|
for (Long limitId : limitIdsList) {
|
||||||
|
for (int i = 0; i < 5; i++) {
|
||||||
|
operations.add(createOperation(limitId, "Operation-odc-1-%s-%s".formatted(limitId, i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
operationDao.saveBatch(operations);
|
||||||
|
|
||||||
|
List<LimitValue> currentLimitValue = operationDao.getCurrentLimitValue(limitNamesList);
|
||||||
|
assertEquals(10, currentLimitValue.size());
|
||||||
|
currentLimitValue.forEach(value -> assertEquals(0, value.getCommitValue()));
|
||||||
|
currentLimitValue.forEach(value -> assertNotEquals(0, value.getHoldValue()));
|
||||||
|
|
||||||
|
List<String> commitOperations = operations.subList(0, 20).stream()
|
||||||
|
.map(Operation::getOperationId)
|
||||||
|
.toList();
|
||||||
|
operationDao.commit(commitOperations);
|
||||||
|
List<String> rollbackOperations = operations.subList(20, 35).stream()
|
||||||
|
.map(Operation::getOperationId)
|
||||||
|
.toList();
|
||||||
|
operationDao.rollback(rollbackOperations);
|
||||||
|
|
||||||
|
List<LimitValue> limitValuesAfterChanges = operationDao.getCurrentLimitValue(limitNamesList);
|
||||||
|
List<LimitValue> limitValuesWithCommitData = limitValuesAfterChanges.stream()
|
||||||
|
.filter(value -> value.getCommitValue() > 0 && value.getHoldValue() == 0)
|
||||||
|
.toList();
|
||||||
|
assertEquals(4, limitValuesWithCommitData.size());
|
||||||
|
|
||||||
|
List<LimitValue> limitValuesWithHoldData = limitValuesAfterChanges.stream()
|
||||||
|
.filter(value -> value.getCommitValue() == 0 && value.getHoldValue() > 0)
|
||||||
|
.toList();
|
||||||
|
assertEquals(3, limitValuesWithHoldData.size());
|
||||||
|
|
||||||
|
List<LimitValue> limitValuesWithoutData = limitValuesAfterChanges.stream()
|
||||||
|
.filter(value -> value.getCommitValue() == 0 && value.getHoldValue() == 0)
|
||||||
|
.toList();
|
||||||
|
assertEquals(3, limitValuesWithoutData.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void operationDaoCurrentLimitWithOperationIdTest() {
|
||||||
|
String limitName = "Limit-odc-2";
|
||||||
|
Long limitId = limitDataDao.save(new LimitData(null, limitName, LocalDate.now(), LocalDateTime.now()));
|
||||||
|
List<Operation> operations = new ArrayList<>();
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
Operation operation = createOperation(
|
||||||
|
limitId,
|
||||||
|
"Operation-odc-2-%s-%s".formatted(limitId, i),
|
||||||
|
LocalDateTime.now().minusMinutes(11L - i));
|
||||||
|
operationDao.save(operation);
|
||||||
|
operations.add(operation);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<LimitValue> valuesForFifthOperation =
|
||||||
|
operationDao.getCurrentLimitValue(List.of(limitName), operations.get(2).getOperationId());
|
||||||
|
LimitValue limitValue = valuesForFifthOperation.get(0);
|
||||||
|
assertEquals(300, limitValue.getHoldValue());
|
||||||
|
|
||||||
|
valuesForFifthOperation =
|
||||||
|
operationDao.getCurrentLimitValue(List.of(limitName), operations.get(5).getOperationId());
|
||||||
|
assertEquals(600, valuesForFifthOperation.get(0).getHoldValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Operation createOperation(Long limitId, String operationId) {
|
||||||
|
return createOperation(limitId, operationId, LocalDateTime.now());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Operation createOperation(Long limitId, String operationId, LocalDateTime createdAt) {
|
||||||
|
Operation operation = new Operation();
|
||||||
|
operation.setLimitId(limitId);
|
||||||
|
operation.setOperationId(operationId);
|
||||||
|
operation.setState(OperationState.HOLD);
|
||||||
|
operation.setAmount(100L);
|
||||||
|
operation.setCreatedAt(createdAt);
|
||||||
|
return operation;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,97 @@
|
|||||||
|
package com.empayre.liminator.service;
|
||||||
|
|
||||||
|
import com.empayre.liminator.config.PostgresqlSpringBootITest;
|
||||||
|
import dev.vality.liminator.CreateLimitRequest;
|
||||||
|
import dev.vality.liminator.LimitRequest;
|
||||||
|
import dev.vality.liminator.LimitResponse;
|
||||||
|
import org.apache.thrift.TException;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
@PostgresqlSpringBootITest
|
||||||
|
public class LiminatorServiceTest {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private LiminatorService liminatorService;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void createLimitTest() throws TException {
|
||||||
|
String limitName = "TestLimitCreate";
|
||||||
|
CreateLimitRequest request = new CreateLimitRequest()
|
||||||
|
.setLimitName(limitName);
|
||||||
|
|
||||||
|
LimitResponse response = liminatorService.create(request);
|
||||||
|
assertEquals(limitName, response.getLimitName());
|
||||||
|
assertEquals(0, response.getHoldValue());
|
||||||
|
assertEquals(0, response.getCommitValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void holdValueTest() throws TException {
|
||||||
|
String limitName = "TestLimitHold";
|
||||||
|
CreateLimitRequest createRequest = new CreateLimitRequest()
|
||||||
|
.setLimitName(limitName);
|
||||||
|
liminatorService.create(createRequest);
|
||||||
|
|
||||||
|
String operationId = "OpHold";
|
||||||
|
LimitRequest holdRequest = new LimitRequest()
|
||||||
|
.setLimitName(limitName)
|
||||||
|
.setOperationId(operationId)
|
||||||
|
.setValue(500L);
|
||||||
|
List<LimitResponse> holdResponse = liminatorService.hold(List.of(holdRequest));
|
||||||
|
assertEquals(1, holdResponse.size());
|
||||||
|
LimitResponse response = holdResponse.get(0);
|
||||||
|
assertEquals(500, response.getHoldValue());
|
||||||
|
assertEquals(0, response.getCommitValue());
|
||||||
|
assertEquals(limitName, response.getLimitName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void commitValueTest() throws TException {
|
||||||
|
String limitName = "TestLimitCommit";
|
||||||
|
CreateLimitRequest createRequest = new CreateLimitRequest()
|
||||||
|
.setLimitName(limitName);
|
||||||
|
liminatorService.create(createRequest);
|
||||||
|
|
||||||
|
String operationId = "OpComit";
|
||||||
|
LimitRequest holdRequest = new LimitRequest()
|
||||||
|
.setLimitName(limitName)
|
||||||
|
.setOperationId(operationId)
|
||||||
|
.setValue(500L);
|
||||||
|
liminatorService.hold(List.of(holdRequest));
|
||||||
|
assertTrue(liminatorService.commit(List.of(holdRequest)));
|
||||||
|
|
||||||
|
List<LimitResponse> limitResponses = liminatorService.get(List.of(limitName));
|
||||||
|
assertEquals(1, limitResponses.size());
|
||||||
|
assertEquals(0, limitResponses.get(0).getHoldValue());
|
||||||
|
assertEquals(500, limitResponses.get(0).getCommitValue());
|
||||||
|
assertEquals(limitName, limitResponses.get(0).getLimitName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rollbackValueTest() throws TException {
|
||||||
|
String limitName = "TestLimitRollback";
|
||||||
|
CreateLimitRequest createRequest = new CreateLimitRequest()
|
||||||
|
.setLimitName(limitName);
|
||||||
|
liminatorService.create(createRequest);
|
||||||
|
|
||||||
|
String operationId = "Op-112";
|
||||||
|
LimitRequest holdRequest = new LimitRequest()
|
||||||
|
.setLimitName(limitName)
|
||||||
|
.setOperationId(operationId)
|
||||||
|
.setValue(500L);
|
||||||
|
liminatorService.hold(List.of(holdRequest));
|
||||||
|
assertTrue(liminatorService.rollback(List.of(holdRequest)));
|
||||||
|
|
||||||
|
List<LimitResponse> limitResponses = liminatorService.get(List.of(limitName));
|
||||||
|
assertEquals(1, limitResponses.size());
|
||||||
|
assertEquals(0, limitResponses.get(0).getHoldValue());
|
||||||
|
assertEquals(0, limitResponses.get(0).getCommitValue());
|
||||||
|
assertEquals(limitName, limitResponses.get(0).getLimitName());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user