@apollo-deploy/tesseract 0.9.1 → 1.2.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/dist/adapters/csharp/index.d.ts +12 -0
- package/dist/adapters/csharp/index.d.ts.map +1 -0
- package/dist/adapters/csharp/index.js +99 -0
- package/dist/adapters/csharp/index.js.map +1 -0
- package/dist/adapters/go/index.d.ts +12 -0
- package/dist/adapters/go/index.d.ts.map +1 -0
- package/dist/adapters/go/index.js +135 -0
- package/dist/adapters/go/index.js.map +1 -0
- package/dist/adapters/kotlin/index.d.ts +13 -0
- package/dist/adapters/kotlin/index.d.ts.map +1 -0
- package/dist/adapters/kotlin/index.js +188 -0
- package/dist/adapters/kotlin/index.js.map +1 -0
- package/dist/adapters/php/index.d.ts +20 -0
- package/dist/adapters/php/index.d.ts.map +1 -0
- package/dist/adapters/php/index.js +123 -0
- package/dist/adapters/php/index.js.map +1 -0
- package/dist/adapters/python/index.d.ts +12 -0
- package/dist/adapters/python/index.d.ts.map +1 -0
- package/dist/adapters/python/index.js +145 -0
- package/dist/adapters/python/index.js.map +1 -0
- package/dist/adapters/ruby/index.d.ts +12 -0
- package/dist/adapters/ruby/index.d.ts.map +1 -0
- package/dist/adapters/ruby/index.js +86 -0
- package/dist/adapters/ruby/index.js.map +1 -0
- package/dist/adapters/rust/index.d.ts +13 -0
- package/dist/adapters/rust/index.d.ts.map +1 -0
- package/dist/adapters/rust/index.js +213 -0
- package/dist/adapters/rust/index.js.map +1 -0
- package/dist/adapters/typescript/emitter/emitSchema.d.ts +1 -1
- package/dist/adapters/typescript/emitter/emitSchema.d.ts.map +1 -1
- package/dist/adapters/typescript/emitter/emitSchema.js +1 -1
- 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.d.ts.map +1 -1
- package/dist/adapters/typescript/emitter/emitTypeScriptModels.js +5 -3
- package/dist/adapters/typescript/emitter/emitTypeScriptModels.js.map +1 -1
- package/dist/adapters/typescript/emitter/importCollector.d.ts +1 -1
- package/dist/adapters/typescript/emitter/importCollector.d.ts.map +1 -1
- package/dist/adapters/typescript/index.d.ts.map +1 -1
- package/dist/adapters/typescript/index.js +43 -2
- package/dist/adapters/typescript/index.js.map +1 -1
- package/dist/cli.js +2 -1
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -0
- package/dist/index.js.map +1 -1
- package/dist/pipeline/intake.d.ts.map +1 -1
- package/dist/pipeline/intake.js +32 -21
- package/dist/pipeline/intake.js.map +1 -1
- package/dist/types/ir.d.ts +1 -1
- package/dist/types/ir.d.ts.map +1 -1
- package/dist/types/sdk-module.d.ts +22 -2
- package/dist/types/sdk-module.d.ts.map +1 -1
- package/dist/types/sdk-module.js +1 -1
- package/package.json +1 -1
- package/templates/csharp/client.hbs +95 -0
- package/templates/csharp/csproj.hbs +18 -0
- package/templates/csharp/domain.hbs +183 -0
- package/templates/csharp/errors.hbs +45 -0
- package/templates/csharp/readme.hbs +76 -0
- package/templates/csharp/transport.hbs +291 -0
- package/templates/csharp/types.hbs +104 -0
- package/templates/go/client.hbs +158 -0
- package/templates/go/domain.hbs +89 -0
- package/templates/go/errors.hbs +38 -0
- package/templates/go/go-mod.hbs +6 -0
- package/templates/go/readme.hbs +73 -0
- package/templates/go/transport.hbs +379 -0
- package/templates/go/types.hbs +80 -0
- package/templates/go/utils.hbs +45 -0
- package/templates/kotlin/build-gradle.hbs +151 -0
- package/templates/kotlin/client.hbs +57 -0
- package/templates/kotlin/domain.hbs +73 -0
- package/templates/kotlin/errors.hbs +42 -0
- package/templates/kotlin/gradle-properties.hbs +2 -0
- package/templates/kotlin/readme.hbs +133 -0
- package/templates/kotlin/settings-gradle.hbs +1 -0
- package/templates/kotlin/transport.hbs +246 -0
- package/templates/kotlin/types.hbs +100 -0
- package/templates/php/client.hbs +74 -0
- package/templates/php/composer.hbs +19 -0
- package/templates/php/domain.hbs +173 -0
- package/templates/php/errors.hbs +131 -0
- package/templates/php/readme.hbs +89 -0
- package/templates/php/transport.hbs +285 -0
- package/templates/php/types.hbs +95 -0
- package/templates/python/client.hbs +102 -0
- package/templates/python/domain.hbs +139 -0
- package/templates/python/index.hbs +20 -0
- package/templates/python/pyproject.hbs +26 -0
- package/templates/python/readme.hbs +64 -0
- package/templates/python/resources-init.hbs +11 -0
- package/templates/python/transport.hbs +191 -0
- package/templates/python/types-errors.hbs +75 -0
- package/templates/python/types.hbs +79 -0
- package/templates/ruby/Gemfile.hbs +5 -0
- package/templates/ruby/client.hbs +86 -0
- package/templates/ruby/domain.hbs +110 -0
- package/templates/ruby/errors.hbs +150 -0
- package/templates/ruby/gemspec.hbs +30 -0
- package/templates/ruby/index.hbs +54 -0
- package/templates/ruby/readme.hbs +113 -0
- package/templates/ruby/transport.hbs +177 -0
- package/templates/ruby/types.hbs +126 -0
- package/templates/ruby/version.hbs +5 -0
- package/templates/rust/api-mod.hbs +3 -0
- package/templates/rust/cargo.hbs +22 -0
- package/templates/rust/client.hbs +114 -0
- package/templates/rust/domain.hbs +94 -0
- package/templates/rust/error.hbs +74 -0
- package/templates/rust/lib.hbs +85 -0
- package/templates/rust/readme.hbs +77 -0
- package/templates/rust/transport.hbs +287 -0
- package/templates/rust/types.hbs +142 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
package {{packageName}}
|
|
2
|
+
|
|
3
|
+
import io.ktor.client.*
|
|
4
|
+
import io.ktor.client.engine.cio.*
|
|
5
|
+
import io.ktor.client.plugins.*
|
|
6
|
+
import io.ktor.client.plugins.contentnegotiation.*
|
|
7
|
+
import io.ktor.client.request.*
|
|
8
|
+
import io.ktor.client.statement.*
|
|
9
|
+
import io.ktor.http.*
|
|
10
|
+
import io.ktor.serialization.kotlinx.json.*
|
|
11
|
+
import kotlinx.serialization.json.Json
|
|
12
|
+
import kotlinx.serialization.json.JsonObject
|
|
13
|
+
import kotlinx.serialization.json.jsonPrimitive
|
|
14
|
+
import kotlinx.coroutines.*
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Internal transport configuration.
|
|
18
|
+
*/
|
|
19
|
+
data class TransportConfig(
|
|
20
|
+
val baseUrl: String,
|
|
21
|
+
{{#each securitySchemes}}
|
|
22
|
+
val {{configKey}}: String? = null,
|
|
23
|
+
{{/each}}
|
|
24
|
+
val timeoutMs: Long = 15000,
|
|
25
|
+
val maxRetries: Int = 3,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* HTTP transport layer for the SDK.
|
|
30
|
+
*
|
|
31
|
+
* Handles request building, authentication, retries, and error mapping
|
|
32
|
+
* using Ktor's HttpClient.
|
|
33
|
+
*/
|
|
34
|
+
class Transport(
|
|
35
|
+
private val config: TransportConfig
|
|
36
|
+
) {
|
|
37
|
+
private val json = Json {
|
|
38
|
+
ignoreUnknownKeys = true
|
|
39
|
+
isLenient = true
|
|
40
|
+
encodeDefaults = true
|
|
41
|
+
coerceInputValues = true
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
private val client = HttpClient(CIO) {
|
|
45
|
+
install(ContentNegotiation) {
|
|
46
|
+
json(json)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
install(HttpTimeout) {
|
|
50
|
+
requestTimeoutMillis = config.timeoutMs
|
|
51
|
+
connectTimeoutMillis = config.timeoutMs
|
|
52
|
+
socketTimeoutMillis = config.timeoutMs
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
defaultRequest {
|
|
56
|
+
url(config.baseUrl)
|
|
57
|
+
contentType(ContentType.Application.Json)
|
|
58
|
+
accept(ContentType.Application.Json)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Execute an HTTP request, deserializing the response body as JSON.
|
|
64
|
+
*/
|
|
65
|
+
suspend inline fun <reified T> execute(
|
|
66
|
+
method: HttpMethod,
|
|
67
|
+
path: String,
|
|
68
|
+
query: Map<String, String?>? = null,
|
|
69
|
+
body: Any? = null,
|
|
70
|
+
headers: Map<String, String>? = null,
|
|
71
|
+
): T {
|
|
72
|
+
val response = executeRaw(method, path, query, body, headers)
|
|
73
|
+
return json.decodeFromString<T>(response)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Execute an HTTP request, returning the raw response body as a string.
|
|
78
|
+
*/
|
|
79
|
+
suspend fun executeRaw(
|
|
80
|
+
method: HttpMethod,
|
|
81
|
+
path: String,
|
|
82
|
+
query: Map<String, String?>? = null,
|
|
83
|
+
body: Any? = null,
|
|
84
|
+
headers: Map<String, String>? = null,
|
|
85
|
+
): String {
|
|
86
|
+
return executeWithRetry(method, path, query, body, headers)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Close the underlying HTTP client. Call this when the SDK is no longer needed.
|
|
91
|
+
*/
|
|
92
|
+
fun close() {
|
|
93
|
+
client.close()
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ── private helpers ─────────────────────────────────────────────────────
|
|
97
|
+
|
|
98
|
+
@OptIn(ExperimentalCoroutinesApi::class)
|
|
99
|
+
private suspend fun executeWithRetry(
|
|
100
|
+
method: HttpMethod,
|
|
101
|
+
path: String,
|
|
102
|
+
query: Map<String, String?>? = null,
|
|
103
|
+
body: Any? = null,
|
|
104
|
+
headers: Map<String, String>? = null,
|
|
105
|
+
attempt: Int = 0,
|
|
106
|
+
): String {
|
|
107
|
+
return try {
|
|
108
|
+
val response = client.request {
|
|
109
|
+
this.method = method
|
|
110
|
+
url {
|
|
111
|
+
// Base URL is set as default; append the path
|
|
112
|
+
val fullUrl = "${config.baseUrl.trimEnd('/')}${path}"
|
|
113
|
+
takeFrom(fullUrl)
|
|
114
|
+
|
|
115
|
+
// Add query parameters
|
|
116
|
+
query?.forEach { (key, value) ->
|
|
117
|
+
if (value != null) {
|
|
118
|
+
parameters.append(key, value)
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Set request body
|
|
124
|
+
if (body != null) {
|
|
125
|
+
setBody(body)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Set custom headers
|
|
129
|
+
headers?.forEach { (key, value) ->
|
|
130
|
+
this.headers.append(key, value)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Apply authentication
|
|
134
|
+
applyAuth()
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
val status = response.status.value
|
|
138
|
+
|
|
139
|
+
if (status in 200..299) {
|
|
140
|
+
response.bodyAsText()
|
|
141
|
+
} else if ((status >= 500 || status == 429) && attempt < config.maxRetries) {
|
|
142
|
+
// Retry on server errors and rate limiting
|
|
143
|
+
val backoffMs = 200L * (1L shl attempt)
|
|
144
|
+
delay(backoffMs)
|
|
145
|
+
executeWithRetry(method, path, query, body, headers, attempt + 1)
|
|
146
|
+
} else {
|
|
147
|
+
throw buildSdkException(response)
|
|
148
|
+
}
|
|
149
|
+
} catch (e: SdkException) {
|
|
150
|
+
throw e
|
|
151
|
+
} catch (e: CancellationException) {
|
|
152
|
+
throw e
|
|
153
|
+
} catch (e: Exception) {
|
|
154
|
+
if (attempt < config.maxRetries) {
|
|
155
|
+
val backoffMs = 200L * (1L shl attempt)
|
|
156
|
+
delay(backoffMs)
|
|
157
|
+
executeWithRetry(method, path, query, body, headers, attempt + 1)
|
|
158
|
+
} else {
|
|
159
|
+
throw SdkException(
|
|
160
|
+
status = null,
|
|
161
|
+
code = null,
|
|
162
|
+
message = "Network error: ${e.message}",
|
|
163
|
+
requestId = null,
|
|
164
|
+
cause = e,
|
|
165
|
+
)
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
private suspend fun buildSdkException(response: HttpResponse): SdkException {
|
|
171
|
+
val status = response.status.value
|
|
172
|
+
val body = try {
|
|
173
|
+
response.bodyAsText()
|
|
174
|
+
} catch (e: Exception) {
|
|
175
|
+
null
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
val requestId = response.headers["x-request-id"]
|
|
179
|
+
?: response.headers["request-id"]
|
|
180
|
+
|
|
181
|
+
// Try to extract structured error info from the JSON body
|
|
182
|
+
var code: String? = null
|
|
183
|
+
var message: String = body ?: "API error (status $status)"
|
|
184
|
+
|
|
185
|
+
if (body != null) {
|
|
186
|
+
try {
|
|
187
|
+
val jsonBody = json.decodeFromString<JsonObject>(body)
|
|
188
|
+
code = jsonBody["code"]?.jsonPrimitive?.content
|
|
189
|
+
?: jsonBody["error"]?.let { err ->
|
|
190
|
+
when (err) {
|
|
191
|
+
is JsonObject -> err["code"]?.jsonPrimitive?.content
|
|
192
|
+
else -> err.jsonPrimitive?.content
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
message = jsonBody["message"]?.jsonPrimitive?.content
|
|
196
|
+
?: jsonBody["error"]?.let { err ->
|
|
197
|
+
when (err) {
|
|
198
|
+
is JsonObject -> err["message"]?.jsonPrimitive?.content
|
|
199
|
+
else -> err.jsonPrimitive?.content
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
?: message
|
|
203
|
+
} catch (_: Exception) {
|
|
204
|
+
// Use raw body as the message
|
|
205
|
+
message = body
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return SdkException(
|
|
210
|
+
status = status,
|
|
211
|
+
code = code,
|
|
212
|
+
message = message,
|
|
213
|
+
requestId = requestId,
|
|
214
|
+
)
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
private fun HttpRequestBuilder.applyAuth() {
|
|
218
|
+
{{#each securitySchemes}}
|
|
219
|
+
{{#if (eq type "apiKey")}}
|
|
220
|
+
{{#if (eq in "header")}}
|
|
221
|
+
config.{{configKey}}?.let { key ->
|
|
222
|
+
headers.append("{{paramName}}", key)
|
|
223
|
+
}
|
|
224
|
+
{{/if}}
|
|
225
|
+
{{#if (eq in "query")}}
|
|
226
|
+
// API key via query parameter handled during URL construction
|
|
227
|
+
{{/if}}
|
|
228
|
+
{{/if}}
|
|
229
|
+
{{#if (and (eq type "http") (eq scheme "bearer"))}}
|
|
230
|
+
config.{{configKey}}?.let { token ->
|
|
231
|
+
headers.append("Authorization", "Bearer $token")
|
|
232
|
+
}
|
|
233
|
+
{{/if}}
|
|
234
|
+
{{#if (eq type "oauth2")}}
|
|
235
|
+
config.{{configKey}}?.let { token ->
|
|
236
|
+
headers.append("Authorization", "Bearer $token")
|
|
237
|
+
}
|
|
238
|
+
{{/if}}
|
|
239
|
+
{{#if (eq type "openIdConnect")}}
|
|
240
|
+
config.{{configKey}}?.let { token ->
|
|
241
|
+
headers.append("Authorization", "Bearer $token")
|
|
242
|
+
}
|
|
243
|
+
{{/if}}
|
|
244
|
+
{{/each}}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
package {{packageName}}
|
|
2
|
+
|
|
3
|
+
import kotlinx.serialization.*
|
|
4
|
+
import kotlinx.serialization.descriptors.*
|
|
5
|
+
import kotlinx.serialization.encoding.*
|
|
6
|
+
import kotlinx.serialization.json.*
|
|
7
|
+
|
|
8
|
+
{{#each schemas}}
|
|
9
|
+
{{#if (not ownership)}}
|
|
10
|
+
{{#unless (or externalImport (eq ownership.kind "external"))}}
|
|
11
|
+
{{#if (not isEnum)}}
|
|
12
|
+
{{#if (not isUnionType)}}
|
|
13
|
+
{{#if (not isIntersectionType)}}
|
|
14
|
+
{{#if (not isTypeAlias)}}
|
|
15
|
+
{{#if description}}
|
|
16
|
+
/**
|
|
17
|
+
* {{description}}
|
|
18
|
+
*/
|
|
19
|
+
{{/if}}
|
|
20
|
+
@Serializable
|
|
21
|
+
data class {{name}}(
|
|
22
|
+
{{#each properties}}
|
|
23
|
+
{{#if description}}
|
|
24
|
+
/** {{description}} */
|
|
25
|
+
{{/if}}
|
|
26
|
+
{{#if deprecated}}
|
|
27
|
+
@Deprecated("{{#if deprecationMessage}}{{deprecationMessage}}{{else}}Use a different property.{{/if}}")
|
|
28
|
+
{{/if}}
|
|
29
|
+
{{#if (and nullable (not required))}}
|
|
30
|
+
@SerialName("{{name}}")
|
|
31
|
+
val {{name}}: {{{kotlinType type}}}? = null,
|
|
32
|
+
{{else if nullable}}
|
|
33
|
+
@SerialName("{{name}}")
|
|
34
|
+
val {{name}}: {{{kotlinType type}}}? = null,
|
|
35
|
+
{{else if required}}
|
|
36
|
+
@SerialName("{{name}}")
|
|
37
|
+
val {{name}}: {{{kotlinType type}}},
|
|
38
|
+
{{else}}
|
|
39
|
+
@SerialName("{{name}}")
|
|
40
|
+
val {{name}}: {{{kotlinType type}}}? = null,
|
|
41
|
+
{{/if}}
|
|
42
|
+
{{/each}}
|
|
43
|
+
)
|
|
44
|
+
{{/if}}
|
|
45
|
+
{{/if}}
|
|
46
|
+
{{/if}}
|
|
47
|
+
{{/if}}
|
|
48
|
+
{{/unless}}
|
|
49
|
+
{{/if}}
|
|
50
|
+
|
|
51
|
+
{{#if isEnum}}
|
|
52
|
+
{{#if description}}
|
|
53
|
+
/**
|
|
54
|
+
* {{description}}
|
|
55
|
+
*/
|
|
56
|
+
{{/if}}
|
|
57
|
+
@Serializable
|
|
58
|
+
enum class {{name}} {
|
|
59
|
+
{{#each enumValues}}
|
|
60
|
+
@SerialName("{{this}}")
|
|
61
|
+
{{pascalCase this}},
|
|
62
|
+
{{/each}}
|
|
63
|
+
}
|
|
64
|
+
{{/if}}
|
|
65
|
+
|
|
66
|
+
{{#if isUnionType}}
|
|
67
|
+
{{#if unionMembers.length}}
|
|
68
|
+
{{#if description}}
|
|
69
|
+
/**
|
|
70
|
+
* {{description}}
|
|
71
|
+
*/
|
|
72
|
+
{{/if}}
|
|
73
|
+
@Serializable
|
|
74
|
+
sealed class {{name}} {
|
|
75
|
+
{{#each unionMembers}}
|
|
76
|
+
@Serializable
|
|
77
|
+
data class {{pascalCase this}}(val value: {{{kotlinType this}}}) : {{../name}}()
|
|
78
|
+
{{/each}}
|
|
79
|
+
}
|
|
80
|
+
{{/if}}
|
|
81
|
+
{{/if}}
|
|
82
|
+
|
|
83
|
+
{{#if isIntersectionType}}
|
|
84
|
+
{{#if intersectionMembers.length}}
|
|
85
|
+
{{#if description}}
|
|
86
|
+
/**
|
|
87
|
+
* {{description}}
|
|
88
|
+
*/
|
|
89
|
+
{{/if}}
|
|
90
|
+
{{! Intersection type — emit as interface implemented by concrete types }}
|
|
91
|
+
@Serializable
|
|
92
|
+
data class {{name}}(
|
|
93
|
+
// Intersection of: {{join intersectionMembers ", "}}
|
|
94
|
+
// Fields from all intersected types should be inlined here.
|
|
95
|
+
// TODO: generate combined fields
|
|
96
|
+
)
|
|
97
|
+
{{/if}}
|
|
98
|
+
{{/if}}
|
|
99
|
+
|
|
100
|
+
{{/each}}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @generated
|
|
5
|
+
* This file was automatically generated by Tesseract.
|
|
6
|
+
* DO NOT MODIFY THIS FILE DIRECTLY.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
declare(strict_types=1);
|
|
10
|
+
|
|
11
|
+
namespace {{namespace}};
|
|
12
|
+
|
|
13
|
+
{{#each groups}}
|
|
14
|
+
use {{namespace}}\Api\{{className}};
|
|
15
|
+
{{/each}}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* {{title}} SDK Client.
|
|
19
|
+
*
|
|
20
|
+
* Entry point for all API operations. Provides accessor methods
|
|
21
|
+
* for each domain group and manages shared transport configuration.
|
|
22
|
+
*
|
|
23
|
+
* Usage:
|
|
24
|
+
* <code>
|
|
25
|
+
* $client = new Client(['baseUrl' => '{{baseUrl}}']);
|
|
26
|
+
* $result = $client->{{groups.[0].name}}()->{{groups.[0].operations.[0].name}}(...);
|
|
27
|
+
* </code>
|
|
28
|
+
*/
|
|
29
|
+
class Client
|
|
30
|
+
{
|
|
31
|
+
private Transport $transport;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @param array{
|
|
35
|
+
* baseUrl?: string,
|
|
36
|
+
* timeoutMs?: int,
|
|
37
|
+
* retries?: array{attempts?: int, backoffMs?: int, jitter?: bool, maxBackoffMs?: int},
|
|
38
|
+
* defaultHeaders?: array<string, string>,
|
|
39
|
+
{{#each securitySchemes}}
|
|
40
|
+
* {{configKey}}?: string,
|
|
41
|
+
{{/each}}
|
|
42
|
+
* } $config
|
|
43
|
+
*/
|
|
44
|
+
public function __construct(array $config = [])
|
|
45
|
+
{
|
|
46
|
+
$this->transport = new Transport($config);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Get the underlying transport instance for advanced use.
|
|
51
|
+
*/
|
|
52
|
+
public function getTransport(): Transport
|
|
53
|
+
{
|
|
54
|
+
return $this->transport;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
{{#each groups}}
|
|
58
|
+
/** @var {{className}}|null */
|
|
59
|
+
private ?{{className}} $_{{{name}}} = null;
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
{{#if description}}
|
|
63
|
+
* {{description}}
|
|
64
|
+
*
|
|
65
|
+
{{/if}}
|
|
66
|
+
* @return {{className}}
|
|
67
|
+
*/
|
|
68
|
+
public function {{name}}(): {{className}}
|
|
69
|
+
{
|
|
70
|
+
return $this->_{{{name}}} ??= new {{className}}($this->transport);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
{{/each}}
|
|
74
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{composerName}}",
|
|
3
|
+
"description": "{{#if description}}{{description}}{{else}}PHP SDK for {{title}}{{/if}}",
|
|
4
|
+
"type": "library",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"autoload": {
|
|
7
|
+
"psr-4": {
|
|
8
|
+
"{{namespace}}\\": "src/"
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
"require": {
|
|
12
|
+
"php": ">=8.1",
|
|
13
|
+
"guzzlehttp/guzzle": "^7.0",
|
|
14
|
+
"ext-json": "*"
|
|
15
|
+
},
|
|
16
|
+
"require-dev": {
|
|
17
|
+
"phpunit/phpunit": "^10.0"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @generated
|
|
5
|
+
* This file was automatically generated by Tesseract.
|
|
6
|
+
* DO NOT MODIFY THIS FILE DIRECTLY.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
declare(strict_types=1);
|
|
10
|
+
|
|
11
|
+
namespace {{namespace}}\Api;
|
|
12
|
+
|
|
13
|
+
use Psr\Http\Message\ResponseInterface;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* {{interfaceName}} domain operations.
|
|
17
|
+
*
|
|
18
|
+
{{#if description}}
|
|
19
|
+
* {{description}}
|
|
20
|
+
*
|
|
21
|
+
{{/if}}
|
|
22
|
+
* @generated
|
|
23
|
+
*/
|
|
24
|
+
class {{className}}
|
|
25
|
+
{
|
|
26
|
+
private Transport $transport;
|
|
27
|
+
|
|
28
|
+
public function __construct(Transport $transport)
|
|
29
|
+
{
|
|
30
|
+
$this->transport = $transport;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
{{#each operations}}
|
|
34
|
+
{{#if (isEventStream this)}}
|
|
35
|
+
// SSE operations are not yet supported in the PHP adapter — skipping {{name}}
|
|
36
|
+
|
|
37
|
+
{{else}}
|
|
38
|
+
/**
|
|
39
|
+
{{#if deprecated}}
|
|
40
|
+
* @deprecated{{#if deprecationMessage}} {{deprecationMessage}}{{/if}}
|
|
41
|
+
*
|
|
42
|
+
{{/if}}
|
|
43
|
+
{{#if (or summary description)}}
|
|
44
|
+
* {{#if summary}}{{summary}}{{#if description}} — {{description}}{{/if}}{{else}}{{description}}{{/if}}
|
|
45
|
+
*
|
|
46
|
+
{{/if}}
|
|
47
|
+
{{#each pathParams}}
|
|
48
|
+
* @param {{type}} ${{name}}{{#if description}} {{description}}{{/if}}
|
|
49
|
+
{{/each}}
|
|
50
|
+
{{#if requestBody}}
|
|
51
|
+
* @param array<string, mixed>{{#unless requestBody.required}}|null{{/unless}} $input Request body
|
|
52
|
+
{{/if}}
|
|
53
|
+
{{#each queryParams}}
|
|
54
|
+
* @param {{type}}|null ${{name}}{{#if description}} {{description}}{{/if}}
|
|
55
|
+
{{/each}}
|
|
56
|
+
{{#each headerParams}}
|
|
57
|
+
* @param {{type}}|null ${{name}}{{#if description}} {{description}}{{/if}}
|
|
58
|
+
{{/each}}
|
|
59
|
+
* @return {{responseType}}
|
|
60
|
+
* @throws SDKError
|
|
61
|
+
*/
|
|
62
|
+
public function {{name}}(
|
|
63
|
+
{{#each pathParams}}
|
|
64
|
+
{{type}} ${{name}},
|
|
65
|
+
{{/each}}
|
|
66
|
+
{{#if requestBody}}
|
|
67
|
+
{{#unless requestBody.required}}?{{/unless}}array $input{{#unless requestBody.required}} = null{{/unless}},
|
|
68
|
+
{{/if}}
|
|
69
|
+
{{#each queryParams}}
|
|
70
|
+
?{{type}} ${{name}} = null,
|
|
71
|
+
{{/each}}
|
|
72
|
+
{{#each headerParams}}
|
|
73
|
+
?{{type}} ${{name}} = null,
|
|
74
|
+
{{/each}}
|
|
75
|
+
): {{responseType}}
|
|
76
|
+
{
|
|
77
|
+
// Build path with parameter substitution
|
|
78
|
+
$path = '{{path}}';
|
|
79
|
+
{{#if (hasPathParams this)}}
|
|
80
|
+
$path = str_replace(
|
|
81
|
+
[{{#each pathParams}}'{{@root.LBRACE}}{{originalName}}{{@root.RBRACE}}'{{#unless @last}}, {{/unless}}{{/each}}],
|
|
82
|
+
[{{#each pathParams}}${{name}}{{#unless @last}}, {{/unless}}{{/each}}],
|
|
83
|
+
$path
|
|
84
|
+
);
|
|
85
|
+
{{/if}}
|
|
86
|
+
|
|
87
|
+
// Build query parameters
|
|
88
|
+
$query = [];
|
|
89
|
+
{{#if (hasQueryParams this)}}
|
|
90
|
+
{{#each queryParams}}
|
|
91
|
+
if (${{name}} !== null) {
|
|
92
|
+
$query['{{originalName}}'] = ${{name}};
|
|
93
|
+
}
|
|
94
|
+
{{/each}}
|
|
95
|
+
{{/if}}
|
|
96
|
+
|
|
97
|
+
// Build extra headers
|
|
98
|
+
$headers = [];
|
|
99
|
+
{{#each headerParams}}
|
|
100
|
+
if (${{name}} !== null) {
|
|
101
|
+
$headers['{{originalName}}'] = (string) ${{name}};
|
|
102
|
+
}
|
|
103
|
+
{{/each}}
|
|
104
|
+
|
|
105
|
+
// Execute request
|
|
106
|
+
$requestDef = [
|
|
107
|
+
'method' => '{{httpMethod}}',
|
|
108
|
+
'path' => $path,
|
|
109
|
+
'query' => $query,
|
|
110
|
+
'headers' => $headers,
|
|
111
|
+
];
|
|
112
|
+
|
|
113
|
+
{{#if (hasRequestBody this)}}
|
|
114
|
+
if ($input !== null) {
|
|
115
|
+
$requestDef['body'] = $input;
|
|
116
|
+
}
|
|
117
|
+
{{/if}}
|
|
118
|
+
|
|
119
|
+
$response = $this->transport->executeRequest($requestDef);
|
|
120
|
+
|
|
121
|
+
// Deserialize response
|
|
122
|
+
$data = $this->decodeResponse($response);
|
|
123
|
+
|
|
124
|
+
{{#if (eq responseType "void")}}
|
|
125
|
+
return;
|
|
126
|
+
{{else if (eq responseType "string")}}
|
|
127
|
+
return (string) $data;
|
|
128
|
+
{{else if (eq responseType "int")}}
|
|
129
|
+
return (int) $data;
|
|
130
|
+
{{else if (eq responseType "float")}}
|
|
131
|
+
return (float) $data;
|
|
132
|
+
{{else if (eq responseType "bool")}}
|
|
133
|
+
return (bool) $data;
|
|
134
|
+
{{else if (eq responseType "array")}}
|
|
135
|
+
return $data;
|
|
136
|
+
{{else}}
|
|
137
|
+
// Complex type: deserialize via fromArray
|
|
138
|
+
if (class_exists({{responseType}}::class) && method_exists({{responseType}}::class, 'fromArray')) {
|
|
139
|
+
return {{responseType}}::fromArray($data);
|
|
140
|
+
}
|
|
141
|
+
return $data;
|
|
142
|
+
{{/if}}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
{{/if}}
|
|
146
|
+
{{/each}}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Decode a PSR-7 response body to an associative array or raw value.
|
|
150
|
+
*
|
|
151
|
+
* @return array<string, mixed>|string
|
|
152
|
+
*/
|
|
153
|
+
private function decodeResponse(ResponseInterface $response): array|string
|
|
154
|
+
{
|
|
155
|
+
$contents = $response->getBody()->__toString();
|
|
156
|
+
|
|
157
|
+
try {
|
|
158
|
+
$decoded = json_decode($contents, true, 512, JSON_THROW_ON_ERROR);
|
|
159
|
+
if (is_array($decoded)) {
|
|
160
|
+
return $decoded;
|
|
161
|
+
}
|
|
162
|
+
if (is_scalar($decoded)) {
|
|
163
|
+
return (string) $decoded;
|
|
164
|
+
}
|
|
165
|
+
return ['raw' => $decoded];
|
|
166
|
+
} catch (\JsonException) {
|
|
167
|
+
if (trim($contents) === '') {
|
|
168
|
+
return [];
|
|
169
|
+
}
|
|
170
|
+
return $contents;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|