99import io .split .engine .evaluator .Evaluator ;
1010import io .split .engine .SDKReadinessGates ;
1111import io .split .engine .evaluator .EvaluatorImp ;
12+ import io .split .engine .evaluator .Labels ;
13+
1214import io .split .engine .metrics .Metrics ;
1315import io .split .grammar .Treatments ;
16+ import io .split .inputValidation .EventsValidator ;
17+ import io .split .inputValidation .KeyValidator ;
18+ import io .split .inputValidation .SplitNameValidator ;
19+ import io .split .inputValidation .TrafficTypeValidator ;
1420import org .slf4j .Logger ;
1521import org .slf4j .LoggerFactory ;
1622
1723import java .util .Collections ;
1824import java .util .HashMap ;
1925import java .util .Map ;
26+ import java .util .Optional ;
2027import java .util .concurrent .TimeoutException ;
21- import java .util .regex .Pattern ;
2228
2329import static com .google .common .base .Preconditions .checkNotNull ;
2430
2834 * @author adil
2935 */
3036public final class SplitClientImpl implements SplitClient {
31- public static final Pattern EVENT_TYPE_MATCHER = Pattern .compile ("^[a-zA-Z0-9][-_.:a-zA-Z0-9]{0,79}$" );
3237 public static final SplitResult SPLIT_RESULT_CONTROL = new SplitResult (Treatments .CONTROL , null );
3338
34- private static final String GET_TREATMENT_LABEL = "sdk. getTreatment" ;
35- private static final String DEFINITION_NOT_FOUND = "definition not found " ;
39+ private static final String GET_TREATMENT = "getTreatment" ;
40+ private static final String GET_TREATMENT_WITH_CONFIG = "getTreatmentWithConfig " ;
3641
3742 private static final Logger _log = LoggerFactory .getLogger (SplitClientImpl .class );
3843
@@ -70,59 +75,27 @@ public String getTreatment(String key, String split) {
7075
7176 @ Override
7277 public String getTreatment (String key , String split , Map <String , Object > attributes ) {
73- return getTreatmentWithConfigInternal (GET_TREATMENT_LABEL , key , null , split , attributes ).treatment ();
78+ return getTreatmentWithConfigInternal (GET_TREATMENT , key , null , split , attributes ).treatment ();
7479 }
7580
7681 @ Override
7782 public String getTreatment (Key key , String split , Map <String , Object > attributes ) {
78- if (key == null ) {
79- _log .error ("getTreatment: you passed a null key, the key must be a non-empty string" );
80- return Treatments .CONTROL ;
81- }
82-
83- if (key .matchingKey () == null ) {
84- _log .error ("getTreatment: you passed a null matchingKey, the matchingKey must be a non-empty string" );
85- return Treatments .CONTROL ;
86- }
87-
88-
89- if (key .bucketingKey () == null ) {
90- _log .error ("getTreatment: you passed a null bucketingKey, the bucketingKey must be a non-empty string" );
91- return Treatments .CONTROL ;
92- }
93-
94- return getTreatmentWithConfigInternal (GET_TREATMENT_LABEL , key .matchingKey (), key .bucketingKey (), split , attributes ).treatment ();
83+ return getTreatmentWithConfigInternal (GET_TREATMENT , key .matchingKey (), key .bucketingKey (), split , attributes ).treatment ();
9584 }
9685
9786 @ Override
9887 public SplitResult getTreatmentWithConfig (String key , String split ) {
99- return getTreatmentWithConfigInternal (GET_TREATMENT_LABEL , key , null , split , Collections .<String , Object >emptyMap ());
88+ return getTreatmentWithConfigInternal (GET_TREATMENT_WITH_CONFIG , key , null , split , Collections .<String , Object >emptyMap ());
10089 }
10190
10291 @ Override
10392 public SplitResult getTreatmentWithConfig (String key , String split , Map <String , Object > attributes ) {
104- return getTreatmentWithConfigInternal (GET_TREATMENT_LABEL , key , null , split , attributes );
93+ return getTreatmentWithConfigInternal (GET_TREATMENT_WITH_CONFIG , key , null , split , attributes );
10594 }
10695
10796 @ Override
10897 public SplitResult getTreatmentWithConfig (Key key , String split , Map <String , Object > attributes ) {
109- if (key == null ) {
110- _log .error ("getTreatment: you passed a null key, the key must be a non-empty string" );
111- return SPLIT_RESULT_CONTROL ;
112- }
113-
114- if (key .matchingKey () == null ) {
115- _log .error ("getTreatment: you passed a null matchingKey, the matchingKey must be a non-empty string" );
116- return SPLIT_RESULT_CONTROL ;
117- }
118-
119-
120- if (key .bucketingKey () == null ) {
121- _log .error ("getTreatment: you passed a null bucketingKey, the bucketingKey must be a non-empty string" );
122- return SPLIT_RESULT_CONTROL ;
123- }
124-
125- return getTreatmentWithConfigInternal (GET_TREATMENT_LABEL , key .matchingKey (), key .bucketingKey (), split , attributes );
98+ return getTreatmentWithConfigInternal (GET_TREATMENT_WITH_CONFIG , key .matchingKey (), key .bucketingKey (), split , attributes );
12699 }
127100
128101 @ Override
@@ -178,144 +151,59 @@ private boolean track(Event event) {
178151 }
179152
180153 // Traffic Type validations
181- if (event .trafficTypeName == null ) {
182- _log .error ("track: you passed a null trafficTypeName, trafficTypeName must be a non-empty string" );
183- return false ;
184- }
185-
186- if (event .trafficTypeName .isEmpty ()) {
187- _log .error ("track: you passed an empty trafficTypeName, trafficTypeName must be a non-empty string" );
154+ Optional <String > trafficTypeResult = TrafficTypeValidator .isValid (event .trafficTypeName , _splitCache , "track" );
155+ if (!trafficTypeResult .isPresent ()) {
188156 return false ;
189157 }
190-
191- if (!event .trafficTypeName .equals (event .trafficTypeName .toLowerCase ())) {
192- _log .warn ("track: trafficTypeName should be all lowercase - converting string to lowercase" );
193- event .trafficTypeName = event .trafficTypeName .toLowerCase ();
194- }
195-
196- if (!_splitCache .trafficTypeExists (event .trafficTypeName )) {
197- _log .warn ("track: Traffic Type " + event .trafficTypeName + " does not have any corresponding Splits in this environment, " +
198- "make sure you’re tracking your events to a valid traffic type defined in the Split console." );
199- }
158+ event .trafficTypeName = trafficTypeResult .get ();
200159
201160 // EventType validations
202- if (event .eventTypeId == null ) {
203- _log .error ("track: you passed a null eventTypeId, eventTypeId must be a non-empty string" );
204- return false ;
205- }
206-
207- if (event .eventTypeId .isEmpty ()) {
208- _log .error ("track:you passed an empty eventTypeId, eventTypeId must be a non-empty string" );
209- return false ;
210- }
211-
212- if (!EVENT_TYPE_MATCHER .matcher (event .eventTypeId ).find ()) {
213- _log .error ("track: you passed " + event .eventTypeId + ", eventTypeId must adhere to the regular expression " +
214- "[a-zA-Z0-9][-_.:a-zA-Z0-9]{0,79}. This means an eventTypeID must be alphanumeric, " +
215- "cannot be more than 80 characters long, and can only include a dash, underscore, period, " +
216- "or colon as separators of alphanumeric characters" );
161+ if (!EventsValidator .typeIsValid (event .eventTypeId , "track" )) {
217162 return false ;
218163 }
219164
220165 // Key Validations
221- if (event .key == null ) {
222- _log .error ("track: you passed a null key, key must be a non-empty string" );
166+ if (!KeyValidator .isValid (event .key , "key" , _config .maxStringLength (), "track" )) {
223167 return false ;
224168 }
225169
226- if (event .key .isEmpty ()) {
227- _log .error ("track: you passed an empty key, key must be a non-empty string" );
170+ // Properties validations
171+ EventsValidator .EventValidatorResult propertiesResult = EventsValidator .propertiesAreValid (event .properties );
172+ if (!propertiesResult .getSuccess ()) {
228173 return false ;
229174 }
230175
231- if (event .key .length () > _config .maxStringLength ()) {
232- _log .error ("track: key too long - must be " + _config .maxStringLength () + "characters or less" );
233- return false ;
234- }
235-
236- int size = 1024 ; // We assume 1kb events without properties (750 bytes avg measured)
237- if (null != event .properties ) {
238- if (event .properties .size () > 300 ) {
239- _log .warn ("Event has more than 300 properties. Some of them will be trimmed when processed" );
240- }
241-
242- for (Map .Entry <String , Object > entry : event .properties .entrySet ()) {
243- size += entry .getKey ().length ();
244- Object value = entry .getValue ();
245- if (null == value ) {
246- continue ;
247- }
248-
249- if (!(value instanceof Number ) && !(value instanceof Boolean ) && !(value instanceof String )) {
250- _log .warn (String .format ("Property %s is of invalid type. Setting value to null" , entry .getKey ()));
251- entry .setValue (null );
252- }
253-
254- if (value instanceof String ) {
255- size += ((String ) value ).length ();
256- }
257-
258- if (size > Event .MAX_PROPERTIES_LENGTH_BYTES ) {
259- _log .error (String .format ("The maximum size allowed for the properties is 32768 bytes. "
260- + "Current one is %s bytes. Event not queued" , size ));
261- return false ;
262- }
263- }
264-
265- }
176+ event .properties = propertiesResult .getValue ();
266177
267- return _eventClient .track (event , size );
178+ return _eventClient .track (event , propertiesResult . getEventSize () );
268179 }
269180
270- private SplitResult getTreatmentWithConfigInternal (String label , String matchingKey , String bucketingKey , String split , Map <String , Object > attributes ) {
181+ private SplitResult getTreatmentWithConfigInternal (String method , String matchingKey , String bucketingKey , String split , Map <String , Object > attributes ) {
271182 try {
272183 if (_container .isDestroyed ()) {
273184 _log .error ("Client has already been destroyed - no calls possible" );
274185 return SPLIT_RESULT_CONTROL ;
275186 }
276187
277- if (matchingKey == null ) {
278- _log .error ("getTreatmentWithConfig: you passed a null matchingKey, the matchingKey must be a non-empty string" );
279- return SPLIT_RESULT_CONTROL ;
280- }
281- if (matchingKey .length () > _config .maxStringLength ()) {
282- _log .error ("getTreatmentWithConfig: matchingKey too long - must be " + _config .maxStringLength () + " characters or less" );
283- return SPLIT_RESULT_CONTROL ;
284- }
285- if (matchingKey .isEmpty ()) {
286- _log .error ("getTreatmentWithConfig: you passed an empty string, matchingKey must be a non-empty string" );
287- return SPLIT_RESULT_CONTROL ;
288- }
289- if (bucketingKey != null && bucketingKey .isEmpty ()) {
290- _log .error ("getTreatmentWithConfig: you passed an empty string, bucketingKey must be a non-empty string" );
291- return SPLIT_RESULT_CONTROL ;
292- }
293- if (bucketingKey != null && bucketingKey .length () > _config .maxStringLength ()) {
294- _log .error ("getTreatmentWithConfig: bucketingKey too long - must be " + _config .maxStringLength () + " characters or less" );
188+ if (!KeyValidator .isValid (matchingKey , "matchingKey" , _config .maxStringLength (), method )) {
295189 return SPLIT_RESULT_CONTROL ;
296190 }
297191
298- if (split == null ) {
299- _log .error ("getTreatmentWithConfig: you passed a null split name, split name must be a non-empty string" );
192+ if (!KeyValidator .bucketingKeyIsValid (bucketingKey , _config .maxStringLength (), method )) {
300193 return SPLIT_RESULT_CONTROL ;
301194 }
302195
303- if ( split . isEmpty ()) {
304- _log . error ( "getTreatmentWithConfig: you passed an empty split name, split name must be a non-empty string" );
196+ Optional < String > splitNameResult = SplitNameValidator . isValid ( split , method );
197+ if (! splitNameResult . isPresent ()) {
305198 return SPLIT_RESULT_CONTROL ;
306199 }
307-
308- String trimmed = split .trim ();
309- if (!trimmed .equals (split )) {
310- _log .warn ("getTreatmentWithConfig: split name \" " + split + "\" has extra whitespace, trimming" );
311- split = trimmed ;
312- }
200+ split = splitNameResult .get ();
313201
314202 long start = System .currentTimeMillis ();
315203
316204 EvaluatorImp .TreatmentLabelAndChangeNumber result = _evaluator .evaluateFeature (matchingKey , bucketingKey , split , attributes );
317205
318- if (result .treatment .equals (Treatments .CONTROL ) && result .label .equals (DEFINITION_NOT_FOUND ) && _gates .isSDKReadyNow ()) {
206+ if (result .treatment .equals (Treatments .CONTROL ) && result .label .equals (Labels . DEFINITION_NOT_FOUND ) && _gates .isSDKReadyNow ()) {
319207 _log .warn (
320208 "getTreatment: you passed \" " + split + "\" that does not exist in this environment, " +
321209 "please double check what Splits exist in the web console." );
@@ -327,7 +215,7 @@ private SplitResult getTreatmentWithConfigInternal(String label, String matching
327215 split ,
328216 start ,
329217 result .treatment ,
330- label ,
218+ String . format ( "sdk.%s" , method ) ,
331219 _config .labelsEnabled () ? result .label : null ,
332220 result .changeNumber ,
333221 attributes
0 commit comments