Add nootification to result

This commit is contained in:
k.struzhkin 2018-12-28 17:23:56 +03:00
parent 67e63848af
commit b75c08cb64
14 changed files with 139 additions and 70 deletions

26
pom.xml
View File

@ -11,10 +11,11 @@
</parent>
<artifactId>fraudo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<version>0.0.2-SNAPSHOT</version>
<properties>
<hamcrest.junit.version>2.0.0.0</hamcrest.junit.version>
<sonar.jacoco.reportPath>${project.basedir}/../target/jacoco.exec</sonar.jacoco.reportPath>
</properties>
<dependencies>
@ -74,6 +75,29 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.8.2</version>
<configuration>
<destFile>${sonar.jacoco.reportPath}</destFile>
<append>true</append>
<excludes>
<exclude>com/rbkmoney/fraudo/constant/**/*</exclude>
<exclude>com/rbkmoney/fraudo/model/**/*</exclude>
</excludes>
</configuration>
<executions>
<execution>
<id>agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,13 +1,14 @@
package com.rbkmoney.fraudo.aggregator;
import com.rbkmoney.fraudo.constant.CheckedField;
import com.rbkmoney.fraudo.model.FraudModel;
public interface CountAggregator {
Integer count(CheckedField checkedField, String valueField, Long timeInMinutes);
Integer count(CheckedField checkedField, FraudModel model, Long timeInMinutes);
Integer countSuccess(CheckedField checkedField, String valueField, Long timeInMinutes);
Integer countSuccess(CheckedField checkedField, FraudModel model, Long timeInMinutes);
Integer countError(CheckedField checkedField, String valueField, Long timeInMinutes, String errorCode);
Integer countError(CheckedField checkedField, FraudModel model, Long timeInMinutes, String errorCode);
}

View File

@ -1,13 +1,14 @@
package com.rbkmoney.fraudo.aggregator;
import com.rbkmoney.fraudo.constant.CheckedField;
import com.rbkmoney.fraudo.model.FraudModel;
public interface SumAggregator {
Double sum(CheckedField checkedField, String email, Long timeInMinutes);
Double sum(CheckedField checkedField, FraudModel model, Long timeInMinutes);
Double sumSuccess(CheckedField checkedField, String valueField, Long timeInMinutes);
Double sumSuccess(CheckedField checkedField, FraudModel model, Long timeInMinutes);
Double sumError(CheckedField checkedField, String valueField, Long timeInMinutes, String errorCode);
Double sumError(CheckedField checkedField, FraudModel model, Long timeInMinutes, String errorCode);
}

View File

@ -8,6 +8,7 @@ public enum ResultStatus {
ACCEPT("accept"),
THREE_DS("3ds"),
DECLINE("decline"),
NORMAL("normal"),
NOTIFY("notify");
private String value;

View File

@ -0,0 +1,15 @@
package com.rbkmoney.fraudo.model;
import com.rbkmoney.fraudo.constant.ResultStatus;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.List;
@Data
@AllArgsConstructor
public class ResultModel {
private ResultStatus resultStatus;
private List<String> notificationsRule;
}

View File

@ -18,7 +18,7 @@ public class CountVisitorImpl extends FraudoBaseVisitor<Object> {
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(),
return (double) countAggregator.count(CheckedField.getByValue(countTarget), fraudModel,
Long.valueOf(time));
}
@ -26,7 +26,7 @@ public class CountVisitorImpl extends FraudoBaseVisitor<Object> {
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(),
return (double) countAggregator.countSuccess(CheckedField.getByValue(countTarget), fraudModel,
Long.valueOf(time));
}
@ -35,7 +35,7 @@ public class CountVisitorImpl extends FraudoBaseVisitor<Object> {
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(),
return (double) countAggregator.countError(CheckedField.getByValue(countTarget), fraudModel,
Long.valueOf(time), errorCode);
}

View File

@ -5,10 +5,12 @@ 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.model.ResultModel;
import com.rbkmoney.fraudo.utils.TextUtil;
import lombok.RequiredArgsConstructor;
import java.util.Optional;
import java.util.ArrayList;
import java.util.List;
@RequiredArgsConstructor
public class FastFraudVisitorImpl extends FraudoBaseVisitor<Object> {
@ -23,19 +25,25 @@ public class FastFraudVisitorImpl extends FraudoBaseVisitor<Object> {
if (asBoolean(ctx.expression())) {
return super.visit(ctx.result());
}
return ResultStatus.ACCEPT;
return ResultStatus.NORMAL;
}
@Override
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();
return Optional.ofNullable(ResultStatus.getByValue(result))
.orElseThrow(() -> new UnknownResultException(result));
if (result != null && ResultStatus.NOTIFY.equals(ResultStatus.getByValue(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);
}
}
}
return ResultStatus.ACCEPT;
return new ResultModel(ResultStatus.NORMAL, notifications);
}
@Override

View File

@ -18,20 +18,20 @@ public class SumVisitorImpl extends FraudoBaseVisitor<Object> {
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));
return sumAggregator.sum(CheckedField.getByValue(countTarget), fraudModel, 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()));
return sumAggregator.sumSuccess(CheckedField.getByValue(countTarget), fraudModel, 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(),
return sumAggregator.sumError(CheckedField.getByValue(countTarget), fraudModel,
Long.valueOf(ctx.DECIMAL().getText()), errorCode);
}

View File

@ -8,6 +8,7 @@ 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.model.ResultModel;
import com.rbkmoney.fraudo.resolver.CountryResolver;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
@ -51,38 +52,39 @@ public class FraudoTest {
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,
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());
Assert.assertEquals(ResultStatus.THREE_DS, result);
Assert.assertEquals(ResultStatus.THREE_DS, result.getResultStatus());
}
@Test
public void notifyTest() throws Exception {
InputStream resourceAsStream = FraudoTest.class.getResourceAsStream("/rules/notify.frd");
Object result = parseAndVisit(resourceAsStream);
Assert.assertEquals(ResultStatus.NOTIFY, result);
ResultModel result = parseAndVisit(resourceAsStream);
Assert.assertEquals(ResultStatus.NORMAL, result.getResultStatus());
Assert.assertEquals(1, result.getNotificationsRule().size());
}
@Test
public void declineTest() throws Exception {
InputStream resourceAsStream = FraudoTest.class.getResourceAsStream("/rules/decline.frd");
Object result = parseAndVisit(resourceAsStream);
Assert.assertEquals(ResultStatus.DECLINE, result);
ResultModel result = parseAndVisit(resourceAsStream);
Assert.assertEquals(ResultStatus.DECLINE, result.getResultStatus());
}
@Test
public void acceptTest() throws Exception {
InputStream resourceAsStream = FraudoTest.class.getResourceAsStream("/rules/accept.frd");
Object result = parseAndVisit(resourceAsStream);
Assert.assertEquals(ResultStatus.ACCEPT, result);
ResultModel result = parseAndVisit(resourceAsStream);
Assert.assertEquals(ResultStatus.ACCEPT, result.getResultStatus());
}
@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);
ResultModel result = parseAndVisit(resourceAsStream);
Assert.assertEquals(ResultStatus.NORMAL, result.getResultStatus());
}
@Test(expected = UnknownResultException.class)
@ -94,37 +96,39 @@ public class FraudoTest {
@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);
Mockito.when(countAggregator.count(anyObject(), any(), anyLong())).thenReturn(10);
Mockito.when(countAggregator.countError(anyObject(), any(), anyLong(), anyString())).thenReturn(6);
Mockito.when(countAggregator.countSuccess(anyObject(), any(), anyLong())).thenReturn(4);
com.rbkmoney.fraudo.FraudoParser.ParseContext parseContext = getParseContext(resourceAsStream);
Object result = invokeParse(parseContext);
Assert.assertEquals(ResultStatus.NOTIFY, result);
ResultModel result = invokeParse(parseContext);
Assert.assertEquals(ResultStatus.DECLINE, result.getResultStatus());
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);
Mockito.when(countAggregator.count(anyObject(), any(), anyLong())).thenReturn(9);
Mockito.when(countAggregator.countError(anyObject(), any(), anyLong(), anyString())).thenReturn(6);
Mockito.when(countAggregator.countSuccess(anyObject(), any(), anyLong())).thenReturn(6);
result = invokeParse(parseContext);
Assert.assertEquals(ResultStatus.NOTIFY, result);
Assert.assertEquals(ResultStatus.DECLINE, result.getResultStatus());
}
@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);
Mockito.when(sumAggregator.sum(anyObject(), any(), anyLong())).thenReturn(10500.60);
Mockito.when(sumAggregator.sumError(anyObject(), any(), anyLong(), anyString())).thenReturn(524.0);
Mockito.when(sumAggregator.sumSuccess(anyObject(), any(), anyLong())).thenReturn(4.0);
com.rbkmoney.fraudo.FraudoParser.ParseContext parseContext = getParseContext(resourceAsStream);
Object result = invokeParse(parseContext);
Assert.assertEquals(ResultStatus.NOTIFY, result);
ResultModel result = invokeParse(parseContext);
Assert.assertEquals(ResultStatus.NORMAL, result.getResultStatus());
Assert.assertEquals(1, result.getNotificationsRule().size());
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);
Mockito.when(sumAggregator.sum(anyObject(), any(), anyLong())).thenReturn(90.0);
Mockito.when(sumAggregator.sumError(anyObject(), any(), anyLong(), anyString())).thenReturn(504.0);
Mockito.when(sumAggregator.sumSuccess(anyObject(), any(), anyLong())).thenReturn(501.0);
result = invokeParse(parseContext);
Assert.assertEquals(ResultStatus.NOTIFY, result);
Assert.assertEquals(ResultStatus.NORMAL, result.getResultStatus());
Assert.assertEquals(0, result.getNotificationsRule().size());
}
@Test
@ -133,8 +137,8 @@ public class FraudoTest {
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);
ResultModel result = invoke(parseContext, model);
Assert.assertEquals(ResultStatus.ACCEPT, result.getResultStatus());
}
@Test
@ -143,12 +147,12 @@ public class FraudoTest {
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);
ResultModel result = invoke(parseContext, model);
Assert.assertEquals(ResultStatus.ACCEPT, result.getResultStatus());
model.setEmail("teeeee");
result = invoke(parseContext, model);
Assert.assertEquals(ResultStatus.ACCEPT, result);
Assert.assertEquals(ResultStatus.NORMAL, result.getResultStatus());
}
@Test
@ -157,8 +161,8 @@ public class FraudoTest {
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);
ResultModel result = invoke(parseContext, model);
Assert.assertEquals(ResultStatus.NORMAL, result.getResultStatus());
}
@Test
@ -166,8 +170,8 @@ public class FraudoTest {
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);
ResultModel result = invokeParse(parseContext);
Assert.assertEquals(ResultStatus.DECLINE, result.getResultStatus());
}
@Test
@ -175,14 +179,15 @@ public class FraudoTest {
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);
ResultModel result = invokeParse(parseContext);
Assert.assertEquals(ResultStatus.DECLINE, result.getResultStatus());
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);
Assert.assertEquals(ResultStatus.NORMAL, result.getResultStatus());
Assert.assertEquals(1, result.getNotificationsRule().size());
}
@Test
@ -191,27 +196,33 @@ public class FraudoTest {
Mockito.when(countryResolver.resolveCountry(any(), anyString())).thenReturn("RU");
Object result = parseAndVisit(resourceAsStream);
Assert.assertEquals(ResultStatus.NOTIFY, result);
ResultModel result = parseAndVisit(resourceAsStream);
Assert.assertEquals(ResultStatus.NORMAL, result.getResultStatus());
Assert.assertEquals(1, result.getNotificationsRule().size());
Mockito.when(countryResolver.resolveCountry(any(), anyString())).thenReturn("US");
resourceAsStream = FraudoTest.class.getResourceAsStream("/rules/eq_country.frd");
result = parseAndVisit(resourceAsStream);
Assert.assertEquals(ResultStatus.ACCEPT, result);
Assert.assertEquals(ResultStatus.NORMAL, result.getResultStatus());
resourceAsStream = FraudoTest.class.getResourceAsStream("/rules/accept_with_notify.frd");
result = parseAndVisit(resourceAsStream);
Assert.assertEquals(ResultStatus.ACCEPT, result.getResultStatus());
Assert.assertEquals(2, result.getNotificationsRule().size());
}
private Object parseAndVisit(InputStream resourceAsStream) throws IOException {
private ResultModel 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) {
private ResultModel 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,
private ResultModel invoke(com.rbkmoney.fraudo.FraudoParser.ParseContext parse, FraudModel model) {
return (ResultModel) new FastFraudVisitorFactory().createVisitor(model, countAggregator,
sumAggregator, uniqueValueAggregator, countryResolver, blackListFinder, whiteListFinder).visit(parse);
}

View File

@ -0,0 +1,8 @@
rule: 12 > 3
-> notify;
rule: 12 > 3
-> notify;
rule: 12 > 3
-> accept;

View File

@ -1,3 +1,3 @@
rule: (count("ip", 1444) >= 10 OR countSuccess("email", 1444) > 5)
AND countError("fingerprint", 1444, "error_code") > 5
-> notify;
-> decline;

View File

@ -1,2 +1,2 @@
rule: in("email", "test@gmail", "test@gmail.ru")
-> notify;
-> accept;

View File

@ -1,2 +1,2 @@
rule: like("email", "test.*")
-> notify;
-> accept;

View File

@ -1,2 +1,2 @@
rule: inWhiteList("email")
-> notify;
-> decline;