infrahub-server 1.7.0__py3-none-any.whl → 1.7.0rc0__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/core/migrations/schema/attribute_kind_update.py +4 -5
- infrahub/core/schema/attribute_parameters.py +1 -28
- infrahub/core/schema/attribute_schema.py +2 -9
- infrahub/core/validators/attribute/kind.py +2 -5
- infrahub/graphql/manager.py +2 -8
- infrahub/lock.py +0 -7
- infrahub/services/adapters/cache/redis.py +0 -7
- infrahub_sdk/analyzer.py +2 -2
- infrahub_sdk/branch.py +39 -12
- infrahub_sdk/checks.py +4 -4
- infrahub_sdk/client.py +0 -36
- infrahub_sdk/ctl/cli_commands.py +1 -2
- infrahub_sdk/ctl/graphql.py +4 -15
- infrahub_sdk/ctl/utils.py +2 -2
- infrahub_sdk/graphql/renderers.py +0 -21
- infrahub_sdk/graphql/utils.py +0 -85
- infrahub_sdk/node/attribute.py +2 -12
- infrahub_sdk/node/constants.py +0 -11
- infrahub_sdk/node/node.py +14 -65
- infrahub_sdk/node/property.py +0 -3
- infrahub_sdk/node/related_node.py +1 -24
- infrahub_sdk/node/relationship.py +1 -10
- infrahub_sdk/operation.py +2 -2
- infrahub_sdk/schema/repository.py +2 -1
- infrahub_sdk/transforms.py +2 -2
- infrahub_sdk/types.py +2 -18
- {infrahub_server-1.7.0.dist-info → infrahub_server-1.7.0rc0.dist-info}/METADATA +1 -1
- {infrahub_server-1.7.0.dist-info → infrahub_server-1.7.0rc0.dist-info}/RECORD +32 -34
- infrahub_testcontainers/performance_test.py +1 -1
- infrahub_sdk/enums.py +0 -6
- infrahub_sdk/node/metadata.py +0 -69
- {infrahub_server-1.7.0.dist-info → infrahub_server-1.7.0rc0.dist-info}/WHEEL +0 -0
- {infrahub_server-1.7.0.dist-info → infrahub_server-1.7.0rc0.dist-info}/entry_points.txt +0 -0
- {infrahub_server-1.7.0.dist-info → infrahub_server-1.7.0rc0.dist-info}/licenses/LICENSE.txt +0 -0
infrahub_sdk/ctl/graphql.py
CHANGED
|
@@ -22,12 +22,7 @@ from rich.console import Console
|
|
|
22
22
|
from ..async_typer import AsyncTyper
|
|
23
23
|
from ..ctl.client import initialize_client
|
|
24
24
|
from ..ctl.utils import catch_exception
|
|
25
|
-
from ..graphql.utils import
|
|
26
|
-
insert_fragments_inline,
|
|
27
|
-
remove_fragment_import,
|
|
28
|
-
strip_typename_from_fragment,
|
|
29
|
-
strip_typename_from_operation,
|
|
30
|
-
)
|
|
25
|
+
from ..graphql.utils import insert_fragments_inline, remove_fragment_import
|
|
31
26
|
from .parameters import CONFIG_PARAM
|
|
32
27
|
|
|
33
28
|
app = AsyncTyper()
|
|
@@ -157,18 +152,12 @@ async def generate_return_types(
|
|
|
157
152
|
queries = filter_operations_definitions(definitions)
|
|
158
153
|
fragments = filter_fragments_definitions(definitions)
|
|
159
154
|
|
|
160
|
-
# Strip __typename fields from operations and fragments before code generation.
|
|
161
|
-
# __typename is a GraphQL introspection meta-field that isn't part of the schema's
|
|
162
|
-
# type definitions, causing ariadne-codegen to fail with "Redefinition of reserved type 'String'"
|
|
163
|
-
stripped_queries = [strip_typename_from_operation(q) for q in queries]
|
|
164
|
-
stripped_fragments = [strip_typename_from_fragment(f) for f in fragments]
|
|
165
|
-
|
|
166
155
|
package_generator = get_package_generator(
|
|
167
156
|
schema=graphql_schema,
|
|
168
|
-
fragments=
|
|
157
|
+
fragments=fragments,
|
|
169
158
|
settings=ClientSettings(
|
|
170
159
|
schema_path=str(schema),
|
|
171
|
-
target_package_name=directory.name
|
|
160
|
+
target_package_name=directory.name,
|
|
172
161
|
queries_path=str(directory),
|
|
173
162
|
include_comments=CommentsStrategy.NONE,
|
|
174
163
|
),
|
|
@@ -177,7 +166,7 @@ async def generate_return_types(
|
|
|
177
166
|
|
|
178
167
|
parsing_failed = False
|
|
179
168
|
try:
|
|
180
|
-
for query_operation in
|
|
169
|
+
for query_operation in queries:
|
|
181
170
|
package_generator.add_operation(query_operation)
|
|
182
171
|
except ParsingError as exc:
|
|
183
172
|
console.print(f"[red]Unable to process {gql_file.name}: {exc}")
|
infrahub_sdk/ctl/utils.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import asyncio
|
|
4
4
|
import logging
|
|
5
5
|
import traceback
|
|
6
6
|
from collections.abc import Callable, Coroutine
|
|
@@ -83,7 +83,7 @@ def catch_exception(
|
|
|
83
83
|
console = Console()
|
|
84
84
|
|
|
85
85
|
def decorator(func: Callable[..., T]) -> Callable[..., T | Coroutine[Any, Any, T]]:
|
|
86
|
-
if
|
|
86
|
+
if asyncio.iscoroutinefunction(func):
|
|
87
87
|
|
|
88
88
|
@wraps(func)
|
|
89
89
|
async def async_wrapper(*args: Any, **kwargs: Any) -> T:
|
|
@@ -7,8 +7,6 @@ from typing import Any
|
|
|
7
7
|
|
|
8
8
|
from pydantic import BaseModel
|
|
9
9
|
|
|
10
|
-
from infrahub_sdk.types import Order
|
|
11
|
-
|
|
12
10
|
from .constants import VARIABLE_TYPE_MAPPING
|
|
13
11
|
|
|
14
12
|
|
|
@@ -55,16 +53,6 @@ def convert_to_graphql_as_string(value: Any, convert_enum: bool = False) -> str:
|
|
|
55
53
|
if isinstance(value, list):
|
|
56
54
|
values_as_string = [convert_to_graphql_as_string(value=item, convert_enum=convert_enum) for item in value]
|
|
57
55
|
return "[" + ", ".join(values_as_string) + "]"
|
|
58
|
-
if isinstance(value, Order):
|
|
59
|
-
data = value.model_dump(exclude_none=True)
|
|
60
|
-
return (
|
|
61
|
-
"{ "
|
|
62
|
-
+ ", ".join(
|
|
63
|
-
f"{key}: {convert_to_graphql_as_string(value=val, convert_enum=convert_enum)}"
|
|
64
|
-
for key, val in data.items()
|
|
65
|
-
)
|
|
66
|
-
+ " }"
|
|
67
|
-
)
|
|
68
56
|
if isinstance(value, BaseModel):
|
|
69
57
|
data = value.model_dump()
|
|
70
58
|
return (
|
|
@@ -75,15 +63,6 @@ def convert_to_graphql_as_string(value: Any, convert_enum: bool = False) -> str:
|
|
|
75
63
|
)
|
|
76
64
|
+ " }"
|
|
77
65
|
)
|
|
78
|
-
if isinstance(value, dict):
|
|
79
|
-
return (
|
|
80
|
-
"{ "
|
|
81
|
-
+ ", ".join(
|
|
82
|
-
f"{key}: {convert_to_graphql_as_string(value=val, convert_enum=convert_enum)}"
|
|
83
|
-
for key, val in value.items()
|
|
84
|
-
)
|
|
85
|
-
+ " }"
|
|
86
|
-
)
|
|
87
66
|
|
|
88
67
|
return str(value)
|
|
89
68
|
|
infrahub_sdk/graphql/utils.py
CHANGED
|
@@ -1,90 +1,5 @@
|
|
|
1
1
|
import ast
|
|
2
2
|
|
|
3
|
-
from graphql import (
|
|
4
|
-
FieldNode,
|
|
5
|
-
FragmentDefinitionNode,
|
|
6
|
-
FragmentSpreadNode,
|
|
7
|
-
InlineFragmentNode,
|
|
8
|
-
OperationDefinitionNode,
|
|
9
|
-
SelectionNode,
|
|
10
|
-
SelectionSetNode,
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
def strip_typename_from_selection_set(selection_set: SelectionSetNode | None) -> SelectionSetNode | None:
|
|
15
|
-
"""Recursively strip __typename fields from a SelectionSetNode.
|
|
16
|
-
|
|
17
|
-
The __typename meta-field is an introspection field that is not part of the schema's
|
|
18
|
-
type definitions. When code generation tools like ariadne-codegen try to look up
|
|
19
|
-
__typename in the schema, they fail because it's a reserved introspection field.
|
|
20
|
-
|
|
21
|
-
This function removes all __typename fields from the selection set, allowing
|
|
22
|
-
code generation to proceed without errors.
|
|
23
|
-
"""
|
|
24
|
-
if selection_set is None:
|
|
25
|
-
return None
|
|
26
|
-
|
|
27
|
-
new_selections: list[SelectionNode] = []
|
|
28
|
-
for selection in selection_set.selections:
|
|
29
|
-
if isinstance(selection, FieldNode):
|
|
30
|
-
# Skip __typename fields
|
|
31
|
-
if selection.name.value == "__typename":
|
|
32
|
-
continue
|
|
33
|
-
# Recursively process nested selection sets
|
|
34
|
-
new_field = FieldNode(
|
|
35
|
-
alias=selection.alias,
|
|
36
|
-
name=selection.name,
|
|
37
|
-
arguments=selection.arguments,
|
|
38
|
-
directives=selection.directives,
|
|
39
|
-
selection_set=strip_typename_from_selection_set(selection.selection_set),
|
|
40
|
-
)
|
|
41
|
-
new_selections.append(new_field)
|
|
42
|
-
elif isinstance(selection, InlineFragmentNode):
|
|
43
|
-
# Process inline fragments
|
|
44
|
-
new_inline = InlineFragmentNode(
|
|
45
|
-
type_condition=selection.type_condition,
|
|
46
|
-
directives=selection.directives,
|
|
47
|
-
selection_set=strip_typename_from_selection_set(selection.selection_set),
|
|
48
|
-
)
|
|
49
|
-
new_selections.append(new_inline)
|
|
50
|
-
elif isinstance(selection, FragmentSpreadNode):
|
|
51
|
-
# FragmentSpread references a named fragment - keep as-is
|
|
52
|
-
new_selections.append(selection)
|
|
53
|
-
else:
|
|
54
|
-
raise TypeError(f"Unexpected GraphQL selection node type '{type(selection).__name__}'.")
|
|
55
|
-
|
|
56
|
-
return SelectionSetNode(selections=tuple(new_selections))
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
def strip_typename_from_operation(operation: OperationDefinitionNode) -> OperationDefinitionNode:
|
|
60
|
-
"""Strip __typename fields from an operation definition.
|
|
61
|
-
|
|
62
|
-
Returns a new OperationDefinitionNode with all __typename fields removed
|
|
63
|
-
from its selection set and any nested selection sets.
|
|
64
|
-
"""
|
|
65
|
-
return OperationDefinitionNode(
|
|
66
|
-
operation=operation.operation,
|
|
67
|
-
name=operation.name,
|
|
68
|
-
variable_definitions=operation.variable_definitions,
|
|
69
|
-
directives=operation.directives,
|
|
70
|
-
selection_set=strip_typename_from_selection_set(operation.selection_set),
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
def strip_typename_from_fragment(fragment: FragmentDefinitionNode) -> FragmentDefinitionNode:
|
|
75
|
-
"""Strip __typename fields from a fragment definition.
|
|
76
|
-
|
|
77
|
-
Returns a new FragmentDefinitionNode with all __typename fields removed
|
|
78
|
-
from its selection set and any nested selection sets.
|
|
79
|
-
"""
|
|
80
|
-
return FragmentDefinitionNode(
|
|
81
|
-
name=fragment.name,
|
|
82
|
-
type_condition=fragment.type_condition,
|
|
83
|
-
variable_definitions=fragment.variable_definitions,
|
|
84
|
-
directives=fragment.directives,
|
|
85
|
-
selection_set=strip_typename_from_selection_set(fragment.selection_set),
|
|
86
|
-
)
|
|
87
|
-
|
|
88
3
|
|
|
89
4
|
def get_class_def_index(module: ast.Module) -> int:
|
|
90
5
|
"""Get the index of the first class definition in the module.
|
infrahub_sdk/node/attribute.py
CHANGED
|
@@ -6,7 +6,7 @@ from typing import TYPE_CHECKING, Any, get_args
|
|
|
6
6
|
|
|
7
7
|
from ..protocols_base import CoreNodeBase
|
|
8
8
|
from ..uuidt import UUIDT
|
|
9
|
-
from .constants import
|
|
9
|
+
from .constants import IP_TYPES, PROPERTIES_FLAG, PROPERTIES_OBJECT, SAFE_VALUE
|
|
10
10
|
from .property import NodeProperty
|
|
11
11
|
|
|
12
12
|
if TYPE_CHECKING:
|
|
@@ -57,16 +57,11 @@ class Attribute:
|
|
|
57
57
|
|
|
58
58
|
self.source: NodeProperty | None = None
|
|
59
59
|
self.owner: NodeProperty | None = None
|
|
60
|
-
self.updated_by: NodeProperty | None = None
|
|
61
60
|
|
|
62
61
|
for prop_name in self._properties_object:
|
|
63
62
|
if data.get(prop_name):
|
|
64
63
|
setattr(self, prop_name, NodeProperty(data=data.get(prop_name))) # type: ignore[arg-type]
|
|
65
64
|
|
|
66
|
-
for prop_name in ATTRIBUTE_METADATA_OBJECT:
|
|
67
|
-
if data.get(prop_name):
|
|
68
|
-
setattr(self, prop_name, NodeProperty(data=data.get(prop_name))) # type: ignore[arg-type]
|
|
69
|
-
|
|
70
65
|
@property
|
|
71
66
|
def value(self) -> Any:
|
|
72
67
|
return self._value
|
|
@@ -109,7 +104,7 @@ class Attribute:
|
|
|
109
104
|
|
|
110
105
|
return {"data": data, "variables": variables}
|
|
111
106
|
|
|
112
|
-
def _generate_query_data(self, property: bool = False
|
|
107
|
+
def _generate_query_data(self, property: bool = False) -> dict | None:
|
|
113
108
|
data: dict[str, Any] = {"value": None}
|
|
114
109
|
|
|
115
110
|
if property:
|
|
@@ -120,11 +115,6 @@ class Attribute:
|
|
|
120
115
|
for prop_name in self._properties_object:
|
|
121
116
|
data[prop_name] = {"id": None, "display_label": None, "__typename": None}
|
|
122
117
|
|
|
123
|
-
if include_metadata:
|
|
124
|
-
data["updated_at"] = None
|
|
125
|
-
for prop_name in ATTRIBUTE_METADATA_OBJECT:
|
|
126
|
-
data[prop_name] = {"id": None, "display_label": None, "__typename": None}
|
|
127
|
-
|
|
128
118
|
return data
|
|
129
119
|
|
|
130
120
|
def _generate_mutation_query(self) -> dict[str, Any]:
|
infrahub_sdk/node/constants.py
CHANGED
|
@@ -3,17 +3,6 @@ import re
|
|
|
3
3
|
|
|
4
4
|
PROPERTIES_FLAG = ["is_protected", "updated_at"]
|
|
5
5
|
PROPERTIES_OBJECT = ["source", "owner"]
|
|
6
|
-
|
|
7
|
-
# Attribute-level metadata object fields (in addition to PROPERTIES_OBJECT)
|
|
8
|
-
ATTRIBUTE_METADATA_OBJECT = ["updated_by"]
|
|
9
|
-
|
|
10
|
-
# Node metadata fields (for node_metadata in GraphQL response)
|
|
11
|
-
NODE_METADATA_FIELDS_FLAG = ["created_at", "updated_at"]
|
|
12
|
-
NODE_METADATA_FIELDS_OBJECT = ["created_by", "updated_by"]
|
|
13
|
-
|
|
14
|
-
# Relationship metadata fields (for relationship_metadata in GraphQL response)
|
|
15
|
-
RELATIONSHIP_METADATA_FIELDS_FLAG = ["updated_at"]
|
|
16
|
-
RELATIONSHIP_METADATA_FIELDS_OBJECT = ["updated_by"]
|
|
17
6
|
SAFE_VALUE = re.compile(r"(^[\. /:a-zA-Z0-9_-]+$)|(^$)")
|
|
18
7
|
|
|
19
8
|
IP_TYPES = ipaddress.IPv4Interface | ipaddress.IPv6Interface | ipaddress.IPv4Network | ipaddress.IPv6Network
|
infrahub_sdk/node/node.py
CHANGED
|
@@ -23,7 +23,6 @@ from .constants import (
|
|
|
23
23
|
ARTIFACT_GENERATE_FEATURE_NOT_SUPPORTED_MESSAGE,
|
|
24
24
|
PROPERTIES_OBJECT,
|
|
25
25
|
)
|
|
26
|
-
from .metadata import NodeMetadata
|
|
27
26
|
from .related_node import RelatedNode, RelatedNodeBase, RelatedNodeSync
|
|
28
27
|
from .relationship import RelationshipManager, RelationshipManagerBase, RelationshipManagerSync
|
|
29
28
|
|
|
@@ -51,7 +50,6 @@ class InfrahubNodeBase:
|
|
|
51
50
|
self._branch = branch
|
|
52
51
|
self._existing: bool = True
|
|
53
52
|
self._attribute_data: dict[str, Attribute] = {}
|
|
54
|
-
self._metadata: NodeMetadata | None = None
|
|
55
53
|
|
|
56
54
|
# Generate a unique ID only to be used inside the SDK
|
|
57
55
|
# The format if this ID is purposely different from the ID used by the API
|
|
@@ -154,10 +152,6 @@ class InfrahubNodeBase:
|
|
|
154
152
|
def hfid_str(self) -> str | None:
|
|
155
153
|
return self.get_human_friendly_id_as_string(include_kind=True)
|
|
156
154
|
|
|
157
|
-
def get_node_metadata(self) -> NodeMetadata | None:
|
|
158
|
-
"""Returns the node metadata (created_at, created_by, updated_at, updated_by) if fetched."""
|
|
159
|
-
return self._metadata
|
|
160
|
-
|
|
161
155
|
def _init_attributes(self, data: dict | None = None) -> None:
|
|
162
156
|
for attr_schema in self._schema.attributes:
|
|
163
157
|
attr_data = data.get(attr_schema.name, None) if isinstance(data, dict) else None
|
|
@@ -198,8 +192,8 @@ class InfrahubNodeBase:
|
|
|
198
192
|
return self._schema.kind
|
|
199
193
|
|
|
200
194
|
def get_all_kinds(self) -> list[str]:
|
|
201
|
-
if
|
|
202
|
-
return [self._schema.kind] + inherit_from
|
|
195
|
+
if hasattr(self._schema, "inherit_from"):
|
|
196
|
+
return [self._schema.kind] + self._schema.inherit_from
|
|
203
197
|
return [self._schema.kind]
|
|
204
198
|
|
|
205
199
|
def is_ip_prefix(self) -> bool:
|
|
@@ -216,7 +210,7 @@ class InfrahubNodeBase:
|
|
|
216
210
|
def get_raw_graphql_data(self) -> dict | None:
|
|
217
211
|
return self._data
|
|
218
212
|
|
|
219
|
-
def _generate_input_data( # noqa: C901
|
|
213
|
+
def _generate_input_data( # noqa: C901
|
|
220
214
|
self,
|
|
221
215
|
exclude_unmodified: bool = False,
|
|
222
216
|
exclude_hfid: bool = False,
|
|
@@ -259,10 +253,7 @@ class InfrahubNodeBase:
|
|
|
259
253
|
rel: RelatedNodeBase | RelationshipManagerBase = getattr(self, item_name)
|
|
260
254
|
|
|
261
255
|
if rel_schema.cardinality == RelationshipCardinality.ONE and rel_schema.optional and not rel.initialized:
|
|
262
|
-
|
|
263
|
-
# For new nodes, omit the field to allow object template defaults to be applied
|
|
264
|
-
if self._existing:
|
|
265
|
-
data[item_name] = None
|
|
256
|
+
data[item_name] = None
|
|
266
257
|
continue
|
|
267
258
|
|
|
268
259
|
if rel is None or not rel.initialized:
|
|
@@ -428,16 +419,12 @@ class InfrahubNodeBase:
|
|
|
428
419
|
exclude: list[str] | None = None,
|
|
429
420
|
partial_match: bool = False,
|
|
430
421
|
order: Order | None = None,
|
|
431
|
-
include_metadata: bool = False,
|
|
432
422
|
) -> dict[str, Any | dict]:
|
|
433
423
|
data: dict[str, Any] = {
|
|
434
424
|
"count": None,
|
|
435
425
|
"edges": {"node": {"id": None, "hfid": None, "display_label": None, "__typename": None}},
|
|
436
426
|
}
|
|
437
427
|
|
|
438
|
-
if include_metadata:
|
|
439
|
-
data["edges"]["node_metadata"] = NodeMetadata._generate_query_data()
|
|
440
|
-
|
|
441
428
|
data["@filters"] = deepcopy(filters) if filters is not None else {}
|
|
442
429
|
|
|
443
430
|
if order:
|
|
@@ -509,12 +496,8 @@ class InfrahubNode(InfrahubNodeBase):
|
|
|
509
496
|
"""
|
|
510
497
|
self._client = client
|
|
511
498
|
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
if isinstance(data, dict):
|
|
515
|
-
node_metadata_data = data.get("node_metadata")
|
|
516
|
-
if isinstance(data.get("node"), dict):
|
|
517
|
-
data = data.get("node")
|
|
499
|
+
if isinstance(data, dict) and isinstance(data.get("node"), dict):
|
|
500
|
+
data = data.get("node")
|
|
518
501
|
|
|
519
502
|
self._relationship_cardinality_many_data: dict[str, RelationshipManager] = {}
|
|
520
503
|
self._relationship_cardinality_one_data: dict[str, RelatedNode] = {}
|
|
@@ -522,10 +505,6 @@ class InfrahubNode(InfrahubNodeBase):
|
|
|
522
505
|
|
|
523
506
|
super().__init__(schema=schema, branch=branch or client.default_branch, data=data)
|
|
524
507
|
|
|
525
|
-
# Initialize metadata after base class init
|
|
526
|
-
if node_metadata_data:
|
|
527
|
-
self._metadata = NodeMetadata(node_metadata_data)
|
|
528
|
-
|
|
529
508
|
@classmethod
|
|
530
509
|
async def from_graphql(
|
|
531
510
|
cls,
|
|
@@ -806,7 +785,6 @@ class InfrahubNode(InfrahubNodeBase):
|
|
|
806
785
|
partial_match: bool = False,
|
|
807
786
|
property: bool = False,
|
|
808
787
|
order: Order | None = None,
|
|
809
|
-
include_metadata: bool = False,
|
|
810
788
|
) -> dict[str, Any | dict]:
|
|
811
789
|
data = self.generate_query_data_init(
|
|
812
790
|
filters=filters,
|
|
@@ -816,7 +794,6 @@ class InfrahubNode(InfrahubNodeBase):
|
|
|
816
794
|
exclude=exclude,
|
|
817
795
|
partial_match=partial_match,
|
|
818
796
|
order=order,
|
|
819
|
-
include_metadata=include_metadata,
|
|
820
797
|
)
|
|
821
798
|
data["edges"]["node"].update(
|
|
822
799
|
await self.generate_query_data_node(
|
|
@@ -825,7 +802,6 @@ class InfrahubNode(InfrahubNodeBase):
|
|
|
825
802
|
prefetch_relationships=prefetch_relationships,
|
|
826
803
|
inherited=True,
|
|
827
804
|
property=property,
|
|
828
|
-
include_metadata=include_metadata,
|
|
829
805
|
)
|
|
830
806
|
)
|
|
831
807
|
|
|
@@ -849,7 +825,6 @@ class InfrahubNode(InfrahubNodeBase):
|
|
|
849
825
|
inherited=False,
|
|
850
826
|
insert_alias=True,
|
|
851
827
|
property=property,
|
|
852
|
-
include_metadata=include_metadata,
|
|
853
828
|
)
|
|
854
829
|
|
|
855
830
|
if child_data:
|
|
@@ -865,7 +840,6 @@ class InfrahubNode(InfrahubNodeBase):
|
|
|
865
840
|
insert_alias: bool = False,
|
|
866
841
|
prefetch_relationships: bool = False,
|
|
867
842
|
property: bool = False,
|
|
868
|
-
include_metadata: bool = False,
|
|
869
843
|
) -> dict[str, Any | dict]:
|
|
870
844
|
"""Generate the node part of a GraphQL Query with attributes and nodes.
|
|
871
845
|
|
|
@@ -876,7 +850,6 @@ class InfrahubNode(InfrahubNodeBase):
|
|
|
876
850
|
Defaults to True.
|
|
877
851
|
insert_alias (bool, optional): If True, inserts aliases in the query for each attribute or relationship.
|
|
878
852
|
prefetch_relationships (bool, optional): If True, pre-fetches relationship data as part of the query.
|
|
879
|
-
include_metadata (bool, optional): If True, includes node_metadata and relationship_metadata in the query.
|
|
880
853
|
|
|
881
854
|
Returns:
|
|
882
855
|
dict[str, Union[Any, Dict]]: GraphQL query in dictionary format
|
|
@@ -893,7 +866,7 @@ class InfrahubNode(InfrahubNodeBase):
|
|
|
893
866
|
if not inherited and attr._schema.inherited:
|
|
894
867
|
continue
|
|
895
868
|
|
|
896
|
-
attr_data = attr._generate_query_data(property=property
|
|
869
|
+
attr_data = attr._generate_query_data(property=property)
|
|
897
870
|
if attr_data:
|
|
898
871
|
data[attr_name] = attr_data
|
|
899
872
|
if insert_alias:
|
|
@@ -925,14 +898,11 @@ class InfrahubNode(InfrahubNodeBase):
|
|
|
925
898
|
peer_node = InfrahubNode(client=self._client, schema=peer_schema, branch=self._branch)
|
|
926
899
|
peer_data = await peer_node.generate_query_data_node(
|
|
927
900
|
property=property,
|
|
928
|
-
include_metadata=include_metadata,
|
|
929
901
|
)
|
|
930
902
|
|
|
931
903
|
rel_data: dict[str, Any]
|
|
932
904
|
if rel_schema and rel_schema.cardinality == "one":
|
|
933
|
-
rel_data = RelatedNode._generate_query_data(
|
|
934
|
-
peer_data=peer_data, property=property, include_metadata=include_metadata
|
|
935
|
-
)
|
|
905
|
+
rel_data = RelatedNode._generate_query_data(peer_data=peer_data, property=property)
|
|
936
906
|
# Nodes involved in a hierarchy are required to inherit from a common ancestor node, and graphql
|
|
937
907
|
# tries to resolve attributes in this ancestor instead of actual node. To avoid
|
|
938
908
|
# invalid queries issues when attribute is missing in the common ancestor, we use a fragment
|
|
@@ -942,9 +912,7 @@ class InfrahubNode(InfrahubNodeBase):
|
|
|
942
912
|
rel_data["node"] = {}
|
|
943
913
|
rel_data["node"][f"...on {rel_schema.peer}"] = data_node
|
|
944
914
|
elif rel_schema and rel_schema.cardinality == "many":
|
|
945
|
-
rel_data = RelationshipManager._generate_query_data(
|
|
946
|
-
peer_data=peer_data, property=property, include_metadata=include_metadata
|
|
947
|
-
)
|
|
915
|
+
rel_data = RelationshipManager._generate_query_data(peer_data=peer_data, property=property)
|
|
948
916
|
else:
|
|
949
917
|
continue
|
|
950
918
|
|
|
@@ -1317,12 +1285,8 @@ class InfrahubNodeSync(InfrahubNodeBase):
|
|
|
1317
1285
|
"""
|
|
1318
1286
|
self._client = client
|
|
1319
1287
|
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
if isinstance(data, dict):
|
|
1323
|
-
node_metadata_data = data.get("node_metadata")
|
|
1324
|
-
if isinstance(data.get("node"), dict):
|
|
1325
|
-
data = data.get("node")
|
|
1288
|
+
if isinstance(data, dict) and isinstance(data.get("node"), dict):
|
|
1289
|
+
data = data.get("node")
|
|
1326
1290
|
|
|
1327
1291
|
self._relationship_cardinality_many_data: dict[str, RelationshipManagerSync] = {}
|
|
1328
1292
|
self._relationship_cardinality_one_data: dict[str, RelatedNodeSync] = {}
|
|
@@ -1330,10 +1294,6 @@ class InfrahubNodeSync(InfrahubNodeBase):
|
|
|
1330
1294
|
|
|
1331
1295
|
super().__init__(schema=schema, branch=branch or client.default_branch, data=data)
|
|
1332
1296
|
|
|
1333
|
-
# Initialize metadata after base class init
|
|
1334
|
-
if node_metadata_data:
|
|
1335
|
-
self._metadata = NodeMetadata(node_metadata_data)
|
|
1336
|
-
|
|
1337
1297
|
@classmethod
|
|
1338
1298
|
def from_graphql(
|
|
1339
1299
|
cls,
|
|
@@ -1611,7 +1571,6 @@ class InfrahubNodeSync(InfrahubNodeBase):
|
|
|
1611
1571
|
partial_match: bool = False,
|
|
1612
1572
|
property: bool = False,
|
|
1613
1573
|
order: Order | None = None,
|
|
1614
|
-
include_metadata: bool = False,
|
|
1615
1574
|
) -> dict[str, Any | dict]:
|
|
1616
1575
|
data = self.generate_query_data_init(
|
|
1617
1576
|
filters=filters,
|
|
@@ -1621,7 +1580,6 @@ class InfrahubNodeSync(InfrahubNodeBase):
|
|
|
1621
1580
|
exclude=exclude,
|
|
1622
1581
|
partial_match=partial_match,
|
|
1623
1582
|
order=order,
|
|
1624
|
-
include_metadata=include_metadata,
|
|
1625
1583
|
)
|
|
1626
1584
|
data["edges"]["node"].update(
|
|
1627
1585
|
self.generate_query_data_node(
|
|
@@ -1630,7 +1588,6 @@ class InfrahubNodeSync(InfrahubNodeBase):
|
|
|
1630
1588
|
prefetch_relationships=prefetch_relationships,
|
|
1631
1589
|
inherited=True,
|
|
1632
1590
|
property=property,
|
|
1633
|
-
include_metadata=include_metadata,
|
|
1634
1591
|
)
|
|
1635
1592
|
)
|
|
1636
1593
|
|
|
@@ -1653,7 +1610,6 @@ class InfrahubNodeSync(InfrahubNodeBase):
|
|
|
1653
1610
|
inherited=False,
|
|
1654
1611
|
insert_alias=True,
|
|
1655
1612
|
property=property,
|
|
1656
|
-
include_metadata=include_metadata,
|
|
1657
1613
|
)
|
|
1658
1614
|
|
|
1659
1615
|
if child_data:
|
|
@@ -1669,7 +1625,6 @@ class InfrahubNodeSync(InfrahubNodeBase):
|
|
|
1669
1625
|
insert_alias: bool = False,
|
|
1670
1626
|
prefetch_relationships: bool = False,
|
|
1671
1627
|
property: bool = False,
|
|
1672
|
-
include_metadata: bool = False,
|
|
1673
1628
|
) -> dict[str, Any | dict]:
|
|
1674
1629
|
"""Generate the node part of a GraphQL Query with attributes and nodes.
|
|
1675
1630
|
|
|
@@ -1680,7 +1635,6 @@ class InfrahubNodeSync(InfrahubNodeBase):
|
|
|
1680
1635
|
Defaults to True.
|
|
1681
1636
|
insert_alias (bool, optional): If True, inserts aliases in the query for each attribute or relationship.
|
|
1682
1637
|
prefetch_relationships (bool, optional): If True, pre-fetches relationship data as part of the query.
|
|
1683
|
-
include_metadata (bool, optional): If True, includes node_metadata and relationship_metadata in the query.
|
|
1684
1638
|
|
|
1685
1639
|
Returns:
|
|
1686
1640
|
dict[str, Union[Any, Dict]]: GraphQL query in dictionary format
|
|
@@ -1697,7 +1651,7 @@ class InfrahubNodeSync(InfrahubNodeBase):
|
|
|
1697
1651
|
if not inherited and attr._schema.inherited:
|
|
1698
1652
|
continue
|
|
1699
1653
|
|
|
1700
|
-
attr_data = attr._generate_query_data(property=property
|
|
1654
|
+
attr_data = attr._generate_query_data(property=property)
|
|
1701
1655
|
if attr_data:
|
|
1702
1656
|
data[attr_name] = attr_data
|
|
1703
1657
|
if insert_alias:
|
|
@@ -1729,14 +1683,11 @@ class InfrahubNodeSync(InfrahubNodeBase):
|
|
|
1729
1683
|
peer_node = InfrahubNodeSync(client=self._client, schema=peer_schema, branch=self._branch)
|
|
1730
1684
|
peer_data = peer_node.generate_query_data_node(
|
|
1731
1685
|
property=property,
|
|
1732
|
-
include_metadata=include_metadata,
|
|
1733
1686
|
)
|
|
1734
1687
|
|
|
1735
1688
|
rel_data: dict[str, Any]
|
|
1736
1689
|
if rel_schema and rel_schema.cardinality == "one":
|
|
1737
|
-
rel_data = RelatedNodeSync._generate_query_data(
|
|
1738
|
-
peer_data=peer_data, property=property, include_metadata=include_metadata
|
|
1739
|
-
)
|
|
1690
|
+
rel_data = RelatedNodeSync._generate_query_data(peer_data=peer_data, property=property)
|
|
1740
1691
|
# Nodes involved in a hierarchy are required to inherit from a common ancestor node, and graphql
|
|
1741
1692
|
# tries to resolve attributes in this ancestor instead of actual node. To avoid
|
|
1742
1693
|
# invalid queries issues when attribute is missing in the common ancestor, we use a fragment
|
|
@@ -1746,9 +1697,7 @@ class InfrahubNodeSync(InfrahubNodeBase):
|
|
|
1746
1697
|
rel_data["node"] = {}
|
|
1747
1698
|
rel_data["node"][f"...on {rel_schema.peer}"] = data_node
|
|
1748
1699
|
elif rel_schema and rel_schema.cardinality == "many":
|
|
1749
|
-
rel_data = RelationshipManagerSync._generate_query_data(
|
|
1750
|
-
peer_data=peer_data, property=property, include_metadata=include_metadata
|
|
1751
|
-
)
|
|
1700
|
+
rel_data = RelationshipManagerSync._generate_query_data(peer_data=peer_data, property=property)
|
|
1752
1701
|
else:
|
|
1753
1702
|
continue
|
|
1754
1703
|
|
infrahub_sdk/node/property.py
CHANGED
|
@@ -20,8 +20,5 @@ class NodeProperty:
|
|
|
20
20
|
self.display_label = data.get("display_label", None)
|
|
21
21
|
self.typename = data.get("__typename", None)
|
|
22
22
|
|
|
23
|
-
def __repr__(self) -> str:
|
|
24
|
-
return f"NodeProperty({{'id': {self.id!r}, 'display_label': {self.display_label!r}, '__typename': {self.typename!r}}})"
|
|
25
|
-
|
|
26
23
|
def _generate_input_data(self) -> str | None:
|
|
27
24
|
return self.id
|
|
@@ -6,7 +6,6 @@ from typing import TYPE_CHECKING, Any
|
|
|
6
6
|
from ..exceptions import Error
|
|
7
7
|
from ..protocols_base import CoreNodeBase
|
|
8
8
|
from .constants import PROFILE_KIND_PREFIX, PROPERTIES_FLAG, PROPERTIES_OBJECT
|
|
9
|
-
from .metadata import NodeMetadata, RelationshipMetadata
|
|
10
9
|
|
|
11
10
|
if TYPE_CHECKING:
|
|
12
11
|
from ..client import InfrahubClient, InfrahubClientSync
|
|
@@ -41,13 +40,11 @@ class RelatedNodeBase:
|
|
|
41
40
|
self._typename: str | None = None
|
|
42
41
|
self._kind: str | None = None
|
|
43
42
|
self._source_typename: str | None = None
|
|
44
|
-
self._relationship_metadata: RelationshipMetadata | None = None
|
|
45
43
|
|
|
46
44
|
if isinstance(data, (CoreNodeBase)):
|
|
47
45
|
self._peer = data
|
|
48
46
|
for prop in self._properties:
|
|
49
47
|
setattr(self, prop, None)
|
|
50
|
-
self._relationship_metadata = None
|
|
51
48
|
|
|
52
49
|
elif isinstance(data, list):
|
|
53
50
|
data = {"hfid": data}
|
|
@@ -84,10 +81,6 @@ class RelatedNodeBase:
|
|
|
84
81
|
else:
|
|
85
82
|
setattr(self, prop, None)
|
|
86
83
|
|
|
87
|
-
# Parse relationship metadata (at edge level)
|
|
88
|
-
if data.get("relationship_metadata"):
|
|
89
|
-
self._relationship_metadata = RelationshipMetadata(data["relationship_metadata"])
|
|
90
|
-
|
|
91
84
|
@property
|
|
92
85
|
def id(self) -> str | None:
|
|
93
86
|
if self._peer:
|
|
@@ -141,10 +134,6 @@ class RelatedNodeBase:
|
|
|
141
134
|
return False
|
|
142
135
|
return bool(re.match(rf"^{PROFILE_KIND_PREFIX}[A-Z]", self._source_typename))
|
|
143
136
|
|
|
144
|
-
def get_relationship_metadata(self) -> RelationshipMetadata | None:
|
|
145
|
-
"""Returns the relationship metadata (updated_at, updated_by) if fetched."""
|
|
146
|
-
return self._relationship_metadata
|
|
147
|
-
|
|
148
137
|
def _generate_input_data(self, allocate_from_pool: bool = False) -> dict[str, Any]:
|
|
149
138
|
data: dict[str, Any] = {}
|
|
150
139
|
|
|
@@ -171,17 +160,12 @@ class RelatedNodeBase:
|
|
|
171
160
|
return {}
|
|
172
161
|
|
|
173
162
|
@classmethod
|
|
174
|
-
def _generate_query_data(
|
|
175
|
-
cls, peer_data: dict[str, Any] | None = None, property: bool = False, include_metadata: bool = False
|
|
176
|
-
) -> dict:
|
|
163
|
+
def _generate_query_data(cls, peer_data: dict[str, Any] | None = None, property: bool = False) -> dict:
|
|
177
164
|
"""Generates the basic structure of a GraphQL query for a single relationship.
|
|
178
165
|
|
|
179
166
|
Args:
|
|
180
167
|
peer_data (dict[str, Union[Any, Dict]], optional): Additional data to be included in the query for the node.
|
|
181
168
|
This is used to add extra fields when prefetching related node data.
|
|
182
|
-
property (bool, optional): If True, includes property fields (is_protected, source, owner, etc.).
|
|
183
|
-
include_metadata (bool, optional): If True, includes node_metadata (for the peer node) and
|
|
184
|
-
relationship_metadata (for the relationship edge) fields.
|
|
185
169
|
|
|
186
170
|
Returns:
|
|
187
171
|
Dict: A dictionary representing the basic structure of a GraphQL query, including the node's ID, display label,
|
|
@@ -197,13 +181,6 @@ class RelatedNodeBase:
|
|
|
197
181
|
properties[prop_name] = {"id": None, "display_label": None, "__typename": None}
|
|
198
182
|
|
|
199
183
|
data["properties"] = properties
|
|
200
|
-
|
|
201
|
-
if include_metadata:
|
|
202
|
-
# node_metadata is for the peer InfrahubNode (populated via from_graphql)
|
|
203
|
-
data["node_metadata"] = NodeMetadata._generate_query_data()
|
|
204
|
-
# relationship_metadata is for the relationship edge itself
|
|
205
|
-
data["relationship_metadata"] = RelationshipMetadata._generate_query_data()
|
|
206
|
-
|
|
207
184
|
if peer_data:
|
|
208
185
|
data["node"].update(peer_data)
|
|
209
186
|
|
|
@@ -10,7 +10,6 @@ from ..exceptions import (
|
|
|
10
10
|
)
|
|
11
11
|
from ..types import Order
|
|
12
12
|
from .constants import PROPERTIES_FLAG, PROPERTIES_OBJECT
|
|
13
|
-
from .metadata import NodeMetadata, RelationshipMetadata
|
|
14
13
|
from .related_node import RelatedNode, RelatedNodeSync
|
|
15
14
|
|
|
16
15
|
if TYPE_CHECKING:
|
|
@@ -73,16 +72,12 @@ class RelationshipManagerBase:
|
|
|
73
72
|
return {}
|
|
74
73
|
|
|
75
74
|
@classmethod
|
|
76
|
-
def _generate_query_data(
|
|
77
|
-
cls, peer_data: dict[str, Any] | None = None, property: bool = False, include_metadata: bool = False
|
|
78
|
-
) -> dict:
|
|
75
|
+
def _generate_query_data(cls, peer_data: dict[str, Any] | None = None, property: bool = False) -> dict:
|
|
79
76
|
"""Generates the basic structure of a GraphQL query for relationships with multiple nodes.
|
|
80
77
|
|
|
81
78
|
Args:
|
|
82
79
|
peer_data (dict[str, Union[Any, Dict]], optional): Additional data to be included in the query for each node.
|
|
83
80
|
This is used to add extra fields when prefetching related node data in many-to-many relationships.
|
|
84
|
-
property (bool, optional): If True, includes property fields (is_protected, source, owner, etc.).
|
|
85
|
-
include_metadata (bool, optional): If True, includes node_metadata and relationship_metadata fields.
|
|
86
81
|
|
|
87
82
|
Returns:
|
|
88
83
|
Dict: A dictionary representing the basic structure of a GraphQL query for multiple related nodes.
|
|
@@ -102,10 +97,6 @@ class RelationshipManagerBase:
|
|
|
102
97
|
properties[prop_name] = {"id": None, "display_label": None, "__typename": None}
|
|
103
98
|
data["edges"]["properties"] = properties
|
|
104
99
|
|
|
105
|
-
if include_metadata:
|
|
106
|
-
data["edges"]["node_metadata"] = NodeMetadata._generate_query_data()
|
|
107
|
-
data["edges"]["relationship_metadata"] = RelationshipMetadata._generate_query_data()
|
|
108
|
-
|
|
109
100
|
if peer_data:
|
|
110
101
|
data["edges"]["node"].update(peer_data)
|
|
111
102
|
|