geniespec 0.1.2.dev7__tar.gz → 0.1.2.dev8__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.dev7 → geniespec-0.1.2.dev8}/PKG-INFO +3 -1
  2. {geniespec-0.1.2.dev7 → geniespec-0.1.2.dev8}/README.md +2 -0
  3. {geniespec-0.1.2.dev7 → geniespec-0.1.2.dev8}/pyproject.toml +1 -1
  4. {geniespec-0.1.2.dev7 → geniespec-0.1.2.dev8}/pyproject.toml.orig +1 -1
  5. {geniespec-0.1.2.dev7 → geniespec-0.1.2.dev8}/src/geniespec/languages.py +27 -7
  6. {geniespec-0.1.2.dev7 → geniespec-0.1.2.dev8}/src/geniespec/templates/python-server/{service}/v{version}/resources.py.j2 +5 -1
  7. {geniespec-0.1.2.dev7 → geniespec-0.1.2.dev8}/src/geniespec/types.py +2 -1
  8. {geniespec-0.1.2.dev7 → geniespec-0.1.2.dev8}/src/geniespec/__init__.py +0 -0
  9. {geniespec-0.1.2.dev7 → geniespec-0.1.2.dev8}/src/geniespec/api_schema.yaml +0 -0
  10. {geniespec-0.1.2.dev7 → geniespec-0.1.2.dev8}/src/geniespec/cli.py +0 -0
  11. {geniespec-0.1.2.dev7 → geniespec-0.1.2.dev8}/src/geniespec/genie.py +0 -0
  12. {geniespec-0.1.2.dev7 → geniespec-0.1.2.dev8}/src/geniespec/renderable_types.py +0 -0
  13. {geniespec-0.1.2.dev7 → geniespec-0.1.2.dev8}/src/geniespec/templates/python-server/{service}/__init__.py.j2 +0 -0
  14. {geniespec-0.1.2.dev7 → geniespec-0.1.2.dev8}/src/geniespec/templates/python-server/{service}/v{version}/__init__.py.j2 +0 -0
  15. {geniespec-0.1.2.dev7 → geniespec-0.1.2.dev8}/src/geniespec/templates/python-server/{service}/v{version}/api.py.j2 +0 -0
  16. {geniespec-0.1.2.dev7 → geniespec-0.1.2.dev8}/src/geniespec/templates/python-server/{service}/v{version}/endpoints.py.j2 +0 -0
  17. {geniespec-0.1.2.dev7 → geniespec-0.1.2.dev8}/src/geniespec/templates/python-server/{service}/v{version}/exceptions.py.j2 +0 -0
  18. {geniespec-0.1.2.dev7 → geniespec-0.1.2.dev8}/src/geniespec/templates/python-server/{service}/v{version}/internal.py.j2 +0 -0
  19. {geniespec-0.1.2.dev7 → geniespec-0.1.2.dev8}/src/geniespec/templates/typescript-client/{service}/v{version}/client.ts.j2 +0 -0
  20. {geniespec-0.1.2.dev7 → geniespec-0.1.2.dev8}/src/geniespec/templates/typescript-client/{service}/v{version}/endpoints.ts.j2 +0 -0
  21. {geniespec-0.1.2.dev7 → geniespec-0.1.2.dev8}/src/geniespec/templates/typescript-client/{service}/v{version}/index.ts.j2 +0 -0
  22. {geniespec-0.1.2.dev7 → geniespec-0.1.2.dev8}/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.dev7
3
+ Version: 0.1.2.dev8
4
4
  Summary: Generate API code from an api.yaml spec
5
5
  License-Expression: MIT
6
6
  Requires-Dist: jinja2>=3.1.6
@@ -70,6 +70,8 @@ Fields use it like any other type (`type: ViewContent`, also inside `List` and `
70
70
 
71
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
72
 
73
+ `type: BigInt` is an integer of any size that travels as a JSON string, because JSON numbers past 2^53 lose precision in JavaScript. `python-server` types it as `int` (a `BigInt` alias in `resources`), accepts `"123"` or `123` in requests, always writes it as a string and documents it as `{"type": "string"}`; `typescript-client` types it as `bigint`, parsed with `BigInt(...)` and sent with `String(...)`.
74
+
73
75
  ## Rate limits
74
76
 
75
77
  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:
@@ -56,6 +56,8 @@ Fields use it like any other type (`type: ViewContent`, also inside `List` and `
56
56
 
57
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
58
 
59
+ `type: BigInt` is an integer of any size that travels as a JSON string, because JSON numbers past 2^53 lose precision in JavaScript. `python-server` types it as `int` (a `BigInt` alias in `resources`), accepts `"123"` or `123` in requests, always writes it as a string and documents it as `{"type": "string"}`; `typescript-client` types it as `bigint`, parsed with `BigInt(...)` and sent with `String(...)`.
60
+
59
61
  ## Rate limits
60
62
 
61
63
  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.dev7"
3
+ version = "0.1.2.dev8"
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.dev7"
3
+ version = "0.1.2.dev8"
4
4
  description = "Generate API code from an api.yaml spec"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -78,6 +78,9 @@ class BaseLanguageDefinition:
78
78
  def uuid_type(self, isNullable: bool) -> str:
79
79
  raise NotImplementedError
80
80
 
81
+ def big_int_type(self, isNullable: bool, isLocalReference: bool) -> str:
82
+ raise NotImplementedError
83
+
81
84
  def enum_type(self, isNullable: bool, renderableType: RenderableType, apiVersion: Version, isLocalReference: bool) -> str:
82
85
  raise NotImplementedError
83
86
 
@@ -117,6 +120,8 @@ class BaseLanguageDefinition:
117
120
  return self.string_type(isNullable=isNullable)
118
121
  if yamlType == PrimitiveTypes.UUID:
119
122
  return self.uuid_type(isNullable=isNullable)
123
+ if yamlType == PrimitiveTypes.BIG_INT:
124
+ return self.big_int_type(isNullable=isNullable, isLocalReference=isLocalReference)
120
125
  if yamlType == PrimitiveTypes.BOOLEAN:
121
126
  return self.boolean_type(isNullable=isNullable)
122
127
  if yamlType == PrimitiveTypes.DATETIME:
@@ -353,6 +358,10 @@ class PythonLanguageDefinition(BaseLanguageDefinition):
353
358
  def uuid_type(self, isNullable: bool) -> str:
354
359
  return 'uuid.UUID | None' if isNullable else 'uuid.UUID'
355
360
 
361
+ def big_int_type(self, isNullable: bool, isLocalReference: bool) -> str:
362
+ typeName = 'BigInt' if isLocalReference else 'resources.BigInt'
363
+ return f'{typeName} | None' if isNullable else typeName
364
+
356
365
  def enum_type(self, isNullable: bool, renderableType: RenderableType, apiVersion: Version, isLocalReference: bool) -> str:
357
366
  if renderableType.className is None:
358
367
  typeName = self.enum_literal(values=renderableType.enumValues)
@@ -412,10 +421,6 @@ class PythonLanguageDefinition(BaseLanguageDefinition):
412
421
  hasResponseDescriptions = any(field.description is not None for operation in api.operations for field in operation.responseFields or [])
413
422
  return self.requires_optional_field_default(api=api) or hasRequestDescriptions or hasResponseDescriptions
414
423
 
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
424
  @staticmethod
420
425
  def _field_default(default: str | None, description: str | None, fieldFunction: str = 'Field', shouldUseField: bool = False) -> str:
421
426
  if description is None and not shouldUseField:
@@ -487,13 +492,21 @@ class PythonLanguageDefinition(BaseLanguageDefinition):
487
492
  'collections': lambda part: part.isList or isinstance(part, MappingRenderableType),
488
493
  'datetime': lambda part: part.rawType in (PrimitiveTypes.DATETIME, PrimitiveTypes.DATE),
489
494
  'json': lambda part: part.rawType == PrimitiveTypes.JSON,
490
- 'resources': lambda part: part.className is not None,
495
+ 'resources': lambda part: part.className is not None or part.rawType == PrimitiveTypes.BIG_INT,
491
496
  'uuid': lambda part: part.rawType == PrimitiveTypes.UUID,
492
497
  }
493
498
  return any(predicates[requirement](part) for fieldType in self._module_field_types(api=api, module=module) for part in self._type_parts(renderableType=fieldType))
494
499
 
500
+ def requires_big_int(self, api: RenderableApi) -> bool:
501
+ return any(part.rawType == PrimitiveTypes.BIG_INT for module in ('resources', 'internal', 'exceptions') for fieldType in self._module_field_types(api=api, module=module) for part in self._type_parts(renderableType=fieldType))
502
+
495
503
  def resources_require_typing(self, api: RenderableApi) -> bool:
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)
504
+ hasInlineEnums = any(self.is_inline_enum(renderableType=attribute.fieldType) for resource in api.resources for attribute in resource.attributes)
505
+ return len(api.enums) > 0 or len(api.oneOfs) > 0 or hasInlineEnums or self.requires_big_int(api=api)
506
+
507
+ def resources_require_pydantic(self, api: RenderableApi) -> bool:
508
+ hasDescriptions = any(attribute.description is not None for resource in api.resources for attribute in resource.attributes)
509
+ return len(api.oneOfs) > 0 or hasDescriptions or self.module_requires(api=api, module='resources', requirement='json') or self.requires_big_int(api=api)
497
510
 
498
511
  def one_of_alias(self, oneOf: RenderableOneOf, apiVersion: Version) -> str:
499
512
  return f'typing.Annotated[{" | ".join(self.one_of_member_class_names(oneOf=oneOf, apiVersion=apiVersion))}, pydantic.Field(discriminator={self.identifier(name=oneOf.discriminator)!r})]'
@@ -602,6 +615,9 @@ class TypeScriptLanguageDefinition(BaseLanguageDefinition):
602
615
  def long_integer_type(self, isNullable: bool) -> str:
603
616
  return self._nullable(typeName='number', isNullable=isNullable)
604
617
 
618
+ def big_int_type(self, isNullable: bool, isLocalReference: bool) -> str: # noqa: ARG002
619
+ return self._nullable(typeName='bigint', isNullable=isNullable)
620
+
605
621
  def mapping_type(self, isNullable: bool, keyType: str, valueType: str) -> str: # noqa: ARG002
606
622
  # NOTE(krishan711): json object keys are always strings
607
623
  return self._nullable(typeName=f'Record<string, {valueType}>', isNullable=isNullable)
@@ -752,6 +768,8 @@ class TypeScriptLanguageDefinition(BaseLanguageDefinition):
752
768
  return f'String({value})'
753
769
  if renderableType.rawType in (PrimitiveTypes.INTEGER, PrimitiveTypes.LONG_INTEGER, PrimitiveTypes.FLOAT, PrimitiveTypes.DOUBLE):
754
770
  return f'Number({value})'
771
+ if renderableType.rawType == PrimitiveTypes.BIG_INT:
772
+ return f'BigInt(String({value}))'
755
773
  if renderableType.rawType == PrimitiveTypes.BOOLEAN:
756
774
  return f'Boolean({value})'
757
775
  if renderableType.rawType in (PrimitiveTypes.DATE, PrimitiveTypes.DATETIME):
@@ -765,7 +783,7 @@ class TypeScriptLanguageDefinition(BaseLanguageDefinition):
765
783
  def _is_serialized_as_is(self, renderableType: RenderableType) -> bool:
766
784
  if isinstance(renderableType, MappingRenderableType):
767
785
  return self._is_serialized_as_is(renderableType=renderableType.valueType)
768
- return renderableType.rawType not in ('class', RenderableRawTypes.ONE_OF, PrimitiveTypes.DATE, PrimitiveTypes.DATETIME)
786
+ return renderableType.rawType not in ('class', RenderableRawTypes.ONE_OF, PrimitiveTypes.DATE, PrimitiveTypes.DATETIME, PrimitiveTypes.BIG_INT)
769
787
 
770
788
  def to_object(self, renderableType: RenderableType, value: str, apiVersion: Version, isOptional: bool = False, isLocalReference: bool = False) -> str:
771
789
  if self._is_serialized_as_is(renderableType=renderableType):
@@ -787,4 +805,6 @@ class TypeScriptLanguageDefinition(BaseLanguageDefinition):
787
805
  return f'dateToString({value})'
788
806
  if renderableType.rawType == PrimitiveTypes.DATE:
789
807
  return f"dateToString({value}, 'yyyy-MM-dd')"
808
+ if renderableType.rawType == PrimitiveTypes.BIG_INT:
809
+ return f'String({value})'
790
810
  raise KibaException(f'Failed to render conversion to object for: {renderableType}')
@@ -14,13 +14,17 @@ import typing
14
14
  import uuid
15
15
  {% endif %}
16
16
 
17
- {% if target.module_requires(api=api, module='resources', requirement='json') or api.oneOfs or target.resources_require_field(api=api) %}
17
+ {% if target.resources_require_pydantic(api=api) %}
18
18
  import pydantic
19
19
  {% endif %}
20
20
  {% if api.resources %}
21
21
  from pydantic import BaseModel
22
22
  {% endif %}
23
23
 
24
+ {% if target.requires_big_int(api=api) %}
25
+ BigInt = typing.Annotated[int, pydantic.PlainSerializer(str, return_type=str), pydantic.WithJsonSchema({'type': 'string'})]
26
+
27
+ {% endif %}
24
28
  {% for enum in api.enums %}
25
29
  {{ target.enum_class_name(enum=enum, apiVersion=api.version) }} = {{ target.enum_literal(values=enum.values) }}
26
30
  {% if loop.last %}
@@ -13,11 +13,12 @@ class PrimitiveTypes:
13
13
  DOUBLE = 'Double'
14
14
  STRING = 'String'
15
15
  UUID = 'Uuid'
16
+ BIG_INT = 'BigInt'
16
17
  BOOLEAN = 'Boolean'
17
18
  DATETIME = 'Datetime'
18
19
  DATE = 'Date'
19
20
  JSON = 'Json'
20
- ALL: ClassVar[list[str]] = [LONG_INTEGER, INTEGER, FLOAT, DOUBLE, STRING, UUID, BOOLEAN, DATETIME, DATE, JSON]
21
+ ALL: ClassVar[list[str]] = [LONG_INTEGER, INTEGER, FLOAT, DOUBLE, STRING, UUID, BIG_INT, BOOLEAN, DATETIME, DATE, JSON]
21
22
 
22
23
 
23
24
  class FieldSources: