@apollo-deploy/tesseract 0.9.1 → 1.0.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 +186 -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 +2 -2
- 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/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.js +23 -16
- 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 +41 -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 +105 -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,95 @@
|
|
|
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 schemas}}
|
|
14
|
+
{{#if isEnum}}
|
|
15
|
+
/**
|
|
16
|
+
{{#if description}}
|
|
17
|
+
* {{description}}
|
|
18
|
+
*
|
|
19
|
+
{{/if}}
|
|
20
|
+
* @generated
|
|
21
|
+
*/
|
|
22
|
+
enum {{name}}: {{#each properties}}{{#if @first}}{{type}}{{/if}}{{/each}}
|
|
23
|
+
{
|
|
24
|
+
{{#each enumValues}}
|
|
25
|
+
case {{this}} = '{{this}}';
|
|
26
|
+
{{/each}}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
{{else if isTypeAlias}}
|
|
30
|
+
{{!-- PHP type aliases are not direct; they are used as docblock hints --}}
|
|
31
|
+
{{!-- Skipping type alias: {{name}} --}}
|
|
32
|
+
|
|
33
|
+
{{else if isUnionType}}
|
|
34
|
+
{{!-- PHP union types are handled via properties, not standalone declarations --}}
|
|
35
|
+
{{!-- Skipping union type: {{name}} --}}
|
|
36
|
+
|
|
37
|
+
{{else}}
|
|
38
|
+
/**
|
|
39
|
+
{{#if description}}
|
|
40
|
+
* {{description}}
|
|
41
|
+
*
|
|
42
|
+
{{/if}}
|
|
43
|
+
* @generated
|
|
44
|
+
*/
|
|
45
|
+
class {{name}}{{#if extends}} extends {{extends}}{{/if}}
|
|
46
|
+
{
|
|
47
|
+
{{#each properties}}
|
|
48
|
+
/**
|
|
49
|
+
* @var {{tsType type nullable}}
|
|
50
|
+
*/
|
|
51
|
+
public {{#if nullable}}?{{/if}}{{type}} ${{name}}{{#if defaultValue}} = {{{json defaultValue}}}{{/if}};
|
|
52
|
+
|
|
53
|
+
{{/each}}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @param array<string, mixed> $data
|
|
57
|
+
*/
|
|
58
|
+
public static function fromArray(array $data): self
|
|
59
|
+
{
|
|
60
|
+
$instance = new self();
|
|
61
|
+
|
|
62
|
+
{{#each properties}}
|
|
63
|
+
{{#if (eq type "string")}}
|
|
64
|
+
$instance->{{name}} = isset($data['{{name}}']) ? (string) $data['{{name}}'] : {{#if required}}throw new \InvalidArgumentException('Missing required field: {{name}}'){{else}}null{{/if}};
|
|
65
|
+
{{else if (eq type "int")}}
|
|
66
|
+
$instance->{{name}} = isset($data['{{name}}']) ? (int) $data['{{name}}'] : {{#if required}}throw new \InvalidArgumentException('Missing required field: {{name}}'){{else}}null{{/if}};
|
|
67
|
+
{{else if (eq type "float")}}
|
|
68
|
+
$instance->{{name}} = isset($data['{{name}}']) ? (float) $data['{{name}}'] : {{#if required}}throw new \InvalidArgumentException('Missing required field: {{name}}'){{else}}null{{/if}};
|
|
69
|
+
{{else if (eq type "bool")}}
|
|
70
|
+
$instance->{{name}} = isset($data['{{name}}']) ? (bool) $data['{{name}}'] : {{#if required}}throw new \InvalidArgumentException('Missing required field: {{name}}'){{else}}null{{/if}};
|
|
71
|
+
{{else if (eq type "array")}}
|
|
72
|
+
$instance->{{name}} = isset($data['{{name}}']) ? (array) $data['{{name}}'] : {{#if required}}[]{{else}}null{{/if}};
|
|
73
|
+
{{else}}
|
|
74
|
+
$instance->{{name}} = $data['{{name}}'] ?? {{#if required}}throw new \InvalidArgumentException('Missing required field: {{name}}'){{else}}null{{/if}};
|
|
75
|
+
{{/if}}
|
|
76
|
+
|
|
77
|
+
{{/each}}
|
|
78
|
+
return $instance;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* @return array<string, mixed>
|
|
83
|
+
*/
|
|
84
|
+
public function toArray(): array
|
|
85
|
+
{
|
|
86
|
+
return [
|
|
87
|
+
{{#each properties}}
|
|
88
|
+
'{{name}}' => $this->{{name}},
|
|
89
|
+
{{/each}}
|
|
90
|
+
];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
{{/if}}
|
|
95
|
+
{{/each}}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""{{title}} SDK Client"""
|
|
2
|
+
|
|
3
|
+
from typing import Optional, Dict, Any, List, Callable, Union
|
|
4
|
+
import httpx
|
|
5
|
+
from .transport import create_transport, SDKTransport, RequestOptions
|
|
6
|
+
{{#each groups}}
|
|
7
|
+
from .resources.{{fileName}} import {{interfaceName}}
|
|
8
|
+
{{/each}}
|
|
9
|
+
|
|
10
|
+
{{#if hasSSE}}
|
|
11
|
+
# SSE support requires the sseclient-py package:
|
|
12
|
+
# pip install sseclient-py>=1.8.0
|
|
13
|
+
{{/if}}
|
|
14
|
+
|
|
15
|
+
class {{clientName}}ClientConfig:
|
|
16
|
+
"""Configuration for {{title}} client."""
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
base_url: str = "{{baseUrl}}",
|
|
21
|
+
{{#each securitySchemes}}
|
|
22
|
+
{{#if (eq type "apiKey")}}
|
|
23
|
+
{{configKey}}: Optional[str] = None,
|
|
24
|
+
{{/if}}
|
|
25
|
+
{{#if (and (eq type "http") (eq scheme "bearer"))}}
|
|
26
|
+
{{configKey}}: Optional[str] = None,
|
|
27
|
+
{{/if}}
|
|
28
|
+
{{#if (eq type "oauth2")}}
|
|
29
|
+
{{configKey}}: Optional[str] = None,
|
|
30
|
+
{{/if}}
|
|
31
|
+
{{#if (eq type "openIdConnect")}}
|
|
32
|
+
{{configKey}}: Optional[str] = None,
|
|
33
|
+
{{/if}}
|
|
34
|
+
{{/each}}
|
|
35
|
+
timeout_ms: int = 15000,
|
|
36
|
+
retries: int = 3,
|
|
37
|
+
default_headers: Optional[Dict[str, str]] = None,
|
|
38
|
+
on_request: Optional[Callable] = None,
|
|
39
|
+
on_response: Optional[Callable] = None,
|
|
40
|
+
on_error: Optional[Callable] = None,
|
|
41
|
+
):
|
|
42
|
+
self.base_url = base_url
|
|
43
|
+
{{#each securitySchemes}}
|
|
44
|
+
self.{{configKey}} = {{configKey}}
|
|
45
|
+
{{/each}}
|
|
46
|
+
self.timeout_ms = timeout_ms
|
|
47
|
+
self.retries = retries
|
|
48
|
+
self.default_headers = default_headers or {}
|
|
49
|
+
self.on_request = on_request
|
|
50
|
+
self.on_response = on_response
|
|
51
|
+
self.on_error = on_error
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class {{clientName}}Client:
|
|
55
|
+
"""{{title}} SDK Client"""
|
|
56
|
+
|
|
57
|
+
def __init__(self, config: Optional[{{clientName}}ClientConfig] = None):
|
|
58
|
+
self.config = config or {{clientName}}ClientConfig()
|
|
59
|
+
self._transport = create_transport(
|
|
60
|
+
base_url=self.config.base_url,
|
|
61
|
+
{{#each securitySchemes}}
|
|
62
|
+
{{configKey}}=self.config.{{configKey}},
|
|
63
|
+
{{/each}}
|
|
64
|
+
timeout_ms=self.config.timeout_ms,
|
|
65
|
+
retries=self.config.retries,
|
|
66
|
+
default_headers=self.config.default_headers,
|
|
67
|
+
on_request=self.config.on_request,
|
|
68
|
+
on_response=self.config.on_response,
|
|
69
|
+
on_error=self.config.on_error,
|
|
70
|
+
)
|
|
71
|
+
{{#each groups}}
|
|
72
|
+
self.{{snakeCase name}} = {{interfaceName}}(self._transport)
|
|
73
|
+
{{/each}}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def create_{{snakeCase clientName}}_client(
|
|
77
|
+
base_url: str = "{{baseUrl}}",
|
|
78
|
+
{{#each securitySchemes}}
|
|
79
|
+
{{#if (eq type "apiKey")}}
|
|
80
|
+
{{configKey}}: Optional[str] = None,
|
|
81
|
+
{{/if}}
|
|
82
|
+
{{#if (and (eq type "http") (eq scheme "bearer"))}}
|
|
83
|
+
{{configKey}}: Optional[str] = None,
|
|
84
|
+
{{/if}}
|
|
85
|
+
{{#if (eq type "oauth2")}}
|
|
86
|
+
{{configKey}}: Optional[str] = None,
|
|
87
|
+
{{/if}}
|
|
88
|
+
{{#if (eq type "openIdConnect")}}
|
|
89
|
+
{{configKey}}: Optional[str] = None,
|
|
90
|
+
{{/if}}
|
|
91
|
+
{{/each}}
|
|
92
|
+
**kwargs,
|
|
93
|
+
) -> {{clientName}}Client:
|
|
94
|
+
"""Create a new {{title}} SDK client."""
|
|
95
|
+
config = {{clientName}}ClientConfig(
|
|
96
|
+
base_url=base_url,
|
|
97
|
+
{{#each securitySchemes}}
|
|
98
|
+
{{configKey}}={{configKey}},
|
|
99
|
+
{{/each}}
|
|
100
|
+
**kwargs,
|
|
101
|
+
)
|
|
102
|
+
return {{clientName}}Client(config)
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"""{{interfaceName}} operations."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional, Dict, Any, List
|
|
4
|
+
{{#if imports.length}}
|
|
5
|
+
from ..types import {{#each imports}}{{this}}{{#unless @last}}, {{/unless}}{{/each}}
|
|
6
|
+
{{/if}}
|
|
7
|
+
from ..transport import SDKTransport, RequestOptions
|
|
8
|
+
from ..errors import SDKError
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class {{interfaceName}}:
|
|
12
|
+
"""{{#if description}}{{description}}{{else}}{{interfaceName}} API operations{{/if}}"""
|
|
13
|
+
|
|
14
|
+
def __init__(self, transport: SDKTransport):
|
|
15
|
+
self._transport = transport
|
|
16
|
+
|
|
17
|
+
{{#each operations}}
|
|
18
|
+
{{#if isEventStream}}
|
|
19
|
+
def {{snakeCase name}}(
|
|
20
|
+
self,
|
|
21
|
+
{{#each pathParams}}
|
|
22
|
+
{{name}}: str,
|
|
23
|
+
{{/each}}
|
|
24
|
+
{{#if (and queryType (not queryParams.length))}}
|
|
25
|
+
*,
|
|
26
|
+
query: Optional[Dict[str, Any]] = None,
|
|
27
|
+
{{/if}}
|
|
28
|
+
{{#if queryParams.length}}
|
|
29
|
+
*,
|
|
30
|
+
{{#each queryParams}}
|
|
31
|
+
{{name}}: Optional[{{pythonType type}}] = None,
|
|
32
|
+
{{/each}}
|
|
33
|
+
{{/if}}
|
|
34
|
+
options: Optional[RequestOptions] = None,
|
|
35
|
+
):
|
|
36
|
+
"""{{#if summary}}{{summary}}{{#if description}} - {{description}}{{/if}}{{else if description}}{{description}}{{/if}} (SSE stream)
|
|
37
|
+
"""
|
|
38
|
+
{{#if deprecated}}
|
|
39
|
+
# @deprecated {{deprecationMessage}}
|
|
40
|
+
{{/if}}
|
|
41
|
+
# Build path with parameter substitution
|
|
42
|
+
_path = "{{path}}"
|
|
43
|
+
{{#each pathParams}}
|
|
44
|
+
_path = _path.replace("{" + "{{originalName}}" + "}", str({{name}}))
|
|
45
|
+
{{/each}}
|
|
46
|
+
|
|
47
|
+
# Build query parameters
|
|
48
|
+
_query: Dict[str, Any] = {}
|
|
49
|
+
{{#each queryParams}}
|
|
50
|
+
if query is not None and hasattr(query, "{{name}}") and getattr(query, "{{name}}") is not None:
|
|
51
|
+
_query["{{originalName}}"] = getattr(query, "{{name}}")
|
|
52
|
+
{{/each}}
|
|
53
|
+
|
|
54
|
+
response = self._transport.request(
|
|
55
|
+
method="{{httpMethod}}",
|
|
56
|
+
path=_path,
|
|
57
|
+
query=_query if _query else None,
|
|
58
|
+
options=options,
|
|
59
|
+
)
|
|
60
|
+
return response.json()
|
|
61
|
+
|
|
62
|
+
{{else}}
|
|
63
|
+
def {{snakeCase name}}(
|
|
64
|
+
self,
|
|
65
|
+
{{#each pathParams}}
|
|
66
|
+
{{name}}: str,
|
|
67
|
+
{{/each}}
|
|
68
|
+
{{#if requestBody}}
|
|
69
|
+
body: {{pythonType requestBody.type}},
|
|
70
|
+
{{/if}}
|
|
71
|
+
{{#if (and queryType (not queryParams.length))}}
|
|
72
|
+
*,
|
|
73
|
+
query: Optional[Dict[str, Any]] = None,
|
|
74
|
+
{{/if}}
|
|
75
|
+
{{#if queryParams.length}}
|
|
76
|
+
*,
|
|
77
|
+
{{#each queryParams}}
|
|
78
|
+
{{name}}: Optional[{{pythonType type}}] = None,
|
|
79
|
+
{{/each}}
|
|
80
|
+
{{/if}}
|
|
81
|
+
{{#if headerType}}
|
|
82
|
+
headers: Optional[{{pythonType headerType}}] = None,
|
|
83
|
+
{{/if}}
|
|
84
|
+
options: Optional[RequestOptions] = None,
|
|
85
|
+
) -> {{pythonType responseType}}:
|
|
86
|
+
"""{{#if summary}}{{summary}}{{#if description}} - {{description}}{{/if}}{{else if description}}{{description}}{{/if}}
|
|
87
|
+
"""
|
|
88
|
+
{{#if deprecated}}
|
|
89
|
+
# @deprecated {{deprecationMessage}}
|
|
90
|
+
{{/if}}
|
|
91
|
+
# Build path with parameter substitution
|
|
92
|
+
_path = "{{path}}"
|
|
93
|
+
{{#each pathParams}}
|
|
94
|
+
_path = _path.replace("{" + "{{originalName}}" + "}", str({{name}}))
|
|
95
|
+
{{/each}}
|
|
96
|
+
|
|
97
|
+
# Build query parameters
|
|
98
|
+
_query: Dict[str, Any] = {}
|
|
99
|
+
{{#each queryParams}}
|
|
100
|
+
if {{name}} is not None:
|
|
101
|
+
_query["{{originalName}}"] = {{name}}
|
|
102
|
+
{{/each}}
|
|
103
|
+
{{#if queryType}}
|
|
104
|
+
if query is not None:
|
|
105
|
+
_items = query.__dict__.items() if hasattr(query, "__dict__") else []
|
|
106
|
+
for k, v in _items:
|
|
107
|
+
if v is not None:
|
|
108
|
+
_query[k] = v
|
|
109
|
+
{{/if}}
|
|
110
|
+
|
|
111
|
+
# Build headers
|
|
112
|
+
_headers: Dict[str, str] = {}
|
|
113
|
+
{{#each headerParams}}
|
|
114
|
+
if headers is not None and hasattr(headers, "{{name}}") and getattr(headers, "{{name}}") is not None:
|
|
115
|
+
_headers["{{originalName}}"] = str(getattr(headers, "{{name}}"))
|
|
116
|
+
{{/each}}
|
|
117
|
+
|
|
118
|
+
{{#if (hasRequestBody this)}}
|
|
119
|
+
_content_type = "{{requestBody.contentType}}"
|
|
120
|
+
{{/if}}
|
|
121
|
+
|
|
122
|
+
response = self._transport.request(
|
|
123
|
+
method="{{httpMethod}}",
|
|
124
|
+
path=_path,
|
|
125
|
+
{{#if (or queryParams.length queryType)}}
|
|
126
|
+
query=_query if _query else None,
|
|
127
|
+
{{/if}}
|
|
128
|
+
{{#if (hasRequestBody this)}}
|
|
129
|
+
body=body,
|
|
130
|
+
content_type=_content_type,
|
|
131
|
+
{{/if}}
|
|
132
|
+
{{#if (or headerParams.length (hasRequestBody this))}}
|
|
133
|
+
options=RequestOptions(headers=_headers if _headers else None),
|
|
134
|
+
{{/if}}
|
|
135
|
+
)
|
|
136
|
+
return response.json()
|
|
137
|
+
|
|
138
|
+
{{/if}}
|
|
139
|
+
{{/each}}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""
|
|
2
|
+
{{title}} Python SDK
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .client import create_{{snakeCase clientName}}_client, {{clientName}}Client
|
|
6
|
+
from .errors import SDKError
|
|
7
|
+
from .resources import (
|
|
8
|
+
{{#each groups}}
|
|
9
|
+
{{interfaceName}},
|
|
10
|
+
{{/each}}
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"create_{{snakeCase clientName}}_client",
|
|
15
|
+
"{{clientName}}Client",
|
|
16
|
+
"SDKError",
|
|
17
|
+
{{#each groups}}
|
|
18
|
+
"{{interfaceName}}",
|
|
19
|
+
{{/each}}
|
|
20
|
+
]
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "{{packageName}}"
|
|
7
|
+
version = "{{#if packageVersion}}{{packageVersion}}{{else}}{{version}}{{/if}}"
|
|
8
|
+
description = "{{#if description}}{{description}}{{else}}Python SDK for {{title}}{{/if}}"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
license = {text = "MIT"}
|
|
12
|
+
dependencies = [
|
|
13
|
+
"httpx>=0.27.0",
|
|
14
|
+
"pydantic>=2.0.0",
|
|
15
|
+
"python-dateutil>=2.8.0",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[project.optional-dependencies]
|
|
19
|
+
dev = [
|
|
20
|
+
"pytest>=8.0",
|
|
21
|
+
"pytest-asyncio>=0.24.0",
|
|
22
|
+
"mypy>=1.0",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[tool.setuptools.packages.find]
|
|
26
|
+
include = ["{{moduleName}}*"]
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# {{title}} Python SDK
|
|
2
|
+
|
|
3
|
+
{{#if description}}
|
|
4
|
+
{{description}}
|
|
5
|
+
{{/if}}
|
|
6
|
+
|
|
7
|
+
> Auto-generated by Tesseract.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pip install {{packageName}}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```python
|
|
18
|
+
from {{moduleName}} import create_{{snakeCase clientName}}_client
|
|
19
|
+
|
|
20
|
+
client = create_{{snakeCase clientName}}_client(
|
|
21
|
+
base_url="{{baseUrl}}",
|
|
22
|
+
{{#each securitySchemes}}
|
|
23
|
+
{{#if (eq type "apiKey")}}
|
|
24
|
+
{{configKey}}="your-api-key",
|
|
25
|
+
{{/if}}
|
|
26
|
+
{{#if (and (eq type "http") (eq scheme "bearer"))}}
|
|
27
|
+
{{configKey}}="your-bearer-token",
|
|
28
|
+
{{/if}}
|
|
29
|
+
{{/each}}
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
{{#with groups.[0]}}
|
|
33
|
+
# Example: {{interfaceName}}
|
|
34
|
+
result = client.{{snakeCase name}}.{{snakeCase operations.[0].name}}()
|
|
35
|
+
{{/with}}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## API Reference
|
|
39
|
+
|
|
40
|
+
{{#each groups}}
|
|
41
|
+
### {{interfaceName}}
|
|
42
|
+
|
|
43
|
+
{{#if description}}
|
|
44
|
+
{{description}}
|
|
45
|
+
{{/if}}
|
|
46
|
+
|
|
47
|
+
| Method | HTTP | Path | Description |
|
|
48
|
+
|--------|------|------|-------------|
|
|
49
|
+
{{#each operations}}
|
|
50
|
+
| `{{name}}` | {{httpMethod}} | `{{path}}` | {{#if description}}{{description}}{{else}}-{{/if}} |
|
|
51
|
+
{{/each}}
|
|
52
|
+
|
|
53
|
+
{{/each}}
|
|
54
|
+
|
|
55
|
+
## Error Handling
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from {{moduleName}}.errors import SDKError
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
result = client.some_api.some_method()
|
|
62
|
+
except SDKError as e:
|
|
63
|
+
print(f"Error {e.status}: {e.message} ({e.code})")
|
|
64
|
+
```
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"""HTTP transport layer for {{moduleName}}."""
|
|
2
|
+
|
|
3
|
+
import time
|
|
4
|
+
import json
|
|
5
|
+
import random
|
|
6
|
+
from typing import Optional, Dict, Any, Callable
|
|
7
|
+
from urllib.parse import urlencode
|
|
8
|
+
import httpx
|
|
9
|
+
from .errors import SDKError
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class RequestOptions:
|
|
13
|
+
"""Per-request options."""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
timeout_ms: Optional[int] = None,
|
|
18
|
+
headers: Optional[Dict[str, str]] = None,
|
|
19
|
+
signal: Optional[Any] = None,
|
|
20
|
+
):
|
|
21
|
+
self.timeout_ms = timeout_ms
|
|
22
|
+
self.headers = headers or {}
|
|
23
|
+
self.signal = signal
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class SDKTransport:
|
|
27
|
+
"""HTTP transport with retry, auth, and error handling."""
|
|
28
|
+
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
base_url: str,
|
|
32
|
+
{{#each securitySchemes}}
|
|
33
|
+
{{configKey}}: Optional[str] = None,
|
|
34
|
+
{{/each}}
|
|
35
|
+
timeout_ms: int = 15000,
|
|
36
|
+
retries: int = 3,
|
|
37
|
+
default_headers: Optional[Dict[str, str]] = None,
|
|
38
|
+
on_request: Optional[Callable] = None,
|
|
39
|
+
on_response: Optional[Callable] = None,
|
|
40
|
+
on_error: Optional[Callable] = None,
|
|
41
|
+
):
|
|
42
|
+
self.base_url = base_url.rstrip("/")
|
|
43
|
+
{{#each securitySchemes}}
|
|
44
|
+
self._{{configKey}} = {{configKey}}
|
|
45
|
+
{{/each}}
|
|
46
|
+
self.timeout_ms = timeout_ms
|
|
47
|
+
self.max_retries = retries
|
|
48
|
+
self.default_headers = default_headers or {}
|
|
49
|
+
self.on_request = on_request
|
|
50
|
+
self.on_response = on_response
|
|
51
|
+
self.on_error = on_error
|
|
52
|
+
self._client = httpx.Client(timeout=timeout_ms / 1000.0)
|
|
53
|
+
|
|
54
|
+
def _build_headers(self, extra_headers: Optional[Dict[str, str]] = None) -> Dict[str, str]:
|
|
55
|
+
headers = {**self.default_headers}
|
|
56
|
+
{{#each securitySchemes}}
|
|
57
|
+
{{#if (eq type "apiKey")}}
|
|
58
|
+
if self._{{configKey}}:
|
|
59
|
+
headers["{{paramName}}"] = self._{{configKey}}
|
|
60
|
+
{{/if}}
|
|
61
|
+
{{#if (and (eq type "http") (eq scheme "bearer"))}}
|
|
62
|
+
if self._{{configKey}}:
|
|
63
|
+
headers["Authorization"] = "Bearer " + self._{{configKey}}
|
|
64
|
+
{{/if}}
|
|
65
|
+
{{#if (eq type "oauth2")}}
|
|
66
|
+
if self._{{configKey}}:
|
|
67
|
+
headers["Authorization"] = "Bearer " + self._{{configKey}}
|
|
68
|
+
{{/if}}
|
|
69
|
+
{{#if (eq type "openIdConnect")}}
|
|
70
|
+
if self._{{configKey}}:
|
|
71
|
+
headers["Authorization"] = "Bearer " + self._{{configKey}}
|
|
72
|
+
{{/if}}
|
|
73
|
+
{{/each}}
|
|
74
|
+
if extra_headers:
|
|
75
|
+
headers.update(extra_headers)
|
|
76
|
+
return headers
|
|
77
|
+
|
|
78
|
+
def request(
|
|
79
|
+
self,
|
|
80
|
+
method: str,
|
|
81
|
+
path: str,
|
|
82
|
+
query: Optional[Dict[str, Any]] = None,
|
|
83
|
+
body: Optional[Any] = None,
|
|
84
|
+
content_type: str = "application/json",
|
|
85
|
+
options: Optional[RequestOptions] = None,
|
|
86
|
+
) -> httpx.Response:
|
|
87
|
+
"""Execute an HTTP request with retry logic."""
|
|
88
|
+
url = self.base_url + path
|
|
89
|
+
headers = self._build_headers(options.headers if options else None)
|
|
90
|
+
timeout = (options.timeout_ms if options and options.timeout_ms else self.timeout_ms) / 1000.0
|
|
91
|
+
|
|
92
|
+
if query:
|
|
93
|
+
filtered = {k: v for k, v in query.items() if v is not None}
|
|
94
|
+
if filtered:
|
|
95
|
+
url += "?" + urlencode(filtered)
|
|
96
|
+
|
|
97
|
+
data = None
|
|
98
|
+
if body is not None:
|
|
99
|
+
if content_type == "application/json":
|
|
100
|
+
data = json.dumps(body, default=str)
|
|
101
|
+
headers["Content-Type"] = "application/json"
|
|
102
|
+
else:
|
|
103
|
+
data = body
|
|
104
|
+
|
|
105
|
+
last_error = None
|
|
106
|
+
for attempt in range(self.max_retries + 1):
|
|
107
|
+
try:
|
|
108
|
+
if self.on_request:
|
|
109
|
+
self.on_request(method, url, attempt)
|
|
110
|
+
|
|
111
|
+
response = self._client.request(
|
|
112
|
+
method=method,
|
|
113
|
+
url=url,
|
|
114
|
+
headers=headers,
|
|
115
|
+
content=data,
|
|
116
|
+
timeout=timeout,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
if self.on_response:
|
|
120
|
+
self.on_response(response)
|
|
121
|
+
|
|
122
|
+
if response.status_code >= 400:
|
|
123
|
+
self._handle_error_response(response)
|
|
124
|
+
|
|
125
|
+
return response
|
|
126
|
+
|
|
127
|
+
except httpx.TimeoutException as e:
|
|
128
|
+
last_error = SDKError("Request timed out: " + str(e), 0, "timeout")
|
|
129
|
+
except httpx.NetworkError as e:
|
|
130
|
+
last_error = SDKError("Network error: " + str(e), 0, "network_error")
|
|
131
|
+
except SDKError:
|
|
132
|
+
raise
|
|
133
|
+
except Exception as e:
|
|
134
|
+
last_error = SDKError("Request failed: " + str(e), 0, "unknown_error")
|
|
135
|
+
|
|
136
|
+
if attempt < self.max_retries:
|
|
137
|
+
delay = min(1000 * (2 ** attempt) + random.uniform(0, 500), 30000) / 1000.0
|
|
138
|
+
time.sleep(delay)
|
|
139
|
+
|
|
140
|
+
if self.on_error and last_error:
|
|
141
|
+
self.on_error(last_error)
|
|
142
|
+
raise last_error
|
|
143
|
+
|
|
144
|
+
def _handle_error_response(self, response: httpx.Response) -> None:
|
|
145
|
+
try:
|
|
146
|
+
body = response.json()
|
|
147
|
+
error_code = body.get("error", "unknown_error")
|
|
148
|
+
message = body.get("message", response.text)
|
|
149
|
+
request_id = body.get("request_id")
|
|
150
|
+
raise SDKError(message, response.status_code, error_code, request_id)
|
|
151
|
+
except (json.JSONDecodeError, KeyError):
|
|
152
|
+
raise SDKError(
|
|
153
|
+
"HTTP " + str(response.status_code) + ": " + str(response.text)[:200],
|
|
154
|
+
response.status_code,
|
|
155
|
+
"http_error",
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
def close(self):
|
|
159
|
+
self._client.close()
|
|
160
|
+
|
|
161
|
+
def __enter__(self):
|
|
162
|
+
return self
|
|
163
|
+
|
|
164
|
+
def __exit__(self, *args):
|
|
165
|
+
self.close()
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def create_transport(
|
|
169
|
+
base_url: str,
|
|
170
|
+
{{#each securitySchemes}}
|
|
171
|
+
{{configKey}}: Optional[str] = None,
|
|
172
|
+
{{/each}}
|
|
173
|
+
timeout_ms: int = 15000,
|
|
174
|
+
retries: int = 3,
|
|
175
|
+
default_headers: Optional[Dict[str, str]] = None,
|
|
176
|
+
on_request: Optional[Callable] = None,
|
|
177
|
+
on_response: Optional[Callable] = None,
|
|
178
|
+
on_error: Optional[Callable] = None,
|
|
179
|
+
) -> SDKTransport:
|
|
180
|
+
return SDKTransport(
|
|
181
|
+
base_url=base_url,
|
|
182
|
+
{{#each securitySchemes}}
|
|
183
|
+
{{configKey}}={{configKey}},
|
|
184
|
+
{{/each}}
|
|
185
|
+
timeout_ms=timeout_ms,
|
|
186
|
+
retries=retries,
|
|
187
|
+
default_headers=default_headers,
|
|
188
|
+
on_request=on_request,
|
|
189
|
+
on_response=on_response,
|
|
190
|
+
on_error=on_error,
|
|
191
|
+
)
|