clever_tap_dubit 0.3.2
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 +7 -0
- data/.gitignore +13 -0
- data/.rspec +1 -0
- data/.rubocop.yml +48 -0
- data/.travis.yml +6 -0
- data/Gemfile +16 -0
- data/LICENSE.txt +21 -0
- data/README.md +164 -0
- data/Rakefile +6 -0
- data/bin/console +8 -0
- data/bin/setup +8 -0
- data/clever_tap.gemspec +33 -0
- data/lib/clever_tap/client.rb +113 -0
- data/lib/clever_tap/config.rb +25 -0
- data/lib/clever_tap/entity.rb +87 -0
- data/lib/clever_tap/event.rb +30 -0
- data/lib/clever_tap/failed_response.rb +28 -0
- data/lib/clever_tap/profile.rb +7 -0
- data/lib/clever_tap/response.rb +34 -0
- data/lib/clever_tap/successful_response.rb +30 -0
- data/lib/clever_tap/uploader.rb +72 -0
- data/lib/clever_tap/version.rb +3 -0
- data/lib/clever_tap.rb +79 -0
- data/lib/clevertap-ruby.rb +1 -0
- data/spec/factories/profile.rb +36 -0
- data/spec/integrations/clever_tap_spec.rb +81 -0
- data/spec/rubocop_spec.rb +12 -0
- data/spec/shared/clever_tap_client.rb +13 -0
- data/spec/shared/entity.rb +105 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/units/clever_tap_client_spec.rb +277 -0
- data/spec/units/clever_tap_spec.rb +88 -0
- data/spec/units/event_spec.rb +43 -0
- data/spec/units/failed_response_spec.rb +31 -0
- data/spec/units/profile_spec.rb +29 -0
- data/spec/units/response_spec.rb +63 -0
- data/spec/units/successful_response_spec.rb +112 -0
- data/spec/units/uploader_spec.rb +129 -0
- data/spec/vcr_cassettes/CleverTap/uploading_a_many_profiles/when_only_some_are_valid/partially_succeds.yml +42 -0
- data/spec/vcr_cassettes/CleverTap/uploading_a_profile/when_is_invalid/fails.yml +41 -0
- data/spec/vcr_cassettes/CleverTap/uploading_a_profile/when_is_valid/succeed.yml +35 -0
- data/spec/vcr_cassettes/CleverTap/uploading_an_event/when_is_valid/succeed.yml +34 -0
- data/spec/vcr_cassettes/CleverTap_Client/_upload/when_upload_records_are_homogenous/and_invalid_records/calls_on_failed_upload_once.yml +38 -0
- data/spec/vcr_cassettes/CleverTap_Client/_upload/when_upload_records_are_homogenous/and_invalid_records/returns_an_array_with_one_failed_Response_object.yml +38 -0
- data/spec/vcr_cassettes/CleverTap_Client/_upload/when_upload_records_are_homogenous/and_valid_records/and_objects_do_not_fit_upload_limit_/calls_on_successful_upload_proc_twice.yml +67 -0
- data/spec/vcr_cassettes/CleverTap_Client/_upload/when_upload_records_are_homogenous/and_valid_records/and_objects_do_not_fit_upload_limit_/returns_an_array_with_two_successful_Response_objects.yml +67 -0
- data/spec/vcr_cassettes/CleverTap_Client/_upload/when_upload_records_are_homogenous/and_valid_records/and_objects_fit_upload_limit_/calls_on_successful_upload_proc_once.yml +36 -0
- data/spec/vcr_cassettes/CleverTap_Client/_upload/when_upload_records_are_homogenous/and_valid_records/and_objects_fit_upload_limit_/returns_an_array_with_one_successful_Response_object.yml +36 -0
- data/spec/vcr_cassettes/CleverTap_Uploader/_call/when_age_is_invalid/behaves_like_validation_failure/failed_to_upload_the_profiles.yml +48 -0
- data/spec/vcr_cassettes/CleverTap_Uploader/_call/when_education_status_is_invalid/behaves_like_validation_failure/failed_to_upload_the_profiles.yml +49 -0
- data/spec/vcr_cassettes/CleverTap_Uploader/_call/when_email_is_invalid/behaves_like_validation_failure/failed_to_upload_the_profiles.yml +48 -0
- data/spec/vcr_cassettes/CleverTap_Uploader/_call/when_employment_status_is_invalid/behaves_like_validation_failure/failed_to_upload_the_profiles.yml +48 -0
- data/spec/vcr_cassettes/CleverTap_Uploader/_call/when_marital_status_is_invalid/behaves_like_validation_failure/failed_to_upload_the_profiles.yml +48 -0
- data/spec/vcr_cassettes/CleverTap_Uploader/_call/when_phone_is_invalid/behaves_like_validation_failure/failed_to_upload_the_profiles.yml +48 -0
- data/spec/vcr_cassettes/CleverTap_Uploader/_call/when_the_creation_date_field_is_missing/behaves_like_validation_failure/failed_to_upload_the_profiles.yml +48 -0
- data/spec/vcr_cassettes/CleverTap_Uploader/_call/with_invalid_credentials/failed_to_upload_the_profiles.yml +36 -0
- data/spec/vcr_cassettes/CleverTap_Uploader/_call/with_valid_data/makes_successful_upload.yml +36 -0
- data/spec/vcr_config.rb +13 -0
- metadata +192 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: bc381afc5e3bb56358a3b82b80c5bd708869afd4bae69d9b372aac143972a6dd
|
4
|
+
data.tar.gz: c90a3514b5a31f292e5ecc7187a94741e2efe98e0671acc54d1851f48d98dd33
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 25a6f726cfb4f9621fcef18cfe450a9e8d5b8f5ba9b949ce8e863bd37fd1db0bfb18e5924a302857daa5ef7e2e1e95df2135142db7d30e784b285123d0415c1a
|
7
|
+
data.tar.gz: 2f21f704d1b73a781cdededa55d5870c2645146b32123baae43686db50bda9b1830976efe04ed5affd5994f12f867df14b12a8a23dcc573cd7686095468a280c
|
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color --format documentation
|
data/.rubocop.yml
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
################################## Layout #######################################
|
2
|
+
Layout/IndentHash:
|
3
|
+
EnforcedStyle: consistent
|
4
|
+
|
5
|
+
Layout/SpaceInLambdaLiteral:
|
6
|
+
EnforcedStyle: require_space
|
7
|
+
|
8
|
+
################################## Naming #######################################
|
9
|
+
Naming/FileName:
|
10
|
+
Exclude:
|
11
|
+
- 'lib/clevertap-ruby.rb'
|
12
|
+
|
13
|
+
################################## Style #######################################
|
14
|
+
|
15
|
+
Style/FormatString:
|
16
|
+
EnforcedStyle: format
|
17
|
+
|
18
|
+
Style/FrozenStringLiteralComment:
|
19
|
+
Enabled: false
|
20
|
+
|
21
|
+
Style/MultilineBlockChain:
|
22
|
+
Enabled: false
|
23
|
+
|
24
|
+
Style/EmptyCaseCondition:
|
25
|
+
Enabled: false
|
26
|
+
|
27
|
+
################################## Metrics #####################################
|
28
|
+
|
29
|
+
Metrics/AbcSize:
|
30
|
+
Max: 120
|
31
|
+
|
32
|
+
Metrics/BlockLength:
|
33
|
+
Enabled: false
|
34
|
+
|
35
|
+
Metrics/CyclomaticComplexity:
|
36
|
+
Max: 40
|
37
|
+
|
38
|
+
Metrics/LineLength:
|
39
|
+
Max: 140
|
40
|
+
|
41
|
+
Metrics/MethodLength:
|
42
|
+
Max: 70
|
43
|
+
|
44
|
+
Metrics/ModuleLength:
|
45
|
+
Max: 200
|
46
|
+
|
47
|
+
Metrics/PerceivedComplexity:
|
48
|
+
Max: 10
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2017 Tradeo team
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
clevertap-ruby
|
2
|
+
==============
|
3
|
+
|
4
|
+
Module providing access to the [CleverTap](https://clevertap.com/) API
|
5
|
+
|
6
|
+
## Install
|
7
|
+
Add to your Gemfile
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'clever_tap', github: 'tradeo/clevertap-ruby'
|
11
|
+
```
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
### Configure the client
|
16
|
+
|
17
|
+
Create an instance of `CleverTap` object:
|
18
|
+
__Available in v0.2.0 but will be depricated as of v1.0.0__
|
19
|
+
```ruby
|
20
|
+
CLEVER_TAP = CleverTap.new(account_id: '<your account ID>', passcode: '<your passcode>')
|
21
|
+
```
|
22
|
+
|
23
|
+
You can add configuration settings as parameters like above and/or using a block.
|
24
|
+
```ruby
|
25
|
+
CLEVER_TAP = CleverTap.new do |config|
|
26
|
+
config.account_id = '<your account ID>' # mandatory
|
27
|
+
config.passcode = '<your passcode>' # mandatory
|
28
|
+
config.identity_field = 'ID' # default value "identity"
|
29
|
+
|
30
|
+
config.configure_faraday do |faraday_config| # optional
|
31
|
+
faraday_config.adapter :httpclient # default adapter "net_http"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
```
|
35
|
+
|
36
|
+
__As of v0.3.0 the new way of setting up CleverTap is:__
|
37
|
+
```ruby
|
38
|
+
CleverTap.setup do |config|
|
39
|
+
# Default ID could be reset later
|
40
|
+
# case by case when uploading Profile/Event
|
41
|
+
config.identity_field = 'ID'
|
42
|
+
|
43
|
+
config.account_id = 'the-account-id'
|
44
|
+
config.account_passcode = 'the-passcode'
|
45
|
+
end
|
46
|
+
```
|
47
|
+
|
48
|
+
Then creating a shared instance to use trough the app (v0.3.0 and above)
|
49
|
+
```ruby
|
50
|
+
clevertap = CleverTap::Client.new
|
51
|
+
|
52
|
+
# You can add callbacks for successfull and failed calls as so:
|
53
|
+
clevertap.on_successful_upload do |response|
|
54
|
+
# log the response as response.to_s
|
55
|
+
# response is of type CleverTap::Response
|
56
|
+
# having methods `success` (true or false)
|
57
|
+
# and `failures` (empty if success == true)
|
58
|
+
# contains hash with errors returned from CleverTap endpoint
|
59
|
+
end
|
60
|
+
|
61
|
+
clevertap.on_failed_upload do |response|
|
62
|
+
...
|
63
|
+
end
|
64
|
+
```
|
65
|
+
|
66
|
+
### Upload a profile
|
67
|
+
|
68
|
+
`.upload_profile` accepts as a first argument an object responding to `#to_h` and `#[]`.
|
69
|
+
```ruby
|
70
|
+
profile = {
|
71
|
+
'identity' => '666',
|
72
|
+
'Name' => 'John Bravo'
|
73
|
+
}
|
74
|
+
|
75
|
+
client = CleverTap.new(account_id: '<your account ID>', passcode: '<your passcode>')
|
76
|
+
response = client.upload_profile(profile)
|
77
|
+
|
78
|
+
response.success # => true / false
|
79
|
+
response.status # => 'success' / 'partial' / 'fail'
|
80
|
+
response.errors # => [ { }, ...]
|
81
|
+
```
|
82
|
+
|
83
|
+
__Date field__ used as a time stamp is optional.
|
84
|
+
If it's missing the current time stamp will be send instead.
|
85
|
+
The value should respond to `.to_i` and return epoch time.
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
profile = CleverTap::Profile.new(
|
89
|
+
data: { 'ID' => 1, 'Name' => 'John Doe' }, # MANDATORY, expects to receive a hash containing the identity field specified in `CleverTap.setup`, or below
|
90
|
+
identity_field: 'field_name' | false, # optional
|
91
|
+
fbid: '34322423', # optional, facebook id, can replace original identity
|
92
|
+
gpid: '34322423', # optional, google plus id, can replace original identity
|
93
|
+
objectId: '0f5d5fff698245f1ac5f192c', # optional, uniq CleverTap identifier
|
94
|
+
timestamp_field: 'Created At', # optional, has to be present in the `data` hash, else it throws
|
95
|
+
custom_timestamp: 1468308340 # optional, custom time stamp if user needs to set a particular timestamp, not presented in the object, takes precedence
|
96
|
+
)
|
97
|
+
|
98
|
+
clever_tap.upload(profile)
|
99
|
+
clever_tap.upload(profiles) # works as well with [CleverTap::Profile]
|
100
|
+
```
|
101
|
+
|
102
|
+
### Upload an event
|
103
|
+
|
104
|
+
__Available in v0.2.0 but will be depricated as of v1.0.0__
|
105
|
+
`.upload_event` accepts as a first argument an object responding to `#to_h` and `#[]` and a second parameter keyword argument `name: <name>`.
|
106
|
+
```ruby
|
107
|
+
event = {
|
108
|
+
'identity' => '666',
|
109
|
+
'Name' => 'Jonh Bravo',
|
110
|
+
'Cookie ID' => '424242'
|
111
|
+
}
|
112
|
+
|
113
|
+
client = CleverTap.new(account_id: '<your account ID>', passcode: '<your passcode>')
|
114
|
+
response = client.upload_event(event, name: 'registration')
|
115
|
+
|
116
|
+
response.success # => true / false
|
117
|
+
response.status # => 'success' / 'partial' / 'fail'
|
118
|
+
response.errors # => [ { }, ...]
|
119
|
+
```
|
120
|
+
__As of v0.3.0 the new way of uploading Event(s) is:__
|
121
|
+
```ruby
|
122
|
+
event = CleverTap::Event.new(
|
123
|
+
data: { 'ID' => 1, 'Field' => 'Value' } # MANDATORY, expects to receive a hash containing the identity field specified in `CleverTap.setup`, or below
|
124
|
+
name: 'Event Name', # MANDATORY
|
125
|
+
identity_field: 'field_name' | false, # optional, has to be present in the `data` hash, else it throws
|
126
|
+
fbid: '34322423', # optional, facebook id, can replace original identity
|
127
|
+
gpid: '34322423', # optional, google plus id, can replace original identity
|
128
|
+
objectId: '0f5d5fff698245f1ac5f192c', # optional, uniq CleverTap identifier, can replace identity
|
129
|
+
timestamp_field: 'Open Time', # optional, has to be present in the `data` hash, else it throws
|
130
|
+
custom_timestamp: 1468308340, # optional, custom time stamp if user needs to set a particular timestamp, not presented in the object
|
131
|
+
)
|
132
|
+
|
133
|
+
clevertap.upload(event)
|
134
|
+
clevertap.upload(events) # Works as well with [CleverTap::Event]
|
135
|
+
```
|
136
|
+
|
137
|
+
### Send requests as *Dry Run*
|
138
|
+
|
139
|
+
Passing parameter `dry_run: true` to upload methods you can test the data submitted for a validation errors.
|
140
|
+
The record won't be persisted.
|
141
|
+
|
142
|
+
__Available in v0.2.0 but will be depricated as of v1.0.0__
|
143
|
+
```ruby
|
144
|
+
client = CleverTap.new(account_id: '<your account ID>', passcode: '<your passcode>')
|
145
|
+
client.upload_profile(profile, dry_run: true)
|
146
|
+
```
|
147
|
+
|
148
|
+
__As of v0.3.0 the new way of using a Dry Run is :__
|
149
|
+
```ruby
|
150
|
+
clevertap.upload(event, dry_run: true)
|
151
|
+
```
|
152
|
+
|
153
|
+
### Handle the response
|
154
|
+
|
155
|
+
The CleverTap response object has the following interface:
|
156
|
+
1. `#status` - __"success"__ / __"partial"__ / __"fail"__
|
157
|
+
2. `#success` - `true` when is the status is __"success"__ and `false` otherwise
|
158
|
+
3. `#errors` - it's actually `unprocessed` synonym returned when the request is successful(code 200), but will contains the failed records even when the code is different than 200.
|
159
|
+
4. `#code` - codes from 200-500. When it's __200__, errors can contains a validation
|
160
|
+
error codes. When it's __-1__ the error is custom and more info can be found in the message. More info about the codes can find [CleverTap Docs](https://support.clevertap.com/docs/api/working-with-user-profiles.html#uploading-user-profiles)
|
161
|
+
|
162
|
+
|
163
|
+
|
164
|
+
#### __More documentation you can find in the specs__
|
data/Rakefile
ADDED
data/bin/console
ADDED
data/bin/setup
ADDED
data/clever_tap.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
$LOAD_PATH.push File.expand_path('../lib', __FILE__)
|
2
|
+
|
3
|
+
require 'clever_tap/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'clever_tap_dubit'
|
7
|
+
spec.version = CleverTap::VERSION
|
8
|
+
spec.authors = ['Kamen Kanev', 'Svetoslav Blyahov']
|
9
|
+
spec.email = ['opensource@tradeo.com']
|
10
|
+
spec.license = 'MIT'
|
11
|
+
spec.homepage = 'https://github.com/tradeo/clevertap-ruby'
|
12
|
+
spec.summary = 'CleverTap API client'
|
13
|
+
spec.description = 'Gem providing easy access to the CleverTap API'
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(spec)/}) }
|
16
|
+
spec.test_files = Dir['spec/**/*']
|
17
|
+
spec.require_paths = ['lib']
|
18
|
+
|
19
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
20
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
21
|
+
if spec.respond_to?(:metadata)
|
22
|
+
spec.metadata['allowed_push_host'] = "https://rubygems.org"
|
23
|
+
else
|
24
|
+
raise 'RubyGems 2.0 or newer is required to protect against ' \
|
25
|
+
'public gem pushes.'
|
26
|
+
end
|
27
|
+
|
28
|
+
spec.add_dependency 'faraday', '~> 1.0'
|
29
|
+
spec.add_dependency 'json'
|
30
|
+
|
31
|
+
spec.add_development_dependency 'bundler', '> 1.14'
|
32
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
33
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
|
3
|
+
class CleverTap
|
4
|
+
class NotConsistentArrayError < RuntimeError
|
5
|
+
def message
|
6
|
+
'Some elements in the collection are of different type than the others'
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class Client
|
11
|
+
DOMAIN = 'https://api.clevertap.com'.freeze
|
12
|
+
API_VERSION = 1
|
13
|
+
HTTP_PATH = 'upload'.freeze
|
14
|
+
DEFAULT_SUCCESS = proc { |r| r.to_s }
|
15
|
+
DEFAULT_FAILURE = proc { |r| r.to_s }
|
16
|
+
|
17
|
+
ACCOUNT_HEADER = 'X-CleverTap-Account-Id'.freeze
|
18
|
+
PASSCODE_HEADER = 'X-CleverTap-Passcode'.freeze
|
19
|
+
|
20
|
+
attr_accessor :account_id, :passcode, :configure, :on_success, :on_failure
|
21
|
+
|
22
|
+
def initialize(account_id = nil, passcode = nil, &configure)
|
23
|
+
@account_id = assign_account_id(account_id)
|
24
|
+
@passcode = assign_passcode(passcode)
|
25
|
+
@configure = configure || proc {}
|
26
|
+
@on_success = DEFAULT_SUCCESS
|
27
|
+
@on_failure = DEFAULT_FAILURE
|
28
|
+
end
|
29
|
+
|
30
|
+
def connection
|
31
|
+
# TODO: pass the config to a block
|
32
|
+
@connection ||= Faraday.new("#{DOMAIN}/#{API_VERSION}") do |config|
|
33
|
+
configure.call(config)
|
34
|
+
|
35
|
+
# NOTE: set adapter only if there isn't one set
|
36
|
+
config.adapter :net_http if config.builder.adapter.nil?
|
37
|
+
|
38
|
+
config.headers['Content-Type'] = 'application/json'
|
39
|
+
config.headers[ACCOUNT_HEADER] = account_id
|
40
|
+
config.headers[PASSCODE_HEADER] = passcode
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def post(*args, &block)
|
45
|
+
connection.post(*args, &block)
|
46
|
+
end
|
47
|
+
|
48
|
+
def get(*args, &block)
|
49
|
+
connection.get(*args, &block)
|
50
|
+
end
|
51
|
+
|
52
|
+
def on_successful_upload(&block)
|
53
|
+
@on_success = block
|
54
|
+
end
|
55
|
+
|
56
|
+
def on_failed_upload(&block)
|
57
|
+
@on_failure = block
|
58
|
+
end
|
59
|
+
|
60
|
+
def upload(records, dry_run: 0)
|
61
|
+
payload = ensure_array(records)
|
62
|
+
entity = determine_type(payload)
|
63
|
+
all_responses = []
|
64
|
+
batched_upload(entity, payload, dry_run) do |response|
|
65
|
+
all_responses << response
|
66
|
+
end
|
67
|
+
|
68
|
+
all_responses
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def batched_upload(entity, payload, dry_run)
|
74
|
+
payload.each_slice(entity.upload_limit) do |group|
|
75
|
+
response = post(HTTP_PATH, request_body(group)) do |request|
|
76
|
+
request.params.merge!(dryRun: dry_run)
|
77
|
+
end
|
78
|
+
|
79
|
+
clevertap_response = Response.new(response)
|
80
|
+
|
81
|
+
if clevertap_response.success
|
82
|
+
@on_success.call(clevertap_response)
|
83
|
+
else
|
84
|
+
@on_failure.call(clevertap_response)
|
85
|
+
end
|
86
|
+
|
87
|
+
yield(clevertap_response) if block_given?
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def request_body(records)
|
92
|
+
{ 'd' => records.map(&:to_h) }.to_json
|
93
|
+
end
|
94
|
+
|
95
|
+
def determine_type(records)
|
96
|
+
types = records.map(&:class).uniq
|
97
|
+
raise NotConsistentArrayError unless types.one?
|
98
|
+
types.first
|
99
|
+
end
|
100
|
+
|
101
|
+
def ensure_array(records)
|
102
|
+
Array(records)
|
103
|
+
end
|
104
|
+
|
105
|
+
def assign_account_id(account_id)
|
106
|
+
account_id || CleverTap.account_id || raise('Clever Tap `account_id` missing')
|
107
|
+
end
|
108
|
+
|
109
|
+
def assign_passcode(passcode)
|
110
|
+
passcode || CleverTap.account_passcode || raise('Clever Tap `passcode` missing')
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
class CleverTap
|
2
|
+
# CleverTap instance's config store object
|
3
|
+
class Config
|
4
|
+
DEFAULT_IDENTITY_FIELD = 'identity'.freeze
|
5
|
+
|
6
|
+
attr_accessor :account_id, :passcode, :identity_field
|
7
|
+
|
8
|
+
def initialize(**config)
|
9
|
+
@account_id = config[:account_id]
|
10
|
+
@passcode = config[:passcode]
|
11
|
+
@identity_field = config[:identity_field] || DEFAULT_IDENTITY_FIELD
|
12
|
+
@configure_faraday = config[:configure_faraday]
|
13
|
+
end
|
14
|
+
|
15
|
+
# NOTE: reader or writer depending if the block is given
|
16
|
+
def configure_faraday(&block)
|
17
|
+
block ? @configure_faraday = block : @configure_faraday
|
18
|
+
end
|
19
|
+
|
20
|
+
def validate
|
21
|
+
raise 'Missing authentication parameter `account_id`' unless account_id
|
22
|
+
raise 'Missing authentication parameter `passcode`' unless passcode
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
class CleverTap
|
2
|
+
class NoDataError < RuntimeError
|
3
|
+
def message
|
4
|
+
'No `data` param provided for Event'
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class MissingIdentityError < RuntimeError
|
9
|
+
def message
|
10
|
+
"Couldn'n find `identity` in CleverTap.config or `data`"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class Entity
|
15
|
+
ALLOWED_IDENTITIES = %w(objectId FBID GPID).freeze
|
16
|
+
IDENTITY_STRING = 'identity'.freeze
|
17
|
+
TIMESTAMP_STRING = 'ts'.freeze
|
18
|
+
TYPE_KEY_STRING = 'type'.freeze
|
19
|
+
UPLOAD_LIMIT = 'Needs child class value'.freeze
|
20
|
+
TYPE_VALUE_STRING = 'Needs child class value'.freeze
|
21
|
+
|
22
|
+
class << self
|
23
|
+
def upload_limit
|
24
|
+
self::UPLOAD_LIMIT
|
25
|
+
end
|
26
|
+
|
27
|
+
def all_same_type?(items)
|
28
|
+
items.all? { |i| i.class == self }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize(**args)
|
33
|
+
@data = args[:data]
|
34
|
+
@identity = choose_identity(args)
|
35
|
+
@timestamp = choose_timestamp(args)
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_h
|
39
|
+
put_identity_pair
|
40
|
+
.merge(put_timestamp_pair)
|
41
|
+
.merge(put_type_pair)
|
42
|
+
.merge(put_data)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def put_identity_pair
|
48
|
+
raise NoDataError if @data.to_h.empty?
|
49
|
+
raise MissingIdentityError if @identity == '' || @data[@identity].nil?
|
50
|
+
return { @identity => @data[@identity].to_s } if allowed?(@identity)
|
51
|
+
{ IDENTITY_STRING => @data[@identity].to_s }
|
52
|
+
end
|
53
|
+
|
54
|
+
def put_timestamp_pair
|
55
|
+
return {} unless @timestamp
|
56
|
+
{ TIMESTAMP_STRING => @timestamp }
|
57
|
+
end
|
58
|
+
|
59
|
+
def put_type_pair
|
60
|
+
{ TYPE_KEY_STRING => self.class::TYPE_VALUE_STRING }
|
61
|
+
end
|
62
|
+
|
63
|
+
def put_data
|
64
|
+
raise NoDataError if @data.to_h.empty?
|
65
|
+
@data.delete(@identity) if allowed?(@identity)
|
66
|
+
{
|
67
|
+
self.class::DATA_STRING => @data
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
def choose_identity(args)
|
72
|
+
identity = args[:identity].to_s
|
73
|
+
|
74
|
+
return identity if allowed?(identity) && @data.to_h.key?(identity)
|
75
|
+
CleverTap.identity_field.to_s
|
76
|
+
end
|
77
|
+
|
78
|
+
def choose_timestamp(args)
|
79
|
+
return args[:custom_timestamp].to_i if args[:custom_timestamp]
|
80
|
+
return @data.delete(args[:timestamp_field].to_s).to_i if args[:timestamp_field]
|
81
|
+
end
|
82
|
+
|
83
|
+
def allowed?(identity)
|
84
|
+
ALLOWED_IDENTITIES.include?(identity)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class CleverTap
|
2
|
+
class MissingEventNameError < RuntimeError
|
3
|
+
def message
|
4
|
+
"Couldn't find `name:` with value in Event#new(options)"
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class Event < Entity
|
9
|
+
DATA_STRING = 'evtData'.freeze
|
10
|
+
EVENT_NAME_STRING = 'evtName'.freeze
|
11
|
+
TYPE_VALUE_STRING = 'event'.freeze
|
12
|
+
UPLOAD_LIMIT = 1000
|
13
|
+
|
14
|
+
def initialize(**args)
|
15
|
+
super(**args)
|
16
|
+
@name = args[:name]
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_h
|
20
|
+
super.merge(put_event_name_pair)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def put_event_name_pair
|
26
|
+
raise MissingEventNameError if @name.nil?
|
27
|
+
{ EVENT_NAME_STRING => @name }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class CleverTap
|
2
|
+
# Introduce unified interface as the `SuccessfulResponse`
|
3
|
+
class FailedResponse
|
4
|
+
FAIL_STATUS = 'fail'.freeze
|
5
|
+
|
6
|
+
attr_reader :records, :message, :code
|
7
|
+
|
8
|
+
def initialize(records:, message:, code: -1)
|
9
|
+
@records = records
|
10
|
+
@message = message
|
11
|
+
@code = code
|
12
|
+
end
|
13
|
+
|
14
|
+
def status
|
15
|
+
FAIL_STATUS
|
16
|
+
end
|
17
|
+
|
18
|
+
def success
|
19
|
+
false
|
20
|
+
end
|
21
|
+
|
22
|
+
def errors
|
23
|
+
records.map do |record|
|
24
|
+
{ 'status' => FAIL_STATUS, 'code' => code, 'error' => message, 'record' => record }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class CleverTap
|
2
|
+
class Response
|
3
|
+
attr_accessor :response, :success, :failures
|
4
|
+
|
5
|
+
def initialize(response)
|
6
|
+
@response = response.success? ? JSON.parse(response.body) : extract_json_body(response)
|
7
|
+
process_response
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def extract_json_body(response)
|
13
|
+
return JSON.parse(response.body)
|
14
|
+
rescue JSON::ParserError, TypeError
|
15
|
+
return { resp_string: response.body.to_s }.to_json
|
16
|
+
end
|
17
|
+
|
18
|
+
def process_response
|
19
|
+
return process_success if response['status'] == 'success'
|
20
|
+
@success = false
|
21
|
+
@failures = [response]
|
22
|
+
end
|
23
|
+
|
24
|
+
def process_success
|
25
|
+
if response['unprocessed'].to_a.empty?
|
26
|
+
@success = true
|
27
|
+
@failures = []
|
28
|
+
else
|
29
|
+
@success = false
|
30
|
+
@failures = response['unprocessed']
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|