@apollo-deploy/tesseract 1.9.0 → 2.1.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.
- package/README.md +218 -110
- package/dist/adapters/csharp/index.d.ts +5 -8
- package/dist/adapters/csharp/index.d.ts.map +1 -1
- package/dist/adapters/csharp/index.js +314 -74
- package/dist/adapters/csharp/index.js.map +1 -1
- package/dist/adapters/documentation.d.ts +17 -0
- package/dist/adapters/documentation.d.ts.map +1 -0
- package/dist/adapters/documentation.js +268 -0
- package/dist/adapters/documentation.js.map +1 -0
- package/dist/adapters/go/index.d.ts +5 -8
- package/dist/adapters/go/index.d.ts.map +1 -1
- package/dist/adapters/go/index.js +197 -101
- package/dist/adapters/go/index.js.map +1 -1
- package/dist/adapters/kotlin/index.d.ts +5 -9
- package/dist/adapters/kotlin/index.d.ts.map +1 -1
- package/dist/adapters/kotlin/index.js +280 -212
- package/dist/adapters/kotlin/index.js.map +1 -1
- package/dist/adapters/php/index.d.ts +5 -15
- package/dist/adapters/php/index.d.ts.map +1 -1
- package/dist/adapters/php/index.js +362 -90
- package/dist/adapters/php/index.js.map +1 -1
- package/dist/adapters/python/index.d.ts +5 -8
- package/dist/adapters/python/index.d.ts.map +1 -1
- package/dist/adapters/python/index.js +240 -109
- package/dist/adapters/python/index.js.map +1 -1
- package/dist/adapters/ruby/index.d.ts +3 -3
- package/dist/adapters/ruby/index.d.ts.map +1 -1
- package/dist/adapters/ruby/index.js +235 -46
- package/dist/adapters/ruby/index.js.map +1 -1
- package/dist/adapters/rust/index.d.ts +5 -9
- package/dist/adapters/rust/index.d.ts.map +1 -1
- package/dist/adapters/rust/index.js +237 -169
- package/dist/adapters/rust/index.js.map +1 -1
- package/dist/adapters/shared.d.ts +7 -0
- package/dist/adapters/shared.d.ts.map +1 -0
- package/dist/adapters/shared.js +68 -0
- package/dist/adapters/shared.js.map +1 -0
- package/dist/adapters/type-utils.d.ts +13 -0
- package/dist/adapters/type-utils.d.ts.map +1 -0
- package/dist/adapters/type-utils.js +105 -0
- package/dist/adapters/type-utils.js.map +1 -0
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/adapters/typescript/emitter/emitSchema.d.ts +2 -2
- package/dist/adapters/typescript/emitter/emitSchema.js +39 -27
- package/dist/adapters/typescript/emitter/emitSchema.js.map +1 -1
- package/dist/adapters/typescript/emitter/emitTypeScriptModels.d.ts +2 -2
- package/dist/adapters/typescript/emitter/emitTypeScriptModels.js +15 -15
- package/dist/adapters/typescript/emitter/importCollector.d.ts +2 -4
- package/dist/adapters/typescript/emitter/importCollector.d.ts.map +1 -1
- package/dist/adapters/typescript/emitter/importCollector.js +40 -50
- package/dist/adapters/typescript/emitter/importCollector.js.map +1 -1
- package/dist/adapters/typescript/emitter/parsers.d.ts +1 -1
- package/dist/adapters/typescript/emitter/parsers.js +13 -13
- package/dist/adapters/typescript/emitter/typeHoister.d.ts +1 -1
- package/dist/adapters/typescript/emitter/typeHoister.d.ts.map +1 -1
- package/dist/adapters/typescript/emitter/typeHoister.js +12 -20
- package/dist/adapters/typescript/emitter/typeHoister.js.map +1 -1
- package/dist/adapters/typescript/index.d.ts +3 -3
- package/dist/adapters/typescript/index.d.ts.map +1 -1
- package/dist/adapters/typescript/index.js +148 -166
- package/dist/adapters/typescript/index.js.map +1 -1
- package/dist/adapters/typescript/shared.d.ts +2 -2
- package/dist/adapters/typescript/shared.js +12 -12
- package/dist/cli.js +50 -37
- package/dist/cli.js.map +1 -1
- package/dist/collector.d.ts +32 -7
- package/dist/collector.d.ts.map +1 -1
- package/dist/collector.js +149 -49
- package/dist/collector.js.map +1 -1
- package/dist/elysia.js +2 -2
- package/dist/elysia.js.map +1 -1
- package/dist/express.d.ts.map +1 -1
- package/dist/express.js.map +1 -1
- package/dist/fastify.d.ts +9 -2
- package/dist/fastify.d.ts.map +1 -1
- package/dist/fastify.js +40 -21
- package/dist/fastify.js.map +1 -1
- package/dist/helpers/handlebars.d.ts +7 -16
- package/dist/helpers/handlebars.d.ts.map +1 -1
- package/dist/helpers/handlebars.js +127 -177
- package/dist/helpers/handlebars.js.map +1 -1
- package/dist/hono.d.ts.map +1 -1
- package/dist/hono.js.map +1 -1
- package/dist/index.d.ts +14 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +32 -30
- package/dist/index.js.map +1 -1
- package/dist/koa.d.ts.map +1 -1
- package/dist/koa.js.map +1 -1
- package/dist/nestjs.d.ts.map +1 -1
- package/dist/nestjs.js +41 -14
- package/dist/nestjs.js.map +1 -1
- package/dist/pipeline/enrich.d.ts +2 -13
- package/dist/pipeline/enrich.d.ts.map +1 -1
- package/dist/pipeline/enrich.js +157 -212
- package/dist/pipeline/enrich.js.map +1 -1
- package/dist/pipeline/intake.d.ts +3 -3
- package/dist/pipeline/intake.d.ts.map +1 -1
- package/dist/pipeline/intake.js +342 -238
- package/dist/pipeline/intake.js.map +1 -1
- package/dist/pipeline/write.d.ts +1 -14
- package/dist/pipeline/write.d.ts.map +1 -1
- package/dist/pipeline/write.js +73 -113
- package/dist/pipeline/write.js.map +1 -1
- package/dist/types/config.d.ts +14 -26
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +16 -12
- package/dist/types/config.js.map +1 -1
- package/dist/types/ir.d.ts +12 -8
- package/dist/types/ir.d.ts.map +1 -1
- package/dist/types/manifest.d.ts +10 -11
- package/dist/types/manifest.d.ts.map +1 -1
- package/dist/types/manifest.js +54 -14
- package/dist/types/manifest.js.map +1 -1
- package/dist/types/sdk-module.d.ts +2 -2
- package/dist/types/sdk-module.js +1 -1
- package/dist/utils/format.d.ts +1 -3
- package/dist/utils/format.d.ts.map +1 -1
- package/dist/utils/format.js +3 -10
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/naming.d.ts.map +1 -1
- package/dist/utils/naming.js +174 -184
- package/dist/utils/naming.js.map +1 -1
- package/dist/utils/npm-token.d.ts +1 -17
- package/dist/utils/npm-token.d.ts.map +1 -1
- package/dist/utils/npm-token.js +27 -76
- package/dist/utils/npm-token.js.map +1 -1
- package/dist/utils/version.d.ts +14 -0
- package/dist/utils/version.d.ts.map +1 -0
- package/dist/utils/version.js +60 -0
- package/dist/utils/version.js.map +1 -0
- package/package.json +4 -3
- package/templates/csharp/client.hbs +26 -49
- package/templates/csharp/csproj.hbs +7 -0
- package/templates/csharp/domain.hbs +48 -130
- package/templates/csharp/errors.hbs +17 -25
- package/templates/csharp/readme.hbs +31 -44
- package/templates/csharp/transport.hbs +238 -136
- package/templates/csharp/types.hbs +30 -76
- package/templates/go/client.hbs +44 -109
- package/templates/go/domain.hbs +83 -25
- package/templates/go/go-mod.hbs +1 -4
- package/templates/go/readme.hbs +32 -29
- package/templates/go/transport.hbs +174 -173
- package/templates/go/types.hbs +17 -53
- package/templates/go/utils.hbs +3 -2
- package/templates/kotlin/build-gradle.hbs +1 -1
- package/templates/kotlin/client.hbs +18 -15
- package/templates/kotlin/domain.hbs +54 -51
- package/templates/kotlin/errors.hbs +1 -1
- package/templates/kotlin/readme.hbs +35 -137
- package/templates/kotlin/transport.hbs +140 -123
- package/templates/kotlin/types.hbs +30 -81
- package/templates/php/client.hbs +8 -46
- package/templates/php/composer.hbs +2 -1
- package/templates/php/domain.hbs +86 -117
- package/templates/php/errors.hbs +1 -1
- package/templates/php/readme.hbs +42 -49
- package/templates/php/transport.hbs +192 -145
- package/templates/php/types.hbs +63 -60
- package/templates/python/client.hbs +15 -2
- package/templates/python/domain.hbs +50 -108
- package/templates/python/pyproject.hbs +5 -0
- package/templates/python/readme.hbs +47 -30
- package/templates/python/resources-init.hbs +1 -1
- package/templates/python/transport.hbs +167 -53
- package/templates/python/types.hbs +22 -58
- package/templates/ruby/client.hbs +10 -6
- package/templates/ruby/domain.hbs +48 -67
- package/templates/ruby/errors.hbs +3 -3
- package/templates/ruby/gemspec.hbs +3 -10
- package/templates/ruby/index.hbs +3 -3
- package/templates/ruby/readme.hbs +33 -70
- package/templates/ruby/transport.hbs +129 -103
- package/templates/ruby/types.hbs +11 -16
- package/templates/ruby/version.hbs +1 -1
- package/templates/rust/cargo.hbs +2 -0
- package/templates/rust/client.hbs +10 -1
- package/templates/rust/domain.hbs +69 -52
- package/templates/rust/error.hbs +3 -1
- package/templates/rust/lib.hbs +4 -5
- package/templates/rust/readme.hbs +33 -46
- package/templates/rust/transport.hbs +170 -159
- package/templates/rust/types.hbs +34 -111
- package/templates/typescript/domain-class.hbs +14 -6
- package/templates/typescript/domain.hbs +13 -5
- package/templates/typescript/package-json.hbs +3 -1
- package/templates/typescript/readme.hbs +42 -42
- package/templates/typescript/transport.hbs +46 -8
- package/templates/typescript/types-errors.hbs +1 -1
- package/dist/adapters/typescript/model-emitter.d.ts +0 -13
- package/dist/adapters/typescript/model-emitter.d.ts.map +0 -1
- package/dist/adapters/typescript/model-emitter.js +0 -415
- package/dist/adapters/typescript/model-emitter.js.map +0 -1
|
@@ -2,89 +2,91 @@
|
|
|
2
2
|
|
|
3
3
|
require "faraday"
|
|
4
4
|
require "json"
|
|
5
|
+
require "time"
|
|
6
|
+
require "uri"
|
|
5
7
|
require_relative "errors"
|
|
6
8
|
|
|
7
|
-
module {{
|
|
9
|
+
module {{moduleName}}
|
|
8
10
|
# HTTP transport layer built on Faraday.
|
|
9
11
|
class Transport
|
|
12
|
+
RETRYABLE_METHODS = %i[get head options put delete].freeze
|
|
13
|
+
RETRYABLE_STATUSES = [408, 425, 429, 500, 502, 503, 504].freeze
|
|
14
|
+
|
|
10
15
|
attr_reader :config
|
|
11
16
|
attr_reader :connection
|
|
12
17
|
|
|
13
18
|
def initialize(config)
|
|
14
19
|
@config = config
|
|
15
|
-
@connection = Faraday.new(url: config.base_url) do |
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
@connection = Faraday.new(url: config.base_url) do |faraday|
|
|
21
|
+
faraday.request :json
|
|
22
|
+
faraday.response :raise_error
|
|
23
|
+
faraday.options.timeout = config.timeout
|
|
24
|
+
faraday.options.open_timeout = [config.timeout / 3.0, 2.0].max
|
|
25
|
+
faraday.headers["Accept"] = "application/json"
|
|
26
|
+
faraday.headers["User-Agent"] = "{{gemName}}-ruby-sdk/{{meta.packageVersion}}"
|
|
27
|
+
config.default_headers.each { |key, value| faraday.headers[key] = value }
|
|
28
|
+
faraday.adapter Faraday.default_adapter
|
|
24
29
|
end
|
|
25
30
|
end
|
|
26
31
|
|
|
27
32
|
def request(method:, path:, path_params: {}, query: nil, body: nil, headers: {}, content_type: nil)
|
|
28
33
|
url = build_url(path, path_params)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
body = body.to_h if body.respond_to?(:to_h)
|
|
35
|
+
method = method.to_sym
|
|
36
|
+
max_retries = [(@config.retries[:attempts] || 3).to_i, 0].max
|
|
37
|
+
attempt = 0
|
|
38
|
+
|
|
34
39
|
begin
|
|
35
|
-
attempts += 1
|
|
36
40
|
response = @connection.run_request(
|
|
37
|
-
method
|
|
38
|
-
) do |
|
|
39
|
-
|
|
41
|
+
method, url, body, build_headers(headers, content_type)
|
|
42
|
+
) do |request|
|
|
43
|
+
request.params.merge!(query) if query && !query.empty?
|
|
40
44
|
end
|
|
41
45
|
parse_response(response)
|
|
42
|
-
rescue Faraday::Error =>
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
rescue Faraday::Error => error
|
|
47
|
+
mapped = map_error(error, method: method, path: path)
|
|
48
|
+
unless retryable_request?(method, headers) && mapped.retryable? && attempt < max_retries
|
|
49
|
+
raise mapped
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
sleep(retry_delay(error, attempt))
|
|
53
|
+
attempt += 1
|
|
45
54
|
retry
|
|
46
55
|
end
|
|
47
56
|
end
|
|
48
57
|
|
|
49
|
-
def
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
request(method: :patch, path: path, body: body, path_params: path_params,
|
|
65
|
-
query: query, headers: headers, content_type: content_type)
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def delete(path:, path_params: {}, query: nil, headers: {})
|
|
69
|
-
request(method: :delete, path: path, path_params: path_params, query: query, headers: headers)
|
|
58
|
+
def stream(method:, path:, path_params: {}, query: nil, body: nil, headers: {}, content_type: nil)
|
|
59
|
+
Enumerator.new do |output|
|
|
60
|
+
url = build_url(path, path_params)
|
|
61
|
+
@connection.run_request(
|
|
62
|
+
method.to_sym,
|
|
63
|
+
url,
|
|
64
|
+
body&.respond_to?(:to_h) ? body.to_h : body,
|
|
65
|
+
build_headers(headers, content_type)
|
|
66
|
+
) do |request|
|
|
67
|
+
request.params.merge!(query) if query && !query.empty?
|
|
68
|
+
request.options.on_data = proc { |chunk, _bytes| output << chunk }
|
|
69
|
+
end
|
|
70
|
+
rescue Faraday::Error => error
|
|
71
|
+
raise map_error(error, method: method, path: path)
|
|
72
|
+
end
|
|
70
73
|
end
|
|
71
74
|
|
|
72
75
|
private
|
|
73
76
|
|
|
74
77
|
def build_url(path, path_params)
|
|
75
78
|
return path if path_params.empty?
|
|
76
|
-
|
|
77
|
-
|
|
79
|
+
|
|
80
|
+
path.gsub(/(?:\$\{(\w+)\}|:(\w+))/) do |match|
|
|
81
|
+
key = Regexp.last_match(1) || Regexp.last_match(2)
|
|
78
82
|
value = path_params[key.to_sym] || path_params[key]
|
|
79
|
-
value.nil? ?
|
|
83
|
+
value.nil? ? match : URI.encode_www_form_component(value.to_s)
|
|
80
84
|
end
|
|
81
85
|
end
|
|
82
86
|
|
|
83
87
|
def build_headers(extra_headers = {}, content_type = nil)
|
|
84
88
|
headers = {}
|
|
85
|
-
if content_type
|
|
86
|
-
headers["Content-Type"] = content_type
|
|
87
|
-
end
|
|
89
|
+
headers["Content-Type"] = content_type if content_type
|
|
88
90
|
{{#each securitySchemes}}
|
|
89
91
|
{{#if (eq type "apiKey")}}
|
|
90
92
|
{{#if (eq in "header")}}
|
|
@@ -92,13 +94,13 @@ module {{pascalCase meta.clientName}}
|
|
|
92
94
|
{{/if}}
|
|
93
95
|
{{/if}}
|
|
94
96
|
{{#if (and (eq type "http") (eq scheme "bearer"))}}
|
|
95
|
-
headers["Authorization"] = "Bearer " +
|
|
97
|
+
headers["Authorization"] = "Bearer " + @config.{{camelCase name}} if @config.{{camelCase name}}
|
|
96
98
|
{{/if}}
|
|
97
99
|
{{#if (eq type "oauth2")}}
|
|
98
|
-
headers["Authorization"] = "Bearer " +
|
|
100
|
+
headers["Authorization"] = "Bearer " + @config.{{camelCase name}} if @config.{{camelCase name}}
|
|
99
101
|
{{/if}}
|
|
100
102
|
{{#if (eq type "openIdConnect")}}
|
|
101
|
-
headers["Authorization"] = "Bearer " +
|
|
103
|
+
headers["Authorization"] = "Bearer " + @config.{{camelCase name}} if @config.{{camelCase name}}
|
|
102
104
|
{{/if}}
|
|
103
105
|
{{/each}}
|
|
104
106
|
headers.merge!(extra_headers)
|
|
@@ -108,70 +110,94 @@ module {{pascalCase meta.clientName}}
|
|
|
108
110
|
def parse_response(response)
|
|
109
111
|
body = response.body
|
|
110
112
|
return nil if body.nil? || (body.is_a?(String) && body.strip.empty?)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
113
|
+
return body unless body.is_a?(String)
|
|
114
|
+
|
|
115
|
+
JSON.parse(body)
|
|
116
|
+
rescue JSON::ParserError
|
|
117
|
+
body
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def retryable_request?(method, headers)
|
|
121
|
+
return true if @config.retry_unsafe_requests
|
|
122
|
+
return true if RETRYABLE_METHODS.include?(method)
|
|
123
|
+
|
|
124
|
+
@config.default_headers.merge(headers).any? do |key, value|
|
|
125
|
+
key.to_s.casecmp("X-Idempotency-Key").zero? && !value.to_s.empty?
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def retry_delay(error, attempt)
|
|
130
|
+
retry_after = retry_after_seconds(error)
|
|
131
|
+
return retry_after unless retry_after.nil?
|
|
132
|
+
|
|
133
|
+
base = (@config.retries[:backoff] || 0.4).to_f
|
|
134
|
+
max_backoff = (@config.retries[:max_backoff] || 30.0).to_f
|
|
135
|
+
delay = [base * (2**attempt), max_backoff].min
|
|
136
|
+
@config.retries.fetch(:jitter, true) ? rand * delay : delay
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def retry_after_seconds(error)
|
|
140
|
+
headers = error.response&.[](:headers) || {}
|
|
141
|
+
value = headers["retry-after"] || headers["Retry-After"]
|
|
142
|
+
return nil if value.nil? || value.to_s.empty?
|
|
143
|
+
|
|
144
|
+
[Float(value), (@config.retries[:max_backoff] || 30.0).to_f].min
|
|
145
|
+
rescue ArgumentError, TypeError
|
|
146
|
+
begin
|
|
147
|
+
[
|
|
148
|
+
[Time.httpdate(value.to_s) - Time.now, 0.0].max,
|
|
149
|
+
(@config.retries[:max_backoff] || 30.0).to_f
|
|
150
|
+
].min
|
|
151
|
+
rescue ArgumentError
|
|
152
|
+
nil
|
|
119
153
|
end
|
|
120
154
|
end
|
|
121
155
|
|
|
122
156
|
def map_error(error, method:, path:)
|
|
157
|
+
status = error.response&.[](:status).to_i
|
|
158
|
+
headers = error.response&.[](:headers) || {}
|
|
159
|
+
request_id = headers["x-request-id"] || headers["X-Request-ID"] || headers["request-id"]
|
|
160
|
+
|
|
123
161
|
case error
|
|
124
162
|
when Faraday::TimeoutError
|
|
125
|
-
SDKError.new("Request timed out", 0, "
|
|
163
|
+
SDKError.new("Request timed out", 0, "timeout", method: method, path: path)
|
|
126
164
|
when Faraday::ConnectionFailed
|
|
127
|
-
SDKError.new("Connection failed:
|
|
128
|
-
when Faraday::ResourceNotFound
|
|
129
|
-
SDKError.new("Not found: " + error.message, 404, "not_found", method: method, path: path)
|
|
130
|
-
when Faraday::ClientError
|
|
131
|
-
status = error.response&.[](:status) || 400
|
|
132
|
-
begin
|
|
133
|
-
body = error.response&.[](:body)
|
|
134
|
-
envelope = body.is_a?(String) ? JSON.parse(body) : body
|
|
135
|
-
if envelope.is_a?(Hash)
|
|
136
|
-
SDKError.new(
|
|
137
|
-
envelope["message"] || envelope["error"] || "Request failed",
|
|
138
|
-
status,
|
|
139
|
-
envelope["error"] || envelope["code"] || "unknown_error",
|
|
140
|
-
request_id: envelope["request_id"],
|
|
141
|
-
method: method, path: path
|
|
142
|
-
)
|
|
143
|
-
else
|
|
144
|
-
SDKError.new("Request failed with status " + status.to_s, status, "unknown_error", method: method, path: path)
|
|
145
|
-
end
|
|
146
|
-
rescue
|
|
147
|
-
SDKError.new("Request failed with status " + status.to_s, status, "unknown_error", method: method, path: path)
|
|
148
|
-
end
|
|
149
|
-
when Faraday::ServerError
|
|
150
|
-
status = error.response&.[](:status) || 500
|
|
151
|
-
SDKError.new("Server error: " + error.message, status, "internal_server_error", method: method, path: path)
|
|
165
|
+
SDKError.new("Connection failed: #{error.message}", 0, "network_error", method: method, path: path)
|
|
152
166
|
else
|
|
153
|
-
|
|
167
|
+
body = error.response&.[](:body)
|
|
168
|
+
envelope = parse_error_body(body)
|
|
169
|
+
message = envelope["message"] || envelope.dig("error", "message") || error.message
|
|
170
|
+
code = envelope["code"] || envelope.dig("error", "code")
|
|
171
|
+
code ||= envelope["error"] if envelope["error"].is_a?(String)
|
|
172
|
+
SDKError.new(
|
|
173
|
+
message,
|
|
174
|
+
status,
|
|
175
|
+
code || default_error_code(status),
|
|
176
|
+
request_id: envelope["request_id"] || request_id,
|
|
177
|
+
method: method,
|
|
178
|
+
path: path,
|
|
179
|
+
details: envelope["details"]
|
|
180
|
+
)
|
|
154
181
|
end
|
|
155
182
|
end
|
|
156
183
|
|
|
157
|
-
def
|
|
158
|
-
return
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
else
|
|
166
|
-
false
|
|
167
|
-
end
|
|
184
|
+
def parse_error_body(body)
|
|
185
|
+
return body if body.is_a?(Hash)
|
|
186
|
+
return {} unless body.is_a?(String) && !body.empty?
|
|
187
|
+
|
|
188
|
+
parsed = JSON.parse(body)
|
|
189
|
+
parsed.is_a?(Hash) ? parsed : {}
|
|
190
|
+
rescue JSON::ParserError
|
|
191
|
+
{}
|
|
168
192
|
end
|
|
169
193
|
|
|
170
|
-
def
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
194
|
+
def default_error_code(status)
|
|
195
|
+
return "network_error" if status.zero?
|
|
196
|
+
return "rate_limit_exceeded" if status == 429
|
|
197
|
+
return "request_timeout" if status == 408
|
|
198
|
+
return "internal_server_error" if status >= 500
|
|
199
|
+
|
|
200
|
+
"http_error"
|
|
175
201
|
end
|
|
176
202
|
end
|
|
177
203
|
end
|
package/templates/ruby/types.hbs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
module {{
|
|
3
|
+
module {{moduleName}}
|
|
4
4
|
{{#each schemas}}
|
|
5
5
|
{{#if isEnum}}
|
|
6
6
|
# {{#if description}}{{description}}{{else}}Enum for {{name}} values.{{/if}}
|
|
@@ -34,13 +34,8 @@ module {{pascalCase meta.clientName}}
|
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
{{else if isTypeAlias}}
|
|
37
|
-
{{
|
|
38
|
-
|
|
39
|
-
{{pascalCase name}} = Class.new
|
|
40
|
-
{{else}}
|
|
41
|
-
# {{pascalCase name}} — schema type alias
|
|
42
|
-
{{pascalCase name}} = Class.new
|
|
43
|
-
{{/if}}
|
|
37
|
+
# {{pascalCase name}} — type alias for {{aliasDocType}}
|
|
38
|
+
{{pascalCase name}} = {{aliasTarget}}
|
|
44
39
|
|
|
45
40
|
{{else if isUnionType}}
|
|
46
41
|
# {{pascalCase name}} — union type: {{#each unionMembers}}{{this}}{{#unless @last}} | {{/unless}}{{/each}}
|
|
@@ -56,14 +51,14 @@ module {{pascalCase meta.clientName}}
|
|
|
56
51
|
# {{#if description}}{{description}}{{else}}Schema type: {{name}}{{/if}}
|
|
57
52
|
class {{pascalCase name}}
|
|
58
53
|
{{#each properties}}
|
|
59
|
-
# @return [{{
|
|
60
|
-
attr_accessor :{{
|
|
54
|
+
# @return [{{docType}}]{{#if description}} {{description}}{{/if}}
|
|
55
|
+
attr_accessor :{{propertyName}}
|
|
61
56
|
{{/each}}
|
|
62
57
|
|
|
63
|
-
# @param {{#each properties}}{{
|
|
64
|
-
def initialize({{#each properties}}{{
|
|
58
|
+
# @param {{#each properties}}{{propertyName}} [{{docType}}]{{#unless @last}}, {{/unless}}{{/each}}
|
|
59
|
+
def initialize({{#each properties}}{{propertyName}}: {{^required}}nil{{/required}}{{#unless @last}}, {{/unless}}{{/each}})
|
|
65
60
|
{{#each properties}}
|
|
66
|
-
@{{
|
|
61
|
+
@{{propertyName}} = {{propertyName}}
|
|
67
62
|
{{/each}}
|
|
68
63
|
end
|
|
69
64
|
|
|
@@ -72,9 +67,9 @@ module {{pascalCase meta.clientName}}
|
|
|
72
67
|
{
|
|
73
68
|
{{#each properties}}
|
|
74
69
|
{{#if (eq type "DateTime")}}
|
|
75
|
-
{{
|
|
70
|
+
{{propertyName}}: @{{propertyName}}&.iso8601,
|
|
76
71
|
{{else}}
|
|
77
|
-
{{
|
|
72
|
+
{{propertyName}}: @{{propertyName}},
|
|
78
73
|
{{/if}}
|
|
79
74
|
{{/each}}
|
|
80
75
|
}.compact
|
|
@@ -88,7 +83,7 @@ module {{pascalCase meta.clientName}}
|
|
|
88
83
|
|
|
89
84
|
new(
|
|
90
85
|
{{#each properties}}
|
|
91
|
-
{{
|
|
86
|
+
{{propertyName}}: attributes.key?("{{name}}") ? attributes["{{name}}"] : attributes[:{{propertyName}}],
|
|
92
87
|
{{/each}}
|
|
93
88
|
)
|
|
94
89
|
end
|
package/templates/rust/cargo.hbs
CHANGED
|
@@ -13,6 +13,8 @@ reqwest = { version = "0.12", default-features = false, features = ["json", "rus
|
|
|
13
13
|
tokio = { version = "1", features = ["full"] }
|
|
14
14
|
thiserror = "2"
|
|
15
15
|
url = "2"
|
|
16
|
+
urlencoding = "2"
|
|
17
|
+
httpdate = "1"
|
|
16
18
|
{{#if hasSSE}}
|
|
17
19
|
futures-core = "0.3"
|
|
18
20
|
tokio-stream = "0.1"
|
|
@@ -2,6 +2,7 @@ use crate::transport::{Transport, TransportConfig};
|
|
|
2
2
|
{{#each groups}}
|
|
3
3
|
use crate::api::{{snakeCase fileName}}::{{interfaceName}};
|
|
4
4
|
{{/each}}
|
|
5
|
+
use std::collections::HashMap;
|
|
5
6
|
use std::time::Duration;
|
|
6
7
|
|
|
7
8
|
/// Configuration for the {{title}} API client.
|
|
@@ -31,6 +32,10 @@ pub struct ClientConfig {
|
|
|
31
32
|
pub timeout: Duration,
|
|
32
33
|
/// Maximum number of retry attempts.
|
|
33
34
|
pub max_retries: u32,
|
|
35
|
+
/// Allow automatic retries for unsafe methods without an idempotency key.
|
|
36
|
+
pub retry_unsafe_requests: bool,
|
|
37
|
+
/// Headers sent with every request.
|
|
38
|
+
pub default_headers: HashMap<String, String>,
|
|
34
39
|
}
|
|
35
40
|
|
|
36
41
|
impl Default for ClientConfig {
|
|
@@ -42,6 +47,8 @@ impl Default for ClientConfig {
|
|
|
42
47
|
{{/each}}
|
|
43
48
|
timeout: Duration::from_secs(15),
|
|
44
49
|
max_retries: 3,
|
|
50
|
+
retry_unsafe_requests: false,
|
|
51
|
+
default_headers: HashMap::new(),
|
|
45
52
|
}
|
|
46
53
|
}
|
|
47
54
|
}
|
|
@@ -77,13 +84,15 @@ impl Client {
|
|
|
77
84
|
/// let client = Client::new(config);
|
|
78
85
|
/// ```
|
|
79
86
|
pub fn new(config: ClientConfig) -> Self {
|
|
80
|
-
let
|
|
87
|
+
let transport_config = TransportConfig {
|
|
81
88
|
base_url: config.base_url,
|
|
82
89
|
{{#each securitySchemes}}
|
|
83
90
|
{{snakeCase configKey}}: config.{{snakeCase configKey}},
|
|
84
91
|
{{/each}}
|
|
85
92
|
timeout: config.timeout,
|
|
86
93
|
max_retries: config.max_retries,
|
|
94
|
+
retry_unsafe_requests: config.retry_unsafe_requests,
|
|
95
|
+
default_headers: config.default_headers,
|
|
87
96
|
};
|
|
88
97
|
|
|
89
98
|
let transport = Transport::new(transport_config);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
use crate::transport::Transport;
|
|
2
1
|
use crate::error::SdkError;
|
|
2
|
+
use crate::transport::Transport;
|
|
3
3
|
use reqwest::Method;
|
|
4
|
+
use serde::Serialize;
|
|
4
5
|
{{#if imports.length}}
|
|
5
6
|
use crate::types::{ {{join imports ", "}} };
|
|
6
7
|
{{/if}}
|
|
@@ -16,79 +17,95 @@ impl {{interfaceName}} {
|
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
{{#each operations}}
|
|
19
|
-
|
|
20
|
+
{{#if summary}}
|
|
20
21
|
/// {{summary}}{{#if description}}
|
|
21
22
|
///
|
|
22
23
|
/// {{description}}{{/if}}
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
{{/if}}
|
|
25
|
+
{{#if deprecated}}
|
|
25
26
|
#[deprecated{{#if deprecationMessage}}(note = "{{deprecationMessage}}"){{/if}}]
|
|
26
|
-
|
|
27
|
-
pub async fn {{
|
|
28
|
-
&self,
|
|
27
|
+
{{/if}}
|
|
28
|
+
pub async fn {{methodName}}(
|
|
29
|
+
&self{{#if hasParameters}},
|
|
30
|
+
{{{signature}}}{{/if}},
|
|
31
|
+
) -> Result<{{targetResponseType}}, SdkError> {
|
|
32
|
+
let mut path = "{{path}}".to_string();
|
|
29
33
|
{{#each pathParams}}
|
|
30
|
-
{{
|
|
34
|
+
path = path.replace("{{placeholder}}", &encode_path(&{{parameterName}}.to_string()));
|
|
31
35
|
{{/each}}
|
|
32
|
-
|
|
33
|
-
body: &{{{requestBody.rustType}}},
|
|
34
|
-
{{/if}}
|
|
35
|
-
{{#if queryType}}
|
|
36
|
-
query: Option<&{{{rustQueryType queryType}}}>,
|
|
37
|
-
{{/if}}
|
|
38
|
-
) -> Result<{{#if isEventStream}}Vec<{{#if eventSchema}}{{{rustType eventSchema}}}{{else}}serde_json::Value{{/if}}>{{else}}{{{rustResponseType}}}{{/if}}, SdkError> {
|
|
39
|
-
let path = format!("{{path}}"{{#each pathParams}}, {{snakeCase name}} = {{#if (isRustCopyType type)}}{{snakeCase name}}{{else}}{{snakeCase name}}{{/if}}{{/each}});
|
|
40
|
-
{{#if (hasQueryParams this)}}
|
|
36
|
+
|
|
41
37
|
let mut query_params: Vec<(String, String)> = Vec::new();
|
|
42
|
-
|
|
43
|
-
{{#
|
|
44
|
-
|
|
45
|
-
query_params.push(("{{originalName}}".to_string(), v.to_string()));
|
|
46
|
-
}
|
|
47
|
-
{{/each}}
|
|
48
|
-
}
|
|
49
|
-
let query_slice: Option<&[(String, String)]> = if query_params.is_empty() { None } else { Some(&query_params) };
|
|
50
|
-
{{else if queryType}}
|
|
51
|
-
// Query params from typed query object
|
|
52
|
-
let query_slice: Option<&[(String, String)]> = None; // TODO: serialize query type
|
|
38
|
+
{{#each queryParams}}
|
|
39
|
+
{{#if required}}
|
|
40
|
+
query_params.push(("{{originalName}}".to_string(), encode_value(&{{parameterName}})?));
|
|
53
41
|
{{else}}
|
|
54
|
-
let
|
|
42
|
+
if let Some(value) = {{parameterName}}.as_ref() {
|
|
43
|
+
query_params.push(("{{originalName}}".to_string(), encode_value(value)?));
|
|
44
|
+
}
|
|
55
45
|
{{/if}}
|
|
46
|
+
{{/each}}
|
|
47
|
+
let query = (!query_params.is_empty()).then_some(query_params.as_slice());
|
|
56
48
|
|
|
57
|
-
{{#if (or (hasRequestBody this) (needsRequestHeaders this))}}
|
|
58
49
|
let mut headers: Vec<(String, String)> = Vec::new();
|
|
59
|
-
{{#if (hasRequestBody this)}}
|
|
60
|
-
{{#if (and (neq requestBody.contentType "application/json") (neq requestBody.contentType "multipart/form-data"))}}
|
|
61
|
-
headers.push(("Content-Type".to_string(), "{{requestBody.contentType}}".to_string()));
|
|
62
|
-
{{/if}}
|
|
63
|
-
{{/if}}
|
|
64
|
-
{{#if (and headerParams.length headerType)}}
|
|
65
|
-
// Header params injected from header options
|
|
66
|
-
{{/if}}
|
|
67
50
|
{{#each headerParams}}
|
|
68
|
-
|
|
69
|
-
|
|
51
|
+
{{#if required}}
|
|
52
|
+
headers.push(("{{originalName}}".to_string(), encode_value(&{{parameterName}})?));
|
|
53
|
+
{{else}}
|
|
54
|
+
if let Some(value) = {{parameterName}}.as_ref() {
|
|
55
|
+
headers.push(("{{originalName}}".to_string(), encode_value(value)?));
|
|
70
56
|
}
|
|
71
|
-
{{/each}}
|
|
72
|
-
{{#if cookieParams}}
|
|
73
|
-
// Cookie params
|
|
74
57
|
{{/if}}
|
|
75
|
-
|
|
58
|
+
{{/each}}
|
|
59
|
+
{{#each cookieParams}}
|
|
60
|
+
{{#if required}}
|
|
61
|
+
headers.push(("Cookie".to_string(), format!("{{originalName}}={}", encode_value(&{{parameterName}})?)));
|
|
76
62
|
{{else}}
|
|
77
|
-
let
|
|
63
|
+
if let Some(value) = {{parameterName}}.as_ref() {
|
|
64
|
+
headers.push(("Cookie".to_string(), format!("{{originalName}}={}", encode_value(value)?)));
|
|
65
|
+
}
|
|
78
66
|
{{/if}}
|
|
67
|
+
{{/each}}
|
|
68
|
+
let headers = (!headers.is_empty()).then_some(headers.as_slice());
|
|
79
69
|
|
|
80
|
-
|
|
70
|
+
{{#if isEventStream}}
|
|
71
|
+
self.transport.execute_raw(
|
|
72
|
+
Method::{{httpMethod}},
|
|
73
|
+
&path,
|
|
74
|
+
query,
|
|
75
|
+
{{{bodyArgument}}},
|
|
76
|
+
headers,
|
|
77
|
+
).await
|
|
78
|
+
{{else if isVoid}}
|
|
79
|
+
self.transport.execute_raw(
|
|
81
80
|
Method::{{httpMethod}},
|
|
82
81
|
&path,
|
|
83
|
-
|
|
84
|
-
{{
|
|
85
|
-
|
|
82
|
+
query,
|
|
83
|
+
{{{bodyArgument}}},
|
|
84
|
+
headers,
|
|
85
|
+
).await.map(|_| ())
|
|
86
86
|
{{else}}
|
|
87
|
-
|
|
88
|
-
{{
|
|
89
|
-
|
|
87
|
+
self.transport.execute::<{{targetResponseType}}>(
|
|
88
|
+
Method::{{httpMethod}},
|
|
89
|
+
&path,
|
|
90
|
+
query,
|
|
91
|
+
{{{bodyArgument}}},
|
|
92
|
+
headers,
|
|
90
93
|
).await
|
|
94
|
+
{{/if}}
|
|
91
95
|
}
|
|
92
96
|
|
|
93
97
|
{{/each}}
|
|
94
98
|
}
|
|
99
|
+
|
|
100
|
+
fn encode_path(value: &str) -> String {
|
|
101
|
+
urlencoding::encode(value).into_owned()
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
fn encode_value<T: Serialize>(value: &T) -> Result<String, SdkError> {
|
|
105
|
+
match serde_json::to_value(value)
|
|
106
|
+
.map_err(|error| SdkError::ValidationError(error.to_string()))?
|
|
107
|
+
{
|
|
108
|
+
serde_json::Value::String(value) => Ok(value),
|
|
109
|
+
value => Ok(value.to_string()),
|
|
110
|
+
}
|
|
111
|
+
}
|
package/templates/rust/error.hbs
CHANGED
|
@@ -14,6 +14,8 @@ pub enum SdkError {
|
|
|
14
14
|
code: Option<String>,
|
|
15
15
|
/// Human-readable error message.
|
|
16
16
|
message: String,
|
|
17
|
+
/// Server-provided request identifier.
|
|
18
|
+
request_id: Option<String>,
|
|
17
19
|
/// Raw response body for debugging.
|
|
18
20
|
body: String,
|
|
19
21
|
},
|
|
@@ -56,7 +58,7 @@ impl SdkError {
|
|
|
56
58
|
self,
|
|
57
59
|
Self::NetworkError(_)
|
|
58
60
|
| Self::ApiError {
|
|
59
|
-
status: 429 | 502 | 503 | 504,
|
|
61
|
+
status: 408 | 425 | 429 | 500 | 502 | 503 | 504,
|
|
60
62
|
..
|
|
61
63
|
}
|
|
62
64
|
)
|
package/templates/rust/lib.hbs
CHANGED
|
@@ -59,12 +59,11 @@
|
|
|
59
59
|
//! - **DeserializationError** — response body could not be parsed
|
|
60
60
|
//! - **ValidationError** — invalid request parameters
|
|
61
61
|
//!
|
|
62
|
-
//! ```rust
|
|
63
|
-
//!
|
|
62
|
+
//! ```rust,ignore
|
|
63
|
+
//! let result = client.resource.some_method().await;
|
|
64
|
+
//! match result {
|
|
64
65
|
//! Ok(response) => println!("Success: {:?}", response),
|
|
65
|
-
//! Err(
|
|
66
|
-
//! Err(SdkError::NetworkError(e)) => eprintln!("Network error: {}", e),
|
|
67
|
-
//! Err(e) => eprintln!("Other error: {}", e),
|
|
66
|
+
//! Err(error) => eprintln!("SDK error: {}", error),
|
|
68
67
|
//! }
|
|
69
68
|
//! ```
|
|
70
69
|
|