geniespec 0.1.2.dev4__tar.gz → 0.1.2.dev6__tar.gz
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.
- {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev6}/PKG-INFO +27 -1
- {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev6}/README.md +26 -0
- {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev6}/pyproject.toml +1 -1
- {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev6}/pyproject.toml.orig +1 -1
- {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev6}/src/geniespec/api_schema.yaml +35 -3
- {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev6}/src/geniespec/genie.py +12 -1
- {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev6}/src/geniespec/languages.py +85 -17
- {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev6}/src/geniespec/renderable_types.py +8 -1
- {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev6}/src/geniespec/templates/python-server/{service}/v{version}/api.py.j2 +8 -3
- {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev6}/src/geniespec/templates/python-server/{service}/v{version}/endpoints.py.j2 +14 -5
- {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev6}/src/geniespec/templates/python-server/{service}/v{version}/exceptions.py.j2 +13 -1
- {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev6}/src/geniespec/templates/python-server/{service}/v{version}/internal.py.j2 +11 -2
- {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev6}/src/geniespec/templates/python-server/{service}/v{version}/resources.py.j2 +11 -2
- {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev6}/src/geniespec/templates/typescript-client/{service}/v{version}/client.ts.j2 +6 -0
- {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev6}/src/geniespec/templates/typescript-client/{service}/v{version}/endpoints.ts.j2 +3 -1
- {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev6}/src/geniespec/templates/typescript-client/{service}/v{version}/index.ts.j2 +2 -0
- {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev6}/src/geniespec/templates/typescript-client/{service}/v{version}/resources.ts.j2 +5 -4
- {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev6}/src/geniespec/types.py +10 -1
- {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev6}/src/geniespec/__init__.py +0 -0
- {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev6}/src/geniespec/cli.py +0 -0
- {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev6}/src/geniespec/templates/python-server/{service}/__init__.py.j2 +0 -0
- {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev6}/src/geniespec/templates/python-server/{service}/v{version}/__init__.py.j2 +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: geniespec
|
|
3
|
-
Version: 0.1.2.
|
|
3
|
+
Version: 0.1.2.dev6
|
|
4
4
|
Summary: Generate API code from an api.yaml spec
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
Requires-Dist: jinja2>=3.1.6
|
|
@@ -68,6 +68,8 @@ resources:
|
|
|
68
68
|
|
|
69
69
|
Fields use it like any other type (`type: ViewContent`, also inside `List` and `Mapping`). `python-server` generates a `ViewContentV1` pydantic discriminated union in `resources`; `typescript-client` generates a `ViewContentV1` union type and a `viewContentV1FromObject` function that builds the member class named by the discriminator (and throws for unknown values).
|
|
70
70
|
|
|
71
|
+
`type: Uuid` is a string that must be a valid UUID. It works anywhere `String` does, including as a primary attribute in paths. `python-server` types it as `uuid.UUID`, so pydantic rejects other strings with a 400, internal methods get `uuid.UUID` objects and responses write them in the standard lowercase hyphenated form; `typescript-client` types it as `string`.
|
|
72
|
+
|
|
71
73
|
## Rate limits
|
|
72
74
|
|
|
73
75
|
Name rate limits at the top level, then pick one per transition with `rateLimit`. `defaultRateLimit` applies to every transition that doesn't set one, and `rateLimit: none` turns it off for a transition, the same way `defaultAuthentication` and `authentication` work:
|
|
@@ -91,6 +93,30 @@ transitions:
|
|
|
91
93
|
|
|
92
94
|
A rate limit needs at least one of `perMinute`, `perFiveMinutes`, `perHour` and `perDay`. `keyBy` is `user` (the default) or `ip`. Requests over any window are rejected with a 429. Limits keyed by `user` count per authenticated user, so a transition using one needs `authentication`. `python-server` writes each rate limit as a `RATE_LIMIT_<NAME>` kiba-core `RateLimitConfig` and passes it to `route`; keying by `ip` needs kiba-core's `OriginIpMiddleware`. `typescript-client` ignores rate limits.
|
|
93
95
|
|
|
96
|
+
## API docs
|
|
97
|
+
|
|
98
|
+
Transitions can set `operationId`, `summary`, `description`, `tags` and `extensions`, and attributes, request fields and response fields can set a `description`:
|
|
99
|
+
|
|
100
|
+
```yaml
|
|
101
|
+
resources:
|
|
102
|
+
- singleName: Member
|
|
103
|
+
collectionName: Members
|
|
104
|
+
attributes:
|
|
105
|
+
- name: member-id
|
|
106
|
+
type: Uuid
|
|
107
|
+
isPrimary: true
|
|
108
|
+
description: The member's id.
|
|
109
|
+
transitions:
|
|
110
|
+
- action: GET
|
|
111
|
+
operationId: getMember
|
|
112
|
+
summary: Get a member
|
|
113
|
+
description: Returns one member.
|
|
114
|
+
tags: [Members]
|
|
115
|
+
extensions: {deprecated: true}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
`python-server` passes the transition fields to kiba-core's `route`, whose `OpenApiSchemaGenerator` documents every route with an `operationId` (and skips the rest); `extensions` reach `OpenApiExtension`s as `metadata['extensions']`. Field descriptions become pydantic `Field(description=...)`, so they show up in the request and response schemas. Each `operationId` must be unique, so a transition that generates more than one route (for example `GET` on a resource with unique attributes) can't set one. `typescript-client` ignores these fields.
|
|
119
|
+
|
|
94
120
|
# Development
|
|
95
121
|
|
|
96
122
|
```
|
|
@@ -54,6 +54,8 @@ resources:
|
|
|
54
54
|
|
|
55
55
|
Fields use it like any other type (`type: ViewContent`, also inside `List` and `Mapping`). `python-server` generates a `ViewContentV1` pydantic discriminated union in `resources`; `typescript-client` generates a `ViewContentV1` union type and a `viewContentV1FromObject` function that builds the member class named by the discriminator (and throws for unknown values).
|
|
56
56
|
|
|
57
|
+
`type: Uuid` is a string that must be a valid UUID. It works anywhere `String` does, including as a primary attribute in paths. `python-server` types it as `uuid.UUID`, so pydantic rejects other strings with a 400, internal methods get `uuid.UUID` objects and responses write them in the standard lowercase hyphenated form; `typescript-client` types it as `string`.
|
|
58
|
+
|
|
57
59
|
## Rate limits
|
|
58
60
|
|
|
59
61
|
Name rate limits at the top level, then pick one per transition with `rateLimit`. `defaultRateLimit` applies to every transition that doesn't set one, and `rateLimit: none` turns it off for a transition, the same way `defaultAuthentication` and `authentication` work:
|
|
@@ -77,6 +79,30 @@ transitions:
|
|
|
77
79
|
|
|
78
80
|
A rate limit needs at least one of `perMinute`, `perFiveMinutes`, `perHour` and `perDay`. `keyBy` is `user` (the default) or `ip`. Requests over any window are rejected with a 429. Limits keyed by `user` count per authenticated user, so a transition using one needs `authentication`. `python-server` writes each rate limit as a `RATE_LIMIT_<NAME>` kiba-core `RateLimitConfig` and passes it to `route`; keying by `ip` needs kiba-core's `OriginIpMiddleware`. `typescript-client` ignores rate limits.
|
|
79
81
|
|
|
82
|
+
## API docs
|
|
83
|
+
|
|
84
|
+
Transitions can set `operationId`, `summary`, `description`, `tags` and `extensions`, and attributes, request fields and response fields can set a `description`:
|
|
85
|
+
|
|
86
|
+
```yaml
|
|
87
|
+
resources:
|
|
88
|
+
- singleName: Member
|
|
89
|
+
collectionName: Members
|
|
90
|
+
attributes:
|
|
91
|
+
- name: member-id
|
|
92
|
+
type: Uuid
|
|
93
|
+
isPrimary: true
|
|
94
|
+
description: The member's id.
|
|
95
|
+
transitions:
|
|
96
|
+
- action: GET
|
|
97
|
+
operationId: getMember
|
|
98
|
+
summary: Get a member
|
|
99
|
+
description: Returns one member.
|
|
100
|
+
tags: [Members]
|
|
101
|
+
extensions: {deprecated: true}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`python-server` passes the transition fields to kiba-core's `route`, whose `OpenApiSchemaGenerator` documents every route with an `operationId` (and skips the rest); `extensions` reach `OpenApiExtension`s as `metadata['extensions']`. Field descriptions become pydantic `Field(description=...)`, so they show up in the request and response schemas. Each `operationId` must be unique, so a transition that generates more than one route (for example `GET` on a resource with unique attributes) can't set one. `typescript-client` ignores these fields.
|
|
105
|
+
|
|
80
106
|
# Development
|
|
81
107
|
|
|
82
108
|
```
|
|
@@ -48,7 +48,7 @@ properties:
|
|
|
48
48
|
type:
|
|
49
49
|
- "null"
|
|
50
50
|
- string
|
|
51
|
-
pattern: '^[a-z][a-z0-
|
|
51
|
+
pattern: '^[a-z][a-z0-9_-]*$'
|
|
52
52
|
rateLimits:
|
|
53
53
|
type: array
|
|
54
54
|
items:
|
|
@@ -158,6 +158,8 @@ definitions:
|
|
|
158
158
|
type: boolean
|
|
159
159
|
isDeprecated:
|
|
160
160
|
type: boolean
|
|
161
|
+
description:
|
|
162
|
+
type: string
|
|
161
163
|
defaultValue:
|
|
162
164
|
type:
|
|
163
165
|
- string
|
|
@@ -230,7 +232,7 @@ definitions:
|
|
|
230
232
|
type:
|
|
231
233
|
- "null"
|
|
232
234
|
- string
|
|
233
|
-
pattern: '^[a-z][a-z0-
|
|
235
|
+
pattern: '^[a-z][a-z0-9_-]*$'
|
|
234
236
|
rateLimit:
|
|
235
237
|
type:
|
|
236
238
|
- "null"
|
|
@@ -242,6 +244,19 @@ definitions:
|
|
|
242
244
|
- json
|
|
243
245
|
- stream
|
|
244
246
|
- redirect
|
|
247
|
+
operationId:
|
|
248
|
+
type: string
|
|
249
|
+
pattern: '^[A-Za-z][A-Za-z0-9_]*$'
|
|
250
|
+
summary:
|
|
251
|
+
type: string
|
|
252
|
+
description:
|
|
253
|
+
type: string
|
|
254
|
+
tags:
|
|
255
|
+
type: array
|
|
256
|
+
items:
|
|
257
|
+
type: string
|
|
258
|
+
extensions:
|
|
259
|
+
type: object
|
|
245
260
|
Transition:
|
|
246
261
|
type: object
|
|
247
262
|
additionalProperties: false
|
|
@@ -266,7 +281,7 @@ definitions:
|
|
|
266
281
|
type:
|
|
267
282
|
- "null"
|
|
268
283
|
- string
|
|
269
|
-
pattern: '^[a-z][a-z0-
|
|
284
|
+
pattern: '^[a-z][a-z0-9_-]*$'
|
|
270
285
|
rateLimit:
|
|
271
286
|
type:
|
|
272
287
|
- "null"
|
|
@@ -285,6 +300,19 @@ definitions:
|
|
|
285
300
|
- collection
|
|
286
301
|
isIndirectAction:
|
|
287
302
|
type: boolean
|
|
303
|
+
operationId:
|
|
304
|
+
type: string
|
|
305
|
+
pattern: '^[A-Za-z][A-Za-z0-9_]*$'
|
|
306
|
+
summary:
|
|
307
|
+
type: string
|
|
308
|
+
description:
|
|
309
|
+
type: string
|
|
310
|
+
tags:
|
|
311
|
+
type: array
|
|
312
|
+
items:
|
|
313
|
+
type: string
|
|
314
|
+
extensions:
|
|
315
|
+
type: object
|
|
288
316
|
RootTransition:
|
|
289
317
|
type: object
|
|
290
318
|
allOf:
|
|
@@ -328,6 +356,8 @@ definitions:
|
|
|
328
356
|
- 'null'
|
|
329
357
|
isDeprecated:
|
|
330
358
|
type: boolean
|
|
359
|
+
description:
|
|
360
|
+
type: string
|
|
331
361
|
maxValue:
|
|
332
362
|
type:
|
|
333
363
|
- string
|
|
@@ -374,6 +404,8 @@ definitions:
|
|
|
374
404
|
type: boolean
|
|
375
405
|
isList:
|
|
376
406
|
type: boolean
|
|
407
|
+
description:
|
|
408
|
+
type: string
|
|
377
409
|
Exception:
|
|
378
410
|
type: object
|
|
379
411
|
additionalProperties: false
|
|
@@ -199,6 +199,7 @@ class Renderer:
|
|
|
199
199
|
isRequired=requestField.isRequired,
|
|
200
200
|
isDeprecated=requestField.isDeprecated,
|
|
201
201
|
source=requestField.source,
|
|
202
|
+
description=requestField.description,
|
|
202
203
|
)
|
|
203
204
|
|
|
204
205
|
def _response_field_to_renderable_field(self, responseField: ResponseField, knownTypeNames: dict[str, str]) -> RenderableField:
|
|
@@ -214,6 +215,7 @@ class Renderer:
|
|
|
214
215
|
mappingValueTypes=responseField.mappingValueTypes,
|
|
215
216
|
enumValues=responseField.enumValues,
|
|
216
217
|
),
|
|
218
|
+
description=responseField.description,
|
|
217
219
|
)
|
|
218
220
|
|
|
219
221
|
def _attribute_to_renderable_attribute(self, attribute: Attribute, knownTypeNames: dict[str, str], isNullable: bool | None = None) -> RenderableAttribute:
|
|
@@ -236,6 +238,7 @@ class Renderer:
|
|
|
236
238
|
isAccessibleIndependently=attribute.isAccessibleIndependently,
|
|
237
239
|
isSettable=attribute.isSettable,
|
|
238
240
|
isNullable=attribute.isNullable,
|
|
241
|
+
description=attribute.description,
|
|
239
242
|
)
|
|
240
243
|
|
|
241
244
|
def _attribute_to_renderable_parameter(self, attribute: Attribute, isRequired: bool, knownTypeNames: dict[str, str], shouldUseDefault: bool = True) -> RenderableParameter:
|
|
@@ -254,8 +257,13 @@ class Renderer:
|
|
|
254
257
|
defaultValue=attribute.defaultValue if shouldUseDefault else None,
|
|
255
258
|
isRequired=isRequired,
|
|
256
259
|
isDeprecated=attribute.isDeprecated,
|
|
260
|
+
description=attribute.description,
|
|
257
261
|
)
|
|
258
262
|
|
|
263
|
+
@staticmethod
|
|
264
|
+
def _operation_documentation(transition: BaseTransition) -> dict[str, object]:
|
|
265
|
+
return {'operationId': transition.operationId, 'summary': transition.summary, 'description': transition.description, 'tags': transition.tags, 'extensions': transition.extensions}
|
|
266
|
+
|
|
259
267
|
@staticmethod
|
|
260
268
|
def _resource_full_name(resource: Resource, name: str) -> str:
|
|
261
269
|
return f'{resource.parentResource or ""}{name}'
|
|
@@ -458,7 +466,7 @@ class Renderer:
|
|
|
458
466
|
knownTypeNames=knownTypeNames,
|
|
459
467
|
defaultAuthentication=api.defaultAuthentication,
|
|
460
468
|
defaultRateLimit=api.defaultRateLimit,
|
|
461
|
-
)
|
|
469
|
+
).model_copy(update=self._operation_documentation(transition=transition))
|
|
462
470
|
for transition in api.transitions
|
|
463
471
|
]
|
|
464
472
|
renderableExceptions = [
|
|
@@ -564,6 +572,7 @@ class Renderer:
|
|
|
564
572
|
for endpoint in customEndpoints:
|
|
565
573
|
customEndpoint = RenderableEndpoint(components=[*endpoint.components, ActionEndpointComponent(actionName=RenderableString.from_separated_string(string=transition.action, separator='-'))])
|
|
566
574
|
renderableApi.operations.append(self._action_operation(transition=transition, endpoint=customEndpoint, knownTypeNames=knownTypeNames, defaultAuthentication=api.defaultAuthentication, defaultRateLimit=api.defaultRateLimit, isIndirectAction=transition.isIndirectAction))
|
|
575
|
+
renderableApi.operations[operationCount:] = [operation.model_copy(update=self._operation_documentation(transition=transition)) for operation in renderableApi.operations[operationCount:]]
|
|
567
576
|
if len(renderableApi.operations) == operationCount:
|
|
568
577
|
uncoveredTransitions.append(f'{renderableResource.singleName.pascalCaseString} {transition.action}')
|
|
569
578
|
if uncoveredTransitions:
|
|
@@ -573,6 +582,8 @@ class Renderer:
|
|
|
573
582
|
rateLimitsByName = {rateLimit.name.kebabCaseString: rateLimit for rateLimit in renderableApi.rateLimits}
|
|
574
583
|
operationErrors = [error for operation in renderableApi.operations for error in self._operation_errors(operation=operation, rateLimitsByName=rateLimitsByName)]
|
|
575
584
|
operationErrors += self._file_errors(resources=renderableResources, exceptions=renderableExceptions)
|
|
585
|
+
operationIdCounts = collections.Counter(operation.operationId for operation in renderableApi.operations if operation.operationId is not None)
|
|
586
|
+
operationErrors += [f'Duplicate operationId: {operationId}' for operationId, count in operationIdCounts.items() if count > 1]
|
|
576
587
|
if operationErrors:
|
|
577
588
|
raise SemanticValidationException(message='\n'.join(operationErrors))
|
|
578
589
|
renderableApi.resources.extend(renderableResources)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import collections
|
|
2
2
|
import keyword
|
|
3
3
|
import re
|
|
4
|
+
import typing
|
|
4
5
|
|
|
5
6
|
from core.exceptions import KibaException
|
|
6
7
|
|
|
@@ -8,6 +9,7 @@ from geniespec.renderable_types import ActionEndpointComponent
|
|
|
8
9
|
from geniespec.renderable_types import AttributeEndpointComponent
|
|
9
10
|
from geniespec.renderable_types import MappingRenderableType
|
|
10
11
|
from geniespec.renderable_types import RenderableApi
|
|
12
|
+
from geniespec.renderable_types import RenderableAttribute
|
|
11
13
|
from geniespec.renderable_types import RenderableEndpointComponent
|
|
12
14
|
from geniespec.renderable_types import RenderableEnum
|
|
13
15
|
from geniespec.renderable_types import RenderableException
|
|
@@ -73,6 +75,9 @@ class BaseLanguageDefinition:
|
|
|
73
75
|
def string_type(self, isNullable: bool) -> str:
|
|
74
76
|
raise NotImplementedError
|
|
75
77
|
|
|
78
|
+
def uuid_type(self, isNullable: bool) -> str:
|
|
79
|
+
raise NotImplementedError
|
|
80
|
+
|
|
76
81
|
def enum_type(self, isNullable: bool, renderableType: RenderableType, apiVersion: Version, isLocalReference: bool) -> str:
|
|
77
82
|
raise NotImplementedError
|
|
78
83
|
|
|
@@ -110,6 +115,8 @@ class BaseLanguageDefinition:
|
|
|
110
115
|
return self.double_type(isNullable=isNullable)
|
|
111
116
|
if yamlType == PrimitiveTypes.STRING:
|
|
112
117
|
return self.string_type(isNullable=isNullable)
|
|
118
|
+
if yamlType == PrimitiveTypes.UUID:
|
|
119
|
+
return self.uuid_type(isNullable=isNullable)
|
|
113
120
|
if yamlType == PrimitiveTypes.BOOLEAN:
|
|
114
121
|
return self.boolean_type(isNullable=isNullable)
|
|
115
122
|
if yamlType == PrimitiveTypes.DATETIME:
|
|
@@ -227,9 +234,9 @@ class BaseLanguageDefinition:
|
|
|
227
234
|
return output
|
|
228
235
|
if component.attribute.isUnique and not component.attribute.isPrimary:
|
|
229
236
|
output.append(self.kebab_name(name=component.attribute.name))
|
|
230
|
-
if component.attribute.fieldType.rawType not in (PrimitiveTypes.INTEGER, PrimitiveTypes.LONG_INTEGER, PrimitiveTypes.STRING):
|
|
231
|
-
raise KibaException(message=f'ResourceEndpointComponent created with an invalid attribute type of {component.attribute.fieldType}. Valid types are: integer, long integer and
|
|
232
|
-
output.append(self.path_parameter_segment(parameterName=self.identifier(name=component.attribute.name), isInteger=component.attribute.fieldType.rawType
|
|
237
|
+
if component.attribute.fieldType.rawType not in (PrimitiveTypes.INTEGER, PrimitiveTypes.LONG_INTEGER, PrimitiveTypes.STRING, PrimitiveTypes.UUID):
|
|
238
|
+
raise KibaException(message=f'ResourceEndpointComponent created with an invalid attribute type of {component.attribute.fieldType}. Valid types are: integer, long integer, string and uuid.')
|
|
239
|
+
output.append(self.path_parameter_segment(parameterName=self.identifier(name=component.attribute.name), isInteger=component.attribute.fieldType.rawType in (PrimitiveTypes.INTEGER, PrimitiveTypes.LONG_INTEGER)))
|
|
233
240
|
return output
|
|
234
241
|
raise KibaException(message=f'Unsupported endpoint component: {type(component)}')
|
|
235
242
|
|
|
@@ -343,6 +350,9 @@ class PythonLanguageDefinition(BaseLanguageDefinition):
|
|
|
343
350
|
def string_type(self, isNullable: bool) -> str:
|
|
344
351
|
return 'str | None' if isNullable else 'str'
|
|
345
352
|
|
|
353
|
+
def uuid_type(self, isNullable: bool) -> str:
|
|
354
|
+
return 'uuid.UUID | None' if isNullable else 'uuid.UUID'
|
|
355
|
+
|
|
346
356
|
def enum_type(self, isNullable: bool, renderableType: RenderableType, apiVersion: Version, isLocalReference: bool) -> str:
|
|
347
357
|
if renderableType.className is None:
|
|
348
358
|
typeName = self.enum_literal(values=renderableType.enumValues)
|
|
@@ -397,6 +407,43 @@ class PythonLanguageDefinition(BaseLanguageDefinition):
|
|
|
397
407
|
def requires_optional_field_default(self, api: RenderableApi) -> bool:
|
|
398
408
|
return any(not self.is_undefined_parameter(operation=operation, parameter=parameter) and not parameter.isRequired and not parameter.fieldType.isNullable for operation in api.operations for parameter in self.request_parameters(operation=operation))
|
|
399
409
|
|
|
410
|
+
def endpoints_require_field(self, api: RenderableApi) -> bool:
|
|
411
|
+
hasRequestDescriptions = any(parameter.description is not None for operation in api.operations for parameter in self.request_parameters(operation=operation))
|
|
412
|
+
hasResponseDescriptions = any(field.description is not None for operation in api.operations for field in operation.responseFields or [])
|
|
413
|
+
return self.requires_optional_field_default(api=api) or hasRequestDescriptions or hasResponseDescriptions
|
|
414
|
+
|
|
415
|
+
@staticmethod
|
|
416
|
+
def resources_require_field(api: RenderableApi) -> bool:
|
|
417
|
+
return any(attribute.description is not None for resource in api.resources for attribute in resource.attributes)
|
|
418
|
+
|
|
419
|
+
@staticmethod
|
|
420
|
+
def _field_default(default: str | None, description: str | None, fieldFunction: str = 'Field', shouldUseField: bool = False) -> str:
|
|
421
|
+
if description is None and not shouldUseField:
|
|
422
|
+
return '' if default is None else f' = {default}'
|
|
423
|
+
arguments = ([] if default is None else [f'default={default}']) + ([] if description is None else [f'description={description!r}'])
|
|
424
|
+
return f' = {fieldFunction}({", ".join(arguments)})'
|
|
425
|
+
|
|
426
|
+
def request_field_declaration(self, operation: RenderableOperation, parameter: RenderableParameter, apiVersion: Version) -> str:
|
|
427
|
+
typeName = self.type(renderableType=parameter.fieldType, apiVersion=apiVersion)
|
|
428
|
+
if self.is_undefined_parameter(operation=operation, parameter=parameter):
|
|
429
|
+
return f'UpdateValue[{typeName}]{self._field_default(default="UNDEFINED", description=parameter.description)}'
|
|
430
|
+
if parameter.isRequired:
|
|
431
|
+
return f'{typeName}{self._field_default(default=None, description=parameter.description)}'
|
|
432
|
+
if parameter.fieldType.isNullable:
|
|
433
|
+
return f'{typeName}{self._field_default(default="None", description=parameter.description)}'
|
|
434
|
+
return f'{typeName}{self._field_default(default=f"typing.cast({typeName}, None)", description=parameter.description, shouldUseField=True)}'
|
|
435
|
+
|
|
436
|
+
def response_field_declaration(self, field: RenderableField, apiVersion: Version) -> str:
|
|
437
|
+
return f'{self.type(renderableType=field.fieldType, apiVersion=apiVersion)}{self._field_default(default=None, description=field.description)}'
|
|
438
|
+
|
|
439
|
+
def attribute_declaration(self, attribute: RenderableAttribute, apiVersion: Version) -> str:
|
|
440
|
+
return f'{self.type(renderableType=attribute.fieldType, apiVersion=apiVersion, isLocalReference=True)}{self._field_default(default=None, description=attribute.description, fieldFunction="pydantic.Field")}'
|
|
441
|
+
|
|
442
|
+
@staticmethod
|
|
443
|
+
def route_documentation_arguments(operation: RenderableOperation) -> str:
|
|
444
|
+
arguments = {'operationId': operation.operationId, 'summary': operation.summary, 'description': operation.description, 'tags': operation.tags or None, 'extensions': operation.extensions or None}
|
|
445
|
+
return ''.join(f', {name}={value!r}' for name, value in arguments.items() if value is not None)
|
|
446
|
+
|
|
400
447
|
def requires_streaming(self, api: RenderableApi) -> bool:
|
|
401
448
|
return any(self.is_streaming(operation=operation) for operation in api.operations)
|
|
402
449
|
|
|
@@ -418,13 +465,32 @@ class PythonLanguageDefinition(BaseLanguageDefinition):
|
|
|
418
465
|
return parameterName if self.requires_parameter_preprocessing(operation=operation, parameter=parameter) else f'request.data.{parameterName}'
|
|
419
466
|
|
|
420
467
|
@staticmethod
|
|
421
|
-
def
|
|
468
|
+
def _type_parts(renderableType: RenderableType) -> list[RenderableType]:
|
|
422
469
|
if isinstance(renderableType, MappingRenderableType):
|
|
423
|
-
return PythonLanguageDefinition.
|
|
424
|
-
return renderableType
|
|
425
|
-
|
|
426
|
-
def
|
|
427
|
-
|
|
470
|
+
return [renderableType, *PythonLanguageDefinition._type_parts(renderableType=renderableType.keyType), *PythonLanguageDefinition._type_parts(renderableType=renderableType.valueType)]
|
|
471
|
+
return [renderableType]
|
|
472
|
+
|
|
473
|
+
def _module_field_types(self, api: RenderableApi, module: str) -> list[RenderableType]:
|
|
474
|
+
responseFieldTypes = [field.fieldType for operation in api.operations for field in operation.responseFields or []]
|
|
475
|
+
if module == 'resources':
|
|
476
|
+
return [attribute.fieldType for resource in api.resources for attribute in resource.attributes]
|
|
477
|
+
if module == 'endpoints':
|
|
478
|
+
return [parameter.fieldType for operation in api.operations for parameter in self.request_parameters(operation=operation)] + responseFieldTypes
|
|
479
|
+
if module == 'internal':
|
|
480
|
+
return [parameter.fieldType for operation in api.operations for parameter in operation.parameters] + responseFieldTypes
|
|
481
|
+
if module == 'exceptions':
|
|
482
|
+
return [field.fieldType for exception in api.exceptions for field in exception.fields]
|
|
483
|
+
raise KibaException(message=f'Unknown module: {module}')
|
|
484
|
+
|
|
485
|
+
def module_requires(self, api: RenderableApi, module: str, requirement: str) -> bool:
|
|
486
|
+
predicates: dict[str, typing.Callable[[RenderableType], bool]] = {
|
|
487
|
+
'collections': lambda part: part.isList or isinstance(part, MappingRenderableType),
|
|
488
|
+
'datetime': lambda part: part.rawType in (PrimitiveTypes.DATETIME, PrimitiveTypes.DATE),
|
|
489
|
+
'json': lambda part: part.rawType == PrimitiveTypes.JSON,
|
|
490
|
+
'resources': lambda part: part.className is not None,
|
|
491
|
+
'uuid': lambda part: part.rawType == PrimitiveTypes.UUID,
|
|
492
|
+
}
|
|
493
|
+
return any(predicates[requirement](part) for fieldType in self._module_field_types(api=api, module=module) for part in self._type_parts(renderableType=fieldType))
|
|
428
494
|
|
|
429
495
|
def resources_require_typing(self, api: RenderableApi) -> bool:
|
|
430
496
|
return len(api.enums) > 0 or len(api.oneOfs) > 0 or any(self.is_inline_enum(renderableType=attribute.fieldType) for resource in api.resources for attribute in resource.attributes)
|
|
@@ -439,13 +505,6 @@ class PythonLanguageDefinition(BaseLanguageDefinition):
|
|
|
439
505
|
def requires_uploaded_file(self, api: RenderableApi) -> bool:
|
|
440
506
|
return any(self.is_file_operation(operation=operation) for operation in api.operations)
|
|
441
507
|
|
|
442
|
-
def operations_require_json(self, api: RenderableApi) -> bool:
|
|
443
|
-
fieldTypes = [parameter.fieldType for operation in api.operations for parameter in operation.parameters] + [field.fieldType for operation in api.operations for field in operation.responseFields or []]
|
|
444
|
-
return any(self._contains_json(renderableType=fieldType) for fieldType in fieldTypes)
|
|
445
|
-
|
|
446
|
-
def exceptions_require_json(self, api: RenderableApi) -> bool:
|
|
447
|
-
return any(self._contains_json(renderableType=field.fieldType) for exception in api.exceptions for field in exception.fields)
|
|
448
|
-
|
|
449
508
|
def route_ordered_operations(self, api: RenderableApi) -> list[RenderableOperation]:
|
|
450
509
|
return sorted(api.operations, key=lambda operation: tuple(segment.startswith('{') for segment in self.endpoint(operation=operation).split('/')))
|
|
451
510
|
|
|
@@ -562,6 +621,9 @@ class TypeScriptLanguageDefinition(BaseLanguageDefinition):
|
|
|
562
621
|
def string_type(self, isNullable: bool) -> str:
|
|
563
622
|
return self._nullable(typeName='string', isNullable=isNullable)
|
|
564
623
|
|
|
624
|
+
def uuid_type(self, isNullable: bool) -> str:
|
|
625
|
+
return self._nullable(typeName='string', isNullable=isNullable)
|
|
626
|
+
|
|
565
627
|
def enum_type(self, isNullable: bool, renderableType: RenderableType, apiVersion: Version, isLocalReference: bool) -> str:
|
|
566
628
|
if renderableType.className is None:
|
|
567
629
|
return self._nullable(typeName=f'({self.enum_union(values=renderableType.enumValues)})', isNullable=isNullable)
|
|
@@ -649,6 +711,12 @@ class TypeScriptLanguageDefinition(BaseLanguageDefinition):
|
|
|
649
711
|
def requires_request_data(self, api: RenderableApi) -> bool:
|
|
650
712
|
return any(self.body_parameters(operation=operation) and not self.is_file_operation(operation=operation) for operation in self.client_operations(api=api))
|
|
651
713
|
|
|
714
|
+
def requires_rest_method(self, api: RenderableApi) -> bool:
|
|
715
|
+
return any(not self.is_file_operation(operation=operation) for operation in self.client_operations(api=api))
|
|
716
|
+
|
|
717
|
+
def has_endpoint_classes(self, api: RenderableApi) -> bool:
|
|
718
|
+
return len(self.body_parameter_types(api=api)) > 0 or len(self.response_field_types(api=api)) > 0
|
|
719
|
+
|
|
652
720
|
def form_value(self, renderableType: RenderableType, value: str) -> str:
|
|
653
721
|
if renderableType.rawType == RenderableRawTypes.FILE:
|
|
654
722
|
return value
|
|
@@ -680,7 +748,7 @@ class TypeScriptLanguageDefinition(BaseLanguageDefinition):
|
|
|
680
748
|
return f'{parserName if isLocalReference else f"Resources.{parserName}"}({value} as RawObject)'
|
|
681
749
|
if renderableType.rawType == 'class' and renderableType.className is not None:
|
|
682
750
|
return f'{self.type(renderableType=renderableType, apiVersion=apiVersion, isLocalReference=isLocalReference)}.fromObject({value} as RawObject)'
|
|
683
|
-
if renderableType.rawType
|
|
751
|
+
if renderableType.rawType in (PrimitiveTypes.STRING, PrimitiveTypes.UUID):
|
|
684
752
|
return f'String({value})'
|
|
685
753
|
if renderableType.rawType in (PrimitiveTypes.INTEGER, PrimitiveTypes.LONG_INTEGER, PrimitiveTypes.FLOAT, PrimitiveTypes.DOUBLE):
|
|
686
754
|
return f'Number({value})'
|
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import re
|
|
4
4
|
|
|
5
|
+
import pydantic
|
|
5
6
|
from core.exceptions import KibaException
|
|
6
7
|
from pydantic import BaseModel
|
|
7
8
|
|
|
@@ -128,6 +129,7 @@ class RenderableField(BaseModel):
|
|
|
128
129
|
name: RenderableString
|
|
129
130
|
fieldType: RenderableType
|
|
130
131
|
defaultValue: str | int | float | bool | None = None
|
|
132
|
+
description: str | None = None
|
|
131
133
|
|
|
132
134
|
|
|
133
135
|
class RenderableAttribute(RenderableField):
|
|
@@ -191,7 +193,7 @@ class RenderableEndpoint(BaseModel):
|
|
|
191
193
|
@property
|
|
192
194
|
def urlParameters(self) -> list[RenderableParameter]: # noqa: N802
|
|
193
195
|
return [
|
|
194
|
-
RenderableParameter(name=component.attribute.name, fieldType=component.attribute.fieldType, isRequired=True, isDeprecated=False)
|
|
196
|
+
RenderableParameter(name=component.attribute.name, fieldType=component.attribute.fieldType, isRequired=True, isDeprecated=False, description=component.attribute.description)
|
|
195
197
|
for component in self.components
|
|
196
198
|
if isinstance(component, ResourceEndpointComponent) and component.attribute is not None
|
|
197
199
|
]
|
|
@@ -215,6 +217,11 @@ class RenderableOperation(BaseModel):
|
|
|
215
217
|
rateLimit: str | None = None
|
|
216
218
|
isIndirectAction: bool = False
|
|
217
219
|
responseType: str = ResponseTypes.JSON
|
|
220
|
+
operationId: str | None = None
|
|
221
|
+
summary: str | None = None
|
|
222
|
+
description: str | None = None
|
|
223
|
+
tags: list[str] = []
|
|
224
|
+
extensions: dict[str, pydantic.JsonValue] = {}
|
|
218
225
|
|
|
219
226
|
@property
|
|
220
227
|
def urlParameters(self) -> list[RenderableParameter]: # noqa: N802
|
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
# ruff: noqa: ARG001
|
|
3
3
|
{% if target.requires_streaming(api=api) %}
|
|
4
4
|
import collections.abc
|
|
5
|
-
{% endif %}
|
|
6
|
-
import datetime
|
|
7
5
|
|
|
6
|
+
{% endif %}
|
|
7
|
+
{% if api.operations %}
|
|
8
8
|
from core.api.api_request import KibaApiRequest
|
|
9
9
|
from core.api.route import route
|
|
10
|
+
{% endif %}
|
|
10
11
|
from core.api.route_auth import RouteAuthResolver
|
|
11
12
|
{% if api.rateLimits %}
|
|
12
13
|
from core.api.route_metadata import RateLimitConfig
|
|
@@ -14,10 +15,14 @@ from core.api.route_metadata import RateLimitConfig
|
|
|
14
15
|
{% if target.requires_redirect(api=api) %}
|
|
15
16
|
from core.exceptions import FoundRedirectException
|
|
16
17
|
{% endif %}
|
|
18
|
+
{% if api.operations %}
|
|
17
19
|
from core.http.rest_method import RestMethod
|
|
20
|
+
{% endif %}
|
|
18
21
|
from starlette.routing import BaseRoute
|
|
22
|
+
{% if api.operations %}
|
|
19
23
|
from starlette.routing import Route
|
|
20
24
|
from . import endpoints
|
|
25
|
+
{% endif %}
|
|
21
26
|
from .internal import {{ target.internal_class_name(api=api) }}
|
|
22
27
|
{% for rateLimit in api.rateLimits %}
|
|
23
28
|
{{ target.rate_limit_constant_name(name=rateLimit.name.kebabCaseString) }}: RateLimitConfig = {{ target.rate_limit_config(rateLimit=rateLimit) }}
|
|
@@ -30,7 +35,7 @@ def create_routes(internal: {{ target.internal_class_name(api=api) }}, {% if tar
|
|
|
30
35
|
{% set rate_limit = target.route_rate_limit(operation=operation) %}
|
|
31
36
|
{% set class_name = target.operation_class_name(operation=operation) %}
|
|
32
37
|
{% set result_name = target.identifier(name=operation.responseFields[0].name) if operation.responseFields else 'result' %}
|
|
33
|
-
@route(requestType=endpoints.{{ class_name }}Request, responseType=endpoints.{{ class_name }}Response{% if target.is_streaming(operation=operation) %}, isStreaming=True{% endif %}{% if auth is not none %}, authResolver=authResolver, auth='{{ auth }}'{% endif %}{% if rate_limit is not none %}, rateLimit={{ target.rate_limit_constant_name(name=rate_limit) }}{% endif %})
|
|
38
|
+
@route(requestType=endpoints.{{ class_name }}Request, responseType=endpoints.{{ class_name }}Response{% if target.is_streaming(operation=operation) %}, isStreaming=True{% endif %}{% if auth is not none %}, authResolver=authResolver, auth='{{ auth }}'{% endif %}{% if rate_limit is not none %}, rateLimit={{ target.rate_limit_constant_name(name=rate_limit) }}{% endif %}{{ target.route_documentation_arguments(operation=operation) }})
|
|
34
39
|
async def {{ target.operation_name(operation=operation) }}(request: KibaApiRequest[endpoints.{{ class_name }}Request]) -> {% if target.is_streaming(operation=operation) %}collections.abc.AsyncIterator[endpoints.{{ class_name }}Response]{% else %}endpoints.{{ class_name }}Response{% endif %}:
|
|
35
40
|
{% for parameter in target.request_parameters(operation=operation) %}
|
|
36
41
|
{% set param_name = target.identifier(name=parameter.name) %}
|
|
@@ -1,16 +1,23 @@
|
|
|
1
1
|
{{ autogenerated_notice }}
|
|
2
|
+
{% if target.module_requires(api=api, module='endpoints', requirement='collections') %}
|
|
2
3
|
import collections.abc
|
|
4
|
+
{% endif %}
|
|
5
|
+
{% if target.module_requires(api=api, module='endpoints', requirement='datetime') %}
|
|
3
6
|
import datetime
|
|
7
|
+
{% endif %}
|
|
4
8
|
{% if target.requires_optional_field_default(api=api) or target.operations_require_typing(api=api) %}
|
|
5
9
|
import typing
|
|
6
10
|
{% endif %}
|
|
11
|
+
{% if target.module_requires(api=api, module='endpoints', requirement='uuid') %}
|
|
12
|
+
import uuid
|
|
13
|
+
{% endif %}
|
|
7
14
|
|
|
8
|
-
{% if target.
|
|
15
|
+
{% if target.module_requires(api=api, module='endpoints', requirement='json') %}
|
|
9
16
|
import pydantic
|
|
10
17
|
{% endif %}
|
|
11
|
-
{% if target.
|
|
18
|
+
{% if target.endpoints_require_field(api=api) %}
|
|
12
19
|
from pydantic import BaseModel, Field
|
|
13
|
-
{%
|
|
20
|
+
{% elif api.operations %}
|
|
14
21
|
from pydantic import BaseModel
|
|
15
22
|
{% endif %}
|
|
16
23
|
{% if target.requires_undefined(api=api) %}
|
|
@@ -19,21 +26,23 @@ from core.util.typing_util import UNDEFINED, UpdateValue
|
|
|
19
26
|
{% if target.requires_uploaded_file(api=api) %}
|
|
20
27
|
from core.api.uploaded_file import UploadedFile
|
|
21
28
|
{% endif %}
|
|
29
|
+
{% if target.module_requires(api=api, module='endpoints', requirement='resources') %}
|
|
22
30
|
|
|
23
31
|
from . import resources
|
|
32
|
+
{% endif %}
|
|
24
33
|
|
|
25
34
|
{% for operation in api.operations %}
|
|
26
35
|
|
|
27
36
|
class {{ target.operation_class_name(operation=operation) }}Request(BaseModel):
|
|
28
37
|
{% for parameter in target.request_parameters(operation=operation) %}
|
|
29
|
-
{{ target.identifier(name=parameter.name) }}: {
|
|
38
|
+
{{ target.identifier(name=parameter.name) }}: {{ target.request_field_declaration(operation=operation, parameter=parameter, apiVersion=api.version) }}
|
|
30
39
|
{% else %}
|
|
31
40
|
pass
|
|
32
41
|
{% endfor %}
|
|
33
42
|
|
|
34
43
|
class {{ target.operation_class_name(operation=operation) }}Response(BaseModel):
|
|
35
44
|
{% if operation.responseFields and operation.responseFields | length > 0 %}
|
|
36
|
-
{{ target.identifier(name=operation.responseFields[0].name) }}: {{ target.
|
|
45
|
+
{{ target.identifier(name=operation.responseFields[0].name) }}: {{ target.response_field_declaration(field=operation.responseFields[0], apiVersion=api.version) }}
|
|
37
46
|
{% else %}
|
|
38
47
|
pass
|
|
39
48
|
{% endif %}
|
|
@@ -1,13 +1,25 @@
|
|
|
1
1
|
{{ autogenerated_notice }}
|
|
2
2
|
from __future__ import annotations
|
|
3
|
+
{% if target.module_requires(api=api, module='exceptions', requirement='collections') %}
|
|
3
4
|
import collections.abc
|
|
5
|
+
{% endif %}
|
|
6
|
+
{% if target.module_requires(api=api, module='exceptions', requirement='datetime') %}
|
|
7
|
+
import datetime
|
|
8
|
+
{% endif %}
|
|
4
9
|
import typing
|
|
5
|
-
{% if target.
|
|
10
|
+
{% if target.module_requires(api=api, module='exceptions', requirement='uuid') %}
|
|
11
|
+
import uuid
|
|
12
|
+
{% endif %}
|
|
13
|
+
{% if target.module_requires(api=api, module='exceptions', requirement='json') %}
|
|
6
14
|
import pydantic
|
|
7
15
|
{% endif %}
|
|
16
|
+
{% if api.exceptions %}
|
|
8
17
|
from core import exceptions
|
|
9
18
|
from core.util.typing_util import Json
|
|
19
|
+
{% endif %}
|
|
20
|
+
{% if target.module_requires(api=api, module='exceptions', requirement='resources') %}
|
|
10
21
|
from . import resources
|
|
22
|
+
{% endif %}
|
|
11
23
|
|
|
12
24
|
|
|
13
25
|
class KibaExceptionTypedDict(typing.TypedDict):
|
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
{{ autogenerated_notice }}
|
|
2
2
|
import abc
|
|
3
|
-
|
|
3
|
+
{% if target.module_requires(api=api, module='internal', requirement='collections') or target.requires_streaming(api=api) %}
|
|
4
4
|
import collections.abc
|
|
5
|
+
{% endif %}
|
|
6
|
+
{% if target.module_requires(api=api, module='internal', requirement='datetime') %}
|
|
7
|
+
import datetime
|
|
8
|
+
{% endif %}
|
|
5
9
|
{% if target.operations_require_typing(api=api) %}
|
|
6
10
|
import typing
|
|
7
11
|
{% endif %}
|
|
12
|
+
{% if target.module_requires(api=api, module='internal', requirement='uuid') %}
|
|
13
|
+
import uuid
|
|
14
|
+
{% endif %}
|
|
8
15
|
|
|
9
|
-
{% if target.
|
|
16
|
+
{% if target.module_requires(api=api, module='internal', requirement='json') %}
|
|
10
17
|
import pydantic
|
|
11
18
|
|
|
12
19
|
{% endif %}
|
|
20
|
+
{% if target.module_requires(api=api, module='internal', requirement='resources') %}
|
|
13
21
|
from . import resources
|
|
22
|
+
{% endif %}
|
|
14
23
|
{% if target.requires_undefined(api=api) %}
|
|
15
24
|
from core.util.typing_util import UNDEFINED, UpdateValue
|
|
16
25
|
{% endif %}
|
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
{{ autogenerated_notice }}
|
|
2
2
|
from __future__ import annotations
|
|
3
3
|
|
|
4
|
+
{% if target.module_requires(api=api, module='resources', requirement='collections') %}
|
|
4
5
|
import collections.abc
|
|
6
|
+
{% endif %}
|
|
7
|
+
{% if target.module_requires(api=api, module='resources', requirement='datetime') %}
|
|
5
8
|
import datetime
|
|
9
|
+
{% endif %}
|
|
6
10
|
{% if target.resources_require_typing(api=api) %}
|
|
7
11
|
import typing
|
|
8
12
|
{% endif %}
|
|
13
|
+
{% if target.module_requires(api=api, module='resources', requirement='uuid') %}
|
|
14
|
+
import uuid
|
|
15
|
+
{% endif %}
|
|
9
16
|
|
|
10
|
-
{% if target.
|
|
17
|
+
{% if target.module_requires(api=api, module='resources', requirement='json') or api.oneOfs or target.resources_require_field(api=api) %}
|
|
11
18
|
import pydantic
|
|
12
19
|
{% endif %}
|
|
20
|
+
{% if api.resources %}
|
|
13
21
|
from pydantic import BaseModel
|
|
22
|
+
{% endif %}
|
|
14
23
|
|
|
15
24
|
{% for enum in api.enums %}
|
|
16
25
|
{{ target.enum_class_name(enum=enum, apiVersion=api.version) }} = {{ target.enum_literal(values=enum.values) }}
|
|
@@ -25,7 +34,7 @@ class {{ target.resource_class_name(resource=resource, apiVersion=api.version) }
|
|
|
25
34
|
pass
|
|
26
35
|
{% endif %}
|
|
27
36
|
{% for attribute in resource.attributes %}
|
|
28
|
-
{{ target.identifier(name=attribute.name) }}: {{ target.
|
|
37
|
+
{{ target.identifier(name=attribute.name) }}: {{ target.attribute_declaration(attribute=attribute, apiVersion=api.version) }}
|
|
29
38
|
{% endfor %}
|
|
30
39
|
|
|
31
40
|
{% endfor %}
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
{{ autogenerated_notice }}
|
|
2
|
+
{% if target.requires_rest_method(api=api) %}
|
|
2
3
|
import { RestMethod, ServiceClient } from '@kibalabs/core';
|
|
4
|
+
{% else %}
|
|
5
|
+
import { ServiceClient } from '@kibalabs/core';
|
|
6
|
+
{% endif %}
|
|
7
|
+
{% if target.has_endpoint_classes(api=api) %}
|
|
3
8
|
|
|
4
9
|
import * as Endpoints from './endpoints';
|
|
10
|
+
{% endif %}
|
|
5
11
|
{% if target.references_resources(renderableTypes=target.response_field_types(api=api)) %}
|
|
6
12
|
import * as Resources from './resources';
|
|
7
13
|
{% endif %}
|
|
@@ -4,10 +4,12 @@
|
|
|
4
4
|
{% set coreImports = [] %}
|
|
5
5
|
{% if target.requires_date_parsing(renderableTypes=responseTypes) %}{% set _ = coreImports.append('dateFromString') %}{% endif %}
|
|
6
6
|
{% if target.requires_date_parsing(renderableTypes=bodyTypes) %}{% set _ = coreImports.append('dateToString') %}{% endif %}
|
|
7
|
-
{% set _ = coreImports.append('RawObject') %}
|
|
7
|
+
{% if target.requires_request_data(api=api) or responseTypes %}{% set _ = coreImports.append('RawObject') %}{% endif %}
|
|
8
8
|
{% if target.requires_request_data(api=api) %}{% set _ = coreImports.append('RequestData') %}{% endif %}
|
|
9
9
|
{% if responseTypes %}{% set _ = coreImports.append('ResponseData') %}{% endif %}
|
|
10
|
+
{% if coreImports %}
|
|
10
11
|
import { {{ coreImports | join(', ') }} } from '@kibalabs/core';
|
|
12
|
+
{% endif %}
|
|
11
13
|
{% if target.references_resources(renderableTypes=responseTypes + bodyTypes) %}
|
|
12
14
|
|
|
13
15
|
import * as Resources from './resources';
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{{ autogenerated_notice }}
|
|
2
|
-
{%
|
|
3
|
-
|
|
4
|
-
{%
|
|
5
|
-
|
|
2
|
+
{% set coreImports = [] %}
|
|
3
|
+
{% if target.requires_date_parsing(renderableTypes=target.resource_field_types(api=api)) %}{% set _ = coreImports.extend(['dateFromString', 'dateToString']) %}{% endif %}
|
|
4
|
+
{% if api.resources or api.oneOfs %}{% set _ = coreImports.append('RawObject') %}{% endif %}
|
|
5
|
+
{% if coreImports %}
|
|
6
|
+
import { {{ coreImports | join(', ') }} } from '@kibalabs/core';
|
|
6
7
|
{% endif %}
|
|
7
8
|
|
|
8
9
|
export type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };
|
|
@@ -12,11 +12,12 @@ class PrimitiveTypes:
|
|
|
12
12
|
FLOAT = 'Float'
|
|
13
13
|
DOUBLE = 'Double'
|
|
14
14
|
STRING = 'String'
|
|
15
|
+
UUID = 'Uuid'
|
|
15
16
|
BOOLEAN = 'Boolean'
|
|
16
17
|
DATETIME = 'Datetime'
|
|
17
18
|
DATE = 'Date'
|
|
18
19
|
JSON = 'Json'
|
|
19
|
-
ALL: ClassVar[list[str]] = [LONG_INTEGER, INTEGER, FLOAT, DOUBLE, STRING, BOOLEAN, DATETIME, DATE, JSON]
|
|
20
|
+
ALL: ClassVar[list[str]] = [LONG_INTEGER, INTEGER, FLOAT, DOUBLE, STRING, UUID, BOOLEAN, DATETIME, DATE, JSON]
|
|
20
21
|
|
|
21
22
|
|
|
22
23
|
class FieldSources:
|
|
@@ -84,6 +85,7 @@ class Attribute(BaseModel):
|
|
|
84
85
|
isSettable: bool = False
|
|
85
86
|
isAccessibleIndependently: bool = False
|
|
86
87
|
defaultValue: str | int | float | bool | None = None
|
|
88
|
+
description: str | None = None
|
|
87
89
|
|
|
88
90
|
|
|
89
91
|
class ResponseField(BaseModel):
|
|
@@ -95,6 +97,7 @@ class ResponseField(BaseModel):
|
|
|
95
97
|
enumValues: list[str] = []
|
|
96
98
|
isNullable: bool = False
|
|
97
99
|
isList: bool = False
|
|
100
|
+
description: str | None = None
|
|
98
101
|
|
|
99
102
|
|
|
100
103
|
class RequestField(BaseModel):
|
|
@@ -110,6 +113,7 @@ class RequestField(BaseModel):
|
|
|
110
113
|
maxValue: str | int | float | None = None
|
|
111
114
|
minValue: str | int | float | None = None
|
|
112
115
|
source: str = FieldSources.REQUEST
|
|
116
|
+
description: str | None = None
|
|
113
117
|
|
|
114
118
|
|
|
115
119
|
class BaseTransition(BaseModel):
|
|
@@ -120,6 +124,11 @@ class BaseTransition(BaseModel):
|
|
|
120
124
|
authentication: str | None = None
|
|
121
125
|
rateLimit: str | None = None
|
|
122
126
|
responseType: str = ResponseTypes.JSON
|
|
127
|
+
operationId: str | None = None
|
|
128
|
+
summary: str | None = None
|
|
129
|
+
description: str | None = None
|
|
130
|
+
tags: list[str] = []
|
|
131
|
+
extensions: dict[str, pydantic.JsonValue] = {}
|
|
123
132
|
|
|
124
133
|
|
|
125
134
|
class Transition(BaseTransition):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|