kameleoon-client-ruby 1.0.9 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/kameleoon/client.rb +62 -11
- data/lib/kameleoon/exceptions.rb +5 -0
- data/lib/kameleoon/query_graphql.rb +17 -7
- data/lib/kameleoon/request.rb +25 -1
- data/lib/kameleoon/targeting/conditions/custom_datum.rb +1 -1
- data/lib/kameleoon/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3e62c8e4dfa35f91706257ff5367a266736af7c03786803ad8f9fff06aa8c194
|
4
|
+
data.tar.gz: 7773e082be938108b42c65cd0109392413bf938619cf04cc994617111c746f15
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d74b03762402a1f4c6e7349bb085e3b185ee564f0a7f645a16f7bb8da0321b73689adad785d52bdce48e9a0646f6b10de6c3f2fc8016b9459b713eafbda77e6f
|
7
|
+
data.tar.gz: a835f8b23265a2ae8241114fb2c5fe1f8965544fb348c2f6c16d714fe543fa6396b5bc6cbf7f68d8ac5e5391b8cbf594f55086e8c24290fda57a7b18ac5f57c4
|
data/lib/kameleoon/client.rb
CHANGED
@@ -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
|
-
|
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
|
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
|
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
|
-
|
350
|
+
|
351
|
+
API_SSX_URL = 'https://api-ssx.kameleoon.com'
|
318
352
|
REFERENCE = 0
|
319
|
-
STATUS_ACTIVE =
|
320
|
-
FEATURE_STATUS_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(
|
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(
|
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}}
|
data/lib/kameleoon/exceptions.rb
CHANGED
@@ -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",
|
data/lib/kameleoon/request.rb
CHANGED
@@ -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).
|
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 }.
|
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
|
data/lib/kameleoon/version.rb
CHANGED
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.
|
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:
|
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
|