Add catch exception and change readme (#8)

* Add catch exception and change readme

* Add get current amount

* Delete from country field check
This commit is contained in:
Kostya 2019-02-06 12:13:30 +03:00 committed by GitHub
parent 65f5f33836
commit d479347a90
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 99 additions and 41 deletions

2
.gitignore vendored
View File

@ -71,3 +71,5 @@ fabric.properties
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
env.list
gen

View File

@ -19,8 +19,19 @@
* inWhiteList("field")
* inBlackList("field")
* like("field", "regexp_in_java_style"[1])
* countryBy("field") - this function can return result "unknown", you must remember it!
* amount()
* country() - this function can return result "unknown", you must remember it!
~~~~
##### group_field:
* email,
* ip,
* fingerprint,
* bin,
* shop_ip,
* party_id,
* card_token
1. [regexp_in_java_style](https://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html)
##### RESULTS:
~~~~
@ -54,9 +65,14 @@ rule: unique("email", "ip") < 4
~~~~
###### Check country by ip:
~~~~
rule: countryBy("ip") = "RU"
rule: country() = "RU"
-> notify;
~~~~
###### Check current amount:
~~~~
rule: amount() < 100
-> accept;
~~~~
###### Combined check:
~~~~
rule: 3 > 2 AND 1 > 1

View File

@ -5,7 +5,7 @@ parse
;
fraud_rule
: RULE_BLOCK expression RETURN result SCOL
: RULE_BLOCK expression RETURN result (CATCH_ERROR catch_result)? SCOL
;
expression
@ -25,7 +25,8 @@ expression
| in_white_list #inWhiteListExpression
| in_black_list #inBlackListExpression
| like #likeFunctionExpression
| country_by #countryByFunctionExpression
| country #countryFunctionExpression
| amount #amountFunctionExpression
| IDENTIFIER #identifierExpression
| DECIMAL #decimalExpression
| STRING #stringExpression
@ -43,6 +44,10 @@ bool
: TRUE | FALSE
;
amount
: 'amount' LPAREN RPAREN
;
count
: 'count' LPAREN STRING DELIMETER DECIMAL RPAREN
;
@ -87,14 +92,18 @@ like
: 'like' LPAREN STRING DELIMETER STRING RPAREN
;
country_by
: 'countryBy' LPAREN STRING RPAREN
country
: 'country' LPAREN RPAREN
;
result
: 'accept' | '3ds' | 'decline' | 'notify'
;
catch_result
: 'accept' | '3ds' | 'decline' | 'notify'
;
string_list
: STRING (',' STRING | WS)+
;
@ -110,12 +119,13 @@ COMMENT
;
RETURN : '->' ;
CATCH_ERROR: 'catch:' ;
RULE_BLOCK : 'rule:' ;
AND : 'AND' ;
OR : 'OR' ;
NOT : 'NOT';
TRUE : 'TRUE' ;
FALSE : 'FALSE' ;
AND : 'AND' | 'and';
OR : 'OR' | 'or' ;
NOT : 'NOT' | 'not';
TRUE : 'TRUE' | 'true';
FALSE : 'FALSE' | 'false';
GT : '>' ;
GE : '>=' ;
LT : '<' ;

View File

@ -1,11 +1,9 @@
package com.rbkmoney.fraudo.resolver;
import com.rbkmoney.fraudo.constant.CheckedField;
public interface CountryResolver {
String UNKNOWN_VALUE = "unknown";
String resolveCountry(CheckedField checkedField, String value);
String resolveCountryByIp(String value);
}

View File

@ -19,10 +19,8 @@ public class CustomFuncVisitorImpl extends FraudoBaseVisitor<Object> {
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);
public Object visitCountry(FraudoParser.CountryContext ctx) {
return countryResolver.resolveCountryByIp(fraudModel.getIp());
}
@Override
@ -53,4 +51,8 @@ public class CustomFuncVisitorImpl extends FraudoBaseVisitor<Object> {
CheckedField.getByValue(fieldBy));
}
@Override
public Object visitAmount(FraudoParser.AmountContext ctx) {
return (double) fraudModel.getAmount();
}
}

View File

@ -22,8 +22,15 @@ public class FastFraudVisitorImpl extends FraudoBaseVisitor<Object> {
@Override
public Object visitFraud_rule(com.rbkmoney.fraudo.FraudoParser.Fraud_ruleContext ctx) {
try {
if (asBoolean(ctx.expression())) {
return super.visit(ctx.result());
return ResultStatus.getByValue((String) super.visit(ctx.result()));
}
} catch (Exception e) {
if (ctx.catch_result() != null && ctx.catch_result().getText() != null) {
return ResultStatus.getByValue(ctx.catch_result().getText());
}
return ResultStatus.THREE_DS;
}
return ResultStatus.NORMAL;
}
@ -32,15 +39,13 @@ public class FastFraudVisitorImpl extends FraudoBaseVisitor<Object> {
public Object visitParse(com.rbkmoney.fraudo.FraudoParser.ParseContext ctx) {
List<String> notifications = new ArrayList<>();
for (com.rbkmoney.fraudo.FraudoParser.Fraud_ruleContext fraud_ruleContext : ctx.fraud_rule()) {
if (asBoolean(fraud_ruleContext.expression())) {
String result = fraud_ruleContext.result().getText();
if (result != null && ResultStatus.NOTIFY.equals(ResultStatus.getByValue(result))) {
ResultStatus result = (ResultStatus) visitFraud_rule(fraud_ruleContext);
if (result != null && ResultStatus.NOTIFY.equals(result)) {
notifications.add(String.valueOf(fraud_ruleContext.getRuleIndex()));
} else if (result != null && ResultStatus.getByValue(result) != null) {
return new ResultModel(ResultStatus.getByValue(result), notifications);
} else {
throw new UnknownResultException(result);
}
} else if (result != null && !ResultStatus.NORMAL.equals(result)) {
return new ResultModel(result, notifications);
} else if (result == null) {
throw new UnknownResultException(fraud_ruleContext.getText());
}
}
return new ResultModel(ResultStatus.NORMAL, notifications);
@ -133,8 +138,8 @@ public class FastFraudVisitorImpl extends FraudoBaseVisitor<Object> {
}
@Override
public Object visitCountry_by(FraudoParser.Country_byContext ctx) {
return customFuncVisitor.visitCountry_by(ctx);
public Object visitCountry(FraudoParser.CountryContext ctx) {
return customFuncVisitor.visitCountry(ctx);
}
@Override
@ -162,6 +167,11 @@ public class FastFraudVisitorImpl extends FraudoBaseVisitor<Object> {
return listVisitor.visitIn_black_list(ctx);
}
@Override
public Object visitAmount(FraudoParser.AmountContext ctx) {
return customFuncVisitor.visitAmount(ctx);
}
private boolean asBoolean(com.rbkmoney.fraudo.FraudoParser.ExpressionContext ctx) {
return (boolean) visit(ctx);
}

View File

@ -49,12 +49,8 @@ public class FraudoTest {
@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(), any(), anyLong())).thenReturn(10);
ResultModel result = (ResultModel) new FastFraudVisitorFactory().createVisitor(new FraudModel(), countAggregator,
sumAggregator, uniqueValueAggregator, countryResolver, blackListFinder, whiteListFinder).visit(parser.parse());
ResultModel result = parseAndVisit(resourceAsStream);
Assert.assertEquals(ResultStatus.THREE_DS, result.getResultStatus());
}
@ -141,6 +137,25 @@ public class FraudoTest {
Assert.assertEquals(ResultStatus.ACCEPT, result.getResultStatus());
}
@Test
public void amountTest() throws Exception {
InputStream resourceAsStream = FraudoTest.class.getResourceAsStream("/rules/amount.frd");
com.rbkmoney.fraudo.FraudoParser.ParseContext parseContext = getParseContext(resourceAsStream);
FraudModel model = new FraudModel();
model.setAmount(56L);
ResultModel result = invoke(parseContext, model);
Assert.assertEquals(ResultStatus.ACCEPT, result.getResultStatus());
}
@Test
public void catchTest() throws Exception {
InputStream resourceAsStream = FraudoTest.class.getResourceAsStream("/rules/catch.frd");
Mockito.when(uniqueValueAggregator.countUniqueValue(any(), any(), any())).thenThrow(new UnknownResultException("as"));
com.rbkmoney.fraudo.FraudoParser.ParseContext parseContext = getParseContext(resourceAsStream);
ResultModel result = invokeParse(parseContext);
Assert.assertEquals(ResultStatus.DECLINE, result.getResultStatus());
}
@Test
public void likeTest() throws Exception {
InputStream resourceAsStream = FraudoTest.class.getResourceAsStream("/rules/like.frd");
@ -194,13 +209,13 @@ public class FraudoTest {
public void eqCountryTest() throws Exception {
InputStream resourceAsStream = FraudoTest.class.getResourceAsStream("/rules/eq_country.frd");
Mockito.when(countryResolver.resolveCountry(any(), anyString())).thenReturn("RU");
Mockito.when(countryResolver.resolveCountryByIp( anyString())).thenReturn("RU");
ResultModel result = parseAndVisit(resourceAsStream);
Assert.assertEquals(ResultStatus.NORMAL, result.getResultStatus());
Assert.assertEquals(1, result.getNotificationsRule().size());
Mockito.when(countryResolver.resolveCountry(any(), anyString())).thenReturn("US");
Mockito.when(countryResolver.resolveCountryByIp( anyString())).thenReturn("US");
resourceAsStream = FraudoTest.class.getResourceAsStream("/rules/eq_country.frd");
result = parseAndVisit(resourceAsStream);
Assert.assertEquals(ResultStatus.NORMAL, result.getResultStatus());

View File

@ -0,0 +1,2 @@
rule: amount() < 100
-> accept;

View File

@ -0,0 +1,3 @@
rule: unique("email", "ip") < 4
-> accept
catch: decline;

View File

@ -1,2 +1,2 @@
rule: countryBy("ip") = "RU"
rule: country() = "RU"
-> notify;

View File

@ -1,3 +1,3 @@
rule: (sum("ip", 1444) >= 10500.50 OR sumSuccess("email", 1444) > 500)
AND sumError("fingerprint", 1444, "error_code") > 523.12
rule: (sum("ip", 1444) >= 10500.50 or sumSuccess("email", 1444) > 500)
and sumError("fingerprint", 1444, "error_code") > 523.12
-> notify;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 17 KiB