skyfall 0.1.2 → 0.1.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6bc7ba74da06a265f8b1af42633b4c462f87fbf5764b38505628d9255bd97831
4
- data.tar.gz: 9bbb8e00563fdab6ee906ce82a3ddcde4936b8f0ee187da4d4f8032f5f344446
3
+ metadata.gz: b1d8eea31c1c63f4aabd513a470083862b653f4fb1ba5a0a638da1d9e8cba3af
4
+ data.tar.gz: 15e57a872800ad3a0c2a0f38be1ed31694617e82fe0981e1c9a1598c9f70e325
5
5
  SHA512:
6
- metadata.gz: 440c94077709828c54814ed8a4d520dbb513755412fa5baaee443d55f2493833e5c9196ffbcee256a118b8bdc6e41d92479fa041514ab8d9d16978aa4ec1d4c7
7
- data.tar.gz: b43da862f141b6ed476036eee85c8514a3b93a7a93bcaa16e089ee6149137c6b548639351445b2d3bddb3e2c458e8dc6e2c3eaa3f71d2efb9d92dad734f2fbf1
6
+ metadata.gz: 84544c24ff44ed583df898567b260db3b23517de6248b105f6199de49f2a3bcfaaada701bb62252c59a44af2845321637cfa4ab5061b9f8f800d27107fb2f83e
7
+ data.tar.gz: fb62308d1e517fdd257633a19f503dc9f9fce7d5b6b4d778a588c19facbe8c45ff60cca3e242ae9024c4975c9ca84326083e4eeddedd0da8d34f3ae9f647d05b
data/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## [0.1.3] - 2023-07-04
2
+
3
+ - allow passing a previously saved cursor to websocket to replay any missed events
4
+ - the cursor is also kept in memory and automatically used when reconnecting
5
+ - added "connecting" callback with url as argument
6
+ - fixed connecting to websocket when endpoint is given as a string
7
+ - improved error handling during parsing
8
+
1
9
  ## [0.1.2] - 2023-06-15
2
10
 
3
11
  - added rkey property for Operation
@@ -4,4 +4,15 @@ module Skyfall
4
4
 
5
5
  class UnsupportedError < StandardError
6
6
  end
7
+
8
+ class SubscriptionError < StandardError
9
+ attr_reader :error_type, :error_message
10
+
11
+ def initialize(error_type, error_message = nil)
12
+ @error_type = error_type
13
+ @error_message = error_message
14
+
15
+ super("Subscription error: #{error_type}" + (error_message ? " (#{error_message})" : ""))
16
+ end
17
+ end
7
18
  end
@@ -1,4 +1,6 @@
1
1
  require_relative 'websocket_message'
2
+
3
+ require 'uri'
2
4
  require 'websocket-client-simple'
3
5
 
4
6
  module Skyfall
@@ -9,11 +11,12 @@ module Skyfall
9
11
  :subscribe_repos => SUBSCRIBE_REPOS
10
12
  }
11
13
 
12
- attr_accessor :heartbeat_timeout, :heartbeat_interval
14
+ attr_accessor :heartbeat_timeout, :heartbeat_interval, :cursor
13
15
 
14
- def initialize(server, endpoint)
16
+ def initialize(server, endpoint, cursor = nil)
15
17
  @endpoint = check_endpoint(endpoint)
16
18
  @server = check_hostname(server)
19
+ @cursor = cursor
17
20
  @handlers = {}
18
21
  @heartbeat_mutex = Mutex.new
19
22
  @heartbeat_interval = 5
@@ -24,19 +27,21 @@ module Skyfall
24
27
  def connect
25
28
  return if @websocket
26
29
 
27
- url = "wss://#{@server}/xrpc/#{@endpoint}"
30
+ url = build_websocket_url
28
31
  handlers = @handlers
29
32
  stream = self
30
33
 
34
+ handlers[:connecting]&.call(url)
35
+
31
36
  @websocket = WebSocket::Client::Simple.connect(url) do |ws|
32
37
  ws.on :message do |msg|
33
38
  stream.notify_heartbeat
34
- handlers[:raw_message]&.call(msg.data)
35
39
 
36
- if handlers[:message]
37
- atp_message = Skyfall::WebsocketMessage.new(msg.data)
38
- handlers[:message].call(atp_message)
39
- end
40
+ atp_message = Skyfall::WebsocketMessage.new(msg.data)
41
+ stream.cursor = atp_message.seq
42
+
43
+ handlers[:raw_message]&.call(msg.data)
44
+ handlers[:message]&.call(atp_message)
40
45
  end
41
46
 
42
47
  ws.on :open do
@@ -116,6 +121,10 @@ module Skyfall
116
121
  @handlers[:raw_message] = block
117
122
  end
118
123
 
124
+ def on_connecting(&block)
125
+ @handlers[:connecting] = block
126
+ end
127
+
119
128
  def on_connect(&block)
120
129
  @handlers[:connect] = block
121
130
  end
@@ -135,6 +144,12 @@ module Skyfall
135
144
 
136
145
  private
137
146
 
147
+ def build_websocket_url
148
+ url = "wss://#{@server}/xrpc/#{@endpoint}"
149
+ url += "?cursor=#{@cursor}" if @cursor
150
+ url
151
+ end
152
+
138
153
  def check_endpoint(endpoint)
139
154
  if endpoint.is_a?(String)
140
155
  raise ArgumentError("Invalid endpoint name: #{endpoint}") if endpoint.strip.empty? || !endpoint.include?('.')
@@ -144,6 +159,8 @@ module Skyfall
144
159
  else
145
160
  raise ArgumentError("Endpoint should be a string or a symbol")
146
161
  end
162
+
163
+ endpoint
147
164
  end
148
165
 
149
166
  def check_hostname(server)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Skyfall
4
- VERSION = "0.1.2"
4
+ VERSION = "0.1.3"
5
5
  end
@@ -15,15 +15,7 @@ module Skyfall
15
15
  attr_reader :type, :repo, :time, :seq, :commit, :prev, :blocks, :operations
16
16
 
17
17
  def initialize(data)
18
- objects = CBOR.decode_sequence(data)
19
- raise DecodeError.new("Invalid number of objects: #{objects.length}") unless objects.length == 2
20
-
21
- @type_object, @data_object = objects
22
- raise DecodeError.new("Invalid object type: #{@type_object}") unless @type_object.is_a?(Hash)
23
- raise DecodeError.new("Invalid object type: #{@data_object}") unless @data_object.is_a?(Hash)
24
- raise DecodeError.new("Missing data: #{@type_object}") unless @type_object['op'] && @type_object['t']
25
- raise DecodeError.new("Invalid message type: #{@type_object['t']}") unless @type_object['t'].start_with?('#')
26
- raise UnsupportedError.new("Unexpected CBOR object: #{@type_object}") unless @type_object['op'] == 1
18
+ @type_object, @data_object = decode_cbor_objects(data)
27
19
 
28
20
  @type = @type_object['t'][1..-1].to_sym
29
21
  @operations = []
@@ -54,5 +46,32 @@ module Skyfall
54
46
  vars = keys.map { |v| "#{v}=#{instance_variable_get(v).inspect}" }.join(", ")
55
47
  "#<#{self.class}:0x#{object_id} #{vars}>"
56
48
  end
49
+
50
+ private
51
+
52
+ def decode_cbor_objects(data)
53
+ objects = CBOR.decode_sequence(data)
54
+
55
+ if objects.length < 2
56
+ raise DecodeError.new("Malformed message: #{objects.inspect}")
57
+ elsif objects.length > 2
58
+ raise DecodeError.new("Invalid number of objects: #{objects.length}")
59
+ end
60
+
61
+ type_object, data_object = objects
62
+
63
+ if data_object['error']
64
+ raise SubscriptionError.new(data_object['error'], data_object['message'])
65
+ end
66
+
67
+ raise DecodeError.new("Invalid object type: #{type_object}") unless type_object.is_a?(Hash)
68
+ raise UnsupportedError.new("Unexpected CBOR object: #{type_object}") unless type_object['op'] == 1
69
+ raise DecodeError.new("Missing data: #{type_object} #{objects.inspect}") unless type_object['op'] && type_object['t']
70
+ raise DecodeError.new("Invalid message type: #{type_object['t']}") unless type_object['t'].start_with?('#')
71
+
72
+ raise DecodeError.new("Invalid object type: #{data_object}") unless data_object.is_a?(Hash)
73
+
74
+ [type_object, data_object]
75
+ end
57
76
  end
58
77
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: skyfall
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kuba Suder
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-15 00:00:00.000000000 Z
11
+ date: 2023-07-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base32