basecrm 1.0.0 → 1.1.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 +4 -4
- data/README.md +38 -16
- data/lib/basecrm.rb +53 -39
- data/lib/basecrm/http_client.rb +9 -9
- data/lib/basecrm/models/sync_meta.rb +29 -0
- data/lib/basecrm/models/sync_queue.rb +15 -0
- data/lib/basecrm/models/sync_session.rb +11 -0
- data/lib/basecrm/services/sync_service.rb +94 -0
- data/lib/basecrm/sync.rb +82 -0
- data/lib/basecrm/version.rb +1 -1
- data/spec/basecrm/sync_spec.rb +94 -0
- data/spec/models/sync_meta_spec.rb +59 -0
- data/spec/services/sync_service_spec.rb +283 -0
- metadata +14 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9b39240e639171c284af2016c897affee25c599f
|
4
|
+
data.tar.gz: 4b67891d5ff434ee7e0394fca959b6e06d2d70b0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 339f88a6b215e7c3c717d3200c6fa753dfdba54aba7418c72cf9dc5d69893fd4b004ac456ca97d1c3e4f5aa947556a5965cc81fe59c21d3eb87176b57355d0a7
|
7
|
+
data.tar.gz: a535ff335c8c6972c0a0f369e358cd90bcbd257b23003bc7033aea5b553a53a579dd6eb069310e38bc4587faeb60aed0ebd34a662af7e5956f4c8b5edaa87dec
|
data/README.md
CHANGED
@@ -9,7 +9,7 @@ Make sure you have [rubygems](https://rubygems.org) installed.
|
|
9
9
|
The gem is available via Rubygems. To install, use the following command:
|
10
10
|
|
11
11
|
```ruby
|
12
|
-
|
12
|
+
gem install basecrm
|
13
13
|
```
|
14
14
|
|
15
15
|
If you use Bundler, put the line below in your Gemfile:
|
@@ -75,7 +75,7 @@ client.deals.all.map { |deal| deal.name } # => Array<String>
|
|
75
75
|
To retrieve list of resources and use filtering you will call `#where` method:
|
76
76
|
|
77
77
|
```ruby
|
78
|
-
client = BaseCRM::Client.new(
|
78
|
+
client = BaseCRM::Client.new(access_token: "<YOUR_PERSONAL_ACCESS_TOKEN>")
|
79
79
|
client.deals.where(organization_id: google.id, hot: true) # => Array<BaseCRM::Deal>
|
80
80
|
```
|
81
81
|
|
@@ -102,7 +102,7 @@ client.deals.update(deal) # => BaseCRM::Deal
|
|
102
102
|
To destroy a resource use `#destroy` method:
|
103
103
|
|
104
104
|
```ruby
|
105
|
-
client = BaseCRM::Client.new(access_token: "<
|
105
|
+
client = BaseCRM::Client.new(access_token: "<YOUR_PERSONAL_ACCESS_TOKEN>")
|
106
106
|
client.deals.destroy(id) # => true
|
107
107
|
```
|
108
108
|
|
@@ -120,6 +120,28 @@ lead.website = "http://www.designservices.com"
|
|
120
120
|
client.leads.update(lead)
|
121
121
|
```
|
122
122
|
|
123
|
+
## Sync API
|
124
|
+
|
125
|
+
The following sample code shows how to perform a full synchronization flow using high-level wrapper.
|
126
|
+
|
127
|
+
First of all you need an instance of `BaseCRM::Client`. High-level `BaseCRM::Sync` wrapper is using `BaseCRM::SyncService` to interact with the Sync API.
|
128
|
+
In addition to the client instance, you must provide a device’s UUID within `device_uuid` parameter. The device’s UUID must not change between synchronization sessions, otherwise the sync service will not recognize the device and will send all the data again.
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
client = BaseCRM::Client.new(access_token: "<YOUR_PERSONAL_ACCESS_TOKEN>")
|
132
|
+
sync = BaseCRM::Sync.new(client: client, device_uuid: "<YOUR_DEVICES_UUID>")
|
133
|
+
```
|
134
|
+
|
135
|
+
Now all you have to do is to call `#fetch` method and pass a block that you might use to store fetched data to a database.
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
sync.fetch do |sync_meta, resource|
|
139
|
+
DB.send(sync_meta.event_type, resource) ? sync_meta.ack : sync_meta.nack
|
140
|
+
end
|
141
|
+
```
|
142
|
+
|
143
|
+
Notice that you must call either `#ack` or `#nack` method.
|
144
|
+
|
123
145
|
## Resources and actions
|
124
146
|
|
125
147
|
Documentation for every action can be found in corresponding service files under `lib/basecrm/services` directory.
|
@@ -127,7 +149,7 @@ Documentation for every action can be found in corresponding service files under
|
|
127
149
|
### Account
|
128
150
|
|
129
151
|
```ruby
|
130
|
-
client = BaseCRM::Client.new(access_token: "<
|
152
|
+
client = BaseCRM::Client.new(access_token: "<YOUR_PERSONAL_ACCESS_TOKEN>")
|
131
153
|
client.accounts # => BaseCRM::AccountsService
|
132
154
|
```
|
133
155
|
|
@@ -137,7 +159,7 @@ Actions:
|
|
137
159
|
### AssociatedContact
|
138
160
|
|
139
161
|
```ruby
|
140
|
-
client = BaseCRM::Client.new(access_token: "<
|
162
|
+
client = BaseCRM::Client.new(access_token: "<YOUR_PERSONAL_ACCESS_TOKEN>")
|
141
163
|
client.associated_contacts # => BaseCRM::AssociatedContactsService
|
142
164
|
```
|
143
165
|
|
@@ -149,7 +171,7 @@ Actions:
|
|
149
171
|
### Contact
|
150
172
|
|
151
173
|
```ruby
|
152
|
-
client = BaseCRM::Client.new(access_token: "<
|
174
|
+
client = BaseCRM::Client.new(access_token: "<YOUR_PERSONAL_ACCESS_TOKEN>")
|
153
175
|
client.contacts # => BaseCRM::ContactsService
|
154
176
|
```
|
155
177
|
|
@@ -163,7 +185,7 @@ Actions:
|
|
163
185
|
### Deal
|
164
186
|
|
165
187
|
```ruby
|
166
|
-
client = BaseCRM::Client.new(access_token: "<
|
188
|
+
client = BaseCRM::Client.new(access_token: "<YOUR_PERSONAL_ACCESS_TOKEN>")
|
167
189
|
client.deals # => BaseCRM::DealsService
|
168
190
|
```
|
169
191
|
|
@@ -177,7 +199,7 @@ Actions:
|
|
177
199
|
### Lead
|
178
200
|
|
179
201
|
```ruby
|
180
|
-
client = BaseCRM::Client.new(access_token: "<
|
202
|
+
client = BaseCRM::Client.new(access_token: "<YOUR_PERSONAL_ACCESS_TOKEN>")
|
181
203
|
client.leads # => BaseCRM::LeadsService
|
182
204
|
```
|
183
205
|
|
@@ -191,7 +213,7 @@ Actions:
|
|
191
213
|
### LossReason
|
192
214
|
|
193
215
|
```ruby
|
194
|
-
client = BaseCRM::Client.new(access_token: "<
|
216
|
+
client = BaseCRM::Client.new(access_token: "<YOUR_PERSONAL_ACCESS_TOKEN>")
|
195
217
|
client.loss_reasons # => BaseCRM::LossReasonsService
|
196
218
|
```
|
197
219
|
|
@@ -205,7 +227,7 @@ Actions:
|
|
205
227
|
### Note
|
206
228
|
|
207
229
|
```ruby
|
208
|
-
client = BaseCRM::Client.new(access_token: "<
|
230
|
+
client = BaseCRM::Client.new(access_token: "<YOUR_PERSONAL_ACCESS_TOKEN>")
|
209
231
|
client.notes # => BaseCRM::NotesService
|
210
232
|
```
|
211
233
|
|
@@ -219,7 +241,7 @@ Actions:
|
|
219
241
|
### Pipeline
|
220
242
|
|
221
243
|
```ruby
|
222
|
-
client = BaseCRM::Client.new(access_token: "<
|
244
|
+
client = BaseCRM::Client.new(access_token: "<YOUR_PERSONAL_ACCESS_TOKEN>")
|
223
245
|
client.pipelines # => BaseCRM::PipelinesService
|
224
246
|
```
|
225
247
|
|
@@ -229,7 +251,7 @@ Actions:
|
|
229
251
|
### Source
|
230
252
|
|
231
253
|
```ruby
|
232
|
-
client = BaseCRM::Client.new(access_token: "<
|
254
|
+
client = BaseCRM::Client.new(access_token: "<YOUR_PERSONAL_ACCESS_TOKEN>")
|
233
255
|
client.sources # => BaseCRM::SourcesService
|
234
256
|
```
|
235
257
|
|
@@ -243,7 +265,7 @@ Actions:
|
|
243
265
|
### Stage
|
244
266
|
|
245
267
|
```ruby
|
246
|
-
client = BaseCRM::Client.new(access_token: "<
|
268
|
+
client = BaseCRM::Client.new(access_token: "<YOUR_PERSONAL_ACCESS_TOKEN>")
|
247
269
|
client.stages # => BaseCRM::StagesService
|
248
270
|
```
|
249
271
|
|
@@ -253,7 +275,7 @@ Actions:
|
|
253
275
|
### Tag
|
254
276
|
|
255
277
|
```ruby
|
256
|
-
client = BaseCRM::Client.new(access_token: "<
|
278
|
+
client = BaseCRM::Client.new(access_token: "<YOUR_PERSONAL_ACCESS_TOKEN>")
|
257
279
|
client.tags # => BaseCRM::TagsService
|
258
280
|
```
|
259
281
|
|
@@ -267,7 +289,7 @@ Actions:
|
|
267
289
|
### Task
|
268
290
|
|
269
291
|
```ruby
|
270
|
-
client = BaseCRM::Client.new(access_token: "<
|
292
|
+
client = BaseCRM::Client.new(access_token: "<YOUR_PERSONAL_ACCESS_TOKEN>")
|
271
293
|
client.tasks # => BaseCRM::TasksService
|
272
294
|
```
|
273
295
|
|
@@ -281,7 +303,7 @@ Actions:
|
|
281
303
|
### User
|
282
304
|
|
283
305
|
```ruby
|
284
|
-
client = BaseCRM::Client.new(access_token: "<
|
306
|
+
client = BaseCRM::Client.new(access_token: "<YOUR_PERSONAL_ACCESS_TOKEN>")
|
285
307
|
client.users # => BaseCRM::UsersService
|
286
308
|
```
|
287
309
|
|
data/lib/basecrm.rb
CHANGED
@@ -22,6 +22,9 @@ require 'basecrm/models/stage'
|
|
22
22
|
require 'basecrm/models/tag'
|
23
23
|
require 'basecrm/models/task'
|
24
24
|
require 'basecrm/models/user'
|
25
|
+
require 'basecrm/models/sync_queue'
|
26
|
+
require 'basecrm/models/sync_session'
|
27
|
+
require 'basecrm/models/sync_meta'
|
25
28
|
|
26
29
|
require 'basecrm/paginated_resource'
|
27
30
|
require 'basecrm/services/accounts_service'
|
@@ -37,6 +40,9 @@ require 'basecrm/services/stages_service'
|
|
37
40
|
require 'basecrm/services/tags_service'
|
38
41
|
require 'basecrm/services/tasks_service'
|
39
42
|
require 'basecrm/services/users_service'
|
43
|
+
require 'basecrm/services/sync_service'
|
44
|
+
|
45
|
+
require 'basecrm/sync'
|
40
46
|
|
41
47
|
module BaseCRM
|
42
48
|
class Client
|
@@ -66,121 +72,129 @@ module BaseCRM
|
|
66
72
|
@http_client = HttpClient.new(@config)
|
67
73
|
end
|
68
74
|
|
69
|
-
# Access all Accounts related actions.
|
70
|
-
# @see AccountsService
|
75
|
+
# Access all Accounts related actions.
|
76
|
+
# @see AccountsService
|
71
77
|
# @see Account
|
72
|
-
#
|
78
|
+
#
|
73
79
|
# @return [AccountsService] Service object for resources.
|
74
80
|
def accounts
|
75
81
|
@accounts ||= AccountsService.new(@http_client)
|
76
82
|
end
|
77
83
|
|
78
|
-
# Access all AssociatedContacts related actions.
|
79
|
-
# @see AssociatedContactsService
|
84
|
+
# Access all AssociatedContacts related actions.
|
85
|
+
# @see AssociatedContactsService
|
80
86
|
# @see AssociatedContact
|
81
|
-
#
|
87
|
+
#
|
82
88
|
# @return [AssociatedContactsService] Service object for resources.
|
83
89
|
def associated_contacts
|
84
90
|
@associated_contacts ||= AssociatedContactsService.new(@http_client)
|
85
91
|
end
|
86
92
|
|
87
|
-
# Access all Contacts related actions.
|
88
|
-
# @see ContactsService
|
93
|
+
# Access all Contacts related actions.
|
94
|
+
# @see ContactsService
|
89
95
|
# @see Contact
|
90
|
-
#
|
96
|
+
#
|
91
97
|
# @return [ContactsService] Service object for resources.
|
92
98
|
def contacts
|
93
99
|
@contacts ||= ContactsService.new(@http_client)
|
94
100
|
end
|
95
101
|
|
96
|
-
# Access all Deals related actions.
|
97
|
-
# @see DealsService
|
102
|
+
# Access all Deals related actions.
|
103
|
+
# @see DealsService
|
98
104
|
# @see Deal
|
99
|
-
#
|
105
|
+
#
|
100
106
|
# @return [DealsService] Service object for resources.
|
101
107
|
def deals
|
102
108
|
@deals ||= DealsService.new(@http_client)
|
103
109
|
end
|
104
110
|
|
105
|
-
# Access all Leads related actions.
|
106
|
-
# @see LeadsService
|
111
|
+
# Access all Leads related actions.
|
112
|
+
# @see LeadsService
|
107
113
|
# @see Lead
|
108
|
-
#
|
114
|
+
#
|
109
115
|
# @return [LeadsService] Service object for resources.
|
110
116
|
def leads
|
111
117
|
@leads ||= LeadsService.new(@http_client)
|
112
118
|
end
|
113
119
|
|
114
|
-
# Access all LossReasons related actions.
|
115
|
-
# @see LossReasonsService
|
120
|
+
# Access all LossReasons related actions.
|
121
|
+
# @see LossReasonsService
|
116
122
|
# @see LossReason
|
117
|
-
#
|
123
|
+
#
|
118
124
|
# @return [LossReasonsService] Service object for resources.
|
119
125
|
def loss_reasons
|
120
126
|
@loss_reasons ||= LossReasonsService.new(@http_client)
|
121
127
|
end
|
122
128
|
|
123
|
-
# Access all Notes related actions.
|
124
|
-
# @see NotesService
|
129
|
+
# Access all Notes related actions.
|
130
|
+
# @see NotesService
|
125
131
|
# @see Note
|
126
|
-
#
|
132
|
+
#
|
127
133
|
# @return [NotesService] Service object for resources.
|
128
134
|
def notes
|
129
135
|
@notes ||= NotesService.new(@http_client)
|
130
136
|
end
|
131
137
|
|
132
|
-
# Access all Pipelines related actions.
|
133
|
-
# @see PipelinesService
|
138
|
+
# Access all Pipelines related actions.
|
139
|
+
# @see PipelinesService
|
134
140
|
# @see Pipeline
|
135
|
-
#
|
141
|
+
#
|
136
142
|
# @return [PipelinesService] Service object for resources.
|
137
143
|
def pipelines
|
138
144
|
@pipelines ||= PipelinesService.new(@http_client)
|
139
145
|
end
|
140
146
|
|
141
|
-
# Access all Sources related actions.
|
142
|
-
# @see SourcesService
|
147
|
+
# Access all Sources related actions.
|
148
|
+
# @see SourcesService
|
143
149
|
# @see Source
|
144
|
-
#
|
150
|
+
#
|
145
151
|
# @return [SourcesService] Service object for resources.
|
146
152
|
def sources
|
147
153
|
@sources ||= SourcesService.new(@http_client)
|
148
154
|
end
|
149
155
|
|
150
|
-
# Access all Stages related actions.
|
151
|
-
# @see StagesService
|
156
|
+
# Access all Stages related actions.
|
157
|
+
# @see StagesService
|
152
158
|
# @see Stage
|
153
|
-
#
|
159
|
+
#
|
154
160
|
# @return [StagesService] Service object for resources.
|
155
161
|
def stages
|
156
162
|
@stages ||= StagesService.new(@http_client)
|
157
163
|
end
|
158
164
|
|
159
|
-
# Access all Tags related actions.
|
160
|
-
# @see TagsService
|
165
|
+
# Access all Tags related actions.
|
166
|
+
# @see TagsService
|
161
167
|
# @see Tag
|
162
|
-
#
|
168
|
+
#
|
163
169
|
# @return [TagsService] Service object for resources.
|
164
170
|
def tags
|
165
171
|
@tags ||= TagsService.new(@http_client)
|
166
172
|
end
|
167
173
|
|
168
|
-
# Access all Tasks related actions.
|
169
|
-
# @see TasksService
|
174
|
+
# Access all Tasks related actions.
|
175
|
+
# @see TasksService
|
170
176
|
# @see Task
|
171
|
-
#
|
177
|
+
#
|
172
178
|
# @return [TasksService] Service object for resources.
|
173
179
|
def tasks
|
174
180
|
@tasks ||= TasksService.new(@http_client)
|
175
181
|
end
|
176
182
|
|
177
|
-
# Access all Users related actions.
|
178
|
-
# @see UsersService
|
183
|
+
# Access all Users related actions.
|
184
|
+
# @see UsersService
|
179
185
|
# @see User
|
180
|
-
#
|
186
|
+
#
|
181
187
|
# @return [UsersService] Service object for resources.
|
182
188
|
def users
|
183
189
|
@users ||= UsersService.new(@http_client)
|
184
190
|
end
|
191
|
+
|
192
|
+
# Access Sync API related low-level actions.
|
193
|
+
# @see SyncService
|
194
|
+
#
|
195
|
+
# @return [SyncService] Service object for Sync API.
|
196
|
+
def sync
|
197
|
+
@sync ||= SyncService.new(@http_client)
|
198
|
+
end
|
185
199
|
end
|
186
200
|
end
|
data/lib/basecrm/http_client.rb
CHANGED
@@ -30,25 +30,25 @@ module BaseCRM
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
def get(path, params={})
|
34
|
-
request(:get, path, params)
|
33
|
+
def get(path, params={}, headers={})
|
34
|
+
request(:get, path, params, headers)
|
35
35
|
end
|
36
36
|
|
37
|
-
def post(path, body={})
|
38
|
-
request(:post, path, body)
|
37
|
+
def post(path, body={}, headers={})
|
38
|
+
request(:post, path, body, headers)
|
39
39
|
end
|
40
40
|
|
41
|
-
def put(path, body={})
|
42
|
-
request(:put, path, body)
|
41
|
+
def put(path, body={}, headers={})
|
42
|
+
request(:put, path, body, headers)
|
43
43
|
end
|
44
44
|
|
45
|
-
def delete(path, params={})
|
45
|
+
def delete(path, params={}, headers={})
|
46
46
|
request(:delete, path, params)
|
47
47
|
end
|
48
48
|
|
49
|
-
def request(method, path, data={})
|
49
|
+
def request(method, path, data={}, headers={})
|
50
50
|
options = {
|
51
|
-
headers: @headers
|
51
|
+
headers: @headers.merge(headers.to_h)
|
52
52
|
}
|
53
53
|
|
54
54
|
case method.to_s.downcase.to_sym
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module BaseCRM
|
2
|
+
class SyncMeta < Model
|
3
|
+
# @attribute [r] event_type
|
4
|
+
# @return [String] An event type. Possible values: `created`, `updated`, `deleted`.
|
5
|
+
# attr_reader :event_type
|
6
|
+
|
7
|
+
# @attribute [r] ack_key
|
8
|
+
# @return [String] An acknowledgement key.
|
9
|
+
# attr_reader :ack_key
|
10
|
+
|
11
|
+
# @attribute [r] revision
|
12
|
+
# @return [String] Data revision.
|
13
|
+
# attr_reader :revision
|
14
|
+
|
15
|
+
def acknowledged?
|
16
|
+
!!@acknowledged
|
17
|
+
end
|
18
|
+
|
19
|
+
def ack
|
20
|
+
@acknowledged = true
|
21
|
+
[:ack, self.ack_key]
|
22
|
+
end
|
23
|
+
|
24
|
+
def nack
|
25
|
+
@acknowledged = true
|
26
|
+
[:nack, self.ack_key]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module BaseCRM
|
2
|
+
class SyncQueue < Model
|
3
|
+
# @attribute [r] name
|
4
|
+
# @return [String] Name of the queue.
|
5
|
+
# attr_reader :name
|
6
|
+
|
7
|
+
# @attribute [r] pages
|
8
|
+
# @return [Integer] Number of pages to request.
|
9
|
+
# attr_reader :pages
|
10
|
+
|
11
|
+
# @attribute [r] total_count
|
12
|
+
# @return [Integer] Total number of items in the queue to synchronize.
|
13
|
+
# attr_reader :total_count
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module BaseCRM
|
2
|
+
class SyncSession < Model
|
3
|
+
# @attribute [r] id
|
4
|
+
# @return [String] Unique identifier for the sync session.
|
5
|
+
# attr_reader :id
|
6
|
+
|
7
|
+
# @attribute [r] queues
|
8
|
+
# @return [Array<SyncQueue>] A list of sync queues.
|
9
|
+
# attr_reader :queues
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module BaseCRM
|
2
|
+
class SyncService
|
3
|
+
|
4
|
+
def initialize(client)
|
5
|
+
@client = client
|
6
|
+
end
|
7
|
+
|
8
|
+
# Start synchronization flow
|
9
|
+
#
|
10
|
+
# post '/sync/start'
|
11
|
+
#
|
12
|
+
# Starts a new synchronization session.
|
13
|
+
# This is the first endpoint to call, in order to start a new synchronization session.
|
14
|
+
#
|
15
|
+
# @param device_uuid [String] Device's UUID for which to perform synchronization.
|
16
|
+
# @return [SyncSession] The resulting object is the synchronization session object or nil if there is nothing to synchronize.
|
17
|
+
def start(device_uuid)
|
18
|
+
validate_device!(device_uuid)
|
19
|
+
|
20
|
+
status, _, root = @client.post("/sync/start", {}, build_headers(device_uuid))
|
21
|
+
return nil if status == 204
|
22
|
+
|
23
|
+
build_session(root)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Get data from queue
|
27
|
+
#
|
28
|
+
# get '/sync/{session_id}/queues/main'
|
29
|
+
#
|
30
|
+
# Fetch fresh data from the main queue.
|
31
|
+
# Using session identifier you call continously the `#fetch` method to drain the main queue.
|
32
|
+
#
|
33
|
+
# @param device_uuid [String] Device's UUID for which to perform synchronization
|
34
|
+
# @param session_id [String] Unique identifier of a synchronization session.
|
35
|
+
# @param queue [String|Symbol] Queue name.
|
36
|
+
# @return [Array<Array<SyncMeta, Model>>] The list of sync's metadata associated with data and data.
|
37
|
+
def fetch(device_uuid, session_id, queue='main')
|
38
|
+
validate_device!(device_uuid)
|
39
|
+
raise ArgumentError, "session_id must not be nil nor empty" unless session_id && !session_id.strip.empty?
|
40
|
+
raise ArgumentError, "queue name must not be nil nor empty" unless queue && !queue.strip.empty?
|
41
|
+
|
42
|
+
status, _, root = @client.get("/sync/#{session_id}/queues/#{queue}", {}, build_headers(device_uuid))
|
43
|
+
return [] if status == 204
|
44
|
+
|
45
|
+
root[:items].map do |item|
|
46
|
+
klass = classify_type(item[:meta][:type])
|
47
|
+
[SyncMeta.new(item[:meta][:sync]), klass.new(item[:data])]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Acknowledge received data
|
52
|
+
#
|
53
|
+
# post '/sync/ack'
|
54
|
+
#
|
55
|
+
# Send acknowledgement keys to let know the Sync service which data you have.
|
56
|
+
# As you fetch new data, you need to send acknowledgement keys.
|
57
|
+
#
|
58
|
+
# @param device_uuid [String] Device's UUID for which to perform synchronization.
|
59
|
+
# @param ack_keys [Array<String>] The list of acknowledgement keys.
|
60
|
+
# @return [Boolean] Status of the operation.
|
61
|
+
def ack(device_uuid, ack_keys)
|
62
|
+
validate_device!(device_uuid)
|
63
|
+
raise ArgumentError, "ack_keys must not be nil and an array" unless ack_keys && ack_keys.is_a?(Array)
|
64
|
+
return true if ack_keys.empty?
|
65
|
+
|
66
|
+
payload = {
|
67
|
+
:ack_keys => ack_keys.map(&:to_s)
|
68
|
+
}
|
69
|
+
status, _, _ = @client.post('/sync/ack', payload, build_headers(device_uuid))
|
70
|
+
status == 202
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
def validate_device!(device_uuid)
|
75
|
+
raise ArgumentError, "device_uuid must not be nil nor empty" unless device_uuid && !device_uuid.strip.empty?
|
76
|
+
end
|
77
|
+
|
78
|
+
def build_headers(device_uuid)
|
79
|
+
{
|
80
|
+
"X-Basecrm-Device-UUID" => device_uuid
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
def classify_type(type)
|
85
|
+
BaseCRM.const_get(type.split('_').map(&:capitalize).join)
|
86
|
+
end
|
87
|
+
|
88
|
+
def build_session(root)
|
89
|
+
session_data = root[:data]
|
90
|
+
session_data[:queues] = session_data[:queues].map { |queue| SyncQueue.new(queue[:data]) }
|
91
|
+
SyncSession.new(session_data)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/lib/basecrm/sync.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
module BaseCRM
|
2
|
+
class Sync
|
3
|
+
attr_reader :device_uuid
|
4
|
+
attr_reader :client
|
5
|
+
|
6
|
+
# Intantiate a new BaseCRM Sync API V2 high-level wrapper
|
7
|
+
#
|
8
|
+
# @param options[Hash] Wrapper options
|
9
|
+
# @option options [String] :device_uuid Device's UUID.
|
10
|
+
# @option options [BaseCRM::Client] :client BaseCRM API v2 client instance.
|
11
|
+
#
|
12
|
+
# @raise [ConfigurationError] if no device's uuid provided
|
13
|
+
# @raise [ConfigurationError] if no client instance provided
|
14
|
+
#
|
15
|
+
# @return [Sync] New wrapper instance
|
16
|
+
#
|
17
|
+
# @see Client
|
18
|
+
# @see SyncService
|
19
|
+
def initialize(options)
|
20
|
+
@device_uuid = options[:device_uuid]
|
21
|
+
@client = options[:client]
|
22
|
+
|
23
|
+
validate!
|
24
|
+
end
|
25
|
+
|
26
|
+
# Perform a full synchronization flow.
|
27
|
+
# See the following example:
|
28
|
+
#
|
29
|
+
# client = BaseCRM::Client.new(access_token: "<YOUR_ACCESS_TOKEN>")
|
30
|
+
# sync = BaseCRM::Sync.new(client: client, device_uuid: "<YOUR_DEVICES_UUID>")
|
31
|
+
# sync.fetch do |sync_meta, resource|
|
32
|
+
# DB.send(sync_meta.event_type, entity) ? sync_meta.ack : sync_meta.nack
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# @param block [Proc] Procedure that will be called for every item in the queue. Takes two input arguments: SyncMeta instance
|
36
|
+
# associated with the resource, and the resource. You should use either `SyncQueue#ack` or `SyncQueue#nack` to return value from the block.
|
37
|
+
#
|
38
|
+
# @return nil
|
39
|
+
def fetch(&block)
|
40
|
+
return unless block_given?
|
41
|
+
|
42
|
+
# Set up a new synchronization session for given device's UUID
|
43
|
+
session = @client.sync.start(@device_uuid)
|
44
|
+
|
45
|
+
# Check if there is anything to synchronize
|
46
|
+
return unless session && session.id
|
47
|
+
|
48
|
+
# Drain the main queue unitl there is no more data (empty array)
|
49
|
+
loop do
|
50
|
+
queued_data = @client.sync.fetch(@device_uuid, session.id)
|
51
|
+
|
52
|
+
# nothing more to synchronize ?
|
53
|
+
break if queued_data.empty?
|
54
|
+
|
55
|
+
ack_keys = []
|
56
|
+
queued_data.each do |sync_meta, resource|
|
57
|
+
op, ack_key = block.call(sync_meta, resource)
|
58
|
+
ack_keys << ack_key if op == :ack
|
59
|
+
end
|
60
|
+
|
61
|
+
# As we fetch new data, we need to send ackwledgement keys - if any
|
62
|
+
@client.sync.ack(@device_uuid, ack_keys) unless ack_keys.empty?
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
def validate!
|
68
|
+
unless @device_uuid
|
69
|
+
raise ConfigurationError.new('No device UUID provided. '\
|
70
|
+
'The UUID must not change between synchronization sessions. '\
|
71
|
+
'Set your device\'s UUID during wrapper initialization using: '\
|
72
|
+
'"Base::Sync.new(device_uuid: <YOUR_DEVICES_UUID>, client: client)".')
|
73
|
+
end
|
74
|
+
|
75
|
+
unless @client
|
76
|
+
raise ConfigurationError.new('No "BaseCRM::Client" instance provided. '\
|
77
|
+
'The high-level sync wrapper is using "BaseCRM::SyncService"\'s lowl-level interface '\
|
78
|
+
'exposed within "BaseCRM::Client" scope.')
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/lib/basecrm/version.rb
CHANGED
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe BaseCRM::Sync do
|
4
|
+
let(:device_uuid) { '6dadcec8-6e61-4691-b318-1aab27b8fecf' }
|
5
|
+
let(:session_id) { '29f2aeeb-8d68-4ea7-95c3-a2c8e151f5a3' }
|
6
|
+
|
7
|
+
subject { BaseCRM::Sync.new(device_uuid: device_uuid, client: client) }
|
8
|
+
|
9
|
+
describe 'Responds to' do
|
10
|
+
it { should respond_to :fetch }
|
11
|
+
end
|
12
|
+
|
13
|
+
describe :initialize do
|
14
|
+
describe 'validation' do
|
15
|
+
context 'no device_uuid option' do
|
16
|
+
it 'raises BaseCRM::ConfigurationError exception' do
|
17
|
+
expect { BaseCRM::Sync.new(client: client) }.to raise_error BaseCRM::ConfigurationError
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'no client option' do
|
22
|
+
it 'raises BaseCRM::ConfigurationError exception' do
|
23
|
+
expect { BaseCRM::Sync.new(device_uuid: device_uuid) }.to raise_error BaseCRM::ConfigurationError
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'required options passed' do
|
28
|
+
it 'raises no exception' do
|
29
|
+
expect { BaseCRM::Sync.new(client: client, device_uuid: device_uuid) }.not_to raise_error
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe :fetch do
|
36
|
+
context 'no block passed' do
|
37
|
+
it 'does nothing' do
|
38
|
+
expect(client).not_to receive(:sync)
|
39
|
+
subject.fetch
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'nothing to synchronize' do
|
44
|
+
before :each do
|
45
|
+
expect(client.sync).to receive(:start).with(device_uuid).and_return(nil)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'returns early' do
|
49
|
+
expect(subject.fetch { |s, r| :nop }).to be_nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'fresh data to synchronize' do
|
54
|
+
let(:session) do
|
55
|
+
BaseCRM::SyncSession.new(id: session_id)
|
56
|
+
end
|
57
|
+
|
58
|
+
let(:ack_keys) do
|
59
|
+
['User-1234-1', 'Source-1234-1']
|
60
|
+
end
|
61
|
+
|
62
|
+
let(:queue_items) do
|
63
|
+
[
|
64
|
+
[BaseCRM::SyncMeta.new(event_type: 'created', ack_key: 'User-1234-1'), BaseCRM::User.new(id: 1)],
|
65
|
+
[BaseCRM::SyncMeta.new(event_type: 'created', ack_key: 'Source-1234-1'), BaseCRM::Source.new(id: 1)]
|
66
|
+
]
|
67
|
+
end
|
68
|
+
|
69
|
+
before :each do
|
70
|
+
expect(client.sync).to receive(:start).with(device_uuid).and_return(session)
|
71
|
+
expect(client.sync).to receive(:fetch).with(device_uuid, session_id).and_return(queue_items)
|
72
|
+
expect(client.sync).to receive(:fetch).with(device_uuid, session_id).and_return([])
|
73
|
+
expect(client.sync).to receive(:ack).with(device_uuid, ack_keys).and_return(true)
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'does whole synchronization flow' do
|
77
|
+
subject.fetch { |s, r| s.ack }
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'calls a provided block as many times as items in the queue' do
|
81
|
+
counter = 0
|
82
|
+
subject.fetch { |s, r| counter += 1; s.ack }
|
83
|
+
expect(counter).to eq(2)
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'passes two elements to provided block: first element is BaseCRM::SyncMeta and the second is a resource' do
|
87
|
+
subject.fetch do |s, r|
|
88
|
+
expect(s).to be_a BaseCRM::SyncMeta
|
89
|
+
s.ack
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe BaseCRM::SyncMeta do
|
4
|
+
describe 'Responds to' do
|
5
|
+
it { should respond_to :ack }
|
6
|
+
it { should respond_to :nack }
|
7
|
+
it { should respond_to :acknowledged? }
|
8
|
+
end
|
9
|
+
|
10
|
+
describe :ack do
|
11
|
+
subject { BaseCRM::SyncMeta }
|
12
|
+
|
13
|
+
it 'returns array' do
|
14
|
+
expect(subject.new.ack).to be_an Array
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'returns two elements array' do
|
18
|
+
expect(subject.new(ack_key: "123").ack).to eq([:ack, "123"])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe :nack do
|
23
|
+
subject { BaseCRM::SyncMeta }
|
24
|
+
|
25
|
+
it 'returns array' do
|
26
|
+
expect(subject.new.nack).to be_an Array
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'returns two elements array' do
|
30
|
+
expect(subject.new(ack_key: "123").nack).to eq([:nack, "123"])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe :acknowledged? do
|
35
|
+
subject { BaseCRM::SyncMeta }
|
36
|
+
|
37
|
+
context 'neither ack nor nack method called' do
|
38
|
+
it 'return false value' do
|
39
|
+
expect(subject.new.acknowledged?).to eq(false)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'ack method called' do
|
44
|
+
it 'return true value' do
|
45
|
+
ack_meta = subject.new
|
46
|
+
ack_meta.ack
|
47
|
+
expect(ack_meta.acknowledged?).to eq(true)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'nack method called' do
|
52
|
+
it 'return true value' do
|
53
|
+
ack_meta = subject.new
|
54
|
+
ack_meta.nack
|
55
|
+
expect(ack_meta.acknowledged?).to eq(true)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,283 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe BaseCRM::SyncService do
|
4
|
+
let(:device_uuid) { '6dadcec8-6e61-4691-b318-1aab27b8fecf' }
|
5
|
+
let(:session_id) { '29f2aeeb-8d68-4ea7-95c3-a2c8e151f5a3' }
|
6
|
+
|
7
|
+
describe 'Responds to' do
|
8
|
+
subject { BaseCRM::SyncService.new(double) }
|
9
|
+
|
10
|
+
it { should respond_to :start }
|
11
|
+
it { should respond_to :ack }
|
12
|
+
it { should respond_to :fetch }
|
13
|
+
end
|
14
|
+
|
15
|
+
describe 'Client respond to' do
|
16
|
+
subject { client }
|
17
|
+
|
18
|
+
it { should respond_to :sync }
|
19
|
+
end
|
20
|
+
|
21
|
+
describe 'Client#sync' do
|
22
|
+
it 'returns BaseCRM::SyncService instance' do
|
23
|
+
expect(client.sync).to be_a BaseCRM::SyncService
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe :start do
|
28
|
+
describe 'validation' do
|
29
|
+
context 'device_uuid is nil' do
|
30
|
+
it 'raises ArgumentError exception' do
|
31
|
+
expect { client.sync.start(nil) }.to raise_error(ArgumentError)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'device_uuid is empty' do
|
36
|
+
it 'raises ArgumentError exception' do
|
37
|
+
expect { client.sync.start(" ") }.to raise_error(ArgumentError)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'nothing new to fetch' do
|
43
|
+
let(:http_response) do
|
44
|
+
[204, {}, nil]
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'returns nil' do
|
48
|
+
expect(client.http_client).to receive(:post).with('/sync/start', {}, {'X-Basecrm-Device-UUID' => device_uuid}).and_return(http_response)
|
49
|
+
expect(client.sync.start(device_uuid)).to be_nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'we have to data to synchronize' do
|
54
|
+
let(:payload) do
|
55
|
+
{
|
56
|
+
data: {
|
57
|
+
id: session_id,
|
58
|
+
queues: [
|
59
|
+
data: {
|
60
|
+
name: 'main',
|
61
|
+
pages: 1,
|
62
|
+
total_count: 2
|
63
|
+
},
|
64
|
+
meta: {
|
65
|
+
type: :sync_queue
|
66
|
+
}
|
67
|
+
]
|
68
|
+
},
|
69
|
+
meta: {
|
70
|
+
type: :sync_session
|
71
|
+
}
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
let(:http_response) do
|
76
|
+
[201, {}, payload]
|
77
|
+
end
|
78
|
+
|
79
|
+
before :each do
|
80
|
+
expect(client.http_client).to receive(:post).with('/sync/start', {}, {'X-Basecrm-Device-UUID' => device_uuid}).and_return(http_response)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'returns an instance of BaseCRM::SyncSession' do
|
84
|
+
expect(client.sync.start(device_uuid)).to be_an BaseCRM::SyncSession
|
85
|
+
end
|
86
|
+
|
87
|
+
it "flattens BaseCRM::SyncSession's queues" do
|
88
|
+
client.sync.start(device_uuid).queues.each do |queue|
|
89
|
+
expect(queue).to be_an BaseCRM::SyncQueue
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe :ack do
|
96
|
+
let(:http_response) do
|
97
|
+
[202, {}, nil]
|
98
|
+
end
|
99
|
+
|
100
|
+
let(:ack_keys) do
|
101
|
+
['User-1234-1', 'Source-1234-1']
|
102
|
+
end
|
103
|
+
|
104
|
+
describe 'validation' do
|
105
|
+
context 'device_uuid is nil' do
|
106
|
+
it 'raises ArgumentError exception' do
|
107
|
+
expect { client.sync.ack(nil, ack_keys) }.to raise_error(ArgumentError)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context 'device_uuid is empty' do
|
112
|
+
it 'raises ArgumentError exception' do
|
113
|
+
expect { client.sync.ack(" ", ack_keys) }.to raise_error(ArgumentError)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'ack_keys is nil' do
|
118
|
+
it 'raises ArgumentError exception' do
|
119
|
+
expect { client.sync.ack(device_uuid, nil) }.to raise_error(ArgumentError)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
context 'ack_keys is not an Array' do
|
124
|
+
it 'raises ArgumentError exception' do
|
125
|
+
expect { client.sync.ack(device_uuid, {}) }.to raise_error(ArgumentError)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context 'empty ack_keys array' do
|
131
|
+
let(:ack_keys) do
|
132
|
+
[]
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'returns imedieatly with true value' do
|
136
|
+
expect(client.http_client).not_to receive(:post)
|
137
|
+
expect(client.sync.ack(device_uuid, ack_keys)).to eq(true)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
context 'non empty ack_keys call' do
|
142
|
+
it 'returns true value' do
|
143
|
+
expect(client.http_client).to receive(:post).with('/sync/ack', {ack_keys: ack_keys}, {'X-Basecrm-Device-UUID' => device_uuid}).and_return(http_response)
|
144
|
+
expect(client.sync.ack(device_uuid, ack_keys)).to eq(true)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
describe :fetch do
|
150
|
+
context 'validation' do
|
151
|
+
context 'device_uuid is nil' do
|
152
|
+
it 'raises ArgumentError exception' do
|
153
|
+
expect { client.sync.fetch(nil, session_id) }.to raise_error(ArgumentError)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
context 'device_uuid is empty' do
|
158
|
+
it 'raises ArgumentError exception' do
|
159
|
+
expect { client.sync.fetch(" ", session_id) }.to raise_error(ArgumentError)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
context 'session_id is nil' do
|
164
|
+
it 'raises ArgumentError exception' do
|
165
|
+
expect { client.sync.fetch(device_uuid, nil) }.to raise_error(ArgumentError)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
context 'session_id is empty' do
|
170
|
+
it 'raises ArgumentError exception' do
|
171
|
+
expect { client.sync.fetch(device_uuid, ' ') }.to raise_error(ArgumentError)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
context 'queue names is nil' do
|
176
|
+
it 'raises ArgumentError exception' do
|
177
|
+
expect { client.sync.fetch(device_uuid, session_id, nil) }.to raise_error(ArgumentError)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
context 'queue names is empty' do
|
182
|
+
it 'raises ArgumentError exception' do
|
183
|
+
expect { client.sync.fetch(device_uuid, session_id, ' ') }.to raise_error(ArgumentError)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
context 'no more data to fetch' do
|
189
|
+
let(:http_response) do
|
190
|
+
[204, {}, nil]
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'returns an empty array' do
|
194
|
+
expect(client.http_client).to receive(:get).with("/sync/#{session_id}/queues/main", {}, {'X-Basecrm-Device-UUID' => device_uuid}).and_return(http_response)
|
195
|
+
expect(client.sync.fetch(device_uuid, session_id)).to eq([])
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
context 'there is still data in the main queue' do
|
200
|
+
let(:payload) do
|
201
|
+
{
|
202
|
+
items: [
|
203
|
+
{
|
204
|
+
data: {
|
205
|
+
id: 1
|
206
|
+
},
|
207
|
+
meta: {
|
208
|
+
type: 'user',
|
209
|
+
sync: {
|
210
|
+
event_type: 'created',
|
211
|
+
ack_key: 'User-123-1',
|
212
|
+
revision: 1
|
213
|
+
}
|
214
|
+
}
|
215
|
+
},
|
216
|
+
{
|
217
|
+
data: {
|
218
|
+
id: 1
|
219
|
+
},
|
220
|
+
meta: {
|
221
|
+
type: 'source',
|
222
|
+
sync: {
|
223
|
+
event_type: 'created',
|
224
|
+
ack_key: 'Source-123-1',
|
225
|
+
revision: 1
|
226
|
+
}
|
227
|
+
}
|
228
|
+
}
|
229
|
+
],
|
230
|
+
meta: {
|
231
|
+
type: 'collection',
|
232
|
+
count: 2,
|
233
|
+
count_left: 0
|
234
|
+
}
|
235
|
+
}
|
236
|
+
end
|
237
|
+
|
238
|
+
let(:http_response) do
|
239
|
+
[200, {}, payload]
|
240
|
+
end
|
241
|
+
|
242
|
+
before :each do
|
243
|
+
expect(client.http_client).to receive(:get).with("/sync/#{session_id}/queues/main", {}, {'X-Basecrm-Device-UUID' => device_uuid}).and_return(http_response)
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'returns an array' do
|
247
|
+
expect(client.sync.fetch(device_uuid, session_id)).to be_an Array
|
248
|
+
end
|
249
|
+
|
250
|
+
it 'returns a non empty array' do
|
251
|
+
expect(client.sync.fetch(device_uuid, session_id)).not_to be_empty
|
252
|
+
end
|
253
|
+
|
254
|
+
it 'returns an array of two items' do
|
255
|
+
expect(client.sync.fetch(device_uuid, session_id).length).to eq(2)
|
256
|
+
end
|
257
|
+
|
258
|
+
it 'returns an array of arrays' do
|
259
|
+
client.sync.fetch(device_uuid, session_id).each do |item|
|
260
|
+
expect(item).to be_an Array
|
261
|
+
expect(item).not_to be_empty
|
262
|
+
expect(item.length).to eq(2)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
it 'returns an array where the first element is BaseCRM::SyncMeta and the second is a model' do
|
267
|
+
items = client.sync.fetch(device_uuid, session_id)
|
268
|
+
|
269
|
+
sync_meta, user = items[0]
|
270
|
+
expect(sync_meta).to be_a BaseCRM::SyncMeta
|
271
|
+
expect(user).to be_a BaseCRM::User
|
272
|
+
expect(sync_meta.ack_key).to eq('User-123-1')
|
273
|
+
expect(user.id).to eq(1)
|
274
|
+
|
275
|
+
sync_meta, source = items[1]
|
276
|
+
expect(sync_meta).to be_a BaseCRM::SyncMeta
|
277
|
+
expect(source).to be_a BaseCRM::Source
|
278
|
+
expect(sync_meta.ack_key).to eq('Source-123-1')
|
279
|
+
expect(source.id).to eq(1)
|
280
|
+
end
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: basecrm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- BaseCRM developers
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-05-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -148,6 +148,9 @@ files:
|
|
148
148
|
- lib/basecrm/models/pipeline.rb
|
149
149
|
- lib/basecrm/models/source.rb
|
150
150
|
- lib/basecrm/models/stage.rb
|
151
|
+
- lib/basecrm/models/sync_meta.rb
|
152
|
+
- lib/basecrm/models/sync_queue.rb
|
153
|
+
- lib/basecrm/models/sync_session.rb
|
151
154
|
- lib/basecrm/models/tag.rb
|
152
155
|
- lib/basecrm/models/task.rb
|
153
156
|
- lib/basecrm/models/user.rb
|
@@ -162,10 +165,13 @@ files:
|
|
162
165
|
- lib/basecrm/services/pipelines_service.rb
|
163
166
|
- lib/basecrm/services/sources_service.rb
|
164
167
|
- lib/basecrm/services/stages_service.rb
|
168
|
+
- lib/basecrm/services/sync_service.rb
|
165
169
|
- lib/basecrm/services/tags_service.rb
|
166
170
|
- lib/basecrm/services/tasks_service.rb
|
167
171
|
- lib/basecrm/services/users_service.rb
|
172
|
+
- lib/basecrm/sync.rb
|
168
173
|
- lib/basecrm/version.rb
|
174
|
+
- spec/basecrm/sync_spec.rb
|
169
175
|
- spec/factories/associated_contact.rb
|
170
176
|
- spec/factories/contact.rb
|
171
177
|
- spec/factories/deal.rb
|
@@ -175,6 +181,7 @@ files:
|
|
175
181
|
- spec/factories/source.rb
|
176
182
|
- spec/factories/tag.rb
|
177
183
|
- spec/factories/task.rb
|
184
|
+
- spec/models/sync_meta_spec.rb
|
178
185
|
- spec/services/accounts_service_spec.rb
|
179
186
|
- spec/services/associated_contacts_service_spec.rb
|
180
187
|
- spec/services/contacts_service_spec.rb
|
@@ -185,6 +192,7 @@ files:
|
|
185
192
|
- spec/services/pipelines_service_spec.rb
|
186
193
|
- spec/services/sources_service_spec.rb
|
187
194
|
- spec/services/stages_service_spec.rb
|
195
|
+
- spec/services/sync_service_spec.rb
|
188
196
|
- spec/services/tags_service_spec.rb
|
189
197
|
- spec/services/tasks_service_spec.rb
|
190
198
|
- spec/services/users_service_spec.rb
|
@@ -210,11 +218,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
210
218
|
version: '0'
|
211
219
|
requirements: []
|
212
220
|
rubyforge_project:
|
213
|
-
rubygems_version: 2.
|
221
|
+
rubygems_version: 2.4.3
|
214
222
|
signing_key:
|
215
223
|
specification_version: 4
|
216
224
|
summary: BaseCRM Official API V2 library client for ruby
|
217
225
|
test_files:
|
226
|
+
- spec/basecrm/sync_spec.rb
|
218
227
|
- spec/factories/associated_contact.rb
|
219
228
|
- spec/factories/contact.rb
|
220
229
|
- spec/factories/deal.rb
|
@@ -224,6 +233,7 @@ test_files:
|
|
224
233
|
- spec/factories/source.rb
|
225
234
|
- spec/factories/tag.rb
|
226
235
|
- spec/factories/task.rb
|
236
|
+
- spec/models/sync_meta_spec.rb
|
227
237
|
- spec/services/accounts_service_spec.rb
|
228
238
|
- spec/services/associated_contacts_service_spec.rb
|
229
239
|
- spec/services/contacts_service_spec.rb
|
@@ -234,6 +244,7 @@ test_files:
|
|
234
244
|
- spec/services/pipelines_service_spec.rb
|
235
245
|
- spec/services/sources_service_spec.rb
|
236
246
|
- spec/services/stages_service_spec.rb
|
247
|
+
- spec/services/sync_service_spec.rb
|
237
248
|
- spec/services/tags_service_spec.rb
|
238
249
|
- spec/services/tasks_service_spec.rb
|
239
250
|
- spec/services/users_service_spec.rb
|