@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
|
@@ -4,16 +4,15 @@ using System;
|
|
|
4
4
|
using System.Collections.Generic;
|
|
5
5
|
using System.IO;
|
|
6
6
|
using System.Linq;
|
|
7
|
+
using System.Net;
|
|
7
8
|
using System.Net.Http;
|
|
8
9
|
using System.Text;
|
|
9
10
|
using System.Text.Json;
|
|
10
11
|
using System.Threading;
|
|
11
12
|
using System.Threading.Tasks;
|
|
12
13
|
|
|
13
|
-
/// <summary>
|
|
14
|
-
|
|
15
|
-
/// </summary>
|
|
16
|
-
public class TransportConfig
|
|
14
|
+
/// <summary>Configuration for the HTTP transport layer.</summary>
|
|
15
|
+
public sealed class TransportConfig
|
|
17
16
|
{
|
|
18
17
|
public string BaseUrl { get; set; } = "";
|
|
19
18
|
{{#each securitySchemes}}
|
|
@@ -21,33 +20,54 @@ public class TransportConfig
|
|
|
21
20
|
{{/each}}
|
|
22
21
|
public int TimeoutMs { get; set; } = 15000;
|
|
23
22
|
public int MaxRetries { get; set; } = 3;
|
|
23
|
+
public bool RetryUnsafeRequests { get; set; }
|
|
24
24
|
public Dictionary<string, string>? DefaultHeaders { get; set; }
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
/// <summary>
|
|
28
|
-
/// Low-level HTTP transport handling authentication, serialization, retries,
|
|
28
|
+
/// Low-level HTTP transport handling authentication, serialization, bounded retries,
|
|
29
|
+
/// cancellation, and normalized API errors.
|
|
29
30
|
/// </summary>
|
|
30
|
-
public class Transport
|
|
31
|
+
public sealed class Transport : IDisposable
|
|
31
32
|
{
|
|
33
|
+
private static readonly HashSet<HttpStatusCode> RetryableStatuses = new()
|
|
34
|
+
{
|
|
35
|
+
HttpStatusCode.RequestTimeout,
|
|
36
|
+
(HttpStatusCode)425,
|
|
37
|
+
(HttpStatusCode)429,
|
|
38
|
+
HttpStatusCode.InternalServerError,
|
|
39
|
+
HttpStatusCode.BadGateway,
|
|
40
|
+
HttpStatusCode.ServiceUnavailable,
|
|
41
|
+
HttpStatusCode.GatewayTimeout,
|
|
42
|
+
};
|
|
43
|
+
|
|
32
44
|
private readonly TransportConfig _config;
|
|
33
45
|
private readonly HttpClient _httpClient;
|
|
34
|
-
private
|
|
46
|
+
private readonly bool _ownsHttpClient;
|
|
47
|
+
|
|
48
|
+
private static readonly JsonSerializerOptions JsonOptions = new()
|
|
35
49
|
{
|
|
36
50
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
|
37
51
|
PropertyNameCaseInsensitive = true,
|
|
38
52
|
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull,
|
|
39
53
|
};
|
|
40
54
|
|
|
41
|
-
public Transport(TransportConfig config)
|
|
55
|
+
public Transport(TransportConfig config, HttpClient? httpClient = null)
|
|
42
56
|
{
|
|
57
|
+
if (string.IsNullOrWhiteSpace(config.BaseUrl))
|
|
58
|
+
throw new ArgumentException("BaseUrl must not be blank", nameof(config));
|
|
59
|
+
if (config.TimeoutMs <= 0)
|
|
60
|
+
throw new ArgumentOutOfRangeException(nameof(config), "TimeoutMs must be greater than zero");
|
|
61
|
+
if (config.MaxRetries < 0)
|
|
62
|
+
throw new ArgumentOutOfRangeException(nameof(config), "MaxRetries must not be negative");
|
|
63
|
+
|
|
43
64
|
_config = config;
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
};
|
|
65
|
+
_ownsHttpClient = httpClient is null;
|
|
66
|
+
_httpClient = httpClient ?? new HttpClient();
|
|
67
|
+
_httpClient.Timeout = TimeSpan.FromMilliseconds(config.TimeoutMs);
|
|
48
68
|
}
|
|
49
69
|
|
|
50
|
-
public class Request
|
|
70
|
+
public sealed class Request
|
|
51
71
|
{
|
|
52
72
|
public HttpMethod Method { get; set; } = HttpMethod.Get;
|
|
53
73
|
public string Path { get; set; } = "";
|
|
@@ -59,136 +79,165 @@ public class Transport
|
|
|
59
79
|
|
|
60
80
|
public async Task<T> ExecuteAsync<T>(Request request, CancellationToken cancellationToken = default)
|
|
61
81
|
{
|
|
62
|
-
|
|
82
|
+
using var response = await SendWithRetriesAsync(
|
|
83
|
+
request,
|
|
84
|
+
HttpCompletionOption.ResponseContentRead,
|
|
85
|
+
cancellationToken);
|
|
86
|
+
var body = await response.Content.ReadAsStringAsync(cancellationToken);
|
|
87
|
+
EnsureSuccess(response, body);
|
|
88
|
+
|
|
89
|
+
if (string.IsNullOrWhiteSpace(body)) return default!;
|
|
90
|
+
|
|
91
|
+
try
|
|
92
|
+
{
|
|
93
|
+
return JsonSerializer.Deserialize<T>(body, JsonOptions)
|
|
94
|
+
?? throw new JsonException("Response body was JSON null");
|
|
95
|
+
}
|
|
96
|
+
catch (JsonException error)
|
|
97
|
+
{
|
|
98
|
+
throw new SdkException(
|
|
99
|
+
(int)response.StatusCode,
|
|
100
|
+
"DESERIALIZE_ERROR",
|
|
101
|
+
"Failed to deserialize response body",
|
|
102
|
+
GetRequestId(response),
|
|
103
|
+
error);
|
|
104
|
+
}
|
|
63
105
|
}
|
|
64
106
|
|
|
65
107
|
public async Task<byte[]> ExecuteRawAsync(Request request, CancellationToken cancellationToken = default)
|
|
66
108
|
{
|
|
67
|
-
|
|
109
|
+
using var response = await SendWithRetriesAsync(
|
|
110
|
+
request,
|
|
111
|
+
HttpCompletionOption.ResponseContentRead,
|
|
112
|
+
cancellationToken);
|
|
113
|
+
var body = await response.Content.ReadAsByteArrayAsync(cancellationToken);
|
|
114
|
+
if (!response.IsSuccessStatusCode)
|
|
115
|
+
throw ParseErrorResponse(response, Encoding.UTF8.GetString(body));
|
|
116
|
+
return body;
|
|
68
117
|
}
|
|
69
118
|
|
|
70
|
-
|
|
119
|
+
/// <summary>
|
|
120
|
+
/// Executes a streaming request. The caller owns and must dispose the returned response.
|
|
121
|
+
/// </summary>
|
|
122
|
+
public async Task<HttpResponseMessage> StreamAsync(
|
|
123
|
+
Request request,
|
|
124
|
+
CancellationToken cancellationToken = default)
|
|
71
125
|
{
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
httpRequest,
|
|
126
|
+
var response = await SendWithRetriesAsync(
|
|
127
|
+
request,
|
|
75
128
|
HttpCompletionOption.ResponseHeadersRead,
|
|
76
129
|
cancellationToken);
|
|
77
|
-
if (
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
130
|
+
if (response.IsSuccessStatusCode) return response;
|
|
131
|
+
|
|
132
|
+
var body = await response.Content.ReadAsStringAsync(cancellationToken);
|
|
133
|
+
var error = ParseErrorResponse(response, body);
|
|
134
|
+
response.Dispose();
|
|
135
|
+
throw error;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
public void Dispose()
|
|
139
|
+
{
|
|
140
|
+
if (_ownsHttpClient) _httpClient.Dispose();
|
|
83
141
|
}
|
|
84
142
|
|
|
85
|
-
private async Task<
|
|
86
|
-
Request request,
|
|
143
|
+
private async Task<HttpResponseMessage> SendWithRetriesAsync(
|
|
144
|
+
Request request,
|
|
145
|
+
HttpCompletionOption completionOption,
|
|
146
|
+
CancellationToken cancellationToken)
|
|
87
147
|
{
|
|
88
|
-
|
|
89
|
-
|
|
148
|
+
var canRetry = CanRetry(request);
|
|
149
|
+
|
|
150
|
+
for (var attempt = 0; attempt <= _config.MaxRetries; attempt++)
|
|
90
151
|
{
|
|
91
|
-
if (attempt > 0)
|
|
92
|
-
{
|
|
93
|
-
var backoff = (int)Math.Pow(2, attempt - 1) * 200;
|
|
94
|
-
var jitter = Random.Shared.Next(backoff / 2);
|
|
95
|
-
await Task.Delay(backoff + jitter, cancellationToken);
|
|
96
|
-
}
|
|
97
152
|
try
|
|
98
153
|
{
|
|
99
154
|
using var httpRequest = BuildRequest(request);
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
return result;
|
|
110
|
-
}
|
|
111
|
-
if ((int)response.StatusCode >= 500 && attempt < maxRetries)
|
|
155
|
+
var response = await _httpClient.SendAsync(
|
|
156
|
+
httpRequest,
|
|
157
|
+
completionOption,
|
|
158
|
+
cancellationToken);
|
|
159
|
+
|
|
160
|
+
if (response.IsSuccessStatusCode ||
|
|
161
|
+
!canRetry ||
|
|
162
|
+
!RetryableStatuses.Contains(response.StatusCode) ||
|
|
163
|
+
attempt == _config.MaxRetries)
|
|
112
164
|
{
|
|
113
|
-
|
|
114
|
-
continue;
|
|
165
|
+
return response;
|
|
115
166
|
}
|
|
116
|
-
throw ParseErrorResponse(response, body);
|
|
117
|
-
}
|
|
118
|
-
catch (SdkException) { throw; }
|
|
119
|
-
catch (TaskCanceledException) when (cancellationToken.IsCancellationRequested) { throw; }
|
|
120
|
-
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) { throw; }
|
|
121
|
-
catch (Exception ex) when (IsRetryable(ex) && attempt < maxRetries) { lastException = ex; }
|
|
122
|
-
catch (Exception ex)
|
|
123
|
-
{
|
|
124
|
-
throw new SdkException(0, "REQUEST_ERROR", ex.Message, null, ex);
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
throw new SdkException(0, "MAX_RETRIES_EXCEEDED",
|
|
128
|
-
"Request failed after " + (maxRetries + 1) + " attempts", null, lastException);
|
|
129
|
-
}
|
|
130
167
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
{
|
|
137
|
-
if (attempt > 0)
|
|
168
|
+
var delay = GetRetryDelay(response, attempt);
|
|
169
|
+
response.Dispose();
|
|
170
|
+
await Task.Delay(delay, cancellationToken);
|
|
171
|
+
}
|
|
172
|
+
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
|
|
138
173
|
{
|
|
139
|
-
|
|
140
|
-
var jitter = Random.Shared.Next(backoff / 2);
|
|
141
|
-
await Task.Delay(backoff + jitter, cancellationToken);
|
|
174
|
+
throw;
|
|
142
175
|
}
|
|
143
|
-
|
|
176
|
+
catch (Exception error) when (IsNetworkError(error))
|
|
144
177
|
{
|
|
145
|
-
|
|
146
|
-
using var response = await _httpClient.SendAsync(httpRequest, cancellationToken);
|
|
147
|
-
var body = await response.Content.ReadAsByteArrayAsync(cancellationToken);
|
|
148
|
-
if (response.IsSuccessStatusCode) return body;
|
|
149
|
-
var bodyString = Encoding.UTF8.GetString(body);
|
|
150
|
-
if ((int)response.StatusCode >= 500 && attempt < maxRetries)
|
|
178
|
+
if (!canRetry || attempt == _config.MaxRetries)
|
|
151
179
|
{
|
|
152
|
-
|
|
153
|
-
|
|
180
|
+
throw new SdkException(
|
|
181
|
+
0,
|
|
182
|
+
"REQUEST_ERROR",
|
|
183
|
+
"Network error: " + error.Message,
|
|
184
|
+
null,
|
|
185
|
+
error);
|
|
154
186
|
}
|
|
155
|
-
|
|
187
|
+
|
|
188
|
+
await Task.Delay(GetBackoff(attempt), cancellationToken);
|
|
156
189
|
}
|
|
157
|
-
catch (SdkException)
|
|
158
|
-
catch (TaskCanceledException) when (cancellationToken.IsCancellationRequested) { throw; }
|
|
159
|
-
catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested) { throw; }
|
|
160
|
-
catch (Exception ex) when (IsRetryable(ex) && attempt < maxRetries) { lastException = ex; }
|
|
161
|
-
catch (Exception ex)
|
|
190
|
+
catch (SdkException)
|
|
162
191
|
{
|
|
163
|
-
throw
|
|
192
|
+
throw;
|
|
193
|
+
}
|
|
194
|
+
catch (Exception error)
|
|
195
|
+
{
|
|
196
|
+
throw new SdkException(0, "REQUEST_ERROR", error.Message, null, error);
|
|
164
197
|
}
|
|
165
198
|
}
|
|
166
|
-
|
|
167
|
-
|
|
199
|
+
|
|
200
|
+
throw new SdkException(
|
|
201
|
+
0,
|
|
202
|
+
"MAX_RETRIES_EXCEEDED",
|
|
203
|
+
"Request failed after " + (_config.MaxRetries + 1) + " attempts",
|
|
204
|
+
null);
|
|
168
205
|
}
|
|
169
206
|
|
|
170
207
|
private HttpRequestMessage BuildRequest(Request request)
|
|
171
208
|
{
|
|
172
|
-
var baseUri = new Uri(_config.BaseUrl.TrimEnd('/') + "/");
|
|
173
|
-
var
|
|
209
|
+
var baseUri = new Uri(_config.BaseUrl.TrimEnd('/') + "/", UriKind.Absolute);
|
|
210
|
+
var builder = new UriBuilder(new Uri(baseUri, request.Path.TrimStart('/')));
|
|
211
|
+
var query = new List<string>();
|
|
174
212
|
|
|
175
|
-
if (
|
|
213
|
+
if (!string.IsNullOrEmpty(builder.Query))
|
|
214
|
+
query.Add(builder.Query.TrimStart('?'));
|
|
215
|
+
|
|
216
|
+
if (request.Query != null)
|
|
176
217
|
{
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
Uri.EscapeDataString(kv.Key) + "=" + Uri.EscapeDataString(kv.Value)));
|
|
180
|
-
var separator = fullUri.Query.Length > 0 ? "&" : "";
|
|
181
|
-
fullUri = new Uri(fullUri, fullUri.PathAndQuery + separator + queryString);
|
|
218
|
+
query.AddRange(request.Query.Select(pair =>
|
|
219
|
+
Uri.EscapeDataString(pair.Key) + "=" + Uri.EscapeDataString(pair.Value)));
|
|
182
220
|
}
|
|
183
221
|
|
|
184
|
-
|
|
222
|
+
{{#each securitySchemes}}
|
|
223
|
+
{{#if (eq type "apiKey")}}
|
|
224
|
+
{{#if (eq in "query")}}
|
|
225
|
+
if (!string.IsNullOrEmpty(_config.{{pascalCase configKey}}))
|
|
226
|
+
query.Add("{{paramName}}=" + Uri.EscapeDataString(_config.{{pascalCase configKey}}));
|
|
227
|
+
{{/if}}
|
|
228
|
+
{{/if}}
|
|
229
|
+
{{/each}}
|
|
230
|
+
|
|
231
|
+
builder.Query = string.Join("&", query.Where(value => !string.IsNullOrWhiteSpace(value)));
|
|
232
|
+
var httpRequest = new HttpRequestMessage(request.Method, builder.Uri);
|
|
233
|
+
|
|
234
|
+
httpRequest.Headers.TryAddWithoutValidation("Accept", "application/json");
|
|
235
|
+
httpRequest.Headers.TryAddWithoutValidation("User-Agent", "{{packageName}}/{{packageVersion}}");
|
|
185
236
|
|
|
186
237
|
if (_config.DefaultHeaders != null)
|
|
187
238
|
{
|
|
188
239
|
foreach (var (key, value) in _config.DefaultHeaders)
|
|
189
|
-
{
|
|
190
240
|
httpRequest.Headers.TryAddWithoutValidation(key, value);
|
|
191
|
-
}
|
|
192
241
|
}
|
|
193
242
|
|
|
194
243
|
{{#each securitySchemes}}
|
|
@@ -197,14 +246,6 @@ public class Transport
|
|
|
197
246
|
if (!string.IsNullOrEmpty(_config.{{pascalCase configKey}}))
|
|
198
247
|
httpRequest.Headers.TryAddWithoutValidation("{{paramName}}", _config.{{pascalCase configKey}});
|
|
199
248
|
{{/if}}
|
|
200
|
-
{{#if (eq in "query")}}
|
|
201
|
-
if (!string.IsNullOrEmpty(_config.{{pascalCase configKey}}))
|
|
202
|
-
{
|
|
203
|
-
var sep = fullUri.Query.Length > 0 ? "&" : "";
|
|
204
|
-
fullUri = new Uri(fullUri, fullUri.PathAndQuery + sep + "{{paramName}}=" + Uri.EscapeDataString(_config.{{pascalCase configKey}}));
|
|
205
|
-
httpRequest.RequestUri = fullUri;
|
|
206
|
-
}
|
|
207
|
-
{{/if}}
|
|
208
249
|
{{/if}}
|
|
209
250
|
{{#if (and (eq type "http") (eq scheme "bearer"))}}
|
|
210
251
|
if (!string.IsNullOrEmpty(_config.{{pascalCase configKey}}))
|
|
@@ -223,7 +264,7 @@ public class Transport
|
|
|
223
264
|
if (request.Body != null)
|
|
224
265
|
{
|
|
225
266
|
var contentType = request.ContentType ?? "application/json";
|
|
226
|
-
var json = JsonSerializer.Serialize(request.Body,
|
|
267
|
+
var json = JsonSerializer.Serialize(request.Body, JsonOptions);
|
|
227
268
|
httpRequest.Content = new StringContent(json, Encoding.UTF8, contentType);
|
|
228
269
|
}
|
|
229
270
|
|
|
@@ -232,28 +273,66 @@ public class Transport
|
|
|
232
273
|
foreach (var (key, value) in request.Headers)
|
|
233
274
|
{
|
|
234
275
|
if (key.Equals("Content-Type", StringComparison.OrdinalIgnoreCase) && httpRequest.Content != null)
|
|
235
|
-
{
|
|
236
276
|
httpRequest.Content.Headers.ContentType =
|
|
237
277
|
new System.Net.Http.Headers.MediaTypeHeaderValue(value);
|
|
238
|
-
}
|
|
239
278
|
else
|
|
240
|
-
{
|
|
241
279
|
httpRequest.Headers.TryAddWithoutValidation(key, value);
|
|
242
|
-
}
|
|
243
280
|
}
|
|
244
281
|
}
|
|
245
282
|
|
|
246
283
|
return httpRequest;
|
|
247
284
|
}
|
|
248
285
|
|
|
249
|
-
private
|
|
286
|
+
private bool CanRetry(Request request)
|
|
287
|
+
{
|
|
288
|
+
if (_config.RetryUnsafeRequests ||
|
|
289
|
+
request.Method == HttpMethod.Get ||
|
|
290
|
+
request.Method == HttpMethod.Head ||
|
|
291
|
+
request.Method == HttpMethod.Options ||
|
|
292
|
+
request.Method == HttpMethod.Put ||
|
|
293
|
+
request.Method == HttpMethod.Delete)
|
|
294
|
+
{
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
return _config.DefaultHeaders?.Any(pair =>
|
|
299
|
+
pair.Key.Equals("X-Idempotency-Key", StringComparison.OrdinalIgnoreCase) &&
|
|
300
|
+
!string.IsNullOrEmpty(pair.Value)) == true ||
|
|
301
|
+
request.Headers?.Any(pair =>
|
|
302
|
+
pair.Key.Equals("X-Idempotency-Key", StringComparison.OrdinalIgnoreCase) &&
|
|
303
|
+
!string.IsNullOrEmpty(pair.Value)) == true;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
private static TimeSpan GetRetryDelay(HttpResponseMessage response, int attempt)
|
|
250
307
|
{
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
308
|
+
var retryAfter = response.Headers.RetryAfter;
|
|
309
|
+
if (retryAfter?.Delta is { } delta)
|
|
310
|
+
return ClampDelay(delta);
|
|
311
|
+
if (retryAfter?.Date is { } date)
|
|
312
|
+
return ClampDelay(date - DateTimeOffset.UtcNow);
|
|
313
|
+
return GetBackoff(attempt);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
private static TimeSpan GetBackoff(int attempt)
|
|
317
|
+
{
|
|
318
|
+
var exponential = Math.Min(30_000, 200 * Math.Pow(2, Math.Min(attempt, 10)));
|
|
319
|
+
var jitter = Random.Shared.NextDouble() * exponential / 2;
|
|
320
|
+
return TimeSpan.FromMilliseconds(Math.Min(30_000, exponential + jitter));
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
private static TimeSpan ClampDelay(TimeSpan delay)
|
|
324
|
+
{
|
|
325
|
+
if (delay < TimeSpan.Zero) return TimeSpan.Zero;
|
|
326
|
+
return delay > TimeSpan.FromSeconds(30) ? TimeSpan.FromSeconds(30) : delay;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
private static bool IsNetworkError(Exception error) =>
|
|
330
|
+
error is HttpRequestException or IOException or TaskCanceledException or TimeoutException;
|
|
331
|
+
|
|
332
|
+
private static void EnsureSuccess(HttpResponseMessage response, string body)
|
|
333
|
+
{
|
|
334
|
+
if (!response.IsSuccessStatusCode)
|
|
335
|
+
throw ParseErrorResponse(response, body);
|
|
257
336
|
}
|
|
258
337
|
|
|
259
338
|
private static SdkException ParseErrorResponse(HttpResponseMessage response, string body)
|
|
@@ -261,31 +340,54 @@ public class Transport
|
|
|
261
340
|
var requestId = GetRequestId(response);
|
|
262
341
|
try
|
|
263
342
|
{
|
|
264
|
-
using var
|
|
265
|
-
var root =
|
|
266
|
-
var code = "";
|
|
267
|
-
var message = "";
|
|
268
|
-
|
|
269
|
-
if (root.TryGetProperty("
|
|
270
|
-
|
|
271
|
-
|
|
343
|
+
using var document = JsonDocument.Parse(body);
|
|
344
|
+
var root = document.RootElement;
|
|
345
|
+
var code = ReadString(root, "code");
|
|
346
|
+
var message = ReadString(root, "message");
|
|
347
|
+
|
|
348
|
+
if (root.TryGetProperty("error", out var error))
|
|
349
|
+
{
|
|
350
|
+
if (error.ValueKind == JsonValueKind.Object)
|
|
351
|
+
{
|
|
352
|
+
code ??= ReadString(error, "code");
|
|
353
|
+
message ??= ReadString(error, "message");
|
|
354
|
+
}
|
|
355
|
+
else if (error.ValueKind == JsonValueKind.String)
|
|
356
|
+
{
|
|
357
|
+
message ??= error.GetString();
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
message ??= string.IsNullOrWhiteSpace(body)
|
|
362
|
+
? "HTTP " + (int)response.StatusCode + ": " + response.ReasonPhrase
|
|
363
|
+
: body.Trim();
|
|
364
|
+
|
|
272
365
|
return new SdkException((int)response.StatusCode, code, message, requestId);
|
|
273
366
|
}
|
|
274
367
|
catch (JsonException)
|
|
275
368
|
{
|
|
276
|
-
return new SdkException(
|
|
369
|
+
return new SdkException(
|
|
370
|
+
(int)response.StatusCode,
|
|
371
|
+
null,
|
|
277
372
|
string.IsNullOrWhiteSpace(body)
|
|
278
373
|
? "HTTP " + (int)response.StatusCode + ": " + response.ReasonPhrase
|
|
279
|
-
: body.Trim(),
|
|
374
|
+
: body.Trim(),
|
|
375
|
+
requestId);
|
|
280
376
|
}
|
|
281
377
|
}
|
|
282
378
|
|
|
379
|
+
private static string? ReadString(JsonElement value, string property) =>
|
|
380
|
+
value.TryGetProperty(property, out var element) && element.ValueKind == JsonValueKind.String
|
|
381
|
+
? element.GetString()
|
|
382
|
+
: null;
|
|
383
|
+
|
|
283
384
|
private static string? GetRequestId(HttpResponseMessage response)
|
|
284
385
|
{
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
386
|
+
foreach (var name in new[] { "x-request-id", "request-id" })
|
|
387
|
+
{
|
|
388
|
+
if (response.Headers.TryGetValues(name, out var values))
|
|
389
|
+
return values.FirstOrDefault();
|
|
390
|
+
}
|
|
289
391
|
return null;
|
|
290
392
|
}
|
|
291
393
|
}
|
|
@@ -2,103 +2,57 @@ namespace {{namespace}};
|
|
|
2
2
|
|
|
3
3
|
using System;
|
|
4
4
|
using System.Collections.Generic;
|
|
5
|
+
using System.Runtime.Serialization;
|
|
5
6
|
using System.Text.Json;
|
|
6
7
|
using System.Text.Json.Serialization;
|
|
7
8
|
|
|
8
9
|
{{#each schemas}}
|
|
9
|
-
{{#if
|
|
10
|
-
{{
|
|
11
|
-
|
|
12
|
-
{
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
{{
|
|
17
|
-
/// </summary>
|
|
10
|
+
{{#if external}}
|
|
11
|
+
/// <summary>External schema placeholder for {{targetName}}.</summary>
|
|
12
|
+
public sealed class {{targetName}}
|
|
13
|
+
{
|
|
14
|
+
public JsonElement Value { get; set; }
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
{{else if isEnum}}
|
|
18
|
+
/// <summary>{{#if description}}{{description}}{{else}}{{targetName}} enumeration.{{/if}}</summary>
|
|
18
19
|
[JsonConverter(typeof(JsonStringEnumConverter))]
|
|
19
|
-
public enum {{
|
|
20
|
+
public enum {{targetName}}
|
|
20
21
|
{
|
|
21
22
|
{{#each enumValues}}
|
|
22
|
-
|
|
23
|
-
/// {{this}} value.
|
|
24
|
-
/// </summary>
|
|
25
|
-
[JsonStringEnumMemberName("{{this}}")]
|
|
26
|
-
{{pascalCase ../name}}{{pascalCase this}},
|
|
23
|
+
[EnumMember(Value = "{{value}}")] {{name}},
|
|
27
24
|
{{/each}}
|
|
28
25
|
}
|
|
29
26
|
|
|
30
27
|
{{else if isUnionType}}
|
|
31
|
-
/// <summary>
|
|
32
|
-
{{
|
|
33
|
-
/// {{description}}
|
|
34
|
-
{{else}}
|
|
35
|
-
/// {{name}} union type.
|
|
36
|
-
{{/if}}
|
|
37
|
-
/// </summary>
|
|
38
|
-
public class {{name}}
|
|
28
|
+
/// <summary>{{#if description}}{{description}}{{else}}{{targetName}} union value.{{/if}}</summary>
|
|
29
|
+
public sealed class {{targetName}}
|
|
39
30
|
{
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
{{#each unionMembers}}
|
|
44
|
-
/// <summary>
|
|
45
|
-
/// Creates a {{../name}} from a {{this}} value.
|
|
46
|
-
/// </summary>
|
|
47
|
-
public {{../name}}({{this}} value)
|
|
48
|
-
{
|
|
49
|
-
_value = value!;
|
|
50
|
-
_type = typeof({{this}});
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
{{/each}}
|
|
31
|
+
public {{targetName}}(object value) => Value = value;
|
|
32
|
+
public object Value { get; }
|
|
33
|
+
}
|
|
54
34
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
/// </summary>
|
|
58
|
-
public object Value => _value;
|
|
35
|
+
{{else if isTypeAlias}}
|
|
36
|
+
// {{name}} is emitted inline as {{{targetAliasType}}}.
|
|
59
37
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
{
|
|
65
|
-
if (_value is T v)
|
|
66
|
-
{
|
|
67
|
-
value = v;
|
|
68
|
-
return true;
|
|
69
|
-
}
|
|
70
|
-
value = default;
|
|
71
|
-
return false;
|
|
72
|
-
}
|
|
38
|
+
{{else if isIntersectionType}}
|
|
39
|
+
/// <summary>{{#if description}}{{description}}{{else}}{{targetName}} intersection value.{{/if}}</summary>
|
|
40
|
+
public sealed class {{targetName}}
|
|
41
|
+
{
|
|
42
|
+
public Dictionary<string, JsonElement> Values { get; set; } = new();
|
|
73
43
|
}
|
|
74
44
|
|
|
75
45
|
{{else}}
|
|
76
|
-
/// <summary>
|
|
77
|
-
{{#if
|
|
78
|
-
/// {{description}}
|
|
79
|
-
{{else}}
|
|
80
|
-
/// {{name}} schema type.
|
|
81
|
-
{{/if}}
|
|
82
|
-
/// </summary>
|
|
83
|
-
public class {{name}}
|
|
46
|
+
/// <summary>{{#if description}}{{description}}{{else}}{{targetName}} schema type.{{/if}}</summary>
|
|
47
|
+
public class {{targetName}}{{#if targetExtends}} : {{targetExtends}}{{/if}}
|
|
84
48
|
{
|
|
85
49
|
{{#each properties}}
|
|
86
|
-
|
|
87
|
-
{{#if
|
|
88
|
-
|
|
89
|
-
{{
|
|
90
|
-
/// </summary>
|
|
91
|
-
[JsonPropertyName("{{name}}")]
|
|
92
|
-
{{#if (or nullable (not required))}}
|
|
93
|
-
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
|
94
|
-
{{/if}}
|
|
95
|
-
{{#if deprecated}}
|
|
96
|
-
[Obsolete]
|
|
97
|
-
{{/if}}
|
|
98
|
-
public {{#if (eq type "string")}}{{#if (eq format "date-time")}}DateTime{{else}}string{{/if}}{{else if (eq type "integer")}}int{{else if (eq type "number")}}double{{else if (eq type "boolean")}}bool{{else if (eq type "array")}}List<object>?{{else}}{{type}}{{/if}}{{#if (or nullable (not required))}}?{{/if}} {{pascalCase name}} { get; set; }
|
|
50
|
+
[JsonPropertyName("{{name}}")]{{#if optional}}
|
|
51
|
+
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]{{/if}}{{#if deprecated}}
|
|
52
|
+
[Obsolete]{{/if}}
|
|
53
|
+
public {{targetType}} {{pascalCase name}} { get; set; }{{{initializer}}}
|
|
99
54
|
{{/each}}
|
|
100
55
|
}
|
|
101
|
-
{{/if}}
|
|
102
56
|
|
|
103
57
|
{{/if}}
|
|
104
58
|
{{/each}}
|