infrahub-server 1.6.3__py3-none-any.whl → 1.7.0b0__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 +4 -2
- infrahub/api/schema.py +3 -1
- infrahub/artifacts/tasks.py +1 -0
- infrahub/auth.py +2 -2
- infrahub/cli/db.py +6 -6
- infrahub/computed_attribute/gather.py +3 -4
- infrahub/computed_attribute/tasks.py +23 -6
- infrahub/config.py +8 -0
- infrahub/constants/enums.py +12 -0
- infrahub/core/account.py +5 -8
- infrahub/core/attribute.py +106 -108
- infrahub/core/branch/models.py +44 -71
- infrahub/core/branch/tasks.py +5 -3
- infrahub/core/changelog/diff.py +1 -20
- infrahub/core/changelog/models.py +0 -7
- infrahub/core/constants/__init__.py +17 -0
- infrahub/core/constants/database.py +0 -1
- infrahub/core/constants/schema.py +0 -1
- infrahub/core/convert_object_type/repository_conversion.py +3 -4
- infrahub/core/diff/data_check_synchronizer.py +3 -2
- infrahub/core/diff/enricher/cardinality_one.py +1 -1
- infrahub/core/diff/merger/merger.py +27 -1
- infrahub/core/diff/merger/serializer.py +3 -10
- infrahub/core/diff/model/diff.py +1 -1
- infrahub/core/diff/query/merge.py +376 -135
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/graph/constraints.py +2 -2
- infrahub/core/graph/schema.py +2 -12
- infrahub/core/manager.py +132 -126
- infrahub/core/metadata/__init__.py +0 -0
- infrahub/core/metadata/interface.py +37 -0
- infrahub/core/metadata/model.py +31 -0
- infrahub/core/metadata/query/__init__.py +0 -0
- infrahub/core/metadata/query/node_metadata.py +301 -0
- infrahub/core/migrations/graph/__init__.py +4 -0
- infrahub/core/migrations/graph/m013_convert_git_password_credential.py +3 -8
- infrahub/core/migrations/graph/m017_add_core_profile.py +5 -2
- infrahub/core/migrations/graph/m018_uniqueness_nulls.py +2 -1
- infrahub/core/migrations/graph/m019_restore_rels_to_time.py +0 -10
- infrahub/core/migrations/graph/m020_duplicate_edges.py +0 -8
- infrahub/core/migrations/graph/m025_uniqueness_nulls.py +2 -1
- infrahub/core/migrations/graph/m026_0000_prefix_fix.py +2 -1
- infrahub/core/migrations/graph/m029_duplicates_cleanup.py +0 -1
- infrahub/core/migrations/graph/m031_check_number_attributes.py +2 -2
- infrahub/core/migrations/graph/m038_redo_0000_prefix_fix.py +2 -1
- infrahub/core/migrations/graph/m049_remove_is_visible_relationship.py +38 -0
- infrahub/core/migrations/graph/m050_backfill_vertex_metadata.py +168 -0
- infrahub/core/migrations/query/attribute_add.py +17 -6
- infrahub/core/migrations/query/attribute_remove.py +19 -5
- infrahub/core/migrations/query/attribute_rename.py +21 -5
- infrahub/core/migrations/query/node_duplicate.py +19 -4
- infrahub/core/migrations/schema/attribute_kind_update.py +25 -7
- infrahub/core/migrations/schema/attribute_supports_profile.py +3 -1
- infrahub/core/migrations/schema/models.py +3 -0
- infrahub/core/migrations/schema/node_attribute_add.py +4 -1
- infrahub/core/migrations/schema/node_remove.py +24 -2
- infrahub/core/migrations/schema/tasks.py +4 -1
- infrahub/core/migrations/shared.py +13 -6
- infrahub/core/models.py +6 -6
- infrahub/core/node/__init__.py +156 -57
- infrahub/core/node/create.py +7 -3
- infrahub/core/node/standard.py +100 -14
- infrahub/core/property.py +0 -1
- infrahub/core/protocols_base.py +6 -2
- infrahub/core/query/__init__.py +6 -7
- infrahub/core/query/attribute.py +161 -46
- infrahub/core/query/branch.py +57 -69
- infrahub/core/query/diff.py +4 -4
- infrahub/core/query/node.py +618 -180
- infrahub/core/query/relationship.py +449 -300
- infrahub/core/query/standard_node.py +25 -5
- infrahub/core/query/utils.py +2 -4
- infrahub/core/relationship/constraints/profiles_removal.py +168 -0
- infrahub/core/relationship/model.py +293 -139
- infrahub/core/schema/attribute_parameters.py +1 -28
- infrahub/core/schema/attribute_schema.py +17 -11
- infrahub/core/schema/manager.py +63 -43
- infrahub/core/schema/relationship_schema.py +6 -2
- infrahub/core/schema/schema_branch.py +48 -76
- infrahub/core/task/task.py +4 -2
- infrahub/core/utils.py +0 -22
- infrahub/core/validators/attribute/kind.py +2 -5
- infrahub/core/validators/determiner.py +3 -3
- infrahub/database/__init__.py +3 -3
- infrahub/dependencies/builder/constraint/grouped/node_runner.py +2 -0
- infrahub/dependencies/builder/constraint/relationship_manager/profiles_removal.py +8 -0
- infrahub/dependencies/registry.py +2 -0
- infrahub/display_labels/tasks.py +12 -3
- infrahub/git/integrator.py +18 -18
- infrahub/git/tasks.py +1 -1
- infrahub/graphql/app.py +2 -2
- infrahub/graphql/constants.py +3 -0
- infrahub/graphql/context.py +1 -1
- infrahub/graphql/initialization.py +11 -0
- infrahub/graphql/loaders/account.py +134 -0
- infrahub/graphql/loaders/node.py +5 -12
- infrahub/graphql/loaders/peers.py +5 -7
- infrahub/graphql/manager.py +158 -18
- infrahub/graphql/metadata.py +91 -0
- infrahub/graphql/models.py +33 -3
- infrahub/graphql/mutations/account.py +5 -5
- infrahub/graphql/mutations/attribute.py +0 -2
- infrahub/graphql/mutations/branch.py +9 -5
- infrahub/graphql/mutations/computed_attribute.py +1 -1
- infrahub/graphql/mutations/display_label.py +1 -1
- infrahub/graphql/mutations/hfid.py +1 -1
- infrahub/graphql/mutations/ipam.py +4 -6
- infrahub/graphql/mutations/main.py +9 -4
- infrahub/graphql/mutations/profile.py +16 -22
- infrahub/graphql/mutations/proposed_change.py +4 -4
- infrahub/graphql/mutations/relationship.py +40 -10
- infrahub/graphql/mutations/repository.py +14 -12
- infrahub/graphql/mutations/schema.py +2 -2
- infrahub/graphql/queries/branch.py +62 -6
- infrahub/graphql/queries/diff/tree.py +5 -5
- infrahub/graphql/resolvers/account_metadata.py +84 -0
- infrahub/graphql/resolvers/ipam.py +6 -8
- infrahub/graphql/resolvers/many_relationship.py +77 -35
- infrahub/graphql/resolvers/resolver.py +16 -12
- infrahub/graphql/resolvers/single_relationship.py +87 -23
- infrahub/graphql/subscription/graphql_query.py +2 -0
- infrahub/graphql/types/__init__.py +0 -1
- infrahub/graphql/types/attribute.py +10 -5
- infrahub/graphql/types/branch.py +40 -53
- infrahub/graphql/types/enums.py +3 -0
- infrahub/graphql/types/metadata.py +28 -0
- infrahub/graphql/types/node.py +22 -2
- infrahub/graphql/types/relationship.py +10 -2
- infrahub/graphql/types/standard_node.py +4 -3
- infrahub/hfid/tasks.py +12 -3
- infrahub/profiles/gather.py +56 -0
- infrahub/profiles/mandatory_fields_checker.py +116 -0
- infrahub/profiles/models.py +66 -0
- infrahub/profiles/node_applier.py +153 -12
- infrahub/profiles/queries/get_profile_data.py +143 -31
- infrahub/profiles/tasks.py +79 -27
- infrahub/profiles/triggers.py +22 -0
- infrahub/proposed_change/tasks.py +4 -1
- infrahub/tasks/artifact.py +1 -0
- infrahub/transformations/tasks.py +2 -2
- infrahub/trigger/catalogue.py +2 -0
- infrahub/trigger/models.py +1 -0
- infrahub/trigger/setup.py +3 -3
- infrahub/trigger/tasks.py +3 -0
- infrahub/validators/tasks.py +1 -0
- infrahub/webhook/models.py +1 -1
- infrahub/webhook/tasks.py +1 -1
- infrahub/workers/dependencies.py +9 -3
- infrahub/workers/infrahub_async.py +13 -4
- infrahub/workflows/catalogue.py +19 -0
- infrahub_sdk/node/constants.py +1 -0
- infrahub_sdk/node/related_node.py +13 -4
- infrahub_sdk/node/relationship.py +8 -0
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0b0.dist-info}/METADATA +17 -16
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0b0.dist-info}/RECORD +161 -143
- infrahub_testcontainers/container.py +3 -3
- infrahub_testcontainers/docker-compose-cluster.test.yml +7 -7
- infrahub_testcontainers/docker-compose.test.yml +13 -5
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0b0.dist-info}/WHEEL +0 -0
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0b0.dist-info}/entry_points.txt +0 -0
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0b0.dist-info}/licenses/LICENSE.txt +0 -0
infrahub/graphql/manager.py
CHANGED
|
@@ -21,8 +21,10 @@ from infrahub.core.schema import (
|
|
|
21
21
|
from infrahub.graphql.mutations.attribute import BaseAttributeCreate, BaseAttributeUpdate
|
|
22
22
|
from infrahub.graphql.mutations.graphql_query import InfrahubGraphQLQueryMutation
|
|
23
23
|
from infrahub.graphql.mutations.profile import InfrahubProfileMutation
|
|
24
|
+
from infrahub.graphql.types.metadata import OrderInput
|
|
24
25
|
from infrahub.types import ATTRIBUTE_TYPES, InfrahubDataType, get_attribute_type
|
|
25
26
|
|
|
27
|
+
from .constants import NODE_METADATA_TYPE, RELATIONSHIP_METADATA_TYPE
|
|
26
28
|
from .directives import DIRECTIVES
|
|
27
29
|
from .enums import generate_graphql_enum, get_enum_attribute_type_name
|
|
28
30
|
from .metrics import SCHEMA_GENERATE_GRAPHQL_METRICS
|
|
@@ -42,6 +44,7 @@ from .mutations.resource_manager import (
|
|
|
42
44
|
)
|
|
43
45
|
from .mutations.webhook import InfrahubWebhookMutation
|
|
44
46
|
from .registry import registry
|
|
47
|
+
from .resolvers.account_metadata import account_metadata_resolver
|
|
45
48
|
from .resolvers.ipam import ipam_paginated_list_resolver
|
|
46
49
|
from .resolvers.resolver import (
|
|
47
50
|
account_resolver,
|
|
@@ -65,8 +68,10 @@ from .types import (
|
|
|
65
68
|
)
|
|
66
69
|
from .types.attribute import BaseAttribute as BaseAttributeType
|
|
67
70
|
from .types.attribute import TextAttributeType
|
|
71
|
+
from .types.branch import InfrahubBranchEdge
|
|
68
72
|
from .types.context import ContextInput
|
|
69
73
|
from .types.event import EVENT_TYPES
|
|
74
|
+
from .types.node import InfrahubObjectWithoutMeta
|
|
70
75
|
|
|
71
76
|
if TYPE_CHECKING:
|
|
72
77
|
from graphql import GraphQLSchema
|
|
@@ -82,10 +87,6 @@ class DeleteInput(graphene.InputObjectType):
|
|
|
82
87
|
GraphQLTypes = type[InfrahubMutation] | type[BaseAttributeType] | type[graphene.Interface] | type[graphene.ObjectType]
|
|
83
88
|
|
|
84
89
|
|
|
85
|
-
class OrderInput(graphene.InputObjectType):
|
|
86
|
-
disable = graphene.Boolean(required=False)
|
|
87
|
-
|
|
88
|
-
|
|
89
90
|
@dataclass
|
|
90
91
|
class GraphqlMutations:
|
|
91
92
|
create: type[InfrahubMutation]
|
|
@@ -243,15 +244,37 @@ class GraphQLSchemaManager:
|
|
|
243
244
|
self.set_type(name=event._meta.name, graphql_type=event)
|
|
244
245
|
|
|
245
246
|
def _load_node_interface(self) -> None:
|
|
247
|
+
"""Load the base CoreNode interface. Edged/paginated objects are created later in generate_object_types."""
|
|
248
|
+
node_interface_schema = GenericSchema(
|
|
249
|
+
name="Node", namespace="Core", description="Interface for all nodes in Infrahub"
|
|
250
|
+
)
|
|
251
|
+
self.generate_interface_object(schema=node_interface_schema, populate_cache=True)
|
|
252
|
+
|
|
253
|
+
def _complete_node_interface(self, node_metadata: type[InfrahubObject]) -> None:
|
|
254
|
+
"""Complete the CoreNode interface by creating its edged and paginated objects."""
|
|
246
255
|
node_interface_schema = GenericSchema(
|
|
247
256
|
name="Node", namespace="Core", description="Interface for all nodes in Infrahub"
|
|
248
257
|
)
|
|
249
|
-
|
|
258
|
+
# Re-call generate_interface_object to get the InterfaceReference (will use cached version)
|
|
259
|
+
interface = self.generate_interface_object(schema=node_interface_schema, populate_cache=False)
|
|
250
260
|
edged_interface = self.generate_graphql_edged_object(
|
|
251
|
-
schema=node_interface_schema, node=interface, populate_cache=True
|
|
261
|
+
schema=node_interface_schema, node=interface, node_metadata=node_metadata, populate_cache=True
|
|
252
262
|
)
|
|
253
263
|
self.generate_graphql_paginated_object(schema=node_interface_schema, edge=edged_interface, populate_cache=True)
|
|
254
264
|
|
|
265
|
+
def _patch_static_types(self, node_metadata: type[InfrahubObject]) -> None:
|
|
266
|
+
"""Patch statically defined GraphQL types to use dynamically generated types.
|
|
267
|
+
|
|
268
|
+
Some GraphQL types like InfrahubBranchEdge are defined statically but need to
|
|
269
|
+
reference dynamically generated types (like node_metadata with GenericAccount).
|
|
270
|
+
This method patches those static types after the dynamic types are created.
|
|
271
|
+
|
|
272
|
+
The method checks if the patch has already been applied to avoid redundant updates.
|
|
273
|
+
"""
|
|
274
|
+
current_field = InfrahubBranchEdge._meta.fields.get("node_metadata")
|
|
275
|
+
if current_field is None or current_field.type != node_metadata:
|
|
276
|
+
InfrahubBranchEdge._meta.fields["node_metadata"] = graphene.Field(node_metadata, required=True)
|
|
277
|
+
|
|
255
278
|
def _load_all_enum_types(self, node_schemas: Iterable[MainSchemaTypes]) -> None:
|
|
256
279
|
for node_schema in node_schemas:
|
|
257
280
|
self._load_enum_type(node_schema=node_schema)
|
|
@@ -311,25 +334,49 @@ class GraphQLSchemaManager:
|
|
|
311
334
|
|
|
312
335
|
full_schema = self.schema.get_all(duplicate=False)
|
|
313
336
|
|
|
314
|
-
# Generate all GraphQL Interface
|
|
337
|
+
# Pass 1: Generate all GraphQL Interface objects first (without edged/paginated)
|
|
338
|
+
# This ensures GENERICACCOUNT exists before we create node_metadata
|
|
315
339
|
for node_schema in full_schema.values():
|
|
316
|
-
if
|
|
317
|
-
|
|
318
|
-
interface = self.generate_interface_object(schema=node_schema, populate_cache=True)
|
|
319
|
-
edged_interface = self.generate_graphql_edged_object(
|
|
320
|
-
schema=node_schema, node=interface, populate_cache=True
|
|
321
|
-
)
|
|
322
|
-
self.generate_graphql_paginated_object(schema=node_schema, edge=edged_interface, populate_cache=True)
|
|
340
|
+
if isinstance(node_schema, GenericSchema):
|
|
341
|
+
self.generate_interface_object(schema=node_schema, populate_cache=True)
|
|
323
342
|
|
|
324
343
|
# Define LineageSource and LineageOwner
|
|
325
344
|
data_source = self.get_type(name=InfrahubKind.LINEAGESOURCE)
|
|
326
345
|
data_owner = self.get_type(name=InfrahubKind.LINEAGEOWNER)
|
|
327
346
|
self.define_relationship_property(data_source=data_source, data_owner=data_owner)
|
|
347
|
+
|
|
348
|
+
# Now that GENERICACCOUNT exists, create node_metadata and relationship_metadata
|
|
349
|
+
account_type = self.get_type(name=InfrahubKind.GENERICACCOUNT)
|
|
350
|
+
self.define_node_metadata(account_type=account_type)
|
|
351
|
+
self.define_relationship_metadata(account_type=account_type)
|
|
352
|
+
node_metadata = self.get_type(name=NODE_METADATA_TYPE)
|
|
353
|
+
relationship_metadata = self.get_type(name=RELATIONSHIP_METADATA_TYPE)
|
|
354
|
+
|
|
355
|
+
# Complete the CoreNode interface (edged/paginated) now that node_metadata exists
|
|
356
|
+
self._complete_node_interface(node_metadata=node_metadata)
|
|
357
|
+
|
|
358
|
+
# Patch statically defined types to use dynamically generated types
|
|
359
|
+
self._patch_static_types(node_metadata=node_metadata)
|
|
360
|
+
|
|
328
361
|
relationship_property = self.get_type(name="RelationshipProperty")
|
|
329
362
|
for data_type in ATTRIBUTE_TYPES.values():
|
|
330
363
|
gql_type = self.get_type(name=data_type.get_graphql_type_name())
|
|
331
364
|
gql_type._meta.fields["source"] = graphene.Field(data_source)
|
|
332
365
|
gql_type._meta.fields["owner"] = graphene.Field(data_owner)
|
|
366
|
+
gql_type._meta.fields["updated_by"] = graphene.Field(
|
|
367
|
+
account_type, required=False, resolver=account_metadata_resolver
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
# Pass 2: Generate edged/paginated objects for all GenericSchema interfaces
|
|
371
|
+
for node_schema in full_schema.values():
|
|
372
|
+
if not isinstance(node_schema, GenericSchema):
|
|
373
|
+
continue
|
|
374
|
+
# Re-call generate_interface_object to get the InterfaceReference (will use cached version)
|
|
375
|
+
interface = self.generate_interface_object(schema=node_schema, populate_cache=False)
|
|
376
|
+
edged_interface = self.generate_graphql_edged_object(
|
|
377
|
+
schema=node_schema, node=interface, node_metadata=node_metadata, populate_cache=True
|
|
378
|
+
)
|
|
379
|
+
self.generate_graphql_paginated_object(schema=node_schema, edge=edged_interface, populate_cache=True)
|
|
333
380
|
|
|
334
381
|
# Generate all Nested, Edged and NestedEdged Interfaces and store them in the registry
|
|
335
382
|
for node_name, node_schema in full_schema.items():
|
|
@@ -340,7 +387,9 @@ class GraphQLSchemaManager:
|
|
|
340
387
|
nested_edged_interface = self.generate_nested_interface_object(
|
|
341
388
|
schema=node_schema,
|
|
342
389
|
base_interface=node_interface,
|
|
390
|
+
node_metadata=node_metadata,
|
|
343
391
|
relation_property=relationship_property,
|
|
392
|
+
relationship_metadata=relationship_metadata,
|
|
344
393
|
)
|
|
345
394
|
|
|
346
395
|
nested_interface = self.generate_paginated_interface_object(
|
|
@@ -356,11 +405,13 @@ class GraphQLSchemaManager:
|
|
|
356
405
|
if isinstance(node_schema, NodeSchema | ProfileSchema | TemplateSchema):
|
|
357
406
|
node_object_type = self.generate_graphql_object(schema=node_schema, populate_cache=True)
|
|
358
407
|
node_type_edged = self.generate_graphql_edged_object(
|
|
359
|
-
schema=node_schema, node=node_object_type, populate_cache=True
|
|
408
|
+
schema=node_schema, node=node_object_type, node_metadata=node_metadata, populate_cache=True
|
|
360
409
|
)
|
|
361
410
|
nested_node_type_edged = self.generate_graphql_edged_object(
|
|
362
411
|
schema=node_schema,
|
|
363
412
|
node=node_object_type,
|
|
413
|
+
node_metadata=node_metadata,
|
|
414
|
+
relationship_metadata=relationship_metadata,
|
|
364
415
|
relation_property=relationship_property,
|
|
365
416
|
populate_cache=True,
|
|
366
417
|
)
|
|
@@ -615,7 +666,6 @@ class GraphQLSchemaManager:
|
|
|
615
666
|
}
|
|
616
667
|
|
|
617
668
|
main_attrs = {
|
|
618
|
-
"is_visible": graphene.Boolean(required=False),
|
|
619
669
|
"is_protected": graphene.Boolean(required=False),
|
|
620
670
|
"updated_at": graphene.DateTime(required=False),
|
|
621
671
|
"source": graphene.Field(data_source),
|
|
@@ -627,6 +677,42 @@ class GraphQLSchemaManager:
|
|
|
627
677
|
|
|
628
678
|
self.set_type(name=type_name, graphql_type=relationship_property)
|
|
629
679
|
|
|
680
|
+
def define_node_metadata(self, account_type: type[InfrahubObject]) -> None:
|
|
681
|
+
meta_attrs = {
|
|
682
|
+
"name": NODE_METADATA_TYPE,
|
|
683
|
+
"description": "Defines node metadata information",
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
main_attrs = {
|
|
687
|
+
"created_at": graphene.DateTime(required=False),
|
|
688
|
+
"created_by": graphene.Field(account_type, required=False, resolver=account_metadata_resolver),
|
|
689
|
+
"updated_at": graphene.DateTime(required=False),
|
|
690
|
+
"updated_by": graphene.Field(account_type, required=False, resolver=account_metadata_resolver),
|
|
691
|
+
"Meta": type("Meta", (object,), meta_attrs),
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
node_metadata = type(NODE_METADATA_TYPE, (graphene.ObjectType,), main_attrs)
|
|
695
|
+
|
|
696
|
+
self.set_type(name=NODE_METADATA_TYPE, graphql_type=node_metadata)
|
|
697
|
+
|
|
698
|
+
def define_relationship_metadata(self, account_type: type[InfrahubObject]) -> None:
|
|
699
|
+
meta_attrs = {
|
|
700
|
+
"name": RELATIONSHIP_METADATA_TYPE,
|
|
701
|
+
"description": "Defines relationship metadata information",
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
main_attrs = {
|
|
705
|
+
"created_at": graphene.DateTime(required=False),
|
|
706
|
+
"created_by": graphene.Field(account_type, required=False, resolver=account_metadata_resolver),
|
|
707
|
+
"updated_at": graphene.DateTime(required=False),
|
|
708
|
+
"updated_by": graphene.Field(account_type, required=False, resolver=account_metadata_resolver),
|
|
709
|
+
"Meta": type("Meta", (object,), meta_attrs),
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
relationship_metadata = type(RELATIONSHIP_METADATA_TYPE, (graphene.ObjectType,), main_attrs)
|
|
713
|
+
|
|
714
|
+
self.set_type(name=RELATIONSHIP_METADATA_TYPE, graphql_type=relationship_metadata)
|
|
715
|
+
|
|
630
716
|
def generate_graphql_mutations(
|
|
631
717
|
self,
|
|
632
718
|
schema: NodeSchema | ProfileSchema | TemplateSchema,
|
|
@@ -935,6 +1021,9 @@ class GraphQLSchemaManager:
|
|
|
935
1021
|
filters.update(get_attribute_type().get_graphql_filters(name="any"))
|
|
936
1022
|
filters["partial_match"] = graphene.Boolean()
|
|
937
1023
|
|
|
1024
|
+
# Add metadata filters for filtering by created_by, updated_by, created_at, updated_at
|
|
1025
|
+
filters.update(self._generate_metadata_filters())
|
|
1026
|
+
|
|
938
1027
|
if schema.kind in [InfrahubKind.IPADDRESS, InfrahubKind.IPPREFIX]:
|
|
939
1028
|
# This is only available for IPAM generics
|
|
940
1029
|
filters["include_available"] = graphene.Boolean()
|
|
@@ -961,10 +1050,53 @@ class GraphQLSchemaManager:
|
|
|
961
1050
|
|
|
962
1051
|
return filters
|
|
963
1052
|
|
|
1053
|
+
def _generate_metadata_filters(self) -> dict[str, Any]:
|
|
1054
|
+
"""Generate GraphQL filters for object-level metadata fields.
|
|
1055
|
+
|
|
1056
|
+
These filters allow querying nodes based on their metadata:
|
|
1057
|
+
- created_by: Filter by the account that created the node
|
|
1058
|
+
- updated_by: Filter by the account that last updated the node
|
|
1059
|
+
- created_at: Filter by creation timestamp
|
|
1060
|
+
- updated_at: Filter by last update timestamp
|
|
1061
|
+
|
|
1062
|
+
Returns:
|
|
1063
|
+
dict: Filter definitions with names as keys and graphene types as values
|
|
1064
|
+
"""
|
|
1065
|
+
return {
|
|
1066
|
+
# Account-based filters (created_by)
|
|
1067
|
+
"node_metadata__created_by__id": graphene.ID(description="Filter by exact creator account UUID"),
|
|
1068
|
+
"node_metadata__created_by__ids": graphene.List(
|
|
1069
|
+
graphene.ID, description="Filter by list of creator account UUIDs"
|
|
1070
|
+
),
|
|
1071
|
+
# Account-based filters (updated_by)
|
|
1072
|
+
"node_metadata__updated_by__id": graphene.ID(description="Filter by exact updater account UUID"),
|
|
1073
|
+
"node_metadata__updated_by__ids": graphene.List(
|
|
1074
|
+
graphene.ID, description="Filter by list of updater account UUIDs"
|
|
1075
|
+
),
|
|
1076
|
+
# DateTime-based filters (created_at)
|
|
1077
|
+
"node_metadata__created_at": graphene.DateTime(description="Filter by exact creation timestamp"),
|
|
1078
|
+
"node_metadata__created_at__before": graphene.DateTime(
|
|
1079
|
+
description="Filter for objects created before this timestamp"
|
|
1080
|
+
),
|
|
1081
|
+
"node_metadata__created_at__after": graphene.DateTime(
|
|
1082
|
+
description="Filter for objects created after this timestamp"
|
|
1083
|
+
),
|
|
1084
|
+
# DateTime-based filters (updated_at)
|
|
1085
|
+
"node_metadata__updated_at": graphene.DateTime(description="Filter by exact update timestamp"),
|
|
1086
|
+
"node_metadata__updated_at__before": graphene.DateTime(
|
|
1087
|
+
description="Filter for objects updated before this timestamp"
|
|
1088
|
+
),
|
|
1089
|
+
"node_metadata__updated_at__after": graphene.DateTime(
|
|
1090
|
+
description="Filter for objects updated after this timestamp"
|
|
1091
|
+
),
|
|
1092
|
+
}
|
|
1093
|
+
|
|
964
1094
|
def generate_graphql_edged_object(
|
|
965
1095
|
self,
|
|
966
1096
|
schema: MainSchemaTypes,
|
|
967
1097
|
node: InterfaceReference | InfrahubObjectReference,
|
|
1098
|
+
node_metadata: type[InfrahubObject],
|
|
1099
|
+
relationship_metadata: type[InfrahubObject] | None = None,
|
|
968
1100
|
relation_property: type[InfrahubObject] | None = None,
|
|
969
1101
|
populate_cache: bool = False,
|
|
970
1102
|
) -> InfrahubEdgedReference:
|
|
@@ -987,15 +1119,18 @@ class GraphQLSchemaManager:
|
|
|
987
1119
|
|
|
988
1120
|
main_attrs: dict[str, Any] = {
|
|
989
1121
|
"node": graphene.Field(node.reference, required=False),
|
|
1122
|
+
"node_metadata": graphene.Field(node_metadata, required=False),
|
|
990
1123
|
"Meta": type("Meta", (object,), meta_attrs),
|
|
991
1124
|
}
|
|
992
1125
|
|
|
993
1126
|
if relation_property:
|
|
994
1127
|
main_attrs["properties"] = graphene.Field(relation_property, required=False)
|
|
1128
|
+
if relationship_metadata:
|
|
1129
|
+
main_attrs["relationship_metadata"] = graphene.Field(relationship_metadata, required=False)
|
|
995
1130
|
|
|
996
1131
|
graphql_edged_object = registry.get_edge_type(reference_hash=edge_hash, schema_hash=self.schema_hash)
|
|
997
1132
|
if not graphql_edged_object:
|
|
998
|
-
graphql_edged_object = type(object_name, (
|
|
1133
|
+
graphql_edged_object = type(object_name, (InfrahubObjectWithoutMeta,), main_attrs)
|
|
999
1134
|
registry.set_edge_type(
|
|
1000
1135
|
reference=graphql_edged_object, reference_hash=edge_hash, schema_hash=self.schema_hash
|
|
1001
1136
|
)
|
|
@@ -1039,7 +1174,7 @@ class GraphQLSchemaManager:
|
|
|
1039
1174
|
)
|
|
1040
1175
|
if not graphql_paginated_object:
|
|
1041
1176
|
main_attrs["Meta"] = type("Meta", (object,), meta_attrs)
|
|
1042
|
-
graphql_paginated_object = type(object_name, (
|
|
1177
|
+
graphql_paginated_object = type(object_name, (InfrahubObjectWithoutMeta,), main_attrs)
|
|
1043
1178
|
registry.set_paginated_type(
|
|
1044
1179
|
reference=graphql_paginated_object, reference_hash=paginated_hash, schema_hash=self.schema_hash
|
|
1045
1180
|
)
|
|
@@ -1054,6 +1189,8 @@ class GraphQLSchemaManager:
|
|
|
1054
1189
|
schema: GenericSchema,
|
|
1055
1190
|
relation_property: graphene.ObjectType,
|
|
1056
1191
|
base_interface: graphene.ObjectType,
|
|
1192
|
+
node_metadata: type[InfrahubObject],
|
|
1193
|
+
relationship_metadata: type[InfrahubObject] | None = None,
|
|
1057
1194
|
populate_cache: bool = False,
|
|
1058
1195
|
) -> type[InfrahubObject]:
|
|
1059
1196
|
meta_attrs: dict[str, Any] = {
|
|
@@ -1065,11 +1202,14 @@ class GraphQLSchemaManager:
|
|
|
1065
1202
|
main_attrs: dict[str, Any] = {
|
|
1066
1203
|
"node": graphene.Field(base_interface, required=False),
|
|
1067
1204
|
"_updated_at": graphene.DateTime(required=False),
|
|
1205
|
+
"node_metadata": graphene.Field(node_metadata, required=True),
|
|
1068
1206
|
"Meta": type("Meta", (object,), meta_attrs),
|
|
1069
1207
|
}
|
|
1070
1208
|
|
|
1071
1209
|
if relation_property:
|
|
1072
1210
|
main_attrs["properties"] = graphene.Field(relation_property, required=False)
|
|
1211
|
+
if relationship_metadata:
|
|
1212
|
+
main_attrs["relationship_metadata"] = graphene.Field(relationship_metadata, required=False)
|
|
1073
1213
|
|
|
1074
1214
|
object_name = f"NestedEdged{schema.kind}"
|
|
1075
1215
|
md5hash = hashlib.md5(usedforsecurity=False)
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from infrahub.core.constants import MetadataOptions
|
|
6
|
+
from infrahub.core.metadata.model import MetadataQueryOptions
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_metadata_options_from_fields(fields: dict[str, Any]) -> MetadataOptions:
|
|
10
|
+
"""Convert a dict of requested metadata fields to MetadataOptions flags."""
|
|
11
|
+
options = MetadataOptions.NONE
|
|
12
|
+
if "created_at" in fields:
|
|
13
|
+
options |= MetadataOptions.CREATED_AT
|
|
14
|
+
if "created_by" in fields:
|
|
15
|
+
options |= MetadataOptions.CREATED_BY
|
|
16
|
+
if "updated_at" in fields or "_updated_at" in fields:
|
|
17
|
+
options |= MetadataOptions.UPDATED_AT
|
|
18
|
+
if "updated_by" in fields:
|
|
19
|
+
options |= MetadataOptions.UPDATED_BY
|
|
20
|
+
if "source" in fields or "_relation__source" in fields:
|
|
21
|
+
options |= MetadataOptions.SOURCE
|
|
22
|
+
if "owner" in fields or "_relation__owner" in fields:
|
|
23
|
+
options |= MetadataOptions.OWNER
|
|
24
|
+
if "is_protected" in fields:
|
|
25
|
+
options |= MetadataOptions.IS_PROTECTED
|
|
26
|
+
return options
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _extract_attribute_metadata_from_node_fields(node_fields: dict[str, Any]) -> MetadataOptions:
|
|
30
|
+
"""Extract attribute-level metadata options from nested node fields.
|
|
31
|
+
|
|
32
|
+
This handles GraphQL query structures like:
|
|
33
|
+
name {
|
|
34
|
+
value
|
|
35
|
+
updated_by { id }
|
|
36
|
+
updated_at
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
Where `updated_by` and `updated_at` are attribute-level metadata.
|
|
40
|
+
"""
|
|
41
|
+
attribute_metadata_options = MetadataOptions.NONE
|
|
42
|
+
|
|
43
|
+
for field_properties_dict in node_fields.values():
|
|
44
|
+
if not field_properties_dict or not isinstance(field_properties_dict, dict):
|
|
45
|
+
continue
|
|
46
|
+
|
|
47
|
+
# For attributes, metadata fields are at the same level as 'value'
|
|
48
|
+
# Check if this looks like an attribute (has metadata fields directly)
|
|
49
|
+
metadata_properties_dict = field_properties_dict
|
|
50
|
+
|
|
51
|
+
# Extract metadata from attribute fields
|
|
52
|
+
if "updated_at" in metadata_properties_dict or "_updated_at" in metadata_properties_dict:
|
|
53
|
+
attribute_metadata_options |= MetadataOptions.UPDATED_AT
|
|
54
|
+
if "updated_by" in metadata_properties_dict:
|
|
55
|
+
attribute_metadata_options |= MetadataOptions.UPDATED_BY
|
|
56
|
+
if "created_at" in metadata_properties_dict:
|
|
57
|
+
attribute_metadata_options |= MetadataOptions.CREATED_AT
|
|
58
|
+
if "created_by" in metadata_properties_dict:
|
|
59
|
+
attribute_metadata_options |= MetadataOptions.CREATED_BY
|
|
60
|
+
if "source" in metadata_properties_dict or "_relation__source" in metadata_properties_dict:
|
|
61
|
+
attribute_metadata_options |= MetadataOptions.SOURCE
|
|
62
|
+
if "owner" in metadata_properties_dict or "_relation__owner" in metadata_properties_dict:
|
|
63
|
+
attribute_metadata_options |= MetadataOptions.OWNER
|
|
64
|
+
|
|
65
|
+
return attribute_metadata_options
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def build_metadata_query_options(
|
|
69
|
+
node_metadata_fields: dict[str, Any] | None = None,
|
|
70
|
+
relationship_metadata_fields: dict[str, Any] | None = None,
|
|
71
|
+
node_fields: dict[str, Any] | None = None,
|
|
72
|
+
) -> MetadataQueryOptions:
|
|
73
|
+
"""Build MetadataQueryOptions from GraphQL extracted fields.
|
|
74
|
+
|
|
75
|
+
Args:
|
|
76
|
+
node_metadata_fields: Fields from the node_metadata section of a GraphQL query.
|
|
77
|
+
relationship_metadata_fields: Fields from the relationship_metadata section.
|
|
78
|
+
node_fields: The node fields dict to extract attribute-level metadata from.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
MetadataQueryOptions with appropriate flags set for each level.
|
|
82
|
+
"""
|
|
83
|
+
node_level = get_metadata_options_from_fields(node_metadata_fields or {})
|
|
84
|
+
relationship_level = get_metadata_options_from_fields(relationship_metadata_fields or {})
|
|
85
|
+
attribute_level = _extract_attribute_metadata_from_node_fields(node_fields or {})
|
|
86
|
+
|
|
87
|
+
return MetadataQueryOptions(
|
|
88
|
+
node_level=node_level,
|
|
89
|
+
attribute_level=attribute_level,
|
|
90
|
+
relationship_level=relationship_level,
|
|
91
|
+
)
|
infrahub/graphql/models.py
CHANGED
|
@@ -1,6 +1,36 @@
|
|
|
1
|
-
from
|
|
1
|
+
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import TYPE_CHECKING, Any
|
|
3
5
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
+
from infrahub.exceptions import ValidationError
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from infrahub.constants.enums import OrderDirection
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class NodeMetaOrder:
|
|
14
|
+
created_at: OrderDirection | None = None
|
|
15
|
+
updated_at: OrderDirection | None = None
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class OrderModel:
|
|
20
|
+
# Corresponds to infrahub.graphql.manager.OrderInput
|
|
6
21
|
disable: bool | None = None
|
|
22
|
+
node_metadata: NodeMetaOrder | None = None
|
|
23
|
+
|
|
24
|
+
@classmethod
|
|
25
|
+
def from_input(cls, input_data: dict[str, Any] | None) -> OrderModel | None:
|
|
26
|
+
"""Convert the dictionary type input data from GraphQL into an OrderModel instance."""
|
|
27
|
+
if not input_data:
|
|
28
|
+
return None
|
|
29
|
+
|
|
30
|
+
order_model = cls(**input_data)
|
|
31
|
+
order_model.validate()
|
|
32
|
+
return order_model
|
|
33
|
+
|
|
34
|
+
def validate(self) -> None:
|
|
35
|
+
if self.node_metadata and self.node_metadata.created_at and self.node_metadata.updated_at:
|
|
36
|
+
raise ValidationError("Cannot order by both created_at and updated_at simultaneously.")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from typing import TYPE_CHECKING, Any
|
|
2
2
|
|
|
3
|
-
from graphene import Boolean, Field, InputField, InputObjectType, Mutation, String
|
|
3
|
+
from graphene import Boolean, Field, InputField, InputObjectType, Mutation, ObjectType, String
|
|
4
4
|
from graphql import GraphQLResolveInfo
|
|
5
5
|
from infrahub_sdk.uuidt import UUIDT
|
|
6
6
|
from typing_extensions import Self
|
|
@@ -36,7 +36,7 @@ class InfrahubAccountUpdateSelfInput(InputObjectType):
|
|
|
36
36
|
description = InputField(String(required=False), description="Description to use instead of the current one")
|
|
37
37
|
|
|
38
38
|
|
|
39
|
-
class ValueType(
|
|
39
|
+
class ValueType(ObjectType):
|
|
40
40
|
value = String(required=True)
|
|
41
41
|
|
|
42
42
|
|
|
@@ -99,7 +99,7 @@ class AccountMixin:
|
|
|
99
99
|
)
|
|
100
100
|
|
|
101
101
|
async with db.start_transaction() as dbt:
|
|
102
|
-
await obj.save(db=dbt)
|
|
102
|
+
await obj.save(db=dbt, user_id=account.id)
|
|
103
103
|
|
|
104
104
|
fields = extract_graphql_fields(info=info)
|
|
105
105
|
return cls(object=await obj.to_graphql(db=db, fields=fields.get("object", {})), ok=True) # type: ignore[call-arg]
|
|
@@ -126,7 +126,7 @@ class AccountMixin:
|
|
|
126
126
|
raise NodeNotFoundError(node_type="AccountToken", identifier=token_id)
|
|
127
127
|
|
|
128
128
|
async with db.start_transaction() as dbt:
|
|
129
|
-
await results[0].delete(db=dbt)
|
|
129
|
+
await results[0].delete(db=dbt, user_id=account.id)
|
|
130
130
|
|
|
131
131
|
return cls(ok=True) # type: ignore[call-arg]
|
|
132
132
|
|
|
@@ -144,7 +144,7 @@ class AccountMixin:
|
|
|
144
144
|
getattr(account, field).value = value
|
|
145
145
|
|
|
146
146
|
async with db.start_transaction() as dbt:
|
|
147
|
-
await account.save(db=dbt)
|
|
147
|
+
await account.save(db=dbt, user_id=account.id)
|
|
148
148
|
|
|
149
149
|
return cls(ok=True) # type: ignore[call-arg]
|
|
150
150
|
|
|
@@ -8,7 +8,6 @@ from infrahub.graphql.types.attribute import GenericPoolInput
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class BaseAttributeCreate(InputObjectType):
|
|
11
|
-
is_visible = Boolean(required=False)
|
|
12
11
|
is_protected = Boolean(required=False)
|
|
13
12
|
source = String(required=False)
|
|
14
13
|
owner = String(required=False)
|
|
@@ -21,7 +20,6 @@ class BaseAttributeCreate(InputObjectType):
|
|
|
21
20
|
|
|
22
21
|
class BaseAttributeUpdate(InputObjectType):
|
|
23
22
|
is_default = Boolean(required=False)
|
|
24
|
-
is_visible = Boolean(required=False)
|
|
25
23
|
is_protected = Boolean(required=False)
|
|
26
24
|
source = String(required=False)
|
|
27
25
|
owner = String(required=False)
|
|
@@ -98,7 +98,11 @@ class BranchCreate(Mutation):
|
|
|
98
98
|
# Retrieve created branch
|
|
99
99
|
obj = await Branch.get_by_name(db=graphql_context.db, name=model.name)
|
|
100
100
|
fields = extract_graphql_fields(info=info)
|
|
101
|
-
return cls(
|
|
101
|
+
return cls(
|
|
102
|
+
object=await obj.to_graphql_flat(fields=fields.get("object", {})),
|
|
103
|
+
ok=True,
|
|
104
|
+
task=task,
|
|
105
|
+
)
|
|
102
106
|
|
|
103
107
|
|
|
104
108
|
class BranchNameInput(InputObjectType):
|
|
@@ -172,7 +176,7 @@ class BranchUpdate(Mutation):
|
|
|
172
176
|
setattr(obj, field_name, data[field_name])
|
|
173
177
|
|
|
174
178
|
async with graphql_context.db.start_transaction() as db:
|
|
175
|
-
await obj.save(db=db)
|
|
179
|
+
await obj.save(db=db, user_id=graphql_context.active_account_session.account_id)
|
|
176
180
|
|
|
177
181
|
return cls(ok=True)
|
|
178
182
|
|
|
@@ -218,7 +222,7 @@ class BranchRebase(Mutation):
|
|
|
218
222
|
fields = extract_graphql_fields(info=info)
|
|
219
223
|
ok = True
|
|
220
224
|
|
|
221
|
-
return cls(object=await obj.
|
|
225
|
+
return cls(object=await obj.to_graphql_flat(fields=fields.get("object", {})), ok=ok, task=task)
|
|
222
226
|
|
|
223
227
|
|
|
224
228
|
class BranchValidate(Mutation):
|
|
@@ -260,7 +264,7 @@ class BranchValidate(Mutation):
|
|
|
260
264
|
|
|
261
265
|
fields = extract_graphql_fields(info=info)
|
|
262
266
|
|
|
263
|
-
return cls(object=await obj.
|
|
267
|
+
return cls(object=await obj.to_graphql_flat(fields=fields.get("object", {})), ok=ok, task=task)
|
|
264
268
|
|
|
265
269
|
|
|
266
270
|
class BranchMerge(Mutation):
|
|
@@ -315,4 +319,4 @@ class BranchMerge(Mutation):
|
|
|
315
319
|
fields = extract_graphql_fields(info=info)
|
|
316
320
|
ok = True
|
|
317
321
|
|
|
318
|
-
return cls(object=await obj.
|
|
322
|
+
return cls(object=await obj.to_graphql_flat(fields=fields.get("object", {})), ok=ok, task=task)
|
|
@@ -102,7 +102,7 @@ class UpdateComputedAttribute(Mutation):
|
|
|
102
102
|
if attribute_field.value != str(data.value):
|
|
103
103
|
attribute_field.value = str(data.value)
|
|
104
104
|
async with graphql_context.db.start_transaction() as dbt:
|
|
105
|
-
await target_node.save(db=dbt, fields=[str(data.attribute)])
|
|
105
|
+
await target_node.save(db=dbt, fields=[str(data.attribute)], user_id=graphql_context.assigned_user_id)
|
|
106
106
|
|
|
107
107
|
log_data = get_log_data()
|
|
108
108
|
request_id = log_data.get("request_id", "")
|
|
@@ -93,7 +93,7 @@ class UpdateDisplayLabel(Mutation):
|
|
|
93
93
|
await target_node.set_display_label(value=str(data.value))
|
|
94
94
|
|
|
95
95
|
async with graphql_context.db.start_transaction() as dbt:
|
|
96
|
-
await target_node.save(db=dbt, fields=["display_label"])
|
|
96
|
+
await target_node.save(db=dbt, fields=["display_label"], user_id=graphql_context.assigned_user_id)
|
|
97
97
|
|
|
98
98
|
log_data = get_log_data()
|
|
99
99
|
request_id = log_data.get("request_id", "")
|
|
@@ -100,7 +100,7 @@ class UpdateHFID(Mutation):
|
|
|
100
100
|
await target_node.set_human_friendly_id(value=updated_hfid)
|
|
101
101
|
|
|
102
102
|
async with graphql_context.db.start_transaction() as dbt:
|
|
103
|
-
await target_node.save(db=dbt, fields=["human_friendly_id"])
|
|
103
|
+
await target_node.save(db=dbt, fields=["human_friendly_id"], user_id=graphql_context.assigned_user_id)
|
|
104
104
|
|
|
105
105
|
log_data = get_log_data()
|
|
106
106
|
request_id = log_data.get("request_id", "")
|
|
@@ -9,7 +9,7 @@ from typing_extensions import Self
|
|
|
9
9
|
from infrahub import lock
|
|
10
10
|
from infrahub.core import registry
|
|
11
11
|
from infrahub.core.branch import Branch
|
|
12
|
-
from infrahub.core.constants import InfrahubKind
|
|
12
|
+
from infrahub.core.constants import InfrahubKind, MetadataOptions
|
|
13
13
|
from infrahub.core.ipam.reconciler import IpamReconciler
|
|
14
14
|
from infrahub.core.manager import NodeManager
|
|
15
15
|
from infrahub.core.node import Node
|
|
@@ -196,8 +196,7 @@ class InfrahubIPAddressMutation(InfrahubMutationMixin, Mutation):
|
|
|
196
196
|
kind=cls._meta.schema.kind,
|
|
197
197
|
id=data.get("id"),
|
|
198
198
|
branch=branch,
|
|
199
|
-
|
|
200
|
-
include_source=True,
|
|
199
|
+
include_metadata=MetadataOptions.LINKED_NODES,
|
|
201
200
|
)
|
|
202
201
|
namespace = await address.ip_namespace.get_peer(db)
|
|
203
202
|
namespace_id = await validate_namespace(db=db, branch=branch, data=data, existing_namespace_id=namespace.id)
|
|
@@ -356,8 +355,7 @@ class InfrahubIPPrefixMutation(InfrahubMutationMixin, Mutation):
|
|
|
356
355
|
kind=cls._meta.schema.kind,
|
|
357
356
|
id=data.get("id"),
|
|
358
357
|
branch=branch,
|
|
359
|
-
|
|
360
|
-
include_source=True,
|
|
358
|
+
include_metadata=MetadataOptions.LINKED_NODES,
|
|
361
359
|
)
|
|
362
360
|
namespace = await prefix.ip_namespace.get_peer(db)
|
|
363
361
|
namespace_id = await validate_namespace(db=db, branch=branch, data=data, existing_namespace_id=namespace.id)
|
|
@@ -435,7 +433,7 @@ class InfrahubIPPrefixMutation(InfrahubMutationMixin, Mutation):
|
|
|
435
433
|
data.get("id"), graphql_context.db, branch=branch, prefetch_relationships=True
|
|
436
434
|
)
|
|
437
435
|
if not prefix:
|
|
438
|
-
raise NodeNotFoundError(
|
|
436
|
+
raise NodeNotFoundError(node_type=cls._meta.schema.kind, identifier=data.get("id"), branch_name=branch.name)
|
|
439
437
|
|
|
440
438
|
namespace_rels = await prefix.ip_namespace.get_relationships(db=db)
|
|
441
439
|
namespace_id = namespace_rels[0].peer_id
|