kameleoon-client-ruby 1.0.7 → 1.0.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1a893238d33ee61086e79cb093d442dfefb16fac9e757ea7353d60458869cd13
4
- data.tar.gz: 8485bb0f875a2fa286a03fe872b88dea170477200fc1429d84b5d28711463885
3
+ metadata.gz: b70d1fc8cd47248dd68ba217e9a860577008e008cac7e6f562ab1856b3dc24c3
4
+ data.tar.gz: 03ddd4ce4f1ea480bc5adfc347c9f2168af952cc9036ce5cff494af9699a4dfe
5
5
  SHA512:
6
- metadata.gz: 0ff7b4461c7c4bbf8a7c5399450db7920bae12e65ed9eb604718f9e51fd33a79f24c4803e144ba7260946b1f9c50e2832ff55459c190d33eb2a65658b5c0575e
7
- data.tar.gz: f8e17afae3086d5b0402a30b7121febed07b16eb58a05e3082d73100c4e28d778746c353bce2bd50620b0dc267e8eb427b89e30dd066f9f951519cc5c67b77a4
6
+ metadata.gz: d3d39ad2e27a0312ff090f58a93cba6aa69450fd7763fbdc1375bd146bf1af909dde84815073fc7cf7d60b8aa123cb4cc139e8dee19c30aba3a88ae021475633
7
+ data.tar.gz: bfb92b7c42f02fde8d90484efef51abe294eaece630e97e7d0168e7974dd89d06b9069e9226c23bc2cf25755a2c1d4e41c87d114a1093b6e391a04d440f60de7
data/README.md CHANGED
@@ -1,22 +1,7 @@
1
- # Kameleoon RUBY SDK
1
+ # Kameleoon Ruby SDK
2
2
 
3
- This is the repository for the Kameleoon Ruby SDK.
3
+ ## Getting Started
4
4
 
5
- ### How to build and install Kameleoon Gem locally
6
- #### Prerequisite:
7
- * [Install ruby](https://www.ruby-lang.org/en/documentation/installation)
5
+ Our SDK gives you the possibility of running experiments and activating feature flags on your Ruby platform. Integrating our SDK into your applications is easy, and its footprint (in terms of memory and network usage) is low.
8
6
 
9
- #### Build and install the gem:
10
- * Run `./buildAndInstallGem.sh`
11
-
12
- ### How to run tests
13
- #### Prerequisite:
14
- * Build and install Kameleoon Gem locally (infos above).
15
- * Install rake: `gem install rake`
16
-
17
- #### Run Tests:
18
- ##### Unit
19
- * Run `./test.sh -u`
20
- ##### Integration
21
- Make sure you kill manually the server test app after the tests are done.
22
- * Run `./test.sh -i`
7
+ You can refer to the [SDK reference](https://developers.kameleoon.com/ruby-sdk.html#reference) to check out all possible features of the SDK. Also make sure you check out our [Getting started tutorial](https://developers.kameleoon.com/ruby-sdk.html#getting-started) which we have prepared to walk you through the installation and implementation.
@@ -1,12 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'kameleoon/targeting/models'
2
4
  require 'kameleoon/request'
3
5
  require 'kameleoon/exceptions'
4
6
  require 'kameleoon/cookie'
7
+ require 'kameleoon/query_graphql'
5
8
  require 'rufus/scheduler'
6
9
  require 'yaml'
7
10
  require 'json'
8
11
  require 'em-synchrony'
9
12
  require 'objspace'
13
+ require 'time'
10
14
 
11
15
  module Kameleoon
12
16
  ##
@@ -30,6 +34,7 @@ module Kameleoon
30
34
  @client_id = client_id || config['client_id']
31
35
  @client_secret = client_secret || config['client_secret']
32
36
  @data_maximum_size = config['visitor_data_maximum_size'] || 500 # mb
37
+ @environment = config['environment'] || DEFAULT_ENVIRONMENT
33
38
  @verbose_mode = config['verbose_mode'] || false
34
39
  @experiments = []
35
40
  @feature_flags = []
@@ -83,8 +88,10 @@ module Kameleoon
83
88
  # @raise [Kameleoon::Exception::ExperimentConfigurationNotFound] Raise when experiment configuration is not found
84
89
  # @raise [Kameleoon::Exception::NotActivated] The visitor triggered the experiment, but did not activate it. Usually, this happens because the user has been associated with excluded traffic
85
90
  # @raise [Kameleoon::Exception::NotTargeted] The visitor is not targeted by the experiment, as the associated targeting segment conditions were not fulfilled. He should see the reference variation
91
+ # @raise [Kameleoon::Exception::VisitorCodeNotValid] If the visitor code is empty or longer than 255 chars
86
92
  #
87
93
  def trigger_experiment(visitor_code, experiment_id, timeout = @default_timeout)
94
+ check_visitor_code(visitor_code)
88
95
  experiment = @experiments.find { |experiment| experiment['id'].to_s == experiment_id.to_s }
89
96
  if experiment.nil?
90
97
  raise Exception::ExperimentConfigurationNotFound.new(experiment_id)
@@ -115,6 +122,7 @@ module Kameleoon
115
122
  end
116
123
  variation_id.to_i
117
124
  else
125
+ check_site_code_enable(experiment)
118
126
  visitor_data = @data.select { |key, value| key.to_s == visitor_code }.values.flatten! || []
119
127
  if experiment['targetingSegment'].nil? || experiment['targetingSegment'].check_tree(visitor_data)
120
128
  threshold = obtain_hash_double(visitor_code, experiment['respoolTime'], experiment['id'])
@@ -143,7 +151,10 @@ module Kameleoon
143
151
  # @param [String] visitor_code Visitor code
144
152
  # @param [...Data] data Data to associate with the visitor code
145
153
  #
154
+ # @raise [Kameleoon::Exception::VisitorCodeNotValid] If the visitor code is empty or longer than 255 chars
155
+ #
146
156
  def add_data(visitor_code, *args)
157
+ check_visitor_code(visitor_code)
147
158
  while ObjectSpace.memsize_of(@data) > @data_maximum_size * (2**20) do
148
159
  @data.shift
149
160
  end
@@ -167,7 +178,10 @@ module Kameleoon
167
178
  # @param [Integer] goal_id Id of the goal
168
179
  # @param [Float] revenue Optional - Revenue of the conversion.
169
180
  #
181
+ # @raise [Kameleoon::Exception::VisitorCodeNotValid] If the visitor code is empty or longer than 255 chars
182
+ #
170
183
  def track_conversion(visitor_code, goal_id, revenue = 0.0)
184
+ check_visitor_code(visitor_code)
171
185
  add_data(visitor_code, Conversion.new(goal_id, revenue))
172
186
  flush(visitor_code)
173
187
  end
@@ -181,7 +195,10 @@ module Kameleoon
181
195
  #
182
196
  # @param [String] visitor_code Optional field - Visitor code, without visitor code it flush all of the data
183
197
  #
198
+ # @raise [Kameleoon::Exception::VisitorCodeNotValid] If the visitor code is not null and empty or longer than 255 chars
199
+ #
184
200
  def flush(visitor_code = nil)
201
+ check_visitor_code(visitor_code) unless visitor_code.nil?
185
202
  track_data(visitor_code)
186
203
  end
187
204
 
@@ -203,9 +220,9 @@ module Kameleoon
203
220
  def obtain_variation_associated_data(variation_id)
204
221
  variation = @experiments.map { |experiment| experiment['variations'] }.flatten.select { |variation| variation['id'].to_i == variation_id.to_i }.first
205
222
  if variation.nil?
206
- raise Exception::VariationNotFound.new(variation_id)
223
+ raise Exception::VariationConfigurationNotFound.new(variation_id)
207
224
  else
208
- variation['customJson']
225
+ JSON.parse(variation['customJson'])
209
226
  end
210
227
  end
211
228
 
@@ -221,8 +238,10 @@ module Kameleoon
221
238
  #
222
239
  # @raise [Kameleoon::Exception::FeatureConfigurationNotFound]
223
240
  # @raise [Kameleoon::Exception::NotTargeted]
241
+ # @raise [Kameleoon::Exception::VisitorCodeNotValid] If the visitor code is empty or longer than 255 chars
224
242
  #
225
243
  def activate_feature(visitor_code, feature_key, timeout = @default_timeout)
244
+ check_visitor_code(visitor_code)
226
245
  feature_flag = get_feature_flag(feature_key)
227
246
  id = feature_flag['id']
228
247
  if @blocking
@@ -247,18 +266,23 @@ module Kameleoon
247
266
  result.to_s != "null"
248
267
 
249
268
  else
269
+ check_site_code_enable(feature_flag)
250
270
  visitor_data = @data.select { |key, value| key.to_s == visitor_code }.values.flatten! || []
251
- if feature_flag['targetingSegment'].nil? || feature_flag['targetingSegment'].check_tree(visitor_data)
271
+ unless feature_flag['targetingSegment'].nil? || feature_flag['targetingSegment'].check_tree(visitor_data)
272
+ raise Exception::NotTargeted.new(visitor_code)
273
+ end
274
+
275
+ if is_feature_flag_scheduled(feature_flag, Time.now.to_i)
252
276
  threshold = obtain_hash_double(visitor_code, {}, id)
253
- if threshold <= feature_flag['expositionRate']
254
- track_experiment(visitor_code, id, feature_flag["variationsId"].first)
255
- return true
277
+ if threshold >= 1 - feature_flag['expositionRate']
278
+ track_experiment(visitor_code, id, feature_flag["variations"].first['id'])
279
+ true
256
280
  else
257
281
  track_experiment(visitor_code, id, REFERENCE, true)
258
- return false
282
+ false
259
283
  end
260
284
  else
261
- raise Exception::NotTargeted.new(visitor_code)
285
+ false
262
286
  end
263
287
  end
264
288
  end
@@ -276,39 +300,52 @@ module Kameleoon
276
300
  #
277
301
  def obtain_feature_variable(feature_key, variable_key)
278
302
  feature_flag = get_feature_flag(feature_key)
279
- custom_json = feature_flag["variations"].first['customJson'][variable_key.to_s]
303
+ custom_json = JSON.parse(feature_flag["variations"].first['customJson'])[variable_key.to_s]
280
304
  if custom_json.nil?
281
305
  raise Exception::FeatureVariableNotFound.new("Feature variable not found")
282
306
  end
283
307
  case custom_json['type']
284
308
  when "Boolean"
285
- return !!custom_json['value']
309
+ return custom_json['value'].downcase == "true"
286
310
  when "String"
287
- return custom_json['value'].to_s
288
- when "Number"
289
- return custom_json['value'].to_f
290
- when "Json"
291
311
  return custom_json['value']
312
+ when "Number"
313
+ return custom_json['value'].to_i
314
+ when "JSON"
315
+ return JSON.parse(custom_json['value'])
292
316
  else
293
317
  raise TypeError.new("Unknown type for feature variable")
294
318
  end
295
319
  end
296
320
 
297
321
  private
298
- API_SSX_URL = "https://api-ssx.kameleoon.com"
322
+
323
+ API_SSX_URL = 'https://api-ssx.kameleoon.com'
299
324
  REFERENCE = 0
325
+ STATUS_ACTIVE = 'ACTIVE'
326
+ FEATURE_STATUS_DEACTIVATED = 'DEACTIVATED'
327
+ DEFAULT_ENVIRONMENT = 'production'
300
328
  attr :site_code, :client_id, :client_secret, :access_token, :experiments, :feature_flags, :scheduler, :data,
301
329
  :blocking, :tracking_url, :default_timeout, :interval, :memory_limit, :verbose_mode
302
330
 
303
331
  def fetch_configuration
304
332
  @scheduler = Rufus::Scheduler.singleton
305
333
  @scheduler.every @interval do
306
- log("Scheduled job to fetch configuration is starting.")
307
- fetch_configuration_job
334
+ log('Scheduled job to fetch configuration is starting.')
335
+ fetch_configuration_job_graphql
308
336
  end
309
337
  @scheduler.schedule '0s' do
310
- log("Start-up, fetching is starting")
311
- fetch_configuration_job
338
+ log('Start-up, fetching is starting')
339
+ fetch_configuration_job_graphql
340
+ end
341
+ end
342
+
343
+ def fetch_configuration_job_graphql
344
+ EM.synchrony do
345
+ obtain_access_token
346
+ @experiments = obtain_experiments_graphql(@site_code) || @experiments
347
+ @feature_flags = obtain_feature_flags_graphql(@site_code, @environment) || @feature_flags
348
+ EM.stop
312
349
  end
313
350
  end
314
351
 
@@ -387,8 +424,8 @@ module Kameleoon
387
424
  end
388
425
 
389
426
  def complete_experiment(experiment)
390
- unless experiment['variationsId'].nil?
391
- experiment['variations'] = experiment['variationsId'].map { |variationId| obtain_variation(variationId) }
427
+ unless experiment['variations'].nil?
428
+ experiment['variations'] = experiment['variations'].map { |variationId| obtain_variation(variationId) }
392
429
  end
393
430
  unless experiment['targetingSegmentId'].nil?
394
431
  experiment['targetingSegment'] = Kameleoon::Targeting::Segment.new(obtain_segment(experiment['targetingSegmentId']))
@@ -396,6 +433,44 @@ module Kameleoon
396
433
  experiment
397
434
  end
398
435
 
436
+ # fetching segment for both types: experiments and feature_flags (campaigns)
437
+ def complete_campaign_graphql(campaign)
438
+ campaign['id'] = campaign['id'].to_i
439
+ campaign['status'] = campaign['status']
440
+ unless campaign['deviations'].nil?
441
+ campaign['deviations'] = Hash[*campaign['deviations'].map { |it| [ it['variationId'] == '0' ? 'origin' : it['variationId'], it['value'] ] }.flatten]
442
+ end
443
+ unless campaign['respoolTime'].nil?
444
+ campaign['respoolTime'] = Hash[*campaign['respoolTime'].map { |it| [ it['variationId'] == '0' ? 'origin' : it['variationId'], it['value'] ] }.flatten]
445
+ end
446
+ unless campaign['variations'].nil?
447
+ campaign['variations'] = campaign['variations'].map { |it| {'id' => it['id'].to_i, 'customJson' => it['customJson']} }
448
+ end
449
+ unless campaign['segment'].nil?
450
+ campaign['targetingSegment'] = Kameleoon::Targeting::Segment.new((campaign['segment']))
451
+ end
452
+ campaign
453
+ end
454
+
455
+ def complete_experiment(experiment)
456
+ unless experiment['variations'].nil?
457
+ experiment['variations'] = experiment['variations'].map { |variationId| obtain_variation(variationId) }
458
+ end
459
+ unless experiment['targetingSegmentId'].nil?
460
+ experiment['targetingSegment'] = Kameleoon::Targeting::Segment.new(obtain_segment(experiment['targetingSegmentId']))
461
+ end
462
+ experiment
463
+ end
464
+
465
+ def obtain_experiments_graphql(site_code, per_page = -1)
466
+ log "Fetching experiments GraphQL"
467
+ experiments = fetch_all_graphql("v1/graphql?perPage=#{per_page}", Kameleoon::Query.query_experiments(site_code)).map { |it| JSON.parse(it.response)['data']['experiments']['edges'] }.flatten.map do |experiment|
468
+ complete_campaign_graphql(experiment['node'])
469
+ end
470
+ log "Experiment are fetched: " + experiments.inspect
471
+ experiments
472
+ end
473
+
399
474
  def obtain_tests(site_id, per_page = -1)
400
475
  log "Fetching experiments"
401
476
  query_values = { 'perPage' => per_page }
@@ -411,12 +486,21 @@ module Kameleoon
411
486
  experiments
412
487
  end
413
488
 
489
+ def obtain_feature_flags_graphql(site_id, environment = @environment, per_page = -1)
490
+ log "Fetching feature flags GraphQL"
491
+ feature_flags = fetch_all_graphql("v1/graphql?perPage=#{per_page}", Kameleoon::Query.query_feature_flags(site_code, environment)).map { |it| JSON.parse(it.response)['data']['featureFlags']['edges'] }.flatten.map do |feature_flag|
492
+ complete_campaign_graphql(feature_flag['node'])
493
+ end
494
+ log "Feature flags are fetched: " + feature_flags.inspect
495
+ feature_flags
496
+ end
497
+
414
498
  def obtain_feature_flags(site_id, per_page = -1)
415
499
  log "Fetching feature flags"
416
500
  query_values = { 'perPage' => per_page }
417
501
  filters = [
418
502
  hash_filter('siteId', 'EQUAL', [site_id]),
419
- hash_filter('status', 'EQUAL', ['ACTIVE'])
503
+ hash_filter('status', 'IN', ['ACTIVE', 'PAUSED'])
420
504
  ]
421
505
  feature_flags = fetch_all('feature-flags', query_values, filters).map { |it| JSON.parse(it.response) }.flatten.map do |ff|
422
506
  complete_experiment(ff)
@@ -425,6 +509,19 @@ module Kameleoon
425
509
  feature_flags
426
510
  end
427
511
 
512
+ def fetch_all_graphql(path, query_graphql = {})
513
+ results = []
514
+ current_page = 1
515
+ loop do
516
+ http = fetch_one_graphql(path, query_graphql)
517
+ break if http == false
518
+ results.push(http)
519
+ break if http.response_header["X-Pagination-Page-Count"].to_i <= current_page
520
+ current_page += 1
521
+ end
522
+ results
523
+ end
524
+
428
525
  def fetch_all(path, query_values = {}, filters = [])
429
526
  results = []
430
527
  current_page = 1
@@ -439,6 +536,15 @@ module Kameleoon
439
536
  results
440
537
  end
441
538
 
539
+ def fetch_one_graphql(path, query_graphql = {})
540
+ request = EM::Synchrony.sync post({ :path => path, :body => query_graphql, :head => hash_headers })
541
+ unless is_successful(request)
542
+ log "Failed to fetch" + request.inspect
543
+ return false
544
+ end
545
+ request
546
+ end
547
+
442
548
  def fetch_one(path, query_values = {}, filters = [])
443
549
  unless filters.empty?
444
550
  query_values['filter'] = filters.to_json
@@ -481,6 +587,8 @@ module Kameleoon
481
587
  feature_flag = @feature_flags.select { |ff| ff['identificationKey'] == feature_key}.first
482
588
  elsif feature_key.is_a?(Integer)
483
589
  feature_flag = @feature_flags.select { |ff| ff['id'].to_i == feature_key}.first
590
+ print "\nPassing `feature_key` with type of `int` to `activate_feature` or `obtain_feature_variable` "\
591
+ "is deprecated, it will be removed in next releases. This is necessary to support multi-environment feature\n"
484
592
  else
485
593
  raise TypeError.new("Feature key should be a String or an Integer.")
486
594
  end
@@ -490,6 +598,20 @@ module Kameleoon
490
598
  feature_flag
491
599
  end
492
600
 
601
+ def is_feature_flag_scheduled(feature_flag, date)
602
+ current_status = feature_flag['status'] == STATUS_ACTIVE
603
+ if feature_flag['featureStatus'] == FEATURE_STATUS_DEACTIVATED || feature_flag['schedules'].empty?
604
+ return current_status
605
+ end
606
+ feature_flag['schedules'].each do |schedule|
607
+ if (schedule['dateStart'].nil? || Time.parse(schedule['dateStart']).to_i < date) &&
608
+ (schedule['dateEnd'].nil? || Time.parse(schedule['dateEnd']).to_i > date)
609
+ return true
610
+ end
611
+ end
612
+ false
613
+ end
614
+
493
615
  def track_experiment(visitor_code, experiment_id, variation_id = nil, none_variation = false)
494
616
  data_not_sent = data_not_sent(visitor_code)
495
617
  options = {
@@ -498,6 +620,7 @@ module Kameleoon
498
620
  :head => { "Content-Type" => "text/plain"}
499
621
  }
500
622
  trial = 0
623
+ success = false
501
624
  log "Start post tracking experiment: " + data_not_sent.inspect
502
625
  Thread.new do
503
626
  EM.synchrony do
@@ -507,12 +630,18 @@ module Kameleoon
507
630
  if is_successful(request)
508
631
  (data_not_sent.values[0] || []).each { |it| it.sent = true }
509
632
  EM.stop
633
+ success = true
634
+ break
510
635
  end
511
636
  trial += 1
512
637
  end
513
638
  EM.stop
514
639
  end
515
- log "Post to experiment tracking is done after " + trial.to_s + " trials"
640
+ if success
641
+ log "Post to experiment tracking is done after " + (trial + 1).to_s + " trials"
642
+ elsif
643
+ log "Post to experiment tracking is failed after " + trial.to_s + " trials"
644
+ end
516
645
  Thread.exit
517
646
  end
518
647
  end
@@ -551,6 +680,12 @@ module Kameleoon
551
680
  end
552
681
  end
553
682
 
683
+ def check_site_code_enable(exp_or_ff)
684
+ unless exp_or_ff['site'].nil? || exp_or_ff['site']['isKameleoonEnabled']
685
+ raise Exception::SiteCodeDisabled.new(site_code)
686
+ end
687
+ end
688
+
554
689
  def data_not_sent(visitor_code = nil)
555
690
  if visitor_code.nil?
556
691
  @data.select {|key, values| values.any? {|data| !data.sent}}
@@ -565,4 +700,4 @@ module Kameleoon
565
700
  end
566
701
  end
567
702
  end
568
- end
703
+ end
@@ -26,10 +26,18 @@ module Kameleoon
26
26
  identifier = visitor_code.to_s
27
27
  identifier += container_id.to_s
28
28
  if !respool_times.nil? && !respool_times.empty?
29
- identifier += respool_times.values.sort.join.to_s
29
+ identifier += respool_times.sort.to_h.values.join.to_s
30
30
  end
31
31
  (Digest::SHA256.hexdigest(identifier.encode('UTF-8')).to_i(16) / (BigDecimal("2") ** BigDecimal("256"))).round(16)
32
32
  end
33
+
34
+ def check_visitor_code(visitor_code)
35
+ if visitor_code.nil?
36
+ check_default_visitor_code('')
37
+ elsif
38
+ check_default_visitor_code(visitor_code)
39
+ end
40
+ end
33
41
 
34
42
  private
35
43
 
@@ -43,6 +51,7 @@ module Kameleoon
43
51
  if default_visitor_code.nil?
44
52
  return
45
53
  end
54
+ default_visitor_code = default_visitor_code.to_s
46
55
  if default_visitor_code.length == 0
47
56
  raise Kameleoon::Exception::VisitorCodeNotValid.new("Empty visitor Code")
48
57
  end
@@ -1,4 +1,3 @@
1
-
2
1
  module Kameleoon
3
2
  NONCE_LENGTH = 16
4
3
 
@@ -29,6 +28,17 @@ module Kameleoon
29
28
  def obtain_full_post_text_line
30
29
  raise KameleoonError.new("ToDo: implement this method.")
31
30
  end
31
+
32
+ def encode(url)
33
+ encoded_url = CGI.escape(url)
34
+ encoded_url.gsub! "%27", "'"
35
+ encoded_url.gsub! "%21", "!"
36
+ encoded_url.gsub! "+", "%20"
37
+ encoded_url.gsub! "%2A", "*"
38
+ encoded_url.gsub! "%28", "("
39
+ encoded_url.gsub! "%29", ")"
40
+ encoded_url
41
+ end
32
42
  end
33
43
 
34
44
  class CustomData < Data
@@ -63,7 +73,7 @@ module Kameleoon
63
73
  def obtain_full_post_text_line
64
74
  to_encode = "[[\"" + @value.to_s.gsub("\"", "\\\"") + "\",1]]"
65
75
  nonce = Kameleoon::Utils.generate_random_string(NONCE_LENGTH)
66
- "eventType=customData&index=" + @id.to_s + "&valueToCount=" + URI.encode_www_form_component(to_encode) + "&overwrite=true&nonce=" + nonce
76
+ "eventType=customData&index=" + @id.to_s + "&valueToCount=" + encode(to_encode) + "&overwrite=true&nonce=" + nonce
67
77
  end
68
78
  end
69
79
 
@@ -101,9 +111,9 @@ module Kameleoon
101
111
  nonce = Kameleoon::Utils.generate_random_string(NONCE_LENGTH)
102
112
  referrer_text = ""
103
113
  unless @referrer.nil?
104
- referrer_text = "&referrers=[" + @referrer + "]"
114
+ referrer_text = "&referrers=[" + @referrer.to_s + "]"
105
115
  end
106
- "eventType=page&href=" + @url + "&title=" + @title + "&keyPages=[]" + referrer_text + "&nonce=" + nonce
116
+ "eventType=page&href=" + encode(@url) + "&title=" + @title + "&keyPages=[]" + referrer_text + "&nonce=" + nonce
107
117
  end
108
118
  end
109
119
 
@@ -10,7 +10,7 @@ module Kameleoon
10
10
  super(value.to_s + " not found.")
11
11
  end
12
12
  end
13
- class VariationNotFound < NotFound
13
+ class VariationConfigurationNotFound < NotFound
14
14
  def initialize(id = "")
15
15
  super("Variation " + id.to_s)
16
16
  end
@@ -50,5 +50,10 @@ module Kameleoon
50
50
  super("Visitor code not valid: " + message)
51
51
  end
52
52
  end
53
+ class SiteCodeDisabled < KameleoonError
54
+ def initialize(message = "")
55
+ super("Site with siteCode '" + message + "' is disabled")
56
+ end
57
+ end
53
58
  end
54
59
  end
@@ -0,0 +1,76 @@
1
+ module Kameleoon
2
+ module Query
3
+
4
+ def self.query_experiments(site_code)
5
+ '{
6
+ "operationName": "getExperiments",
7
+ "query": "query getExperiments($first: Int, $after: String, $filter: FilteringExpression, $sort: [SortingParameter!]) { experiments(first: $first, after: $after, filter: $filter, sort: $sort) { edges { node { id name type site { id code isKameleoonEnabled } status variations { id customJson } deviations { variationId value } respoolTime {variationId value } segment { id name conditionsData { firstLevelOrOperators firstLevel { orOperators conditions { targetingType isInclude ... on CustomDataTargetingCondition { customDataIndex value valueMatchType } } } } } __typename } __typename } pageInfo { endCursor hasNextPage __typename } totalCount __typename } }",
8
+ "variables": {
9
+ "filter": {
10
+ "and": [{
11
+ "condition": {
12
+ "field": "status",
13
+ "operator": "IN",
14
+ "parameters": ["ACTIVE", "DEVIATED", "USED_AS_PERSONALIZATION"]
15
+ }
16
+ },
17
+ {
18
+ "condition": {
19
+ "field": "type",
20
+ "operator": "IN",
21
+ "parameters": ["SERVER_SIDE", "HYBRID"]
22
+ }
23
+ },
24
+ {
25
+ "condition": {
26
+ "field": "siteCode",
27
+ "operator": "IN",
28
+ "parameters": ["' + site_code + '"]
29
+ }
30
+ }]
31
+ },
32
+ "sort": [{
33
+ "field": "id",
34
+ "direction": "ASC"
35
+ }]
36
+ }
37
+ }'
38
+ end
39
+
40
+ def self.query_feature_flags(site_code, environment)
41
+ '{
42
+ "operationName": "getFeatureFlags",
43
+ "query": "query getFeatureFlags($first: Int, $after: String, $filter: FilteringExpression, $sort: [SortingParameter!]) { featureFlags(first: $first, after: $after, filter: $filter, sort: $sort) { edges { node { id name site { id code isKameleoonEnabled } bypassDeviation status variations { id customJson } respoolTime { variationId value } expositionRate identificationKey featureFlagSdkLanguageType featureStatus schedules { dateStart dateEnd } segment { id name conditionsData { firstLevelOrOperators firstLevel { orOperators conditions { targetingType isInclude ... on CustomDataTargetingCondition { customDataIndex value valueMatchType } } } } } __typename } __typename } pageInfo { endCursor hasNextPage __typename } totalCount __typename } }",
44
+ "variables": {
45
+ "filter": {
46
+ "and": [{
47
+ "condition": {
48
+ "field": "featureStatus",
49
+ "operator": "IN",
50
+ "parameters": ["ACTIVATED", "SCHEDULED", "DEACTIVATED"]
51
+ }
52
+ },
53
+ {
54
+ "condition": {
55
+ "field": "siteCode",
56
+ "operator": "IN",
57
+ "parameters": ["' + site_code + '"]
58
+ }
59
+ },
60
+ {
61
+ "condition": {
62
+ "field": "environment.key",
63
+ "operator": "IN",
64
+ "parameters": ["' + environment + '"]
65
+ }
66
+ }]
67
+ },
68
+ "sort": [{
69
+ "field": "id",
70
+ "direction": "ASC"
71
+ }]
72
+ }
73
+ }'
74
+ end
75
+ end
76
+ end
@@ -1,4 +1,3 @@
1
- require 'kameleoon/targeting/models'
2
1
  require 'kameleoon/targeting/conditions/custom_datum'
3
2
 
4
3
  module Kameleoon
@@ -1,6 +1,5 @@
1
1
  require 'kameleoon/targeting/condition'
2
2
  require 'kameleoon/exceptions'
3
- require 'kameleoon/targeting/models'
4
3
 
5
4
  module Kameleoon
6
5
  #@api private
@@ -20,22 +19,22 @@ module Kameleoon
20
19
  end
21
20
  @operator = json_condition['valueMatchType']
22
21
 
23
- if json_condition['value'].nil?
24
- raise Exception::NotFoundError.new('value')
25
- end
22
+ # if json_condition['value'].nil?
23
+ # raise Exception::NotFoundError.new('value')
24
+ # end
26
25
  @value = json_condition['value']
27
26
 
28
27
  @type = ConditionType::CUSTOM_DATUM
29
28
 
30
- if json_condition['include'].nil?
31
- raise Exception::NotFoundError.new('include')
29
+ if json_condition['include'].nil? && json_condition['isInclude'].nil?
30
+ raise Exception::NotFoundError.new('include / isInclude missed')
32
31
  end
33
- @include = json_condition['include']
32
+ @include = json_condition['include'] || json_condition['isInclude']
34
33
  end
35
34
 
36
35
  def check(datas)
37
36
  is_targeted = false
38
- custom_data = datas.select { |data| data.instance == DataType::CUSTOM && data.id == @index }.first
37
+ custom_data = datas.select { |data| data.instance == DataType::CUSTOM && data.id == @index }.last
39
38
  if custom_data.nil?
40
39
  is_targeted = (@operator == Operator::UNDEFINED.to_s)
41
40
  else
@@ -65,11 +64,11 @@ module Kameleoon
65
64
  is_targeted = true
66
65
  end
67
66
  when Operator::IS_TRUE.to_s
68
- if custom_data.value == true
67
+ if custom_data.value == 'true'
69
68
  is_targeted = true
70
69
  end
71
70
  when Operator::IS_FALSE.to_s
72
- if custom_data.value == false
71
+ if custom_data.value == 'false'
73
72
  is_targeted = true
74
73
  end
75
74
  else
@@ -23,7 +23,7 @@ module Kameleoon
23
23
  if hash["id"].nil?
24
24
  raise Kameleoon::Exception::NotFound.new("id")
25
25
  end
26
- @id = hash["id"]
26
+ @id = hash["id"].to_i
27
27
  if hash["conditionsData"].nil?
28
28
  raise Kameleoon::Exception::NotFound.new(hash["conditionsData"])
29
29
  end
@@ -1,5 +1,4 @@
1
1
  require 'kameleoon/targeting/condition_factory'
2
- require 'kameleoon/targeting/models'
3
2
 
4
3
  module Kameleoon
5
4
  #@api private
@@ -1,3 +1,3 @@
1
1
  module Kameleoon
2
- VERSION = '1.0.7'
2
+ VERSION = '1.0.10'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kameleoon-client-ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.7
4
+ version: 1.0.10
5
5
  platform: ruby
6
6
  authors:
7
- - Kameleoon - Guillaume Grandjean
7
+ - Kameleoon
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-26 00:00:00.000000000 Z
11
+ date: 2022-02-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: em-http-request
@@ -54,7 +54,7 @@ dependencies:
54
54
  version: '3.7'
55
55
  description: Kameleoon Client Ruby Software Development Kit
56
56
  email:
57
- - ggrandjean@kameleoon.com
57
+ - sdk@kameleoon.com
58
58
  executables: []
59
59
  extensions: []
60
60
  extra_rdoc_files: []
@@ -66,6 +66,7 @@ files:
66
66
  - lib/kameleoon/data.rb
67
67
  - lib/kameleoon/exceptions.rb
68
68
  - lib/kameleoon/factory.rb
69
+ - lib/kameleoon/query_graphql.rb
69
70
  - lib/kameleoon/request.rb
70
71
  - lib/kameleoon/targeting/condition.rb
71
72
  - lib/kameleoon/targeting/condition_factory.rb