mirror of
https://github.com/valitydev/fraudo.git
synced 2024-11-06 09:55:16 +00:00
Merge pull request #1 from rbkmoney/ft/create-fraudo-lang
Create lang fraudo BJ-332
This commit is contained in:
commit
67e63848af
73
.gitignore
vendored
Normal file
73
.gitignore
vendored
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
# 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
|
4
.gitmodules
vendored
Normal file
4
.gitmodules
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[submodule "build_utils"]
|
||||||
|
path = build_utils
|
||||||
|
url = git@github.com:rbkmoney/build_utils.git
|
||||||
|
branch = master
|
13
Jenkinsfile
vendored
Normal file
13
Jenkinsfile
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#!groovy
|
||||||
|
build('fraudo', 'docker-host') {
|
||||||
|
checkoutRepo()
|
||||||
|
loadBuildUtils()
|
||||||
|
|
||||||
|
def javaLibPipeline
|
||||||
|
runStage('load JavaLib pipeline') {
|
||||||
|
javaLibPipeline = load("build_utils/jenkins_lib/pipeJavaLib.groovy")
|
||||||
|
}
|
||||||
|
|
||||||
|
def buildImageTag = "4799280a02cb73761a3ba3641285aac8ec4ec482"
|
||||||
|
javaLibPipeline(buildImageTag)
|
||||||
|
}
|
67
README.md
67
README.md
@ -1 +1,66 @@
|
|||||||
# fraudo
|
# fraudo
|
||||||
|
|
||||||
|
![alt text](logo.jpg)
|
||||||
|
|
||||||
|
#### Syntax
|
||||||
|
|
||||||
|
![alt text](syntax.png)
|
||||||
|
|
||||||
|
##### OPERATIONS:
|
||||||
|
~~~~
|
||||||
|
* count("group_field", time_in_minutes)
|
||||||
|
* countSuccess("group_field", time_in_minutes)
|
||||||
|
* countError("group_field", time_in_minutes, "error_code")
|
||||||
|
* sum("group_field", time_in_minutes)
|
||||||
|
* sumSuccess("group_field", time_in_minutes)
|
||||||
|
* sumError(("group_field", time_in_minutes, "error_code")
|
||||||
|
* unique(("group_field", "by_field")
|
||||||
|
* in(("field", "first", "second", ...)
|
||||||
|
* inWhiteList("field")
|
||||||
|
* inBlackList("field")
|
||||||
|
* like("field", "regexp_in_java_style"[1])
|
||||||
|
* countryBy("field")
|
||||||
|
~~~~
|
||||||
|
1. [regexp_in_java_style](https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html)
|
||||||
|
##### RESULTS:
|
||||||
|
~~~~
|
||||||
|
* accept
|
||||||
|
* 3ds
|
||||||
|
* decline
|
||||||
|
* notify
|
||||||
|
~~~~
|
||||||
|
##### EXAMPLES:
|
||||||
|
###### Simple:
|
||||||
|
~~~~
|
||||||
|
rule: 3 > 2 AND 1 = 1
|
||||||
|
-> accept;
|
||||||
|
~~~~
|
||||||
|
###### Black list check:
|
||||||
|
~~~~
|
||||||
|
rule: inBlackList("email")
|
||||||
|
-> notify;
|
||||||
|
~~~~
|
||||||
|
###### Counts check:
|
||||||
|
~~~~
|
||||||
|
rule: (count("ip", 1444) >= 10 OR countSuccess("email", 1444) > 5)
|
||||||
|
AND countError("fingerprint", 1444, "error_code") > 5
|
||||||
|
-> notify;
|
||||||
|
~~~~
|
||||||
|
###### Unique count emails for ip:
|
||||||
|
~~~~
|
||||||
|
rule: unique("email", "ip") < 4
|
||||||
|
-> decline;
|
||||||
|
~~~~
|
||||||
|
###### Check country by ip:
|
||||||
|
~~~~
|
||||||
|
rule: countryBy("ip") = "RU"
|
||||||
|
-> notify;
|
||||||
|
~~~~
|
||||||
|
###### Combined check:
|
||||||
|
~~~~
|
||||||
|
rule: 3 > 2 AND 1 > 1
|
||||||
|
-> decline;
|
||||||
|
|
||||||
|
rule: count("email", 10) <= 10 AND count("ip", 1444) = 10
|
||||||
|
-> 3ds;
|
||||||
|
~~~~
|
1
build_utils
Submodule
1
build_utils
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 9b664082ddc8ec8cdfbe7513d54e433d24198cc2
|
79
pom.xml
Normal file
79
pom.xml
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
<?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>com.rbkmoney</groupId>
|
||||||
|
<artifactId>parent</artifactId>
|
||||||
|
<version>1.0.2</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>fraudo</artifactId>
|
||||||
|
<version>0.0.1-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<hamcrest.junit.version>2.0.0.0</hamcrest.junit.version>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.antlr</groupId>
|
||||||
|
<artifactId>antlr4-runtime</artifactId>
|
||||||
|
<version>4.7.1</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>4.12</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.projectlombok</groupId>
|
||||||
|
<artifactId>lombok</artifactId>
|
||||||
|
<version>1.18.4</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- tests -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.hamcrest</groupId>
|
||||||
|
<artifactId>hamcrest-junit</artifactId>
|
||||||
|
<version>${hamcrest.junit.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-all</artifactId>
|
||||||
|
<version>1.9.5</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.antlr</groupId>
|
||||||
|
<artifactId>antlr4-maven-plugin</artifactId>
|
||||||
|
<version>4.7.1</version>
|
||||||
|
<configuration>
|
||||||
|
<arguments>
|
||||||
|
<argument>-package</argument>
|
||||||
|
<argument>com.rbkmoney.fraudo</argument>
|
||||||
|
</arguments>
|
||||||
|
<listener>false</listener>
|
||||||
|
<visitor>true</visitor>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>antlr</id>
|
||||||
|
<goals>
|
||||||
|
<goal>antlr4</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
129
src/main/antlr4/com.rbkmoney.fraudo/Fraudo.g4
Normal file
129
src/main/antlr4/com.rbkmoney.fraudo/Fraudo.g4
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
grammar Fraudo;
|
||||||
|
|
||||||
|
parse
|
||||||
|
: fraud_rule* EOF
|
||||||
|
;
|
||||||
|
|
||||||
|
fraud_rule
|
||||||
|
: RULE_BLOCK expression RETURN result SCOL
|
||||||
|
;
|
||||||
|
|
||||||
|
expression
|
||||||
|
: LPAREN expression RPAREN #parenExpression
|
||||||
|
| NOT expression #notExpression
|
||||||
|
| left=expression op=comparator right=expression #comparatorExpression
|
||||||
|
| left=expression op=binary right=expression #binaryExpression
|
||||||
|
| bool #boolExpression
|
||||||
|
| count #countExpression
|
||||||
|
| count_success #countSuccessExpression
|
||||||
|
| count_error #countErrorExpression
|
||||||
|
| sum #sumExpression
|
||||||
|
| sum_success #sumSuccessExpression
|
||||||
|
| sum_error #sumErrorExpression
|
||||||
|
| unique #uniqueExpression
|
||||||
|
| in #inFunctionExpression
|
||||||
|
| in_white_list #inWhiteListExpression
|
||||||
|
| in_black_list #inBlackListExpression
|
||||||
|
| like #likeFunctionExpression
|
||||||
|
| country_by #countryByFunctionExpression
|
||||||
|
| IDENTIFIER #identifierExpression
|
||||||
|
| DECIMAL #decimalExpression
|
||||||
|
| STRING #stringExpression
|
||||||
|
;
|
||||||
|
|
||||||
|
comparator
|
||||||
|
: GT | GE | LT | LE | EQ
|
||||||
|
;
|
||||||
|
|
||||||
|
binary
|
||||||
|
: AND | OR
|
||||||
|
;
|
||||||
|
|
||||||
|
bool
|
||||||
|
: TRUE | FALSE
|
||||||
|
;
|
||||||
|
|
||||||
|
count
|
||||||
|
: 'count' LPAREN STRING DELIMETER DECIMAL RPAREN
|
||||||
|
;
|
||||||
|
|
||||||
|
count_success
|
||||||
|
: 'countSuccess' LPAREN STRING DELIMETER DECIMAL RPAREN
|
||||||
|
;
|
||||||
|
|
||||||
|
count_error
|
||||||
|
: 'countError' LPAREN STRING DELIMETER DECIMAL DELIMETER STRING RPAREN
|
||||||
|
;
|
||||||
|
|
||||||
|
sum
|
||||||
|
: 'sum' LPAREN STRING DELIMETER DECIMAL RPAREN
|
||||||
|
;
|
||||||
|
|
||||||
|
sum_success
|
||||||
|
: 'sumSuccess' LPAREN STRING DELIMETER DECIMAL RPAREN
|
||||||
|
;
|
||||||
|
|
||||||
|
sum_error
|
||||||
|
: 'sumError' LPAREN STRING DELIMETER DECIMAL DELIMETER STRING RPAREN
|
||||||
|
;
|
||||||
|
|
||||||
|
unique
|
||||||
|
: 'unique' LPAREN STRING DELIMETER STRING RPAREN
|
||||||
|
;
|
||||||
|
|
||||||
|
in
|
||||||
|
: 'in' LPAREN STRING DELIMETER string_list RPAREN
|
||||||
|
;
|
||||||
|
|
||||||
|
in_white_list
|
||||||
|
: 'inWhiteList' LPAREN STRING RPAREN
|
||||||
|
;
|
||||||
|
|
||||||
|
in_black_list
|
||||||
|
: 'inBlackList' LPAREN STRING RPAREN
|
||||||
|
;
|
||||||
|
|
||||||
|
like
|
||||||
|
: 'like' LPAREN STRING DELIMETER STRING RPAREN
|
||||||
|
;
|
||||||
|
|
||||||
|
country_by
|
||||||
|
: 'countryBy' LPAREN STRING RPAREN
|
||||||
|
;
|
||||||
|
|
||||||
|
result
|
||||||
|
: 'accept' | '3ds' | 'decline' | 'notify'
|
||||||
|
;
|
||||||
|
|
||||||
|
string_list
|
||||||
|
: STRING (',' STRING | WS)+
|
||||||
|
;
|
||||||
|
|
||||||
|
STRING
|
||||||
|
: '"' (~["\r\n] | '""')* '"'
|
||||||
|
;
|
||||||
|
|
||||||
|
DELIMETER : ',' ;
|
||||||
|
|
||||||
|
COMMENT
|
||||||
|
: '#' ~[\r\n]* -> skip
|
||||||
|
;
|
||||||
|
|
||||||
|
RETURN : '->' ;
|
||||||
|
RULE_BLOCK : 'rule:' ;
|
||||||
|
AND : 'AND' ;
|
||||||
|
OR : 'OR' ;
|
||||||
|
NOT : 'NOT';
|
||||||
|
TRUE : 'TRUE' ;
|
||||||
|
FALSE : 'FALSE' ;
|
||||||
|
GT : '>' ;
|
||||||
|
GE : '>=' ;
|
||||||
|
LT : '<' ;
|
||||||
|
LE : '<=' ;
|
||||||
|
EQ : '=' ;
|
||||||
|
LPAREN : '(' ;
|
||||||
|
RPAREN : ')' ;
|
||||||
|
DECIMAL : '-'? [0-9]+ ( '.' [0-9]+ )? ;
|
||||||
|
IDENTIFIER : [a-zA-Z_] [a-zA-Z_0-9]* ;
|
||||||
|
WS : [ \u000C\n]+ -> skip;
|
||||||
|
SCOL : ';';
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.rbkmoney.fraudo.aggregator;
|
||||||
|
|
||||||
|
import com.rbkmoney.fraudo.constant.CheckedField;
|
||||||
|
|
||||||
|
public interface CountAggregator {
|
||||||
|
|
||||||
|
Integer count(CheckedField checkedField, String valueField, Long timeInMinutes);
|
||||||
|
|
||||||
|
Integer countSuccess(CheckedField checkedField, String valueField, Long timeInMinutes);
|
||||||
|
|
||||||
|
Integer countError(CheckedField checkedField, String valueField, Long timeInMinutes, String errorCode);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
package com.rbkmoney.fraudo.aggregator;
|
||||||
|
|
||||||
|
import com.rbkmoney.fraudo.constant.CheckedField;
|
||||||
|
|
||||||
|
public interface SumAggregator {
|
||||||
|
|
||||||
|
Double sum(CheckedField checkedField, String email, Long timeInMinutes);
|
||||||
|
|
||||||
|
Double sumSuccess(CheckedField checkedField, String valueField, Long timeInMinutes);
|
||||||
|
|
||||||
|
Double sumError(CheckedField checkedField, String valueField, Long timeInMinutes, String errorCode);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package com.rbkmoney.fraudo.aggregator;
|
||||||
|
|
||||||
|
import com.rbkmoney.fraudo.constant.CheckedField;
|
||||||
|
|
||||||
|
public interface UniqueValueAggregator {
|
||||||
|
|
||||||
|
Integer countUniqueValue(CheckedField countField, CheckedField onField);
|
||||||
|
|
||||||
|
}
|
35
src/main/java/com/rbkmoney/fraudo/constant/CheckedField.java
Normal file
35
src/main/java/com/rbkmoney/fraudo/constant/CheckedField.java
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package com.rbkmoney.fraudo.constant;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public enum CheckedField {
|
||||||
|
|
||||||
|
EMAIL("email"),
|
||||||
|
IP("ip"),
|
||||||
|
FINGERPRINT("fingerprint"),
|
||||||
|
COUNTRY_BANK("country_bank"),
|
||||||
|
COUNTRY_IP("country_ip"),
|
||||||
|
BIN("bin"),
|
||||||
|
SHOP_ID("shop_ip"),
|
||||||
|
PARTY_ID("party_id"),
|
||||||
|
CARD_TOKEN("card_token");
|
||||||
|
|
||||||
|
private String value;
|
||||||
|
private static Map<String, CheckedField> valueMap = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (CheckedField value : CheckedField.values()) {
|
||||||
|
valueMap.put(value.value, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CheckedField(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CheckedField getByValue(String value) {
|
||||||
|
return valueMap.get(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
30
src/main/java/com/rbkmoney/fraudo/constant/ResultStatus.java
Normal file
30
src/main/java/com/rbkmoney/fraudo/constant/ResultStatus.java
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package com.rbkmoney.fraudo.constant;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public enum ResultStatus {
|
||||||
|
|
||||||
|
ACCEPT("accept"),
|
||||||
|
THREE_DS("3ds"),
|
||||||
|
DECLINE("decline"),
|
||||||
|
NOTIFY("notify");
|
||||||
|
|
||||||
|
private String value;
|
||||||
|
private static Map<String, ResultStatus> valueMap = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
for (ResultStatus value : ResultStatus.values()) {
|
||||||
|
valueMap.put(value.value, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultStatus(String value) {
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ResultStatus getByValue(String value) {
|
||||||
|
return valueMap.get(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
package com.rbkmoney.fraudo.exception;
|
||||||
|
|
||||||
|
public class FieldUnsetException extends RuntimeException {
|
||||||
|
|
||||||
|
public static final String ERROR_MESSAGE = "Count target field is not set or bad format! (must be \"*\")";
|
||||||
|
|
||||||
|
public FieldUnsetException() {
|
||||||
|
super(ERROR_MESSAGE);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.rbkmoney.fraudo.exception;
|
||||||
|
|
||||||
|
public class NotImplementedOperatorException extends RuntimeException {
|
||||||
|
|
||||||
|
public static final String ERROR_MESSAGE = "not implemented: operator ";
|
||||||
|
|
||||||
|
public NotImplementedOperatorException(String operator) {
|
||||||
|
super(ERROR_MESSAGE + operator);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.rbkmoney.fraudo.exception;
|
||||||
|
|
||||||
|
public class UnknownResultException extends RuntimeException {
|
||||||
|
|
||||||
|
public static final String ERROR_MESSAGE = "Unknown result: ";
|
||||||
|
|
||||||
|
public UnknownResultException(String result) {
|
||||||
|
super(ERROR_MESSAGE + result);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
package com.rbkmoney.fraudo.exception;
|
||||||
|
|
||||||
|
public class UnresolvableFieldException extends RuntimeException {
|
||||||
|
|
||||||
|
public static final String ERROR_MESSAGE = "Can't find this field: ";
|
||||||
|
|
||||||
|
public UnresolvableFieldException(String fieldName) {
|
||||||
|
super(ERROR_MESSAGE + fieldName);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package com.rbkmoney.fraudo.factory;
|
||||||
|
|
||||||
|
import com.rbkmoney.fraudo.FraudoVisitor;
|
||||||
|
import com.rbkmoney.fraudo.aggregator.CountAggregator;
|
||||||
|
import com.rbkmoney.fraudo.aggregator.SumAggregator;
|
||||||
|
import com.rbkmoney.fraudo.aggregator.UniqueValueAggregator;
|
||||||
|
import com.rbkmoney.fraudo.finder.InListFinder;
|
||||||
|
import com.rbkmoney.fraudo.model.FraudModel;
|
||||||
|
import com.rbkmoney.fraudo.resolver.CountryResolver;
|
||||||
|
import com.rbkmoney.fraudo.visitor.*;
|
||||||
|
|
||||||
|
public class FastFraudVisitorFactory implements FraudVisitorFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FraudoVisitor<Object> createVisitor(FraudModel model, CountAggregator countAggregator,
|
||||||
|
SumAggregator sumAggregator, UniqueValueAggregator uniqueValueAggregator,
|
||||||
|
CountryResolver countryResolver, InListFinder blackListFinder,
|
||||||
|
InListFinder whiteListFinder) {
|
||||||
|
CountVisitorImpl countVisitor = new CountVisitorImpl(model, countAggregator);
|
||||||
|
SumVisitorImpl sumVisitor = new SumVisitorImpl(model, sumAggregator);
|
||||||
|
ListVisitorImpl listVisitor = new ListVisitorImpl(model, blackListFinder, whiteListFinder);
|
||||||
|
CustomFuncVisitorImpl customFuncVisitor = new CustomFuncVisitorImpl(model, uniqueValueAggregator, countryResolver);
|
||||||
|
return new FastFraudVisitorImpl(countVisitor, sumVisitor, listVisitor, customFuncVisitor);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,18 @@
|
|||||||
|
package com.rbkmoney.fraudo.factory;
|
||||||
|
|
||||||
|
import com.rbkmoney.fraudo.FraudoVisitor;
|
||||||
|
import com.rbkmoney.fraudo.aggregator.CountAggregator;
|
||||||
|
import com.rbkmoney.fraudo.aggregator.SumAggregator;
|
||||||
|
import com.rbkmoney.fraudo.aggregator.UniqueValueAggregator;
|
||||||
|
import com.rbkmoney.fraudo.finder.InListFinder;
|
||||||
|
import com.rbkmoney.fraudo.model.FraudModel;
|
||||||
|
import com.rbkmoney.fraudo.resolver.CountryResolver;
|
||||||
|
|
||||||
|
public interface FraudVisitorFactory {
|
||||||
|
|
||||||
|
FraudoVisitor<Object> createVisitor(FraudModel model, CountAggregator countAggregator,
|
||||||
|
SumAggregator sumAggregator, UniqueValueAggregator uniqueValueAggregator,
|
||||||
|
CountryResolver countryResolver, InListFinder blackListFinder,
|
||||||
|
InListFinder whiteListFinder);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package com.rbkmoney.fraudo.finder;
|
||||||
|
|
||||||
|
import com.rbkmoney.fraudo.constant.CheckedField;
|
||||||
|
|
||||||
|
public interface InListFinder {
|
||||||
|
|
||||||
|
Boolean findInList(CheckedField field, String value);
|
||||||
|
|
||||||
|
}
|
15
src/main/java/com/rbkmoney/fraudo/model/FraudModel.java
Normal file
15
src/main/java/com/rbkmoney/fraudo/model/FraudModel.java
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
package com.rbkmoney.fraudo.model;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class FraudModel {
|
||||||
|
|
||||||
|
private String ip;
|
||||||
|
private String email;
|
||||||
|
private String bin;
|
||||||
|
private String fingerprint;
|
||||||
|
private String shopId;
|
||||||
|
private String partyId;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,9 @@
|
|||||||
|
package com.rbkmoney.fraudo.resolver;
|
||||||
|
|
||||||
|
import com.rbkmoney.fraudo.constant.CheckedField;
|
||||||
|
|
||||||
|
public interface CountryResolver {
|
||||||
|
|
||||||
|
String resolveCountry(CheckedField checkedField, String value);
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,24 @@
|
|||||||
|
package com.rbkmoney.fraudo.resolver;
|
||||||
|
|
||||||
|
import com.rbkmoney.fraudo.constant.CheckedField;
|
||||||
|
import com.rbkmoney.fraudo.exception.UnresolvableFieldException;
|
||||||
|
import com.rbkmoney.fraudo.model.FraudModel;
|
||||||
|
|
||||||
|
public class FieldResolver {
|
||||||
|
|
||||||
|
public static String resolveString(String fieldName, FraudModel fraudModel) {
|
||||||
|
switch (CheckedField.getByValue(fieldName)) {
|
||||||
|
case BIN:
|
||||||
|
return fraudModel.getBin();
|
||||||
|
case IP:
|
||||||
|
return fraudModel.getIp();
|
||||||
|
case FINGERPRINT:
|
||||||
|
return fraudModel.getFingerprint();
|
||||||
|
case EMAIL:
|
||||||
|
return fraudModel.getEmail();
|
||||||
|
default:
|
||||||
|
throw new UnresolvableFieldException(fieldName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
16
src/main/java/com/rbkmoney/fraudo/utils/TextUtil.java
Normal file
16
src/main/java/com/rbkmoney/fraudo/utils/TextUtil.java
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
package com.rbkmoney.fraudo.utils;
|
||||||
|
|
||||||
|
import com.rbkmoney.fraudo.exception.FieldUnsetException;
|
||||||
|
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
public class TextUtil {
|
||||||
|
|
||||||
|
public static String safeGetText(TerminalNode field) {
|
||||||
|
return Optional.ofNullable(field)
|
||||||
|
.orElseThrow(FieldUnsetException::new)
|
||||||
|
.getText().replace("\"", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package com.rbkmoney.fraudo.visitor;
|
||||||
|
|
||||||
|
import com.rbkmoney.fraudo.FraudoBaseVisitor;
|
||||||
|
import com.rbkmoney.fraudo.FraudoParser;
|
||||||
|
import com.rbkmoney.fraudo.aggregator.CountAggregator;
|
||||||
|
import com.rbkmoney.fraudo.constant.CheckedField;
|
||||||
|
import com.rbkmoney.fraudo.model.FraudModel;
|
||||||
|
import com.rbkmoney.fraudo.utils.TextUtil;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CountVisitorImpl extends FraudoBaseVisitor<Object> {
|
||||||
|
|
||||||
|
private final FraudModel fraudModel;
|
||||||
|
private final CountAggregator countAggregator;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitCount(com.rbkmoney.fraudo.FraudoParser.CountContext ctx) {
|
||||||
|
String countTarget = TextUtil.safeGetText(ctx.STRING());
|
||||||
|
String time = TextUtil.safeGetText(ctx.DECIMAL());
|
||||||
|
return (double) countAggregator.count(CheckedField.getByValue(countTarget), fraudModel.getEmail(),
|
||||||
|
Long.valueOf(time));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitCount_success(FraudoParser.Count_successContext ctx) {
|
||||||
|
String countTarget = ctx.STRING().getText();
|
||||||
|
String time = TextUtil.safeGetText(ctx.DECIMAL());
|
||||||
|
return (double) countAggregator.countSuccess(CheckedField.getByValue(countTarget), fraudModel.getEmail(),
|
||||||
|
Long.valueOf(time));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitCount_error(FraudoParser.Count_errorContext ctx) {
|
||||||
|
String countTarget = TextUtil.safeGetText(ctx.STRING(0));
|
||||||
|
String time = TextUtil.safeGetText(ctx.DECIMAL());
|
||||||
|
String errorCode = TextUtil.safeGetText(ctx.STRING(1));
|
||||||
|
return (double) countAggregator.countError(CheckedField.getByValue(countTarget), fraudModel.getEmail(),
|
||||||
|
Long.valueOf(time), errorCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,55 @@
|
|||||||
|
package com.rbkmoney.fraudo.visitor;
|
||||||
|
|
||||||
|
import com.rbkmoney.fraudo.FraudoBaseVisitor;
|
||||||
|
import com.rbkmoney.fraudo.FraudoParser;
|
||||||
|
import com.rbkmoney.fraudo.aggregator.UniqueValueAggregator;
|
||||||
|
import com.rbkmoney.fraudo.constant.CheckedField;
|
||||||
|
import com.rbkmoney.fraudo.model.FraudModel;
|
||||||
|
import com.rbkmoney.fraudo.resolver.CountryResolver;
|
||||||
|
import com.rbkmoney.fraudo.resolver.FieldResolver;
|
||||||
|
import com.rbkmoney.fraudo.utils.TextUtil;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.antlr.v4.runtime.tree.TerminalNode;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class CustomFuncVisitorImpl extends FraudoBaseVisitor<Object> {
|
||||||
|
|
||||||
|
private final FraudModel fraudModel;
|
||||||
|
private final UniqueValueAggregator uniqueValueAggregator;
|
||||||
|
private final CountryResolver countryResolver;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitCountry_by(FraudoParser.Country_byContext ctx) {
|
||||||
|
String fieldName = TextUtil.safeGetText(ctx.STRING());
|
||||||
|
String fieldValue = FieldResolver.resolveString(fieldName, fraudModel);
|
||||||
|
return countryResolver.resolveCountry(CheckedField.getByValue(fieldName), fieldValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitIn(FraudoParser.InContext ctx) {
|
||||||
|
String field = TextUtil.safeGetText(ctx.STRING());
|
||||||
|
String fieldValue = FieldResolver.resolveString(field, fraudModel);
|
||||||
|
for (TerminalNode string : ctx.string_list().STRING()) {
|
||||||
|
if (fieldValue.equals(TextUtil.safeGetText(string))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitLike(FraudoParser.LikeContext ctx) {
|
||||||
|
String fieldName = TextUtil.safeGetText(ctx.STRING(0));
|
||||||
|
String fieldValue = FieldResolver.resolveString(fieldName, fraudModel);
|
||||||
|
String pattern = TextUtil.safeGetText(ctx.STRING(1));
|
||||||
|
return fieldValue.matches(pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitUnique(FraudoParser.UniqueContext ctx) {
|
||||||
|
String field = TextUtil.safeGetText(ctx.STRING(0));
|
||||||
|
String fieldBy = TextUtil.safeGetText(ctx.STRING(1));
|
||||||
|
return (double) uniqueValueAggregator.countUniqueValue(CheckedField.getByValue(field), CheckedField.getByValue(fieldBy));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,164 @@
|
|||||||
|
package com.rbkmoney.fraudo.visitor;
|
||||||
|
|
||||||
|
import com.rbkmoney.fraudo.FraudoBaseVisitor;
|
||||||
|
import com.rbkmoney.fraudo.FraudoParser;
|
||||||
|
import com.rbkmoney.fraudo.constant.ResultStatus;
|
||||||
|
import com.rbkmoney.fraudo.exception.NotImplementedOperatorException;
|
||||||
|
import com.rbkmoney.fraudo.exception.UnknownResultException;
|
||||||
|
import com.rbkmoney.fraudo.utils.TextUtil;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class FastFraudVisitorImpl extends FraudoBaseVisitor<Object> {
|
||||||
|
|
||||||
|
private final CountVisitorImpl countVisitor;
|
||||||
|
private final SumVisitorImpl sumVisitor;
|
||||||
|
private final ListVisitorImpl listVisitor;
|
||||||
|
private final CustomFuncVisitorImpl customFuncVisitor;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitFraud_rule(com.rbkmoney.fraudo.FraudoParser.Fraud_ruleContext ctx) {
|
||||||
|
if (asBoolean(ctx.expression())) {
|
||||||
|
return super.visit(ctx.result());
|
||||||
|
}
|
||||||
|
return ResultStatus.ACCEPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitParse(com.rbkmoney.fraudo.FraudoParser.ParseContext ctx) {
|
||||||
|
for (com.rbkmoney.fraudo.FraudoParser.Fraud_ruleContext fraud_ruleContext : ctx.fraud_rule()) {
|
||||||
|
if (asBoolean(fraud_ruleContext.expression())) {
|
||||||
|
String result = fraud_ruleContext.result().getText();
|
||||||
|
return Optional.ofNullable(ResultStatus.getByValue(result))
|
||||||
|
.orElseThrow(() -> new UnknownResultException(result));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ResultStatus.ACCEPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitResult(com.rbkmoney.fraudo.FraudoParser.ResultContext ctx) {
|
||||||
|
return ctx.getText();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitDecimalExpression(com.rbkmoney.fraudo.FraudoParser.DecimalExpressionContext ctx) {
|
||||||
|
return Double.valueOf(ctx.DECIMAL().getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitStringExpression(FraudoParser.StringExpressionContext ctx) {
|
||||||
|
return TextUtil.safeGetText(ctx.STRING());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitNotExpression(com.rbkmoney.fraudo.FraudoParser.NotExpressionContext ctx) {
|
||||||
|
return !((Boolean) this.visit(ctx.expression()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitParenExpression(com.rbkmoney.fraudo.FraudoParser.ParenExpressionContext ctx) {
|
||||||
|
return super.visit(ctx.expression());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitComparatorExpression(com.rbkmoney.fraudo.FraudoParser.ComparatorExpressionContext ctx) {
|
||||||
|
if (ctx.op.EQ() != null) {
|
||||||
|
return this.visit(ctx.left).equals(this.visit(ctx.right));
|
||||||
|
} else if (ctx.op.LE() != null) {
|
||||||
|
return asDouble(ctx.left) <= asDouble(ctx.right);
|
||||||
|
} else if (ctx.op.GE() != null) {
|
||||||
|
return asDouble(ctx.left) >= asDouble(ctx.right);
|
||||||
|
} else if (ctx.op.LT() != null) {
|
||||||
|
return asDouble(ctx.left) < asDouble(ctx.right);
|
||||||
|
} else if (ctx.op.GT() != null) {
|
||||||
|
return asDouble(ctx.left) > asDouble(ctx.right);
|
||||||
|
}
|
||||||
|
throw new NotImplementedOperatorException(ctx.op.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitBinaryExpression(com.rbkmoney.fraudo.FraudoParser.BinaryExpressionContext ctx) {
|
||||||
|
if (ctx.op.AND() != null) {
|
||||||
|
return asBoolean(ctx.left) && asBoolean(ctx.right);
|
||||||
|
} else if (ctx.op.OR() != null) {
|
||||||
|
return asBoolean(ctx.left) || asBoolean(ctx.right);
|
||||||
|
}
|
||||||
|
throw new NotImplementedOperatorException(ctx.op.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitBoolExpression(com.rbkmoney.fraudo.FraudoParser.BoolExpressionContext ctx) {
|
||||||
|
return Boolean.valueOf(ctx.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitCount(com.rbkmoney.fraudo.FraudoParser.CountContext ctx) {
|
||||||
|
return countVisitor.visitCount(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitCount_success(FraudoParser.Count_successContext ctx) {
|
||||||
|
return countVisitor.visitCount_success(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitCount_error(FraudoParser.Count_errorContext ctx) {
|
||||||
|
return countVisitor.visitCount_error(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitSum(FraudoParser.SumContext ctx) {
|
||||||
|
return sumVisitor.visitSum(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitSum_success(FraudoParser.Sum_successContext ctx) {
|
||||||
|
return sumVisitor.visitSum_success(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitSum_error(FraudoParser.Sum_errorContext ctx) {
|
||||||
|
return sumVisitor.visitSum_error(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitCountry_by(FraudoParser.Country_byContext ctx) {
|
||||||
|
return customFuncVisitor.visitCountry_by(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitIn(FraudoParser.InContext ctx) {
|
||||||
|
return customFuncVisitor.visitIn(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitLike(FraudoParser.LikeContext ctx) {
|
||||||
|
return customFuncVisitor.visitLike(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitUnique(FraudoParser.UniqueContext ctx) {
|
||||||
|
return customFuncVisitor.visitUnique(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitIn_white_list(FraudoParser.In_white_listContext ctx) {
|
||||||
|
return listVisitor.visitIn_white_list(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitIn_black_list(FraudoParser.In_black_listContext ctx) {
|
||||||
|
return listVisitor.visitIn_black_list(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean asBoolean(com.rbkmoney.fraudo.FraudoParser.ExpressionContext ctx) {
|
||||||
|
return (boolean) visit(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
private double asDouble(com.rbkmoney.fraudo.FraudoParser.ExpressionContext ctx) {
|
||||||
|
return (double) visit(ctx);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,32 @@
|
|||||||
|
package com.rbkmoney.fraudo.visitor;
|
||||||
|
|
||||||
|
import com.rbkmoney.fraudo.FraudoBaseVisitor;
|
||||||
|
import com.rbkmoney.fraudo.FraudoParser;
|
||||||
|
import com.rbkmoney.fraudo.constant.CheckedField;
|
||||||
|
import com.rbkmoney.fraudo.finder.InListFinder;
|
||||||
|
import com.rbkmoney.fraudo.model.FraudModel;
|
||||||
|
import com.rbkmoney.fraudo.resolver.FieldResolver;
|
||||||
|
import com.rbkmoney.fraudo.utils.TextUtil;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class ListVisitorImpl extends FraudoBaseVisitor<Object> {
|
||||||
|
|
||||||
|
private final FraudModel fraudModel;
|
||||||
|
private final InListFinder blackListFinder;
|
||||||
|
private final InListFinder whiteListFinder;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitIn_white_list(FraudoParser.In_white_listContext ctx) {
|
||||||
|
String fieldName = TextUtil.safeGetText(ctx.STRING());
|
||||||
|
String fieldValue = FieldResolver.resolveString(fieldName, fraudModel);
|
||||||
|
return whiteListFinder.findInList(CheckedField.getByValue(fieldName), fieldValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitIn_black_list(FraudoParser.In_black_listContext ctx) {
|
||||||
|
String fieldName = TextUtil.safeGetText(ctx.STRING());
|
||||||
|
String fieldValue = FieldResolver.resolveString(fieldName, fraudModel);
|
||||||
|
return blackListFinder.findInList(CheckedField.getByValue(fieldName), fieldValue);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
package com.rbkmoney.fraudo.visitor;
|
||||||
|
|
||||||
|
import com.rbkmoney.fraudo.FraudoBaseVisitor;
|
||||||
|
import com.rbkmoney.fraudo.FraudoParser;
|
||||||
|
import com.rbkmoney.fraudo.aggregator.SumAggregator;
|
||||||
|
import com.rbkmoney.fraudo.constant.CheckedField;
|
||||||
|
import com.rbkmoney.fraudo.model.FraudModel;
|
||||||
|
import com.rbkmoney.fraudo.utils.TextUtil;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class SumVisitorImpl extends FraudoBaseVisitor<Object> {
|
||||||
|
|
||||||
|
private final FraudModel fraudModel;
|
||||||
|
private final SumAggregator sumAggregator;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitSum(FraudoParser.SumContext ctx) {
|
||||||
|
String countTarget = TextUtil.safeGetText(ctx.STRING());
|
||||||
|
String time = TextUtil.safeGetText(ctx.DECIMAL());
|
||||||
|
return sumAggregator.sum(CheckedField.getByValue(countTarget), fraudModel.getEmail(), Long.valueOf(time));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitSum_success(FraudoParser.Sum_successContext ctx) {
|
||||||
|
String countTarget = TextUtil.safeGetText(ctx.STRING());
|
||||||
|
return sumAggregator.sumSuccess(CheckedField.getByValue(countTarget), fraudModel.getEmail(), Long.valueOf(ctx.DECIMAL().getText()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object visitSum_error(FraudoParser.Sum_errorContext ctx) {
|
||||||
|
String countTarget = TextUtil.safeGetText(ctx.STRING(0));
|
||||||
|
String errorCode = TextUtil.safeGetText(ctx.STRING(1));
|
||||||
|
return sumAggregator.sumError(CheckedField.getByValue(countTarget), fraudModel.getEmail(),
|
||||||
|
Long.valueOf(ctx.DECIMAL().getText()), errorCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
224
src/test/java/com/rbkmoney/fraudo/FraudoTest.java
Normal file
224
src/test/java/com/rbkmoney/fraudo/FraudoTest.java
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
package com.rbkmoney.fraudo;
|
||||||
|
|
||||||
|
import com.rbkmoney.fraudo.aggregator.CountAggregator;
|
||||||
|
import com.rbkmoney.fraudo.aggregator.SumAggregator;
|
||||||
|
import com.rbkmoney.fraudo.aggregator.UniqueValueAggregator;
|
||||||
|
import com.rbkmoney.fraudo.constant.ResultStatus;
|
||||||
|
import com.rbkmoney.fraudo.exception.UnknownResultException;
|
||||||
|
import com.rbkmoney.fraudo.factory.FastFraudVisitorFactory;
|
||||||
|
import com.rbkmoney.fraudo.finder.InListFinder;
|
||||||
|
import com.rbkmoney.fraudo.model.FraudModel;
|
||||||
|
import com.rbkmoney.fraudo.resolver.CountryResolver;
|
||||||
|
import org.antlr.v4.runtime.ANTLRInputStream;
|
||||||
|
import org.antlr.v4.runtime.CommonTokenStream;
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import static org.mockito.Matchers.*;
|
||||||
|
|
||||||
|
public class FraudoTest {
|
||||||
|
|
||||||
|
public static final String TEST_GMAIL_RU = "test@gmail.ru";
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
CountAggregator countAggregator;
|
||||||
|
@Mock
|
||||||
|
SumAggregator sumAggregator;
|
||||||
|
@Mock
|
||||||
|
UniqueValueAggregator uniqueValueAggregator;
|
||||||
|
@Mock
|
||||||
|
CountryResolver countryResolver;
|
||||||
|
@Mock
|
||||||
|
InListFinder whiteListFinder;
|
||||||
|
@Mock
|
||||||
|
InListFinder blackListFinder;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void init() {
|
||||||
|
MockitoAnnotations.initMocks(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void threeDsTest() throws Exception {
|
||||||
|
InputStream resourceAsStream = FraudoTest.class.getResourceAsStream("/rules/three_ds.frd");
|
||||||
|
com.rbkmoney.fraudo.FraudoLexer lexer = new com.rbkmoney.fraudo.FraudoLexer(new ANTLRInputStream(resourceAsStream));
|
||||||
|
com.rbkmoney.fraudo.FraudoParser parser = new com.rbkmoney.fraudo.FraudoParser(new CommonTokenStream(lexer));
|
||||||
|
|
||||||
|
Mockito.when(countAggregator.count(anyObject(), anyString(), anyLong())).thenReturn(10);
|
||||||
|
Object result = new FastFraudVisitorFactory().createVisitor(new FraudModel(), countAggregator,
|
||||||
|
sumAggregator, uniqueValueAggregator, countryResolver, blackListFinder, whiteListFinder).visit(parser.parse());
|
||||||
|
Assert.assertEquals(ResultStatus.THREE_DS, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void notifyTest() throws Exception {
|
||||||
|
InputStream resourceAsStream = FraudoTest.class.getResourceAsStream("/rules/notify.frd");
|
||||||
|
Object result = parseAndVisit(resourceAsStream);
|
||||||
|
Assert.assertEquals(ResultStatus.NOTIFY, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void declineTest() throws Exception {
|
||||||
|
InputStream resourceAsStream = FraudoTest.class.getResourceAsStream("/rules/decline.frd");
|
||||||
|
Object result = parseAndVisit(resourceAsStream);
|
||||||
|
Assert.assertEquals(ResultStatus.DECLINE, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void acceptTest() throws Exception {
|
||||||
|
InputStream resourceAsStream = FraudoTest.class.getResourceAsStream("/rules/accept.frd");
|
||||||
|
Object result = parseAndVisit(resourceAsStream);
|
||||||
|
Assert.assertEquals(ResultStatus.ACCEPT, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void ruleIsNotFireTest() throws Exception {
|
||||||
|
InputStream resourceAsStream = FraudoTest.class.getResourceAsStream("/rules/rule_is_not_fire.frd");
|
||||||
|
Object result = parseAndVisit(resourceAsStream);
|
||||||
|
Assert.assertEquals(ResultStatus.ACCEPT, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = UnknownResultException.class)
|
||||||
|
public void notImplOperatorTest() throws Exception {
|
||||||
|
InputStream resourceAsStream = FraudoTest.class.getResourceAsStream("/rules/unknownResult.frd");
|
||||||
|
parseAndVisit(resourceAsStream);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void countTest() throws Exception {
|
||||||
|
InputStream resourceAsStream = FraudoTest.class.getResourceAsStream("/rules/count.frd");
|
||||||
|
Mockito.when(countAggregator.count(anyObject(), anyString(), anyLong())).thenReturn(10);
|
||||||
|
Mockito.when(countAggregator.countError(anyObject(), anyString(), anyLong(), anyString())).thenReturn(6);
|
||||||
|
Mockito.when(countAggregator.countSuccess(anyObject(), anyString(), anyLong())).thenReturn(4);
|
||||||
|
com.rbkmoney.fraudo.FraudoParser.ParseContext parseContext = getParseContext(resourceAsStream);
|
||||||
|
Object result = invokeParse(parseContext);
|
||||||
|
Assert.assertEquals(ResultStatus.NOTIFY, result);
|
||||||
|
|
||||||
|
Mockito.when(countAggregator.count(anyObject(), anyString(), anyLong())).thenReturn(9);
|
||||||
|
Mockito.when(countAggregator.countError(anyObject(), anyString(), anyLong(), anyString())).thenReturn(6);
|
||||||
|
Mockito.when(countAggregator.countSuccess(anyObject(), anyString(), anyLong())).thenReturn(6);
|
||||||
|
|
||||||
|
result = invokeParse(parseContext);
|
||||||
|
Assert.assertEquals(ResultStatus.NOTIFY, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void sumTest() throws Exception {
|
||||||
|
InputStream resourceAsStream = FraudoTest.class.getResourceAsStream("/rules/sum.frd");
|
||||||
|
Mockito.when(sumAggregator.sum(anyObject(), anyString(), anyLong())).thenReturn(10500.60);
|
||||||
|
Mockito.when(sumAggregator.sumError(anyObject(), anyString(), anyLong(), anyString())).thenReturn(524.0);
|
||||||
|
Mockito.when(sumAggregator.sumSuccess(anyObject(), anyString(), anyLong())).thenReturn(4.0);
|
||||||
|
com.rbkmoney.fraudo.FraudoParser.ParseContext parseContext = getParseContext(resourceAsStream);
|
||||||
|
Object result = invokeParse(parseContext);
|
||||||
|
Assert.assertEquals(ResultStatus.NOTIFY, result);
|
||||||
|
|
||||||
|
Mockito.when(sumAggregator.sum(anyObject(), anyString(), anyLong())).thenReturn(90.0);
|
||||||
|
Mockito.when(sumAggregator.sumError(anyObject(), anyString(), anyLong(), anyString())).thenReturn(524.0);
|
||||||
|
Mockito.when(sumAggregator.sumSuccess(anyObject(), anyString(), anyLong())).thenReturn(501.0);
|
||||||
|
|
||||||
|
result = invokeParse(parseContext);
|
||||||
|
Assert.assertEquals(ResultStatus.NOTIFY, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void inTest() throws Exception {
|
||||||
|
InputStream resourceAsStream = FraudoTest.class.getResourceAsStream("/rules/in.frd");
|
||||||
|
com.rbkmoney.fraudo.FraudoParser.ParseContext parseContext = getParseContext(resourceAsStream);
|
||||||
|
FraudModel model = new FraudModel();
|
||||||
|
model.setEmail(TEST_GMAIL_RU);
|
||||||
|
Object result = invoke(parseContext, model);
|
||||||
|
Assert.assertEquals(ResultStatus.NOTIFY, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void likeTest() throws Exception {
|
||||||
|
InputStream resourceAsStream = FraudoTest.class.getResourceAsStream("/rules/like.frd");
|
||||||
|
com.rbkmoney.fraudo.FraudoParser.ParseContext parseContext = getParseContext(resourceAsStream);
|
||||||
|
FraudModel model = new FraudModel();
|
||||||
|
model.setEmail(TEST_GMAIL_RU);
|
||||||
|
Object result = invoke(parseContext, model);
|
||||||
|
Assert.assertEquals(ResultStatus.NOTIFY, result);
|
||||||
|
|
||||||
|
model.setEmail("teeeee");
|
||||||
|
result = invoke(parseContext, model);
|
||||||
|
Assert.assertEquals(ResultStatus.ACCEPT, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void inNotTest() throws Exception {
|
||||||
|
InputStream resourceAsStream = FraudoTest.class.getResourceAsStream("/rules/in_not.frd");
|
||||||
|
com.rbkmoney.fraudo.FraudoParser.ParseContext parseContext = getParseContext(resourceAsStream);
|
||||||
|
FraudModel model = new FraudModel();
|
||||||
|
model.setEmail(TEST_GMAIL_RU);
|
||||||
|
Object result = invoke(parseContext, model);
|
||||||
|
Assert.assertEquals(ResultStatus.ACCEPT, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void uniqCountTest() throws Exception {
|
||||||
|
InputStream resourceAsStream = FraudoTest.class.getResourceAsStream("/rules/count_uniq.frd");
|
||||||
|
Mockito.when(uniqueValueAggregator.countUniqueValue(any(), any())).thenReturn(2);
|
||||||
|
com.rbkmoney.fraudo.FraudoParser.ParseContext parseContext = getParseContext(resourceAsStream);
|
||||||
|
Object result = invokeParse(parseContext);
|
||||||
|
Assert.assertEquals(ResultStatus.DECLINE, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void whiteBlackListTest() throws Exception {
|
||||||
|
InputStream resourceAsStream = FraudoTest.class.getResourceAsStream("/rules/whitelist.frd");
|
||||||
|
com.rbkmoney.fraudo.FraudoParser.ParseContext parseContext = getParseContext(resourceAsStream);
|
||||||
|
Mockito.when(whiteListFinder.findInList(any(), anyString())).thenReturn(true);
|
||||||
|
Object result = invokeParse(parseContext);
|
||||||
|
Assert.assertEquals(ResultStatus.NOTIFY, result);
|
||||||
|
|
||||||
|
resourceAsStream = FraudoTest.class.getResourceAsStream("/rules/blacklist.frd");
|
||||||
|
parseContext = getParseContext(resourceAsStream);
|
||||||
|
Mockito.when(blackListFinder.findInList(any(), anyString())).thenReturn(true);
|
||||||
|
result = invokeParse(parseContext);
|
||||||
|
Assert.assertEquals(ResultStatus.NOTIFY, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void eqCountryTest() throws Exception {
|
||||||
|
InputStream resourceAsStream = FraudoTest.class.getResourceAsStream("/rules/eq_country.frd");
|
||||||
|
|
||||||
|
Mockito.when(countryResolver.resolveCountry(any(), anyString())).thenReturn("RU");
|
||||||
|
|
||||||
|
Object result = parseAndVisit(resourceAsStream);
|
||||||
|
Assert.assertEquals(ResultStatus.NOTIFY, result);
|
||||||
|
|
||||||
|
Mockito.when(countryResolver.resolveCountry(any(), anyString())).thenReturn("US");
|
||||||
|
resourceAsStream = FraudoTest.class.getResourceAsStream("/rules/eq_country.frd");
|
||||||
|
result = parseAndVisit(resourceAsStream);
|
||||||
|
Assert.assertEquals(ResultStatus.ACCEPT, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object parseAndVisit(InputStream resourceAsStream) throws IOException {
|
||||||
|
com.rbkmoney.fraudo.FraudoParser.ParseContext parse = getParseContext(resourceAsStream);
|
||||||
|
return invokeParse(parse);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object invokeParse(com.rbkmoney.fraudo.FraudoParser.ParseContext parse) {
|
||||||
|
FraudModel model = new FraudModel();
|
||||||
|
return invoke(parse, model);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object invoke(com.rbkmoney.fraudo.FraudoParser.ParseContext parse, FraudModel model) {
|
||||||
|
return new FastFraudVisitorFactory().createVisitor(model, countAggregator,
|
||||||
|
sumAggregator, uniqueValueAggregator, countryResolver, blackListFinder, whiteListFinder).visit(parse);
|
||||||
|
}
|
||||||
|
|
||||||
|
private com.rbkmoney.fraudo.FraudoParser.ParseContext getParseContext(InputStream resourceAsStream) throws IOException {
|
||||||
|
com.rbkmoney.fraudo.FraudoLexer lexer = new com.rbkmoney.fraudo.FraudoLexer(new ANTLRInputStream(resourceAsStream));
|
||||||
|
com.rbkmoney.fraudo.FraudoParser parser = new com.rbkmoney.fraudo.FraudoParser(new CommonTokenStream(lexer));
|
||||||
|
|
||||||
|
return parser.parse();
|
||||||
|
}
|
||||||
|
}
|
2
src/test/resources/rules/accept.frd
Normal file
2
src/test/resources/rules/accept.frd
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
rule: 3 > 2 AND 1 = 1
|
||||||
|
-> accept;
|
2
src/test/resources/rules/blacklist.frd
Normal file
2
src/test/resources/rules/blacklist.frd
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
rule: inBlackList("email")
|
||||||
|
-> notify;
|
3
src/test/resources/rules/count.frd
Normal file
3
src/test/resources/rules/count.frd
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
rule: (count("ip", 1444) >= 10 OR countSuccess("email", 1444) > 5)
|
||||||
|
AND countError("fingerprint", 1444, "error_code") > 5
|
||||||
|
-> notify;
|
2
src/test/resources/rules/count_uniq.frd
Normal file
2
src/test/resources/rules/count_uniq.frd
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
rule: unique("email", "ip") < 4
|
||||||
|
-> decline;
|
2
src/test/resources/rules/decline.frd
Normal file
2
src/test/resources/rules/decline.frd
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
rule: 3 > 2 AND 1 = 1
|
||||||
|
-> decline;
|
2
src/test/resources/rules/eq_country.frd
Normal file
2
src/test/resources/rules/eq_country.frd
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
rule: countryBy("ip") = "RU"
|
||||||
|
-> notify;
|
2
src/test/resources/rules/in.frd
Normal file
2
src/test/resources/rules/in.frd
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
rule: in("email", "test@gmail", "test@gmail.ru")
|
||||||
|
-> notify;
|
2
src/test/resources/rules/in_not.frd
Normal file
2
src/test/resources/rules/in_not.frd
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
rule: in("email", "testgmail", "test@g")
|
||||||
|
-> notify;
|
2
src/test/resources/rules/like.frd
Normal file
2
src/test/resources/rules/like.frd
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
rule: like("email", "test.*")
|
||||||
|
-> notify;
|
2
src/test/resources/rules/notify.frd
Normal file
2
src/test/resources/rules/notify.frd
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
rule: 3 > 2 AND 1 = 1
|
||||||
|
-> notify;
|
2
src/test/resources/rules/rule_is_not_fire.frd
Normal file
2
src/test/resources/rules/rule_is_not_fire.frd
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
rule: 3 < 2
|
||||||
|
-> decline;
|
3
src/test/resources/rules/sum.frd
Normal file
3
src/test/resources/rules/sum.frd
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
rule: (sum("ip", 1444) >= 10500.50 OR sumSuccess("email", 1444) > 500)
|
||||||
|
AND sumError("fingerprint", 1444, "error_code") > 523.12
|
||||||
|
-> notify;
|
5
src/test/resources/rules/three_ds.frd
Normal file
5
src/test/resources/rules/three_ds.frd
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
rule: 3 > 2 AND 1 > 1
|
||||||
|
-> decline;
|
||||||
|
|
||||||
|
rule: count("email", 10) <= 10 AND count("ip", 1444) = 10
|
||||||
|
-> 3ds;
|
2
src/test/resources/rules/unknownResult.frd
Normal file
2
src/test/resources/rules/unknownResult.frd
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
rule: 3 > 2
|
||||||
|
-> asd;
|
2
src/test/resources/rules/whitelist.frd
Normal file
2
src/test/resources/rules/whitelist.frd
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
rule: inWhiteList("email")
|
||||||
|
-> notify;
|
BIN
syntax.png
Normal file
BIN
syntax.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
Loading…
Reference in New Issue
Block a user