kameleoon-client-ruby 1.0.9 → 1.1.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 887e6709dff33fa410a8e34a32577d8ad1ffe56fa52cadd24bd1734c68d2e46b
4
- data.tar.gz: e42d4d944e8c1549eae488f74e7b5c8891bf41f35dcce02133bfc68cee969463
3
+ metadata.gz: 3e62c8e4dfa35f91706257ff5367a266736af7c03786803ad8f9fff06aa8c194
4
+ data.tar.gz: 7773e082be938108b42c65cd0109392413bf938619cf04cc994617111c746f15
5
5
  SHA512:
6
- metadata.gz: 3cf8ee8561091145b530082e2ce86614b41f9c63c70b644518c5a9d3738e659084526003ad298cdf320da0570cc7d55b551b3b80e680877a1c4c0bd984bc1b9f
7
- data.tar.gz: 4c9e4873467d1f2578be20f8246a47ad3810401251fb9e3dd34d3baad073184c6083fa0a35893c95e308a3482ebb3b42d58e1fd8e82c9400fa554ebbd1e16d63
6
+ metadata.gz: d74b03762402a1f4c6e7349bb085e3b185ee564f0a7f645a16f7bb8da0321b73689adad785d52bdce48e9a0646f6b10de6c3f2fc8016b9459b713eafbda77e6f
7
+ data.tar.gz: a835f8b23265a2ae8241114fb2c5fe1f8965544fb348c2f6c16d714fe543fa6396b5bc6cbf7f68d8ac5e5391b8cbf594f55086e8c24290fda57a7b18ac5f57c4
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'kameleoon/targeting/models'
2
4
  require 'kameleoon/request'
3
5
  require 'kameleoon/exceptions'
@@ -7,6 +9,8 @@ require 'rufus/scheduler'
7
9
  require 'yaml'
8
10
  require 'json'
9
11
  require 'em-synchrony'
12
+ require 'em-synchrony/em-http'
13
+ require 'em-synchrony/fiber_iterator'
10
14
  require 'objspace'
11
15
  require 'time'
12
16
 
@@ -27,11 +31,14 @@ module Kameleoon
27
31
  @site_code = site_code
28
32
  @blocking = blocking
29
33
  @default_timeout = config['default_timeout'] || default_timeout # in ms
30
- @interval = config['actions_configuration_refresh_interval'].to_s + 'm' || interval
34
+ refresh_interval = config['actions_configuration_refresh_interval']
35
+ @interval = refresh_interval.nil? ? interval : "#{refresh_interval}m"
31
36
  @tracking_url = config['tracking_url'] || "https://api-ssx.kameleoon.com"
37
+ @api_data_url = "https://api-data.kameleoon.com"
32
38
  @client_id = client_id || config['client_id']
33
39
  @client_secret = client_secret || config['client_secret']
34
40
  @data_maximum_size = config['visitor_data_maximum_size'] || 500 # mb
41
+ @environment = config['environment'] || DEFAULT_ENVIRONMENT
35
42
  @verbose_mode = config['verbose_mode'] || false
36
43
  @experiments = []
37
44
  @feature_flags = []
@@ -119,6 +126,7 @@ module Kameleoon
119
126
  end
120
127
  variation_id.to_i
121
128
  else
129
+ check_site_code_enable(experiment)
122
130
  visitor_data = @data.select { |key, value| key.to_s == visitor_code }.values.flatten! || []
123
131
  if experiment['targetingSegment'].nil? || experiment['targetingSegment'].check_tree(visitor_data)
124
132
  threshold = obtain_hash_double(visitor_code, experiment['respoolTime'], experiment['id'])
@@ -262,6 +270,7 @@ module Kameleoon
262
270
  result.to_s != "null"
263
271
 
264
272
  else
273
+ check_site_code_enable(feature_flag)
265
274
  visitor_data = @data.select { |key, value| key.to_s == visitor_code }.values.flatten! || []
266
275
  unless feature_flag['targetingSegment'].nil? || feature_flag['targetingSegment'].check_tree(visitor_data)
267
276
  raise Exception::NotTargeted.new(visitor_code)
@@ -269,7 +278,7 @@ module Kameleoon
269
278
 
270
279
  if is_feature_flag_scheduled(feature_flag, Time.now.to_i)
271
280
  threshold = obtain_hash_double(visitor_code, {}, id)
272
- if threshold <= feature_flag['expositionRate']
281
+ if threshold >= 1 - feature_flag['expositionRate']
273
282
  track_experiment(visitor_code, id, feature_flag["variations"].first['id'])
274
283
  true
275
284
  else
@@ -288,7 +297,7 @@ module Kameleoon
288
297
  # A feature variable can be changed easily via our web application.
289
298
  #
290
299
  # @param [String | Integer] feature_key
291
- # @param [String ] variable_key
300
+ # @param [String] variable_key
292
301
  #
293
302
  # @raise [Kameleoon::Exception::FeatureConfigurationNotFound]
294
303
  # @raise [Kameleoon::Exception::FeatureVariableNotFound]
@@ -313,22 +322,48 @@ module Kameleoon
313
322
  end
314
323
  end
315
324
 
325
+ ##
326
+ # The retrieved_data_from_remote_source method allows you to retrieve data (according to a key passed as argument)
327
+ # stored on a remote Kameleoon server. Usually data will be stored on our remote servers via the use of our Data API.
328
+ # This method, along with the availability of our highly scalable servers for this purpose, provides a convenient way
329
+ # to quickly store massive amounts of data that can be later retrieved for each of your visitors / users.
330
+ #
331
+ # @param [String] key Key you want to retrieve data. This field is mandatory.
332
+ # @param [Int] timeout Timeout for request. Equals default_timeout in a config file. This field is optional.
333
+ #
334
+ # @return [Hash] Hash object of the json object.
335
+ #
336
+ #
337
+ def retrieve_data_from_remote_source(key, timeout = @default_timeout)
338
+ connexion_options = { connect_timeout: (timeout.to_f / 1000.0) }
339
+ path = get_api_data_request_url(key)
340
+ log "Retrieve API Data connexion: #{connexion_options.inspect}"
341
+ response = get_sync(@api_data_url + path, connexion_options)
342
+ if is_successful_sync(response)
343
+ JSON.parse(response.body) unless response.nil?
344
+ else
345
+ return nil
346
+ end
347
+ end
348
+
316
349
  private
317
- API_SSX_URL = "https://api-ssx.kameleoon.com"
350
+
351
+ API_SSX_URL = 'https://api-ssx.kameleoon.com'
318
352
  REFERENCE = 0
319
- STATUS_ACTIVE = "ACTIVE"
320
- FEATURE_STATUS_DEACTIVATED = "DEACTIVATED"
353
+ STATUS_ACTIVE = 'ACTIVE'
354
+ FEATURE_STATUS_DEACTIVATED = 'DEACTIVATED'
355
+ DEFAULT_ENVIRONMENT = 'production'
321
356
  attr :site_code, :client_id, :client_secret, :access_token, :experiments, :feature_flags, :scheduler, :data,
322
357
  :blocking, :tracking_url, :default_timeout, :interval, :memory_limit, :verbose_mode
323
358
 
324
359
  def fetch_configuration
325
360
  @scheduler = Rufus::Scheduler.singleton
326
361
  @scheduler.every @interval do
327
- log("Scheduled job to fetch configuration is starting.")
362
+ log('Scheduled job to fetch configuration is starting.')
328
363
  fetch_configuration_job_graphql
329
364
  end
330
365
  @scheduler.schedule '0s' do
331
- log("Start-up, fetching is starting")
366
+ log('Start-up, fetching is starting')
332
367
  fetch_configuration_job_graphql
333
368
  end
334
369
  end
@@ -337,7 +372,7 @@ module Kameleoon
337
372
  EM.synchrony do
338
373
  obtain_access_token
339
374
  @experiments = obtain_experiments_graphql(@site_code) || @experiments
340
- @feature_flags = obtain_feature_flags_graphql(@site_code) || @feature_flags
375
+ @feature_flags = obtain_feature_flags_graphql(@site_code, @environment) || @feature_flags
341
376
  EM.stop
342
377
  end
343
378
  end
@@ -479,9 +514,9 @@ module Kameleoon
479
514
  experiments
480
515
  end
481
516
 
482
- def obtain_feature_flags_graphql(site_id, per_page = -1)
517
+ def obtain_feature_flags_graphql(site_id, environment = @environment, per_page = -1)
483
518
  log "Fetching feature flags GraphQL"
484
- feature_flags = fetch_all_graphql("v1/graphql?perPage=#{per_page}", Kameleoon::Query.query_feature_flags(site_code)).map { |it| JSON.parse(it.response)['data']['featureFlags']['edges'] }.flatten.map do |feature_flag|
519
+ 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|
485
520
  complete_campaign_graphql(feature_flag['node'])
486
521
  end
487
522
  log "Feature flags are fetched: " + feature_flags.inspect
@@ -575,11 +610,21 @@ module Kameleoon
575
610
  "/dataTracking?" + URI.encode_www_form(get_common_ssx_parameters(visitor_code))
576
611
  end
577
612
 
613
+ def get_api_data_request_url(key)
614
+ mapKey = {
615
+ siteCode: site_code,
616
+ key: key
617
+ }
618
+ "/data?#{URI.encode_www_form(mapKey)}"
619
+ end
620
+
578
621
  def get_feature_flag(feature_key)
579
622
  if feature_key.is_a?(String)
580
623
  feature_flag = @feature_flags.select { |ff| ff['identificationKey'] == feature_key}.first
581
624
  elsif feature_key.is_a?(Integer)
582
625
  feature_flag = @feature_flags.select { |ff| ff['id'].to_i == feature_key}.first
626
+ print "\nPassing `feature_key` with type of `int` to `activate_feature` or `obtain_feature_variable` "\
627
+ "is deprecated, it will be removed in next releases. This is necessary to support multi-environment feature\n"
583
628
  else
584
629
  raise TypeError.new("Feature key should be a String or an Integer.")
585
630
  end
@@ -671,6 +716,12 @@ module Kameleoon
671
716
  end
672
717
  end
673
718
 
719
+ def check_site_code_enable(exp_or_ff)
720
+ unless exp_or_ff['site'].nil? || exp_or_ff['site']['isKameleoonEnabled']
721
+ raise Exception::SiteCodeDisabled.new(site_code)
722
+ end
723
+ end
724
+
674
725
  def data_not_sent(visitor_code = nil)
675
726
  if visitor_code.nil?
676
727
  @data.select {|key, values| values.any? {|data| !data.sent}}
@@ -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
@@ -4,7 +4,7 @@ module Kameleoon
4
4
  def self.query_experiments(site_code)
5
5
  '{
6
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 } 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 } }",
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
8
  "variables": {
9
9
  "filter": {
10
10
  "and": [{
@@ -13,13 +13,15 @@ module Kameleoon
13
13
  "operator": "IN",
14
14
  "parameters": ["ACTIVE", "DEVIATED", "USED_AS_PERSONALIZATION"]
15
15
  }
16
- }, {
16
+ },
17
+ {
17
18
  "condition": {
18
19
  "field": "type",
19
20
  "operator": "IN",
20
21
  "parameters": ["SERVER_SIDE", "HYBRID"]
21
22
  }
22
- }, {
23
+ },
24
+ {
23
25
  "condition": {
24
26
  "field": "siteCode",
25
27
  "operator": "IN",
@@ -35,10 +37,10 @@ module Kameleoon
35
37
  }'
36
38
  end
37
39
 
38
- def self.query_feature_flags(site_code)
40
+ def self.query_feature_flags(site_code, environment)
39
41
  '{
40
42
  "operationName": "getFeatureFlags",
41
- "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 } 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 } }",
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 } }",
42
44
  "variables": {
43
45
  "filter": {
44
46
  "and": [{
@@ -47,13 +49,21 @@ module Kameleoon
47
49
  "operator": "IN",
48
50
  "parameters": ["ACTIVATED", "SCHEDULED", "DEACTIVATED"]
49
51
  }
50
- }, {
52
+ },
53
+ {
51
54
  "condition": {
52
55
  "field": "siteCode",
53
56
  "operator": "IN",
54
57
  "parameters": ["' + site_code + '"]
55
58
  }
56
- }]
59
+ },
60
+ {
61
+ "condition": {
62
+ "field": "environment.key",
63
+ "operator": "IN",
64
+ "parameters": ["' + environment + '"]
65
+ }
66
+ }]
57
67
  },
58
68
  "sort": [{
59
69
  "field": "id",
@@ -1,5 +1,6 @@
1
1
  require "em-synchrony/em-http"
2
2
  require "kameleoon/version"
3
+ require 'net/http'
3
4
 
4
5
  module Kameleoon
5
6
  # @api private
@@ -20,6 +21,10 @@ module Kameleoon
20
21
  request(Method::POST, request_options, url, connexion_options)
21
22
  end
22
23
 
24
+ def get_sync(url = API_URL, connexion_options = {})
25
+ request_sync(Method::GET, url, connexion_options)
26
+ end
27
+
23
28
  private
24
29
 
25
30
  def request(method, request_options, url, connexion_options)
@@ -29,7 +34,22 @@ module Kameleoon
29
34
  when Method::POST then
30
35
  return EventMachine::HttpRequest.new(url, connexion_options).apost request_options
31
36
  when Method::GET then
32
- return EventMachine::HttpRequest.new(url, connexion_options).aget request_options
37
+ return EventMachine::HttpRequest.new(url, connexion_options).get request_options
38
+ else
39
+ print "Unknown request type"
40
+ return false
41
+ end
42
+ end
43
+
44
+ def request_sync(method, url, connexion_options)
45
+ request_options = {} if request_options.nil?
46
+ add_user_agent(request_options)
47
+ case method
48
+ when Method::GET then
49
+ uri = URI(url)
50
+ request = Net::HTTP.new(url)
51
+ response = Net::HTTP.get_response(uri)
52
+ response
33
53
  else
34
54
  print "Unknown request type"
35
55
  return false
@@ -40,6 +60,10 @@ module Kameleoon
40
60
  !request.nil? && request != false && /20\d/.match(request.response_header.status.to_s)
41
61
  end
42
62
 
63
+ def is_successful_sync(response)
64
+ !response.nil? && response != false && response.is_a?(Net::HTTPSuccess)
65
+ end
66
+
43
67
  def add_user_agent(request_options)
44
68
  if request_options[:head].nil?
45
69
  request_options[:head] = {'Kameleoon-Client' => 'sdk/ruby/' + Kameleoon::VERSION}
@@ -34,7 +34,7 @@ module Kameleoon
34
34
 
35
35
  def check(datas)
36
36
  is_targeted = false
37
- 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
38
38
  if custom_data.nil?
39
39
  is_targeted = (@operator == Operator::UNDEFINED.to_s)
40
40
  else
@@ -1,3 +1,3 @@
1
1
  module Kameleoon
2
- VERSION = '1.0.9'
2
+ VERSION = '1.1.1'
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.9
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kameleoon
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-11-25 00:00:00.000000000 Z
11
+ date: 2022-07-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: em-http-request