optimizely-sdk 2.0.0.beta → 2.0.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/optimizely.rb +166 -73
- data/lib/optimizely/audience.rb +6 -3
- data/lib/optimizely/bucketer.rb +21 -16
- data/lib/optimizely/condition.rb +4 -2
- data/lib/optimizely/decision_service.rb +141 -141
- data/lib/optimizely/error_handler.rb +5 -5
- data/lib/optimizely/event_builder.rb +158 -147
- data/lib/optimizely/event_dispatcher.rb +7 -6
- data/lib/optimizely/exceptions.rb +12 -1
- data/lib/optimizely/helpers/constants.rb +64 -63
- data/lib/optimizely/helpers/event_tag_utils.rb +86 -11
- data/lib/optimizely/helpers/group.rb +3 -1
- data/lib/optimizely/helpers/validator.rb +9 -1
- data/lib/optimizely/helpers/variable_type.rb +11 -7
- data/lib/optimizely/logger.rb +5 -5
- data/lib/optimizely/notification_center.rb +150 -0
- data/lib/optimizely/params.rb +3 -1
- data/lib/optimizely/project_config.rb +128 -24
- data/lib/optimizely/user_profile_service.rb +2 -0
- data/lib/optimizely/version.rb +4 -1
- metadata +15 -16
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
#
|
2
4
|
# Copyright 2016-2017, Optimizely and contributors
|
3
5
|
#
|
@@ -16,7 +18,6 @@
|
|
16
18
|
module Optimizely
|
17
19
|
module Helpers
|
18
20
|
module Constants
|
19
|
-
|
20
21
|
JSON_SCHEMA_V2 = {
|
21
22
|
'type' => 'object',
|
22
23
|
'properties' => {
|
@@ -49,9 +50,9 @@ module Optimizely
|
|
49
50
|
'type' => 'integer'
|
50
51
|
}
|
51
52
|
},
|
52
|
-
'required' => [
|
53
|
-
|
54
|
-
|
53
|
+
'required' => %w[
|
54
|
+
entityId
|
55
|
+
endOfRange
|
55
56
|
]
|
56
57
|
}
|
57
58
|
},
|
@@ -84,9 +85,9 @@ module Optimizely
|
|
84
85
|
'type' => 'string'
|
85
86
|
}
|
86
87
|
},
|
87
|
-
'required' => [
|
88
|
-
|
89
|
-
|
88
|
+
'required' => %w[
|
89
|
+
id
|
90
|
+
key
|
90
91
|
]
|
91
92
|
}
|
92
93
|
},
|
@@ -102,9 +103,9 @@ module Optimizely
|
|
102
103
|
'type' => 'integer'
|
103
104
|
}
|
104
105
|
},
|
105
|
-
'required' => [
|
106
|
-
|
107
|
-
|
106
|
+
'required' => %w[
|
107
|
+
entityId
|
108
|
+
endOfRange
|
108
109
|
]
|
109
110
|
}
|
110
111
|
},
|
@@ -118,24 +119,24 @@ module Optimizely
|
|
118
119
|
'type' => 'object'
|
119
120
|
}
|
120
121
|
},
|
121
|
-
'required' => [
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
122
|
+
'required' => %w[
|
123
|
+
id
|
124
|
+
layerId
|
125
|
+
key
|
126
|
+
status
|
127
|
+
variations
|
128
|
+
trafficAllocation
|
129
|
+
audienceIds
|
130
|
+
forcedVariations
|
130
131
|
]
|
131
132
|
}
|
132
133
|
}
|
133
134
|
},
|
134
|
-
'required' => [
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
135
|
+
'required' => %w[
|
136
|
+
id
|
137
|
+
policy
|
138
|
+
trafficAllocation
|
139
|
+
experiments
|
139
140
|
]
|
140
141
|
}
|
141
142
|
},
|
@@ -168,9 +169,9 @@ module Optimizely
|
|
168
169
|
'type' => 'string'
|
169
170
|
}
|
170
171
|
},
|
171
|
-
'required' => [
|
172
|
-
|
173
|
-
|
172
|
+
'required' => %w[
|
173
|
+
id
|
174
|
+
key
|
174
175
|
]
|
175
176
|
}
|
176
177
|
},
|
@@ -186,9 +187,9 @@ module Optimizely
|
|
186
187
|
'type' => 'integer'
|
187
188
|
}
|
188
189
|
},
|
189
|
-
'required' => [
|
190
|
-
|
191
|
-
|
190
|
+
'required' => %w[
|
191
|
+
entityId
|
192
|
+
endOfRange
|
192
193
|
]
|
193
194
|
}
|
194
195
|
},
|
@@ -202,15 +203,15 @@ module Optimizely
|
|
202
203
|
'type' => 'object'
|
203
204
|
}
|
204
205
|
},
|
205
|
-
'required' => [
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
206
|
+
'required' => %w[
|
207
|
+
id
|
208
|
+
key
|
209
|
+
variations
|
210
|
+
trafficAllocation
|
211
|
+
audienceIds
|
212
|
+
forcedVariations
|
213
|
+
status
|
214
|
+
layerId
|
214
215
|
]
|
215
216
|
}
|
216
217
|
},
|
@@ -232,10 +233,10 @@ module Optimizely
|
|
232
233
|
'type' => 'string'
|
233
234
|
}
|
234
235
|
},
|
235
|
-
'required' => [
|
236
|
-
|
237
|
-
|
238
|
-
|
236
|
+
'required' => %w[
|
237
|
+
key
|
238
|
+
experimentIds
|
239
|
+
id
|
239
240
|
]
|
240
241
|
}
|
241
242
|
},
|
@@ -254,10 +255,10 @@ module Optimizely
|
|
254
255
|
'type' => 'string'
|
255
256
|
}
|
256
257
|
},
|
257
|
-
'required' => [
|
258
|
-
|
259
|
-
|
260
|
-
|
258
|
+
'required' => %w[
|
259
|
+
id
|
260
|
+
name
|
261
|
+
conditions
|
261
262
|
]
|
262
263
|
}
|
263
264
|
},
|
@@ -271,11 +272,11 @@ module Optimizely
|
|
271
272
|
},
|
272
273
|
'key' => {
|
273
274
|
'type' => 'string'
|
274
|
-
}
|
275
|
+
}
|
275
276
|
},
|
276
|
-
'required' => [
|
277
|
-
|
278
|
-
|
277
|
+
'required' => %w[
|
278
|
+
id
|
279
|
+
key
|
279
280
|
]
|
280
281
|
}
|
281
282
|
},
|
@@ -286,25 +287,25 @@ module Optimizely
|
|
286
287
|
'type' => 'string'
|
287
288
|
}
|
288
289
|
},
|
289
|
-
'required' => [
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
290
|
+
'required' => %w[
|
291
|
+
projectId
|
292
|
+
accountId
|
293
|
+
experiments
|
294
|
+
events
|
295
|
+
groups
|
296
|
+
audiences
|
297
|
+
attributes
|
298
|
+
version
|
299
|
+
revision
|
299
300
|
]
|
300
|
-
}
|
301
|
+
}.freeze
|
301
302
|
|
302
303
|
VARIABLE_TYPES = {
|
303
304
|
'BOOLEAN' => 'boolean',
|
304
305
|
'DOUBLE' => 'double',
|
305
306
|
'INTEGER' => 'integer',
|
306
307
|
'STRING' => 'string'
|
307
|
-
}
|
308
|
+
}.freeze
|
308
309
|
end
|
309
310
|
end
|
310
311
|
end
|
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
#
|
2
|
-
# Copyright 2017, Optimizely and contributors
|
4
|
+
# Copyright 2017-2018, Optimizely and contributors
|
3
5
|
#
|
4
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
7
|
# you may not use this file except in compliance with the License.
|
@@ -21,35 +23,108 @@ module Optimizely
|
|
21
23
|
module EventTagUtils
|
22
24
|
module_function
|
23
25
|
|
24
|
-
|
26
|
+
REVENUE_EVENT_METRIC_NAME = 'revenue'
|
27
|
+
NUMERIC_EVENT_METRIC_NAME = 'value'
|
28
|
+
|
29
|
+
def get_revenue_value(event_tags, logger)
|
25
30
|
# Grab the revenue value from the event tags. "revenue" is a reserved keyword.
|
26
|
-
#
|
31
|
+
# The value will be parsed to an integer if possible.
|
32
|
+
# Example:
|
33
|
+
# 4.0 or "4.0" will be parsed to int(4).
|
34
|
+
# 4.1 will not be parsed and the method will return nil.
|
27
35
|
# event_tags - Hash representing metadata associated with the event.
|
36
|
+
# logger - Optional component which provides a log method to log messages.
|
37
|
+
#
|
28
38
|
# Returns revenue value as an integer number
|
29
39
|
# Returns nil if revenue can't be retrieved from the event tags.
|
30
40
|
|
31
|
-
if event_tags.nil?
|
41
|
+
if event_tags.nil?
|
42
|
+
logger.log(Logger::DEBUG, 'Event tags is undefined.')
|
43
|
+
return nil
|
44
|
+
end
|
45
|
+
|
46
|
+
unless Helpers::Validator.event_tags_valid?(event_tags)
|
47
|
+
logger.log(Logger::DEBUG, 'Event tags is not a hash.')
|
32
48
|
return nil
|
33
49
|
end
|
34
50
|
|
35
|
-
unless event_tags.
|
51
|
+
unless event_tags.key?(REVENUE_EVENT_METRIC_NAME)
|
52
|
+
logger.log(Logger::DEBUG, 'The revenue key is not defined in the event tags.')
|
36
53
|
return nil
|
37
54
|
end
|
38
55
|
|
39
|
-
|
40
|
-
|
56
|
+
if event_tags[REVENUE_EVENT_METRIC_NAME].nil?
|
57
|
+
logger.log(Logger::DEBUG, 'The revenue key is nil.')
|
58
|
+
return nil
|
59
|
+
end
|
41
60
|
|
42
|
-
|
43
|
-
|
61
|
+
raw_value = event_tags[REVENUE_EVENT_METRIC_NAME]
|
62
|
+
|
63
|
+
unless Helpers::Validator.string_numeric?(raw_value)
|
64
|
+
logger.log(Logger::WARN, 'Revenue value is not an integer or float, or is not a numeric string.')
|
44
65
|
return nil
|
45
66
|
end
|
46
67
|
|
47
|
-
if raw_value.is_a?
|
68
|
+
raw_value = raw_value.to_f if raw_value.is_a? String
|
69
|
+
|
70
|
+
unless raw_value == raw_value.to_i
|
48
71
|
logger.log(Logger::WARN, "Failed to parse revenue value #{raw_value} from event tags.")
|
49
72
|
return nil
|
50
73
|
end
|
51
74
|
|
52
|
-
logger.log(Logger::INFO, "Parsed revenue value #{raw_value} from event tags.")
|
75
|
+
logger.log(Logger::INFO, "Parsed revenue value #{raw_value.to_i} from event tags.")
|
76
|
+
raw_value.to_i
|
77
|
+
end
|
78
|
+
|
79
|
+
def get_numeric_value(event_tags, logger)
|
80
|
+
# Grab the numeric event value from the event tags. "value" is a reserved keyword.
|
81
|
+
# The value of 'value' can be a float or a numeric string
|
82
|
+
#
|
83
|
+
# event_tags - +Hash+ representing metadata associated with the event.
|
84
|
+
# logger - Optional component which provides a log method to log messages.
|
85
|
+
# Returns +Number+ | +nil+ if value can't be retrieved from the event tags.
|
86
|
+
|
87
|
+
if event_tags.nil?
|
88
|
+
logger.log(Logger::DEBUG, 'Event tags is undefined.')
|
89
|
+
return nil
|
90
|
+
end
|
91
|
+
|
92
|
+
unless Helpers::Validator.event_tags_valid?(event_tags)
|
93
|
+
logger.log(Logger::DEBUG, 'Event tags is not a dictionary.')
|
94
|
+
return nil
|
95
|
+
end
|
96
|
+
|
97
|
+
unless event_tags.key?(NUMERIC_EVENT_METRIC_NAME)
|
98
|
+
logger.log(Logger::DEBUG, 'The numeric metric key is not defined in the event tags.')
|
99
|
+
return nil
|
100
|
+
end
|
101
|
+
|
102
|
+
if event_tags[NUMERIC_EVENT_METRIC_NAME].nil?
|
103
|
+
logger.log(Logger::DEBUG, 'The numeric metric key is null.')
|
104
|
+
return nil
|
105
|
+
end
|
106
|
+
|
107
|
+
raw_value = event_tags[NUMERIC_EVENT_METRIC_NAME]
|
108
|
+
|
109
|
+
if raw_value.is_a?(TrueClass) || raw_value.is_a?(FalseClass)
|
110
|
+
logger.log(Logger::DEBUG, 'Provided numeric value is a boolean, which is an invalid format.')
|
111
|
+
return nil
|
112
|
+
end
|
113
|
+
|
114
|
+
if raw_value.is_a?(Array) || raw_value.is_a?(Hash) || raw_value.to_f.nan? || raw_value.to_f.infinite?
|
115
|
+
logger.log(Logger::DEBUG, 'Provided numeric value is in an invalid format.')
|
116
|
+
return nil
|
117
|
+
end
|
118
|
+
|
119
|
+
unless Helpers::Validator.string_numeric?(raw_value)
|
120
|
+
logger.log(Logger::DEBUG, 'Provided numeric value is not a numeric string.')
|
121
|
+
return nil
|
122
|
+
end
|
123
|
+
|
124
|
+
raw_value = raw_value.to_f
|
125
|
+
|
126
|
+
logger.log(Logger::INFO, "The numeric metric value #{raw_value} will be sent to results.")
|
127
|
+
|
53
128
|
raw_value
|
54
129
|
end
|
55
130
|
end
|
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
#
|
2
|
-
# Copyright 2016, Optimizely and contributors
|
4
|
+
# Copyright 2016-2017, Optimizely and contributors
|
3
5
|
#
|
4
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
7
|
# you may not use this file except in compliance with the License.
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
#
|
2
4
|
# Copyright 2016-2017, Optimizely and contributors
|
3
5
|
#
|
@@ -50,7 +52,7 @@ module Optimizely
|
|
50
52
|
# Returns boolean depending on validity of datafile.
|
51
53
|
|
52
54
|
begin
|
53
|
-
datafile = JSON.
|
55
|
+
datafile = JSON.parse(datafile)
|
54
56
|
rescue
|
55
57
|
return false
|
56
58
|
end
|
@@ -87,6 +89,12 @@ module Optimizely
|
|
87
89
|
|
88
90
|
logger.respond_to?(:log)
|
89
91
|
end
|
92
|
+
|
93
|
+
def string_numeric?(str)
|
94
|
+
!Float(str).nil?
|
95
|
+
rescue
|
96
|
+
false
|
97
|
+
end
|
90
98
|
end
|
91
99
|
end
|
92
100
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
#
|
2
4
|
# Copyright 2017, Optimizely and contributors
|
3
5
|
#
|
@@ -30,26 +32,28 @@ module Optimizely
|
|
30
32
|
return_value = nil
|
31
33
|
|
32
34
|
case variable_type
|
33
|
-
when
|
34
|
-
return_value = value ==
|
35
|
-
when
|
35
|
+
when 'boolean'
|
36
|
+
return_value = value == 'true'
|
37
|
+
when 'double'
|
36
38
|
begin
|
37
39
|
return_value = Float(value)
|
38
40
|
rescue => e
|
39
|
-
logger.log(Logger::ERROR, "Unable to cast variable value '#{value}' to type
|
41
|
+
logger.log(Logger::ERROR, "Unable to cast variable value '#{value}' to type "\
|
42
|
+
"'#{variable_type}': #{e.message}.")
|
40
43
|
end
|
41
|
-
when
|
44
|
+
when 'integer'
|
42
45
|
begin
|
43
46
|
return_value = Integer(value)
|
44
47
|
rescue => e
|
45
|
-
logger.log(Logger::ERROR, "Unable to cast variable value '#{value}' to type
|
48
|
+
logger.log(Logger::ERROR, "Unable to cast variable value '#{value}' to type "\
|
49
|
+
"'#{variable_type}': #{e.message}.")
|
46
50
|
end
|
47
51
|
else
|
48
52
|
# default case is string
|
49
53
|
return_value = value
|
50
54
|
end
|
51
55
|
|
52
|
-
|
56
|
+
return_value
|
53
57
|
end
|
54
58
|
end
|
55
59
|
end
|
data/lib/optimizely/logger.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
#
|
2
|
-
# Copyright 2016, Optimizely and contributors
|
4
|
+
# Copyright 2016-2017, Optimizely and contributors
|
3
5
|
#
|
4
6
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
7
|
# you may not use this file except in compliance with the License.
|
@@ -19,15 +21,13 @@ module Optimizely
|
|
19
21
|
class BaseLogger
|
20
22
|
# Class encapsulating logging functionality. Override with your own logger providing log method.
|
21
23
|
|
22
|
-
def log(_level, _message)
|
23
|
-
end
|
24
|
+
def log(_level, _message); end
|
24
25
|
end
|
25
26
|
|
26
27
|
class NoOpLogger < BaseLogger
|
27
28
|
# Class providing log method which logs nothing.
|
28
29
|
|
29
|
-
def log(_level, _message)
|
30
|
-
end
|
30
|
+
def log(_level, _message); end
|
31
31
|
end
|
32
32
|
|
33
33
|
class SimpleLogger < BaseLogger
|