basecrm 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|