geniespec 0.1.2.dev4__tar.gz → 0.1.2.dev5__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.
Files changed (22) hide show
  1. {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev5}/PKG-INFO +3 -1
  2. {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev5}/README.md +2 -0
  3. {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev5}/pyproject.toml +1 -1
  4. {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev5}/pyproject.toml.orig +1 -1
  5. {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev5}/src/geniespec/api_schema.yaml +3 -3
  6. {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev5}/src/geniespec/languages.py +27 -4
  7. {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev5}/src/geniespec/templates/python-server/{service}/v{version}/endpoints.py.j2 +3 -0
  8. {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev5}/src/geniespec/templates/python-server/{service}/v{version}/exceptions.py.j2 +3 -0
  9. {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev5}/src/geniespec/templates/python-server/{service}/v{version}/internal.py.j2 +3 -0
  10. {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev5}/src/geniespec/templates/python-server/{service}/v{version}/resources.py.j2 +3 -0
  11. {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev5}/src/geniespec/types.py +2 -1
  12. {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev5}/src/geniespec/__init__.py +0 -0
  13. {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev5}/src/geniespec/cli.py +0 -0
  14. {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev5}/src/geniespec/genie.py +0 -0
  15. {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev5}/src/geniespec/renderable_types.py +0 -0
  16. {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev5}/src/geniespec/templates/python-server/{service}/__init__.py.j2 +0 -0
  17. {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev5}/src/geniespec/templates/python-server/{service}/v{version}/__init__.py.j2 +0 -0
  18. {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev5}/src/geniespec/templates/python-server/{service}/v{version}/api.py.j2 +0 -0
  19. {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev5}/src/geniespec/templates/typescript-client/{service}/v{version}/client.ts.j2 +0 -0
  20. {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev5}/src/geniespec/templates/typescript-client/{service}/v{version}/endpoints.ts.j2 +0 -0
  21. {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev5}/src/geniespec/templates/typescript-client/{service}/v{version}/index.ts.j2 +0 -0
  22. {geniespec-0.1.2.dev4 → geniespec-0.1.2.dev5}/src/geniespec/templates/typescript-client/{service}/v{version}/resources.ts.j2 +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: geniespec
3
- Version: 0.1.2.dev4
3
+ Version: 0.1.2.dev5
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:
@@ -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:
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "geniespec"
3
- version = "0.1.2.dev4"
3
+ version = "0.1.2.dev5"
4
4
  description = "Generate API code from an api.yaml spec"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "geniespec"
3
- version = "0.1.2.dev4"
3
+ version = "0.1.2.dev5"
4
4
  description = "Generate API code from an api.yaml spec"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -48,7 +48,7 @@ properties:
48
48
  type:
49
49
  - "null"
50
50
  - string
51
- pattern: '^[a-z][a-z0-9-]*$'
51
+ pattern: '^[a-z][a-z0-9_-]*$'
52
52
  rateLimits:
53
53
  type: array
54
54
  items:
@@ -230,7 +230,7 @@ definitions:
230
230
  type:
231
231
  - "null"
232
232
  - string
233
- pattern: '^[a-z][a-z0-9-]*$'
233
+ pattern: '^[a-z][a-z0-9_-]*$'
234
234
  rateLimit:
235
235
  type:
236
236
  - "null"
@@ -266,7 +266,7 @@ definitions:
266
266
  type:
267
267
  - "null"
268
268
  - string
269
- pattern: '^[a-z][a-z0-9-]*$'
269
+ pattern: '^[a-z][a-z0-9_-]*$'
270
270
  rateLimit:
271
271
  type:
272
272
  - "null"
@@ -73,6 +73,9 @@ class BaseLanguageDefinition:
73
73
  def string_type(self, isNullable: bool) -> str:
74
74
  raise NotImplementedError
75
75
 
76
+ def uuid_type(self, isNullable: bool) -> str:
77
+ raise NotImplementedError
78
+
76
79
  def enum_type(self, isNullable: bool, renderableType: RenderableType, apiVersion: Version, isLocalReference: bool) -> str:
77
80
  raise NotImplementedError
78
81
 
@@ -110,6 +113,8 @@ class BaseLanguageDefinition:
110
113
  return self.double_type(isNullable=isNullable)
111
114
  if yamlType == PrimitiveTypes.STRING:
112
115
  return self.string_type(isNullable=isNullable)
116
+ if yamlType == PrimitiveTypes.UUID:
117
+ return self.uuid_type(isNullable=isNullable)
113
118
  if yamlType == PrimitiveTypes.BOOLEAN:
114
119
  return self.boolean_type(isNullable=isNullable)
115
120
  if yamlType == PrimitiveTypes.DATETIME:
@@ -227,9 +232,9 @@ class BaseLanguageDefinition:
227
232
  return output
228
233
  if component.attribute.isUnique and not component.attribute.isPrimary:
229
234
  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 string.')
232
- output.append(self.path_parameter_segment(parameterName=self.identifier(name=component.attribute.name), isInteger=component.attribute.fieldType.rawType != PrimitiveTypes.STRING))
235
+ if component.attribute.fieldType.rawType not in (PrimitiveTypes.INTEGER, PrimitiveTypes.LONG_INTEGER, PrimitiveTypes.STRING, PrimitiveTypes.UUID):
236
+ raise KibaException(message=f'ResourceEndpointComponent created with an invalid attribute type of {component.attribute.fieldType}. Valid types are: integer, long integer, string and uuid.')
237
+ 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
238
  return output
234
239
  raise KibaException(message=f'Unsupported endpoint component: {type(component)}')
235
240
 
@@ -343,6 +348,9 @@ class PythonLanguageDefinition(BaseLanguageDefinition):
343
348
  def string_type(self, isNullable: bool) -> str:
344
349
  return 'str | None' if isNullable else 'str'
345
350
 
351
+ def uuid_type(self, isNullable: bool) -> str:
352
+ return 'uuid.UUID | None' if isNullable else 'uuid.UUID'
353
+
346
354
  def enum_type(self, isNullable: bool, renderableType: RenderableType, apiVersion: Version, isLocalReference: bool) -> str:
347
355
  if renderableType.className is None:
348
356
  typeName = self.enum_literal(values=renderableType.enumValues)
@@ -423,6 +431,18 @@ class PythonLanguageDefinition(BaseLanguageDefinition):
423
431
  return PythonLanguageDefinition._contains_json(renderableType=renderableType.keyType) or PythonLanguageDefinition._contains_json(renderableType=renderableType.valueType)
424
432
  return renderableType.rawType == PrimitiveTypes.JSON
425
433
 
434
+ @staticmethod
435
+ def _contains_uuid(renderableType: RenderableType) -> bool:
436
+ if isinstance(renderableType, MappingRenderableType):
437
+ return PythonLanguageDefinition._contains_uuid(renderableType=renderableType.keyType) or PythonLanguageDefinition._contains_uuid(renderableType=renderableType.valueType)
438
+ return renderableType.rawType == PrimitiveTypes.UUID
439
+
440
+ def requires_uuid(self, api: RenderableApi) -> bool:
441
+ fieldTypes = [attribute.fieldType for resource in api.resources for attribute in [*resource.attributes, *resource.independentAttributes]]
442
+ 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 []]
443
+ fieldTypes += [field.fieldType for exception in api.exceptions for field in exception.fields]
444
+ return any(self._contains_uuid(renderableType=fieldType) for fieldType in fieldTypes)
445
+
426
446
  def resources_require_json(self, api: RenderableApi) -> bool:
427
447
  return any(self._contains_json(renderableType=attribute.fieldType) for resource in api.resources for attribute in resource.attributes)
428
448
 
@@ -562,6 +582,9 @@ class TypeScriptLanguageDefinition(BaseLanguageDefinition):
562
582
  def string_type(self, isNullable: bool) -> str:
563
583
  return self._nullable(typeName='string', isNullable=isNullable)
564
584
 
585
+ def uuid_type(self, isNullable: bool) -> str:
586
+ return self._nullable(typeName='string', isNullable=isNullable)
587
+
565
588
  def enum_type(self, isNullable: bool, renderableType: RenderableType, apiVersion: Version, isLocalReference: bool) -> str:
566
589
  if renderableType.className is None:
567
590
  return self._nullable(typeName=f'({self.enum_union(values=renderableType.enumValues)})', isNullable=isNullable)
@@ -680,7 +703,7 @@ class TypeScriptLanguageDefinition(BaseLanguageDefinition):
680
703
  return f'{parserName if isLocalReference else f"Resources.{parserName}"}({value} as RawObject)'
681
704
  if renderableType.rawType == 'class' and renderableType.className is not None:
682
705
  return f'{self.type(renderableType=renderableType, apiVersion=apiVersion, isLocalReference=isLocalReference)}.fromObject({value} as RawObject)'
683
- if renderableType.rawType == PrimitiveTypes.STRING:
706
+ if renderableType.rawType in (PrimitiveTypes.STRING, PrimitiveTypes.UUID):
684
707
  return f'String({value})'
685
708
  if renderableType.rawType in (PrimitiveTypes.INTEGER, PrimitiveTypes.LONG_INTEGER, PrimitiveTypes.FLOAT, PrimitiveTypes.DOUBLE):
686
709
  return f'Number({value})'
@@ -4,6 +4,9 @@ import datetime
4
4
  {% if target.requires_optional_field_default(api=api) or target.operations_require_typing(api=api) %}
5
5
  import typing
6
6
  {% endif %}
7
+ {% if target.requires_uuid(api=api) %}
8
+ import uuid
9
+ {% endif %}
7
10
 
8
11
  {% if target.operations_require_json(api=api) %}
9
12
  import pydantic
@@ -2,6 +2,9 @@
2
2
  from __future__ import annotations
3
3
  import collections.abc
4
4
  import typing
5
+ {% if target.requires_uuid(api=api) %}
6
+ import uuid
7
+ {% endif %}
5
8
  {% if target.exceptions_require_json(api=api) %}
6
9
  import pydantic
7
10
  {% endif %}
@@ -5,6 +5,9 @@ import collections.abc
5
5
  {% if target.operations_require_typing(api=api) %}
6
6
  import typing
7
7
  {% endif %}
8
+ {% if target.requires_uuid(api=api) %}
9
+ import uuid
10
+ {% endif %}
8
11
 
9
12
  {% if target.operations_require_json(api=api) %}
10
13
  import pydantic
@@ -6,6 +6,9 @@ import datetime
6
6
  {% if target.resources_require_typing(api=api) %}
7
7
  import typing
8
8
  {% endif %}
9
+ {% if target.requires_uuid(api=api) %}
10
+ import uuid
11
+ {% endif %}
9
12
 
10
13
  {% if target.resources_require_json(api=api) or api.oneOfs %}
11
14
  import pydantic
@@ -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: