sockudo 1.0.0 → 2.0.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 +25 -3
- data/lib/sockudo/channel.rb +79 -7
- data/lib/sockudo/client.rb +293 -79
- data/lib/sockudo/request.rb +72 -37
- data/lib/sockudo/resource.rb +11 -5
- data/lib/sockudo/utils.rb +5 -3
- data/lib/sockudo/version.rb +3 -1
- data/lib/sockudo/webhook.rb +24 -24
- data/lib/sockudo.rb +20 -4
- metadata +61 -35
data/lib/sockudo/client.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'base64'
|
|
2
4
|
require 'securerandom'
|
|
3
5
|
require 'pusher-signature'
|
|
@@ -14,7 +16,7 @@ module Sockudo
|
|
|
14
16
|
DEFAULT_SEND_TIMEOUT = 5
|
|
15
17
|
DEFAULT_RECEIVE_TIMEOUT = 5
|
|
16
18
|
DEFAULT_KEEP_ALIVE_TIMEOUT = 30
|
|
17
|
-
DEFAULT_CLUSTER =
|
|
19
|
+
DEFAULT_CLUSTER = 'mt1'
|
|
18
20
|
|
|
19
21
|
# Loads the configuration from an url in the environment
|
|
20
22
|
def self.from_env(key = 'SOCKUDO_URL')
|
|
@@ -30,15 +32,15 @@ module Sockudo
|
|
|
30
32
|
end
|
|
31
33
|
|
|
32
34
|
def initialize(options = {})
|
|
33
|
-
@scheme =
|
|
35
|
+
@scheme = 'https'
|
|
34
36
|
@port = options[:port] || 443
|
|
35
37
|
|
|
36
38
|
if options.key?(:encrypted)
|
|
37
|
-
warn
|
|
39
|
+
warn '[DEPRECATION] `encrypted` is deprecated and will be removed in the next major version. Use `use_tls` instead.'
|
|
38
40
|
end
|
|
39
41
|
|
|
40
42
|
if options[:use_tls] == false || options[:encrypted] == false
|
|
41
|
-
@scheme =
|
|
43
|
+
@scheme = 'http'
|
|
42
44
|
@port = options[:port] || 80
|
|
43
45
|
end
|
|
44
46
|
|
|
@@ -69,18 +71,20 @@ module Sockudo
|
|
|
69
71
|
def authentication_token
|
|
70
72
|
raise ConfigurationError, :key unless @key
|
|
71
73
|
raise ConfigurationError, :secret unless @secret
|
|
72
|
-
|
|
74
|
+
|
|
75
|
+
Pusher::Signature::Token.new(@key, @secret)
|
|
73
76
|
end
|
|
74
77
|
|
|
75
78
|
# @private Builds a url for this app, optionally appending a path
|
|
76
79
|
def url(path = nil)
|
|
77
80
|
raise ConfigurationError, :app_id unless @app_id
|
|
81
|
+
|
|
78
82
|
URI::Generic.build({
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
83
|
+
scheme: @scheme,
|
|
84
|
+
host: @host,
|
|
85
|
+
port: @port,
|
|
86
|
+
path: "/apps/#{@app_id}#{path}"
|
|
87
|
+
})
|
|
84
88
|
end
|
|
85
89
|
|
|
86
90
|
# Configure Sockudo connection by providing a url rather than specifying
|
|
@@ -136,13 +140,15 @@ module Sockudo
|
|
|
136
140
|
# Convenience method to set all timeouts to the same value (in seconds).
|
|
137
141
|
# For more control, use the individual writers.
|
|
138
142
|
def timeout=(value)
|
|
139
|
-
@connect_timeout
|
|
143
|
+
@connect_timeout = value
|
|
144
|
+
@send_timeout = value
|
|
145
|
+
@receive_timeout = value
|
|
140
146
|
end
|
|
141
147
|
|
|
142
148
|
# Set an encryption_master_key to use with private-encrypted channels from
|
|
143
149
|
# a base64 encoded string.
|
|
144
|
-
def encryption_master_key_base64=(
|
|
145
|
-
@encryption_master_key =
|
|
150
|
+
def encryption_master_key_base64=(str)
|
|
151
|
+
@encryption_master_key = str ? Base64.strict_decode64(str) : nil
|
|
146
152
|
end
|
|
147
153
|
|
|
148
154
|
## INTERACT WITH THE API ##
|
|
@@ -169,8 +175,8 @@ module Sockudo
|
|
|
169
175
|
# @raise [Sockudo::Error] Unsuccessful response - see the error message
|
|
170
176
|
# @raise [Sockudo::HTTPError] Error raised inside http client. The original error is wrapped in error.original_error
|
|
171
177
|
#
|
|
172
|
-
def get(path, params = {})
|
|
173
|
-
resource(path).get(params)
|
|
178
|
+
def get(path, params = {}, headers = {})
|
|
179
|
+
resource(path).get(params, headers)
|
|
174
180
|
end
|
|
175
181
|
|
|
176
182
|
# GET arbitrary REST API resource using an asynchronous http client.
|
|
@@ -185,8 +191,8 @@ module Sockudo
|
|
|
185
191
|
#
|
|
186
192
|
# @return Either an EM::DefaultDeferrable or a HTTPClient::Connection
|
|
187
193
|
#
|
|
188
|
-
def get_async(path, params = {})
|
|
189
|
-
resource(path).get_async(params)
|
|
194
|
+
def get_async(path, params = {}, headers = {})
|
|
195
|
+
resource(path).get_async(params, headers)
|
|
190
196
|
end
|
|
191
197
|
|
|
192
198
|
# POST arbitrary REST API resource using a synchronous http client.
|
|
@@ -195,6 +201,12 @@ module Sockudo
|
|
|
195
201
|
resource(path).post(params, headers)
|
|
196
202
|
end
|
|
197
203
|
|
|
204
|
+
# DELETE arbitrary REST API resource using a synchronous http client.
|
|
205
|
+
# All request signing is handled automatically.
|
|
206
|
+
def delete(path, params = {}, headers = {})
|
|
207
|
+
resource(path).delete(params, headers)
|
|
208
|
+
end
|
|
209
|
+
|
|
198
210
|
# POST arbitrary REST API resource using an asynchronous http client.
|
|
199
211
|
# Works identially to get_async method, but posts params as JSON in post
|
|
200
212
|
# body.
|
|
@@ -227,7 +239,7 @@ module Sockudo
|
|
|
227
239
|
Channel.new(nil, channel_name, self)
|
|
228
240
|
end
|
|
229
241
|
|
|
230
|
-
alias
|
|
242
|
+
alias [] channel
|
|
231
243
|
|
|
232
244
|
# Request a list of occupied channels from the API
|
|
233
245
|
#
|
|
@@ -260,6 +272,91 @@ module Sockudo
|
|
|
260
272
|
get("/channels/#{channel_name}", params)
|
|
261
273
|
end
|
|
262
274
|
|
|
275
|
+
# Request durable history for a specific channel
|
|
276
|
+
#
|
|
277
|
+
# GET /apps/[id]/channels/[channel_name]/history
|
|
278
|
+
#
|
|
279
|
+
# @param channel_name [String] Channel name (max 200 characters)
|
|
280
|
+
# @param params [Hash] Hash of parameters for the API. Supported keys include:
|
|
281
|
+
# :limit, :direction, :cursor, :start_serial, :end_serial, :start_time_ms, :end_time_ms
|
|
282
|
+
#
|
|
283
|
+
# @return [Hash] See Sockudo history API docs
|
|
284
|
+
#
|
|
285
|
+
# @raise [Sockudo::Error] Unsuccessful response - see the error message
|
|
286
|
+
# @raise [Sockudo::HTTPError] Error raised inside http client. The original error is wrapped in error.original_error
|
|
287
|
+
#
|
|
288
|
+
def channel_history(channel_name, params = {})
|
|
289
|
+
get("/channels/#{channel_name}/history", params)
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
# Request presence history for a specific presence channel
|
|
293
|
+
#
|
|
294
|
+
# GET /apps/[id]/channels/[channel_name]/presence/history
|
|
295
|
+
#
|
|
296
|
+
# @param channel_name [String] Presence channel name (max 200 characters)
|
|
297
|
+
# @param params [Hash] Hash of parameters for the API. Supported keys include:
|
|
298
|
+
# :limit, :direction, :cursor, :start_serial, :end_serial, :start_time_ms, :end_time_ms
|
|
299
|
+
#
|
|
300
|
+
# @return [Hash] See Sockudo presence history API docs
|
|
301
|
+
#
|
|
302
|
+
def channel_presence_history(channel_name, params = {})
|
|
303
|
+
get("/channels/#{channel_name}/presence/history", params)
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
# Request a reconstructed presence snapshot for a specific presence channel
|
|
307
|
+
#
|
|
308
|
+
# GET /apps/[id]/channels/[channel_name]/presence/history/snapshot
|
|
309
|
+
#
|
|
310
|
+
# @param channel_name [String] Presence channel name (max 200 characters)
|
|
311
|
+
# @param params [Hash] Hash of parameters for the API. Supported keys include:
|
|
312
|
+
# :at_time_ms, :at_serial
|
|
313
|
+
#
|
|
314
|
+
# @return [Hash] See Sockudo presence snapshot API docs
|
|
315
|
+
#
|
|
316
|
+
def channel_presence_snapshot(channel_name, params = {})
|
|
317
|
+
get("/channels/#{channel_name}/presence/history/snapshot", params)
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
# Request the latest visible version of a mutable message
|
|
321
|
+
def get_message(channel_name, message_serial, params = {})
|
|
322
|
+
get("/channels/#{channel_name}/messages/#{message_serial}", params)
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
# Request preserved versions of a mutable message
|
|
326
|
+
def get_message_versions(channel_name, message_serial, params = {})
|
|
327
|
+
get("/channels/#{channel_name}/messages/#{message_serial}/versions", params)
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
# Apply a mutable-message update
|
|
331
|
+
def update_message(channel_name, message_serial, params = {})
|
|
332
|
+
post("/channels/#{channel_name}/messages/#{message_serial}/update", params)
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
# Apply a mutable-message delete
|
|
336
|
+
def delete_message(channel_name, message_serial, params = {})
|
|
337
|
+
post("/channels/#{channel_name}/messages/#{message_serial}/delete", params)
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
# Apply a mutable-message append
|
|
341
|
+
def append_message(channel_name, message_serial, params = {})
|
|
342
|
+
post("/channels/#{channel_name}/messages/#{message_serial}/append", params)
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
# Publish an annotation for a versioned message
|
|
346
|
+
def publish_annotation(channel_name, message_serial, params = {})
|
|
347
|
+
post("/channels/#{channel_name}/messages/#{message_serial}/annotations", params)
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
# Delete an annotation from a versioned message
|
|
351
|
+
def delete_annotation(channel_name, message_serial, annotation_serial, params = {})
|
|
352
|
+
delete("/channels/#{channel_name}/messages/#{message_serial}/annotations/#{annotation_serial}", params)
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
# List raw annotation events for a versioned message
|
|
356
|
+
def list_annotations(channel_name, message_serial, params = {})
|
|
357
|
+
get("/channels/#{channel_name}/messages/#{message_serial}/annotations", params)
|
|
358
|
+
end
|
|
359
|
+
|
|
263
360
|
# Request info for users of a presence channel
|
|
264
361
|
#
|
|
265
362
|
# GET /apps/[id]/channels/[channel_name]/users
|
|
@@ -276,6 +373,114 @@ module Sockudo
|
|
|
276
373
|
get("/channels/#{channel_name}/users", params)
|
|
277
374
|
end
|
|
278
375
|
|
|
376
|
+
# Activate or create a push device registration with admin scope
|
|
377
|
+
def activate_device(device, options = {})
|
|
378
|
+
post(push_path('/deviceRegistrations'), device,
|
|
379
|
+
push_headers('push-admin', nil, rotate_device_identity_token: options[:rotate_device_identity_token]))
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
# Alias of activate_device
|
|
383
|
+
def create_device_activation(device, options = {})
|
|
384
|
+
activate_device(device, options)
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
# Update a push device registration with push-subscribe scope
|
|
388
|
+
def update_device_registration(device, device_identity_token)
|
|
389
|
+
post(push_path('/deviceRegistrations'), device, push_headers('push-subscribe', device_identity_token))
|
|
390
|
+
end
|
|
391
|
+
|
|
392
|
+
# List push device registrations with cursor pagination
|
|
393
|
+
def list_device_registrations(params = {})
|
|
394
|
+
get(push_path('/deviceRegistrations'), params, push_headers('push-admin'))
|
|
395
|
+
end
|
|
396
|
+
|
|
397
|
+
# Get a push device registration
|
|
398
|
+
def get_device_registration(device_id, device_identity_token = nil)
|
|
399
|
+
capability = device_identity_token ? 'push-subscribe' : 'push-admin'
|
|
400
|
+
get(push_path("/deviceRegistrations/#{device_id}"), {}, push_headers(capability, device_identity_token))
|
|
401
|
+
end
|
|
402
|
+
|
|
403
|
+
# Delete a push device registration
|
|
404
|
+
def delete_device_registration(device_id, device_identity_token = nil)
|
|
405
|
+
capability = device_identity_token ? 'push-subscribe' : 'push-admin'
|
|
406
|
+
delete(push_path("/deviceRegistrations/#{device_id}"), {}, push_headers(capability, device_identity_token))
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
# Delete all device registrations for a client identifier
|
|
410
|
+
def remove_device_registrations_by_client(client_id)
|
|
411
|
+
delete(push_path('/deviceRegistrations'), { clientId: client_id }, push_headers('push-admin'))
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
# Upsert a push channel subscription
|
|
415
|
+
def upsert_channel_push_subscription(subscription, device_identity_token = nil)
|
|
416
|
+
capability = device_identity_token ? 'push-subscribe' : 'push-admin'
|
|
417
|
+
post(push_path('/channelSubscriptions'), subscription, push_headers(capability, device_identity_token))
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
# List push channel subscriptions with cursor pagination
|
|
421
|
+
def list_channel_push_subscriptions(params = {}, device_identity_token = nil)
|
|
422
|
+
capability = device_identity_token ? 'push-subscribe' : 'push-admin'
|
|
423
|
+
get(push_path('/channelSubscriptions'), params, push_headers(capability, device_identity_token))
|
|
424
|
+
end
|
|
425
|
+
|
|
426
|
+
# Delete push channel subscriptions
|
|
427
|
+
def delete_channel_push_subscriptions(params = {}, device_identity_token = nil)
|
|
428
|
+
capability = device_identity_token ? 'push-subscribe' : 'push-admin'
|
|
429
|
+
delete(push_path('/channelSubscriptions'), params, push_headers(capability, device_identity_token))
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
# List subscribed channels with cursor pagination
|
|
433
|
+
def list_channel_push_subscription_channels(params = {})
|
|
434
|
+
get(push_path('/channelSubscriptions/channels'), params, push_headers('push-admin'))
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
# List stored push provider credentials with cursor pagination
|
|
438
|
+
def list_push_credentials(params = {})
|
|
439
|
+
get(push_path('/credentials'), params, push_headers('push-admin'))
|
|
440
|
+
end
|
|
441
|
+
|
|
442
|
+
# Store or update a provider credential payload
|
|
443
|
+
def put_push_credential(provider, credential)
|
|
444
|
+
post(push_path("/credentials/#{provider}"), credential, push_headers('push-admin'))
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
# Publish push asynchronously by default
|
|
448
|
+
def publish_push(request)
|
|
449
|
+
post(push_path('/publish'), request.merge(sync: false), push_headers('push-admin'))
|
|
450
|
+
end
|
|
451
|
+
|
|
452
|
+
# Alias of publish_push
|
|
453
|
+
def publish_push_direct(request)
|
|
454
|
+
publish_push(request)
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
# Publish a batch of push notifications asynchronously by default
|
|
458
|
+
def publish_push_batch(requests)
|
|
459
|
+
post(push_path('/batch/publish'), requests.map { |request| request.merge(sync: false) }, push_headers('push-admin'))
|
|
460
|
+
end
|
|
461
|
+
|
|
462
|
+
# Schedule a push publish; requires notBeforeMs in the request
|
|
463
|
+
def schedule_push(request)
|
|
464
|
+
raise Sockudo::Error, 'scheduled push requires notBeforeMs' unless request.key?(:notBeforeMs) || request.key?('notBeforeMs')
|
|
465
|
+
|
|
466
|
+
publish_push(request)
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
# Get the status for a publish id
|
|
470
|
+
def get_publish_status(publish_id)
|
|
471
|
+
get(push_path("/publish/#{publish_id}/status"), {}, push_headers('push-admin'))
|
|
472
|
+
end
|
|
473
|
+
|
|
474
|
+
# Cancel a scheduled publish
|
|
475
|
+
def cancel_scheduled_push(publish_id)
|
|
476
|
+
delete(push_path("/scheduled/#{publish_id}"), {}, push_headers('push-admin'))
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
# Submit a provider delivery status event
|
|
480
|
+
def post_push_delivery_status(event)
|
|
481
|
+
post(push_path('/deliveryStatus'), event, push_headers('push-admin'))
|
|
482
|
+
end
|
|
483
|
+
|
|
279
484
|
# Trigger an event on one or more channels
|
|
280
485
|
#
|
|
281
486
|
# POST /apps/[app_id]/events
|
|
@@ -334,7 +539,6 @@ module Sockudo
|
|
|
334
539
|
post_async('/batch_events', trigger_batch_params(flat_events))
|
|
335
540
|
end
|
|
336
541
|
|
|
337
|
-
|
|
338
542
|
# Generate the expected response for an authentication endpoint.
|
|
339
543
|
# See https://sockudo.com/docs/channels/server_api/authorizing-users for details.
|
|
340
544
|
#
|
|
@@ -399,42 +603,37 @@ module Sockudo
|
|
|
399
603
|
def sync_http_client
|
|
400
604
|
require 'httpclient'
|
|
401
605
|
|
|
402
|
-
@
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
c.keep_alive_timeout = @keep_alive_timeout
|
|
408
|
-
end
|
|
606
|
+
@sync_http_client ||= HTTPClient.new(@http_proxy).tap do |c|
|
|
607
|
+
c.connect_timeout = @connect_timeout
|
|
608
|
+
c.send_timeout = @send_timeout
|
|
609
|
+
c.receive_timeout = @receive_timeout
|
|
610
|
+
c.keep_alive_timeout = @keep_alive_timeout
|
|
409
611
|
end
|
|
410
612
|
end
|
|
411
613
|
|
|
412
614
|
# @private Construct an em-http-request http client
|
|
413
615
|
def em_http_client(uri)
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
end
|
|
418
|
-
require 'em-http' unless defined?(EventMachine::HttpRequest)
|
|
616
|
+
unless defined?(EventMachine) && EventMachine.reactor_running?
|
|
617
|
+
raise Error, 'In order to use async calling you must be running inside an eventmachine loop'
|
|
618
|
+
end
|
|
419
619
|
|
|
420
|
-
|
|
421
|
-
connect_timeout: @connect_timeout,
|
|
422
|
-
inactivity_timeout: @receive_timeout,
|
|
423
|
-
}
|
|
620
|
+
require 'em-http' unless defined?(EventMachine::HttpRequest)
|
|
424
621
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
}
|
|
430
|
-
if @proxy[:user]
|
|
431
|
-
proxy_opts[:authorization] = [@proxy[:user], @proxy[:password]]
|
|
432
|
-
end
|
|
433
|
-
connection_opts[:proxy] = proxy_opts
|
|
434
|
-
end
|
|
622
|
+
connection_opts = {
|
|
623
|
+
connect_timeout: @connect_timeout,
|
|
624
|
+
inactivity_timeout: @receive_timeout
|
|
625
|
+
}
|
|
435
626
|
|
|
436
|
-
|
|
627
|
+
if defined?(@proxy)
|
|
628
|
+
proxy_opts = {
|
|
629
|
+
host: @proxy[:host],
|
|
630
|
+
port: @proxy[:port]
|
|
631
|
+
}
|
|
632
|
+
proxy_opts[:authorization] = [@proxy[:user], @proxy[:password]] if @proxy[:user]
|
|
633
|
+
connection_opts[:proxy] = proxy_opts
|
|
437
634
|
end
|
|
635
|
+
|
|
636
|
+
EventMachine::HttpRequest.new(uri, connection_opts)
|
|
438
637
|
end
|
|
439
638
|
|
|
440
639
|
private
|
|
@@ -443,16 +642,19 @@ module Sockudo
|
|
|
443
642
|
|
|
444
643
|
def inject_auto_idempotency_key(params)
|
|
445
644
|
return params if params.key?(:idempotency_key) || !@auto_idempotency_key
|
|
645
|
+
|
|
446
646
|
serial = (@publish_serial += 1)
|
|
447
647
|
params.merge(idempotency_key: "#{@base_id}:#{serial}")
|
|
448
648
|
end
|
|
449
649
|
|
|
450
650
|
def inject_auto_idempotency_keys_batch!(events)
|
|
451
651
|
return false unless @auto_idempotency_key
|
|
652
|
+
|
|
452
653
|
serial = (@publish_serial += 1)
|
|
453
654
|
injected = false
|
|
454
655
|
events.each_with_index do |event, index|
|
|
455
656
|
next if event.key?(:idempotency_key)
|
|
657
|
+
|
|
456
658
|
event[:idempotency_key] = "#{@base_id}:#{serial}:#{index}"
|
|
457
659
|
injected = true
|
|
458
660
|
end
|
|
@@ -462,19 +664,15 @@ module Sockudo
|
|
|
462
664
|
def post_with_retry(path, body, headers = {})
|
|
463
665
|
last_error = nil
|
|
464
666
|
@max_retries.times do |attempt|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
else
|
|
475
|
-
raise
|
|
476
|
-
end
|
|
477
|
-
end
|
|
667
|
+
return post(path, body, headers)
|
|
668
|
+
rescue Sockudo::HTTPError => e
|
|
669
|
+
last_error = e
|
|
670
|
+
raise unless attempt < @max_retries - 1
|
|
671
|
+
rescue Sockudo::Error => e
|
|
672
|
+
raise unless e.respond_to?(:status) && e.status.is_a?(Integer) && e.status >= 500 && e.status < 600
|
|
673
|
+
|
|
674
|
+
last_error = e
|
|
675
|
+
raise unless attempt < @max_retries - 1
|
|
478
676
|
end
|
|
479
677
|
raise last_error
|
|
480
678
|
end
|
|
@@ -483,18 +681,22 @@ module Sockudo
|
|
|
483
681
|
channels = Array(channels).map(&:to_s)
|
|
484
682
|
raise Sockudo::Error, "Too many channels (#{channels.length}), max 100" if channels.length > 100
|
|
485
683
|
|
|
486
|
-
encoded_data = if channels.any?{ |c| c.match(/^private-encrypted-/) }
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
684
|
+
encoded_data = if channels.any? { |c| c.match(/^private-encrypted-/) }
|
|
685
|
+
if channels.length > 1
|
|
686
|
+
raise Sockudo::Error,
|
|
687
|
+
'Cannot trigger to multiple channels if any are encrypted'
|
|
688
|
+
end
|
|
689
|
+
|
|
690
|
+
encrypt(channels[0], encode_data(data))
|
|
691
|
+
else
|
|
692
|
+
encode_data(data)
|
|
693
|
+
end
|
|
492
694
|
|
|
493
695
|
params.merge({
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
696
|
+
name: event_name,
|
|
697
|
+
channels: channels,
|
|
698
|
+
data: encoded_data
|
|
699
|
+
})
|
|
498
700
|
end
|
|
499
701
|
|
|
500
702
|
def trigger_params_with_headers(channels, event_name, data, params)
|
|
@@ -513,11 +715,11 @@ module Sockudo
|
|
|
513
715
|
{
|
|
514
716
|
batch: events.map do |event|
|
|
515
717
|
event.dup.tap do |e|
|
|
516
|
-
e[:data] = if e[:channel].match(/^private-encrypted-/)
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
718
|
+
e[:data] = if e[:channel].match(/^private-encrypted-/)
|
|
719
|
+
encrypt(e[:channel], encode_data(e[:data]))
|
|
720
|
+
else
|
|
721
|
+
encode_data(e[:data])
|
|
722
|
+
end
|
|
521
723
|
end
|
|
522
724
|
end
|
|
523
725
|
}
|
|
@@ -526,6 +728,7 @@ module Sockudo
|
|
|
526
728
|
# JSON-encode the data if it's not a string
|
|
527
729
|
def encode_data(data)
|
|
528
730
|
return data if data.is_a? String
|
|
731
|
+
|
|
529
732
|
MultiJson.encode(data)
|
|
530
733
|
end
|
|
531
734
|
|
|
@@ -546,9 +749,9 @@ module Sockudo
|
|
|
546
749
|
ciphertext = secret_box.encrypt(nonce, encoded_data)
|
|
547
750
|
|
|
548
751
|
MultiJson.encode({
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
752
|
+
'nonce' => Base64.strict_encode64(nonce),
|
|
753
|
+
'ciphertext' => Base64.strict_encode64(ciphertext)
|
|
754
|
+
})
|
|
552
755
|
end
|
|
553
756
|
|
|
554
757
|
def configured?
|
|
@@ -558,7 +761,7 @@ module Sockudo
|
|
|
558
761
|
def require_rbnacl
|
|
559
762
|
require 'rbnacl'
|
|
560
763
|
rescue LoadError => e
|
|
561
|
-
|
|
764
|
+
warn "You don't have rbnacl installed in your application. Please add it to your Gemfile and run bundle install"
|
|
562
765
|
raise e
|
|
563
766
|
end
|
|
564
767
|
|
|
@@ -575,7 +778,7 @@ module Sockudo
|
|
|
575
778
|
# @raise [Sockudo::Error] if socket_id or custom_string invalid
|
|
576
779
|
#
|
|
577
780
|
def authentication_string(socket_id, custom_string = nil)
|
|
578
|
-
string_to_sign = [socket_id, 'user', custom_string].compact.
|
|
781
|
+
string_to_sign = [socket_id, 'user', custom_string].compact.join('::')
|
|
579
782
|
|
|
580
783
|
_authentication_string(socket_id, string_to_sign, authentication_token, string_to_sign)
|
|
581
784
|
end
|
|
@@ -589,5 +792,16 @@ module Sockudo
|
|
|
589
792
|
def user_data_valid?(data)
|
|
590
793
|
data.is_a?(Hash) && data.key?(:id) && !data[:id].empty? && data[:id].is_a?(String)
|
|
591
794
|
end
|
|
795
|
+
|
|
796
|
+
def push_headers(capability = 'push-admin', device_identity_token = nil, rotate_device_identity_token: false)
|
|
797
|
+
headers = { 'X-Sockudo-Push-Capability' => capability }
|
|
798
|
+
headers['X-Sockudo-Device-Identity-Token'] = device_identity_token if device_identity_token
|
|
799
|
+
headers['X-Sockudo-Rotate-Device-Identity-Token'] = 'true' if rotate_device_identity_token
|
|
800
|
+
headers
|
|
801
|
+
end
|
|
802
|
+
|
|
803
|
+
def push_path(path)
|
|
804
|
+
"/push#{path}"
|
|
805
|
+
end
|
|
592
806
|
end
|
|
593
807
|
end
|