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