infrahub-server 1.6.3__py3-none-any.whl → 1.7.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 +4 -2
- infrahub/api/exceptions.py +2 -2
- infrahub/api/schema.py +3 -1
- infrahub/artifacts/tasks.py +1 -0
- infrahub/auth.py +2 -2
- infrahub/cli/db.py +54 -28
- 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 +4 -4
- 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/branch_differ.py +1 -1
- infrahub/core/diff/conflict_transferer.py +1 -1
- infrahub/core/diff/data_check_synchronizer.py +4 -3
- infrahub/core/diff/enricher/cardinality_one.py +2 -2
- infrahub/core/diff/enricher/hierarchy.py +1 -1
- infrahub/core/diff/enricher/labels.py +1 -1
- infrahub/core/diff/merger/merger.py +28 -2
- 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/diff/repository/repository.py +3 -1
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/graph/constraints.py +3 -3
- infrahub/core/graph/schema.py +2 -12
- infrahub/core/ipam/reconciler.py +8 -6
- infrahub/core/ipam/utilization.py +8 -15
- infrahub/core/manager.py +133 -152
- infrahub/core/merge.py +1 -1
- 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/m012_convert_account_generic.py +12 -12
- infrahub/core/migrations/graph/m013_convert_git_password_credential.py +7 -12
- 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/m041_deleted_dup_edges.py +1 -1
- infrahub/core/migrations/graph/m049_remove_is_visible_relationship.py +53 -0
- infrahub/core/migrations/graph/m050_backfill_vertex_metadata.py +168 -0
- infrahub/core/migrations/query/__init__.py +2 -2
- 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/query/schema_attribute_update.py +1 -1
- infrahub/core/migrations/schema/attribute_kind_update.py +21 -2
- infrahub/core/migrations/schema/attribute_name_update.py +1 -1
- infrahub/core/migrations/schema/attribute_supports_profile.py +5 -3
- infrahub/core/migrations/schema/models.py +3 -0
- infrahub/core/migrations/schema/node_attribute_add.py +5 -2
- infrahub/core/migrations/schema/node_attribute_remove.py +1 -1
- infrahub/core/migrations/schema/node_kind_update.py +1 -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 +157 -58
- infrahub/core/node/base.py +9 -5
- infrahub/core/node/create.py +7 -3
- infrahub/core/node/delete_validator.py +1 -1
- infrahub/core/node/standard.py +100 -14
- infrahub/core/order.py +30 -0
- infrahub/core/property.py +0 -1
- infrahub/core/protocols.py +1 -0
- infrahub/core/protocols_base.py +10 -2
- infrahub/core/query/__init__.py +5 -3
- infrahub/core/query/attribute.py +164 -49
- infrahub/core/query/branch.py +58 -70
- infrahub/core/query/delete.py +1 -1
- infrahub/core/query/diff.py +7 -7
- infrahub/core/query/ipam.py +104 -43
- infrahub/core/query/node.py +1072 -281
- infrahub/core/query/relationship.py +531 -325
- infrahub/core/query/resource_manager.py +107 -18
- infrahub/core/query/standard_node.py +25 -5
- infrahub/core/query/utils.py +2 -4
- infrahub/core/relationship/constraints/count.py +1 -1
- infrahub/core/relationship/constraints/peer_kind.py +1 -1
- infrahub/core/relationship/constraints/peer_parent.py +1 -1
- infrahub/core/relationship/constraints/peer_relatives.py +1 -1
- infrahub/core/relationship/constraints/profiles_kind.py +1 -1
- infrahub/core/relationship/constraints/profiles_removal.py +168 -0
- infrahub/core/relationship/model.py +293 -139
- infrahub/core/schema/attribute_schema.py +2 -2
- infrahub/core/schema/basenode_schema.py +3 -0
- infrahub/core/schema/definitions/core/__init__.py +8 -2
- infrahub/core/schema/definitions/core/account.py +10 -10
- infrahub/core/schema/definitions/core/artifact.py +14 -8
- infrahub/core/schema/definitions/core/check.py +10 -4
- infrahub/core/schema/definitions/core/generator.py +26 -6
- infrahub/core/schema/definitions/core/graphql_query.py +1 -1
- infrahub/core/schema/definitions/core/group.py +9 -2
- infrahub/core/schema/definitions/core/ipam.py +80 -10
- infrahub/core/schema/definitions/core/menu.py +41 -7
- infrahub/core/schema/definitions/core/permission.py +16 -2
- infrahub/core/schema/definitions/core/profile.py +16 -2
- infrahub/core/schema/definitions/core/propose_change.py +24 -4
- infrahub/core/schema/definitions/core/propose_change_comment.py +23 -11
- infrahub/core/schema/definitions/core/propose_change_validator.py +50 -21
- infrahub/core/schema/definitions/core/repository.py +10 -0
- infrahub/core/schema/definitions/core/resource_pool.py +8 -1
- infrahub/core/schema/definitions/core/template.py +19 -2
- infrahub/core/schema/definitions/core/transform.py +11 -5
- infrahub/core/schema/definitions/core/webhook.py +27 -9
- infrahub/core/schema/manager.py +63 -43
- infrahub/core/schema/relationship_schema.py +6 -2
- infrahub/core/schema/schema_branch.py +48 -10
- infrahub/core/task/task.py +4 -2
- infrahub/core/utils.py +3 -25
- infrahub/core/validators/aggregated_checker.py +1 -1
- infrahub/core/validators/attribute/choices.py +1 -1
- infrahub/core/validators/attribute/enum.py +1 -1
- infrahub/core/validators/attribute/kind.py +1 -1
- infrahub/core/validators/attribute/length.py +1 -1
- infrahub/core/validators/attribute/min_max.py +1 -1
- infrahub/core/validators/attribute/number_pool.py +1 -1
- infrahub/core/validators/attribute/optional.py +1 -1
- infrahub/core/validators/attribute/regex.py +1 -1
- infrahub/core/validators/determiner.py +3 -3
- infrahub/core/validators/node/attribute.py +1 -1
- infrahub/core/validators/node/relationship.py +1 -1
- infrahub/core/validators/relationship/peer.py +1 -1
- infrahub/database/__init__.py +4 -4
- 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/git/utils.py +1 -1
- infrahub/graphql/constants.py +3 -0
- infrahub/graphql/context.py +1 -1
- infrahub/graphql/field_extractor.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 +175 -21
- infrahub/graphql/metadata.py +91 -0
- infrahub/graphql/mutations/account.py +6 -6
- 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/order.py +14 -0
- infrahub/graphql/queries/branch.py +62 -6
- infrahub/graphql/queries/resource_manager.py +25 -24
- 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 +59 -14
- 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 +12 -7
- infrahub/hfid/tasks.py +12 -3
- infrahub/lock.py +7 -0
- infrahub/menu/repository.py +1 -1
- infrahub/patch/queries/base.py +1 -1
- infrahub/pools/number.py +1 -8
- 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 +154 -13
- infrahub/profiles/queries/get_profile_data.py +143 -31
- infrahub/profiles/tasks.py +79 -27
- infrahub/profiles/triggers.py +22 -0
- infrahub/proposed_change/action_checker.py +1 -1
- infrahub/proposed_change/tasks.py +4 -1
- infrahub/services/__init__.py +1 -1
- infrahub/services/adapters/cache/nats.py +1 -1
- infrahub/services/adapters/cache/redis.py +7 -0
- 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/gather.py +1 -1
- infrahub/webhook/models.py +1 -1
- infrahub/webhook/tasks.py +23 -7
- infrahub/workers/dependencies.py +9 -3
- infrahub/workers/infrahub_async.py +13 -4
- infrahub/workflows/catalogue.py +19 -0
- infrahub_sdk/analyzer.py +2 -2
- infrahub_sdk/branch.py +12 -39
- infrahub_sdk/checks.py +4 -4
- infrahub_sdk/client.py +36 -0
- infrahub_sdk/ctl/cli_commands.py +2 -1
- infrahub_sdk/ctl/graphql.py +15 -4
- infrahub_sdk/ctl/utils.py +2 -2
- infrahub_sdk/enums.py +6 -0
- infrahub_sdk/graphql/renderers.py +21 -0
- infrahub_sdk/graphql/utils.py +85 -0
- infrahub_sdk/node/attribute.py +12 -2
- infrahub_sdk/node/constants.py +12 -0
- infrahub_sdk/node/metadata.py +69 -0
- infrahub_sdk/node/node.py +65 -14
- infrahub_sdk/node/property.py +3 -0
- infrahub_sdk/node/related_node.py +37 -5
- infrahub_sdk/node/relationship.py +18 -1
- infrahub_sdk/operation.py +2 -2
- infrahub_sdk/schema/repository.py +1 -2
- infrahub_sdk/transforms.py +2 -2
- infrahub_sdk/types.py +18 -2
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0.dist-info}/METADATA +17 -16
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0.dist-info}/RECORD +249 -228
- infrahub_testcontainers/container.py +3 -3
- infrahub_testcontainers/docker-compose-cluster.test.yml +7 -7
- infrahub_testcontainers/docker-compose.test.yml +13 -5
- infrahub_testcontainers/models.py +3 -3
- infrahub_testcontainers/performance_test.py +1 -1
- infrahub/graphql/models.py +0 -6
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0.dist-info}/WHEEL +0 -0
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0.dist-info}/entry_points.txt +0 -0
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0.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."""
|
|
246
248
|
node_interface_schema = GenericSchema(
|
|
247
249
|
name="Node", namespace="Core", description="Interface for all nodes in Infrahub"
|
|
248
250
|
)
|
|
249
|
-
|
|
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."""
|
|
255
|
+
node_interface_schema = GenericSchema(
|
|
256
|
+
name="Node", namespace="Core", description="Interface for all nodes in Infrahub"
|
|
257
|
+
)
|
|
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
|
)
|
|
@@ -547,7 +598,10 @@ class GraphQLSchemaManager:
|
|
|
547
598
|
required=False,
|
|
548
599
|
description="Human friendly identifier",
|
|
549
600
|
),
|
|
550
|
-
"_updated_at": graphene.DateTime(
|
|
601
|
+
"_updated_at": graphene.DateTime(
|
|
602
|
+
required=False,
|
|
603
|
+
deprecation_reason="Query the node_metadata field instead. Will be removed in Infrahub 1.9",
|
|
604
|
+
),
|
|
551
605
|
"display_label": graphene.String(required=False),
|
|
552
606
|
"Meta": type("Meta", (object,), meta_attrs),
|
|
553
607
|
}
|
|
@@ -615,7 +669,6 @@ class GraphQLSchemaManager:
|
|
|
615
669
|
}
|
|
616
670
|
|
|
617
671
|
main_attrs = {
|
|
618
|
-
"is_visible": graphene.Boolean(required=False),
|
|
619
672
|
"is_protected": graphene.Boolean(required=False),
|
|
620
673
|
"updated_at": graphene.DateTime(required=False),
|
|
621
674
|
"source": graphene.Field(data_source),
|
|
@@ -627,6 +680,42 @@ class GraphQLSchemaManager:
|
|
|
627
680
|
|
|
628
681
|
self.set_type(name=type_name, graphql_type=relationship_property)
|
|
629
682
|
|
|
683
|
+
def define_node_metadata(self, account_type: type[InfrahubObject]) -> None:
|
|
684
|
+
meta_attrs = {
|
|
685
|
+
"name": NODE_METADATA_TYPE,
|
|
686
|
+
"description": "Defines node metadata information",
|
|
687
|
+
}
|
|
688
|
+
|
|
689
|
+
main_attrs = {
|
|
690
|
+
"created_at": graphene.DateTime(required=False),
|
|
691
|
+
"created_by": graphene.Field(account_type, required=False, resolver=account_metadata_resolver),
|
|
692
|
+
"updated_at": graphene.DateTime(required=False),
|
|
693
|
+
"updated_by": graphene.Field(account_type, required=False, resolver=account_metadata_resolver),
|
|
694
|
+
"Meta": type("Meta", (object,), meta_attrs),
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
node_metadata = type(NODE_METADATA_TYPE, (graphene.ObjectType,), main_attrs)
|
|
698
|
+
|
|
699
|
+
self.set_type(name=NODE_METADATA_TYPE, graphql_type=node_metadata)
|
|
700
|
+
|
|
701
|
+
def define_relationship_metadata(self, account_type: type[InfrahubObject]) -> None:
|
|
702
|
+
meta_attrs = {
|
|
703
|
+
"name": RELATIONSHIP_METADATA_TYPE,
|
|
704
|
+
"description": "Defines relationship metadata information",
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
main_attrs = {
|
|
708
|
+
"created_at": graphene.DateTime(required=False),
|
|
709
|
+
"created_by": graphene.Field(account_type, required=False, resolver=account_metadata_resolver),
|
|
710
|
+
"updated_at": graphene.DateTime(required=False),
|
|
711
|
+
"updated_by": graphene.Field(account_type, required=False, resolver=account_metadata_resolver),
|
|
712
|
+
"Meta": type("Meta", (object,), meta_attrs),
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
relationship_metadata = type(RELATIONSHIP_METADATA_TYPE, (graphene.ObjectType,), main_attrs)
|
|
716
|
+
|
|
717
|
+
self.set_type(name=RELATIONSHIP_METADATA_TYPE, graphql_type=relationship_metadata)
|
|
718
|
+
|
|
630
719
|
def generate_graphql_mutations(
|
|
631
720
|
self,
|
|
632
721
|
schema: NodeSchema | ProfileSchema | TemplateSchema,
|
|
@@ -919,8 +1008,16 @@ class GraphQLSchemaManager:
|
|
|
919
1008
|
if not top_level:
|
|
920
1009
|
filters["isnull"] = graphene.Boolean()
|
|
921
1010
|
|
|
1011
|
+
if schema.display_label:
|
|
1012
|
+
display_label_schema = schema.get_attribute("display_label")
|
|
1013
|
+
filters.update(
|
|
1014
|
+
get_attribute_type(kind=display_label_schema.kind).get_graphql_filters(
|
|
1015
|
+
name="display_label", include_properties=False, include_isnull=True
|
|
1016
|
+
)
|
|
1017
|
+
)
|
|
1018
|
+
|
|
922
1019
|
if schema.human_friendly_id and top_level:
|
|
923
|
-
#
|
|
1020
|
+
# NOTE: this can loosen to allow filtering at a non-top level once IFC-2110 is implemented
|
|
924
1021
|
filters["hfid"] = graphene.List(graphene.String)
|
|
925
1022
|
|
|
926
1023
|
for attr in schema.attributes:
|
|
@@ -935,6 +1032,9 @@ class GraphQLSchemaManager:
|
|
|
935
1032
|
filters.update(get_attribute_type().get_graphql_filters(name="any"))
|
|
936
1033
|
filters["partial_match"] = graphene.Boolean()
|
|
937
1034
|
|
|
1035
|
+
# Add metadata filters for filtering by created_by, updated_by, created_at, updated_at
|
|
1036
|
+
filters.update(self._generate_metadata_filters())
|
|
1037
|
+
|
|
938
1038
|
if schema.kind in [InfrahubKind.IPADDRESS, InfrahubKind.IPPREFIX]:
|
|
939
1039
|
# This is only available for IPAM generics
|
|
940
1040
|
filters["include_available"] = graphene.Boolean()
|
|
@@ -961,10 +1061,53 @@ class GraphQLSchemaManager:
|
|
|
961
1061
|
|
|
962
1062
|
return filters
|
|
963
1063
|
|
|
1064
|
+
def _generate_metadata_filters(self) -> dict[str, Any]:
|
|
1065
|
+
"""Generate GraphQL filters for object-level metadata fields.
|
|
1066
|
+
|
|
1067
|
+
These filters allow querying nodes based on their metadata:
|
|
1068
|
+
- created_by: Filter by the account that created the node
|
|
1069
|
+
- updated_by: Filter by the account that last updated the node
|
|
1070
|
+
- created_at: Filter by creation timestamp
|
|
1071
|
+
- updated_at: Filter by last update timestamp
|
|
1072
|
+
|
|
1073
|
+
Returns:
|
|
1074
|
+
dict: Filter definitions with names as keys and graphene types as values
|
|
1075
|
+
"""
|
|
1076
|
+
return {
|
|
1077
|
+
# Account-based filters (created_by)
|
|
1078
|
+
"node_metadata__created_by__id": graphene.ID(description="Filter by exact creator account UUID"),
|
|
1079
|
+
"node_metadata__created_by__ids": graphene.List(
|
|
1080
|
+
graphene.ID, description="Filter by list of creator account UUIDs"
|
|
1081
|
+
),
|
|
1082
|
+
# Account-based filters (updated_by)
|
|
1083
|
+
"node_metadata__updated_by__id": graphene.ID(description="Filter by exact updater account UUID"),
|
|
1084
|
+
"node_metadata__updated_by__ids": graphene.List(
|
|
1085
|
+
graphene.ID, description="Filter by list of updater account UUIDs"
|
|
1086
|
+
),
|
|
1087
|
+
# DateTime-based filters (created_at)
|
|
1088
|
+
"node_metadata__created_at": graphene.DateTime(description="Filter by exact creation timestamp"),
|
|
1089
|
+
"node_metadata__created_at__before": graphene.DateTime(
|
|
1090
|
+
description="Filter for objects created before this timestamp"
|
|
1091
|
+
),
|
|
1092
|
+
"node_metadata__created_at__after": graphene.DateTime(
|
|
1093
|
+
description="Filter for objects created after this timestamp"
|
|
1094
|
+
),
|
|
1095
|
+
# DateTime-based filters (updated_at)
|
|
1096
|
+
"node_metadata__updated_at": graphene.DateTime(description="Filter by exact update timestamp"),
|
|
1097
|
+
"node_metadata__updated_at__before": graphene.DateTime(
|
|
1098
|
+
description="Filter for objects updated before this timestamp"
|
|
1099
|
+
),
|
|
1100
|
+
"node_metadata__updated_at__after": graphene.DateTime(
|
|
1101
|
+
description="Filter for objects updated after this timestamp"
|
|
1102
|
+
),
|
|
1103
|
+
}
|
|
1104
|
+
|
|
964
1105
|
def generate_graphql_edged_object(
|
|
965
1106
|
self,
|
|
966
1107
|
schema: MainSchemaTypes,
|
|
967
1108
|
node: InterfaceReference | InfrahubObjectReference,
|
|
1109
|
+
node_metadata: type[InfrahubObject],
|
|
1110
|
+
relationship_metadata: type[InfrahubObject] | None = None,
|
|
968
1111
|
relation_property: type[InfrahubObject] | None = None,
|
|
969
1112
|
populate_cache: bool = False,
|
|
970
1113
|
) -> InfrahubEdgedReference:
|
|
@@ -987,15 +1130,18 @@ class GraphQLSchemaManager:
|
|
|
987
1130
|
|
|
988
1131
|
main_attrs: dict[str, Any] = {
|
|
989
1132
|
"node": graphene.Field(node.reference, required=False),
|
|
1133
|
+
"node_metadata": graphene.Field(node_metadata, required=False),
|
|
990
1134
|
"Meta": type("Meta", (object,), meta_attrs),
|
|
991
1135
|
}
|
|
992
1136
|
|
|
993
1137
|
if relation_property:
|
|
994
1138
|
main_attrs["properties"] = graphene.Field(relation_property, required=False)
|
|
1139
|
+
if relationship_metadata:
|
|
1140
|
+
main_attrs["relationship_metadata"] = graphene.Field(relationship_metadata, required=False)
|
|
995
1141
|
|
|
996
1142
|
graphql_edged_object = registry.get_edge_type(reference_hash=edge_hash, schema_hash=self.schema_hash)
|
|
997
1143
|
if not graphql_edged_object:
|
|
998
|
-
graphql_edged_object = type(object_name, (
|
|
1144
|
+
graphql_edged_object = type(object_name, (InfrahubObjectWithoutMeta,), main_attrs)
|
|
999
1145
|
registry.set_edge_type(
|
|
1000
1146
|
reference=graphql_edged_object, reference_hash=edge_hash, schema_hash=self.schema_hash
|
|
1001
1147
|
)
|
|
@@ -1039,7 +1185,7 @@ class GraphQLSchemaManager:
|
|
|
1039
1185
|
)
|
|
1040
1186
|
if not graphql_paginated_object:
|
|
1041
1187
|
main_attrs["Meta"] = type("Meta", (object,), meta_attrs)
|
|
1042
|
-
graphql_paginated_object = type(object_name, (
|
|
1188
|
+
graphql_paginated_object = type(object_name, (InfrahubObjectWithoutMeta,), main_attrs)
|
|
1043
1189
|
registry.set_paginated_type(
|
|
1044
1190
|
reference=graphql_paginated_object, reference_hash=paginated_hash, schema_hash=self.schema_hash
|
|
1045
1191
|
)
|
|
@@ -1054,6 +1200,8 @@ class GraphQLSchemaManager:
|
|
|
1054
1200
|
schema: GenericSchema,
|
|
1055
1201
|
relation_property: graphene.ObjectType,
|
|
1056
1202
|
base_interface: graphene.ObjectType,
|
|
1203
|
+
node_metadata: type[InfrahubObject],
|
|
1204
|
+
relationship_metadata: type[InfrahubObject] | None = None,
|
|
1057
1205
|
populate_cache: bool = False,
|
|
1058
1206
|
) -> type[InfrahubObject]:
|
|
1059
1207
|
meta_attrs: dict[str, Any] = {
|
|
@@ -1064,12 +1212,18 @@ class GraphQLSchemaManager:
|
|
|
1064
1212
|
|
|
1065
1213
|
main_attrs: dict[str, Any] = {
|
|
1066
1214
|
"node": graphene.Field(base_interface, required=False),
|
|
1067
|
-
"_updated_at": graphene.DateTime(
|
|
1215
|
+
"_updated_at": graphene.DateTime(
|
|
1216
|
+
required=False,
|
|
1217
|
+
deprecation_reason="Query the node_metadata field instead. Will be removed in Infrahub 1.9",
|
|
1218
|
+
),
|
|
1219
|
+
"node_metadata": graphene.Field(node_metadata, required=True),
|
|
1068
1220
|
"Meta": type("Meta", (object,), meta_attrs),
|
|
1069
1221
|
}
|
|
1070
1222
|
|
|
1071
1223
|
if relation_property:
|
|
1072
1224
|
main_attrs["properties"] = graphene.Field(relation_property, required=False)
|
|
1225
|
+
if relationship_metadata:
|
|
1226
|
+
main_attrs["relationship_metadata"] = graphene.Field(relationship_metadata, required=False)
|
|
1073
1227
|
|
|
1074
1228
|
object_name = f"NestedEdged{schema.kind}"
|
|
1075
1229
|
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
|
+
)
|
|
@@ -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
|
|
@@ -9,13 +9,13 @@ from infrahub.auth import AuthType
|
|
|
9
9
|
from infrahub.core.constants import InfrahubKind
|
|
10
10
|
from infrahub.core.manager import NodeManager
|
|
11
11
|
from infrahub.core.node import Node
|
|
12
|
+
from infrahub.core.order import OrderModel
|
|
12
13
|
from infrahub.core.protocols import CoreAccount, CoreNode, InternalAccountToken
|
|
13
14
|
from infrahub.core.timestamp import Timestamp
|
|
14
15
|
from infrahub.database import InfrahubDatabase, retry_db_transaction
|
|
15
16
|
from infrahub.exceptions import NodeNotFoundError, PermissionDeniedError
|
|
16
17
|
from infrahub.graphql.field_extractor import extract_graphql_fields
|
|
17
18
|
|
|
18
|
-
from ..models import OrderModel
|
|
19
19
|
from ..types import InfrahubObjectType
|
|
20
20
|
|
|
21
21
|
if TYPE_CHECKING:
|
|
@@ -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
|