@@ -857,7 +857,7 @@ def get_interface_datatype(
857
857
field ,
858
858
sdk_language ,
859
859
class_name = "" ,
860
- reference_class_name_postfix = "" ,
860
+ reference_class_name_suffix = "" ,
861
861
enhanced_fields = False ,
862
862
nullable = True ,
863
863
):
@@ -877,7 +877,7 @@ def get_interface_datatype(
877
877
878
878
return maybe_null (client_datatype , sdk_language )
879
879
class_name_postfix = generate_identifier (value = field .get ("name" ), primitive = "class" , existing_identifiers = [])
880
- return f"I{ class_name } { reference_class_name_postfix } { class_name_postfix } "
880
+ return f"I{ class_name } { reference_class_name_suffix } { class_name_postfix } "
881
881
882
882
883
883
def datatype_is_primitive (client_datatype , sdk_language ):
@@ -940,9 +940,6 @@ def field_has_row_ownership(field, obj):
940
940
941
941
942
942
def field_is_required (field , obj ):
943
- if field .get ("lev" ) != 1 :
944
- return False
945
-
946
943
db_column_info = field .get ("db_column" )
947
944
948
945
if db_column_info is None :
@@ -1015,10 +1012,14 @@ def get_reduced_field_interface_datatype(field, fields, sdk_language, class_name
1015
1012
1016
1013
# If the reference mapping is "to_many", use an array
1017
1014
ref_mapping = obj_ref .get ("reference_mapping" )
1018
- is_array = "[]" if ref_mapping and ref_mapping .get (
1019
- "to_many" ) == True else ""
1015
+ is_array = ref_mapping and ref_mapping .get ("to_many" , False )
1020
1016
1021
- return datatype + is_array
1017
+ if is_array and sdk_language == "Python" :
1018
+ return f"list[{ datatype } ]"
1019
+ if is_array :
1020
+ return f"{ datatype } []"
1021
+
1022
+ return datatype
1022
1023
1023
1024
return None
1024
1025
@@ -1033,6 +1034,8 @@ def generate_type_declaration(
1033
1034
requires_placeholder = False ,
1034
1035
is_unpacked = False ,
1035
1036
readonly_fields : set [str ] = set (),
1037
+ nesting_fields : set [str ] = set (),
1038
+ nested_value_prefix : str = "" ,
1036
1039
):
1037
1040
if len (fields ) == 0 :
1038
1041
if not requires_placeholder :
@@ -1042,7 +1045,7 @@ def generate_type_declaration(
1042
1045
field_block = [
1043
1046
generate_type_declaration_field (
1044
1047
name ,
1045
- value ,
1048
+ f"I { nested_value_prefix } { value . lstrip ( "I" ) } " if name in nesting_fields else value ,
1046
1049
sdk_language ,
1047
1050
non_mandatory = (name in non_mandatory_fields ),
1048
1051
allowed_special_characters = {"(" , ")" },
@@ -1064,7 +1067,7 @@ def generate_type_declaration(
1064
1067
field_block = [
1065
1068
generate_type_declaration_field (
1066
1069
name ,
1067
- value ,
1070
+ f"list[I { nested_value_prefix } { value . lstrip ( "list[I" ) } " if name in nesting_fields else value ,
1068
1071
sdk_language ,
1069
1072
non_mandatory = (
1070
1073
name in non_mandatory_fields
@@ -1271,6 +1274,7 @@ def generate_interfaces(
1271
1274
):
1272
1275
obj_interfaces : list [str ] = []
1273
1276
interface_fields = {}
1277
+ reduced_to_datatype_fields = {}
1274
1278
param_interface_fields = {}
1275
1279
out_params_interface_fields = {}
1276
1280
obj_unique_fields = {}
@@ -1279,6 +1283,8 @@ def generate_interfaces(
1279
1283
has_nested_fields = False
1280
1284
required_datatypes : set [str ] = set ()
1281
1285
nested_fields : set [str ] = set ()
1286
+ nesting_fields : set [str ] = set ()
1287
+ generated_type_aliases : set [str ] = set ()
1282
1288
1283
1289
# The I{class_name}, I{class_name}Params and I{class_name}Out interfaces
1284
1290
for field in fields :
@@ -1295,13 +1301,17 @@ def generate_interfaces(
1295
1301
)
1296
1302
# Handle references
1297
1303
if field .get ("represents_reference_id" ):
1304
+ # nested field type aliases are already top-level type aliases for other REST objects
1305
+ # we want to re-use them instead of clone them into a new ones
1306
+ nested_class_name = class_name .rstrip (lib .core .convert_path_to_pascal_case (db_obj .get ("name" )))
1307
+ datatype = get_interface_datatype (field , sdk_language , nested_class_name )
1298
1308
has_nested_fields = True
1299
1309
# Check if the field should be reduced to the value of another field
1300
1310
reduced_to_datatype = get_reduced_field_interface_datatype (
1301
- field , fields , sdk_language , class_name
1311
+ field , fields , sdk_language , nested_class_name
1302
1312
)
1303
1313
if reduced_to_datatype :
1304
- interface_fields .update ({field .get ("name" ): reduced_to_datatype })
1314
+ reduced_to_datatype_fields .update ({field .get ("name" ): reduced_to_datatype })
1305
1315
else :
1306
1316
obj_ref = field .get ("object_reference" )
1307
1317
# Add field if the referred table is not unnested
@@ -1342,16 +1352,19 @@ def generate_interfaces(
1342
1352
# Call recursive interface generation
1343
1353
generate_nested_interfaces (
1344
1354
obj_interfaces ,
1345
- interface_fields ,
1355
+ interface_fields | reduced_to_datatype_fields ,
1346
1356
field ,
1347
- reference_class_name_postfix = lib .core .convert_path_to_pascal_case (
1357
+ reference_class_name_suffix = lib .core .convert_path_to_pascal_case (
1348
1358
field .get ("name" )
1349
1359
),
1350
1360
fields = fields ,
1351
- class_name = class_name ,
1361
+ class_name = nested_class_name ,
1352
1362
sdk_language = sdk_language ,
1353
- nested_fields = nested_fields ,
1363
+ nesting_fields = nesting_fields ,
1354
1364
fully_qualified_parent_name = field .get ("name" ),
1365
+ allowed_crud_ops = set (db_object_crud_ops ),
1366
+ reference_obj = obj ,
1367
+ generated_type_aliases = generated_type_aliases ,
1355
1368
)
1356
1369
elif obj .get ("kind" ) == "PARAMETERS" :
1357
1370
# If this field represents an OUT parameter of a SP, add it to the
@@ -1390,6 +1403,9 @@ def generate_interfaces(
1390
1403
1391
1404
if not object_is_routine (db_obj ):
1392
1405
# The object is a TABLE or a VIEW
1406
+ creatable_type_alias_name = f"New{ class_name } "
1407
+ updatable_type_alias_name = f"Update{ class_name } "
1408
+
1393
1409
if sdk_language != "TypeScript" :
1394
1410
# These type declarations are not needed for TypeScript because it uses a Proxy to replace the interface
1395
1411
# and not a wrapper class. This might change in the future.
@@ -1416,7 +1432,8 @@ def generate_interfaces(
1416
1432
)
1417
1433
)
1418
1434
1419
- if "CREATE" in db_object_crud_ops :
1435
+ # Do not generate type aliases that have already been created whilst processing nested fields.
1436
+ if "CREATE" in db_object_crud_ops and creatable_type_alias_name not in generated_type_aliases :
1420
1437
obj_non_mandatory_fields = set (
1421
1438
[
1422
1439
field .get ("name" )
@@ -1426,17 +1443,20 @@ def generate_interfaces(
1426
1443
and field_is_required (field , obj ) is False
1427
1444
]
1428
1445
)
1429
-
1430
1446
obj_interfaces .append (
1431
1447
generate_type_declaration (
1432
- name = f"New { class_name } " ,
1448
+ name = creatable_type_alias_name ,
1433
1449
fields = interface_fields ,
1434
1450
sdk_language = sdk_language ,
1435
1451
non_mandatory_fields = obj_non_mandatory_fields ,
1452
+ nesting_fields = nesting_fields ,
1453
+ nested_value_prefix = "New" ,
1436
1454
)
1437
1455
)
1456
+ generated_type_aliases .add (creatable_type_alias_name )
1438
1457
1439
- if "UPDATE" in db_object_crud_ops :
1458
+ # Do not generate type aliases that have already been created whilst processing nested fields.
1459
+ if "UPDATE" in db_object_crud_ops and updatable_type_alias_name not in generated_type_aliases :
1440
1460
# TODO: No partial update is supported yet. Once it is, the
1441
1461
# `non-mandatory_fields` argument should not change.
1442
1462
# This way, users can know what fields are required and which ones aren't.
@@ -1449,28 +1469,33 @@ def generate_interfaces(
1449
1469
]
1450
1470
obj_interfaces .append (
1451
1471
generate_type_declaration (
1452
- name = f"Update { class_name } " ,
1472
+ name = updatable_type_alias_name ,
1453
1473
fields = interface_fields ,
1454
1474
sdk_language = sdk_language ,
1455
1475
non_mandatory_fields = set (nullable_fields ),
1476
+ nesting_fields = nesting_fields ,
1477
+ nested_value_prefix = "Update" ,
1456
1478
)
1457
1479
)
1480
+ generated_type_aliases .add (updatable_type_alias_name )
1458
1481
1459
- primary_key_fields = [field .get ("name" ) for field in fields if field_is_pk (field )]
1460
- obj_interfaces .append (
1461
- generate_data_class (
1462
- name = class_name ,
1463
- fields = interface_fields ,
1464
- sdk_language = sdk_language ,
1465
- db_object_crud_ops = db_object_crud_ops ,
1466
- obj_endpoint = obj_endpoint ,
1467
- primary_key_fields = set (primary_key_fields ),
1482
+ if class_name not in generated_type_aliases :
1483
+ primary_key_fields = [field .get ("name" ) for field in fields if field_is_pk (field )]
1484
+ obj_interfaces .append (
1485
+ generate_data_class (
1486
+ name = class_name ,
1487
+ fields = interface_fields | reduced_to_datatype_fields ,
1488
+ sdk_language = sdk_language ,
1489
+ db_object_crud_ops = db_object_crud_ops ,
1490
+ obj_endpoint = obj_endpoint ,
1491
+ primary_key_fields = set (primary_key_fields ),
1492
+ )
1468
1493
)
1469
- )
1494
+ generated_type_aliases . add ( class_name )
1470
1495
1471
1496
obj_interfaces .append (
1472
1497
generate_field_enum (
1473
- name = class_name , fields = interface_fields , sdk_language = sdk_language
1498
+ name = class_name , fields = interface_fields | reduced_to_datatype_fields , sdk_language = sdk_language
1474
1499
)
1475
1500
)
1476
1501
@@ -1492,7 +1517,7 @@ def generate_interfaces(
1492
1517
)
1493
1518
1494
1519
obj_interfaces .append (
1495
- generate_selectable (class_name , interface_fields , sdk_language )
1520
+ generate_selectable (class_name , interface_fields | reduced_to_datatype_fields , sdk_language )
1496
1521
)
1497
1522
obj_interfaces .append (
1498
1523
generate_sortable (class_name , obj_sortable_fields , sdk_language )
@@ -1616,15 +1641,22 @@ def generate_interfaces(
1616
1641
# For now, this function is not used for ${DatabaseObject}Params type declarations
1617
1642
def generate_nested_interfaces (
1618
1643
obj_interfaces , parent_interface_fields , parent_field ,
1619
- reference_class_name_postfix ,
1620
- fields , class_name , sdk_language , fully_qualified_parent_name : str = "" , nested_fields : set [str ] = set ()):
1644
+ reference_class_name_suffix ,
1645
+ fields , class_name , reference_obj ,
1646
+ sdk_language : Optional [Literal ["TypeScript" , "Python" ]] = "TypeScript" ,
1647
+ fully_qualified_parent_name : str = "" ,
1648
+ nested_fields : set [str ] = set (),
1649
+ nesting_fields : set [str ] = set (),
1650
+ allowed_crud_ops : set [str ] = set (),
1651
+ generated_type_aliases : set [str ] = set ()):
1621
1652
# Build interface name
1622
- interface_name = f"{ class_name } { reference_class_name_postfix } "
1653
+ interface_name = f"{ class_name } { reference_class_name_suffix } "
1623
1654
1624
1655
# Check if the reference has unnest set, and if so, use the parent_interface_fields
1625
1656
parent_obj_ref = parent_field .get ("object_reference" )
1626
1657
interface_fields = {} if not parent_obj_ref .get (
1627
1658
"unnest" ) else parent_interface_fields
1659
+ reduced_to_datatype_fields = {}
1628
1660
1629
1661
for field in fields :
1630
1662
if (field .get ("parent_reference_id" ) == parent_field .get ("represents_reference_id" ) and
@@ -1635,36 +1667,83 @@ def generate_nested_interfaces(
1635
1667
reduced_to_datatype = get_reduced_field_interface_datatype (
1636
1668
field , fields , sdk_language , class_name )
1637
1669
if reduced_to_datatype :
1638
- interface_fields .update ({ field .get ("name" ): reduced_to_datatype })
1670
+ reduced_to_datatype_fields .update ({ field .get ("name" ): reduced_to_datatype })
1639
1671
else :
1640
1672
obj_ref = field .get ("object_reference" )
1641
1673
field_interface_name = lib .core .convert_path_to_pascal_case (
1642
1674
field .get ("name" ))
1643
1675
# Add field if the referred table is not unnested
1644
1676
if not obj_ref .get ("unnest" ):
1645
- datatype = f"{ class_name } { reference_class_name_postfix + field .get ("name" )} "
1677
+ datatype = f"{ class_name } { reference_class_name_suffix + field .get ("name" )} "
1646
1678
# Should use the corresponding nested field type.
1647
1679
interface_fields .update ({ field .get ("name" ): f"I{ interface_name } " + field_interface_name })
1648
1680
1649
1681
# If not, do recursive call
1650
1682
generate_nested_interfaces (
1651
1683
obj_interfaces , interface_fields , field ,
1652
- reference_class_name_postfix = reference_class_name_postfix + field_interface_name ,
1653
- fields = fields , class_name = class_name , sdk_language = sdk_language , nested_fields = nested_fields ,
1654
- fully_qualified_parent_name = f"{ parent_field .get ("name" )} .{ field .get ("name" )} " )
1684
+ reference_class_name_suffix = reference_class_name_suffix + field_interface_name ,
1685
+ fields = fields , class_name = class_name , reference_obj = reference_obj , sdk_language = sdk_language , nested_fields = nested_fields ,
1686
+ fully_qualified_parent_name = f"{ parent_field .get ("name" )} .{ field .get ("name" )} " , generated_type_aliases = generated_type_aliases )
1655
1687
else :
1656
1688
datatype = get_interface_datatype (field , sdk_language )
1657
1689
interface_fields .update ({ field .get ("name" ): datatype })
1658
1690
1659
1691
if not parent_obj_ref .get ("unnest" ):
1660
- obj_interfaces .append (
1661
- generate_type_declaration (
1662
- name = interface_name ,
1663
- fields = interface_fields ,
1664
- sdk_language = sdk_language ,
1665
- non_mandatory_fields = set (interface_fields ),
1692
+ creatable_type_alias_name = f"New{ interface_name } "
1693
+ updatable_type_alias_name = f"Update{ interface_name } "
1694
+ # Do not generate type aliases that have already been created whilst processing top-level fields.
1695
+ if "CREATE" in allowed_crud_ops and creatable_type_alias_name not in generated_type_aliases :
1696
+ non_mandatory_fields = [
1697
+ field .get ("name" )
1698
+ for field in fields
1699
+ # exclude fields that are out of range (e.g. on different nesting levels)
1700
+ if field .get ("parent_reference_id" ) == parent_field .get ("represents_reference_id" )
1701
+ and field_is_required (field , reference_obj ) is False
1702
+ ]
1703
+ obj_interfaces .append (
1704
+ generate_type_declaration (
1705
+ name = creatable_type_alias_name ,
1706
+ fields = interface_fields ,
1707
+ sdk_language = sdk_language ,
1708
+ non_mandatory_fields = set (non_mandatory_fields ),
1709
+ nesting_fields = nesting_fields ,
1710
+ nested_value_prefix = "New" ,
1711
+ )
1666
1712
)
1667
- )
1713
+ generated_type_aliases .add (creatable_type_alias_name )
1714
+ # Do not generate type aliases that have already been created whilst processing top-level fields.
1715
+ if "UPDATE" in allowed_crud_ops and updatable_type_alias_name not in generated_type_aliases :
1716
+ nullable_fields = [
1717
+ field .get ("name" )
1718
+ for field in fields
1719
+ # exclude fields that are out of range (e.g. on different nesting levels)
1720
+ if field .get ("parent_reference_id" ) == parent_field .get ("represents_reference_id" )
1721
+ and field_is_nullable (field ) or field_has_row_ownership (field , reference_obj )
1722
+ ]
1723
+ obj_interfaces .append (
1724
+ generate_type_declaration (
1725
+ name = updatable_type_alias_name ,
1726
+ fields = interface_fields ,
1727
+ sdk_language = sdk_language ,
1728
+ non_mandatory_fields = set (nullable_fields ),
1729
+ nesting_fields = nesting_fields ,
1730
+ nested_value_prefix = "Update" ,
1731
+ )
1732
+ )
1733
+ generated_type_aliases .add (updatable_type_alias_name )
1734
+ # Do not generate type aliases that have already been created whilst processing top-level fields.
1735
+ if interface_name not in generated_type_aliases :
1736
+ readable_type_alias_fields = interface_fields | reduced_to_datatype_fields
1737
+ obj_interfaces .append (
1738
+ generate_type_declaration (
1739
+ name = interface_name ,
1740
+ fields = readable_type_alias_fields ,
1741
+ sdk_language = sdk_language ,
1742
+ non_mandatory_fields = set (readable_type_alias_fields ),
1743
+ )
1744
+ )
1745
+ generated_type_aliases .add (interface_name )
1746
+ nesting_fields .add (fully_qualified_parent_name )
1668
1747
nested_fields .update ([f"{ fully_qualified_parent_name } .{ field } " for field in interface_fields ])
1669
1748
1670
1749
0 commit comments