sera-2 1.21.2__py3-none-any.whl → 1.24.1__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.
@@ -1,12 +1,14 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import re
4
- from typing import Any, Callable, Optional
4
+ from typing import Any, Callable
5
5
 
6
6
  from codegen.models import AST, ImportHelper, PredefinedFn, Program, expr, stmt
7
7
  from codegen.models.var import DeferredVar
8
8
  from loguru import logger
9
9
 
10
+ from sera.make.ts_frontend.make_class_schema import make_class_schema
11
+ from sera.make.ts_frontend.misc import TS_GLOBAL_IDENTS, get_normalizer
10
12
  from sera.misc import (
11
13
  assert_isinstance,
12
14
  assert_not_null,
@@ -26,13 +28,6 @@ from sera.models import (
26
28
  )
27
29
  from sera.typing import is_set
28
30
 
29
- TS_GLOBAL_IDENTS = {
30
- "normalizers.normalizeNumber": "sera-db.normalizers",
31
- "normalizers.normalizeOptionalNumber": "sera-db.normalizers",
32
- "normalizers.normalizeDate": "sera-db.normalizers",
33
- "normalizers.normalizeOptionalDate": "sera-db.normalizers",
34
- }
35
-
36
31
 
37
32
  def make_typescript_data_model(schema: Schema, target_pkg: Package):
38
33
  """Generate TypeScript data model from the schema. The data model aligns with the public data model in Python, not the database model."""
@@ -440,13 +435,12 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
440
435
  if tstype.type in idprop_aliases:
441
436
  create_propvalue = idprop_aliases[tstype.type].get_default()
442
437
  elif tstype.type in schema.enums:
443
- enum_value = next(
438
+ enum_value_name = next(
444
439
  iter(schema.enums[tstype.type].values.values())
445
- ).value
446
- # TODO: handle enum value integer
447
- assert isinstance(enum_value, str)
440
+ ).name
441
+ assert isinstance(enum_value_name, str), enum_value_name
448
442
  create_propvalue = expr.ExprIdent(
449
- tstype.type + "." + enum_value
443
+ tstype.type + "." + enum_value_name
450
444
  )
451
445
  else:
452
446
  create_propvalue = tstype.get_default()
@@ -1194,311 +1188,6 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
1194
1188
 
1195
1189
  outmod.write(program)
1196
1190
 
1197
- def make_definition(cls: Class, pkg: Package):
1198
- """Make schema definition for the class in frontend so that we can generate components"""
1199
- if not cls.is_public:
1200
- # skip classes that are not public
1201
- return
1202
-
1203
- program = Program()
1204
- prop_defs: list[tuple[DataProperty | ObjectProperty, expr.Expr, expr.Expr]] = []
1205
- prop_normalizers: list[tuple[expr.Expr, expr.Expr]] = []
1206
-
1207
- import_helper = ImportHelper(program, TS_GLOBAL_IDENTS)
1208
-
1209
- for prop in cls.properties.values():
1210
- # we must include private properties that are needed during upsert for our forms.
1211
- # if prop.data.is_private:
1212
- # # skip private fields as this is for APIs exchange
1213
- # continue
1214
- tspropname = to_camel_case(prop.name)
1215
- pypropname = prop.name
1216
- if isinstance(prop, ObjectProperty) and prop.target.db is not None:
1217
- # this is a database object, we append id to the property name
1218
- tspropname = tspropname + "Id"
1219
- pypropname = prop.name + "_id"
1220
-
1221
- tsprop = {}
1222
-
1223
- if isinstance(prop, DataProperty):
1224
- tstype = prop.get_data_model_datatype().get_typescript_type()
1225
- # for schema definition, we need to use the original type, not the type alias
1226
- # if prop.name == idprop.name:
1227
- # # use id type alias
1228
- # tstype = TsTypeWithDep(f"{cls.name}Id")
1229
- for dep in tstype.deps:
1230
- program.import_(dep, True)
1231
- tsprop = [
1232
- (
1233
- expr.ExprIdent("datatype"),
1234
- (
1235
- expr.ExprConstant(tstype.spectype)
1236
- if tstype.type not in schema.enums
1237
- else expr.ExprConstant("enum")
1238
- ),
1239
- ),
1240
- *(
1241
- [(expr.ExprIdent("enumType"), expr.ExprIdent(tstype.type))]
1242
- if tstype.type in schema.enums
1243
- else []
1244
- ),
1245
- (
1246
- expr.ExprIdent("isRequired"),
1247
- expr.ExprConstant(
1248
- not prop.is_optional
1249
- and prop.default_value is None
1250
- and prop.default_factory is None
1251
- ),
1252
- ),
1253
- ]
1254
-
1255
- norm_func = get_normalizer(tstype, import_helper)
1256
- if norm_func is not None:
1257
- # we have a normalizer for this type
1258
- prop_normalizers.append((expr.ExprIdent(tspropname), norm_func))
1259
- else:
1260
- assert isinstance(prop, ObjectProperty)
1261
- if prop.target.db is not None:
1262
- # this class is stored in the database, we store the id instead
1263
- tstype = (
1264
- assert_not_null(prop.target.get_id_property())
1265
- .get_data_model_datatype()
1266
- .get_typescript_type()
1267
- )
1268
- else:
1269
- # we are going to store the whole object
1270
- tstype = TsTypeWithDep(
1271
- type=prop.target.name,
1272
- spectype=prop.target.name,
1273
- deps=[
1274
- f"@.models.{prop.target.get_tsmodule_name()}.{prop.target.name}.{prop.target.name}"
1275
- ],
1276
- )
1277
-
1278
- # we don't store the type itself, but just the name of the type
1279
- # so not need to import the dependency
1280
- # if tstype.dep is not None:
1281
- # program.import_(
1282
- # tstype.dep,
1283
- # True,
1284
- # )
1285
-
1286
- tsprop = [
1287
- (
1288
- expr.ExprIdent("targetClass"),
1289
- expr.ExprConstant(prop.target.name),
1290
- ),
1291
- (
1292
- expr.ExprIdent("datatype"),
1293
- expr.ExprConstant(
1294
- tstype.spectype
1295
- if prop.target.db is not None
1296
- else "undefined"
1297
- ),
1298
- ),
1299
- (
1300
- expr.ExprIdent("cardinality"),
1301
- expr.ExprConstant(prop.cardinality.value),
1302
- ),
1303
- (
1304
- expr.ExprIdent("isEmbedded"),
1305
- expr.ExprConstant(prop.target.db is None),
1306
- ),
1307
- (
1308
- expr.ExprIdent("isRequired"),
1309
- expr.ExprConstant(not prop.is_optional),
1310
- ),
1311
- ]
1312
-
1313
- prop_defs.append(
1314
- (
1315
- prop,
1316
- expr.ExprIdent(tspropname),
1317
- PredefinedFn.dict(
1318
- [
1319
- (expr.ExprIdent("name"), expr.ExprConstant(pypropname)),
1320
- (expr.ExprIdent("tsName"), expr.ExprConstant(tspropname)),
1321
- (
1322
- expr.ExprIdent("updateFuncName"),
1323
- expr.ExprConstant(f"update{to_pascal_case(prop.name)}"),
1324
- ),
1325
- (
1326
- expr.ExprIdent("label"),
1327
- expr.ExprConstant(prop.label.to_dict()),
1328
- ),
1329
- (
1330
- expr.ExprIdent("description"),
1331
- (
1332
- expr.ExprConstant(prop.description.to_dict())
1333
- if not prop.description.is_empty()
1334
- else expr.ExprConstant("undefined")
1335
- ),
1336
- ),
1337
- (
1338
- expr.ExprIdent("constraints"),
1339
- PredefinedFn.list(
1340
- [
1341
- expr.ExprConstant(
1342
- constraint.get_typescript_constraint()
1343
- )
1344
- for constraint in prop.data.constraints
1345
- ]
1346
- ),
1347
- ),
1348
- ]
1349
- + tsprop
1350
- ),
1351
- )
1352
- )
1353
-
1354
- for type in ["ObjectProperty", "DataProperty"]:
1355
- program.import_(f"sera-db.{type}", True)
1356
- if cls.db is not None:
1357
- program.import_(f"sera-db.Schema", True)
1358
- else:
1359
- program.import_(f"sera-db.EmbeddedSchema", True)
1360
-
1361
- program.import_(f"@.models.{pkg.dir.name}.{cls.name}.{cls.name}", True)
1362
- program.import_(
1363
- f"@.models.{pkg.dir.name}.Draft{cls.name}.Draft{cls.name}", True
1364
- )
1365
- program.import_(
1366
- f"@.models.{pkg.dir.name}.Draft{cls.name}.draft{cls.name}Validators", True
1367
- )
1368
- if cls.db is not None:
1369
- program.import_(f"@.models.{pkg.dir.name}.{cls.name}.{cls.name}Id", True)
1370
-
1371
- program.root(
1372
- stmt.LineBreak(),
1373
- stmt.TypescriptStatement(
1374
- f"export type {cls.name}SchemaType = "
1375
- + PredefinedFn.dict(
1376
- (
1377
- [
1378
- (expr.ExprIdent("id"), expr.ExprIdent(f"{cls.name}Id")),
1379
- ]
1380
- if cls.db is not None
1381
- else []
1382
- )
1383
- + [
1384
- (
1385
- expr.ExprIdent("publicProperties"),
1386
- expr.ExprIdent(
1387
- " | ".join(
1388
- [
1389
- expr.ExprConstant(
1390
- to_camel_case(prop.name) + "Id"
1391
- if isinstance(prop, ObjectProperty)
1392
- and prop.target.db is not None
1393
- else to_camel_case(prop.name)
1394
- ).to_typescript()
1395
- for prop in cls.properties.values()
1396
- if not prop.data.is_private
1397
- ]
1398
- )
1399
- ),
1400
- ),
1401
- (
1402
- expr.ExprIdent("allProperties"),
1403
- expr.ExprIdent(
1404
- f"{cls.name}SchemaType['publicProperties']"
1405
- + (
1406
- " | "
1407
- + " | ".join(
1408
- [
1409
- expr.ExprConstant(
1410
- to_camel_case(prop.name)
1411
- ).to_typescript()
1412
- for prop in cls.properties.values()
1413
- if prop.data.is_private
1414
- ]
1415
- )
1416
- if any(
1417
- prop.data.is_private
1418
- for prop in cls.properties.values()
1419
- )
1420
- else ""
1421
- )
1422
- ),
1423
- ),
1424
- (
1425
- expr.ExprIdent("cls"),
1426
- expr.ExprIdent(cls.name),
1427
- ),
1428
- (
1429
- expr.ExprIdent("draftCls"),
1430
- expr.ExprIdent(f"Draft{cls.name}"),
1431
- ),
1432
- ]
1433
- ).to_typescript()
1434
- + ";",
1435
- ),
1436
- stmt.LineBreak(),
1437
- stmt.TypescriptStatement(
1438
- f"const publicProperties: Record<{cls.name}SchemaType['publicProperties'], DataProperty | ObjectProperty> = "
1439
- + PredefinedFn.dict(
1440
- [
1441
- (prop_name, prop_def)
1442
- for prop, prop_name, prop_def in prop_defs
1443
- if not prop.data.is_private
1444
- ]
1445
- ).to_typescript()
1446
- + ";"
1447
- ),
1448
- stmt.LineBreak(),
1449
- stmt.TypescriptStatement(
1450
- (
1451
- f"export const {cls.name}Schema: Schema<{cls.name}SchemaType['id'], {cls.name}SchemaType['cls'], {cls.name}SchemaType['draftCls'], {cls.name}SchemaType['publicProperties'], {cls.name}SchemaType['allProperties'], {cls.name}SchemaType> = "
1452
- if cls.db is not None
1453
- else f"export const {cls.name}Schema: EmbeddedSchema<{cls.name}SchemaType['cls'], {cls.name}SchemaType['draftCls'], {cls.name}SchemaType['publicProperties'], {cls.name}SchemaType['allProperties']> = "
1454
- )
1455
- + PredefinedFn.dict(
1456
- [
1457
- (
1458
- expr.ExprIdent("publicProperties"),
1459
- expr.ExprIdent("publicProperties"),
1460
- ),
1461
- (
1462
- expr.ExprIdent("allProperties"),
1463
- expr.ExprIdent(
1464
- "{ ...publicProperties, "
1465
- + ", ".join(
1466
- [
1467
- f"{prop_name.to_typescript()}: {prop_def.to_typescript()}"
1468
- for prop, prop_name, prop_def in prop_defs
1469
- if prop.data.is_private
1470
- ]
1471
- )
1472
- + "}"
1473
- ),
1474
- ),
1475
- (
1476
- expr.ExprIdent("validators"),
1477
- expr.ExprIdent(f"draft{cls.name}Validators"),
1478
- ),
1479
- (
1480
- expr.ExprIdent("normalizers"),
1481
- PredefinedFn.dict(prop_normalizers),
1482
- ),
1483
- ]
1484
- + (
1485
- [
1486
- (
1487
- expr.ExprIdent("primaryKey"),
1488
- expr.ExprConstant(
1489
- assert_not_null(cls.get_id_property()).name
1490
- ),
1491
- )
1492
- ]
1493
- if cls.db is not None
1494
- else []
1495
- )
1496
- ).to_typescript()
1497
- + ";"
1498
- ),
1499
- )
1500
- pkg.module(cls.name + "Schema").write(program)
1501
-
1502
1191
  def make_index(pkg: Package):
1503
1192
  outmod = pkg.module("index")
1504
1193
  if outmod.exists():
@@ -1554,7 +1243,7 @@ def make_typescript_data_model(schema: Schema, target_pkg: Package):
1554
1243
  make_draft(cls, pkg)
1555
1244
  make_query_processor(cls, pkg)
1556
1245
  make_table(cls, pkg)
1557
- make_definition(cls, pkg)
1246
+ make_class_schema(schema, cls, pkg)
1558
1247
 
1559
1248
  make_index(pkg)
1560
1249
 
@@ -1618,14 +1307,19 @@ def _inject_type_for_invalid_value(tstype: TsTypeWithDep) -> TsTypeWithDep:
1618
1307
  if m is not None:
1619
1308
  # This is an array type, add string to the inner type
1620
1309
  inner_type = m.group(1)
1310
+ inner_spectype = assert_not_null(
1311
+ re.match(r"(\(?[a-zA-Z \|]+\)?)(\[\])", tstype.spectype)
1312
+ ).group(1)
1621
1313
  if "string" not in inner_type:
1622
1314
  if inner_type.startswith("(") and inner_type.endswith(")"):
1623
1315
  # Already has parentheses
1624
1316
  inner_type = f"{inner_type[:-1]} | string)"
1317
+ inner_spectype = f"{inner_spectype[:-1]} | string)"
1625
1318
  else:
1626
1319
  # Need to add parentheses
1627
1320
  inner_type = f"({inner_type} | string)"
1628
- return TsTypeWithDep(inner_type + "[]", tstype.deps)
1321
+ inner_spectype = f"({inner_spectype} | string)"
1322
+ return TsTypeWithDep(inner_type + "[]", inner_spectype + "[]", tstype.deps)
1629
1323
 
1630
1324
  m = re.match(r"^\(?[a-zA-Z \|]+\)?$", tstype.type)
1631
1325
  if m is not None:
@@ -1633,31 +1327,17 @@ def _inject_type_for_invalid_value(tstype: TsTypeWithDep) -> TsTypeWithDep:
1633
1327
  if tstype.type.startswith("(") and tstype.type.endswith(")"):
1634
1328
  # Already has parentheses
1635
1329
  new_type = f"{tstype.type[:-1]} | string)"
1330
+ new_spectype = f"{tstype.spectype[:-1]} | string)"
1636
1331
  else:
1637
1332
  # Needs parentheses for clarity
1638
1333
  new_type = f"({tstype.type} | string)"
1639
- return TsTypeWithDep(new_type, tstype.deps)
1334
+ new_spectype = f"({tstype.spectype} | string)"
1335
+ return TsTypeWithDep(new_type, new_spectype, tstype.deps)
1640
1336
  return tstype
1641
1337
 
1642
1338
  raise NotImplementedError(tstype.type)
1643
1339
 
1644
1340
 
1645
- def get_normalizer(
1646
- tstype: TsTypeWithDep, import_helper: ImportHelper
1647
- ) -> Optional[expr.ExprIdent]:
1648
- if tstype.type == "number":
1649
- return import_helper.use("normalizers.normalizeNumber")
1650
- if tstype.type == "number | undefined":
1651
- return import_helper.use("normalizers.normalizeOptionalNumber")
1652
- if tstype.type == "Date":
1653
- return import_helper.use("normalizers.normalizeDate")
1654
- if tstype.type == "Date | undefined":
1655
- return import_helper.use("normalizers.normalizeOptionalDate")
1656
-
1657
- assert "number" not in tstype.type, tstype.type
1658
- return None
1659
-
1660
-
1661
1341
  def get_norm_func(
1662
1342
  tstype: TsTypeWithDep, import_helper: ImportHelper
1663
1343
  ) -> Callable[[expr.Expr], expr.Expr]:
File without changes