@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.
Files changed (114) hide show
  1. package/dist/adapters/csharp/index.d.ts +12 -0
  2. package/dist/adapters/csharp/index.d.ts.map +1 -0
  3. package/dist/adapters/csharp/index.js +99 -0
  4. package/dist/adapters/csharp/index.js.map +1 -0
  5. package/dist/adapters/go/index.d.ts +12 -0
  6. package/dist/adapters/go/index.d.ts.map +1 -0
  7. package/dist/adapters/go/index.js +135 -0
  8. package/dist/adapters/go/index.js.map +1 -0
  9. package/dist/adapters/kotlin/index.d.ts +13 -0
  10. package/dist/adapters/kotlin/index.d.ts.map +1 -0
  11. package/dist/adapters/kotlin/index.js +188 -0
  12. package/dist/adapters/kotlin/index.js.map +1 -0
  13. package/dist/adapters/php/index.d.ts +20 -0
  14. package/dist/adapters/php/index.d.ts.map +1 -0
  15. package/dist/adapters/php/index.js +123 -0
  16. package/dist/adapters/php/index.js.map +1 -0
  17. package/dist/adapters/python/index.d.ts +12 -0
  18. package/dist/adapters/python/index.d.ts.map +1 -0
  19. package/dist/adapters/python/index.js +145 -0
  20. package/dist/adapters/python/index.js.map +1 -0
  21. package/dist/adapters/ruby/index.d.ts +12 -0
  22. package/dist/adapters/ruby/index.d.ts.map +1 -0
  23. package/dist/adapters/ruby/index.js +86 -0
  24. package/dist/adapters/ruby/index.js.map +1 -0
  25. package/dist/adapters/rust/index.d.ts +13 -0
  26. package/dist/adapters/rust/index.d.ts.map +1 -0
  27. package/dist/adapters/rust/index.js +213 -0
  28. package/dist/adapters/rust/index.js.map +1 -0
  29. package/dist/adapters/typescript/emitter/emitSchema.d.ts +1 -1
  30. package/dist/adapters/typescript/emitter/emitSchema.d.ts.map +1 -1
  31. package/dist/adapters/typescript/emitter/emitSchema.js +1 -1
  32. package/dist/adapters/typescript/emitter/emitSchema.js.map +1 -1
  33. package/dist/adapters/typescript/emitter/emitTypeScriptModels.d.ts +2 -2
  34. package/dist/adapters/typescript/emitter/emitTypeScriptModels.d.ts.map +1 -1
  35. package/dist/adapters/typescript/emitter/emitTypeScriptModels.js +5 -3
  36. package/dist/adapters/typescript/emitter/emitTypeScriptModels.js.map +1 -1
  37. package/dist/adapters/typescript/emitter/importCollector.d.ts +1 -1
  38. package/dist/adapters/typescript/emitter/importCollector.d.ts.map +1 -1
  39. package/dist/adapters/typescript/index.d.ts.map +1 -1
  40. package/dist/adapters/typescript/index.js +43 -2
  41. package/dist/adapters/typescript/index.js.map +1 -1
  42. package/dist/cli.js +2 -1
  43. package/dist/cli.js.map +1 -1
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +14 -0
  46. package/dist/index.js.map +1 -1
  47. package/dist/pipeline/intake.d.ts.map +1 -1
  48. package/dist/pipeline/intake.js +32 -21
  49. package/dist/pipeline/intake.js.map +1 -1
  50. package/dist/types/ir.d.ts +1 -1
  51. package/dist/types/ir.d.ts.map +1 -1
  52. package/dist/types/sdk-module.d.ts +22 -2
  53. package/dist/types/sdk-module.d.ts.map +1 -1
  54. package/dist/types/sdk-module.js +1 -1
  55. package/package.json +1 -1
  56. package/templates/csharp/client.hbs +95 -0
  57. package/templates/csharp/csproj.hbs +18 -0
  58. package/templates/csharp/domain.hbs +183 -0
  59. package/templates/csharp/errors.hbs +45 -0
  60. package/templates/csharp/readme.hbs +76 -0
  61. package/templates/csharp/transport.hbs +291 -0
  62. package/templates/csharp/types.hbs +104 -0
  63. package/templates/go/client.hbs +158 -0
  64. package/templates/go/domain.hbs +89 -0
  65. package/templates/go/errors.hbs +38 -0
  66. package/templates/go/go-mod.hbs +6 -0
  67. package/templates/go/readme.hbs +73 -0
  68. package/templates/go/transport.hbs +379 -0
  69. package/templates/go/types.hbs +80 -0
  70. package/templates/go/utils.hbs +45 -0
  71. package/templates/kotlin/build-gradle.hbs +151 -0
  72. package/templates/kotlin/client.hbs +57 -0
  73. package/templates/kotlin/domain.hbs +73 -0
  74. package/templates/kotlin/errors.hbs +42 -0
  75. package/templates/kotlin/gradle-properties.hbs +2 -0
  76. package/templates/kotlin/readme.hbs +133 -0
  77. package/templates/kotlin/settings-gradle.hbs +1 -0
  78. package/templates/kotlin/transport.hbs +246 -0
  79. package/templates/kotlin/types.hbs +100 -0
  80. package/templates/php/client.hbs +74 -0
  81. package/templates/php/composer.hbs +19 -0
  82. package/templates/php/domain.hbs +173 -0
  83. package/templates/php/errors.hbs +131 -0
  84. package/templates/php/readme.hbs +89 -0
  85. package/templates/php/transport.hbs +285 -0
  86. package/templates/php/types.hbs +95 -0
  87. package/templates/python/client.hbs +102 -0
  88. package/templates/python/domain.hbs +139 -0
  89. package/templates/python/index.hbs +20 -0
  90. package/templates/python/pyproject.hbs +26 -0
  91. package/templates/python/readme.hbs +64 -0
  92. package/templates/python/resources-init.hbs +11 -0
  93. package/templates/python/transport.hbs +191 -0
  94. package/templates/python/types-errors.hbs +75 -0
  95. package/templates/python/types.hbs +79 -0
  96. package/templates/ruby/Gemfile.hbs +5 -0
  97. package/templates/ruby/client.hbs +86 -0
  98. package/templates/ruby/domain.hbs +110 -0
  99. package/templates/ruby/errors.hbs +150 -0
  100. package/templates/ruby/gemspec.hbs +30 -0
  101. package/templates/ruby/index.hbs +54 -0
  102. package/templates/ruby/readme.hbs +113 -0
  103. package/templates/ruby/transport.hbs +177 -0
  104. package/templates/ruby/types.hbs +126 -0
  105. package/templates/ruby/version.hbs +5 -0
  106. package/templates/rust/api-mod.hbs +3 -0
  107. package/templates/rust/cargo.hbs +22 -0
  108. package/templates/rust/client.hbs +114 -0
  109. package/templates/rust/domain.hbs +94 -0
  110. package/templates/rust/error.hbs +74 -0
  111. package/templates/rust/lib.hbs +85 -0
  112. package/templates/rust/readme.hbs +77 -0
  113. package/templates/rust/transport.hbs +287 -0
  114. package/templates/rust/types.hbs +142 -0
@@ -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,11 @@
1
+ """API resource modules for {{moduleName}}."""
2
+
3
+ {{#each groups}}
4
+ from .{{fileName}} import {{interfaceName}}
5
+ {{/each}}
6
+
7
+ __all__ = [
8
+ {{#each groups}}
9
+ "{{interfaceName}}",
10
+ {{/each}}
11
+ ]
@@ -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
+ )
@@ -0,0 +1,75 @@
1
+ """Error classes for {{moduleName}}."""
2
+
3
+ from typing import Optional, Dict, Any
4
+
5
+
6
+ class SDKError(Exception):
7
+ """Structured error thrown by all SDK operations."""
8
+
9
+ def __init__(
10
+ self,
11
+ message: str,
12
+ status: int,
13
+ code: str,
14
+ request_id: Optional[str] = None,
15
+ timestamp: Optional[str] = None,
16
+ path: Optional[str] = None,
17
+ method: Optional[str] = None,
18
+ hint: Optional[str] = None,
19
+ details: Optional[Any] = None,
20
+ ):
21
+ super().__init__(message)
22
+ self.message = message
23
+ self.status = status
24
+ self.code = code
25
+ self.request_id = request_id
26
+ self.timestamp = timestamp
27
+ self.path = path
28
+ self.method = method
29
+ self.hint = hint
30
+ self.details = details
31
+
32
+ def is_rate_limited(self) -> bool:
33
+ return self.code == "rate_limit_exceeded"
34
+
35
+ def is_auth_error(self) -> bool:
36
+ return self.code in ("unauthorized", "credential_expired", "credential_revoked")
37
+
38
+ def is_forbidden(self) -> bool:
39
+ return self.code == "forbidden"
40
+
41
+ def is_not_found(self) -> bool:
42
+ return self.code == "not_found"
43
+
44
+ def is_validation_error(self) -> bool:
45
+ return self.code in (
46
+ "validation_failed",
47
+ "request_validation_failed",
48
+ "bad_request",
49
+ "unprocessable_entity",
50
+ )
51
+
52
+ def is_server_error(self) -> bool:
53
+ return self.status >= 500
54
+
55
+ def is_client_error(self) -> bool:
56
+ return 400 <= self.status < 500
57
+
58
+ def is_retryable(self) -> bool:
59
+ return (
60
+ self.code in ("network_error", "gateway_timeout")
61
+ or self.status == 429
62
+ or self.status >= 500
63
+ )
64
+
65
+ def to_dict(self) -> Dict[str, Any]:
66
+ return {
67
+ "message": self.message,
68
+ "status": self.status,
69
+ "code": self.code,
70
+ "request_id": self.request_id,
71
+ "timestamp": self.timestamp,
72
+ "path": self.path,
73
+ "method": self.method,
74
+ "hint": self.hint,
75
+ }
@@ -0,0 +1,79 @@
1
+ """Generated type definitions for {{moduleName}}."""
2
+
3
+ from typing import Optional, List, Dict, Any, Literal, Union
4
+ from datetime import datetime
5
+ from pydantic import BaseModel, Field
6
+
7
+
8
+ # -- Pagination --------------------------------------------------
9
+
10
+ class PaginationQuery(BaseModel):
11
+ page: Optional[int] = Field(None, description="Page number (1-indexed)")
12
+ limit: Optional[int] = Field(None, description="Number of items per page")
13
+ sort_by: Optional[str] = Field(None, description="Column name to sort by")
14
+ sort_order: Optional[Literal["asc", "desc"]] = Field(None, description="Sort direction")
15
+
16
+
17
+ class PageMeta(BaseModel):
18
+ size: int
19
+ total_pages: int
20
+ has_more: bool
21
+
22
+
23
+ class Page(BaseModel):
24
+ data: List[Any]
25
+ page: PageMeta
26
+
27
+
28
+ # -- Common Responses --------------------------------------------
29
+
30
+ class SuccessResponse(BaseModel):
31
+ success: bool
32
+ message: Optional[str] = None
33
+
34
+
35
+ class MessageResponse(BaseModel):
36
+ message: str
37
+
38
+
39
+ class ApiErrorEnvelope(BaseModel):
40
+ error: str
41
+ message: str
42
+ status: int
43
+ request_id: Optional[str] = None
44
+ timestamp: Optional[str] = None
45
+ path: Optional[str] = None
46
+ method: Optional[str] = None
47
+ hint: Optional[str] = None
48
+
49
+
50
+ # -- Generated Schemas -------------------------------------------
51
+
52
+ {{#each schemas}}
53
+ {{#if (eq ownership.kind "external")}}
54
+ {{else}}
55
+ {{#if isEnum}}
56
+ class {{name}}(str):
57
+ """{{#if description}}{{description}}{{else}}Enum for {{name}}{{/if}}"""
58
+ {{#each enumValues}}
59
+ {{pascalCase this}} = "{{this}}"
60
+ {{/each}}
61
+
62
+ {{else if isUnionType}}
63
+ {{name}} = Union[{{#each unionMembers}}{{this}}{{#unless @last}}, {{/unless}}{{/each}}]
64
+ """{{#if description}}{{description}}{{else}}Union type for {{name}}{{/if}}"""
65
+
66
+ {{else if isTypeAlias}}
67
+ {{name}} = {{additionalPropertiesType}}
68
+ """{{#if description}}{{description}}{{else}}Type alias for {{name}}{{/if}}"""
69
+
70
+ {{else}}
71
+ class {{name}}(BaseModel):
72
+ """{{#if description}}{{description}}{{else}}Schema for {{name}}{{/if}}"""
73
+ {{#each properties}}
74
+ {{name}}: {{#if nullable}}Optional[{{/if}}{{pythonType type}}{{#if nullable}}] = None{{else}}{{#unless required}} = None{{/unless}}{{/if}}
75
+ {{/each}}
76
+
77
+ {{/if}}
78
+ {{/if}}
79
+ {{/each}}
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "transport"
4
+ {{#each groups}}
5
+ require_relative "resources/{{fileName}}"
6
+ {{/each}}
7
+
8
+ module {{pascalCase meta.clientName}}
9
+ # Configuration for the {{meta.title}} SDK client.
10
+ class Config
11
+ # @return [String] the base URL for API requests
12
+ attr_accessor :base_url
13
+ # @return [Integer] request timeout in seconds
14
+ attr_accessor :timeout
15
+ # @return [Hash] retry configuration
16
+ attr_accessor :retries
17
+ # @return [Hash] default headers sent with every request
18
+ attr_accessor :default_headers
19
+ {{#each securitySchemes}}
20
+ # @return [String, nil] credential for {{name}} security scheme
21
+ attr_accessor :{{camelCase name}}
22
+ {{/each}}
23
+
24
+ # @param base_url [String] the base URL for API requests
25
+ # @param timeout [Integer] request timeout in seconds (default: 15)
26
+ # @param retries [Hash] retry configuration
27
+ # @param default_headers [Hash] headers sent with every request
28
+ {{#each securitySchemes}}
29
+ # @param {{camelCase name}} [String, nil] credential for {{name}}
30
+ {{/each}}
31
+ def initialize(
32
+ base_url: '{{meta.baseUrl}}',
33
+ timeout: 15,
34
+ retries: { attempts: 3, backoff: 0.4, jitter: true, max_backoff: 30.0 },
35
+ default_headers: {},
36
+ {{#each securitySchemes}}
37
+ {{camelCase name}}: nil,
38
+ {{/each}}
39
+ **_
40
+ )
41
+ @base_url = base_url
42
+ @timeout = timeout
43
+ @retries = retries
44
+ @default_headers = default_headers
45
+ {{#each securitySchemes}}
46
+ @{{camelCase name}} = {{camelCase name}}
47
+ {{/each}}
48
+ end
49
+ end
50
+
51
+ # Main client for the {{meta.title}} API.
52
+ #
53
+ # Provides access to all API domains as instance methods.
54
+ class Client
55
+ {{#each groups}}
56
+ # @return [{{pascalCase ../meta.clientName}}::{{pascalCase name}}]
57
+ attr_reader :{{name}}
58
+ {{/each}}
59
+
60
+ # @return [{{pascalCase meta.clientName}}::Config] the client configuration
61
+ attr_reader :config
62
+
63
+ # @return [{{pascalCase meta.clientName}}::Transport] the underlying HTTP transport
64
+ attr_reader :transport
65
+
66
+ # Create a new API client.
67
+ #
68
+ # @param config [{{pascalCase meta.clientName}}::Config, Hash, nil]
69
+ # Either a Config instance or keyword arguments matching Config#initialize.
70
+ def initialize(config = nil)
71
+ @config = if config.is_a?(Config)
72
+ config
73
+ elsif config.is_a?(Hash)
74
+ Config.new(**config)
75
+ else
76
+ Config.new
77
+ end
78
+
79
+ @transport = Transport.new(@config)
80
+
81
+ {{#each groups}}
82
+ @{{name}} = {{pascalCase ../meta.clientName}}::{{pascalCase name}}.new(@transport)
83
+ {{/each}}
84
+ end
85
+ end
86
+ end