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)
|
||||
}
|
65
README.md
65
README.md
@ -1 +1,66 @@
|
||||
# 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