1
1
package com .featureprobe .sdk .server .model ;
2
2
3
- import com .featureprobe .sdk .server .FPUser ;
4
- import com .featureprobe .sdk .server .StringMatcher ;
5
- import com .featureprobe .sdk .server .SegmentMatcher ;
3
+ import com .featureprobe .sdk .server .*;
6
4
import org .apache .commons .lang3 .StringUtils ;
5
+ import org .apache .maven .artifact .versioning .ComparableVersion ;
6
+ import org .slf4j .Logger ;
7
7
8
- import java .util .HashMap ;
9
- import java .util .List ;
10
- import java .util .Map ;
11
- import java .util .Objects ;
8
+ import java .util .*;
12
9
import java .util .regex .Pattern ;
13
10
14
11
public final class Condition {
15
12
13
+ private static final Logger logger = Loggers .EVALUATOR ;
14
+
16
15
private ConditionType type ;
17
16
18
17
private String subject ;
@@ -21,13 +20,20 @@ public final class Condition {
21
20
22
21
private List <String > objects ;
23
22
24
- private static final Map <PredicateType , StringMatcher > stringMatchers =
25
- new HashMap <>(PredicateType .values ().length );
23
+ private static final long MILLISECONDS_IN_ONE_SEC = 1000 ;
24
+
25
+ private static final Map <PredicateType , StringMatcher > stringMatchers = new EnumMap <>(PredicateType .class );
26
+
27
+ private static final Map <PredicateType , SegmentMatcher > segmentMatchers = new EnumMap <>(PredicateType .class );
28
+
29
+ private static final Map <PredicateType , DatetimeMatcher > datetimeMatchers = new EnumMap <>(PredicateType .class );
30
+
31
+ private static final Map <PredicateType , NumberMatcher > numberMatchers = new EnumMap <>(PredicateType .class );
26
32
27
- private static final Map <PredicateType , SegmentMatcher > segmentMatchers =
28
- new HashMap <>(PredicateType .values ().length );
33
+ private static final Map <PredicateType , SemverMatcher > semverMatchers = new EnumMap <>(PredicateType .class );
29
34
30
35
static {
36
+
31
37
stringMatchers .put (PredicateType .IS_ONE_OF , (target , objects ) ->
32
38
objects .contains (target ));
33
39
stringMatchers .put (PredicateType .ENDS_WITH , (target , objects ) ->
@@ -50,43 +56,149 @@ public final class Condition {
50
56
objects .stream ().noneMatch (s -> Pattern .compile (s ).matcher (target ).find ()));
51
57
52
58
segmentMatchers .put (PredicateType .IS_IN , (user , segments , objects ) ->
53
- objects .stream ().anyMatch (s -> segments .get (s ).contains (user , segments )));
59
+ objects .stream ().anyMatch (s -> segments .get (s ).contains (user , segments )));
54
60
segmentMatchers .put (PredicateType .IS_NOT_IN , (user , segments , objects ) ->
55
61
objects .stream ().noneMatch (s -> segments .get (s ).contains (user , segments )));
56
62
63
+ datetimeMatchers .put (PredicateType .AFTER , ((target , objects ) ->
64
+ objects .stream ().map (Long ::parseLong ).anyMatch (o -> target >= o )));
65
+ datetimeMatchers .put (PredicateType .BEFORE , ((target , objects ) ->
66
+ objects .stream ().map (Long ::parseLong ).anyMatch (o -> target < o )));
67
+
68
+ numberMatchers .put (PredicateType .EQUAL_TO , ((target , objects ) ->
69
+ objects .stream ().map (Double ::parseDouble ).anyMatch (o -> target == o )));
70
+ numberMatchers .put (PredicateType .NOT_EQUAL_TO , ((target , objects ) ->
71
+ objects .stream ().map (Double ::parseDouble ).noneMatch (o -> target == o )));
72
+ numberMatchers .put (PredicateType .GREATER_THAN , ((target , objects ) ->
73
+ objects .stream ().map (Double ::parseDouble ).anyMatch (o -> target > o )));
74
+ numberMatchers .put (PredicateType .GREATER_OR_EQUAL , ((target , objects ) ->
75
+ objects .stream ().map (Double ::parseDouble ).anyMatch (o -> target >= o )));
76
+ numberMatchers .put (PredicateType .LESS_THAN , ((target , objects ) ->
77
+ objects .stream ().map (Double ::parseDouble ).anyMatch (o -> target < o )));
78
+ numberMatchers .put (PredicateType .LESS_OR_EQUAL , ((target , objects ) ->
79
+ objects .stream ().map (Double ::parseDouble ).anyMatch (o -> target <= o )));
80
+
81
+ semverMatchers .put (PredicateType .EQUAL_TO , ((target , objects ) ->
82
+ objects .stream ().filter (Objects ::nonNull ).map (ComparableVersion ::new ).anyMatch (t -> target .compareTo (t ) == 0 )));
83
+ semverMatchers .put (PredicateType .NOT_EQUAL_TO , ((target , objects ) ->
84
+ objects .stream ().filter (Objects ::nonNull ).map (ComparableVersion ::new ).noneMatch (t -> target .compareTo (t ) == 0 )));
85
+ semverMatchers .put (PredicateType .GREATER_THAN , ((target , objects ) ->
86
+ objects .stream ().filter (Objects ::nonNull ).map (ComparableVersion ::new ).anyMatch (t -> target .compareTo (t ) > 0 )));
87
+ semverMatchers .put (PredicateType .GREATER_OR_EQUAL , ((target , objects ) ->
88
+ objects .stream ().filter (Objects ::nonNull ).map (ComparableVersion ::new ).anyMatch (t -> target .compareTo (t ) >= 0 )));
89
+ semverMatchers .put (PredicateType .LESS_THAN , ((target , objects ) ->
90
+ objects .stream ().filter (Objects ::nonNull ).map (ComparableVersion ::new ).anyMatch (t -> target .compareTo (t ) < 0 )));
91
+ semverMatchers .put (PredicateType .LESS_OR_EQUAL , ((target , objects ) ->
92
+ objects .stream ().filter (Objects ::nonNull ).map (ComparableVersion ::new ).anyMatch (t -> target .compareTo (t ) <= 0 )));
93
+
57
94
}
58
95
59
96
public boolean matchObjects (FPUser user , Map <String , Segment > segments ) {
60
97
switch (type ) {
61
98
case STRING :
62
- String subjectValue = user .getAttrs ().get (subject );
63
- if (StringUtils .isBlank (subjectValue )) {
64
- return false ;
65
- }
66
- return matchStringCondition (subjectValue );
99
+ return matchStringCondition (user );
100
+
67
101
case SEGMENT :
68
102
return matchSegmentCondition (user , segments );
69
- case DATE :
70
- // TODO
103
+
104
+ case DATETIME :
105
+ return matchDatetimeCondition (user );
106
+
107
+ case NUMBER :
108
+ return matchNumberCondition (user );
109
+
110
+ case SEMVER :
111
+ return matchSemverCondition (user );
112
+
71
113
default :
72
114
return false ;
73
115
}
74
116
}
75
117
76
- private boolean matchStringCondition (String subjectValue ) {
118
+ private boolean matchStringCondition (FPUser user ) {
119
+ String subjectValue = user .getAttr (subject );
120
+ if (StringUtils .isBlank (subjectValue )) {
121
+ return false ;
122
+ }
123
+
77
124
StringMatcher stringMatcher = stringMatchers .get (this .predicate );
78
- if (Objects .nonNull (stringMatcher )) {
79
- return stringMatcher . match ( subjectValue , this . objects ) ;
125
+ if (Objects .isNull (stringMatcher )) {
126
+ return false ;
80
127
}
81
- return false ;
128
+
129
+ return stringMatcher .match (subjectValue , this .objects );
82
130
}
83
131
84
132
private boolean matchSegmentCondition (FPUser user , Map <String , Segment > segments ) {
85
133
SegmentMatcher segmentMatcher = segmentMatchers .get (this .predicate );
86
- if (Objects .nonNull (segmentMatcher )) {
87
- return segmentMatcher .match (user , segments , this .objects );
134
+ if (Objects .isNull (segmentMatcher )) {
135
+ return false ;
136
+ }
137
+
138
+ return segmentMatcher .match (user , segments , this .objects );
139
+ }
140
+
141
+ private boolean matchDatetimeCondition (FPUser user ) {
142
+ DatetimeMatcher datetimeMatcher = datetimeMatchers .get (this .predicate );
143
+ if (Objects .isNull (datetimeMatcher )) {
144
+ return false ;
145
+ }
146
+
147
+ String customValue = user .getAttr (this .subject );
148
+ long cv ;
149
+ try {
150
+ cv = StringUtils .isBlank (customValue )
151
+ ? System .currentTimeMillis () / MILLISECONDS_IN_ONE_SEC
152
+ : Long .parseLong (customValue );
153
+ } catch (NumberFormatException e ) {
154
+ logger .error ("User attribute type mismatch. attribute value: {}, target type long" , customValue );
155
+ return false ;
156
+ }
157
+ try {
158
+ return datetimeMatcher .match (cv , objects );
159
+ } catch (NumberFormatException e ) {
160
+ logger .error ("Met a string that cannot be parsed to long in Condition.objects: {}" , e .getMessage ());
161
+ return false ;
162
+ }
163
+ }
164
+
165
+ private boolean matchNumberCondition (FPUser user ) {
166
+ NumberMatcher numberMatcher = numberMatchers .get (this .predicate );
167
+ if (Objects .isNull (numberMatcher )) {
168
+ return false ;
169
+ }
170
+
171
+ String customValue = user .getAttr (this .subject );
172
+ if (StringUtils .isBlank (customValue )) {
173
+ return false ;
174
+ }
175
+ double cv ;
176
+ try {
177
+ cv = Double .parseDouble (customValue );
178
+ } catch (NumberFormatException e ) {
179
+ logger .error ("User attribute type mismatch. attribute value : {}, target type double" , customValue );
180
+ return false ;
181
+ }
182
+ try {
183
+ return numberMatcher .match (cv , this .objects );
184
+ } catch (NumberFormatException e ) {
185
+ logger .error ("Met a string that cannot be parsed to double in Condition.objects: {}" , e .getMessage ());
186
+ return false ;
187
+ }
188
+ }
189
+
190
+ private boolean matchSemverCondition (FPUser user ) {
191
+ SemverMatcher semverMatcher = semverMatchers .get (this .predicate );
192
+ if (Objects .isNull (semverMatcher )) {
193
+ return false ;
194
+ }
195
+
196
+ String customValue = user .getAttr (this .subject );
197
+ if (StringUtils .isBlank (customValue )) {
198
+ return false ;
88
199
}
89
- return false ;
200
+ ComparableVersion cv = new ComparableVersion (customValue );
201
+ return semverMatcher .match (cv , this .objects );
90
202
}
91
203
92
204
public ConditionType getType () {
0 commit comments