clever_tap 0.3.0
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.rb +79 -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 +28 -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/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 +279 -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 +48 -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 +199 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8765e8268c637623be90102da19286b9b6f38ad8
|
4
|
+
data.tar.gz: 8e34ed9c22626404a4e618b11d916b377e1780bf
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a4e852922b234dfda8bd0056ef26e1c0ad34bc39209812b70e0aa8f175341cd73062591e86c0794da4d817947a241f527c8b21255bf273249fb038e500f2298e
|
7
|
+
data.tar.gz: 44cfa234e3fb180d807563f25b5044df182858a6df7a9f44bd548bea6853c7140410e147d99efc910139341b5d927ed1e62f311f4c9406ae6c262543fbe0e042
|
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
@@ -0,0 +1,16 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
gem 'faraday', '>= 0.8', '~> 0.14.0'
|
4
|
+
gem 'json'
|
5
|
+
|
6
|
+
group :test do
|
7
|
+
gem 'rspec', '>= 3.3'
|
8
|
+
gem 'rubocop', '~> 0.52.0'
|
9
|
+
gem 'vcr'
|
10
|
+
end
|
11
|
+
|
12
|
+
group :test, :development do
|
13
|
+
gem 'pry-byebug'
|
14
|
+
end
|
15
|
+
|
16
|
+
gemspec
|
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'
|
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', '>= 0.8', '<= 0.14.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
|
data/lib/clever_tap.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
require 'clever_tap/config'
|
4
|
+
require 'clever_tap/client'
|
5
|
+
require 'clever_tap/entity'
|
6
|
+
require 'clever_tap/event'
|
7
|
+
require 'clever_tap/profile'
|
8
|
+
require 'clever_tap/uploader'
|
9
|
+
require 'clever_tap/response'
|
10
|
+
require 'clever_tap/successful_response'
|
11
|
+
require 'clever_tap/failed_response'
|
12
|
+
|
13
|
+
# the main module of the system
|
14
|
+
class CleverTap
|
15
|
+
attr_reader :config
|
16
|
+
|
17
|
+
class << self
|
18
|
+
# Never instantiated. Variables are stored in the singleton_class.
|
19
|
+
private_class_method :new
|
20
|
+
|
21
|
+
attr_accessor :identity_field
|
22
|
+
attr_accessor :account_id
|
23
|
+
attr_accessor :account_passcode
|
24
|
+
|
25
|
+
def setup
|
26
|
+
yield(self)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(**params)
|
31
|
+
@config = Config.new(params)
|
32
|
+
yield(@config) if block_given?
|
33
|
+
|
34
|
+
@config.validate
|
35
|
+
@config.freeze
|
36
|
+
end
|
37
|
+
|
38
|
+
def client
|
39
|
+
@client ||= Client.new(config.account_id, config.passcode, &config.configure_faraday)
|
40
|
+
end
|
41
|
+
|
42
|
+
def upload_events(events, name:, **rest)
|
43
|
+
options = rest.merge(event_name: name, identity_field: config.identity_field)
|
44
|
+
|
45
|
+
response = Uploader.new(events, options).call(client)
|
46
|
+
|
47
|
+
normalize_response(response, records: events)
|
48
|
+
rescue Faraday::Error::TimeoutError, Faraday::Error::ClientError => e
|
49
|
+
FailedResponse.new(records: events, message: e.message)
|
50
|
+
end
|
51
|
+
|
52
|
+
def upload_event(event, **options)
|
53
|
+
upload_events([event], options)
|
54
|
+
end
|
55
|
+
|
56
|
+
def upload_profiles(profiles, **options)
|
57
|
+
options = options.merge(identity_field: config.identity_field)
|
58
|
+
response = Uploader.new(profiles, **options).call(client)
|
59
|
+
|
60
|
+
normalize_response(response, records: profiles)
|
61
|
+
rescue Faraday::Error::TimeoutError, Faraday::Error::ClientError => e
|
62
|
+
FailedResponse.new(records: profiles, message: e.message)
|
63
|
+
end
|
64
|
+
|
65
|
+
def upload_profile(profile, **options)
|
66
|
+
upload_profiles([profile], options)
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def normalize_response(response, records:)
|
72
|
+
# TODO: handle JSON::ParserError
|
73
|
+
if response.success?
|
74
|
+
SuccessfulResponse.new(JSON.parse(response.body))
|
75
|
+
else
|
76
|
+
FailedResponse.new(records: records, code: response.status, message: response.body)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
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.handlers.empty?
|
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
|