ably-pubsub-core 0.0.1.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/ably/agent.rb +8 -0
- data/lib/ably/auth.rb +791 -0
- data/lib/ably/exceptions.rb +169 -0
- data/lib/ably/logger.rb +119 -0
- data/lib/ably/models/auth_details.rb +45 -0
- data/lib/ably/models/channel_details.rb +69 -0
- data/lib/ably/models/channel_metrics.rb +107 -0
- data/lib/ably/models/channel_occupancy.rb +50 -0
- data/lib/ably/models/channel_options.rb +120 -0
- data/lib/ably/models/channel_state_change.rb +84 -0
- data/lib/ably/models/channel_status.rb +63 -0
- data/lib/ably/models/cipher_params.rb +137 -0
- data/lib/ably/models/connection_details.rb +149 -0
- data/lib/ably/models/connection_state_change.rb +83 -0
- data/lib/ably/models/delta_extras.rb +28 -0
- data/lib/ably/models/device_details.rb +126 -0
- data/lib/ably/models/device_push_details.rb +94 -0
- data/lib/ably/models/error_info.rb +104 -0
- data/lib/ably/models/http_paginated_response.rb +112 -0
- data/lib/ably/models/idiomatic_ruby_wrapper.rb +235 -0
- data/lib/ably/models/message.rb +223 -0
- data/lib/ably/models/message_encoders/base.rb +135 -0
- data/lib/ably/models/message_encoders/base64.rb +40 -0
- data/lib/ably/models/message_encoders/cipher.rb +84 -0
- data/lib/ably/models/message_encoders/json.rb +34 -0
- data/lib/ably/models/message_encoders/utf8.rb +26 -0
- data/lib/ably/models/nil_logger.rb +20 -0
- data/lib/ably/models/paginated_result.rb +201 -0
- data/lib/ably/models/presence_message.rb +244 -0
- data/lib/ably/models/protocol_message.rb +288 -0
- data/lib/ably/models/push_channel_subscription.rb +114 -0
- data/lib/ably/models/stats.rb +242 -0
- data/lib/ably/models/stats_types.rb +107 -0
- data/lib/ably/models/token_details.rb +148 -0
- data/lib/ably/models/token_request.rb +180 -0
- data/lib/ably/modules/ably.rb +26 -0
- data/lib/ably/modules/async_wrapper.rb +69 -0
- data/lib/ably/modules/channels_collection.rb +105 -0
- data/lib/ably/modules/conversions.rb +153 -0
- data/lib/ably/modules/encodeable.rb +111 -0
- data/lib/ably/modules/enum.rb +230 -0
- data/lib/ably/modules/event_emitter.rb +200 -0
- data/lib/ably/modules/event_machine_helpers.rb +26 -0
- data/lib/ably/modules/exception_codes.rb +128 -0
- data/lib/ably/modules/http_helpers.rb +41 -0
- data/lib/ably/modules/message_emitter.rb +78 -0
- data/lib/ably/modules/message_pack.rb +14 -0
- data/lib/ably/modules/model_common.rb +84 -0
- data/lib/ably/modules/safe_deferrable.rb +83 -0
- data/lib/ably/modules/safe_yield.rb +41 -0
- data/lib/ably/modules/state_emitter.rb +181 -0
- data/lib/ably/modules/state_machine.rb +59 -0
- data/lib/ably/modules/statesman_monkey_patch.rb +33 -0
- data/lib/ably/modules/uses_state_machine.rb +111 -0
- data/lib/ably/realtime/auth.rb +283 -0
- data/lib/ably/realtime/channel/channel_manager.rb +272 -0
- data/lib/ably/realtime/channel/channel_properties.rb +40 -0
- data/lib/ably/realtime/channel/channel_state_machine.rb +104 -0
- data/lib/ably/realtime/channel/publisher.rb +82 -0
- data/lib/ably/realtime/channel/push_channel.rb +69 -0
- data/lib/ably/realtime/channel.rb +440 -0
- data/lib/ably/realtime/channels.rb +71 -0
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +225 -0
- data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +87 -0
- data/lib/ably/realtime/client.rb +363 -0
- data/lib/ably/realtime/connection/connection_manager.rb +594 -0
- data/lib/ably/realtime/connection/connection_state_machine.rb +144 -0
- data/lib/ably/realtime/connection/websocket_transport.rb +287 -0
- data/lib/ably/realtime/connection.rb +738 -0
- data/lib/ably/realtime/models/nil_channel.rb +30 -0
- data/lib/ably/realtime/presence/members_map.rb +395 -0
- data/lib/ably/realtime/presence/presence_manager.rb +78 -0
- data/lib/ably/realtime/presence/presence_state_machine.rb +57 -0
- data/lib/ably/realtime/presence.rb +502 -0
- data/lib/ably/realtime/push/admin.rb +75 -0
- data/lib/ably/realtime/push/channel_subscriptions.rb +108 -0
- data/lib/ably/realtime/push/device_registrations.rb +105 -0
- data/lib/ably/realtime/push.rb +25 -0
- data/lib/ably/realtime/recovery_key_context.rb +36 -0
- data/lib/ably/realtime.rb +60 -0
- data/lib/ably/rest/channel/push_channel.rb +59 -0
- data/lib/ably/rest/channel.rb +194 -0
- data/lib/ably/rest/channels.rb +47 -0
- data/lib/ably/rest/client.rb +727 -0
- data/lib/ably/rest/middleware/encoder.rb +49 -0
- data/lib/ably/rest/middleware/exceptions.rb +53 -0
- data/lib/ably/rest/middleware/external_exceptions.rb +24 -0
- data/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +22 -0
- data/lib/ably/rest/middleware/logger.rb +58 -0
- data/lib/ably/rest/middleware/parse_json.rb +27 -0
- data/lib/ably/rest/middleware/parse_message_pack.rb +43 -0
- data/lib/ably/rest/presence.rb +113 -0
- data/lib/ably/rest/push/admin.rb +58 -0
- data/lib/ably/rest/push/channel_subscriptions.rb +121 -0
- data/lib/ably/rest/push/device_registrations.rb +114 -0
- data/lib/ably/rest/push.rb +25 -0
- data/lib/ably/rest.rb +40 -0
- data/lib/ably/util/ably_extensions.rb +29 -0
- data/lib/ably/util/crypto.rb +134 -0
- data/lib/ably/util/pub_sub.rb +45 -0
- data/lib/ably/util/safe_deferrable.rb +44 -0
- data/lib/ably/version.rb +7 -0
- data/lib/ably.rb +16 -0
- metadata +497 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require 'faraday'
|
|
2
|
+
require 'json'
|
|
3
|
+
|
|
4
|
+
module Ably
|
|
5
|
+
module Rest
|
|
6
|
+
module Middleware
|
|
7
|
+
# Encode the body of the message according to the mime type
|
|
8
|
+
class Encoder < Faraday::Middleware
|
|
9
|
+
CONTENT_TYPE = 'Content-Type'.freeze unless defined? CONTENT_TYPE
|
|
10
|
+
|
|
11
|
+
def call(env)
|
|
12
|
+
encode env if env.body
|
|
13
|
+
@app.call env
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
def encode(env)
|
|
18
|
+
env.body = case request_type(env)
|
|
19
|
+
when 'application/x-msgpack'
|
|
20
|
+
to_msgpack(env.body)
|
|
21
|
+
when 'application/json', '', nil
|
|
22
|
+
env.request_headers[CONTENT_TYPE] = 'application/json'
|
|
23
|
+
to_json(env.body)
|
|
24
|
+
else
|
|
25
|
+
env.body
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def to_msgpack(body)
|
|
30
|
+
body.to_msgpack
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def to_json(body)
|
|
34
|
+
if body.kind_of?(String)
|
|
35
|
+
body
|
|
36
|
+
else
|
|
37
|
+
body.to_json
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def request_type(env)
|
|
42
|
+
type = env.request_headers[CONTENT_TYPE].to_s
|
|
43
|
+
type = type.split(';', 2).first if type.index(';')
|
|
44
|
+
type
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require 'faraday'
|
|
2
|
+
require 'json'
|
|
3
|
+
|
|
4
|
+
module Ably
|
|
5
|
+
module Rest
|
|
6
|
+
module Middleware
|
|
7
|
+
# HTTP exceptions raised by Ably due to an error status code
|
|
8
|
+
# Ably returns JSON/Msgpack error codes and messages so include this if possible in the exception messages
|
|
9
|
+
class Exceptions < Faraday::Middleware
|
|
10
|
+
def on_complete(env)
|
|
11
|
+
if env.status >= 400
|
|
12
|
+
error_status_code = env.status
|
|
13
|
+
error_code = nil
|
|
14
|
+
|
|
15
|
+
if env.body.kind_of?(Hash)
|
|
16
|
+
error = env.body.fetch('error', {})
|
|
17
|
+
error_status_code = error['statusCode'].to_i if error['statusCode']
|
|
18
|
+
error_code = error['code'].to_i if error['code']
|
|
19
|
+
|
|
20
|
+
if error
|
|
21
|
+
message = "#{error['message']} (status: #{error_status_code}, code: #{error_code})"
|
|
22
|
+
else
|
|
23
|
+
message = env.body
|
|
24
|
+
end
|
|
25
|
+
else
|
|
26
|
+
message = env.body
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
message = 'Unknown server error' if message.to_s.strip == ''
|
|
30
|
+
request_id = env.request.context[:request_id] if env.request.context
|
|
31
|
+
exception_args = [message, error_status_code, error_code, nil, { request_id: request_id }]
|
|
32
|
+
|
|
33
|
+
if env.status >= 500
|
|
34
|
+
raise Ably::Exceptions::ServerError.new(*exception_args)
|
|
35
|
+
elsif env.status == 401
|
|
36
|
+
if Ably::Exceptions::TOKEN_EXPIRED_CODE.include?(error_code)
|
|
37
|
+
raise Ably::Exceptions::TokenExpired.new(*exception_args)
|
|
38
|
+
else
|
|
39
|
+
raise Ably::Exceptions::UnauthorizedRequest.new(*exception_args)
|
|
40
|
+
end
|
|
41
|
+
elsif env.status == 403
|
|
42
|
+
raise Ably::Exceptions::ForbiddenRequest.new(*exception_args)
|
|
43
|
+
elsif env.status == 404
|
|
44
|
+
raise Ably::Exceptions::ResourceMissing.new(*exception_args)
|
|
45
|
+
else
|
|
46
|
+
raise Ably::Exceptions::InvalidRequest.new(*exception_args)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'faraday'
|
|
2
|
+
|
|
3
|
+
module Ably
|
|
4
|
+
module Rest
|
|
5
|
+
module Middleware
|
|
6
|
+
# HTTP exceptions raised due to a status code error on a 3rd party site
|
|
7
|
+
# Used by auth calls
|
|
8
|
+
class ExternalExceptions < Faraday::Middleware
|
|
9
|
+
def on_complete(env)
|
|
10
|
+
if env.status >= 400
|
|
11
|
+
error_status_code = env.status
|
|
12
|
+
message = "Error #{error_status_code}: #{(env.body || '')[0...200]}"
|
|
13
|
+
|
|
14
|
+
if error_status_code >= 500
|
|
15
|
+
raise Ably::Exceptions::ServerError, message
|
|
16
|
+
else
|
|
17
|
+
raise Ably::Exceptions::InvalidRequest, message
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'faraday'
|
|
2
|
+
require 'json'
|
|
3
|
+
|
|
4
|
+
module Ably
|
|
5
|
+
module Rest
|
|
6
|
+
module Middleware
|
|
7
|
+
class FailIfUnsupportedMimeType < Faraday::Middleware
|
|
8
|
+
def on_complete(env)
|
|
9
|
+
unless env.response_headers['Ably-Middleware-Parsed'] == true
|
|
10
|
+
# Ignore empty body with success status code for no body response
|
|
11
|
+
return if env.body.to_s.empty? && env.status == 204
|
|
12
|
+
|
|
13
|
+
unless (500..599).include?(env.status)
|
|
14
|
+
raise Ably::Exceptions::InvalidResponseBody,
|
|
15
|
+
"Content Type #{env.response_headers['Content-Type']} is not supported by this client library"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require 'faraday'
|
|
2
|
+
|
|
3
|
+
module Ably
|
|
4
|
+
module Rest
|
|
5
|
+
module Middleware
|
|
6
|
+
class Logger < Faraday::Middleware
|
|
7
|
+
extend Forwardable
|
|
8
|
+
|
|
9
|
+
def initialize(app, logger = nil)
|
|
10
|
+
super(app)
|
|
11
|
+
@logger = logger || begin
|
|
12
|
+
require 'logger'
|
|
13
|
+
::Logger.new(STDOUT)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def_delegators :@logger, :debug, :info, :warn, :error, :fatal
|
|
18
|
+
|
|
19
|
+
def call(env)
|
|
20
|
+
debug { "=> URL: #{env.method} #{env.url}, Headers: #{dump_headers env.request_headers}" }
|
|
21
|
+
debug { "=> Body: #{body_for(env)}" }
|
|
22
|
+
super
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def on_complete(env)
|
|
26
|
+
debug "<= Status: #{env.status}, Headers: #{dump_headers env.response_headers}"
|
|
27
|
+
debug "<= Body: #{body_for(env)}"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
def dump_headers(headers)
|
|
32
|
+
headers.map { |k, v| "#{k}: #{v.inspect}" }.join(", ")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def body_for(env)
|
|
36
|
+
return '' if !env.body || env.body.empty?
|
|
37
|
+
|
|
38
|
+
if env.request_headers['Content-Type'] == 'application/x-msgpack'
|
|
39
|
+
MessagePack.unpack(env.body)
|
|
40
|
+
else
|
|
41
|
+
env.body
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
rescue StandardError
|
|
45
|
+
readable_body(env.body)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def readable_body(body)
|
|
49
|
+
if body.respond_to?(:encoding) && body.encoding == Encoding::ASCII_8BIT
|
|
50
|
+
body.unpack('H*')
|
|
51
|
+
else
|
|
52
|
+
body
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'faraday'
|
|
2
|
+
require 'json'
|
|
3
|
+
|
|
4
|
+
module Ably
|
|
5
|
+
module Rest
|
|
6
|
+
module Middleware
|
|
7
|
+
class ParseJson < Faraday::Middleware
|
|
8
|
+
def on_complete(env)
|
|
9
|
+
if env.response_headers['Content-Type'] == 'application/json'
|
|
10
|
+
env.body = parse(env.body) unless env.response_headers['Ably-Middleware-Parsed'] == true
|
|
11
|
+
env.response_headers['Ably-Middleware-Parsed'] = true
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def parse(body)
|
|
16
|
+
if body.length > 0
|
|
17
|
+
JSON.parse(body)
|
|
18
|
+
else
|
|
19
|
+
body
|
|
20
|
+
end
|
|
21
|
+
rescue JSON::ParserError => e
|
|
22
|
+
raise Ably::Exceptions::InvalidResponseBody, "Expected JSON response: #{e.message}"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
require 'faraday'
|
|
2
|
+
require 'msgpack'
|
|
3
|
+
|
|
4
|
+
module Ably
|
|
5
|
+
module Rest
|
|
6
|
+
module Middleware
|
|
7
|
+
class ParseMessagePack < Faraday::Middleware
|
|
8
|
+
def on_complete(env)
|
|
9
|
+
if env.response_headers['Content-Type'] == 'application/x-msgpack'
|
|
10
|
+
env.body = parse(env.body) unless env.response_headers['Ably-Middleware-Parsed'] == true
|
|
11
|
+
env.response_headers['Ably-Middleware-Parsed'] = true
|
|
12
|
+
end
|
|
13
|
+
rescue Ably::Exceptions::InvalidResponseBody => e
|
|
14
|
+
debug_info = {
|
|
15
|
+
method: env.method,
|
|
16
|
+
url: env.url,
|
|
17
|
+
base64_body: base64_body(env.body),
|
|
18
|
+
response_headers: env.response_headers
|
|
19
|
+
}
|
|
20
|
+
raise Ably::Exceptions::InvalidResponseBody, "#{e.message}\nRequest env: #{debug_info}"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def parse(body)
|
|
24
|
+
if body.length > 0
|
|
25
|
+
MessagePack.unpack(body)
|
|
26
|
+
else
|
|
27
|
+
body
|
|
28
|
+
end
|
|
29
|
+
rescue MessagePack::UnknownExtTypeError => e
|
|
30
|
+
raise Ably::Exceptions::InvalidResponseBody, "MessagePack::UnknownExtTypeError body could not be decoded: #{e.message}. Got Base64:\n#{base64_body(body)}"
|
|
31
|
+
rescue MessagePack::MalformedFormatError => e
|
|
32
|
+
raise Ably::Exceptions::InvalidResponseBody, "MessagePack::MalformedFormatError body could not be decoded: #{e.message}. Got Base64:\n#{base64_body(body)}"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def base64_body(body)
|
|
36
|
+
Base64.encode64(body)
|
|
37
|
+
rescue => err
|
|
38
|
+
"[#{err.message}! Could not base64 encode body: '#{body}']"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
module Ably
|
|
2
|
+
module Rest
|
|
3
|
+
# Enables the retrieval of the current and historic presence set for a channel.
|
|
4
|
+
#
|
|
5
|
+
class Presence
|
|
6
|
+
include Ably::Modules::Conversions
|
|
7
|
+
|
|
8
|
+
# {Ably::Rest::Client} for this Presence object
|
|
9
|
+
#
|
|
10
|
+
# @return {Ably::Rest::Client}
|
|
11
|
+
#
|
|
12
|
+
# @private
|
|
13
|
+
attr_reader :client
|
|
14
|
+
|
|
15
|
+
# {Ably::Rest::Channel} this Presence object is associated with
|
|
16
|
+
#
|
|
17
|
+
# @return [Ably::Rest::Channel]
|
|
18
|
+
#
|
|
19
|
+
attr_reader :channel
|
|
20
|
+
|
|
21
|
+
# Initialize a new Presence object
|
|
22
|
+
#
|
|
23
|
+
# @param client [Ably::Rest::Client]
|
|
24
|
+
# @param channel [Channel] The channel object
|
|
25
|
+
#
|
|
26
|
+
def initialize(client, channel)
|
|
27
|
+
@client = client
|
|
28
|
+
@channel = channel
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Retrieves the current members present on the channel and the metadata for each member, such as their
|
|
32
|
+
# {Ably::Models::PresenceMessage::ACTION} and ID. Returns a {Ably::Models::PaginatedResult} object,
|
|
33
|
+
# containing an array of {Ably::Models::PresenceMessage} objects.
|
|
34
|
+
#
|
|
35
|
+
# @spec RSPa, RSP3a, RSP3a2, RSP3a3
|
|
36
|
+
#
|
|
37
|
+
# @param [Hash] options the options for the set of members present
|
|
38
|
+
# @option options [Integer] :limit An upper limit on the number of messages returned. The default is 100, and the maximum is 1000. (RSP3a)
|
|
39
|
+
# @option options [String] :client_id Filters the list of returned presence members by a specific client using its ID. (RSP3a2)
|
|
40
|
+
# @option options [String] :connection_id Filters the list of returned presence members by a specific connection using its ID. (RSP3a3)
|
|
41
|
+
#
|
|
42
|
+
# @return [Ably::Models::PaginatedResult<Ably::Models::PresenceMessage>] A {Ably::Models::PaginatedResult} object containing an array of {Ably::Models::PresenceMessage} objects.
|
|
43
|
+
#
|
|
44
|
+
def get(options = {})
|
|
45
|
+
options = options = {
|
|
46
|
+
:limit => 100
|
|
47
|
+
}.merge(options)
|
|
48
|
+
|
|
49
|
+
paginated_options = {
|
|
50
|
+
coerce_into: 'Ably::Models::PresenceMessage',
|
|
51
|
+
async_blocking_operations: options.delete(:async_blocking_operations),
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
response = client.get(base_path, options)
|
|
55
|
+
|
|
56
|
+
Ably::Models::PaginatedResult.new(response, base_path, client, paginated_options) do |presence_message|
|
|
57
|
+
presence_message.tap do |message|
|
|
58
|
+
decode_message message
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Retrieves a {Ably::Models::PaginatedResult} object, containing an array of historical {Ably::Models::PresenceMessage}
|
|
64
|
+
# objects for the channel. If the channel is configured to persist messages, then presence messages can be retrieved
|
|
65
|
+
# from history for up to 72 hours in the past. If not, presence messages can only be retrieved from history for up to two minutes in the past.
|
|
66
|
+
#
|
|
67
|
+
# @spec RSP4a
|
|
68
|
+
#
|
|
69
|
+
# @param [Hash] options the options for the message history request
|
|
70
|
+
# @option options [Integer,Time] :start The time from which messages are retrieved, specified as milliseconds since the Unix epoch. (RSP4b1)
|
|
71
|
+
# @option options [Integer,Time] :end The time until messages are retrieved, specified as milliseconds since the Unix epoch. (RSP4b1)
|
|
72
|
+
# @option options [Symbol] :direction The order for which messages are returned in. Valid values are backwards which orders messages from most recent to oldest, or forwards which orders messages from oldest to most recent. The default is backwards. (RSP4b2)
|
|
73
|
+
# @option options [Integer] :limit An upper limit on the number of messages returned. The default is 100, and the maximum is 1000. (RSP4b3)
|
|
74
|
+
#
|
|
75
|
+
# @return [Ably::Models::PaginatedResult<Ably::Models::PresenceMessage>] A {Ably::Models::PaginatedResult} object containing an array of {Ably::Models::PresenceMessage} objects.
|
|
76
|
+
#
|
|
77
|
+
def history(options = {})
|
|
78
|
+
url = "#{base_path}/history"
|
|
79
|
+
options = options = {
|
|
80
|
+
:direction => :backwards,
|
|
81
|
+
:limit => 100
|
|
82
|
+
}.merge(options)
|
|
83
|
+
|
|
84
|
+
[:start, :end].each { |option| options[option] = as_since_epoch(options[option]) if options.has_key?(option) }
|
|
85
|
+
raise ArgumentError, ":end must be equal to or after :start" if options[:start] && options[:end] && (options[:start] > options[:end])
|
|
86
|
+
|
|
87
|
+
paginated_options = {
|
|
88
|
+
coerce_into: 'Ably::Models::PresenceMessage',
|
|
89
|
+
async_blocking_operations: options.delete(:async_blocking_operations),
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
response = client.get(url, options)
|
|
93
|
+
|
|
94
|
+
Ably::Models::PaginatedResult.new(response, url, client, paginated_options) do |presence_message|
|
|
95
|
+
presence_message.tap do |message|
|
|
96
|
+
decode_message message
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
private
|
|
102
|
+
def base_path
|
|
103
|
+
"/channels/#{URI.encode_www_form_component(channel.name)}/presence"
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def decode_message(presence_message)
|
|
107
|
+
presence_message.decode client.encoders, channel.options
|
|
108
|
+
rescue Ably::Exceptions::CipherError, Ably::Exceptions::EncoderError => e
|
|
109
|
+
client.logger.error { "Decoding Error on presence channel '#{channel.name}', presence message client_id '#{presence_message.client_id}'. #{e.class.name}: #{e.message}" }
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require 'ably/rest/push/device_registrations'
|
|
2
|
+
require 'ably/rest/push/channel_subscriptions'
|
|
3
|
+
|
|
4
|
+
module Ably::Rest
|
|
5
|
+
class Push
|
|
6
|
+
# Class providing push notification administrative functionality
|
|
7
|
+
# for registering devices and attaching to channels etc.
|
|
8
|
+
class Admin
|
|
9
|
+
include Ably::Modules::Conversions
|
|
10
|
+
|
|
11
|
+
# @api private
|
|
12
|
+
attr_reader :client
|
|
13
|
+
|
|
14
|
+
# @api private
|
|
15
|
+
attr_reader :push
|
|
16
|
+
|
|
17
|
+
def initialize(push)
|
|
18
|
+
@push = push
|
|
19
|
+
@client = push.client
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Publish a push message directly to a single recipient
|
|
23
|
+
#
|
|
24
|
+
# @param recipient [Hash] A recipient device, client_id or raw APNS/FCM/web target. Refer to push documentation
|
|
25
|
+
# @param data [Hash] The notification payload data and fields. Refer to push documentation
|
|
26
|
+
#
|
|
27
|
+
# @return [void]
|
|
28
|
+
#
|
|
29
|
+
def publish(recipient, data)
|
|
30
|
+
raise ArgumentError, "Expecting a Hash object for recipient, got #{recipient.class}" unless recipient.kind_of?(Hash)
|
|
31
|
+
raise ArgumentError, "Recipient data is empty. You must provide recipient details" if recipient.empty?
|
|
32
|
+
raise ArgumentError, "Expecting a Hash object for data, got #{data.class}" unless data.kind_of?(Hash)
|
|
33
|
+
raise ArgumentError, "Push data field is empty. You must provide attributes for the push notification" if data.empty?
|
|
34
|
+
|
|
35
|
+
publish_data = data.merge(recipient: IdiomaticRubyWrapper(recipient))
|
|
36
|
+
# Co-erce to camelCase for notitication fields which are always camelCase
|
|
37
|
+
publish_data[:notification] = IdiomaticRubyWrapper(data[:notification]) if publish_data[:notification].kind_of?(Hash)
|
|
38
|
+
client.post('/push/publish', publish_data)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Manage device registrations
|
|
42
|
+
#
|
|
43
|
+
# @return [Ably::Rest::Push::DeviceRegistrations]
|
|
44
|
+
#
|
|
45
|
+
def device_registrations
|
|
46
|
+
@device_registrations ||= DeviceRegistrations.new(self)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Manage channel subscriptions for devices or clients
|
|
50
|
+
#
|
|
51
|
+
# @return [Ably::Rest::Push::ChannelSubscriptions]
|
|
52
|
+
#
|
|
53
|
+
def channel_subscriptions
|
|
54
|
+
@channel_subscriptions ||= ChannelSubscriptions.new(self)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
module Ably::Rest
|
|
2
|
+
class Push
|
|
3
|
+
# Manage push notification channel subscriptions for devices or client identifiers
|
|
4
|
+
class ChannelSubscriptions
|
|
5
|
+
include Ably::Modules::Conversions
|
|
6
|
+
|
|
7
|
+
# @api private
|
|
8
|
+
attr_reader :client
|
|
9
|
+
|
|
10
|
+
# @api private
|
|
11
|
+
attr_reader :admin
|
|
12
|
+
|
|
13
|
+
def initialize(admin)
|
|
14
|
+
@admin = admin
|
|
15
|
+
@client = admin.client
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# List channel subscriptions filtered by optional params
|
|
19
|
+
#
|
|
20
|
+
# @param [Hash] params the filter options for the list channel subscription request. At least one of channel, client_id or device_id is required
|
|
21
|
+
# @option params [String] :channel filter by realtime pub/sub channel name
|
|
22
|
+
# @option params [String] :client_id filter by devices registered to a client identifier. If provided with device_id param, a concat operation is used so that any device with this client_id or provided device_id is returned.
|
|
23
|
+
# @option params [String] :device_id filter by unique device ID. If provided with client_id param, a concat operation is used so that any device with this device_id or provided client_id is returned.
|
|
24
|
+
# @option params [Integer] :limit maximum number of subscriptions to retrieve up to 1,000, defaults to 100
|
|
25
|
+
#
|
|
26
|
+
# @return [Ably::Models::PaginatedResult<Ably::Models::PushChannelSubscription>] Paginated list of matching {Ably::Models::PushChannelSubscription}
|
|
27
|
+
#
|
|
28
|
+
def list(params)
|
|
29
|
+
raise ArgumentError, "params must be a Hash" unless params.kind_of?(Hash)
|
|
30
|
+
|
|
31
|
+
if (IdiomaticRubyWrapper(params).keys & [:channel, :client_id, :device_id]).length == 0
|
|
32
|
+
raise ArgumentError, "at least one channel, client_id or device_id filter param must be provided"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
params = params.clone
|
|
36
|
+
|
|
37
|
+
paginated_options = {
|
|
38
|
+
coerce_into: 'Ably::Models::PushChannelSubscription',
|
|
39
|
+
async_blocking_operations: params.delete(:async_blocking_operations),
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
response = client.get('/push/channelSubscriptions', IdiomaticRubyWrapper(params).as_json)
|
|
43
|
+
|
|
44
|
+
Ably::Models::PaginatedResult.new(response, '', client, paginated_options)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# List channels with at least one subscribed device
|
|
48
|
+
#
|
|
49
|
+
# @param [Hash] params the options for the list channels request
|
|
50
|
+
# @option params [Integer] :limit maximum number of channels to retrieve up to 1,000, defaults to 100
|
|
51
|
+
#
|
|
52
|
+
# @return [Ably::Models::PaginatedResult<String>] Paginated list of matching {Ably::Models::PushChannelSubscription}
|
|
53
|
+
#
|
|
54
|
+
def list_channels(params = {})
|
|
55
|
+
params = {} if params.nil?
|
|
56
|
+
raise ArgumentError, "params must be a Hash" unless params.kind_of?(Hash)
|
|
57
|
+
|
|
58
|
+
params = params.clone
|
|
59
|
+
|
|
60
|
+
paginated_options = {
|
|
61
|
+
coerce_into: 'String',
|
|
62
|
+
async_blocking_operations: params.delete(:async_blocking_operations),
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
response = client.get('/push/channels', IdiomaticRubyWrapper(params).as_json)
|
|
66
|
+
|
|
67
|
+
Ably::Models::PaginatedResult.new(response, '', client, paginated_options)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Save push channel subscription for a device or client ID
|
|
71
|
+
#
|
|
72
|
+
# @param [Ably::Models::PushChannelSubscription,Hash] push_channel_subscription the push channel subscription details to save
|
|
73
|
+
#
|
|
74
|
+
# @return [void]
|
|
75
|
+
#
|
|
76
|
+
def save(push_channel_subscription)
|
|
77
|
+
push_channel_subscription_object = PushChannelSubscription(push_channel_subscription)
|
|
78
|
+
raise ArgumentError, "Channel is required yet is empty" if push_channel_subscription_object.channel.to_s.empty?
|
|
79
|
+
|
|
80
|
+
client.post("/push/channelSubscriptions", push_channel_subscription_object.as_json)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Remove a push channel subscription
|
|
84
|
+
#
|
|
85
|
+
# @param [Ably::Models::PushChannelSubscription,Hash] push_channel_subscription the push channel subscription details to remove
|
|
86
|
+
#
|
|
87
|
+
# @return [void]
|
|
88
|
+
#
|
|
89
|
+
def remove(push_channel_subscription)
|
|
90
|
+
push_channel_subscription_object = PushChannelSubscription(push_channel_subscription)
|
|
91
|
+
raise ArgumentError, "Channel is required yet is empty" if push_channel_subscription_object.channel.to_s.empty?
|
|
92
|
+
if push_channel_subscription_object.client_id.to_s.empty? && push_channel_subscription_object.device_id.to_s.empty?
|
|
93
|
+
raise ArgumentError, "Either client_id or device_id must be present"
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
client.delete("/push/channelSubscriptions", push_channel_subscription_object.as_json)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Remove all matching push channel subscriptions
|
|
100
|
+
#
|
|
101
|
+
# @param [Hash] params the filter options for the list channel subscription request. At least one of channel, client_id or device_id is required
|
|
102
|
+
# @option params [String] :channel filter by realtime pub/sub channel name
|
|
103
|
+
# @option params [String] :client_id filter by devices registered to a client identifier. If provided with device_id param, a concat operation is used so that any device with this client_id or provided device_id is returned.
|
|
104
|
+
# @option params [String] :device_id filter by unique device ID. If provided with client_id param, a concat operation is used so that any device with this device_id or provided client_id is returned.
|
|
105
|
+
#
|
|
106
|
+
# @return [void]
|
|
107
|
+
#
|
|
108
|
+
def remove_where(params)
|
|
109
|
+
raise ArgumentError, "params must be a Hash" unless params.kind_of?(Hash)
|
|
110
|
+
|
|
111
|
+
if (IdiomaticRubyWrapper(params).keys & [:channel, :client_id, :device_id]).length == 0
|
|
112
|
+
raise ArgumentError, "at least one channel, client_id or device_id filter param must be provided"
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
params = params.clone
|
|
116
|
+
|
|
117
|
+
client.delete("/push/channelSubscriptions", IdiomaticRubyWrapper(params).as_json)
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|