volcano-sdk 0.32.4 → 0.32.6

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: 5ef8f141d5ef4704ebc727b5836e416fb382af153ad9eb7748ef9d77181c947f
4
- data.tar.gz: 271d7a8647274d6229de1f89fae7521486bc8617c9f4caa9b37d69b859fc9e7f
3
+ metadata.gz: '00829ccc78685de8e0fb7fed50d6db137c861678c872b39a75eaba44e391e723'
4
+ data.tar.gz: 7b29d87a5bf1fc3b683620df34d76293c013f7c0dca361107b1045b2b5326cd4
5
5
  SHA512:
6
- metadata.gz: 51b1d9f99ed3b506ae5716c89c29e558d5a7b3cd8b1d324a00c7adc05dc9d4c23753797c0d1d317d0f059a91574e461af32c15b9a37eefe4f43d9820db594b21
7
- data.tar.gz: 60a608b1b29a3599abdcbc815c37a6f49073921eabdec82926f7f6c51024a1b3ca2f803d72b7c4256722955d5db9e7cc2d3dcba22b6005581bc7581019f54201
6
+ metadata.gz: 4c8376c90f0720047bc743b650657f7fc73289e103f57ddd7cb17beb3cdd8c085b6562c48b79967f60c789e6ca4ffa87b88a4ae70c1fec064d966d56342f18c1
7
+ data.tar.gz: 800f5bcc14498c14f08d3b6d96b1c9387fdf47cd51461749a38868417c1dfbea128c9390b71fcc2423176db287edb3f608afe1085b4b274046338dae17a09bb7
data/README.md CHANGED
@@ -912,7 +912,7 @@ not a gem runtime dependency.
912
912
  Run `npm ci && bin/check-openapi` to verify generated provenance. Use
913
913
  `bundle exec rubocop -A` to apply safe formatting and lint fixes. Before
914
914
  changing the facade, run `bundle exec rubocop --parallel`, `bundle exec rspec`,
915
- and the shared Cucumber contract suite.
915
+ and package checks. Hosting owns live acceptance for all SDKs.
916
916
 
917
917
  ## Contributing
918
918
 
@@ -3,7 +3,11 @@
3
3
  module Volcano
4
4
  # Entry point for Volcano API, storage, lock, and realtime operations.
5
5
  class Client
6
- SessionToken = Data.define(:value) do
6
+ SessionToken = Data.define(:value)
7
+
8
+ # Restricts database queries to the captured access token.
9
+ class SessionToken
10
+ # @dynamic value
7
11
  def session_token = value
8
12
 
9
13
  def session_request
@@ -14,21 +18,27 @@ module Volcano
14
18
 
15
19
  attr_reader :auth, :functions, :durable, :logs, :storage, :locks, :realtime
16
20
 
17
- def initialize(
21
+ # @dynamic auth, functions, durable, logs, storage, locks, realtime
22
+
23
+ def initialize( # rubocop:disable Metrics/ParameterLists -- Preserve typed constructor keywords.
18
24
  anon_key:,
19
25
  api_url: 'https://api.volcano.dev',
20
26
  service_key: nil,
21
27
  timeout: 60,
28
+ _transport: nil, # rubocop:disable Lint/UnderscorePrefixedVariableName -- Existing keyword API.
29
+ _realtime_socket_factory: nil, # rubocop:disable Lint/UnderscorePrefixedVariableName -- Existing keyword API.
30
+ _realtime_reconnect_delay: nil, # rubocop:disable Lint/UnderscorePrefixedVariableName -- Existing keyword API.
22
31
  **options
23
32
  )
24
33
  session = SessionCredentials.build(options.delete(:access_token), options.delete(:refresh_token))
25
- transport, socket_factory, reconnect_delay = extract_adapters(options)
34
+ raise ArgumentError, "unknown keyword: #{options.keys.first}" unless options.empty?
35
+
26
36
  @api_url = api_url.delete_suffix('/')
27
37
  @anon_key = anon_key
28
38
  @service_key = service_key
29
39
  @auth_state = AuthState.new(session: session)
30
- @transport = transport || GeneratedTransport.new(api_url: @api_url, timeout: timeout)
31
- initialize_facades(socket_factory, reconnect_delay)
40
+ @transport = _transport || GeneratedTransport.new(api_url: @api_url, timeout: timeout)
41
+ initialize_facades(_realtime_socket_factory, _realtime_reconnect_delay)
32
42
  end
33
43
 
34
44
  def database(name)
@@ -57,9 +67,10 @@ module Volcano
57
67
  alias session_read session_request
58
68
 
59
69
  def service_token
60
- raise Error::AuthenticationError, 'No service key configured' unless @service_key
70
+ service_key = @service_key
71
+ raise Error::AuthenticationError, 'No service key configured' unless service_key
61
72
 
62
- @service_key
73
+ service_key
63
74
  end
64
75
 
65
76
  def function_token
@@ -100,19 +111,10 @@ module Volcano
100
111
 
101
112
  private
102
113
 
103
- def database_with_token(name, token)
114
+ def database_with_token(name, token) # rubocop:disable Lint/UnusedPrivateMethod -- Typed cross-file private dispatch.
104
115
  Database.new(SessionToken.new(value: token), @transport, name)
105
116
  end
106
117
 
107
- def extract_adapters(adapters)
108
- transport = adapters.delete(:_transport)
109
- socket_factory = adapters.delete(:_realtime_socket_factory)
110
- reconnect_delay = adapters.delete(:_realtime_reconnect_delay)
111
- raise ArgumentError, "unknown keyword: #{adapters.keys.first}" unless adapters.empty?
112
-
113
- [transport, socket_factory, reconnect_delay]
114
- end
115
-
116
118
  def initialize_facades(socket_factory, reconnect_delay)
117
119
  @auth = Auth.new(self, @transport, api_url: @api_url)
118
120
  @functions = Functions.new(self, @transport, api_url: @api_url)
@@ -140,7 +140,7 @@ module Volcano
140
140
  def query_body
141
141
  # @type var body: sdk_payload
142
142
  body = { 'table' => @table }
143
- body.tap do |body|
143
+ body.tap do
144
144
  add_query_selection(body)
145
145
  body['limit'] = @limit unless @limit.nil?
146
146
  body['offset'] = @offset unless @offset.nil?
@@ -39,9 +39,6 @@ module Volcano
39
39
 
40
40
  # Reopens the generated record for checked initialization.
41
41
  class DurableExecution
42
- # @dynamic id, function_id, name, status, region, created_at, result, result_expired
43
- # @dynamic error, completed_at, members, with, to_h, deconstruct, deconstruct_keys
44
- # @dynamic self.[], self.members
45
42
  def initialize(**attributes)
46
43
  unknown = attributes.keys - DURABLE_EXECUTION_ATTRIBUTES
47
44
  raise ArgumentError, "unknown keywords: #{unknown.join(', ')}" unless unknown.empty?
@@ -115,7 +115,7 @@ module Volcano
115
115
 
116
116
  def usable_address?(uri)
117
117
  # URI accepts a port outside the range a connection can use.
118
- !uri.host.to_s.empty? && uri.port.to_i.between?(1, 65_535)
118
+ !uri.host.to_s.empty? && (uri.port || 0).between?(1, 65_535)
119
119
  end
120
120
 
121
121
  def usable_scheme?(scheme, api_url)
@@ -5,7 +5,7 @@ require 'securerandom'
5
5
  require 'tempfile'
6
6
  require 'uri'
7
7
 
8
- generated_load_path = File.expand_path('generated/lib', __dir__)
8
+ generated_load_path = File.expand_path('generated/lib', __dir__.to_s)
9
9
  $LOAD_PATH.unshift(generated_load_path) unless $LOAD_PATH.include?(generated_load_path)
10
10
  require 'volcano-generated'
11
11
 
@@ -17,8 +17,8 @@ end
17
17
  # Public namespace for the Volcano Ruby SDK.
18
18
  module Volcano
19
19
  private_constant :Generated
20
- require_relative 'generated_transport_upload_session_api'
21
20
  require_relative 'generated_transport_support'
21
+ require_relative 'generated_transport_upload_session_api'
22
22
  require_relative 'generated_transport_auth'
23
23
  require_relative 'generated_transport_anonymous'
24
24
  require_relative 'generated_transport_confirmation'
@@ -35,7 +35,6 @@ module Volcano
35
35
  # Adapts the generated OpenAPI client to the stable SDK transport contract.
36
36
  class GeneratedTransport
37
37
  include ApiFactory
38
- include LogTransport
39
38
  include ValueNormalization
40
39
 
41
40
  GeneratedApis = Data.define(
@@ -45,7 +44,7 @@ module Volcano
45
44
  def initialize(api_url:, timeout: 60, api_factory: nil)
46
45
  @api_url = api_url
47
46
  @timeout = timeout
48
- @api_factory = api_factory || method(:build_apis)
47
+ @api_factory = api_factory || ->(authorization) { build_apis(authorization) }
49
48
  end
50
49
 
51
50
  def acquire_project_lock(authorization:, key:, ttl:, token:, request_id: SecureRandom.uuid)
@@ -112,7 +111,7 @@ module Volcano
112
111
  def invoke
113
112
  yield
114
113
  rescue Generated::ApiError => e
115
- status = e.code.to_i
114
+ status = error_status(e)
116
115
  raise Error::TransportError.new(e.message), cause: e if status.zero?
117
116
 
118
117
  Transport::Response.new(
@@ -123,6 +122,10 @@ module Volcano
123
122
  )
124
123
  end
125
124
 
125
+ def error_status(error)
126
+ error.code.to_i # rubocop:disable Lint/NumberConversion -- Preserve generated ApiError status coercion.
127
+ end
128
+
126
129
  def response(data, status, headers, binary: nil)
127
130
  Transport::Response.new(
128
131
  status: status,
@@ -7,11 +7,10 @@ module Volcano
7
7
  invoke do
8
8
  apis = @api_factory.call(authorization)
9
9
  request = Generated::AuthSignupAnonymousRequest.new(user_metadata: metadata)
10
- response(
11
- *apis.authentication.auth_signup_anonymous_with_http_info(
12
- auth_signup_anonymous_request: request
13
- )
10
+ data, status, headers = apis.authentication.auth_signup_anonymous_with_http_info(
11
+ auth_signup_anonymous_request: request
14
12
  )
13
+ response(data, status, headers)
15
14
  end
16
15
  end
17
16
 
@@ -19,12 +18,14 @@ module Volcano
19
18
  invoke do
20
19
  apis = @api_factory.call(authorization)
21
20
  attributes = { email:, password:, user_metadata: metadata }
22
- request = Generated::AuthSignupRequest.new(attributes)
21
+ request = Generated::AuthSignupRequest.new(email:, password:, user_metadata: metadata)
23
22
  body, status, headers = apis.authentication.auth_convert_anonymous_with_http_info(
24
23
  request,
25
24
  debug_body: attributes,
26
25
  debug_return_type: 'String'
27
26
  )
27
+ raise TypeError unless body.is_a?(String)
28
+
28
29
  response(JSON.parse(body), status, headers)
29
30
  end
30
31
  rescue JSON::ParserError, TypeError => e
@@ -14,7 +14,8 @@ module Volcano
14
14
  password: password,
15
15
  user_metadata: metadata
16
16
  )
17
- response(*apis.authentication.auth_signup_with_http_info(request))
17
+ data, status, headers = apis.authentication.auth_signup_with_http_info(request)
18
+ response(data, status, headers)
18
19
  end
19
20
  end
20
21
 
@@ -32,11 +33,10 @@ module Volcano
32
33
  invoke do
33
34
  apis = @api_factory.call(authorization)
34
35
  request = Generated::AuthForgotPasswordRequest.new(email: email)
35
- response(
36
- *apis.authentication.auth_forgot_password_with_http_info(
37
- request, debug_return_type: 'String'
38
- )
36
+ data, status, headers = apis.authentication.auth_forgot_password_with_http_info(
37
+ request, debug_return_type: 'String'
39
38
  )
39
+ response(data, status, headers)
40
40
  end
41
41
  end
42
42
 
@@ -44,11 +44,10 @@ module Volcano
44
44
  invoke do
45
45
  apis = @api_factory.call(authorization)
46
46
  request = Generated::AuthResetPasswordRequest.new(token:, new_password:)
47
- response(
48
- *apis.authentication.auth_reset_password_with_http_info(
49
- request, debug_return_type: 'String'
50
- )
47
+ data, status, headers = apis.authentication.auth_reset_password_with_http_info(
48
+ request, debug_return_type: 'String'
51
49
  )
50
+ response(data, status, headers)
52
51
  end
53
52
  end
54
53
 
@@ -58,6 +57,8 @@ module Volcano
58
57
  body, status, headers = apis.authentication.auth_get_user_with_http_info(
59
58
  debug_return_type: 'String'
60
59
  )
60
+ raise TypeError unless body.is_a?(String)
61
+
61
62
  response(JSON.parse(body), status, headers)
62
63
  end
63
64
  rescue JSON::ParserError, TypeError => e
@@ -67,9 +68,10 @@ module Volcano
67
68
  def auth_update_user(authorization:, password:, metadata:)
68
69
  invoke do
69
70
  apis = @api_factory.call(authorization)
70
- body, status, headers = apis.authentication.auth_update_user_with_http_info(
71
- **update_user_options(password:, metadata:)
72
- )
71
+ options = update_user_options(password:, metadata:)
72
+ body, status, headers = apis.authentication.auth_update_user_with_http_info(options)
73
+ raise TypeError unless body.is_a?(String)
74
+
73
75
  response(JSON.parse(body), status, headers)
74
76
  end
75
77
  rescue JSON::ParserError, TypeError => e
@@ -80,7 +82,8 @@ module Volcano
80
82
  invoke do
81
83
  apis = @api_factory.call(authorization)
82
84
  request = Generated::AuthRefreshRequest.new(refresh_token: refresh_token)
83
- response(*apis.authentication.auth_refresh_with_http_info(auth_refresh_request: request))
85
+ data, status, headers = apis.authentication.auth_refresh_with_http_info(auth_refresh_request: request)
86
+ response(data, status, headers)
84
87
  end
85
88
  end
86
89
 
@@ -88,16 +91,15 @@ module Volcano
88
91
  invoke do
89
92
  apis = @api_factory.call(authorization)
90
93
  request = Generated::AuthRefreshRequest.new(refresh_token: refresh_token)
91
- response(*apis.authentication.auth_logout_with_http_info(auth_refresh_request: request))
94
+ data, status, headers = apis.authentication.auth_logout_with_http_info(auth_refresh_request: request)
95
+ response(data, status, headers)
92
96
  end
93
97
  end
94
98
 
95
99
  private
96
100
 
97
101
  def update_user_options(password:, metadata:)
98
- attributes = {}
99
- attributes[:password] = password unless password.nil?
100
- attributes[:user_metadata] = metadata unless metadata.nil?
102
+ attributes = { password: password, user_metadata: metadata }.compact
101
103
  {
102
104
  auth_update_user_request: Generated::AuthUpdateUserRequest.new(attributes),
103
105
  debug_body: attributes,
@@ -7,11 +7,10 @@ module Volcano
7
7
  invoke do
8
8
  apis = @api_factory.call(authorization)
9
9
  request = Generated::AuthConfirmEmailRequest.new(token:)
10
- response(
11
- *apis.authentication.auth_confirm_email_with_http_info(
12
- request, debug_return_type: 'String'
13
- )
10
+ data, status, headers = apis.authentication.auth_confirm_email_with_http_info(
11
+ request, debug_return_type: 'String'
14
12
  )
13
+ response(data, status, headers)
15
14
  end
16
15
  end
17
16
 
@@ -19,11 +18,10 @@ module Volcano
19
18
  invoke do
20
19
  apis = @api_factory.call(authorization)
21
20
  request = Generated::AuthForgotPasswordRequest.new(email:)
22
- response(
23
- *apis.authentication.auth_resend_confirmation_with_http_info(
24
- request, debug_return_type: 'String'
25
- )
21
+ data, status, headers = apis.authentication.auth_resend_confirmation_with_http_info(
22
+ request, debug_return_type: 'String'
26
23
  )
24
+ response(data, status, headers)
27
25
  end
28
26
  end
29
27
  end
@@ -14,6 +14,8 @@ module Volcano
14
14
  request,
15
15
  debug_return_type: 'String'
16
16
  )
17
+ raise TypeError unless data.is_a?(String)
18
+
17
19
  response(JSON.parse(data), status, headers)
18
20
  end
19
21
  rescue JSON::ParserError, TypeError => e
@@ -23,7 +25,8 @@ module Volcano
23
25
  def auth_cancel_email_change(authorization:)
24
26
  invoke do
25
27
  apis = @api_factory.call(authorization)
26
- response(*apis.authentication.auth_cancel_email_change_with_http_info)
28
+ data, status, headers = apis.authentication.auth_cancel_email_change_with_http_info
29
+ response(data, status, headers)
27
30
  end
28
31
  end
29
32
 
@@ -35,6 +38,8 @@ module Volcano
35
38
  request,
36
39
  debug_return_type: 'String'
37
40
  )
41
+ raise TypeError unless body.is_a?(String)
42
+
38
43
  response(JSON.parse(body), status, headers)
39
44
  end
40
45
  rescue JSON::ParserError, TypeError => e
@@ -70,21 +70,28 @@ module Volcano
70
70
  function_id, request, follow_location: false, debug_return_type: 'String'
71
71
  )
72
72
  rescue Generated::ApiError => e
73
- raise if e.code.to_i.zero?
73
+ status = error_status(e)
74
+ raise if status.zero?
74
75
 
75
- [e.response_body, e.code.to_i, e.response_headers || {}]
76
+ [e.response_body, status, e.response_headers || {}]
76
77
  end
77
78
 
78
79
  def function_body(body, headers)
79
- return nil if body.nil? || body.empty?
80
-
81
- text = body.dup.force_encoding(Encoding::UTF_8).scrub.delete_prefix("\uFEFF")
82
- return nil if text.empty?
80
+ text = normalized_function_text(body)
81
+ return nil if text.nil? || text.empty?
83
82
  return text unless function_json?(text, headers)
84
83
 
85
84
  parse_function_json(text)
86
85
  end
87
86
 
87
+ def normalized_function_text(body)
88
+ raise TypeError, 'Expected a string function response' unless body.nil? || body.is_a?(String)
89
+
90
+ return nil if body.nil? || body.empty?
91
+
92
+ body.dup.force_encoding(Encoding::UTF_8).scrub.delete_prefix("\uFEFF")
93
+ end
94
+
88
95
  def parse_function_json(text)
89
96
  parsed = JSON.parse(text)
90
97
  valid_function_encoding?(parsed) ? parsed : text
@@ -1,31 +1,28 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Volcano
4
+ # Logs operations backed by the generated API client.
4
5
  class GeneratedTransport
5
- # Generated OpenAPI operations used by the public logs facade.
6
- module LogTransport
7
- def search_project_logs(authorization:, project_id:, request:)
8
- invoke do
9
- apis = @api_factory.call(authorization)
10
- body = Generated::LogSearchRequest.new(deep_symbolize(request))
11
- data, status, headers = apis.logs.search_project_logs_with_http_info(
12
- project_id, body, debug_return_type: 'String'
13
- )
14
- response(parse_body(data), status, headers)
15
- end
6
+ def search_project_logs(authorization:, project_id:, request:)
7
+ invoke do
8
+ apis = @api_factory.call(authorization)
9
+ body = Generated::LogSearchRequest.new(deep_symbolize(request))
10
+ data, status, headers = apis.logs.search_project_logs_with_http_info(
11
+ project_id, body, debug_return_type: 'String'
12
+ )
13
+ response(parse_body(data), status, headers)
16
14
  end
15
+ end
17
16
 
18
- def get_project_log_activity(authorization:, project_id:, request:)
19
- invoke do
20
- apis = @api_factory.call(authorization)
21
- body = Generated::LogActivityRequest.new(deep_symbolize(request))
22
- data, status, headers = apis.logs.get_project_log_activity_with_http_info(
23
- project_id, body, debug_return_type: 'String'
24
- )
25
- response(parse_body(data), status, headers)
26
- end
17
+ def get_project_log_activity(authorization:, project_id:, request:)
18
+ invoke do
19
+ apis = @api_factory.call(authorization)
20
+ body = Generated::LogActivityRequest.new(deep_symbolize(request))
21
+ data, status, headers = apis.logs.get_project_log_activity_with_http_info(
22
+ project_id, body, debug_return_type: 'String'
23
+ )
24
+ response(parse_body(data), status, headers)
27
25
  end
28
26
  end
29
- private_constant :LogTransport
30
27
  end
31
28
  end
@@ -4,52 +4,61 @@ module Volcano
4
4
  # OAuth operations for the internal generated transport.
5
5
  class GeneratedTransport
6
6
  def auth_oauth_authorization_url(anon_key:, provider:, redirect_url:, client_state:)
7
- oauth_authorization_api.auth_o_auth_authorize_with_http_info(
7
+ url = oauth_authorization_api.auth_o_auth_authorize_with_http_info(
8
8
  provider, anon_key,
9
9
  redirect_url:, client_state:, response_mode: 'code'
10
10
  ).first
11
+ raise TypeError, 'Expected OAuth authorization URL' unless url.is_a?(String)
12
+
13
+ url
11
14
  end
12
15
 
13
16
  def auth_oauth_exchange(authorization:, code:, redirect_url:)
14
17
  invoke do
15
18
  apis = @api_factory.call(authorization)
16
19
  request = Generated::AuthOAuthExchangeRequest.new(code:, redirect_url:)
17
- response(*apis.oauth.auth_o_auth_exchange_with_http_info(request))
20
+ data, status, headers = apis.oauth.auth_o_auth_exchange_with_http_info(request)
21
+ response(data, status, headers)
18
22
  end
19
23
  end
20
24
 
21
25
  def auth_link_oauth_provider(authorization:, provider:)
22
26
  invoke do
23
27
  apis = @api_factory.call(authorization)
24
- response(*apis.oauth.auth_link_o_auth_provider_with_http_info(provider))
28
+ data, status, headers = apis.oauth.auth_link_o_auth_provider_with_http_info(provider)
29
+ response(data, status, headers)
25
30
  end
26
31
  end
27
32
 
28
33
  def auth_list_oauth_providers(authorization:)
29
34
  invoke do
30
35
  apis = @api_factory.call(authorization)
31
- response(*apis.oauth.auth_list_o_auth_providers_with_http_info)
36
+ data, status, headers = apis.oauth.auth_list_o_auth_providers_with_http_info
37
+ response(data, status, headers)
32
38
  end
33
39
  end
34
40
 
35
41
  def auth_unlink_oauth_provider(authorization:, provider:)
36
42
  invoke do
37
43
  apis = @api_factory.call(authorization)
38
- response(*apis.oauth.auth_unlink_o_auth_provider_with_http_info(provider))
44
+ data, status, headers = apis.oauth.auth_unlink_o_auth_provider_with_http_info(provider)
45
+ response(data, status, headers)
39
46
  end
40
47
  end
41
48
 
42
49
  def auth_get_oauth_provider_token(authorization:, provider:)
43
50
  invoke do
44
51
  apis = @api_factory.call(authorization)
45
- response(*apis.oauth.get_o_auth_provider_token_with_http_info(provider))
52
+ data, status, headers = apis.oauth.get_o_auth_provider_token_with_http_info(provider)
53
+ response(data, status, headers)
46
54
  end
47
55
  end
48
56
 
49
57
  def auth_refresh_oauth_provider_token(authorization:, provider:)
50
58
  invoke do
51
59
  apis = @api_factory.call(authorization)
52
- response(*apis.oauth.refresh_o_auth_provider_token_with_http_info(provider))
60
+ data, status, headers = apis.oauth.refresh_o_auth_provider_token_with_http_info(provider)
61
+ response(data, status, headers)
53
62
  end
54
63
  end
55
64
 
@@ -57,7 +66,8 @@ module Volcano
57
66
  invoke do
58
67
  apis = @api_factory.call(authorization)
59
68
  request = Generated::CallOAuthProviderAPIRequest.new(endpoint:, method:, body:)
60
- response(*apis.oauth.call_o_auth_provider_api_with_http_info(provider, request))
69
+ data, status, headers = apis.oauth.call_o_auth_provider_api_with_http_info(provider, request)
70
+ response(data, status, headers)
61
71
  end
62
72
  end
63
73
  end
@@ -6,21 +6,24 @@ module Volcano
6
6
  def auth_get_my_sessions(authorization:, page:, limit:)
7
7
  invoke do
8
8
  apis = @api_factory.call(authorization)
9
- response(*apis.authentication.auth_get_my_sessions_with_http_info(page: page, limit: limit))
9
+ data, status, headers = apis.authentication.auth_get_my_sessions_with_http_info(page: page, limit: limit)
10
+ response(data, status, headers)
10
11
  end
11
12
  end
12
13
 
13
14
  def auth_delete_all_my_sessions(authorization:)
14
15
  invoke do
15
16
  apis = @api_factory.call(authorization)
16
- response(*apis.authentication.auth_delete_all_my_sessions_with_http_info)
17
+ data, status, headers = apis.authentication.auth_delete_all_my_sessions_with_http_info
18
+ response(data, status, headers)
17
19
  end
18
20
  end
19
21
 
20
22
  def auth_delete_my_session(authorization:, session_id:)
21
23
  invoke do
22
24
  apis = @api_factory.call(authorization)
23
- response(*apis.authentication.auth_delete_my_session_with_http_info(session_id))
25
+ data, status, headers = apis.authentication.auth_delete_my_session_with_http_info(session_id)
26
+ response(data, status, headers)
24
27
  end
25
28
  end
26
29
  end
@@ -10,7 +10,7 @@ module Volcano
10
10
  options = { content_type: content_type }.compact
11
11
  result = apis.storage.upload_storage_object_with_http_info(bucket_name, path, file, options)
12
12
  data_value, status, headers = result
13
- return response(data_value, status, headers)
13
+ response(data_value, status, headers)
14
14
  end
15
15
  end
16
16
  end
@@ -16,11 +16,12 @@ module Volcano
16
16
  private
17
17
 
18
18
  def deserialize_private_model(klass, value)
19
- if klass.respond_to?(:openapi_any_of) || klass.respond_to?(:openapi_one_of)
20
- klass.build(value)
21
- else
22
- klass.build_from_hash(value)
23
- end
19
+ method_name = if klass.respond_to?(:openapi_any_of) || klass.respond_to?(:openapi_one_of)
20
+ :build
21
+ else
22
+ :build_from_hash
23
+ end
24
+ klass.method(method_name).call(value)
24
25
  end
25
26
  end
26
27
  private_constant :ModelDeserialization
@@ -64,8 +65,7 @@ module Volcano
64
65
 
65
66
  # Implements storage endpoints omitted by the generated API surface.
66
67
  class StorageApi < Generated::StorageObjectsApi
67
- include UploadSessionStorageApi
68
-
68
+ DOWNLOAD_HEADERS = { 'Accept' => 'application/octet-stream, application/json' }.freeze
69
69
  UPLOAD_OPTIONS = {
70
70
  operation: :'StorageObjectsApi.upload_storage_object',
71
71
  header_params: { 'Accept' => 'application/json', 'Content-Type' => 'multipart/form-data' }.freeze,
@@ -74,7 +74,7 @@ module Volcano
74
74
  }.freeze
75
75
  DOWNLOAD_OPTIONS = {
76
76
  operation: :'StorageObjectsApi.download_storage_object',
77
- header_params: { 'Accept' => 'application/octet-stream, application/json' }.freeze,
77
+ header_params: DOWNLOAD_HEADERS,
78
78
  auth_names: %w[ServiceRoleKey AuthUserAccessToken AnonKey].freeze,
79
79
  return_type: 'File'
80
80
  }.freeze
@@ -101,7 +101,10 @@ module Volcano
101
101
  end
102
102
 
103
103
  def download_storage_object_with_http_info(bucket_name, path, opts = {})
104
- header_params = DOWNLOAD_OPTIONS.fetch(:header_params).merge(opts[:header_params] || {})
104
+ provided_headers = opts[:header_params]
105
+ raise TypeError, 'Invalid storage headers' unless provided_headers.nil? || provided_headers.is_a?(Hash)
106
+
107
+ header_params = DOWNLOAD_HEADERS.merge(provided_headers || {})
105
108
  header_params['Range'] = opts[:range] unless opts[:range].nil?
106
109
  options = opts.merge(DOWNLOAD_OPTIONS, header_params: header_params)
107
110
 
@@ -134,15 +137,19 @@ module Volcano
134
137
  private
135
138
 
136
139
  def parse_body(value)
137
- return {} if value.nil? || value.empty?
140
+ # @type var empty_body: Hash[String, Object?]
141
+ empty_body = {}
142
+ return empty_body if value.nil?
143
+ raise TypeError, 'Expected a string response body' unless value.is_a?(String)
144
+ return empty_body if value.empty?
138
145
 
139
146
  JSON.parse(value)
140
147
  rescue JSON::ParserError
141
- {}
148
+ empty_body
142
149
  end
143
150
 
144
151
  def plain_value(value)
145
- value = value.to_hash if value.respond_to?(:to_hash)
152
+ value = value.method(:to_hash).call if value.respond_to?(:to_hash)
146
153
  case value
147
154
  when Hash then plain_hash(value)
148
155
  when Array then value.map { |item| plain_value(item) }
@@ -155,9 +162,13 @@ module Volcano
155
162
  end
156
163
 
157
164
  def deep_symbolize(value)
165
+ value.to_h { |key, item| [key.to_sym, deep_symbolize_value(item)] }
166
+ end
167
+
168
+ def deep_symbolize_value(value)
158
169
  case value
159
- when Hash then value.to_h { |key, item| [key.to_sym, deep_symbolize(item)] }
160
- when Array then value.map { |item| deep_symbolize(item) }
170
+ when Hash then deep_symbolize(value)
171
+ when Array then value.map { |item| deep_symbolize_value(item) }
161
172
  else value
162
173
  end
163
174
  end
@@ -166,19 +177,24 @@ module Volcano
166
177
  return value.to_s.b unless value.respond_to?(:read)
167
178
 
168
179
  prepare_stream(value)
169
- value.read.b
180
+ result = value.method(:read).call
181
+ raise TypeError, 'Expected a binary stream' unless result.is_a?(String)
182
+
183
+ result.b
170
184
  ensure
171
- value.close! if value.respond_to?(:close!)
185
+ value.method(:close!).call if value.respond_to?(:close!)
172
186
  end
173
187
 
174
188
  def prepare_stream(value)
175
189
  reopen_stream(value)
176
- value.binmode if value.respond_to?(:binmode)
177
- value.rewind if value.respond_to?(:rewind)
190
+ value.method(:binmode).call if value.respond_to?(:binmode)
191
+ value.method(:rewind).call if value.respond_to?(:rewind)
178
192
  end
179
193
 
180
194
  def reopen_stream(value)
181
- value.open if value.respond_to?(:closed?) && value.closed? && value.respond_to?(:open)
195
+ return unless value.respond_to?(:closed?) && value.method(:closed?).call && value.respond_to?(:open)
196
+
197
+ value.method(:open).call
182
198
  end
183
199
  end
184
200
 
@@ -192,11 +208,12 @@ module Volcano
192
208
  end
193
209
 
194
210
  def generated_configuration(authorization)
195
- uri = URI(@api_url)
211
+ uri = URI.parse(@api_url)
212
+ path = uri.path || ''
196
213
  Generated::Configuration.new.tap do |configuration|
197
214
  configuration.scheme = uri.scheme
198
215
  configuration.host = host_with_port(uri)
199
- configuration.base_path = uri.path == '/' ? '' : uri.path
216
+ configuration.base_path = path == '/' ? '' : path
200
217
  configuration.ignore_operation_servers = true
201
218
  configuration.access_token = authorization
202
219
  configuration.timeout = timeout_milliseconds
@@ -226,9 +243,11 @@ module Volcano
226
243
  end
227
244
 
228
245
  def host_with_port(uri)
229
- return uri.host if uri.port == uri.default_port
246
+ host = uri.host
247
+ raise ArgumentError, 'API URL requires a host' unless host
248
+ return host if uri.port == uri.default_port
230
249
 
231
- "#{uri.host}:#{uri.port}"
250
+ "#{host}:#{uri.port}"
232
251
  end
233
252
  end
234
253
  end
@@ -2,7 +2,11 @@
2
2
 
3
3
  module Volcano
4
4
  # Corrects generated HTTP operations for resumable storage uploads.
5
- module UploadSessionStorageApi
5
+ class GeneratedTransport::StorageApi
6
+ UPLOAD_PART_HEADERS = { 'Accept' => 'application/json', 'Content-Type' => 'application/octet-stream' }.freeze
7
+ COMPLETE_HEADERS = { 'Accept' => 'application/json', 'Content-Type' => 'application/json' }.freeze
8
+ STATUS_HEADERS = { 'Accept' => 'application/json' }.freeze
9
+ ABORT_HEADERS = { 'Accept' => 'application/json' }.freeze
6
10
  CREATE_OPTIONS = {
7
11
  operation: :'StorageObjectsApi.upload_storage_object',
8
12
  header_params: { 'Accept' => 'application/json', 'Content-Type' => 'application/json' }.freeze,
@@ -11,25 +15,25 @@ module Volcano
11
15
  }.freeze
12
16
  UPLOAD_PART_OPTIONS = {
13
17
  operation: :'StorageObjectsApi.upload_part',
14
- header_params: { 'Accept' => 'application/json', 'Content-Type' => 'application/octet-stream' }.freeze,
18
+ header_params: UPLOAD_PART_HEADERS,
15
19
  auth_names: %w[ServiceRoleKey AuthUserAccessToken AnonKey].freeze,
16
20
  return_type: 'UploadSessionPart'
17
21
  }.freeze
18
22
  COMPLETE_OPTIONS = {
19
23
  operation: :'StorageObjectsApi.upload_storage_object',
20
- header_params: { 'Accept' => 'application/json', 'Content-Type' => 'application/json' }.freeze,
24
+ header_params: COMPLETE_HEADERS,
21
25
  auth_names: %w[ServiceRoleKey AuthUserAccessToken AnonKey].freeze,
22
26
  return_type: 'CompleteUploadSessionResponse'
23
27
  }.freeze
24
28
  STATUS_OPTIONS = {
25
29
  operation: :'StorageObjectsApi.download_storage_object',
26
- header_params: { 'Accept' => 'application/json' }.freeze,
30
+ header_params: STATUS_HEADERS,
27
31
  auth_names: %w[ServiceRoleKey AuthUserAccessToken AnonKey].freeze,
28
32
  return_type: 'UploadSessionStatusResponse'
29
33
  }.freeze
30
34
  ABORT_OPTIONS = {
31
35
  operation: :'StorageObjectsApi.delete_storage_object',
32
- header_params: { 'Accept' => 'application/json' }.freeze,
36
+ header_params: ABORT_HEADERS,
33
37
  auth_names: %w[ServiceRoleKey AuthUserAccessToken].freeze
34
38
  }.freeze
35
39
 
@@ -39,7 +43,7 @@ module Volcano
39
43
  end
40
44
 
41
45
  def upload_part_with_http_info(bucket_name, path, session_id, part_number, data)
42
- headers = UPLOAD_PART_OPTIONS.fetch(:header_params).merge(
46
+ headers = UPLOAD_PART_HEADERS.merge(
43
47
  'X-Upload-Session' => session_id, 'X-Part-Number' => part_number.to_s
44
48
  )
45
49
  call_storage_api(
@@ -48,22 +52,21 @@ module Volcano
48
52
  end
49
53
 
50
54
  def complete_upload_session_with_http_info(bucket_name, path, session_id)
51
- headers = COMPLETE_OPTIONS.fetch(:header_params).merge(
55
+ headers = COMPLETE_HEADERS.merge(
52
56
  'X-Upload-Session' => session_id, 'X-Upload-Complete' => 'true'
53
57
  )
54
- options = COMPLETE_OPTIONS.merge(header_params: headers, body: JSON.generate({}))
58
+ options = COMPLETE_OPTIONS.merge(header_params: headers, body: '{}')
55
59
  call_storage_api(:POST, bucket_name, path, options)
56
60
  end
57
61
 
58
62
  def get_upload_session_with_http_info(bucket_name, path, session_id)
59
- headers = STATUS_OPTIONS.fetch(:header_params).merge('X-Upload-Session' => session_id)
63
+ headers = STATUS_HEADERS.merge('X-Upload-Session' => session_id)
60
64
  call_storage_api(:GET, bucket_name, path, STATUS_OPTIONS.merge(header_params: headers))
61
65
  end
62
66
 
63
67
  def abort_upload_session_with_http_info(bucket_name, path, session_id)
64
- headers = ABORT_OPTIONS.fetch(:header_params).merge('X-Upload-Session' => session_id)
68
+ headers = ABORT_HEADERS.merge('X-Upload-Session' => session_id)
65
69
  call_storage_api(:DELETE, bucket_name, path, ABORT_OPTIONS.merge(header_params: headers))
66
70
  end
67
71
  end
68
- private_constant :UploadSessionStorageApi
69
72
  end
@@ -14,7 +14,7 @@ module Volcano
14
14
  def initialize(lease, ttl:, started_at:)
15
15
  @lease = lease
16
16
  @ttl = ttl
17
- @lease_clock = LockLeaseClock.new(ttl: ttl, started_at: started_at)
17
+ @lease_clock = LockLeaseClock.new(ttl: ttl, started_at: started_at) # rubocop:disable Lint/NameTypo -- Inherited Class#new.
18
18
  @mutex = Mutex.new
19
19
  @changed = ConditionVariable.new
20
20
  @failure = nil
@@ -11,10 +11,6 @@ module Volcano
11
11
 
12
12
  # Reopens the generated record for checked initialization.
13
13
  class AuthSession
14
- # @dynamic id, user_id, provider, expires_at, is_active, is_current, user_agent
15
- # @dynamic ip_address, last_ip_address, last_activity_at, session_started_at
16
- # @dynamic created_at, updated_at, members, with, to_h, deconstruct
17
- # @dynamic deconstruct_keys, self.[], self.members
18
14
  def initialize(**attributes)
19
15
  unknown = attributes.keys - AUTH_SESSION_ATTRIBUTES
20
16
  raise ArgumentError, "unknown keywords: #{unknown.join(', ')}" unless unknown.empty?
@@ -51,8 +47,6 @@ module Volcano
51
47
 
52
48
  # Reopens the generated record for checked initialization.
53
49
  class LinkedOAuthProvider
54
- # @dynamic provider, linked_at, updated_at, members, with, to_h, deconstruct
55
- # @dynamic deconstruct_keys, self.[], self.members
56
50
  def initialize(provider:, linked_at:, updated_at:)
57
51
  provider = immutable_value(provider)
58
52
  linked_at = immutable_value(linked_at)
@@ -99,7 +99,9 @@ module Volcano
99
99
  def open_socket(address)
100
100
  require 'async/http/endpoint'
101
101
  require 'async/websocket/client'
102
- Async::WebSocket::Client.connect(Async::HTTP::Endpoint.parse(address))
102
+ # Use the HTTP/1.1 upgrade supported by the public API.
103
+ endpoint = Async::HTTP::Endpoint.parse(address, protocol: Async::HTTP::Protocol::HTTP11)
104
+ Async::WebSocket::Client.connect(endpoint)
103
105
  end
104
106
 
105
107
  def realtime_secrets = [@client.anon_token, @client.current_session&.access_token]
@@ -8,8 +8,6 @@ module Volcano
8
8
 
9
9
  # Immutable snapshot of a Postgres change publication.
10
10
  class PostgresChange
11
- # @dynamic type, schema, table, record, old_record, columns, timestamp, id, mode
12
- # @dynamic members, with, to_h, deconstruct, deconstruct_keys, self.[], self.members
13
11
  def initialize(type:, schema:, table:, timestamp:, **payload)
14
12
  type = Immutable.call(type.to_s)
15
13
  schema = Immutable.call(schema.to_s)
@@ -189,8 +189,6 @@ module Volcano
189
189
 
190
190
  attr_reader :name
191
191
 
192
- # @dynamic name
193
-
194
192
  def initialize(realtime, protocol_provider, name, type, batch_config:)
195
193
  @realtime = realtime
196
194
  @protocol_provider = protocol_provider
@@ -5,7 +5,7 @@ require 'time'
5
5
 
6
6
  module Volcano
7
7
  SESSION_USER_CODER = JSON::Coder.new(freeze: true, allow_duplicate_key: false) do |value|
8
- value.instance_of?(Time) ? Time.at(value.to_r).utc.iso8601(9) : value
8
+ value.instance_of?(Time) ? Time.at(Rational(value)).utc.iso8601(9) : value
9
9
  end
10
10
  private_constant :SESSION_USER_CODER
11
11
 
@@ -14,8 +14,6 @@ module Volcano
14
14
 
15
15
  # Reopens the generated record for checked initialization.
16
16
  class Session
17
- # @dynamic access_token, refresh_token, user_id, user, members, with, to_h
18
- # @dynamic deconstruct, deconstruct_keys, self.[], self.members
19
17
  def initialize(access_token:, refresh_token: nil, user_id: nil, user: nil)
20
18
  user = immutable_user(user)
21
19
  super
@@ -10,9 +10,6 @@ module Volcano
10
10
 
11
11
  # Validates and freezes object metadata returned by storage.
12
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
16
13
  def initialize(**attributes)
17
14
  unknown = attributes.keys - STORAGE_OBJECT_ATTRIBUTES
18
15
  raise ArgumentError, "unknown keywords: #{unknown.join(', ')}" unless unknown.empty?
@@ -76,9 +76,6 @@ module Volcano
76
76
 
77
77
  # Freezes upload progress returned by storage.
78
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
82
79
  def initialize(**attributes)
83
80
  unknown = attributes.keys - UPLOAD_SESSION_STATUS_ATTRIBUTES
84
81
  raise ArgumentError, "unknown keywords: #{unknown.join(', ')}" unless unknown.empty?
@@ -52,45 +52,6 @@ module Volcano
52
52
  message.is_a?(String) ? message : 'Volcano request failed'
53
53
  end
54
54
 
55
- def self.json_value(value)
56
- case value
57
- when Array then value.map { |entry| json_value(entry) }
58
- when Hash then json_object(value)
59
- else json_scalar(value)
60
- end
61
- end
62
-
63
- def self.json_object(value, message: 'Expected a JSON object')
64
- raise TypeError, message unless value.is_a?(Hash)
65
-
66
- value.to_h do |key, item|
67
- raise TypeError, message unless key.is_a?(String)
68
-
69
- [key, json_value(item)]
70
- end
71
- end
72
-
73
- def self.json_rows(value)
74
- rows = json_object(value)['data']
75
- raise TypeError, 'Expected database rows' unless rows.is_a?(Array)
76
-
77
- rows.map { |row| json_object(row) }
78
- end
79
-
80
- def self.finite_float(value)
81
- raise TypeError, 'Expected a finite JSON number' unless value.finite?
82
-
83
- value
84
- end
85
-
86
- def self.json_scalar(value)
87
- case value
88
- when NilClass, TrueClass, FalseClass, Integer, String then value
89
- when Float then finite_float(value)
90
- else raise TypeError, 'Expected a JSON response'
91
- end
92
- end
93
-
94
55
  def self.error_type(status)
95
56
  ERROR_TYPES.fetch(status) do
96
57
  status.between?(500, 599) ? Error::ServerError : Error::VolcanoError
@@ -101,6 +62,8 @@ module Volcano
101
62
  value = headers&.find { |key, _| key.casecmp?(name) }&.last
102
63
  Integer(value, exception: false)
103
64
  end
104
- private_class_method :error_type, :integer_header, :response_error, :finite_float, :json_scalar
65
+ private_class_method :error_type, :integer_header, :response_error
105
66
  end
106
67
  end
68
+
69
+ require_relative 'transport_json'
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Volcano
6
+ # Validates and copies JSON response values from function and database transports.
7
+ module Transport
8
+ def self.json_value(value)
9
+ # @type var active: Hash[Integer, bool]
10
+ active = {}
11
+ copy = json_copy(value, active)
12
+ validate_json_encoding(copy)
13
+ copy
14
+ end
15
+
16
+ def self.json_copy(value, active)
17
+ case value
18
+ when Array then json_container(value, active) { value.map { |entry| json_copy(entry, active) } }
19
+ when Hash then json_hash(value, active, message: 'Expected a JSON object')
20
+ else json_scalar(value)
21
+ end
22
+ end
23
+
24
+ def self.json_object(value, message: 'Expected a JSON object')
25
+ # @type var active: Hash[Integer, bool]
26
+ active = {}
27
+ copy = json_hash(value, active, message:)
28
+ validate_json_encoding(copy)
29
+ copy
30
+ end
31
+
32
+ def self.json_hash(value, active, message:)
33
+ raise TypeError, message unless value.is_a?(Hash)
34
+
35
+ json_container(value, active) do
36
+ value.to_h do |key, item|
37
+ raise TypeError, message unless key.is_a?(String)
38
+
39
+ [key, json_copy(item, active)]
40
+ end
41
+ end
42
+ end
43
+
44
+ def self.json_container(value, active)
45
+ id = value.object_id
46
+ raise TypeError, 'Expected a JSON response' if active.key?(id) || active.size >= 100
47
+
48
+ active[id] = true
49
+ copy = yield
50
+ active.delete(id)
51
+ copy
52
+ end
53
+
54
+ def self.validate_json_encoding(value)
55
+ JSON.generate(value)
56
+ nil
57
+ rescue JSON::GeneratorError
58
+ raise TypeError, 'Expected a JSON response', cause: nil
59
+ end
60
+
61
+ def self.json_rows(value)
62
+ rows = json_object(value)['data']
63
+ raise TypeError, 'Expected database rows' unless rows.is_a?(Array)
64
+
65
+ rows.map { |row| json_object(row) }
66
+ end
67
+
68
+ def self.finite_float(value)
69
+ raise TypeError, 'Expected a finite JSON number' unless value.finite?
70
+
71
+ value
72
+ end
73
+
74
+ def self.json_scalar(value)
75
+ case value
76
+ when NilClass, TrueClass, FalseClass, Integer, String then value
77
+ when Float then finite_float(value)
78
+ else raise TypeError, 'Expected a JSON response'
79
+ end
80
+ end
81
+
82
+ private_class_method :json_copy, :json_hash, :json_container, :validate_json_encoding, :finite_float, :json_scalar
83
+ end
84
+ end
data/lib/volcano/user.rb CHANGED
@@ -14,9 +14,6 @@ module Volcano
14
14
 
15
15
  # Reopens the generated record for checked initialization.
16
16
  class User
17
- # @dynamic id, project_id, email, email_confirmed, user_metadata, app_metadata
18
- # @dynamic avatar_url, status, banned_until, last_sign_in_at, created_at, updated_at
19
- # @dynamic members, with, to_h, deconstruct, deconstruct_keys, self.[], self.members
20
17
  def initialize(id:, email:, status:, **attributes)
21
18
  validate_optional_attributes(attributes)
22
19
  id = freeze_value(id)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Volcano
4
- VERSION = '0.32.4'
4
+ VERSION = '0.32.6'
5
5
  end
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.32.4
4
+ version: 0.32.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kong
@@ -613,6 +613,7 @@ files:
613
613
  - lib/volcano/storage_upload_models.rb
614
614
  - lib/volcano/storage_upload_sessions.rb
615
615
  - lib/volcano/transport.rb
616
+ - lib/volcano/transport_json.rb
616
617
  - lib/volcano/user.rb
617
618
  - lib/volcano/version.rb
618
619
  - sig/auth_credentials.rbs