infrahub-server 1.4.12__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/tasks.py +46 -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/calculator.py +2 -2
- 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/delete_query.py +9 -5
- infrahub/core/diff/query/field_summary.py +1 -0
- infrahub/core/diff/query/merge.py +39 -23
- 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 +13 -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/m041_deleted_dup_edges.py +149 -0
- 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/diff.py +61 -16
- infrahub/core/query/ipam.py +16 -4
- infrahub/core/query/node.py +92 -212
- 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 +42 -2
- infrahub/git/integrator.py +22 -14
- infrahub/git/tasks.py +52 -2
- infrahub/graphql/analyzer.py +9 -0
- 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/message_bus/types.py +1 -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 +67 -14
- 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/http/__init__.py +5 -0
- infrahub/services/adapters/workflow/worker.py +14 -3
- infrahub/task_manager/event.py +5 -0
- infrahub/task_manager/models.py +7 -0
- infrahub/task_manager/task.py +73 -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 +10 -2
- infrahub/workflows/catalogue.py +118 -3
- infrahub/workflows/initialization.py +21 -0
- infrahub/workflows/models.py +17 -2
- infrahub/workflows/utils.py +2 -1
- 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.12.dist-info → infrahub_server-1.5.0.dist-info}/METADATA +9 -10
- {infrahub_server-1.4.12.dist-info → infrahub_server-1.5.0.dist-info}/RECORD +233 -176
- 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.12.dist-info → infrahub_server-1.5.0.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.4.12.dist-info → infrahub_server-1.5.0.dist-info}/WHEEL +0 -0
- {infrahub_server-1.4.12.dist-info → infrahub_server-1.5.0.dist-info}/entry_points.txt +0 -0
|
@@ -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",
|
|
@@ -78,7 +78,7 @@ class GeneratedAttributeSchema(HashableModel):
|
|
|
78
78
|
read_only: bool = Field(
|
|
79
79
|
default=False,
|
|
80
80
|
description="Set the attribute as Read-Only, users won't be able to change its value. Mainly relevant for internal object.",
|
|
81
|
-
json_schema_extra={"update": "
|
|
81
|
+
json_schema_extra={"update": "migration_required"},
|
|
82
82
|
)
|
|
83
83
|
unique: bool = Field(
|
|
84
84
|
default=False,
|
|
@@ -88,7 +88,7 @@ class GeneratedAttributeSchema(HashableModel):
|
|
|
88
88
|
optional: bool = Field(
|
|
89
89
|
default=False,
|
|
90
90
|
description="Indicate if this attribute is mandatory or optional.",
|
|
91
|
-
json_schema_extra={"update": "
|
|
91
|
+
json_schema_extra={"update": "migration_required"},
|
|
92
92
|
)
|
|
93
93
|
branch: BranchSupportType | None = Field(
|
|
94
94
|
default=None,
|
|
@@ -58,9 +58,14 @@ class GeneratedBaseNodeSchema(HashableModel):
|
|
|
58
58
|
description="Human friendly and unique identifier for the object.",
|
|
59
59
|
json_schema_extra={"update": "allowed"},
|
|
60
60
|
)
|
|
61
|
+
display_label: str | None = Field(
|
|
62
|
+
default=None,
|
|
63
|
+
description="Attribute or Jinja2 template to use to generate the display label",
|
|
64
|
+
json_schema_extra={"update": "allowed"},
|
|
65
|
+
)
|
|
61
66
|
display_labels: list[str] | None = Field(
|
|
62
67
|
default=None,
|
|
63
|
-
description="List of attributes to use to generate the display label",
|
|
68
|
+
description="List of attributes to use to generate the display label (deprecated)",
|
|
64
69
|
json_schema_extra={"update": "allowed"},
|
|
65
70
|
)
|
|
66
71
|
include_in_menu: bool | None = Field(
|
infrahub/core/schema/manager.py
CHANGED
|
@@ -163,6 +163,9 @@ class SchemaManager(NodeManager):
|
|
|
163
163
|
self._branches[name] = schema
|
|
164
164
|
self._branch_hash_by_name[name] = schema.get_hash()
|
|
165
165
|
|
|
166
|
+
def has_schema_branch(self, name: str) -> bool:
|
|
167
|
+
return name in self._branches
|
|
168
|
+
|
|
166
169
|
def process_schema_branch(self, name: str) -> None:
|
|
167
170
|
schema_branch = self.get_schema_branch(name=name)
|
|
168
171
|
schema_branch.process()
|
|
@@ -91,7 +91,6 @@ class RelationshipSchema(GeneratedRelationshipSchema):
|
|
|
91
91
|
include_match: bool = True,
|
|
92
92
|
param_prefix: str | None = None,
|
|
93
93
|
partial_match: bool = False,
|
|
94
|
-
support_profiles: bool = False, # noqa: ARG002
|
|
95
94
|
) -> tuple[list[QueryElement], dict[str, Any], list[str]]:
|
|
96
95
|
"""Generate Query String Snippet to filter the right node."""
|
|
97
96
|
|