infrahub-server 1.6.3__py3-none-any.whl → 1.7.0b0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- infrahub/actions/tasks.py +4 -2
- infrahub/api/schema.py +3 -1
- infrahub/artifacts/tasks.py +1 -0
- infrahub/auth.py +2 -2
- infrahub/cli/db.py +6 -6
- infrahub/computed_attribute/gather.py +3 -4
- infrahub/computed_attribute/tasks.py +23 -6
- infrahub/config.py +8 -0
- infrahub/constants/enums.py +12 -0
- infrahub/core/account.py +5 -8
- infrahub/core/attribute.py +106 -108
- infrahub/core/branch/models.py +44 -71
- infrahub/core/branch/tasks.py +5 -3
- infrahub/core/changelog/diff.py +1 -20
- infrahub/core/changelog/models.py +0 -7
- infrahub/core/constants/__init__.py +17 -0
- infrahub/core/constants/database.py +0 -1
- infrahub/core/constants/schema.py +0 -1
- infrahub/core/convert_object_type/repository_conversion.py +3 -4
- infrahub/core/diff/data_check_synchronizer.py +3 -2
- infrahub/core/diff/enricher/cardinality_one.py +1 -1
- infrahub/core/diff/merger/merger.py +27 -1
- infrahub/core/diff/merger/serializer.py +3 -10
- infrahub/core/diff/model/diff.py +1 -1
- infrahub/core/diff/query/merge.py +376 -135
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/graph/constraints.py +2 -2
- infrahub/core/graph/schema.py +2 -12
- infrahub/core/manager.py +132 -126
- infrahub/core/metadata/__init__.py +0 -0
- infrahub/core/metadata/interface.py +37 -0
- infrahub/core/metadata/model.py +31 -0
- infrahub/core/metadata/query/__init__.py +0 -0
- infrahub/core/metadata/query/node_metadata.py +301 -0
- infrahub/core/migrations/graph/__init__.py +4 -0
- infrahub/core/migrations/graph/m013_convert_git_password_credential.py +3 -8
- infrahub/core/migrations/graph/m017_add_core_profile.py +5 -2
- infrahub/core/migrations/graph/m018_uniqueness_nulls.py +2 -1
- infrahub/core/migrations/graph/m019_restore_rels_to_time.py +0 -10
- infrahub/core/migrations/graph/m020_duplicate_edges.py +0 -8
- infrahub/core/migrations/graph/m025_uniqueness_nulls.py +2 -1
- infrahub/core/migrations/graph/m026_0000_prefix_fix.py +2 -1
- infrahub/core/migrations/graph/m029_duplicates_cleanup.py +0 -1
- infrahub/core/migrations/graph/m031_check_number_attributes.py +2 -2
- infrahub/core/migrations/graph/m038_redo_0000_prefix_fix.py +2 -1
- infrahub/core/migrations/graph/m049_remove_is_visible_relationship.py +38 -0
- infrahub/core/migrations/graph/m050_backfill_vertex_metadata.py +168 -0
- infrahub/core/migrations/query/attribute_add.py +17 -6
- infrahub/core/migrations/query/attribute_remove.py +19 -5
- infrahub/core/migrations/query/attribute_rename.py +21 -5
- infrahub/core/migrations/query/node_duplicate.py +19 -4
- infrahub/core/migrations/schema/attribute_kind_update.py +25 -7
- infrahub/core/migrations/schema/attribute_supports_profile.py +3 -1
- infrahub/core/migrations/schema/models.py +3 -0
- infrahub/core/migrations/schema/node_attribute_add.py +4 -1
- infrahub/core/migrations/schema/node_remove.py +24 -2
- infrahub/core/migrations/schema/tasks.py +4 -1
- infrahub/core/migrations/shared.py +13 -6
- infrahub/core/models.py +6 -6
- infrahub/core/node/__init__.py +156 -57
- infrahub/core/node/create.py +7 -3
- infrahub/core/node/standard.py +100 -14
- infrahub/core/property.py +0 -1
- infrahub/core/protocols_base.py +6 -2
- infrahub/core/query/__init__.py +6 -7
- infrahub/core/query/attribute.py +161 -46
- infrahub/core/query/branch.py +57 -69
- infrahub/core/query/diff.py +4 -4
- infrahub/core/query/node.py +618 -180
- infrahub/core/query/relationship.py +449 -300
- infrahub/core/query/standard_node.py +25 -5
- infrahub/core/query/utils.py +2 -4
- infrahub/core/relationship/constraints/profiles_removal.py +168 -0
- infrahub/core/relationship/model.py +293 -139
- infrahub/core/schema/attribute_parameters.py +1 -28
- infrahub/core/schema/attribute_schema.py +17 -11
- infrahub/core/schema/manager.py +63 -43
- infrahub/core/schema/relationship_schema.py +6 -2
- infrahub/core/schema/schema_branch.py +48 -76
- infrahub/core/task/task.py +4 -2
- infrahub/core/utils.py +0 -22
- infrahub/core/validators/attribute/kind.py +2 -5
- infrahub/core/validators/determiner.py +3 -3
- infrahub/database/__init__.py +3 -3
- infrahub/dependencies/builder/constraint/grouped/node_runner.py +2 -0
- infrahub/dependencies/builder/constraint/relationship_manager/profiles_removal.py +8 -0
- infrahub/dependencies/registry.py +2 -0
- infrahub/display_labels/tasks.py +12 -3
- infrahub/git/integrator.py +18 -18
- infrahub/git/tasks.py +1 -1
- infrahub/graphql/app.py +2 -2
- infrahub/graphql/constants.py +3 -0
- infrahub/graphql/context.py +1 -1
- infrahub/graphql/initialization.py +11 -0
- infrahub/graphql/loaders/account.py +134 -0
- infrahub/graphql/loaders/node.py +5 -12
- infrahub/graphql/loaders/peers.py +5 -7
- infrahub/graphql/manager.py +158 -18
- infrahub/graphql/metadata.py +91 -0
- infrahub/graphql/models.py +33 -3
- infrahub/graphql/mutations/account.py +5 -5
- infrahub/graphql/mutations/attribute.py +0 -2
- infrahub/graphql/mutations/branch.py +9 -5
- infrahub/graphql/mutations/computed_attribute.py +1 -1
- infrahub/graphql/mutations/display_label.py +1 -1
- infrahub/graphql/mutations/hfid.py +1 -1
- infrahub/graphql/mutations/ipam.py +4 -6
- infrahub/graphql/mutations/main.py +9 -4
- infrahub/graphql/mutations/profile.py +16 -22
- infrahub/graphql/mutations/proposed_change.py +4 -4
- infrahub/graphql/mutations/relationship.py +40 -10
- infrahub/graphql/mutations/repository.py +14 -12
- infrahub/graphql/mutations/schema.py +2 -2
- infrahub/graphql/queries/branch.py +62 -6
- infrahub/graphql/queries/diff/tree.py +5 -5
- infrahub/graphql/resolvers/account_metadata.py +84 -0
- infrahub/graphql/resolvers/ipam.py +6 -8
- infrahub/graphql/resolvers/many_relationship.py +77 -35
- infrahub/graphql/resolvers/resolver.py +16 -12
- infrahub/graphql/resolvers/single_relationship.py +87 -23
- infrahub/graphql/subscription/graphql_query.py +2 -0
- infrahub/graphql/types/__init__.py +0 -1
- infrahub/graphql/types/attribute.py +10 -5
- infrahub/graphql/types/branch.py +40 -53
- infrahub/graphql/types/enums.py +3 -0
- infrahub/graphql/types/metadata.py +28 -0
- infrahub/graphql/types/node.py +22 -2
- infrahub/graphql/types/relationship.py +10 -2
- infrahub/graphql/types/standard_node.py +4 -3
- infrahub/hfid/tasks.py +12 -3
- infrahub/profiles/gather.py +56 -0
- infrahub/profiles/mandatory_fields_checker.py +116 -0
- infrahub/profiles/models.py +66 -0
- infrahub/profiles/node_applier.py +153 -12
- infrahub/profiles/queries/get_profile_data.py +143 -31
- infrahub/profiles/tasks.py +79 -27
- infrahub/profiles/triggers.py +22 -0
- infrahub/proposed_change/tasks.py +4 -1
- infrahub/tasks/artifact.py +1 -0
- infrahub/transformations/tasks.py +2 -2
- infrahub/trigger/catalogue.py +2 -0
- infrahub/trigger/models.py +1 -0
- infrahub/trigger/setup.py +3 -3
- infrahub/trigger/tasks.py +3 -0
- infrahub/validators/tasks.py +1 -0
- infrahub/webhook/models.py +1 -1
- infrahub/webhook/tasks.py +1 -1
- infrahub/workers/dependencies.py +9 -3
- infrahub/workers/infrahub_async.py +13 -4
- infrahub/workflows/catalogue.py +19 -0
- infrahub_sdk/node/constants.py +1 -0
- infrahub_sdk/node/related_node.py +13 -4
- infrahub_sdk/node/relationship.py +8 -0
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0b0.dist-info}/METADATA +17 -16
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0b0.dist-info}/RECORD +161 -143
- infrahub_testcontainers/container.py +3 -3
- infrahub_testcontainers/docker-compose-cluster.test.yml +7 -7
- infrahub_testcontainers/docker-compose.test.yml +13 -5
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0b0.dist-info}/WHEEL +0 -0
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0b0.dist-info}/entry_points.txt +0 -0
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0b0.dist-info}/licenses/LICENSE.txt +0 -0
infrahub/actions/tasks.py
CHANGED
|
@@ -211,8 +211,10 @@ async def configure_action_rules(
|
|
|
211
211
|
service: InfrahubServices,
|
|
212
212
|
) -> None:
|
|
213
213
|
await setup_triggers_specific(
|
|
214
|
-
gatherer=gather_trigger_action_rules,
|
|
215
|
-
|
|
214
|
+
gatherer=gather_trigger_action_rules, # type: ignore[arg-type]
|
|
215
|
+
trigger_type=TriggerType.ACTION_TRIGGER_RULE,
|
|
216
|
+
db=service.database,
|
|
217
|
+
)
|
|
216
218
|
|
|
217
219
|
|
|
218
220
|
async def _get_targets(
|
infrahub/api/schema.py
CHANGED
|
@@ -366,6 +366,7 @@ async def load_schema(
|
|
|
366
366
|
diff=result.diff,
|
|
367
367
|
limit=result.diff.all,
|
|
368
368
|
update_db=True,
|
|
369
|
+
user_id=account_session.account_id,
|
|
369
370
|
)
|
|
370
371
|
branch.update_schema_hash()
|
|
371
372
|
log.info("Schema has been updated", branch=branch.name, hash=branch.active_schema_hash.main)
|
|
@@ -375,7 +376,7 @@ async def load_schema(
|
|
|
375
376
|
branch.is_isolated = True
|
|
376
377
|
log.info("Branch converted to isolated mode because the schema has changed", branch=branch.name)
|
|
377
378
|
|
|
378
|
-
await branch.save(db=dbt)
|
|
379
|
+
await branch.save(db=dbt, user_id=account_session.account_id)
|
|
379
380
|
updated_branch = registry.schema.get_schema_branch(name=branch.name)
|
|
380
381
|
updated_hash = updated_branch.get_hash()
|
|
381
382
|
|
|
@@ -387,6 +388,7 @@ async def load_schema(
|
|
|
387
388
|
new_schema=candidate_schema,
|
|
388
389
|
previous_schema=origin_schema,
|
|
389
390
|
migrations=result.migrations,
|
|
391
|
+
user_id=account_session.account_id,
|
|
390
392
|
)
|
|
391
393
|
migration_error_msgs = await service.workflow.execute_workflow(
|
|
392
394
|
workflow=SCHEMA_APPLY_MIGRATION,
|
infrahub/artifacts/tasks.py
CHANGED
|
@@ -14,6 +14,7 @@ async def create(model: CheckArtifactCreate) -> ValidatorConclusion:
|
|
|
14
14
|
await add_tags(branches=[model.branch_name], nodes=[model.target_id])
|
|
15
15
|
|
|
16
16
|
client = get_client()
|
|
17
|
+
client.request_context = model.context.to_request_context()
|
|
17
18
|
|
|
18
19
|
validator = await client.get(kind=InfrahubKind.ARTIFACTVALIDATOR, id=model.validator_id, include=["checks"])
|
|
19
20
|
|
infrahub/auth.py
CHANGED
|
@@ -73,8 +73,8 @@ async def authenticate_with_password(
|
|
|
73
73
|
) -> models.UserToken:
|
|
74
74
|
selected_branch = await registry.get_branch(db=db, branch=branch)
|
|
75
75
|
|
|
76
|
-
response
|
|
77
|
-
schema=
|
|
76
|
+
response = await NodeManager.query(
|
|
77
|
+
schema=CoreGenericAccount,
|
|
78
78
|
db=db,
|
|
79
79
|
branch=selected_branch,
|
|
80
80
|
filters={"name__value": credentials.username},
|
infrahub/cli/db.py
CHANGED
|
@@ -603,17 +603,17 @@ RETURN vertices, edges
|
|
|
603
603
|
edge_path = export_dir / Path("edges.csv")
|
|
604
604
|
edge_path.touch(exist_ok=True)
|
|
605
605
|
|
|
606
|
-
graph_node_schemas =
|
|
606
|
+
graph_node_schemas = (GraphNodeProperties, GraphRelationshipProperties, GraphAttributeProperties)
|
|
607
607
|
graph_vertex_properties = set()
|
|
608
|
-
for
|
|
609
|
-
for field_name, field_info in
|
|
608
|
+
for graph_node_schema in graph_node_schemas:
|
|
609
|
+
for field_name, field_info in graph_node_schema.model_fields.items():
|
|
610
610
|
property_name = field_info.alias or field_name
|
|
611
611
|
graph_vertex_properties.add(property_name)
|
|
612
612
|
|
|
613
|
-
graph_edge_schemas =
|
|
613
|
+
graph_edge_schemas = (GraphRelationshipIsPartOf, GraphRelationshipDefault)
|
|
614
614
|
graph_edge_properties = set()
|
|
615
|
-
for
|
|
616
|
-
for field_name, field_info in
|
|
615
|
+
for graph_edge_schema in graph_edge_schemas:
|
|
616
|
+
for field_name, field_info in graph_edge_schema.model_fields.items():
|
|
617
617
|
property_name = field_info.alias or field_name
|
|
618
618
|
graph_edge_properties.add(property_name)
|
|
619
619
|
|
|
@@ -7,9 +7,9 @@ from prefect import task
|
|
|
7
7
|
from prefect.cache_policies import NONE
|
|
8
8
|
from prefect.logging import get_run_logger
|
|
9
9
|
|
|
10
|
-
from infrahub.core.constants import InfrahubKind
|
|
11
10
|
from infrahub.core.manager import NodeManager
|
|
12
11
|
from infrahub.core.protocols import CoreGenericRepository, CoreGraphQLQuery
|
|
12
|
+
from infrahub.core.protocols import CoreTransformPython as CoreTransformPythonNode
|
|
13
13
|
from infrahub.core.registry import registry
|
|
14
14
|
from infrahub.database import InfrahubDatabase # noqa: TC001 needed for prefect flow
|
|
15
15
|
from infrahub.git.utils import get_repositories_commit_per_branch
|
|
@@ -24,7 +24,6 @@ from .models import (
|
|
|
24
24
|
)
|
|
25
25
|
|
|
26
26
|
if TYPE_CHECKING:
|
|
27
|
-
from infrahub.core.protocols import CoreTransformPython as CoreTransformPythonNode
|
|
28
27
|
from infrahub.git.models import RepositoryData
|
|
29
28
|
|
|
30
29
|
|
|
@@ -48,9 +47,9 @@ async def gather_python_transform_attributes(
|
|
|
48
47
|
if not transform_names:
|
|
49
48
|
return []
|
|
50
49
|
|
|
51
|
-
transforms
|
|
50
|
+
transforms = await NodeManager.query(
|
|
52
51
|
db=db,
|
|
53
|
-
schema=
|
|
52
|
+
schema=CoreTransformPythonNode,
|
|
54
53
|
branch=branch_name,
|
|
55
54
|
fields={"id": None, "name": None, "repository": None, "query": None},
|
|
56
55
|
filters={"name__values": transform_names},
|
|
@@ -42,8 +42,10 @@ mutation UpdateAttribute(
|
|
|
42
42
|
$kind: String!,
|
|
43
43
|
$attribute: String!,
|
|
44
44
|
$value: String!
|
|
45
|
+
$context_account_id: String!
|
|
45
46
|
) {
|
|
46
47
|
InfrahubUpdateComputedAttribute(
|
|
48
|
+
context: {account: {id: $context_account_id}},
|
|
47
49
|
data: {id: $id, attribute: $attribute, value: $value, kind: $kind}
|
|
48
50
|
) {
|
|
49
51
|
ok
|
|
@@ -62,7 +64,7 @@ async def process_transform(
|
|
|
62
64
|
object_id: str,
|
|
63
65
|
computed_attribute_name: str, # noqa: ARG001
|
|
64
66
|
computed_attribute_kind: str, # noqa: ARG001
|
|
65
|
-
context: InfrahubContext,
|
|
67
|
+
context: InfrahubContext,
|
|
66
68
|
updated_fields: list[str] | None = None, # noqa: ARG001
|
|
67
69
|
) -> None:
|
|
68
70
|
await add_tags(branches=[branch_name], nodes=[object_id])
|
|
@@ -103,7 +105,7 @@ async def process_transform(
|
|
|
103
105
|
name=transform.repository.peer.name.value,
|
|
104
106
|
repository_kind=str(transform.repository.peer.typename),
|
|
105
107
|
commit=repo_node.commit.value,
|
|
106
|
-
)
|
|
108
|
+
)
|
|
107
109
|
|
|
108
110
|
data = await client.query_gql_query(
|
|
109
111
|
name=transform.query.id,
|
|
@@ -120,11 +122,17 @@ async def process_transform(
|
|
|
120
122
|
location=f"{transform.file_path.value}::{transform.class_name.value}",
|
|
121
123
|
data=data,
|
|
122
124
|
convert_query_response=transform.convert_query_response.value,
|
|
123
|
-
) # type: ignore[
|
|
125
|
+
) # type: ignore[call-overload]
|
|
124
126
|
|
|
125
127
|
await client.execute_graphql(
|
|
126
128
|
query=UPDATE_ATTRIBUTE,
|
|
127
|
-
variables={
|
|
129
|
+
variables={
|
|
130
|
+
"id": object_id,
|
|
131
|
+
"kind": node_kind,
|
|
132
|
+
"attribute": attribute_name,
|
|
133
|
+
"value": transformed_data,
|
|
134
|
+
"context_account_id": context.account.account_id,
|
|
135
|
+
},
|
|
128
136
|
branch_name=branch_name,
|
|
129
137
|
)
|
|
130
138
|
|
|
@@ -168,6 +176,7 @@ async def computed_attribute_jinja2_update_value(
|
|
|
168
176
|
node_kind: str,
|
|
169
177
|
attribute_name: str,
|
|
170
178
|
template: Jinja2Template,
|
|
179
|
+
context: InfrahubContext,
|
|
171
180
|
) -> None:
|
|
172
181
|
log = get_run_logger()
|
|
173
182
|
client = get_client()
|
|
@@ -182,7 +191,13 @@ async def computed_attribute_jinja2_update_value(
|
|
|
182
191
|
try:
|
|
183
192
|
await client.execute_graphql(
|
|
184
193
|
query=UPDATE_ATTRIBUTE,
|
|
185
|
-
variables={
|
|
194
|
+
variables={
|
|
195
|
+
"id": obj.node_id,
|
|
196
|
+
"kind": node_kind,
|
|
197
|
+
"attribute": attribute_name,
|
|
198
|
+
"value": value,
|
|
199
|
+
"context_account_id": context.account.account_id,
|
|
200
|
+
},
|
|
186
201
|
branch_name=branch_name,
|
|
187
202
|
)
|
|
188
203
|
log.info(f"Updating computed attribute {node_kind}.{attribute_name}='{value}' ({obj.node_id})")
|
|
@@ -202,7 +217,7 @@ async def process_jinja2(
|
|
|
202
217
|
object_id: str,
|
|
203
218
|
computed_attribute_name: str,
|
|
204
219
|
computed_attribute_kind: str,
|
|
205
|
-
context: InfrahubContext,
|
|
220
|
+
context: InfrahubContext,
|
|
206
221
|
updated_fields: list[str] | None = None,
|
|
207
222
|
) -> None:
|
|
208
223
|
log = get_run_logger()
|
|
@@ -258,6 +273,7 @@ async def process_jinja2(
|
|
|
258
273
|
node_kind=node_schema.kind,
|
|
259
274
|
attribute_name=computed_macro.attribute.name,
|
|
260
275
|
template=jinja_template,
|
|
276
|
+
context=context,
|
|
261
277
|
)
|
|
262
278
|
|
|
263
279
|
_ = [response async for _, response in batch.execute()]
|
|
@@ -462,6 +478,7 @@ async def query_transform_targets(
|
|
|
462
478
|
"object_id": subscriber.object_id,
|
|
463
479
|
"computed_attribute_name": computed_attribute.name,
|
|
464
480
|
"computed_attribute_kind": subscriber.kind,
|
|
481
|
+
"context": context,
|
|
465
482
|
},
|
|
466
483
|
)
|
|
467
484
|
|
infrahub/config.py
CHANGED
|
@@ -207,6 +207,14 @@ class MainSettings(BaseSettings):
|
|
|
207
207
|
def convert_to_path(cls, value: Path | str) -> Path:
|
|
208
208
|
return Path(value) if isinstance(value, str) else value
|
|
209
209
|
|
|
210
|
+
@property
|
|
211
|
+
def infrahub_address(self) -> str:
|
|
212
|
+
"""This is the address that the Prefect worker will use to connect to Infrahub API."""
|
|
213
|
+
if self.internal_address:
|
|
214
|
+
return self.internal_address
|
|
215
|
+
|
|
216
|
+
raise InitializationError()
|
|
217
|
+
|
|
210
218
|
|
|
211
219
|
class FileSystemStorageSettings(BaseSettings):
|
|
212
220
|
# Make variable lookup case-sensitive to avoid fetching $PATH value
|
infrahub/core/account.py
CHANGED
|
@@ -54,15 +54,11 @@ class AccountGlobalPermissionQuery(Query):
|
|
|
54
54
|
name: str = "account_global_permissions"
|
|
55
55
|
type: QueryType = QueryType.READ
|
|
56
56
|
|
|
57
|
-
def __init__(
|
|
58
|
-
self,
|
|
59
|
-
account_id: str,
|
|
60
|
-
branch: Branch | None = None,
|
|
61
|
-
branch_agnostic: bool = False,
|
|
62
|
-
) -> None:
|
|
63
|
-
super().__init__(branch=branch, branch_agnostic=branch_agnostic)
|
|
57
|
+
def __init__(self, account_id: str, **kwargs: Any):
|
|
64
58
|
self.account_id = account_id
|
|
59
|
+
super().__init__(**kwargs)
|
|
65
60
|
|
|
61
|
+
async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
|
|
66
62
|
self.params["account_id"] = self.account_id
|
|
67
63
|
|
|
68
64
|
branch_filter, branch_params = self.branch.get_query_filter_path(
|
|
@@ -70,6 +66,7 @@ class AccountGlobalPermissionQuery(Query):
|
|
|
70
66
|
)
|
|
71
67
|
self.params.update(branch_params)
|
|
72
68
|
|
|
69
|
+
# ruff: noqa: E501
|
|
73
70
|
query = """
|
|
74
71
|
MATCH (account:%(generic_account_node)s)
|
|
75
72
|
WHERE account.uuid = $account_id
|
|
@@ -296,7 +293,7 @@ class AccountObjectPermissionQuery(Query):
|
|
|
296
293
|
|
|
297
294
|
|
|
298
295
|
async def fetch_permissions(account_id: str, db: InfrahubDatabase, branch: Branch) -> AssignedPermissions:
|
|
299
|
-
query1 = AccountGlobalPermissionQuery(branch=branch, account_id=account_id, branch_agnostic=True)
|
|
296
|
+
query1 = await AccountGlobalPermissionQuery.init(db=db, branch=branch, account_id=account_id, branch_agnostic=True)
|
|
300
297
|
await query1.execute(db=db)
|
|
301
298
|
global_permissions = query1.get_permissions()
|
|
302
299
|
|
infrahub/core/attribute.py
CHANGED
|
@@ -15,18 +15,27 @@ from pydantic import BaseModel, Field
|
|
|
15
15
|
from infrahub import config
|
|
16
16
|
from infrahub.core import registry
|
|
17
17
|
from infrahub.core.changelog.models import AttributeChangelog
|
|
18
|
-
from infrahub.core.constants import
|
|
18
|
+
from infrahub.core.constants import (
|
|
19
|
+
NULL_VALUE,
|
|
20
|
+
SYSTEM_USER_ID,
|
|
21
|
+
AttributeDBNodeType,
|
|
22
|
+
BranchSupportType,
|
|
23
|
+
InfrahubKind,
|
|
24
|
+
MetadataOptions,
|
|
25
|
+
)
|
|
26
|
+
from infrahub.core.metadata.interface import MetadataInterface
|
|
27
|
+
from infrahub.core.metadata.model import MetadataInfo
|
|
19
28
|
from infrahub.core.property import FlagPropertyMixin, NodePropertyData, NodePropertyMixin
|
|
20
29
|
from infrahub.core.query.attribute import (
|
|
21
30
|
AttributeClearNodePropertyQuery,
|
|
22
|
-
|
|
31
|
+
AttributeDeleteQuery,
|
|
23
32
|
AttributeUpdateFlagQuery,
|
|
24
33
|
AttributeUpdateNodePropertyQuery,
|
|
25
34
|
AttributeUpdateValueQuery,
|
|
26
35
|
)
|
|
27
36
|
from infrahub.core.query.node import AttributeFromDB, NodeListGetAttributeQuery
|
|
28
37
|
from infrahub.core.timestamp import Timestamp
|
|
29
|
-
from infrahub.core.utils import
|
|
38
|
+
from infrahub.core.utils import convert_ip_to_binary_str
|
|
30
39
|
from infrahub.exceptions import ValidationError
|
|
31
40
|
from infrahub.helpers import hash_password
|
|
32
41
|
|
|
@@ -70,12 +79,11 @@ class AttributeCreateData(BaseModel):
|
|
|
70
79
|
content: dict[str, Any]
|
|
71
80
|
is_default: bool
|
|
72
81
|
is_protected: bool
|
|
73
|
-
is_visible: bool
|
|
74
82
|
source_prop: list[NodePropertyData] = Field(default_factory=list)
|
|
75
83
|
owner_prop: list[NodePropertyData] = Field(default_factory=list)
|
|
76
84
|
|
|
77
85
|
|
|
78
|
-
class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
|
|
86
|
+
class BaseAttribute(FlagPropertyMixin, NodePropertyMixin, MetadataInterface):
|
|
79
87
|
type: type | tuple[type] | None = None
|
|
80
88
|
|
|
81
89
|
_rel_to_node_label: str = RELATIONSHIP_TO_NODE_LABEL
|
|
@@ -91,7 +99,6 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
|
|
|
91
99
|
id: str | None = None,
|
|
92
100
|
db_id: str | None = None,
|
|
93
101
|
data: dict | str | AttributeFromDB | None = None,
|
|
94
|
-
updated_at: Timestamp | str | None = None,
|
|
95
102
|
is_default: bool = False,
|
|
96
103
|
is_from_profile: bool = False,
|
|
97
104
|
**kwargs: dict[str, Any],
|
|
@@ -99,7 +106,6 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
|
|
|
99
106
|
self.id = id
|
|
100
107
|
self.db_id = db_id
|
|
101
108
|
|
|
102
|
-
self.updated_at = updated_at
|
|
103
109
|
self.name = name
|
|
104
110
|
self.node = node
|
|
105
111
|
self.schema = schema
|
|
@@ -112,6 +118,13 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
|
|
|
112
118
|
self._init_node_property_mixin(kwargs)
|
|
113
119
|
self._init_flag_property_mixin(kwargs)
|
|
114
120
|
|
|
121
|
+
self._metadata = MetadataInfo(
|
|
122
|
+
created_at=kwargs.get("created_at"),
|
|
123
|
+
created_by=kwargs.get("created_by"),
|
|
124
|
+
updated_at=kwargs.get("updated_at"),
|
|
125
|
+
updated_by=kwargs.get("updated_by"),
|
|
126
|
+
)
|
|
127
|
+
|
|
115
128
|
self.value = None
|
|
116
129
|
|
|
117
130
|
if isinstance(data, AttributeFromDB):
|
|
@@ -130,8 +143,6 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
|
|
|
130
143
|
for field_name in fields_to_extract_from_data:
|
|
131
144
|
setattr(self, field_name, data.get(field_name, None))
|
|
132
145
|
|
|
133
|
-
if not self.updated_at and "updated_at" in data:
|
|
134
|
-
self.updated_at = Timestamp(data.get("updated_at"))
|
|
135
146
|
elif data is None:
|
|
136
147
|
self.is_default = True
|
|
137
148
|
else:
|
|
@@ -151,9 +162,6 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
|
|
|
151
162
|
if self.is_protected is None:
|
|
152
163
|
self.is_protected = False
|
|
153
164
|
|
|
154
|
-
if self.is_visible is None:
|
|
155
|
-
self.is_visible = True
|
|
156
|
-
|
|
157
165
|
@property
|
|
158
166
|
def is_enum(self) -> bool:
|
|
159
167
|
return bool(self.schema.enum)
|
|
@@ -187,6 +195,30 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
|
|
|
187
195
|
if self.is_enum and self.value:
|
|
188
196
|
self.value = self.schema.convert_value_to_enum(self.value)
|
|
189
197
|
|
|
198
|
+
def _set_created_at(self, value: Timestamp | None) -> None:
|
|
199
|
+
self._metadata.created_at = value
|
|
200
|
+
|
|
201
|
+
def _set_created_by(self, value: str | None) -> None:
|
|
202
|
+
self._metadata.created_by = value
|
|
203
|
+
|
|
204
|
+
def _set_updated_at(self, value: Timestamp | None) -> None:
|
|
205
|
+
self._metadata.updated_at = value
|
|
206
|
+
|
|
207
|
+
def _set_updated_by(self, value: str | None) -> None:
|
|
208
|
+
self._metadata.updated_by = value
|
|
209
|
+
|
|
210
|
+
def _get_created_at(self) -> Timestamp | None:
|
|
211
|
+
return self._metadata.created_at
|
|
212
|
+
|
|
213
|
+
def _get_created_by(self) -> str | None:
|
|
214
|
+
return self._metadata.created_by
|
|
215
|
+
|
|
216
|
+
def _get_updated_at(self) -> Timestamp | None:
|
|
217
|
+
return self._metadata.updated_at
|
|
218
|
+
|
|
219
|
+
def _get_updated_by(self) -> str | None:
|
|
220
|
+
return self._metadata.updated_by
|
|
221
|
+
|
|
190
222
|
@staticmethod
|
|
191
223
|
def get_allowed_property_in_path() -> list[str]:
|
|
192
224
|
return ["value"]
|
|
@@ -302,8 +334,10 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
|
|
|
302
334
|
if prop_name in data.node_properties:
|
|
303
335
|
setattr(self, prop_name, data.node_properties[prop_name].uuid)
|
|
304
336
|
|
|
305
|
-
|
|
306
|
-
|
|
337
|
+
self._set_created_at(data.created_at)
|
|
338
|
+
self._set_created_by(data.created_by)
|
|
339
|
+
self._set_updated_at(data.updated_at)
|
|
340
|
+
self._set_updated_by(data.updated_by)
|
|
307
341
|
|
|
308
342
|
def value_from_db(self, data: AttributeFromDB) -> Any:
|
|
309
343
|
if data.value == NULL_VALUE:
|
|
@@ -320,7 +354,9 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
|
|
|
320
354
|
"""Deserialize the value coming from the database."""
|
|
321
355
|
return data.value
|
|
322
356
|
|
|
323
|
-
async def save(
|
|
357
|
+
async def save(
|
|
358
|
+
self, db: InfrahubDatabase, user_id: str = SYSTEM_USER_ID, at: Timestamp | None = None
|
|
359
|
+
) -> AttributeChangelog | None:
|
|
324
360
|
"""Create or Update the Attribute in the database."""
|
|
325
361
|
|
|
326
362
|
save_at = Timestamp(at)
|
|
@@ -328,70 +364,34 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
|
|
|
328
364
|
if not self.id:
|
|
329
365
|
return None
|
|
330
366
|
|
|
331
|
-
return await self._update(
|
|
367
|
+
return await self._update(db=db, user_id=user_id, at=save_at)
|
|
332
368
|
|
|
333
|
-
async def delete(
|
|
369
|
+
async def delete(
|
|
370
|
+
self, db: InfrahubDatabase, user_id: str = SYSTEM_USER_ID, at: Timestamp | None = None
|
|
371
|
+
) -> AttributeChangelog | None:
|
|
334
372
|
if not self.db_id:
|
|
335
373
|
return None
|
|
336
374
|
|
|
337
375
|
delete_at = Timestamp(at)
|
|
376
|
+
branch = self.get_branch_based_on_support_type()
|
|
338
377
|
|
|
339
|
-
query = await
|
|
378
|
+
query = await AttributeDeleteQuery.init(db=db, branch=branch, attr=self, user_id=user_id, at=delete_at)
|
|
340
379
|
await query.execute(db=db)
|
|
341
|
-
|
|
380
|
+
previous_value = query.get_previous_property_value()
|
|
342
381
|
|
|
343
|
-
if not
|
|
382
|
+
if not previous_value:
|
|
344
383
|
return None
|
|
345
384
|
|
|
346
|
-
|
|
385
|
+
return AttributeChangelog(
|
|
347
386
|
name=self.name,
|
|
348
387
|
value=None,
|
|
349
|
-
value_previous=
|
|
388
|
+
value_previous=previous_value,
|
|
350
389
|
kind=self.schema.kind,
|
|
351
390
|
)
|
|
352
391
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
# Check all the relationship and update the one that are in the same branch
|
|
357
|
-
rel_ids_to_update = set()
|
|
358
|
-
for result in results:
|
|
359
|
-
if result.get_rel("r2").type == "HAS_VALUE":
|
|
360
|
-
changelog.value_previous = result.get_node("ap").get("value")
|
|
361
|
-
properties_to_delete.append((result.get_rel("r2").type, result.get_node("ap").element_id))
|
|
362
|
-
|
|
363
|
-
await add_relationship(
|
|
364
|
-
src_node_id=self.db_id,
|
|
365
|
-
dst_node_id=result.get_node("ap").element_id,
|
|
366
|
-
rel_type=result.get_rel("r2").type,
|
|
367
|
-
branch_name=branch.name,
|
|
368
|
-
branch_level=branch.hierarchy_level,
|
|
369
|
-
at=delete_at,
|
|
370
|
-
status=RelationshipStatus.DELETED,
|
|
371
|
-
db=db,
|
|
372
|
-
)
|
|
373
|
-
|
|
374
|
-
for rel in result.get_rels():
|
|
375
|
-
if rel.get("branch") == branch.name:
|
|
376
|
-
rel_ids_to_update.add(rel.element_id)
|
|
377
|
-
|
|
378
|
-
if rel_ids_to_update:
|
|
379
|
-
await update_relationships_to(ids=list(rel_ids_to_update), to=delete_at, db=db)
|
|
380
|
-
|
|
381
|
-
await add_relationship(
|
|
382
|
-
src_node_id=self.node.db_id,
|
|
383
|
-
dst_node_id=self.db_id,
|
|
384
|
-
rel_type="HAS_ATTRIBUTE",
|
|
385
|
-
branch_name=branch.name,
|
|
386
|
-
branch_level=branch.hierarchy_level,
|
|
387
|
-
at=delete_at,
|
|
388
|
-
status=RelationshipStatus.DELETED,
|
|
389
|
-
db=db,
|
|
390
|
-
)
|
|
391
|
-
|
|
392
|
-
return changelog
|
|
393
|
-
|
|
394
|
-
async def _update(self, db: InfrahubDatabase, at: Timestamp | None = None) -> AttributeChangelog | None:
|
|
392
|
+
async def _update(
|
|
393
|
+
self, db: InfrahubDatabase, user_id: str, at: Timestamp | None = None
|
|
394
|
+
) -> AttributeChangelog | None:
|
|
395
395
|
"""Update the attribute in the database.
|
|
396
396
|
|
|
397
397
|
Get the current value
|
|
@@ -422,13 +422,10 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
|
|
|
422
422
|
fields={self.name: True},
|
|
423
423
|
branch=self.branch,
|
|
424
424
|
at=update_at,
|
|
425
|
-
|
|
426
|
-
include_owner=True,
|
|
425
|
+
include_metadata=MetadataOptions.LINKED_NODES,
|
|
427
426
|
)
|
|
428
427
|
await query.execute(db=db)
|
|
429
|
-
current_attr_data,
|
|
430
|
-
|
|
431
|
-
branch = self.get_branch_based_on_support_type()
|
|
428
|
+
current_attr_data, _ = query.get_result_by_id_and_name(self.node.id, self.name)
|
|
432
429
|
|
|
433
430
|
changelog = AttributeChangelog(
|
|
434
431
|
name=self.name,
|
|
@@ -437,36 +434,26 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
|
|
|
437
434
|
kind=self.schema.kind,
|
|
438
435
|
)
|
|
439
436
|
|
|
437
|
+
branch = self.get_branch_based_on_support_type()
|
|
438
|
+
|
|
440
439
|
# ---------- Update the Value ----------
|
|
441
440
|
if current_attr_data.content != self.to_db():
|
|
442
441
|
# Create the new AttributeValue and update the existing relationship
|
|
443
|
-
query = await AttributeUpdateValueQuery.init(db=db, attr=self, at=update_at)
|
|
442
|
+
query = await AttributeUpdateValueQuery.init(db=db, branch=branch, attr=self, user_id=user_id, at=update_at)
|
|
444
443
|
await query.execute(db=db)
|
|
445
444
|
|
|
446
|
-
# TODO check that everything went well
|
|
447
|
-
rel = current_attr_result.get_rel("r2")
|
|
448
|
-
if rel.get("branch") == branch.name:
|
|
449
|
-
await update_relationships_to([rel.element_id], to=update_at, db=db)
|
|
450
|
-
|
|
451
445
|
# ---------- Update the Flags ----------
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
(
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
)
|
|
464
|
-
query = await AttributeUpdateFlagQuery.init(db=db, attr=self, at=update_at, flag_name=flag_name)
|
|
465
|
-
await query.execute(db=db)
|
|
466
|
-
|
|
467
|
-
rel = current_attr_result.get(rel_name)
|
|
468
|
-
if rel.get("branch") == branch.name:
|
|
469
|
-
await update_relationships_to([rel.element_id], to=update_at, db=db)
|
|
446
|
+
flag_name = "is_protected"
|
|
447
|
+
if current_attr_data.flag_properties[flag_name] != getattr(self, flag_name):
|
|
448
|
+
changelog.add_property(
|
|
449
|
+
name=flag_name,
|
|
450
|
+
value_current=getattr(self, flag_name),
|
|
451
|
+
value_previous=current_attr_data.flag_properties[flag_name],
|
|
452
|
+
)
|
|
453
|
+
query = await AttributeUpdateFlagQuery.init(
|
|
454
|
+
db=db, branch=branch, attr=self, user_id=user_id, at=update_at, flag_name=flag_name
|
|
455
|
+
)
|
|
456
|
+
await query.execute(db=db)
|
|
470
457
|
|
|
471
458
|
# ---------- Update the Node Properties ----------
|
|
472
459
|
for prop_name in self._node_properties:
|
|
@@ -488,21 +475,27 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
|
|
|
488
475
|
|
|
489
476
|
if needs_update:
|
|
490
477
|
query = await AttributeUpdateNodePropertyQuery.init(
|
|
491
|
-
db=db,
|
|
478
|
+
db=db,
|
|
479
|
+
branch=branch,
|
|
480
|
+
attr=self,
|
|
481
|
+
user_id=user_id,
|
|
482
|
+
at=update_at,
|
|
483
|
+
prop_name=prop_name,
|
|
484
|
+
prop_id=current_prop_id,
|
|
492
485
|
)
|
|
493
486
|
await query.execute(db=db)
|
|
494
487
|
|
|
495
488
|
if needs_clear:
|
|
496
489
|
query = await AttributeClearNodePropertyQuery.init(
|
|
497
|
-
db=db,
|
|
490
|
+
db=db,
|
|
491
|
+
branch=branch,
|
|
492
|
+
attr=self,
|
|
493
|
+
user_id=user_id,
|
|
494
|
+
at=update_at,
|
|
495
|
+
prop_name=prop_name,
|
|
498
496
|
)
|
|
499
497
|
await query.execute(db=db)
|
|
500
498
|
|
|
501
|
-
# set the to time on the previously active edge
|
|
502
|
-
rel = current_attr_result.get(f"rel_{prop_name}")
|
|
503
|
-
if rel and rel.get("branch") == branch.name:
|
|
504
|
-
await update_relationships_to([rel.element_id], to=update_at, db=db)
|
|
505
|
-
|
|
506
499
|
if changelog.has_updates:
|
|
507
500
|
return changelog
|
|
508
501
|
|
|
@@ -531,10 +524,19 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
|
|
|
531
524
|
|
|
532
525
|
for field_name in field_names:
|
|
533
526
|
if field_name == "updated_at":
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
527
|
+
updated_at = self._get_updated_at()
|
|
528
|
+
response[field_name] = await updated_at.to_graphql() if updated_at else None
|
|
529
|
+
continue
|
|
530
|
+
|
|
531
|
+
if field_name == "updated_by":
|
|
532
|
+
response[field_name] = (
|
|
533
|
+
{
|
|
534
|
+
"id": self._get_updated_by(),
|
|
535
|
+
"__kind__": InfrahubKind.ACCOUNT,
|
|
536
|
+
}
|
|
537
|
+
if self._get_updated_by()
|
|
538
|
+
else None
|
|
539
|
+
)
|
|
538
540
|
continue
|
|
539
541
|
|
|
540
542
|
if field_name == "__typename":
|
|
@@ -620,9 +622,6 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
|
|
|
620
622
|
if "is_protected" in data and data["is_protected"] != self.is_protected:
|
|
621
623
|
self.is_protected = data["is_protected"]
|
|
622
624
|
changed = True
|
|
623
|
-
if "is_visible" in data and data["is_visible"] != self.is_visible:
|
|
624
|
-
self.is_visible = data["is_visible"]
|
|
625
|
-
changed = True
|
|
626
625
|
|
|
627
626
|
if "source" in data and data["source"] != self.source_id:
|
|
628
627
|
self.source = data["source"]
|
|
@@ -657,7 +656,6 @@ class BaseAttribute(FlagPropertyMixin, NodePropertyMixin):
|
|
|
657
656
|
content=self.to_db(),
|
|
658
657
|
is_default=self.is_default,
|
|
659
658
|
is_protected=self.is_protected,
|
|
660
|
-
is_visible=self.is_visible,
|
|
661
659
|
)
|
|
662
660
|
if self.source_id:
|
|
663
661
|
data.source_prop.append(NodePropertyData(name="source", peer_id=self.source_id))
|