sera-2 1.14.4__py3-none-any.whl → 1.14.6__py3-none-any.whl

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.
sera/make/__main__.py CHANGED
@@ -30,7 +30,7 @@ def cli(
30
30
  "--collection",
31
31
  help="API collections to generate.",
32
32
  ),
33
- ],
33
+ ] = [],
34
34
  language: Annotated[
35
35
  Language,
36
36
  typer.Option("-l", "--language", help="Language of the generated application"),
@@ -23,6 +23,7 @@ from sera.models import (
23
23
  Schema,
24
24
  TsTypeWithDep,
25
25
  )
26
+ from sera.typing import is_set
26
27
 
27
28
 
28
29
  def make_typescript_data_model(schema: Schema, target_pkg: Package):
@@ -336,8 +337,8 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
336
337
  f"{cls.name}Id",
337
338
  deps=[f"@.models.{pkg.dir.name}.{cls.name}.{cls.name}Id"],
338
339
  )
339
- else:
340
- # for none id properties, we need to include a type for "invalid" value
340
+ elif tstype.type not in schema.enums:
341
+ # for none id & none enum properties, we need to include a type for "invalid" value
341
342
  tstype = _inject_type_for_invalid_value(tstype)
342
343
 
343
344
  if prop.is_optional:
@@ -355,9 +356,15 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
355
356
  and prop.db.is_auto_increment
356
357
  ):
357
358
  create_propvalue = expr.ExprConstant(-1)
359
+ elif is_set(prop.data.default_value):
360
+ create_propvalue = expr.ExprConstant(prop.data.default_value)
358
361
  else:
359
362
  if tstype.type in idprop_aliases:
360
363
  create_propvalue = idprop_aliases[tstype.type].get_default()
364
+ elif tstype.type in schema.enums:
365
+ create_propvalue = expr.ExprConstant(
366
+ next(iter(schema.enums[tstype.type].values.values())).value
367
+ )
361
368
  else:
362
369
  create_propvalue = tstype.get_default()
363
370
 
@@ -537,7 +544,6 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
537
544
  ],
538
545
  )
539
546
  if prop.cardinality.is_star_to_many():
540
- tstype = tstype.as_list_type()
541
547
  create_propvalue = expr.ExprConstant([])
542
548
  update_propvalue = PredefinedFn.map_list(
543
549
  PredefinedFn.attr_getter(
@@ -578,6 +584,7 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
578
584
  ),
579
585
  )
580
586
  )
587
+
581
588
  if not prop.data.is_private:
582
589
  # private property does not include in the public record
583
590
  to_record_args.append(
@@ -612,11 +619,9 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
612
619
  ),
613
620
  )
614
621
  )
622
+
623
+ tstype = tstype.as_list_type()
615
624
  else:
616
- if prop.is_optional:
617
- # convert type to optional - for list type, we don't need to do this
618
- # as we will use empty list as no value
619
- tstype = tstype.as_optional_type()
620
625
  create_propvalue = expr.ExprMethodCall(
621
626
  expr.ExprIdent(tstype.type),
622
627
  "create",
@@ -729,6 +734,11 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
729
734
  )
730
735
  )
731
736
 
737
+ if prop.is_optional:
738
+ # convert type to optional - for list type, we don't need to do this
739
+ # as we will use empty list as no value
740
+ tstype = tstype.as_optional_type()
741
+
732
742
  for dep in tstype.deps:
733
743
  program.import_(
734
744
  dep,
@@ -1407,16 +1417,13 @@ def make_typescript_enum(schema: Schema, target_pkg: Package):
1407
1417
  stmt.LineBreak(),
1408
1418
  lambda ast: ast.class_like("enum", enum.name)(
1409
1419
  *[
1410
- stmt.DefClassVarStatement(
1420
+ stmt.DefEnumValueStatement(
1411
1421
  name=value.name,
1412
- type=None,
1413
1422
  value=expr.ExprConstant(value.value),
1414
1423
  )
1415
1424
  for value in enum.values.values()
1416
1425
  ]
1417
1426
  ),
1418
- stmt.LineBreak(),
1419
- stmt.TypescriptStatement("export " + enum.name + ";"),
1420
1427
  )
1421
1428
  pkg.module(enum.get_tsmodule_name()).write(program)
1422
1429
 
sera/misc/_utils.py CHANGED
@@ -4,6 +4,44 @@ import re
4
4
  from typing import Any, Callable, Iterable, Optional, TypeVar
5
5
 
6
6
  T = TypeVar("T")
7
+ reserved_keywords = {
8
+ "and",
9
+ "or",
10
+ "not",
11
+ "is",
12
+ "in",
13
+ "if",
14
+ "else",
15
+ "elif",
16
+ "for",
17
+ "while",
18
+ "def",
19
+ "class",
20
+ "return",
21
+ "yield",
22
+ "import",
23
+ "from",
24
+ "as",
25
+ "with",
26
+ "try",
27
+ "except",
28
+ "finally",
29
+ "raise",
30
+ "assert",
31
+ "break",
32
+ "continue",
33
+ "pass",
34
+ "del",
35
+ "global",
36
+ "nonlocal",
37
+ "lambda",
38
+ "async",
39
+ "await",
40
+ "True",
41
+ "False",
42
+ "None",
43
+ "self",
44
+ }
7
45
 
8
46
 
9
47
  def to_snake_case(camelcase: str) -> str:
@@ -16,13 +54,21 @@ def to_snake_case(camelcase: str) -> str:
16
54
  def to_camel_case(snake: str) -> str:
17
55
  """Convert snake_case to camelCase."""
18
56
  components = snake.split("_")
19
- return components[0] + "".join(x.title() for x in components[1:])
57
+ out = components[0] + "".join(x.title() for x in components[1:])
58
+ # handle a corner case where the _ is added to the end of the string to avoid reserved keywords
59
+ if snake.endswith("_") and snake[:-1] in reserved_keywords:
60
+ out += "_"
61
+ return out
20
62
 
21
63
 
22
64
  def to_pascal_case(snake: str) -> str:
23
65
  """Convert snake_case to PascalCase."""
24
66
  components = snake.split("_")
25
- return "".join(x.title() for x in components)
67
+ out = "".join(x.title() for x in components)
68
+ # handle a corner case where the _ is added to the end of the string to avoid reserved keywords
69
+ if snake.endswith("_") and snake[:-1] in reserved_keywords:
70
+ out += "_"
71
+ return out
26
72
 
27
73
 
28
74
  def assert_isinstance(x: Any, cls: type[T]) -> T:
sera/models/_class.py CHANGED
@@ -51,6 +51,7 @@ class Class:
51
51
  Get the ID property of this class.
52
52
  The ID property is the one tagged with is_primary_key
53
53
  """
54
+ id_props = []
54
55
  for prop in self.properties.values():
55
56
  if (
56
57
  isinstance(prop, DataProperty)
@@ -60,7 +61,14 @@ class Class:
60
61
  assert (
61
62
  self.db is not None
62
63
  ), "This class is not stored in the database and thus, cannot have a primary key"
63
- return prop
64
+ id_props.append(prop)
65
+ if len(id_props) > 1:
66
+ raise ValueError(
67
+ f"Class {self.name} has more than one primary key property: {', '.join(p.name for p in id_props)}"
68
+ )
69
+ if len(id_props) == 1:
70
+ return id_props[0]
71
+ # if there is no primary key, we return None
64
72
  assert (
65
73
  self.db is None
66
74
  ), f"The class {self.name} is stored in the database and thus, must have a primary key"
sera/models/_datatype.py CHANGED
@@ -82,7 +82,7 @@ class TsTypeWithDep:
82
82
  return expr.ExprConstant(0)
83
83
  if self.type == "boolean":
84
84
  return expr.ExprConstant(False)
85
- if self.type == "string | undefined":
85
+ if self.type.endswith("| undefined"):
86
86
  return expr.ExprConstant("undefined")
87
87
  if self.type.endswith("| string)") or self.type.endswith("| string"):
88
88
  return expr.ExprConstant("")
sera/models/_parse.py CHANGED
@@ -36,6 +36,7 @@ from sera.models._property import (
36
36
  SystemControlledAttrs,
37
37
  )
38
38
  from sera.models._schema import Schema
39
+ from sera.typing import UNSET
39
40
 
40
41
 
41
42
  def parse_schema(name: str, files: Sequence[Path | str]) -> Schema:
@@ -140,6 +141,7 @@ def _parse_property(
140
141
  system_controlled=_parse_system_controlled_attrs(
141
142
  _data.get("system_controlled")
142
143
  ),
144
+ default_value=UNSET if "default_value" not in _data else _data["default_value"],
143
145
  )
144
146
 
145
147
  assert isinstance(prop, dict), prop
@@ -237,7 +239,9 @@ def _parse_datatype(schema: Schema, datatype: dict | str) -> DataType:
237
239
  sqltype=SQLTypeWithDep(
238
240
  type="String", mapped_pytype="str", deps=["sqlalchemy.String"]
239
241
  ),
240
- tstype=TsTypeWithDep(type=enum.name, deps=["@/models/enums"]),
242
+ tstype=TsTypeWithDep(
243
+ type=enum.name, deps=[f"@.models.enums.{enum.name}"]
244
+ ),
241
245
  is_list=is_list,
242
246
  )
243
247
 
sera/models/_property.py CHANGED
@@ -2,12 +2,15 @@ from __future__ import annotations
2
2
 
3
3
  from dataclasses import dataclass, field
4
4
  from enum import Enum
5
- from typing import TYPE_CHECKING, Literal, Optional
5
+ from typing import TYPE_CHECKING, Any, Literal, Optional
6
+
7
+ from msgspec import UNSET
6
8
 
7
9
  from sera.models._constraints import Constraint
8
10
  from sera.models._datatype import DataType
9
11
  from sera.models._default import DefaultFactory
10
12
  from sera.models._multi_lingual_string import MultiLingualString
13
+ from sera.typing import UnsetType
11
14
 
12
15
  if TYPE_CHECKING:
13
16
  from sera.models._class import Class
@@ -111,6 +114,9 @@ class PropDataAttrs:
111
114
  # if this property is controlled by the system, the attributes for the system-controlled property
112
115
  system_controlled: Optional[SystemControlledAttrs] = None
113
116
 
117
+ # default value of this property if it is not provided (then optional is false by default)
118
+ default_value: Any | UnsetType = UNSET
119
+
114
120
 
115
121
  @dataclass(kw_only=True)
116
122
  class Property:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sera-2
3
- Version: 1.14.4
3
+ Version: 1.14.6
4
4
  Summary:
5
5
  Author: Binh Vu
6
6
  Author-email: bvu687@gmail.com
@@ -14,28 +14,28 @@ sera/libs/middlewares/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
14
14
  sera/libs/middlewares/auth.py,sha256=r6aix1ZBwxMd1Jv5hMCTB8a_gFOJQ6egvxIrf3DWEOs,2323
15
15
  sera/libs/middlewares/uscp.py,sha256=H5umW8iEQSCdb_MJ5Im49kxg1E7TpxSg1p2_2A5zI1U,2600
16
16
  sera/make/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
- sera/make/__main__.py,sha256=bt-gDF8E026OWc2zqr9_a3paMOiDkFd3ybWn8ltL2g0,1448
17
+ sera/make/__main__.py,sha256=HRfOR53p351h6KblVvYm3DLhDIfEtk6R0kjl78_S_S8,1453
18
18
  sera/make/make_app.py,sha256=n9NtW73O3s_5Q31VHIRmnd-jEIcpDO7ksAsOdovde2s,5999
19
19
  sera/make/make_python_api.py,sha256=iXGbKQ3IJvsY1ur_fhurr_THFNnH66E3Wl85o0emUbw,26853
20
20
  sera/make/make_python_model.py,sha256=cRb-fuHX0WH7XPAAliu6lycC0iEjE5kcKg4bBU40GwQ,61275
21
21
  sera/make/make_python_services.py,sha256=0ZpWLwQ7Nwfn8BXAikAB4JRpNknpSJyJgY5b1cjtxV4,2073
22
- sera/make/make_typescript_model.py,sha256=ugDdSTw_1ayHLuL--92RQ8hf_D-dpJtnvmUZNxcwcDs,63687
22
+ sera/make/make_typescript_model.py,sha256=4yLCLIzP2jeXfmOeee6nNS_qd7_7iPinV2vQh1IfbLs,64000
23
23
  sera/misc/__init__.py,sha256=Dh4uDq0D4N53h3zhvmwfa5a0TPVRSUvLzb0hkFuPirk,411
24
24
  sera/misc/_formatter.py,sha256=aCGYL08l8f3aLODHxSocxBBwkRYEo3K1QzCDEn3suj0,1685
25
- sera/misc/_utils.py,sha256=V5g4oLGHOhUCR75Kkcn1w01pAvGvaepK-T8Z3pIgHjI,1450
25
+ sera/misc/_utils.py,sha256=vBfbEChf7IMdLDj3CbdOXTUAdWNQTLLpAWwBoUcF3u0,2315
26
26
  sera/models/__init__.py,sha256=vJC5Kzo_N7wd16ocNPy1VvAZDGNiWeiAhWJ4ihATKvA,780
27
- sera/models/_class.py,sha256=9XSnJli2SGF-jm-v0swsfY6-omiQ5Xeh33R2pM9Kg_g,2526
27
+ sera/models/_class.py,sha256=1J4Bd_LanzhhDWwZFHWGtFYD7lupe_alaB3D02ebNDI,2862
28
28
  sera/models/_collection.py,sha256=ZnQEriKC4X88Zz48Kn1AVZKH-1_l8OgWa-zf2kcQOOE,1414
29
29
  sera/models/_constraints.py,sha256=RpWDU-TfCslXaMUaTG9utWbl5z8Z6nzvF_fhqlek6ew,1987
30
- sera/models/_datatype.py,sha256=Lq1iEGqQaNHiG5NlRTcOTWROiUFjoiPoTeSJKFU6lSE,7281
30
+ sera/models/_datatype.py,sha256=cC6Wm0IvobDF5Tq9Jy_ncbjPWBRl3ECGBy0wJyDDMzU,7281
31
31
  sera/models/_default.py,sha256=ABggW6qdPR4ZDqIPJdJ0GCGQ-7kfsfZmQ_DchgZEa-I,137
32
32
  sera/models/_enum.py,sha256=sy0q7E646F-APsqrVQ52r1fAQ_DCAeaNq5YM5QN3zIk,2070
33
33
  sera/models/_module.py,sha256=I-GfnTgAa-5R87qTAvEzOt-VVEGeFBBwubGCgUkXVSw,5159
34
34
  sera/models/_multi_lingual_string.py,sha256=JETN6k00VH4wrA4w5vAHMEJV8fp3SY9bJebskFTjQLA,1186
35
- sera/models/_parse.py,sha256=ohexBbjFv6A1wZsEtokfw0zMVoR-RGXYrXa_tAub_Vs,12147
36
- sera/models/_property.py,sha256=Y98bYIjuCQDcSWpDxERNIFgmxXIaJiwBNFtYeGbDYRE,6812
35
+ sera/models/_parse.py,sha256=MaGZty29lsVUFumWqNanIjAwptBNOMbVZMXLZ2A9_2g,12317
36
+ sera/models/_property.py,sha256=2rSLs9JjSesrxrEugE7krYaBQOivKU882I8mZN94FlI,7017
37
37
  sera/models/_schema.py,sha256=VxJEiqgVvbXgcSUK4UW6JnRcggk4nsooVSE6MyXmfNY,1636
38
38
  sera/typing.py,sha256=Fl4-UzLJu1GdLLk_g87fA7nT9wQGelNnGzag6dg_0gs,980
39
- sera_2-1.14.4.dist-info/METADATA,sha256=PTLgXrXqoFQ74mPm4mgMK3DaBGMh0A45FNuUd40tb94,852
40
- sera_2-1.14.4.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
41
- sera_2-1.14.4.dist-info/RECORD,,
39
+ sera_2-1.14.6.dist-info/METADATA,sha256=XuXxkDt84tayCblVI10S594d0t3e0eg5j66iUCB-PKQ,852
40
+ sera_2-1.14.6.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
41
+ sera_2-1.14.6.dist-info/RECORD,,