infrahub-server 1.6.2__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 +12 -9
- 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 +26 -6
- 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 +11 -6
- 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_parameters.py +28 -1
- infrahub/core/schema/attribute_schema.py +11 -17
- 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 +115 -11
- 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 +6 -3
- 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/app.py +2 -2
- 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/diff/tree.py +5 -5
- 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.2.dist-info → infrahub_server-1.7.0.dist-info}/METADATA +17 -16
- {infrahub_server-1.6.2.dist-info → infrahub_server-1.7.0.dist-info}/RECORD +252 -231
- 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.2.dist-info → infrahub_server-1.7.0.dist-info}/WHEEL +0 -0
- {infrahub_server-1.6.2.dist-info → infrahub_server-1.7.0.dist-info}/entry_points.txt +0 -0
- {infrahub_server-1.6.2.dist-info → infrahub_server-1.7.0.dist-info}/licenses/LICENSE.txt +0 -0
infrahub/core/node/standard.py
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import inspect
|
|
4
|
+
from dataclasses import dataclass, field
|
|
4
5
|
from enum import Enum
|
|
5
6
|
from typing import TYPE_CHECKING, Any, Optional, Union, get_args, get_origin
|
|
6
7
|
from uuid import UUID
|
|
7
8
|
|
|
8
9
|
import ujson
|
|
9
10
|
from infrahub_sdk.uuidt import UUIDT
|
|
10
|
-
from pydantic import BaseModel
|
|
11
|
+
from pydantic import BaseModel, Field, field_validator
|
|
11
12
|
|
|
12
|
-
from infrahub.
|
|
13
|
+
from infrahub.constants.enums import OrderByField, OrderDirection
|
|
14
|
+
from infrahub.core.constants import NULL_VALUE, SYSTEM_USER_ID, InfrahubKind
|
|
13
15
|
from infrahub.core.query.standard_node import (
|
|
14
16
|
StandardNodeCreateQuery,
|
|
15
17
|
StandardNodeDeleteQuery,
|
|
@@ -18,6 +20,7 @@ from infrahub.core.query.standard_node import (
|
|
|
18
20
|
StandardNodeQuery,
|
|
19
21
|
StandardNodeUpdateQuery,
|
|
20
22
|
)
|
|
23
|
+
from infrahub.core.timestamp import Timestamp, current_timestamp
|
|
21
24
|
from infrahub.exceptions import Error, InitializationError
|
|
22
25
|
|
|
23
26
|
if TYPE_CHECKING:
|
|
@@ -29,9 +32,25 @@ if TYPE_CHECKING:
|
|
|
29
32
|
from infrahub.database import InfrahubDatabase
|
|
30
33
|
|
|
31
34
|
|
|
35
|
+
@dataclass
|
|
36
|
+
class StandardNodeOrdering:
|
|
37
|
+
order_by: OrderByField = field(default=OrderByField.ID)
|
|
38
|
+
direction: OrderDirection = field(default=OrderDirection.ASC)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@dataclass(slots=True)
|
|
42
|
+
class StandardNodeQueryFields:
|
|
43
|
+
node: dict[str, Any] = field(default_factory=dict)
|
|
44
|
+
node_metadata: dict[str, Any] = field(default_factory=dict)
|
|
45
|
+
|
|
46
|
+
|
|
32
47
|
class StandardNode(BaseModel):
|
|
33
48
|
id: Optional[str] = None
|
|
34
49
|
uuid: Optional[UUID] = None
|
|
50
|
+
created_at: Optional[str] = Field(default=None, validate_default=True)
|
|
51
|
+
created_by: str = Field(default=SYSTEM_USER_ID)
|
|
52
|
+
updated_by: Optional[str] = Field(default=None)
|
|
53
|
+
updated_at: Optional[str] = Field(default=None, validate_default=True)
|
|
35
54
|
|
|
36
55
|
_query: type[StandardNodeQuery] = StandardNodeCreateQuery
|
|
37
56
|
_exclude_attrs: list[str] = ["id", "uuid", "_query"]
|
|
@@ -45,6 +64,11 @@ class StandardNode(BaseModel):
|
|
|
45
64
|
raise ValueError("id isn't defined yet")
|
|
46
65
|
return self.id
|
|
47
66
|
|
|
67
|
+
@field_validator("created_at", mode="before")
|
|
68
|
+
@classmethod
|
|
69
|
+
def set_created_at(cls, value: str) -> str:
|
|
70
|
+
return Timestamp(value).to_string()
|
|
71
|
+
|
|
48
72
|
@staticmethod
|
|
49
73
|
def guess_field_type(field: FieldInfo) -> Any:
|
|
50
74
|
"""Return the type of a Pydantic model field.
|
|
@@ -68,7 +92,14 @@ class StandardNode(BaseModel):
|
|
|
68
92
|
|
|
69
93
|
raise InitializationError("The root node has not been initialized with a uuid")
|
|
70
94
|
|
|
71
|
-
async def
|
|
95
|
+
async def to_graphql_flat(self, fields: dict) -> dict:
|
|
96
|
+
"""Returns the GraphQL representation of the object with only top-level fields.
|
|
97
|
+
|
|
98
|
+
This method does not handle nested fields and is only used for the old `Branch` query which
|
|
99
|
+
will be deprecated in the future and replaced by the `InfrahubBranch` query.
|
|
100
|
+
It's also used for the old style of Branch mutations that will be deprecated in the future,
|
|
101
|
+
when we introduce InfrahubBranch muations for consistency.
|
|
102
|
+
"""
|
|
72
103
|
response: dict[str, Any] = {"id": self.uuid}
|
|
73
104
|
|
|
74
105
|
for field_name in fields.keys():
|
|
@@ -77,21 +108,71 @@ class StandardNode(BaseModel):
|
|
|
77
108
|
if field_name == "__typename":
|
|
78
109
|
response[field_name] = self.get_type()
|
|
79
110
|
continue
|
|
80
|
-
field = getattr(self, field_name)
|
|
111
|
+
field = getattr(self, field_name, None)
|
|
81
112
|
if field is None:
|
|
82
113
|
response[field_name] = None
|
|
83
114
|
continue
|
|
115
|
+
if isinstance(fields.get(field_name), dict):
|
|
116
|
+
result = {}
|
|
117
|
+
for nested_field in fields.get(field_name, {}).keys():
|
|
118
|
+
if nested_field == "value":
|
|
119
|
+
result[nested_field] = field
|
|
120
|
+
continue
|
|
121
|
+
response[field_name] = result
|
|
122
|
+
continue
|
|
123
|
+
|
|
84
124
|
response[field_name] = field
|
|
85
125
|
|
|
86
126
|
return response
|
|
87
127
|
|
|
88
|
-
async def
|
|
128
|
+
async def to_graphql(self, fields: StandardNodeQueryFields) -> dict:
|
|
129
|
+
node_response: dict[str, Any] = {}
|
|
130
|
+
meta_response: dict[str, Any] = {}
|
|
131
|
+
|
|
132
|
+
for field_name in fields.node.keys():
|
|
133
|
+
if field_name == "id":
|
|
134
|
+
node_response["id"] = self.uuid
|
|
135
|
+
continue
|
|
136
|
+
if field_name == "__typename":
|
|
137
|
+
node_response[field_name] = self.get_type()
|
|
138
|
+
continue
|
|
139
|
+
field = getattr(self, field_name, None)
|
|
140
|
+
if field is None:
|
|
141
|
+
node_response[field_name] = None
|
|
142
|
+
continue
|
|
143
|
+
if isinstance(fields.node.get(field_name), dict):
|
|
144
|
+
result = {}
|
|
145
|
+
for nested_field in fields.node.get(field_name, {}).keys():
|
|
146
|
+
if nested_field == "value":
|
|
147
|
+
result[nested_field] = field
|
|
148
|
+
continue
|
|
149
|
+
node_response[field_name] = result
|
|
150
|
+
continue
|
|
151
|
+
|
|
152
|
+
node_response[field_name] = field
|
|
153
|
+
|
|
154
|
+
for field_name in fields.node_metadata.keys():
|
|
155
|
+
match field_name:
|
|
156
|
+
case "created_at":
|
|
157
|
+
meta_response["created_at"] = Timestamp(self.created_at).to_datetime() if self.created_at else None
|
|
158
|
+
case "updated_at":
|
|
159
|
+
meta_response["updated_at"] = Timestamp(self.updated_at).to_datetime() if self.updated_at else None
|
|
160
|
+
case "created_by":
|
|
161
|
+
if self.created_by and self.created_by != SYSTEM_USER_ID:
|
|
162
|
+
meta_response["created_by"] = {"id": self.created_by, "__kind__": InfrahubKind.ACCOUNT}
|
|
163
|
+
case "updated_by":
|
|
164
|
+
if self.updated_by and self.updated_by != SYSTEM_USER_ID:
|
|
165
|
+
meta_response["updated_by"] = {"id": self.updated_by, "__kind__": InfrahubKind.ACCOUNT}
|
|
166
|
+
|
|
167
|
+
return {"node": node_response, "node_metadata": meta_response}
|
|
168
|
+
|
|
169
|
+
async def save(self, db: InfrahubDatabase, user_id: str = SYSTEM_USER_ID) -> bool:
|
|
89
170
|
"""Create or Update the Node in the database."""
|
|
90
171
|
|
|
91
172
|
if self.id:
|
|
92
|
-
return await self.update(db=db)
|
|
173
|
+
return await self.update(db=db, user_id=user_id)
|
|
93
174
|
|
|
94
|
-
return await self.create(db=db)
|
|
175
|
+
return await self.create(db=db, user_id=user_id)
|
|
95
176
|
|
|
96
177
|
async def delete(self, db: InfrahubDatabase) -> None:
|
|
97
178
|
"""Delete the Node in the database."""
|
|
@@ -99,9 +180,11 @@ class StandardNode(BaseModel):
|
|
|
99
180
|
query: Query = await StandardNodeDeleteQuery.init(db=db, node=self)
|
|
100
181
|
await query.execute(db=db)
|
|
101
182
|
|
|
102
|
-
async def create(self, db: InfrahubDatabase) -> bool:
|
|
183
|
+
async def create(self, db: InfrahubDatabase, user_id: str = SYSTEM_USER_ID) -> bool:
|
|
103
184
|
"""Create a new node in the database."""
|
|
104
|
-
|
|
185
|
+
self.created_by = user_id
|
|
186
|
+
self.updated_by = self.created_by
|
|
187
|
+
self.updated_at = self.created_at
|
|
105
188
|
query: Query = await self._query.init(db=db, node=self)
|
|
106
189
|
await query.execute(db=db)
|
|
107
190
|
|
|
@@ -115,9 +198,10 @@ class StandardNode(BaseModel):
|
|
|
115
198
|
|
|
116
199
|
return True
|
|
117
200
|
|
|
118
|
-
async def update(self, db: InfrahubDatabase) -> bool:
|
|
201
|
+
async def update(self, db: InfrahubDatabase, user_id: str = SYSTEM_USER_ID) -> bool:
|
|
119
202
|
"""Update the node in the database if needed."""
|
|
120
|
-
|
|
203
|
+
self.updated_by = user_id
|
|
204
|
+
self.updated_at = current_timestamp()
|
|
121
205
|
query: Query = await StandardNodeUpdateQuery.init(db=db, node=self)
|
|
122
206
|
await query.execute(db=db)
|
|
123
207
|
result = query.get_result()
|
|
@@ -187,7 +271,7 @@ class StandardNode(BaseModel):
|
|
|
187
271
|
else:
|
|
188
272
|
data["uuid"] = str(self.uuid)
|
|
189
273
|
|
|
190
|
-
for attr_name,
|
|
274
|
+
for attr_name, field_info in self.__class__.model_fields.items():
|
|
191
275
|
if attr_name in self._exclude_attrs:
|
|
192
276
|
continue
|
|
193
277
|
|
|
@@ -195,7 +279,7 @@ class StandardNode(BaseModel):
|
|
|
195
279
|
if isinstance(attr_value, Enum):
|
|
196
280
|
attr_value = attr_value.value
|
|
197
281
|
|
|
198
|
-
field_type = self.guess_field_type(
|
|
282
|
+
field_type = self.guess_field_type(field_info)
|
|
199
283
|
|
|
200
284
|
if attr_value is None:
|
|
201
285
|
data[attr_name] = NULL_VALUE
|
|
@@ -219,10 +303,12 @@ class StandardNode(BaseModel):
|
|
|
219
303
|
limit: int = 1000,
|
|
220
304
|
ids: list[str] | None = None,
|
|
221
305
|
name: str | None = None,
|
|
306
|
+
node_ordering: StandardNodeOrdering | None = None,
|
|
222
307
|
**kwargs: dict[str, Any],
|
|
223
308
|
) -> list[Self]:
|
|
309
|
+
node_ordering = node_ordering or StandardNodeOrdering()
|
|
224
310
|
query: Query = await StandardNodeGetListQuery.init(
|
|
225
|
-
db=db, node_class=cls, ids=ids, node_name=name, limit=limit, **kwargs
|
|
311
|
+
db=db, node_class=cls, ids=ids, node_name=name, limit=limit, node_ordering=node_ordering, **kwargs
|
|
226
312
|
)
|
|
227
313
|
await query.execute(db=db)
|
|
228
314
|
|
infrahub/core/order.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Self
|
|
4
|
+
|
|
5
|
+
from pydantic import BaseModel, model_validator
|
|
6
|
+
|
|
7
|
+
from infrahub.constants.enums import OrderDirection # noqa: TC001
|
|
8
|
+
from infrahub.exceptions import ValidationError
|
|
9
|
+
|
|
10
|
+
# Metadata field name constants
|
|
11
|
+
METADATA_CREATED_AT = "created_at"
|
|
12
|
+
METADATA_CREATED_BY = "created_by"
|
|
13
|
+
METADATA_UPDATED_AT = "updated_at"
|
|
14
|
+
METADATA_UPDATED_BY = "updated_by"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class NodeMetaOrder(BaseModel):
|
|
18
|
+
created_at: OrderDirection | None = None
|
|
19
|
+
updated_at: OrderDirection | None = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class OrderModel(BaseModel):
|
|
23
|
+
disable: bool | None = None
|
|
24
|
+
node_metadata: NodeMetaOrder | None = None
|
|
25
|
+
|
|
26
|
+
@model_validator(mode="after")
|
|
27
|
+
def validate_metadata(self) -> Self:
|
|
28
|
+
if self.node_metadata and self.node_metadata.created_at and self.node_metadata.updated_at:
|
|
29
|
+
raise ValidationError("Cannot order by both created_at and updated_at simultaneously.")
|
|
30
|
+
return self
|
infrahub/core/property.py
CHANGED
infrahub/core/protocols.py
CHANGED
infrahub/core/protocols_base.py
CHANGED
|
@@ -4,6 +4,8 @@ from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
|
|
|
4
4
|
|
|
5
5
|
from typing_extensions import Self
|
|
6
6
|
|
|
7
|
+
from infrahub.core.constants import SYSTEM_USER_ID
|
|
8
|
+
|
|
7
9
|
if TYPE_CHECKING:
|
|
8
10
|
from neo4j import AsyncResult, AsyncSession, AsyncTransaction, Record
|
|
9
11
|
|
|
@@ -82,8 +84,10 @@ class CoreNode(Protocol):
|
|
|
82
84
|
at: Timestamp | str | None = None,
|
|
83
85
|
) -> Self: ...
|
|
84
86
|
async def new(self, db: InfrahubDatabase, id: str | None = None, **kwargs: Any) -> Self: ...
|
|
85
|
-
async def save(self, db: InfrahubDatabase, at: Timestamp | None = None) -> Self: ...
|
|
86
|
-
async def delete(
|
|
87
|
+
async def save(self, db: InfrahubDatabase, at: Timestamp | None = None, user_id: str = SYSTEM_USER_ID) -> Self: ...
|
|
88
|
+
async def delete(
|
|
89
|
+
self, db: InfrahubDatabase, at: Timestamp | None = None, user_id: str = SYSTEM_USER_ID
|
|
90
|
+
) -> None: ...
|
|
87
91
|
async def load(
|
|
88
92
|
self,
|
|
89
93
|
db: InfrahubDatabase,
|
|
@@ -102,3 +106,7 @@ class CoreNode(Protocol):
|
|
|
102
106
|
) -> dict: ...
|
|
103
107
|
async def render_display_label(self, db: InfrahubDatabase | None = None) -> str: ...
|
|
104
108
|
async def from_graphql(self, data: dict, db: InfrahubDatabase) -> bool: ...
|
|
109
|
+
def _get_created_at(self) -> Timestamp | None: ...
|
|
110
|
+
def _get_created_by(self) -> str | None: ...
|
|
111
|
+
def _get_updated_at(self) -> Timestamp | None: ...
|
|
112
|
+
def _get_updated_by(self) -> str | None: ...
|
infrahub/core/query/__init__.py
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from abc import ABC, abstractmethod
|
|
4
3
|
from collections import defaultdict
|
|
5
4
|
from dataclasses import dataclass, field
|
|
6
5
|
from enum import Enum
|
|
@@ -13,7 +12,7 @@ from neo4j.graph import Relationship as Neo4jRelationship
|
|
|
13
12
|
from opentelemetry import trace
|
|
14
13
|
|
|
15
14
|
from infrahub import config
|
|
16
|
-
from infrahub.core.constants import PermissionLevel
|
|
15
|
+
from infrahub.core.constants import SYSTEM_USER_ID, PermissionLevel
|
|
17
16
|
from infrahub.core.timestamp import Timestamp
|
|
18
17
|
from infrahub.exceptions import QueryError
|
|
19
18
|
|
|
@@ -160,7 +159,7 @@ def cleanup_return_labels(labels: list[str]) -> list[str]:
|
|
|
160
159
|
|
|
161
160
|
|
|
162
161
|
class QueryResult:
|
|
163
|
-
def __init__(self, data: list[Neo4jNode | Neo4jRelationship | list[Neo4jNode]], labels: list[str]):
|
|
162
|
+
def __init__(self, data: list[Neo4jNode | Neo4jRelationship | list[Neo4jNode]], labels: list[str]) -> None:
|
|
164
163
|
self.data = data
|
|
165
164
|
self.labels = labels
|
|
166
165
|
self.branch_score: int = 0
|
|
@@ -336,7 +335,7 @@ class QueryStat:
|
|
|
336
335
|
return cls(**data)
|
|
337
336
|
|
|
338
337
|
|
|
339
|
-
class Query
|
|
338
|
+
class Query:
|
|
340
339
|
name: str = "base-query"
|
|
341
340
|
type: QueryType
|
|
342
341
|
|
|
@@ -352,7 +351,8 @@ class Query(ABC):
|
|
|
352
351
|
offset: int | None = None,
|
|
353
352
|
order_by: list[str] | None = None,
|
|
354
353
|
branch_agnostic: bool = False,
|
|
355
|
-
|
|
354
|
+
user_id: str = SYSTEM_USER_ID,
|
|
355
|
+
) -> None:
|
|
356
356
|
if branch:
|
|
357
357
|
self.branch = branch
|
|
358
358
|
|
|
@@ -367,6 +367,7 @@ class Query(ABC):
|
|
|
367
367
|
self.limit = limit
|
|
368
368
|
self.offset = offset
|
|
369
369
|
self.order_by = order_by
|
|
370
|
+
self.user_id = user_id
|
|
370
371
|
|
|
371
372
|
# Initialize internal variables
|
|
372
373
|
self.params: dict = {}
|
|
@@ -403,8 +404,12 @@ class Query(ABC):
|
|
|
403
404
|
|
|
404
405
|
return query
|
|
405
406
|
|
|
406
|
-
@abstractmethod
|
|
407
407
|
async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None:
|
|
408
|
+
# Avoid using this method for new queries and look at migrating older queries. The
|
|
409
|
+
# problem here is that we loose so much information with the `**kwargs` we should instead
|
|
410
|
+
# populate this information via the constructor and anything done within the existing query_init methods
|
|
411
|
+
# could either be handled within __init__ or via dedicated methods within each Query class where appropriate,
|
|
412
|
+
# i.e. things might need to happend in a certain order or we just want to separate the logic better.
|
|
408
413
|
raise NotImplementedError
|
|
409
414
|
|
|
410
415
|
def get_context(self) -> dict[str, str]:
|