infrahub-server 1.4.13__py3-none-any.whl → 1.5.0__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.
- infrahub/actions/tasks.py +208 -16
- infrahub/api/artifact.py +3 -0
- infrahub/api/diff/diff.py +1 -1
- infrahub/api/internal.py +2 -0
- infrahub/api/query.py +2 -0
- infrahub/api/schema.py +27 -3
- infrahub/auth.py +5 -5
- infrahub/cli/__init__.py +2 -0
- infrahub/cli/db.py +160 -157
- infrahub/cli/dev.py +118 -0
- infrahub/cli/upgrade.py +56 -9
- infrahub/computed_attribute/tasks.py +19 -7
- infrahub/config.py +7 -2
- infrahub/core/attribute.py +35 -24
- infrahub/core/branch/enums.py +1 -1
- infrahub/core/branch/models.py +9 -5
- infrahub/core/branch/needs_rebase_status.py +11 -0
- infrahub/core/branch/tasks.py +72 -10
- infrahub/core/changelog/models.py +2 -10
- infrahub/core/constants/__init__.py +4 -0
- infrahub/core/constants/infrahubkind.py +1 -0
- infrahub/core/convert_object_type/object_conversion.py +201 -0
- infrahub/core/convert_object_type/repository_conversion.py +89 -0
- infrahub/core/convert_object_type/schema_mapping.py +27 -3
- infrahub/core/diff/model/path.py +4 -0
- infrahub/core/diff/payload_builder.py +1 -1
- infrahub/core/diff/query/artifact.py +1 -0
- infrahub/core/diff/query/field_summary.py +1 -0
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/initialization.py +7 -4
- infrahub/core/manager.py +3 -81
- infrahub/core/migrations/__init__.py +3 -0
- infrahub/core/migrations/exceptions.py +4 -0
- infrahub/core/migrations/graph/__init__.py +11 -10
- infrahub/core/migrations/graph/load_schema_branch.py +21 -0
- infrahub/core/migrations/graph/m013_convert_git_password_credential.py +1 -1
- infrahub/core/migrations/graph/m037_index_attr_vals.py +11 -30
- infrahub/core/migrations/graph/m039_ipam_reconcile.py +9 -7
- infrahub/core/migrations/graph/m042_profile_attrs_in_db.py +147 -0
- infrahub/core/migrations/graph/m043_create_hfid_display_label_in_db.py +164 -0
- infrahub/core/migrations/graph/m044_backfill_hfid_display_label_in_db.py +864 -0
- infrahub/core/migrations/query/__init__.py +7 -8
- infrahub/core/migrations/query/attribute_add.py +8 -6
- infrahub/core/migrations/query/attribute_remove.py +134 -0
- infrahub/core/migrations/runner.py +54 -0
- infrahub/core/migrations/schema/attribute_kind_update.py +9 -3
- infrahub/core/migrations/schema/attribute_supports_profile.py +90 -0
- infrahub/core/migrations/schema/node_attribute_add.py +26 -5
- infrahub/core/migrations/schema/node_attribute_remove.py +13 -109
- infrahub/core/migrations/schema/node_kind_update.py +2 -1
- infrahub/core/migrations/schema/node_remove.py +2 -1
- infrahub/core/migrations/schema/placeholder_dummy.py +3 -2
- infrahub/core/migrations/shared.py +66 -19
- infrahub/core/models.py +2 -2
- infrahub/core/node/__init__.py +207 -54
- infrahub/core/node/create.py +53 -49
- infrahub/core/node/lock_utils.py +124 -0
- infrahub/core/node/node_property_attribute.py +230 -0
- infrahub/core/node/resource_manager/ip_address_pool.py +2 -1
- infrahub/core/node/resource_manager/ip_prefix_pool.py +2 -1
- infrahub/core/node/resource_manager/number_pool.py +2 -1
- infrahub/core/node/standard.py +1 -1
- infrahub/core/property.py +11 -0
- infrahub/core/protocols.py +8 -1
- infrahub/core/query/attribute.py +82 -15
- infrahub/core/query/ipam.py +16 -4
- infrahub/core/query/node.py +66 -188
- infrahub/core/query/relationship.py +44 -26
- infrahub/core/query/subquery.py +0 -8
- infrahub/core/relationship/model.py +69 -24
- infrahub/core/schema/__init__.py +56 -0
- infrahub/core/schema/attribute_schema.py +4 -2
- infrahub/core/schema/basenode_schema.py +42 -2
- infrahub/core/schema/definitions/core/__init__.py +2 -0
- infrahub/core/schema/definitions/core/check.py +1 -1
- infrahub/core/schema/definitions/core/generator.py +2 -0
- infrahub/core/schema/definitions/core/group.py +16 -2
- infrahub/core/schema/definitions/core/repository.py +7 -0
- infrahub/core/schema/definitions/core/transform.py +1 -1
- infrahub/core/schema/definitions/internal.py +12 -3
- infrahub/core/schema/generated/attribute_schema.py +2 -2
- infrahub/core/schema/generated/base_node_schema.py +6 -1
- infrahub/core/schema/manager.py +3 -0
- infrahub/core/schema/node_schema.py +1 -0
- infrahub/core/schema/relationship_schema.py +0 -1
- infrahub/core/schema/schema_branch.py +295 -10
- infrahub/core/schema/schema_branch_display.py +135 -0
- infrahub/core/schema/schema_branch_hfid.py +120 -0
- infrahub/core/validators/aggregated_checker.py +1 -1
- infrahub/database/graph.py +21 -0
- infrahub/display_labels/__init__.py +0 -0
- infrahub/display_labels/gather.py +48 -0
- infrahub/display_labels/models.py +240 -0
- infrahub/display_labels/tasks.py +192 -0
- infrahub/display_labels/triggers.py +22 -0
- infrahub/events/branch_action.py +27 -1
- infrahub/events/group_action.py +1 -1
- infrahub/events/node_action.py +1 -1
- infrahub/generators/constants.py +7 -0
- infrahub/generators/models.py +38 -12
- infrahub/generators/tasks.py +34 -16
- infrahub/git/base.py +38 -1
- infrahub/git/integrator.py +22 -14
- infrahub/graphql/api/dependencies.py +2 -4
- infrahub/graphql/api/endpoints.py +16 -6
- infrahub/graphql/app.py +2 -4
- infrahub/graphql/initialization.py +2 -3
- infrahub/graphql/manager.py +213 -137
- infrahub/graphql/middleware.py +12 -0
- infrahub/graphql/mutations/branch.py +16 -0
- infrahub/graphql/mutations/computed_attribute.py +110 -3
- infrahub/graphql/mutations/convert_object_type.py +44 -13
- infrahub/graphql/mutations/display_label.py +118 -0
- infrahub/graphql/mutations/generator.py +25 -7
- infrahub/graphql/mutations/hfid.py +125 -0
- infrahub/graphql/mutations/ipam.py +73 -41
- infrahub/graphql/mutations/main.py +61 -178
- infrahub/graphql/mutations/profile.py +195 -0
- infrahub/graphql/mutations/proposed_change.py +8 -1
- infrahub/graphql/mutations/relationship.py +2 -2
- infrahub/graphql/mutations/repository.py +22 -83
- infrahub/graphql/mutations/resource_manager.py +2 -2
- infrahub/graphql/mutations/webhook.py +1 -1
- infrahub/graphql/queries/resource_manager.py +1 -1
- infrahub/graphql/registry.py +173 -0
- infrahub/graphql/resolvers/resolver.py +2 -0
- infrahub/graphql/schema.py +8 -1
- infrahub/graphql/schema_sort.py +170 -0
- infrahub/graphql/types/branch.py +4 -1
- infrahub/graphql/types/enums.py +3 -0
- infrahub/groups/tasks.py +1 -1
- infrahub/hfid/__init__.py +0 -0
- infrahub/hfid/gather.py +48 -0
- infrahub/hfid/models.py +240 -0
- infrahub/hfid/tasks.py +191 -0
- infrahub/hfid/triggers.py +22 -0
- infrahub/lock.py +119 -42
- infrahub/locks/__init__.py +0 -0
- infrahub/locks/tasks.py +37 -0
- infrahub/patch/plan_writer.py +2 -2
- infrahub/permissions/constants.py +2 -0
- infrahub/profiles/__init__.py +0 -0
- infrahub/profiles/node_applier.py +101 -0
- infrahub/profiles/queries/__init__.py +0 -0
- infrahub/profiles/queries/get_profile_data.py +98 -0
- infrahub/profiles/tasks.py +63 -0
- infrahub/proposed_change/tasks.py +24 -5
- infrahub/repositories/__init__.py +0 -0
- infrahub/repositories/create_repository.py +113 -0
- infrahub/server.py +9 -1
- infrahub/services/__init__.py +8 -5
- infrahub/services/adapters/workflow/worker.py +5 -2
- infrahub/task_manager/event.py +5 -0
- infrahub/task_manager/models.py +7 -0
- infrahub/tasks/registry.py +6 -4
- infrahub/trigger/catalogue.py +4 -0
- infrahub/trigger/models.py +2 -0
- infrahub/trigger/setup.py +13 -4
- infrahub/trigger/tasks.py +6 -0
- infrahub/webhook/models.py +1 -1
- infrahub/workers/dependencies.py +3 -1
- infrahub/workers/infrahub_async.py +5 -1
- infrahub/workflows/catalogue.py +118 -3
- infrahub/workflows/initialization.py +21 -0
- infrahub/workflows/models.py +17 -2
- infrahub_sdk/branch.py +17 -8
- infrahub_sdk/checks.py +1 -1
- infrahub_sdk/client.py +376 -95
- infrahub_sdk/config.py +29 -2
- infrahub_sdk/convert_object_type.py +61 -0
- infrahub_sdk/ctl/branch.py +3 -0
- infrahub_sdk/ctl/check.py +2 -3
- infrahub_sdk/ctl/cli_commands.py +20 -12
- infrahub_sdk/ctl/config.py +8 -2
- infrahub_sdk/ctl/generator.py +6 -3
- infrahub_sdk/ctl/graphql.py +184 -0
- infrahub_sdk/ctl/repository.py +39 -1
- infrahub_sdk/ctl/schema.py +40 -10
- infrahub_sdk/ctl/task.py +110 -0
- infrahub_sdk/ctl/utils.py +4 -0
- infrahub_sdk/ctl/validate.py +5 -3
- infrahub_sdk/diff.py +4 -5
- infrahub_sdk/exceptions.py +2 -0
- infrahub_sdk/generator.py +7 -1
- infrahub_sdk/graphql/__init__.py +12 -0
- infrahub_sdk/graphql/constants.py +1 -0
- infrahub_sdk/graphql/plugin.py +85 -0
- infrahub_sdk/graphql/query.py +77 -0
- infrahub_sdk/{graphql.py → graphql/renderers.py} +88 -75
- infrahub_sdk/graphql/utils.py +40 -0
- infrahub_sdk/node/attribute.py +2 -0
- infrahub_sdk/node/node.py +28 -20
- infrahub_sdk/node/relationship.py +1 -3
- infrahub_sdk/playback.py +1 -2
- infrahub_sdk/protocols.py +54 -6
- infrahub_sdk/pytest_plugin/plugin.py +7 -4
- infrahub_sdk/pytest_plugin/utils.py +40 -0
- infrahub_sdk/repository.py +1 -2
- infrahub_sdk/schema/__init__.py +70 -4
- infrahub_sdk/schema/main.py +1 -0
- infrahub_sdk/schema/repository.py +8 -0
- infrahub_sdk/spec/models.py +7 -0
- infrahub_sdk/spec/object.py +54 -6
- infrahub_sdk/spec/processors/__init__.py +0 -0
- infrahub_sdk/spec/processors/data_processor.py +10 -0
- infrahub_sdk/spec/processors/factory.py +34 -0
- infrahub_sdk/spec/processors/range_expand_processor.py +56 -0
- infrahub_sdk/spec/range_expansion.py +118 -0
- infrahub_sdk/task/models.py +6 -4
- infrahub_sdk/timestamp.py +18 -6
- infrahub_sdk/transforms.py +1 -1
- {infrahub_server-1.4.13.dist-info → infrahub_server-1.5.0.dist-info}/METADATA +9 -10
- {infrahub_server-1.4.13.dist-info → infrahub_server-1.5.0.dist-info}/RECORD +221 -165
- infrahub_testcontainers/container.py +114 -2
- infrahub_testcontainers/docker-compose-cluster.test.yml +5 -0
- infrahub_testcontainers/docker-compose.test.yml +5 -0
- infrahub_testcontainers/models.py +2 -2
- infrahub_testcontainers/performance_test.py +4 -4
- infrahub/core/convert_object_type/conversion.py +0 -134
- {infrahub_server-1.4.13.dist-info → infrahub_server-1.5.0.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.4.13.dist-info → infrahub_server-1.5.0.dist-info}/WHEEL +0 -0
- {infrahub_server-1.4.13.dist-info → infrahub_server-1.5.0.dist-info}/entry_points.txt +0 -0
|
@@ -350,17 +350,20 @@ class RelationshipUpdatePropertyQuery(RelationshipQuery):
|
|
|
350
350
|
|
|
351
351
|
def __init__(
|
|
352
352
|
self,
|
|
353
|
-
|
|
354
|
-
|
|
353
|
+
rel_node_id: str,
|
|
354
|
+
flag_properties_to_update: dict[str, bool],
|
|
355
|
+
node_properties_to_update: dict[str, str],
|
|
355
356
|
**kwargs,
|
|
356
357
|
):
|
|
357
|
-
self.
|
|
358
|
-
|
|
359
|
-
|
|
358
|
+
self.rel_node_id = rel_node_id
|
|
359
|
+
if not flag_properties_to_update and not node_properties_to_update:
|
|
360
|
+
raise ValueError("Either flag_properties_to_update or node_properties_to_update must be set")
|
|
361
|
+
self.flag_properties_to_update = flag_properties_to_update
|
|
362
|
+
self.node_properties_to_update = node_properties_to_update
|
|
360
363
|
super().__init__(**kwargs)
|
|
361
364
|
|
|
362
365
|
async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: # noqa: ARG002
|
|
363
|
-
self.params["rel_node_id"] = self.
|
|
366
|
+
self.params["rel_node_id"] = self.rel_node_id
|
|
364
367
|
self.params["branch"] = self.branch.name
|
|
365
368
|
self.params["branch_level"] = self.branch.hierarchy_level
|
|
366
369
|
self.params["at"] = self.at.to_string()
|
|
@@ -370,36 +373,51 @@ class RelationshipUpdatePropertyQuery(RelationshipQuery):
|
|
|
370
373
|
"""
|
|
371
374
|
self.add_to_query(query)
|
|
372
375
|
|
|
373
|
-
self.query_add_all_flag_property_merge()
|
|
374
376
|
self.query_add_all_node_property_merge()
|
|
377
|
+
self.query_add_all_flag_property_merge()
|
|
375
378
|
|
|
376
|
-
self.query_add_all_flag_property_create()
|
|
377
379
|
self.query_add_all_node_property_create()
|
|
380
|
+
self.query_add_all_flag_property_create()
|
|
378
381
|
|
|
379
382
|
def query_add_all_flag_property_merge(self) -> None:
|
|
380
|
-
for prop_name in self.
|
|
381
|
-
|
|
382
|
-
self.query_add_flag_property_merge(name=prop_name)
|
|
383
|
+
for prop_name, prop_value in self.flag_properties_to_update.items():
|
|
384
|
+
self.query_add_flag_property_merge(name=prop_name, value=prop_value)
|
|
383
385
|
|
|
384
|
-
def query_add_flag_property_merge(self, name: str) -> None:
|
|
386
|
+
def query_add_flag_property_merge(self, name: str, value: bool) -> None:
|
|
385
387
|
self.add_to_query("MERGE (prop_%s:Boolean { value: $prop_%s })" % (name, name))
|
|
386
|
-
self.params[f"prop_{name}"] =
|
|
388
|
+
self.params[f"prop_{name}"] = value
|
|
387
389
|
self.return_labels.append(f"prop_{name}")
|
|
388
390
|
|
|
389
391
|
def query_add_all_node_property_merge(self) -> None:
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
self.query_add_node_property_merge(name=prop_name)
|
|
392
|
+
branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at)
|
|
393
|
+
self.params.update(branch_params)
|
|
393
394
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
395
|
+
for prop_name, prop_value in self.node_properties_to_update.items():
|
|
396
|
+
self.params[f"prop_{prop_name}"] = prop_value
|
|
397
|
+
if self.branch.is_default or self.branch.is_global:
|
|
398
|
+
node_query = """
|
|
399
|
+
MATCH (prop_%(prop_name)s:Node {uuid: $prop_%(prop_name)s })-[r_%(prop_name)s:IS_PART_OF]->(:Root)
|
|
400
|
+
WHERE r_%(prop_name)s.branch IN $branch0
|
|
401
|
+
AND r_%(prop_name)s.status = "active"
|
|
402
|
+
AND r_%(prop_name)s.from <= $at AND (r_%(prop_name)s.to IS NULL OR r_%(prop_name)s.to > $at)
|
|
403
|
+
WITH *
|
|
404
|
+
LIMIT 1
|
|
405
|
+
""" % {"prop_name": prop_name}
|
|
406
|
+
else:
|
|
407
|
+
node_query = """
|
|
408
|
+
MATCH (prop_%(prop_name)s:Node {uuid: $prop_%(prop_name)s })-[r_%(prop_name)s:IS_PART_OF]->(:Root)
|
|
409
|
+
WHERE all(r in [r_%(prop_name)s] WHERE %(branch_filter)s)
|
|
410
|
+
ORDER BY r_%(prop_name)s.branch_level DESC, r_%(prop_name)s.from DESC, r_%(prop_name)s.status ASC
|
|
411
|
+
LIMIT 1
|
|
412
|
+
WITH *
|
|
413
|
+
WHERE r_%(prop_name)s.status = "active"
|
|
414
|
+
""" % {"branch_filter": branch_filter, "prop_name": prop_name}
|
|
415
|
+
self.add_to_query(node_query)
|
|
416
|
+
self.return_labels.append(f"prop_{prop_name}")
|
|
398
417
|
|
|
399
418
|
def query_add_all_flag_property_create(self) -> None:
|
|
400
|
-
for prop_name in self.
|
|
401
|
-
|
|
402
|
-
self.query_add_flag_property_create(name=prop_name)
|
|
419
|
+
for prop_name in self.flag_properties_to_update:
|
|
420
|
+
self.query_add_flag_property_create(name=prop_name)
|
|
403
421
|
|
|
404
422
|
def query_add_flag_property_create(self, name: str) -> None:
|
|
405
423
|
query = """
|
|
@@ -411,9 +429,8 @@ class RelationshipUpdatePropertyQuery(RelationshipQuery):
|
|
|
411
429
|
self.add_to_query(query)
|
|
412
430
|
|
|
413
431
|
def query_add_all_node_property_create(self) -> None:
|
|
414
|
-
for prop_name in self.
|
|
415
|
-
|
|
416
|
-
self.query_add_node_property_create(name=prop_name)
|
|
432
|
+
for prop_name in self.node_properties_to_update:
|
|
433
|
+
self.query_add_node_property_create(name=prop_name)
|
|
417
434
|
|
|
418
435
|
def query_add_node_property_create(self, name: str) -> None:
|
|
419
436
|
query = """
|
|
@@ -1019,6 +1036,7 @@ class RelationshipCountPerNodeQuery(Query):
|
|
|
1019
1036
|
""" % {"branch_filter": branch_filter, "path": path}
|
|
1020
1037
|
|
|
1021
1038
|
self.add_to_query(query)
|
|
1039
|
+
self.order_by = ["peer_node.uuid"]
|
|
1022
1040
|
self.return_labels = ["peer_node.uuid", "COUNT(peer_node.uuid) as nbr_peers"]
|
|
1023
1041
|
|
|
1024
1042
|
async def get_count_per_peer(self) -> dict[str, int]:
|
infrahub/core/query/subquery.py
CHANGED
|
@@ -25,12 +25,8 @@ async def build_subquery_filter(
|
|
|
25
25
|
partial_match: bool = False,
|
|
26
26
|
optional_match: bool = False,
|
|
27
27
|
result_prefix: str = "filter",
|
|
28
|
-
support_profiles: bool = False,
|
|
29
28
|
extra_tail_properties: dict[str, str] | None = None,
|
|
30
29
|
) -> tuple[str, dict[str, Any], str]:
|
|
31
|
-
support_profiles = (
|
|
32
|
-
support_profiles and field and field.is_attribute and filter_name in ("value", "values", "isnull")
|
|
33
|
-
)
|
|
34
30
|
params = {}
|
|
35
31
|
prefix = f"{result_prefix}{subquery_idx}"
|
|
36
32
|
|
|
@@ -52,7 +48,6 @@ async def build_subquery_filter(
|
|
|
52
48
|
param_prefix=prefix,
|
|
53
49
|
db=db,
|
|
54
50
|
partial_match=partial_match,
|
|
55
|
-
support_profiles=support_profiles,
|
|
56
51
|
)
|
|
57
52
|
params.update(field_params)
|
|
58
53
|
|
|
@@ -109,10 +104,8 @@ async def build_subquery_order(
|
|
|
109
104
|
branch: Branch = None,
|
|
110
105
|
subquery_idx: int = 1,
|
|
111
106
|
result_prefix: str | None = None,
|
|
112
|
-
support_profiles: bool = False,
|
|
113
107
|
extra_tail_properties: dict[str, str] | None = None,
|
|
114
108
|
) -> tuple[str, dict[str, Any], str]:
|
|
115
|
-
support_profiles = support_profiles and field and field.is_attribute and order_by in ("value", "values")
|
|
116
109
|
params = {}
|
|
117
110
|
prefix = result_prefix or f"order{subquery_idx}"
|
|
118
111
|
|
|
@@ -124,7 +117,6 @@ async def build_subquery_order(
|
|
|
124
117
|
filter_value=None,
|
|
125
118
|
branch=branch,
|
|
126
119
|
param_prefix=prefix,
|
|
127
|
-
support_profiles=support_profiles,
|
|
128
120
|
)
|
|
129
121
|
params.update(field_params)
|
|
130
122
|
|
|
@@ -381,14 +381,33 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
381
381
|
|
|
382
382
|
node = await self.get_node(db=db)
|
|
383
383
|
|
|
384
|
+
flag_properties_to_update = {}
|
|
385
|
+
for prop_name in self._flag_properties:
|
|
386
|
+
if prop_name not in properties_to_update:
|
|
387
|
+
continue
|
|
388
|
+
value = getattr(self, prop_name)
|
|
389
|
+
if value is not None:
|
|
390
|
+
flag_properties_to_update[prop_name] = value
|
|
391
|
+
|
|
392
|
+
node_properties_to_update = {}
|
|
393
|
+
for prop_name in self._node_properties:
|
|
394
|
+
if prop_name not in properties_to_update:
|
|
395
|
+
continue
|
|
396
|
+
if value := getattr(self, f"{prop_name}_id"):
|
|
397
|
+
node_properties_to_update[prop_name] = value
|
|
398
|
+
|
|
399
|
+
if not flag_properties_to_update and not node_properties_to_update:
|
|
400
|
+
return
|
|
401
|
+
|
|
384
402
|
query = await RelationshipUpdatePropertyQuery.init(
|
|
385
403
|
db=db,
|
|
404
|
+
branch=branch,
|
|
386
405
|
source=node,
|
|
387
406
|
rel=self,
|
|
388
|
-
properties_to_update=properties_to_update,
|
|
389
|
-
data=data,
|
|
390
|
-
branch=branch,
|
|
391
407
|
at=update_at,
|
|
408
|
+
flag_properties_to_update=flag_properties_to_update,
|
|
409
|
+
node_properties_to_update=node_properties_to_update,
|
|
410
|
+
rel_node_id=data.rel_node_id,
|
|
392
411
|
)
|
|
393
412
|
await query.execute(db=db)
|
|
394
413
|
|
|
@@ -426,15 +445,20 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
426
445
|
)
|
|
427
446
|
await delete_query.execute(db=db)
|
|
428
447
|
|
|
429
|
-
async def resolve(self, db: InfrahubDatabase, at: Timestamp | None = None) -> None:
|
|
448
|
+
async def resolve(self, db: InfrahubDatabase, at: Timestamp | None = None, fields: list[str] | None = None) -> None:
|
|
430
449
|
"""Resolve the peer of the relationship."""
|
|
431
450
|
|
|
451
|
+
fields = fields or []
|
|
452
|
+
query_fields = dict.fromkeys(fields)
|
|
453
|
+
if "display_label" not in query_fields:
|
|
454
|
+
query_fields["display_label"] = None
|
|
455
|
+
|
|
432
456
|
if self._peer is not None:
|
|
433
457
|
return
|
|
434
458
|
|
|
435
459
|
if self.peer_id and not is_valid_uuid(self.peer_id):
|
|
436
460
|
peer = await registry.manager.get_one_by_default_filter(
|
|
437
|
-
db=db, id=self.peer_id, branch=self.branch, kind=self.schema.peer, fields=
|
|
461
|
+
db=db, id=self.peer_id, branch=self.branch, kind=self.schema.peer, fields=query_fields
|
|
438
462
|
)
|
|
439
463
|
if peer:
|
|
440
464
|
self.set_peer(value=peer)
|
|
@@ -451,7 +475,7 @@ class Relationship(FlagPropertyMixin, NodePropertyMixin):
|
|
|
451
475
|
hfid=self.peer_hfid,
|
|
452
476
|
branch=self.branch,
|
|
453
477
|
kind=kind,
|
|
454
|
-
fields=
|
|
478
|
+
fields=query_fields,
|
|
455
479
|
raise_on_error=True,
|
|
456
480
|
)
|
|
457
481
|
self.set_peer(value=peer)
|
|
@@ -570,7 +594,9 @@ class RelationshipValidatorList:
|
|
|
570
594
|
ValidationError: If the number of relationships is not within the min and max count.
|
|
571
595
|
"""
|
|
572
596
|
|
|
573
|
-
def __init__(
|
|
597
|
+
def __init__(
|
|
598
|
+
self, *relationships: Relationship, name: str, min_count: int | None = 0, max_count: int | None = 0
|
|
599
|
+
) -> None:
|
|
574
600
|
"""Initialize list for Relationship but with validation against min/max count.
|
|
575
601
|
|
|
576
602
|
Args:
|
|
@@ -580,8 +606,14 @@ class RelationshipValidatorList:
|
|
|
580
606
|
Raises:
|
|
581
607
|
ValidationError: The number of relationships is not within the min and max count.
|
|
582
608
|
"""
|
|
583
|
-
if max_count < min_count:
|
|
609
|
+
if max_count is not None and min_count is not None and max_count < min_count:
|
|
584
610
|
raise ValidationError({"msg": "max_count must be greater than min_count"})
|
|
611
|
+
|
|
612
|
+
if max_count is None:
|
|
613
|
+
max_count = 0
|
|
614
|
+
if min_count is None:
|
|
615
|
+
min_count = 0
|
|
616
|
+
|
|
585
617
|
self.min_count: int = min_count
|
|
586
618
|
self.max_count: int = max_count
|
|
587
619
|
self.name = name
|
|
@@ -726,15 +758,22 @@ class RelationshipManager:
|
|
|
726
758
|
# TODO Ideally this information should come from the Schema
|
|
727
759
|
self.rel_class = Relationship
|
|
728
760
|
|
|
729
|
-
self._relationships: RelationshipValidatorList =
|
|
730
|
-
name=self.schema.name,
|
|
731
|
-
min_count=0 if self.schema.optional else self.schema.min_count,
|
|
732
|
-
max_count=self.schema.max_count,
|
|
733
|
-
)
|
|
761
|
+
self._relationships: RelationshipValidatorList = self._get_init_relationships()
|
|
734
762
|
self._relationship_id_details: RelationshipUpdateDetails | None = None
|
|
735
763
|
self.has_fetched_relationships: bool = False
|
|
736
764
|
self.lock = asyncio.Lock()
|
|
737
765
|
|
|
766
|
+
def _get_init_relationships(self) -> RelationshipValidatorList:
|
|
767
|
+
min_count = self.schema.min_count
|
|
768
|
+
max_count: int | None = self.schema.max_count if self.schema.max_count > 0 else None
|
|
769
|
+
if self.schema.optional:
|
|
770
|
+
min_count = 0
|
|
771
|
+
return RelationshipValidatorList(
|
|
772
|
+
name=self.schema.name,
|
|
773
|
+
min_count=min_count,
|
|
774
|
+
max_count=max_count,
|
|
775
|
+
)
|
|
776
|
+
|
|
738
777
|
@classmethod
|
|
739
778
|
async def init(
|
|
740
779
|
cls,
|
|
@@ -909,6 +948,19 @@ class RelationshipManager:
|
|
|
909
948
|
return registry.get_global_branch()
|
|
910
949
|
return self.branch
|
|
911
950
|
|
|
951
|
+
async def get_db_peers(
|
|
952
|
+
self, db: InfrahubDatabase, at: Timestamp | None = None, branch_agnostic: bool = False
|
|
953
|
+
) -> list[RelationshipPeerData]:
|
|
954
|
+
query = await RelationshipGetPeerQuery.init(
|
|
955
|
+
db=db,
|
|
956
|
+
source=self.node,
|
|
957
|
+
at=at or self.at,
|
|
958
|
+
rel=self.rel_class(schema=self.schema, branch=self.branch, node=self.node),
|
|
959
|
+
branch_agnostic=branch_agnostic,
|
|
960
|
+
)
|
|
961
|
+
await query.execute(db=db)
|
|
962
|
+
return list(query.get_peers())
|
|
963
|
+
|
|
912
964
|
async def fetch_relationship_ids(
|
|
913
965
|
self,
|
|
914
966
|
db: InfrahubDatabase,
|
|
@@ -926,16 +978,9 @@ class RelationshipManager:
|
|
|
926
978
|
|
|
927
979
|
current_peer_ids = [rel.get_peer_id() for rel in self._relationships]
|
|
928
980
|
|
|
929
|
-
|
|
930
|
-
db=db,
|
|
931
|
-
source=self.node,
|
|
932
|
-
at=at or self.at,
|
|
933
|
-
rel=self.rel_class(schema=self.schema, branch=self.branch, node=self.node),
|
|
934
|
-
branch_agnostic=branch_agnostic,
|
|
935
|
-
)
|
|
936
|
-
await query.execute(db=db)
|
|
981
|
+
peers = await self.get_db_peers(db=db, at=at, branch_agnostic=branch_agnostic)
|
|
937
982
|
|
|
938
|
-
peers_database: dict = {str(peer.peer_id): peer for peer in
|
|
983
|
+
peers_database: dict = {str(peer.peer_id): peer for peer in peers}
|
|
939
984
|
peer_ids = list(peers_database.keys())
|
|
940
985
|
|
|
941
986
|
# Calculate which peer should be added or removed
|
|
@@ -1102,9 +1147,9 @@ class RelationshipManager:
|
|
|
1102
1147
|
|
|
1103
1148
|
return True
|
|
1104
1149
|
|
|
1105
|
-
async def resolve(self, db: InfrahubDatabase) -> None:
|
|
1150
|
+
async def resolve(self, db: InfrahubDatabase, fields: list[str] | None = None) -> None:
|
|
1106
1151
|
for rel in self._relationships:
|
|
1107
|
-
await rel.resolve(db=db)
|
|
1152
|
+
await rel.resolve(db=db, fields=fields)
|
|
1108
1153
|
|
|
1109
1154
|
async def remove_locally(
|
|
1110
1155
|
self,
|
infrahub/core/schema/__init__.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import uuid
|
|
4
|
+
from enum import Enum
|
|
4
5
|
from typing import Any, TypeAlias
|
|
5
6
|
|
|
6
7
|
from infrahub_sdk.utils import deep_merge_dict
|
|
@@ -44,6 +45,21 @@ class SchemaExtension(HashableModel):
|
|
|
44
45
|
nodes: list[NodeExtensionSchema] = Field(default_factory=list)
|
|
45
46
|
|
|
46
47
|
|
|
48
|
+
class SchemaWarningType(Enum):
|
|
49
|
+
DEPRECATION = "deprecation"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class SchemaWarningKind(BaseModel):
|
|
53
|
+
kind: str = Field(..., description="The kind impacted by the warning")
|
|
54
|
+
field: str | None = Field(default=None, description="The attribute or relationship impacted by the warning")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class SchemaWarning(BaseModel):
|
|
58
|
+
type: SchemaWarningType = Field(..., description="The type of warning")
|
|
59
|
+
kinds: list[SchemaWarningKind] = Field(default_factory=list, description="The kinds impacted by the warning")
|
|
60
|
+
message: str = Field(..., description="The message that describes the warning")
|
|
61
|
+
|
|
62
|
+
|
|
47
63
|
class SchemaRoot(BaseModel):
|
|
48
64
|
model_config = ConfigDict(extra="forbid")
|
|
49
65
|
|
|
@@ -80,6 +96,46 @@ class SchemaRoot(BaseModel):
|
|
|
80
96
|
|
|
81
97
|
return errors
|
|
82
98
|
|
|
99
|
+
def gather_warnings(self) -> list[SchemaWarning]:
|
|
100
|
+
models = self.nodes + self.generics
|
|
101
|
+
warnings: list[SchemaWarning] = []
|
|
102
|
+
for model in models:
|
|
103
|
+
if model.display_labels is not None:
|
|
104
|
+
warnings.append(
|
|
105
|
+
SchemaWarning(
|
|
106
|
+
type=SchemaWarningType.DEPRECATION,
|
|
107
|
+
kinds=[SchemaWarningKind(kind=model.kind)],
|
|
108
|
+
message="display_labels are deprecated, use display_label instead",
|
|
109
|
+
)
|
|
110
|
+
)
|
|
111
|
+
if model.default_filter is not None:
|
|
112
|
+
warnings.append(
|
|
113
|
+
SchemaWarning(
|
|
114
|
+
type=SchemaWarningType.DEPRECATION,
|
|
115
|
+
kinds=[SchemaWarningKind(kind=model.kind)],
|
|
116
|
+
message="default_filter is deprecated",
|
|
117
|
+
)
|
|
118
|
+
)
|
|
119
|
+
for attribute in model.attributes:
|
|
120
|
+
if attribute.max_length is not None:
|
|
121
|
+
warnings.append(
|
|
122
|
+
SchemaWarning(
|
|
123
|
+
type=SchemaWarningType.DEPRECATION,
|
|
124
|
+
kinds=[SchemaWarningKind(kind=model.kind, field=attribute.name)],
|
|
125
|
+
message="Use of 'max_length' on attributes is deprecated, use parameters instead",
|
|
126
|
+
)
|
|
127
|
+
)
|
|
128
|
+
if attribute.min_length is not None:
|
|
129
|
+
warnings.append(
|
|
130
|
+
SchemaWarning(
|
|
131
|
+
type=SchemaWarningType.DEPRECATION,
|
|
132
|
+
kinds=[SchemaWarningKind(kind=model.kind, field=attribute.name)],
|
|
133
|
+
message="Use of 'min_length' on attributes is deprecated, use parameters instead",
|
|
134
|
+
)
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
return warnings
|
|
138
|
+
|
|
83
139
|
def generate_uuid(self) -> None:
|
|
84
140
|
"""Generate UUID for all nodes, attributes & relationships
|
|
85
141
|
Mainly useful during unit tests."""
|
|
@@ -68,6 +68,10 @@ class AttributeSchema(GeneratedAttributeSchema):
|
|
|
68
68
|
def is_deprecated(self) -> bool:
|
|
69
69
|
return bool(self.deprecation)
|
|
70
70
|
|
|
71
|
+
@property
|
|
72
|
+
def support_profiles(self) -> bool:
|
|
73
|
+
return self.read_only is False and self.optional is True
|
|
74
|
+
|
|
71
75
|
def get_id(self) -> str:
|
|
72
76
|
if self.id is None:
|
|
73
77
|
raise InitializationError("The attribute schema has not been saved yet and doesn't have an id")
|
|
@@ -202,7 +206,6 @@ class AttributeSchema(GeneratedAttributeSchema):
|
|
|
202
206
|
param_prefix: str | None = None,
|
|
203
207
|
db: InfrahubDatabase | None = None,
|
|
204
208
|
partial_match: bool = False,
|
|
205
|
-
support_profiles: bool = False,
|
|
206
209
|
) -> tuple[list[QueryElement], dict[str, Any], list[str]]:
|
|
207
210
|
if self.enum:
|
|
208
211
|
filter_value = self.convert_enum_to_value(filter_value)
|
|
@@ -217,7 +220,6 @@ class AttributeSchema(GeneratedAttributeSchema):
|
|
|
217
220
|
param_prefix=param_prefix,
|
|
218
221
|
db=db,
|
|
219
222
|
partial_match=partial_match,
|
|
220
|
-
support_profiles=support_profiles,
|
|
221
223
|
)
|
|
222
224
|
|
|
223
225
|
|
|
@@ -9,7 +9,7 @@ from enum import Enum
|
|
|
9
9
|
from typing import TYPE_CHECKING, Any, Callable, Iterable, Literal, overload
|
|
10
10
|
|
|
11
11
|
from infrahub_sdk.utils import compare_lists, intersection
|
|
12
|
-
from pydantic import field_validator
|
|
12
|
+
from pydantic import ConfigDict, field_validator
|
|
13
13
|
|
|
14
14
|
from infrahub.core.constants import HashableModelState, RelationshipCardinality, RelationshipKind
|
|
15
15
|
from infrahub.core.models import HashableModel, HashableModelDiff
|
|
@@ -19,6 +19,7 @@ from .generated.base_node_schema import GeneratedBaseNodeSchema
|
|
|
19
19
|
from .relationship_schema import RelationshipSchema
|
|
20
20
|
|
|
21
21
|
if TYPE_CHECKING:
|
|
22
|
+
from pydantic.config import JsonDict
|
|
22
23
|
from typing_extensions import Self
|
|
23
24
|
|
|
24
25
|
from infrahub.core.schema import GenericSchema, NodeSchema
|
|
@@ -26,6 +27,7 @@ if TYPE_CHECKING:
|
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
NODE_METADATA_ATTRIBUTES = ["_source", "_owner"]
|
|
30
|
+
NODE_PROPERTY_ATTRIBUTES = ["display_label", "human_friendly_id"]
|
|
29
31
|
INHERITED = "INHERITED"
|
|
30
32
|
|
|
31
33
|
OPTIONAL_TEXT_FIELDS = [
|
|
@@ -39,10 +41,43 @@ OPTIONAL_TEXT_FIELDS = [
|
|
|
39
41
|
]
|
|
40
42
|
|
|
41
43
|
|
|
44
|
+
def _json_schema_extra(schema: JsonDict) -> None:
|
|
45
|
+
"""
|
|
46
|
+
Mutate the generated JSON Schema in place to:
|
|
47
|
+
- allow `null` for `display_labels`
|
|
48
|
+
- mark the non-null branch as deprecated
|
|
49
|
+
"""
|
|
50
|
+
props = schema.get("properties")
|
|
51
|
+
if not isinstance(props, dict):
|
|
52
|
+
return
|
|
53
|
+
dl = props.get("display_labels")
|
|
54
|
+
if not isinstance(dl, dict):
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
if "anyOf" in dl:
|
|
58
|
+
dl["anyOf"] = [
|
|
59
|
+
{
|
|
60
|
+
"type": "array",
|
|
61
|
+
"items": {
|
|
62
|
+
"type": "string",
|
|
63
|
+
"deprecationMessage": "display_labels are deprecated use display_label instead",
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
{"type": "null"},
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
|
|
42
70
|
class BaseNodeSchema(GeneratedBaseNodeSchema):
|
|
43
71
|
_exclude_from_hash: list[str] = ["attributes", "relationships"]
|
|
44
72
|
_sort_by: list[str] = ["namespace", "name"]
|
|
45
73
|
|
|
74
|
+
model_config = ConfigDict(extra="forbid", json_schema_extra=_json_schema_extra)
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def is_schema_node(self) -> bool:
|
|
78
|
+
"""Tell if this node represent a part of the schema. Not to confuse this with `is_node_schema`."""
|
|
79
|
+
return self.namespace == "Schema"
|
|
80
|
+
|
|
46
81
|
@property
|
|
47
82
|
def is_node_schema(self) -> bool:
|
|
48
83
|
return False
|
|
@@ -240,6 +275,11 @@ class BaseNodeSchema(GeneratedBaseNodeSchema):
|
|
|
240
275
|
return None
|
|
241
276
|
|
|
242
277
|
def get_attribute(self, name: str) -> AttributeSchema:
|
|
278
|
+
if name == "human_friendly_id":
|
|
279
|
+
return AttributeSchema(name="human_friendly_id", kind="List", optional=True, branch=self.branch)
|
|
280
|
+
if name == "display_label":
|
|
281
|
+
return AttributeSchema(name="display_label", kind="Text", optional=True, branch=self.branch)
|
|
282
|
+
|
|
243
283
|
for item in self.attributes:
|
|
244
284
|
if item.name == name:
|
|
245
285
|
return item
|
|
@@ -329,7 +369,7 @@ class BaseNodeSchema(GeneratedBaseNodeSchema):
|
|
|
329
369
|
|
|
330
370
|
@property
|
|
331
371
|
def valid_input_names(self) -> list[str]:
|
|
332
|
-
return self.attribute_names + self.relationship_names + NODE_METADATA_ATTRIBUTES
|
|
372
|
+
return self.attribute_names + self.relationship_names + NODE_METADATA_ATTRIBUTES + NODE_PROPERTY_ATTRIBUTES
|
|
333
373
|
|
|
334
374
|
@property
|
|
335
375
|
def valid_local_names(self) -> list[str]:
|
|
@@ -29,6 +29,7 @@ from .core import core_node, core_task_target
|
|
|
29
29
|
from .generator import core_generator_definition, core_generator_instance
|
|
30
30
|
from .graphql_query import core_graphql_query
|
|
31
31
|
from .group import (
|
|
32
|
+
core_generator_aware_group,
|
|
32
33
|
core_generator_group,
|
|
33
34
|
core_graphql_query_group,
|
|
34
35
|
core_group,
|
|
@@ -128,6 +129,7 @@ core_models_mixed: dict[str, list] = {
|
|
|
128
129
|
core_group_action,
|
|
129
130
|
core_standard_group,
|
|
130
131
|
core_generator_group,
|
|
132
|
+
core_generator_aware_group,
|
|
131
133
|
core_graphql_query_group,
|
|
132
134
|
core_repository_group,
|
|
133
135
|
builtin_tag,
|
|
@@ -29,7 +29,7 @@ core_check_definition = NodeSchema(
|
|
|
29
29
|
Attr(name="description", kind="Text", optional=True),
|
|
30
30
|
Attr(name="file_path", kind="Text"),
|
|
31
31
|
Attr(name="class_name", kind="Text"),
|
|
32
|
-
Attr(name="timeout", kind="Number", default_value=
|
|
32
|
+
Attr(name="timeout", kind="Number", default_value=60),
|
|
33
33
|
Attr(name="parameters", kind="JSON", optional=True),
|
|
34
34
|
],
|
|
35
35
|
relationships=[
|
|
@@ -33,6 +33,8 @@ core_generator_definition = NodeSchema(
|
|
|
33
33
|
Attr(name="file_path", kind="Text"),
|
|
34
34
|
Attr(name="class_name", kind="Text"),
|
|
35
35
|
Attr(name="convert_query_response", kind="Boolean", optional=True, default_value=False),
|
|
36
|
+
Attr(name="execute_in_proposed_change", kind="Boolean", optional=True, default_value=True),
|
|
37
|
+
Attr(name="execute_after_merge", kind="Boolean", optional=True, default_value=True),
|
|
36
38
|
],
|
|
37
39
|
relationships=[
|
|
38
40
|
Rel(
|
|
@@ -70,10 +70,10 @@ core_standard_group = NodeSchema(
|
|
|
70
70
|
core_generator_group = NodeSchema(
|
|
71
71
|
name="GeneratorGroup",
|
|
72
72
|
namespace="Core",
|
|
73
|
-
description="Group of nodes that are created by a generator.",
|
|
73
|
+
description="Group of nodes that are created by a generator. (local)",
|
|
74
74
|
include_in_menu=False,
|
|
75
75
|
icon="mdi:state-machine",
|
|
76
|
-
label="Generator Group",
|
|
76
|
+
label="Generator Group (local)",
|
|
77
77
|
default_filter="name__value",
|
|
78
78
|
order_by=["name__value"],
|
|
79
79
|
display_labels=["name__value"],
|
|
@@ -82,6 +82,20 @@ core_generator_group = NodeSchema(
|
|
|
82
82
|
generate_profile=False,
|
|
83
83
|
)
|
|
84
84
|
|
|
85
|
+
core_generator_aware_group = NodeSchema(
|
|
86
|
+
name="GeneratorAwareGroup",
|
|
87
|
+
namespace="Core",
|
|
88
|
+
description="Group of nodes that are created by a generator. (Aware)",
|
|
89
|
+
include_in_menu=False,
|
|
90
|
+
icon="mdi:state-machine",
|
|
91
|
+
label="Generator Group (aware)",
|
|
92
|
+
default_filter="name__value",
|
|
93
|
+
order_by=["name__value"],
|
|
94
|
+
display_labels=["name__value"],
|
|
95
|
+
branch=BranchSupportType.AWARE,
|
|
96
|
+
inherit_from=[InfrahubKind.GENERICGROUP],
|
|
97
|
+
generate_profile=False,
|
|
98
|
+
)
|
|
85
99
|
|
|
86
100
|
core_graphql_query_group = NodeSchema(
|
|
87
101
|
name="GraphQLQueryGroup",
|
|
@@ -282,5 +282,12 @@ core_generic_repository = GenericSchema(
|
|
|
282
282
|
cardinality=Cardinality.MANY,
|
|
283
283
|
order_weight=12000,
|
|
284
284
|
),
|
|
285
|
+
Rel(
|
|
286
|
+
name="groups_objects",
|
|
287
|
+
peer=InfrahubKind.REPOSITORYGROUP,
|
|
288
|
+
optional=True,
|
|
289
|
+
cardinality=Cardinality.MANY,
|
|
290
|
+
order_weight=13000,
|
|
291
|
+
),
|
|
285
292
|
],
|
|
286
293
|
)
|
|
@@ -29,7 +29,7 @@ core_transform = GenericSchema(
|
|
|
29
29
|
Attr(name="name", kind="Text", unique=True),
|
|
30
30
|
Attr(name="label", kind="Text", optional=True),
|
|
31
31
|
Attr(name="description", kind="Text", optional=True),
|
|
32
|
-
Attr(name="timeout", kind="Number", default_value=
|
|
32
|
+
Attr(name="timeout", kind="Number", default_value=60),
|
|
33
33
|
],
|
|
34
34
|
relationships=[
|
|
35
35
|
Rel(
|
|
@@ -179,6 +179,7 @@ class SchemaNode(BaseModel):
|
|
|
179
179
|
default_filter: str | None = None
|
|
180
180
|
attributes: list[SchemaAttribute]
|
|
181
181
|
relationships: list[SchemaRelationship]
|
|
182
|
+
display_label: str | None = None
|
|
182
183
|
display_labels: list[str]
|
|
183
184
|
uniqueness_constraints: list[list[str]] | None = None
|
|
184
185
|
|
|
@@ -195,6 +196,7 @@ class SchemaNode(BaseModel):
|
|
|
195
196
|
if attribute.name not in ["id", "attributes", "relationships"]
|
|
196
197
|
],
|
|
197
198
|
"relationships": [relationship.to_dict() for relationship in self.relationships],
|
|
199
|
+
"display_label": self.display_label,
|
|
198
200
|
"display_labels": self.display_labels,
|
|
199
201
|
"uniqueness_constraints": self.uniqueness_constraints,
|
|
200
202
|
}
|
|
@@ -294,11 +296,18 @@ base_node_schema = SchemaNode(
|
|
|
294
296
|
optional=True,
|
|
295
297
|
extra={"update": UpdateSupport.ALLOWED},
|
|
296
298
|
),
|
|
299
|
+
SchemaAttribute(
|
|
300
|
+
name="display_label",
|
|
301
|
+
kind="Text",
|
|
302
|
+
description="Attribute or Jinja2 template to use to generate the display label",
|
|
303
|
+
optional=True,
|
|
304
|
+
extra={"update": UpdateSupport.ALLOWED},
|
|
305
|
+
),
|
|
297
306
|
SchemaAttribute(
|
|
298
307
|
name="display_labels",
|
|
299
308
|
kind="List",
|
|
300
309
|
internal_kind=str,
|
|
301
|
-
description="List of attributes to use to generate the display label",
|
|
310
|
+
description="List of attributes to use to generate the display label (deprecated)",
|
|
302
311
|
optional=True,
|
|
303
312
|
extra={"update": UpdateSupport.ALLOWED},
|
|
304
313
|
),
|
|
@@ -559,7 +568,7 @@ attribute_schema = SchemaNode(
|
|
|
559
568
|
"Mainly relevant for internal object.",
|
|
560
569
|
default_value=False,
|
|
561
570
|
optional=True,
|
|
562
|
-
extra={"update": UpdateSupport.
|
|
571
|
+
extra={"update": UpdateSupport.MIGRATION_REQUIRED},
|
|
563
572
|
),
|
|
564
573
|
SchemaAttribute(
|
|
565
574
|
name="unique",
|
|
@@ -576,7 +585,7 @@ attribute_schema = SchemaNode(
|
|
|
576
585
|
default_value=False,
|
|
577
586
|
override_default_value=False,
|
|
578
587
|
optional=True,
|
|
579
|
-
extra={"update": UpdateSupport.
|
|
588
|
+
extra={"update": UpdateSupport.MIGRATION_REQUIRED},
|
|
580
589
|
),
|
|
581
590
|
SchemaAttribute(
|
|
582
591
|
name="branch",
|