volcano-sdk 0.30.0 → 0.31.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 296cdb524e3552873235adccfe2d5c98b39a3da451c06777020b59d45c6a4be0
4
- data.tar.gz: 9ca783889458304d91b40601f673a4f5015f117ee4f30f90c23891e351cafe3a
3
+ metadata.gz: 36cf6c142c98f421f086d919b4d5e83034e95831d2b4246a892be57ebfaea9d5
4
+ data.tar.gz: 877909af108367683c4cbb434235baa27669af9d303dd0b52307919446f8c348
5
5
  SHA512:
6
- metadata.gz: 81067b14e84482e5dfebf935d27051c109ff14b87d144814a36db6ef5a7de6003e0cde0fe4ed4c54c1e06dcd9db79d8f5088c1aa21fbd4bbc3419da9a585f44e
7
- data.tar.gz: 4346575e985ab233b5afd723f36e1044b4e9e001b1eefa461a2514977f10a240a88a73978f30d88b2b74a3ec2a6bd4565d1e1ab92968e1988069492cbae9c739
6
+ metadata.gz: 832893a1b1ba6caa88f144b81d8b8e155d2041be82fccffe4328eeb1f59f4fc8e3ce84adbb476f3b1a6b26ed719da7293f9dfb6a563270cdcb5222125661f378
7
+ data.tar.gz: acfb489b98c3e8dcca7918ee1136d4e04f62fec2a7287fcc16fcd952edd3bfae2d4973b6307ef53d577a08cb4104511666917e4435c3d7722bb3cde544d7ad3d
data/lib/volcano/auth.rb CHANGED
@@ -9,9 +9,7 @@ module Volcano
9
9
  include AuthUpdateUser
10
10
  include AuthEmailChange
11
11
  include AuthSessions
12
-
13
- INCOMPLETE_SESSION = 'Expected a complete Volcano::Session'
14
- private_constant :INCOMPLETE_SESSION
12
+ include AuthSessionConstruction
15
13
 
16
14
  def initialize(client, transport, api_url:)
17
15
  @client = client
@@ -27,10 +25,10 @@ module Volcano
27
25
  @client.store_session(owned_complete_session(session), event: nil)
28
26
  end
29
27
 
30
- def on_auth_state_change(&callback)
31
- raise ArgumentError, 'callback block required' unless callback
28
+ def on_auth_state_change(&)
29
+ raise ArgumentError, 'callback block required' unless block_given?
32
30
 
33
- @client.subscribe_auth_state_change(&callback)
31
+ @client.subscribe_auth_state_change(&)
34
32
  end
35
33
 
36
34
  def sign_in(email:, password:)
@@ -77,47 +75,5 @@ module Volcano
77
75
  )
78
76
  end
79
77
  end
80
-
81
- def build_session(payload)
82
- owned_session(
83
- access_token: payload.fetch('access_token'),
84
- refresh_token: payload.fetch('refresh_token'),
85
- user_id: payload.fetch('user').fetch('id'),
86
- user: payload.fetch('user')
87
- )
88
- end
89
-
90
- def parse_refresh_session(payload)
91
- owned_complete_session(build_session(payload))
92
- rescue KeyError, TypeError, NoMethodError, ArgumentError => e
93
- raise Error::TransportError, INCOMPLETE_SESSION, cause: e
94
- end
95
-
96
- def complete_session?(session)
97
- return false unless session.is_a?(Session)
98
-
99
- values = [session.access_token, session.refresh_token, session.user_id]
100
- values.all? { |value| value.is_a?(String) && !value.strip.empty? } &&
101
- matching_session_user?(session)
102
- end
103
-
104
- def matching_session_user?(session)
105
- session.user.nil? || (session.user.is_a?(Hash) && session.user['id'] == session.user_id)
106
- end
107
-
108
- def owned_complete_session(session)
109
- raise ArgumentError, INCOMPLETE_SESSION unless complete_session?(session)
110
-
111
- owned_session(**session.to_h)
112
- end
113
-
114
- def owned_session(access_token:, refresh_token:, user_id:, user: nil)
115
- Session.new(
116
- access_token: access_token.dup.freeze,
117
- refresh_token: refresh_token.dup.freeze,
118
- user_id: user_id.dup.freeze,
119
- user: user
120
- )
121
- end
122
78
  end
123
79
  end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Volcano
4
+ # Builds and validates owned credential snapshots for the auth facade.
5
+ module AuthSessionConstruction
6
+ INCOMPLETE_SESSION = 'Expected a complete Volcano::Session'
7
+ private_constant :INCOMPLETE_SESSION
8
+
9
+ private
10
+
11
+ def build_session(payload)
12
+ raise TypeError, INCOMPLETE_SESSION unless payload.is_a?(Hash)
13
+
14
+ user = payload.fetch('user')
15
+ raise TypeError, INCOMPLETE_SESSION unless user.is_a?(Hash)
16
+
17
+ owned_session(
18
+ access_token: required_session_string(payload.fetch('access_token')),
19
+ refresh_token: required_session_string(payload.fetch('refresh_token')),
20
+ user_id: required_session_string(user.fetch('id')),
21
+ user: user
22
+ )
23
+ end
24
+
25
+ def parse_refresh_session(payload)
26
+ owned_complete_session(build_session(payload))
27
+ rescue KeyError, TypeError, NoMethodError, ArgumentError => e
28
+ raise Error::TransportError, INCOMPLETE_SESSION, cause: e
29
+ end
30
+
31
+ def complete_session?(session)
32
+ values = [session.access_token, session.refresh_token, session.user_id]
33
+ values.all? { |value| value.is_a?(String) && !value.strip.empty? } &&
34
+ matching_session_user?(session)
35
+ end
36
+
37
+ def matching_session_user?(session)
38
+ session.user.nil? || (session.user.is_a?(Hash) && session.user['id'] == session.user_id)
39
+ end
40
+
41
+ def owned_complete_session(session)
42
+ raise ArgumentError, INCOMPLETE_SESSION unless session.is_a?(Session) && complete_session?(session)
43
+
44
+ owned_session(
45
+ access_token: required_session_string(session.access_token),
46
+ refresh_token: required_session_string(session.refresh_token),
47
+ user_id: required_session_string(session.user_id),
48
+ user: session.user
49
+ )
50
+ end
51
+
52
+ def required_session_string(value)
53
+ return value if value.is_a?(String) && !value.strip.empty?
54
+
55
+ raise ArgumentError, INCOMPLETE_SESSION
56
+ end
57
+
58
+ def owned_session(access_token:, refresh_token:, user_id:, user: nil)
59
+ Session.new(
60
+ access_token: access_token.dup.freeze,
61
+ refresh_token: refresh_token.dup.freeze,
62
+ user_id: user_id.dup.freeze,
63
+ user: user
64
+ )
65
+ end
66
+ end
67
+ end
@@ -29,6 +29,8 @@ module Volcano
29
29
  end
30
30
 
31
31
  def build_sign_up_result(payload)
32
+ raise TypeError, INVALID_SIGN_UP_RESULT unless payload.is_a?(Hash)
33
+
32
34
  confirmation_required = payload.fetch('confirmation_required')
33
35
  message = payload.fetch('message')
34
36
  valid = [true, false].include?(confirmation_required) && message.is_a?(String)
@@ -6,8 +6,8 @@ module Volcano
6
6
  # Entry point for project object storage.
7
7
  class Storage
8
8
  def initialize(client, transport, api_url:, anon_key:)
9
- @client = client
10
- @transport = transport
9
+ @client = client.is_a?(StorageSessionAdapter) ? client : StorageSessionAdapter.new(client)
10
+ @transport = transport.is_a?(StorageTransportAdapter) ? transport : StorageTransportAdapter.new(transport)
11
11
  @api_url = api_url
12
12
  @anon_key = anon_key
13
13
  end
@@ -25,9 +25,11 @@ module Volcano
25
25
 
26
26
  # Operates on objects in one storage bucket.
27
27
  class StorageBucket
28
+ include StorageObjectResponse
29
+
28
30
  def initialize(client, transport, name, api_url:, anon_key:)
29
- @client = client
30
- @transport = transport
31
+ @client = client.is_a?(StorageSessionAdapter) ? client : StorageSessionAdapter.new(client)
32
+ @transport = transport.is_a?(StorageTransportAdapter) ? transport : StorageTransportAdapter.new(transport)
31
33
  @name = name.dup.freeze
32
34
  @api_url = api_url.dup.freeze
33
35
  @anon_key = anon_key.dup.freeze
@@ -36,7 +38,7 @@ module Volcano
36
38
 
37
39
  def upload(path, value, content_type: nil)
38
40
  mime_type = upload_content_type(content_type)
39
- path = storage_paths(path).first
41
+ path = storage_paths(path).fetch(0)
40
42
  binding = @client.capture_session_binding
41
43
  @client.session_token
42
44
  content = upload_bytes(value).freeze
@@ -49,11 +51,11 @@ module Volcano
49
51
  content_type: mime_type
50
52
  )
51
53
  end
52
- Transport.body(response, 201)
54
+ storage_payload(Transport.body(response, 201))
53
55
  end
54
56
 
55
57
  def download(path, range: nil)
56
- path = storage_paths(path).first
58
+ path = storage_paths(path).fetch(0)
57
59
  range = range&.dup&.freeze
58
60
  response = storage_request do |token|
59
61
  @transport.download_storage_object(
@@ -65,7 +67,7 @@ module Volcano
65
67
  end
66
68
  expected_status = range && response.status == 206 ? 206 : 200
67
69
  Transport.body(response, expected_status)
68
- response.data.b
70
+ storage_string(response.data).b
69
71
  end
70
72
 
71
73
  def list(prefix = '', limit: nil, cursor: nil)
@@ -80,11 +82,10 @@ module Volcano
80
82
  cursor: cursor
81
83
  )
82
84
  end
83
- payload = Transport.body(response, 200)
84
- next_cursor = payload['next_cursor']
85
+ payload = storage_payload(Transport.body(response, 200))
85
86
  StoragePage.new(
86
- objects: payload.fetch('objects', []).map { |object| storage_object(object) },
87
- next_cursor: next_cursor == '' ? nil : next_cursor
87
+ objects: storage_object_list(payload.fetch('objects', [])),
88
+ next_cursor: storage_cursor(payload['next_cursor'])
88
89
  )
89
90
  end
90
91
 
@@ -102,31 +103,14 @@ module Volcano
102
103
  end
103
104
 
104
105
  def upload_bytes(value)
105
- bytes = value.respond_to?(:read) ? value.read : value
106
+ bytes = if value.is_a?(String)
107
+ value
108
+ elsif value.respond_to?(:read)
109
+ value.read
110
+ end
106
111
  raise ArgumentError, 'upload data must be a String or IO' unless bytes.is_a?(String)
107
112
 
108
113
  bytes.b
109
114
  end
110
-
111
- def storage_object(payload)
112
- StorageObject.new(
113
- id: payload.fetch('id'),
114
- bucket_id: payload.fetch('bucket_id'),
115
- name: payload.fetch('name'),
116
- size: payload.fetch('size'),
117
- mime_type: payload.fetch('mime_type'),
118
- is_public: payload.fetch('is_public'),
119
- owner_id: payload['owner_id'],
120
- etag: payload['etag'],
121
- metadata: payload['metadata'],
122
- created_at: parse_time(payload['created_at']),
123
- updated_at: parse_time(payload['updated_at']),
124
- public_url: payload['public_url']
125
- )
126
- end
127
-
128
- def parse_time(value)
129
- value.is_a?(String) ? Time.iso8601(value) : value
130
- end
131
115
  end
132
116
  end
@@ -6,7 +6,13 @@ module Volcano
6
6
  ].freeze
7
7
  private_constant :STORAGE_OBJECT_ATTRIBUTES
8
8
 
9
- StorageObject = Data.define(*STORAGE_OBJECT_ATTRIBUTES) do
9
+ StorageObject = Data.define(*STORAGE_OBJECT_ATTRIBUTES)
10
+
11
+ # Validates and freezes object metadata returned by storage.
12
+ class StorageObject
13
+ # @dynamic id, bucket_id, name, size, mime_type, is_public, owner_id, etag
14
+ # @dynamic metadata, created_at, updated_at, public_url
15
+ # @dynamic members, with, to_h, deconstruct, deconstruct_keys, self.[], self.members
10
16
  def initialize(**attributes)
11
17
  unknown = attributes.keys - STORAGE_OBJECT_ATTRIBUTES
12
18
  raise ArgumentError, "unknown keywords: #{unknown.join(', ')}" unless unknown.empty?
@@ -17,7 +23,8 @@ module Volcano
17
23
  values = STORAGE_OBJECT_ATTRIBUTES.to_h do |name|
18
24
  [name, immutable_value(attributes[name])]
19
25
  end
20
- super(**values)
26
+ attributes = values
27
+ super
21
28
  end
22
29
 
23
30
  private
@@ -40,10 +47,16 @@ module Volcano
40
47
  end
41
48
  end
42
49
 
43
- StoragePage = Data.define(:objects, :next_cursor) do
50
+ StoragePage = Data.define(:objects, :next_cursor)
51
+
52
+ # Freezes a page of storage objects and its cursor.
53
+ class StoragePage
54
+ # @dynamic objects, next_cursor, members, with, to_h, deconstruct, deconstruct_keys
55
+ # @dynamic self.[], self.members
44
56
  def initialize(objects:, next_cursor: nil)
45
- cursor = next_cursor.nil? ? nil : next_cursor.dup.freeze
46
- super(objects: objects.to_a.dup.freeze, next_cursor: cursor)
57
+ objects = objects.to_a.dup.freeze
58
+ next_cursor = next_cursor.nil? ? nil : next_cursor.dup.freeze
59
+ super
47
60
  end
48
61
  end
49
62
  end
@@ -11,7 +11,9 @@ module Volcano
11
11
  end
12
12
 
13
13
  def move(from_path, to_path)
14
- source, destination = storage_paths([from_path, to_path])
14
+ paths = storage_paths([from_path, to_path])
15
+ source = paths.fetch(0)
16
+ destination = paths.fetch(1)
15
17
  response = storage_request do |token|
16
18
  @transport.move_storage_object(
17
19
  authorization: token,
@@ -24,7 +26,9 @@ module Volcano
24
26
  end
25
27
 
26
28
  def copy(from_path, to_path)
27
- source, destination = storage_paths([from_path, to_path])
29
+ paths = storage_paths([from_path, to_path])
30
+ source = paths.fetch(0)
31
+ destination = paths.fetch(1)
28
32
  response = storage_request do |token|
29
33
  @transport.copy_storage_object(
30
34
  authorization: token,
@@ -75,7 +79,8 @@ module Volcano
75
79
  end
76
80
 
77
81
  def visibility_value(value)
78
- return value if [true, false].include?(value)
82
+ return true if value == true
83
+ return false if value == false
79
84
 
80
85
  raise ArgumentError, 'public must be true or false'
81
86
  end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Volcano
4
+ # Validates object metadata and paginated storage responses.
5
+ module StorageObjectResponse
6
+ include StorageResponse
7
+
8
+ private
9
+
10
+ def storage_object(payload)
11
+ payload = storage_payload(payload)
12
+ required = storage_object_required(payload)
13
+ optional = storage_object_optional(payload)
14
+ StorageObject.new(
15
+ id: required.fetch(0), bucket_id: required.fetch(1), name: required.fetch(2),
16
+ size: required.fetch(3), mime_type: required.fetch(4), is_public: required.fetch(5),
17
+ owner_id: optional.fetch(0), etag: optional.fetch(1), metadata: optional.fetch(2),
18
+ created_at: optional.fetch(3), updated_at: optional.fetch(4), public_url: optional.fetch(5)
19
+ )
20
+ end
21
+
22
+ def storage_object_required(payload)
23
+ [
24
+ storage_string(payload.fetch('id')), storage_string(payload.fetch('bucket_id')),
25
+ storage_string(payload.fetch('name')), storage_integer(payload.fetch('size')),
26
+ storage_string(payload.fetch('mime_type')), storage_boolean(payload.fetch('is_public'))
27
+ ]
28
+ end
29
+
30
+ def storage_object_optional(payload)
31
+ [
32
+ storage_optional_string(payload['owner_id']), storage_optional_string(payload['etag']),
33
+ storage_optional_metadata(payload['metadata']), storage_optional_time(payload['created_at']),
34
+ storage_optional_time(payload['updated_at']), storage_optional_string(payload['public_url'])
35
+ ]
36
+ end
37
+
38
+ def storage_object_list(value)
39
+ raise Error::TransportError, 'invalid storage objects' unless value.is_a?(Array)
40
+
41
+ value.map { |object| storage_object(object) }
42
+ end
43
+
44
+ def storage_cursor(value)
45
+ return if value.nil? || value == ''
46
+
47
+ storage_string(value)
48
+ end
49
+
50
+ def storage_boolean(value)
51
+ return true if value == true
52
+ return false if value == false
53
+
54
+ raise Error::TransportError, 'invalid storage boolean'
55
+ end
56
+
57
+ def storage_optional_string(value)
58
+ value.nil? ? nil : storage_string(value)
59
+ end
60
+
61
+ def storage_optional_time(value)
62
+ value.nil? ? nil : storage_time(value)
63
+ end
64
+
65
+ def storage_optional_metadata(value)
66
+ value.nil? ? nil : storage_json_hash(value)
67
+ end
68
+
69
+ def storage_json_hash(value)
70
+ raise Error::TransportError, 'invalid storage JSON object' unless value.is_a?(Hash)
71
+
72
+ value.to_h do |key, item|
73
+ raise Error::TransportError, 'invalid storage JSON key' unless key.is_a?(String)
74
+
75
+ [key, storage_json_value(item)]
76
+ end
77
+ end
78
+
79
+ def storage_json_value(value)
80
+ case value
81
+ when NilClass, TrueClass, FalseClass, Integer, Float, String then value
82
+ when Array then value.map { |item| storage_json_value(item) }
83
+ when Hash then storage_json_hash(value)
84
+ else raise Error::TransportError, 'invalid storage JSON value'
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Volcano
4
+ # Decodes resumable-upload responses before creating public records.
5
+ module StorageResponse
6
+ private
7
+
8
+ def upload_session(payload)
9
+ payload = storage_payload(payload)
10
+ UploadSession.new(
11
+ session_id: storage_string(payload.fetch('session_id')),
12
+ part_size: storage_integer(payload.fetch('part_size')),
13
+ total_parts: storage_integer(payload.fetch('total_parts')),
14
+ expires_at: storage_time(payload.fetch('expires_at'))
15
+ )
16
+ end
17
+
18
+ def upload_part_metadata(payload)
19
+ payload = storage_payload(payload)
20
+ UploadPart.new(
21
+ part_number: storage_integer(payload.fetch('part_number')),
22
+ etag: storage_string(payload.fetch('etag')),
23
+ size: storage_integer(payload.fetch('size'))
24
+ )
25
+ end
26
+
27
+ def upload_session_status(payload)
28
+ payload = storage_payload(payload)
29
+ text = storage_status_text(payload)
30
+ counts = storage_status_counts(payload)
31
+ storage_status_record(payload, text, counts)
32
+ end
33
+
34
+ def storage_status_record(payload, text, counts)
35
+ UploadSessionStatus.new(
36
+ session_id: text.fetch(0), status: text.fetch(1), path: text.fetch(2),
37
+ content_type: text.fetch(3), total_size: counts.fetch(0), part_size: counts.fetch(1),
38
+ total_parts: counts.fetch(2), parts_uploaded: counts.fetch(3),
39
+ bytes_uploaded: counts.fetch(4), parts: storage_parts(payload.fetch('parts', [])),
40
+ expires_at: storage_time(payload.fetch('expires_at')),
41
+ created_at: storage_time(payload.fetch('created_at'))
42
+ )
43
+ end
44
+
45
+ def storage_status_text(payload)
46
+ [
47
+ storage_string(payload.fetch('session_id')), storage_string(payload.fetch('status')),
48
+ storage_string(payload.fetch('path')), storage_string(payload.fetch('content_type'))
49
+ ]
50
+ end
51
+
52
+ def storage_status_counts(payload)
53
+ [
54
+ storage_integer(payload.fetch('total_size')), storage_integer(payload.fetch('part_size')),
55
+ storage_integer(payload.fetch('total_parts')), storage_integer(payload.fetch('parts_uploaded')),
56
+ storage_integer(payload.fetch('bytes_uploaded'))
57
+ ]
58
+ end
59
+
60
+ def storage_parts(value)
61
+ raise Error::TransportError, 'invalid upload parts' unless value.is_a?(Array)
62
+
63
+ value.map { |part| upload_part_metadata(part) }
64
+ end
65
+
66
+ def storage_payload(value)
67
+ raise Error::TransportError, 'invalid storage response' unless value.is_a?(Hash)
68
+ raise Error::TransportError, 'invalid storage response key' unless value.keys.all?(String)
69
+
70
+ value
71
+ end
72
+
73
+ def storage_string(value)
74
+ raise Error::TransportError, 'invalid storage string' unless value.is_a?(String)
75
+
76
+ value
77
+ end
78
+
79
+ def storage_integer(value)
80
+ raise Error::TransportError, 'invalid storage integer' unless value.is_a?(Integer)
81
+
82
+ value
83
+ end
84
+
85
+ def storage_time(value)
86
+ return value if value.is_a?(Time)
87
+ return Time.iso8601(value) if value.is_a?(String)
88
+
89
+ raise Error::TransportError, 'invalid storage time'
90
+ end
91
+ end
92
+ end
@@ -30,7 +30,7 @@ module Volcano
30
30
  def upload_session_parts(path, source, session, total_size, on_progress)
31
31
  uploaded = 0
32
32
  session.total_parts.times do |part_index|
33
- part = source.read(session.part_size)
33
+ part = source.read(session.part_size) || raise(IOError, 'upload source ended before completion')
34
34
  upload_part(
35
35
  path,
36
36
  session_id: session.session_id,
@@ -64,11 +64,22 @@ module Volcano
64
64
  def remaining_upload_bytes(source)
65
65
  return unless source.respond_to?(:size) && source.respond_to?(:pos)
66
66
 
67
- source.size - source.pos
67
+ dimensions = upload_source_dimensions(source)
68
+ return unless dimensions
69
+
70
+ dimensions.fetch(0) - dimensions.fetch(1)
68
71
  rescue IOError, SystemCallError
69
72
  nil
70
73
  end
71
74
 
75
+ def upload_source_dimensions(source)
76
+ size = source.method(:size).call
77
+ position = source.method(:pos).call
78
+ return unless size.is_a?(Integer) && position.is_a?(Integer)
79
+
80
+ [size, position]
81
+ end
82
+
72
83
  def with_spooled_upload_source(source)
73
84
  Tempfile.create('volcano-upload') do |file|
74
85
  file.binmode
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Volcano
4
+ # Validates the client methods that storage uses for session-scoped requests.
5
+ class StorageSessionAdapter
6
+ def initialize(client)
7
+ @client = client
8
+ end
9
+
10
+ def capture_session_binding
11
+ binding = storage_binding_array(@client.method(:capture_session_binding).call)
12
+ generation, owner, session = binding
13
+ [storage_generation(generation), storage_owner(owner), storage_session(session)]
14
+ end
15
+
16
+ def session_token
17
+ token = @client.method(:session_token).call
18
+ raise Error::TransportError, 'invalid storage session token' unless token.is_a?(String)
19
+
20
+ token
21
+ end
22
+
23
+ def session_request(binding:, &)
24
+ method_name = :session_request
25
+ response = @client.public_send(method_name, binding: binding) do |token|
26
+ raise Error::TransportError, 'invalid storage session token' unless token.is_a?(String)
27
+
28
+ yield token
29
+ end
30
+ raise Error::TransportError, 'invalid storage session response' unless response.is_a?(Transport::Response)
31
+
32
+ response
33
+ end
34
+
35
+ private
36
+
37
+ def storage_binding_array(value)
38
+ raise Error::TransportError, 'invalid storage session binding' unless value.is_a?(Array) && value.size == 3
39
+
40
+ value
41
+ end
42
+
43
+ def storage_generation(value)
44
+ raise Error::TransportError, 'invalid storage session binding' unless value.is_a?(Integer)
45
+
46
+ value
47
+ end
48
+
49
+ def storage_owner(value)
50
+ raise Error::TransportError, 'invalid storage session binding' unless value.is_a?(SessionOperations)
51
+
52
+ value
53
+ end
54
+
55
+ def storage_session(value)
56
+ raise Error::TransportError, 'invalid storage session binding' unless value.nil? || value.is_a?(Session)
57
+
58
+ value
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Volcano
4
+ # Narrows generated and test transport calls to checked storage responses.
5
+ class StorageTransportAdapter
6
+ def initialize(transport)
7
+ @transport = transport
8
+ end
9
+
10
+ def upload_storage_object(authorization:, bucket_name:, path:, data:, content_type:)
11
+ invoke(:upload_storage_object, authorization: authorization, bucket_name: bucket_name,
12
+ path: path, data: data, content_type: content_type)
13
+ end
14
+
15
+ def download_storage_object(authorization:, bucket_name:, path:, byte_range:)
16
+ invoke(:download_storage_object, authorization: authorization, bucket_name: bucket_name,
17
+ path: path, byte_range: byte_range)
18
+ end
19
+
20
+ def list_storage_objects(authorization:, bucket_name:, prefix:, limit:, cursor:)
21
+ invoke(:list_storage_objects, authorization: authorization, bucket_name: bucket_name,
22
+ prefix: prefix, limit: limit, cursor: cursor)
23
+ end
24
+
25
+ def delete_storage_object(authorization:, bucket_name:, path:)
26
+ invoke(:delete_storage_object, authorization: authorization, bucket_name: bucket_name, path: path)
27
+ end
28
+
29
+ def move_storage_object(authorization:, bucket_name:, from_path:, to_path:)
30
+ invoke(:move_storage_object, authorization: authorization, bucket_name: bucket_name,
31
+ from_path: from_path, to_path: to_path)
32
+ end
33
+
34
+ def copy_storage_object(authorization:, bucket_name:, from_path:, to_path:)
35
+ invoke(:copy_storage_object, authorization: authorization, bucket_name: bucket_name,
36
+ from_path: from_path, to_path: to_path)
37
+ end
38
+
39
+ def update_storage_object_visibility(authorization:, bucket_name:, path:, is_public:)
40
+ invoke(:update_storage_object_visibility, authorization: authorization, bucket_name: bucket_name,
41
+ path: path, is_public: is_public)
42
+ end
43
+
44
+ def create_upload_session(authorization:, bucket_name:, request:)
45
+ invoke(:create_upload_session, authorization: authorization, bucket_name: bucket_name, request: request)
46
+ end
47
+
48
+ def upload_part(authorization:, bucket_name:, request:)
49
+ invoke(:upload_part, authorization: authorization, bucket_name: bucket_name, request: request)
50
+ end
51
+
52
+ def complete_upload_session(authorization:, bucket_name:, request:)
53
+ invoke(:complete_upload_session, authorization: authorization, bucket_name: bucket_name, request: request)
54
+ end
55
+
56
+ def get_upload_session(authorization:, bucket_name:, request:)
57
+ invoke(:get_upload_session, authorization: authorization, bucket_name: bucket_name, request: request)
58
+ end
59
+
60
+ def abort_upload_session(authorization:, bucket_name:, request:)
61
+ invoke(:abort_upload_session, authorization: authorization, bucket_name: bucket_name, request: request)
62
+ end
63
+
64
+ private
65
+
66
+ def invoke(name, **arguments)
67
+ response = @transport.public_send(name, **arguments)
68
+ raise Error::TransportError, 'invalid storage transport response' unless response.is_a?(Transport::Response)
69
+
70
+ response
71
+ end
72
+ end
73
+ end
@@ -1,51 +1,68 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Volcano
4
- UploadSessionRequest = Data.define(:path, :content_type, :total_size, :part_size) do
4
+ UploadSessionRequest = Data.define(:path, :content_type, :total_size, :part_size)
5
+
6
+ # Freezes the arguments sent to create a resumable upload.
7
+ class UploadSessionRequest
8
+ # @dynamic path, content_type, total_size, part_size
5
9
  def initialize(path:, content_type:, total_size:, part_size: nil)
6
- super(
7
- path: path.dup.freeze,
8
- content_type: content_type.dup.freeze,
9
- total_size: total_size,
10
- part_size: part_size
11
- )
10
+ path = path.dup.freeze
11
+ content_type = content_type.dup.freeze
12
+ super
12
13
  end
13
14
  end
14
15
  private_constant :UploadSessionRequest
15
16
 
16
- UploadPartRequest = Data.define(:path, :session_id, :part_number, :data) do
17
+ UploadPartRequest = Data.define(:path, :session_id, :part_number, :data)
18
+
19
+ # Freezes one resumable upload part before transport dispatch.
20
+ class UploadPartRequest
21
+ # @dynamic path, session_id, part_number, data
17
22
  def initialize(path:, session_id:, part_number:, data:)
18
- super(
19
- path: path.dup.freeze,
20
- session_id: session_id.dup.freeze,
21
- part_number: part_number,
22
- data: data.dup.freeze
23
- )
23
+ path = path.dup.freeze
24
+ session_id = session_id.dup.freeze
25
+ data = data.dup.freeze
26
+ super
24
27
  end
25
28
  end
26
29
  private_constant :UploadPartRequest
27
30
 
28
- UploadSessionReference = Data.define(:path, :session_id) do
31
+ UploadSessionReference = Data.define(:path, :session_id)
32
+
33
+ # Freezes the path and identifier used for upload session operations.
34
+ class UploadSessionReference
35
+ # @dynamic path, session_id
29
36
  def initialize(path:, session_id:)
30
- super(path: path.dup.freeze, session_id: session_id.dup.freeze)
37
+ path = path.dup.freeze
38
+ session_id = session_id.dup.freeze
39
+ super
31
40
  end
32
41
  end
33
42
  private_constant :UploadSessionReference
34
43
 
35
- UploadSession = Data.define(:session_id, :part_size, :total_parts, :expires_at) do
44
+ UploadSession = Data.define(:session_id, :part_size, :total_parts, :expires_at)
45
+
46
+ # Describes a newly created upload session.
47
+ class UploadSession
48
+ # @dynamic session_id, part_size, total_parts, expires_at, members, with, to_h, deconstruct, deconstruct_keys
49
+ # @dynamic self.[], self.members
36
50
  def initialize(session_id:, part_size:, total_parts:, expires_at:)
37
- super(
38
- session_id: session_id.dup.freeze,
39
- part_size: part_size,
40
- total_parts: total_parts,
41
- expires_at: expires_at.dup.freeze
42
- )
51
+ session_id = session_id.dup.freeze
52
+ expires_at = expires_at.dup.freeze
53
+ super
43
54
  end
44
55
  end
45
56
 
46
- UploadPart = Data.define(:part_number, :etag, :size) do
57
+ UploadPart = Data.define(:part_number, :etag, :size)
58
+
59
+ # Describes an accepted upload part.
60
+ class UploadPart
61
+ # @dynamic part_number, etag, size, members, with, to_h, deconstruct, deconstruct_keys
62
+ # @dynamic self.[], self.members
47
63
  def initialize(part_number:, etag:, size:)
48
- super(part_number: part_number, etag: etag.dup.freeze, size: size)
64
+ etag = etag.dup.freeze
65
+ super
49
66
  end
50
67
  end
51
68
 
@@ -55,7 +72,13 @@ module Volcano
55
72
  ].freeze
56
73
  private_constant :UPLOAD_SESSION_STATUS_ATTRIBUTES
57
74
 
58
- UploadSessionStatus = Data.define(*UPLOAD_SESSION_STATUS_ATTRIBUTES) do
75
+ UploadSessionStatus = Data.define(*UPLOAD_SESSION_STATUS_ATTRIBUTES)
76
+
77
+ # Freezes upload progress returned by storage.
78
+ class UploadSessionStatus
79
+ # @dynamic session_id, status, path, content_type, total_size, part_size
80
+ # @dynamic total_parts, parts_uploaded, bytes_uploaded, parts, expires_at, created_at
81
+ # @dynamic members, with, to_h, deconstruct, deconstruct_keys, self.[], self.members
59
82
  def initialize(**attributes)
60
83
  unknown = attributes.keys - UPLOAD_SESSION_STATUS_ATTRIBUTES
61
84
  raise ArgumentError, "unknown keywords: #{unknown.join(', ')}" unless unknown.empty?
@@ -66,7 +89,8 @@ module Volcano
66
89
  values = UPLOAD_SESSION_STATUS_ATTRIBUTES.to_h do |name|
67
90
  [name, immutable_value(attributes.fetch(name))]
68
91
  end
69
- super(**values)
92
+ attributes = values
93
+ super
70
94
  end
71
95
 
72
96
  private
@@ -51,7 +51,7 @@ module Volcano
51
51
  request: request
52
52
  )
53
53
  end
54
- storage_object(Transport.body(response, 200).fetch('object'))
54
+ storage_object(storage_payload(Transport.body(response, 200)).fetch('object'))
55
55
  end
56
56
 
57
57
  def get_upload_session(path, session_id:)
@@ -78,39 +78,5 @@ module Volcano
78
78
  Transport.body(response, 200)
79
79
  nil
80
80
  end
81
-
82
- private
83
-
84
- def upload_session(payload)
85
- UploadSession.new(
86
- session_id: payload.fetch('session_id'),
87
- part_size: payload.fetch('part_size'),
88
- total_parts: payload.fetch('total_parts'),
89
- expires_at: parse_time(payload.fetch('expires_at'))
90
- )
91
- end
92
-
93
- def upload_part_metadata(payload)
94
- UploadPart.new(
95
- part_number: payload.fetch('part_number'),
96
- etag: payload.fetch('etag'),
97
- size: payload.fetch('size')
98
- )
99
- end
100
-
101
- def upload_session_status(payload)
102
- attributes = upload_session_status_attributes(payload)
103
- attributes[:parts] = attributes.fetch(:parts).map { |part| upload_part_metadata(part) }
104
- attributes[:expires_at] = parse_time(attributes.fetch(:expires_at))
105
- attributes[:created_at] = parse_time(attributes.fetch(:created_at))
106
- UploadSessionStatus.new(**attributes)
107
- end
108
-
109
- def upload_session_status_attributes(payload)
110
- UPLOAD_SESSION_STATUS_ATTRIBUTES.to_h do |name|
111
- value = name == :parts ? payload.fetch(name.to_s, []) : payload.fetch(name.to_s)
112
- [name, value]
113
- end
114
- end
115
81
  end
116
82
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Volcano
4
- VERSION = '0.30.0'
4
+ VERSION = '0.31.0'
5
5
  end
data/lib/volcano.rb CHANGED
@@ -26,6 +26,7 @@ require_relative 'volcano/auth_get_user'
26
26
  require_relative 'volcano/auth_update_user'
27
27
  require_relative 'volcano/auth_email_change'
28
28
  require_relative 'volcano/auth_sessions'
29
+ require_relative 'volcano/auth_session_construction'
29
30
  require_relative 'volcano/auth'
30
31
  require_relative 'volcano/auth_sign_up'
31
32
  require_relative 'volcano/sign_up_result'
@@ -44,6 +45,10 @@ require_relative 'volcano/function_resolution'
44
45
  require_relative 'volcano/functions'
45
46
  require_relative 'volcano/durable'
46
47
  require_relative 'volcano/logs'
48
+ require_relative 'volcano/storage_session_adapter'
49
+ require_relative 'volcano/storage_transport_adapter'
50
+ require_relative 'volcano/storage_response'
51
+ require_relative 'volcano/storage_object_response'
47
52
  require_relative 'volcano/storage'
48
53
  require_relative 'volcano/storage_upload_sessions'
49
54
  require_relative 'volcano/storage_resumable_upload'
@@ -63,7 +68,7 @@ require_relative 'volcano/client'
63
68
  # Public namespace for the Volcano Ruby SDK.
64
69
  module Volcano
65
70
  private_constant :AuthRefresh, :AuthSignOut, :AuthProfile, :AuthProfileFields, :AuthUpdateUser, :AuthEmailChange,
66
- :AuthSessions,
71
+ :AuthSessions, :AuthSessionConstruction,
67
72
  :GeneratedTransport, :LockAutoRenewal, :LockLeaseClock, :LockRenewer,
68
73
  :LockSession, :LockResponse, :Redaction, :SessionOperations
69
74
 
@@ -0,0 +1,15 @@
1
+ module Volcano
2
+ class Auth
3
+ def current_session: () -> Session?
4
+ def current_session=: (Session session) -> void
5
+ def on_auth_state_change: () { (Symbol, Session?) -> void } -> AuthSubscription
6
+ def sign_in: (email: String, password: String) -> Session
7
+ def sign_up: (email: String, password: String, ?metadata: session_user_input, ?sign_in_when_allowed: bool) -> SignUpResult
8
+ def sign_in_anonymously: (?metadata: session_user_input) -> Session
9
+ def convert_anonymous: (email: String, password: String, ?metadata: session_user_input) -> User
10
+ def confirm_email: (token: String) -> nil
11
+ def resend_confirmation: (email: String) -> nil
12
+ def reset_password_for_email: (email: String) -> nil
13
+ def reset_password: (token: String, new_password: String) -> nil
14
+ end
15
+ end
data/sig/storage.rbs ADDED
@@ -0,0 +1,29 @@
1
+ module Volcano
2
+ interface _StorageReadable
3
+ def read: (?Integer length) -> String?
4
+ def is_a?: (Class) -> bool
5
+ def respond_to?: (Symbol) -> bool
6
+ def method: (Symbol) -> Method
7
+ end
8
+
9
+ class Storage
10
+ def from: (String bucket) -> StorageBucket
11
+ end
12
+
13
+ class StorageBucket
14
+ def upload: (String path, String | _StorageReadable value, ?content_type: String?) -> Hash[String, Object?]
15
+ def download: (String path, ?range: String?) -> String
16
+ def list: (?String prefix, ?limit: Integer?, ?cursor: String?) -> StoragePage
17
+ def remove: (String | Array[String] paths) -> Array[String]
18
+ def move: (String from_path, String to_path) -> StorageObject
19
+ def copy: (String from_path, String to_path) -> StorageObject
20
+ def update_visibility: (String path, public: bool) -> StorageObject
21
+
22
+ def create_upload_session: (String path, total_size: Integer, ?content_type: String, ?part_size: Integer?) -> UploadSession
23
+ def upload_part: (String path, session_id: String, part_number: Integer, data: String | _StorageReadable) -> UploadPart
24
+ def complete_upload_session: (String path, session_id: String) -> StorageObject
25
+ def get_upload_session: (String path, session_id: String) -> UploadSessionStatus
26
+ def abort_upload_session: (String path, session_id: String) -> nil
27
+ def upload_resumable: (String path, String | _StorageReadable value, ?content_type: String, ?part_size: Integer?, ?on_progress: (^(Integer, Integer) -> void)?) -> StorageObject
28
+ end
29
+ end
@@ -1,4 +1,6 @@
1
1
  module Volcano
2
+ type storage_object_attribute = String | Integer | bool | Hash[String, json_value] | Time | nil
3
+
2
4
  class StorageObject < Data
3
5
  attr_reader id: String
4
6
  attr_reader bucket_id: String
@@ -19,7 +21,7 @@ module Volcano
19
21
  | (id: String, bucket_id: String, name: String, size: Integer, mime_type: String, is_public: bool, ?owner_id: String?, ?etag: String?, ?metadata: Hash[String, json_value]?, ?created_at: Time?, ?updated_at: Time?, ?public_url: String?) -> StorageObject
20
22
  def self.members: () -> [:id, :bucket_id, :name, :size, :mime_type, :is_public, :owner_id, :etag, :metadata, :created_at, :updated_at, :public_url]
21
23
 
22
- def initialize: (id: String, bucket_id: String, name: String, size: Integer, mime_type: String, is_public: bool, ?owner_id: String?, ?etag: String?, ?metadata: Hash[String, json_value]?, ?created_at: Time?, ?updated_at: Time?, ?public_url: String?) -> void
24
+ def initialize: (**storage_object_attribute attributes) -> void
23
25
  def members: () -> [:id, :bucket_id, :name, :size, :mime_type, :is_public, :owner_id, :etag, :metadata, :created_at, :updated_at, :public_url]
24
26
  def with: (?id: String, ?bucket_id: String, ?name: String, ?size: Integer, ?mime_type: String, ?is_public: bool, ?owner_id: String?, ?etag: String?, ?metadata: Hash[String, json_value]?, ?created_at: Time?, ?updated_at: Time?, ?public_url: String?) -> StorageObject
25
27
  def to_h: () -> { id: String, bucket_id: String, name: String, size: Integer, mime_type: String, is_public: bool, owner_id: String?, etag: String?, metadata: Hash[String, json_value]?, created_at: Time?, updated_at: Time?, public_url: String? }
@@ -1,4 +1,6 @@
1
1
  module Volcano
2
+ type upload_status_attribute = String | Integer | Array[UploadPart] | Time
3
+
2
4
  class UploadSessionStatus < Data
3
5
  attr_reader session_id: String
4
6
  attr_reader status: String
@@ -19,7 +21,7 @@ module Volcano
19
21
  | (session_id: String, status: String, path: String, content_type: String, total_size: Integer, part_size: Integer, total_parts: Integer, parts_uploaded: Integer, bytes_uploaded: Integer, parts: Array[UploadPart], expires_at: Time, created_at: Time) -> UploadSessionStatus
20
22
  def self.members: () -> [:session_id, :status, :path, :content_type, :total_size, :part_size, :total_parts, :parts_uploaded, :bytes_uploaded, :parts, :expires_at, :created_at]
21
23
 
22
- def initialize: (session_id: String, status: String, path: String, content_type: String, total_size: Integer, part_size: Integer, total_parts: Integer, parts_uploaded: Integer, bytes_uploaded: Integer, parts: Array[UploadPart], expires_at: Time, created_at: Time) -> void
24
+ def initialize: (**upload_status_attribute attributes) -> void
23
25
  def members: () -> [:session_id, :status, :path, :content_type, :total_size, :part_size, :total_parts, :parts_uploaded, :bytes_uploaded, :parts, :expires_at, :created_at]
24
26
  def with: (?session_id: String, ?status: String, ?path: String, ?content_type: String, ?total_size: Integer, ?part_size: Integer, ?total_parts: Integer, ?parts_uploaded: Integer, ?bytes_uploaded: Integer, ?parts: Array[UploadPart], ?expires_at: Time, ?created_at: Time) -> UploadSessionStatus
25
27
  def to_h: () -> { session_id: String, status: String, path: String, content_type: String, total_size: Integer, part_size: Integer, total_parts: Integer, parts_uploaded: Integer, bytes_uploaded: Integer, parts: Array[UploadPart], expires_at: Time, created_at: Time }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: volcano-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.30.0
4
+ version: 0.31.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kong
@@ -147,6 +147,7 @@ files:
147
147
  - lib/volcano/auth_reset_password.rb
148
148
  - lib/volcano/auth_reset_password_for_email.rb
149
149
  - lib/volcano/auth_session_binding.rb
150
+ - lib/volcano/auth_session_construction.rb
150
151
  - lib/volcano/auth_sessions.rb
151
152
  - lib/volcano/auth_sign_out.rb
152
153
  - lib/volcano/auth_sign_up.rb
@@ -602,13 +603,18 @@ files:
602
603
  - lib/volcano/storage.rb
603
604
  - lib/volcano/storage_models.rb
604
605
  - lib/volcano/storage_mutations.rb
606
+ - lib/volcano/storage_object_response.rb
605
607
  - lib/volcano/storage_public_url.rb
608
+ - lib/volcano/storage_response.rb
606
609
  - lib/volcano/storage_resumable_upload.rb
610
+ - lib/volcano/storage_session_adapter.rb
611
+ - lib/volcano/storage_transport_adapter.rb
607
612
  - lib/volcano/storage_upload_models.rb
608
613
  - lib/volcano/storage_upload_sessions.rb
609
614
  - lib/volcano/transport.rb
610
615
  - lib/volcano/user.rb
611
616
  - lib/volcano/version.rb
617
+ - sig/auth_credentials.rbs
612
618
  - sig/auth_oauth.rbs
613
619
  - sig/auth_profile.rbs
614
620
  - sig/auth_session_records.rbs
@@ -627,6 +633,7 @@ files:
627
633
  - sig/realtime_postgres_change.rbs
628
634
  - sig/session.rbs
629
635
  - sig/sign_up_result.rbs
636
+ - sig/storage.rbs
630
637
  - sig/storage_public_url.rbs
631
638
  - sig/storage_records.rbs
632
639
  - sig/upload_records.rbs