infrahub-server 1.4.10__py3-none-any.whl → 1.5.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 +200 -16
- infrahub/api/artifact.py +3 -0
- infrahub/api/query.py +2 -0
- infrahub/api/schema.py +3 -0
- infrahub/auth.py +5 -5
- infrahub/cli/db.py +2 -2
- infrahub/config.py +7 -2
- infrahub/core/attribute.py +22 -19
- infrahub/core/branch/models.py +2 -2
- infrahub/core/branch/needs_rebase_status.py +11 -0
- infrahub/core/branch/tasks.py +2 -2
- infrahub/core/constants/__init__.py +1 -0
- infrahub/core/convert_object_type/object_conversion.py +201 -0
- infrahub/core/convert_object_type/repository_conversion.py +89 -0
- infrahub/core/convert_object_type/schema_mapping.py +27 -3
- infrahub/core/diff/query/artifact.py +1 -1
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/initialization.py +2 -2
- infrahub/core/manager.py +3 -81
- infrahub/core/migrations/graph/__init__.py +2 -0
- infrahub/core/migrations/graph/m040_profile_attrs_in_db.py +166 -0
- infrahub/core/node/__init__.py +23 -2
- infrahub/core/node/create.py +67 -35
- infrahub/core/node/lock_utils.py +98 -0
- infrahub/core/property.py +11 -0
- infrahub/core/protocols.py +1 -0
- infrahub/core/query/attribute.py +27 -15
- infrahub/core/query/node.py +47 -184
- infrahub/core/query/relationship.py +43 -26
- infrahub/core/query/subquery.py +0 -8
- infrahub/core/relationship/model.py +59 -19
- infrahub/core/schema/attribute_schema.py +0 -2
- infrahub/core/schema/definitions/core/repository.py +7 -0
- infrahub/core/schema/relationship_schema.py +0 -1
- infrahub/core/schema/schema_branch.py +3 -2
- infrahub/generators/models.py +31 -12
- infrahub/generators/tasks.py +3 -1
- infrahub/git/base.py +38 -1
- infrahub/graphql/api/dependencies.py +2 -4
- infrahub/graphql/api/endpoints.py +2 -2
- infrahub/graphql/app.py +2 -4
- infrahub/graphql/initialization.py +2 -3
- infrahub/graphql/manager.py +212 -137
- infrahub/graphql/middleware.py +12 -0
- infrahub/graphql/mutations/branch.py +11 -0
- infrahub/graphql/mutations/computed_attribute.py +110 -3
- infrahub/graphql/mutations/convert_object_type.py +34 -13
- infrahub/graphql/mutations/ipam.py +21 -8
- infrahub/graphql/mutations/main.py +37 -153
- infrahub/graphql/mutations/profile.py +195 -0
- infrahub/graphql/mutations/proposed_change.py +2 -1
- infrahub/graphql/mutations/repository.py +22 -83
- infrahub/graphql/mutations/webhook.py +1 -1
- infrahub/graphql/registry.py +173 -0
- infrahub/graphql/schema.py +4 -1
- infrahub/lock.py +52 -26
- infrahub/locks/__init__.py +0 -0
- infrahub/locks/tasks.py +37 -0
- infrahub/patch/plan_writer.py +2 -2
- infrahub/profiles/__init__.py +0 -0
- infrahub/profiles/node_applier.py +101 -0
- infrahub/profiles/queries/__init__.py +0 -0
- infrahub/profiles/queries/get_profile_data.py +99 -0
- infrahub/profiles/tasks.py +63 -0
- infrahub/repositories/__init__.py +0 -0
- infrahub/repositories/create_repository.py +113 -0
- infrahub/tasks/registry.py +6 -4
- infrahub/webhook/models.py +1 -1
- infrahub/workflows/catalogue.py +38 -3
- infrahub/workflows/models.py +17 -2
- infrahub_sdk/branch.py +5 -8
- infrahub_sdk/client.py +364 -84
- infrahub_sdk/convert_object_type.py +61 -0
- infrahub_sdk/ctl/check.py +2 -3
- infrahub_sdk/ctl/cli_commands.py +16 -12
- infrahub_sdk/ctl/config.py +8 -2
- infrahub_sdk/ctl/generator.py +2 -3
- infrahub_sdk/ctl/repository.py +39 -1
- infrahub_sdk/ctl/schema.py +12 -1
- infrahub_sdk/ctl/utils.py +4 -0
- infrahub_sdk/ctl/validate.py +5 -3
- infrahub_sdk/diff.py +4 -5
- infrahub_sdk/exceptions.py +2 -0
- infrahub_sdk/graphql.py +7 -2
- infrahub_sdk/node/attribute.py +2 -0
- infrahub_sdk/node/node.py +28 -20
- infrahub_sdk/playback.py +1 -2
- infrahub_sdk/protocols.py +40 -6
- infrahub_sdk/pytest_plugin/plugin.py +7 -4
- infrahub_sdk/pytest_plugin/utils.py +40 -0
- infrahub_sdk/repository.py +1 -2
- infrahub_sdk/schema/main.py +1 -0
- infrahub_sdk/spec/object.py +43 -4
- infrahub_sdk/spec/range_expansion.py +118 -0
- infrahub_sdk/timestamp.py +18 -6
- {infrahub_server-1.4.10.dist-info → infrahub_server-1.5.0b0.dist-info}/METADATA +6 -9
- {infrahub_server-1.4.10.dist-info → infrahub_server-1.5.0b0.dist-info}/RECORD +102 -84
- infrahub_testcontainers/models.py +2 -2
- infrahub_testcontainers/performance_test.py +4 -4
- infrahub/core/convert_object_type/conversion.py +0 -134
- {infrahub_server-1.4.10.dist-info → infrahub_server-1.5.0b0.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.4.10.dist-info → infrahub_server-1.5.0b0.dist-info}/WHEEL +0 -0
- {infrahub_server-1.4.10.dist-info → infrahub_server-1.5.0b0.dist-info}/entry_points.txt +0 -0
infrahub/core/query/node.py
CHANGED
|
@@ -11,6 +11,7 @@ from infrahub import config
|
|
|
11
11
|
from infrahub.core import registry
|
|
12
12
|
from infrahub.core.constants import (
|
|
13
13
|
GLOBAL_BRANCH_NAME,
|
|
14
|
+
PROFILE_NODE_RELATIONSHIP_IDENTIFIER,
|
|
14
15
|
AttributeDBNodeType,
|
|
15
16
|
RelationshipDirection,
|
|
16
17
|
RelationshipHierarchyDirection,
|
|
@@ -43,7 +44,6 @@ class NodeToProcess:
|
|
|
43
44
|
|
|
44
45
|
node_id: str
|
|
45
46
|
node_uuid: str
|
|
46
|
-
profile_uuids: list[str]
|
|
47
47
|
|
|
48
48
|
updated_at: str
|
|
49
49
|
|
|
@@ -606,6 +606,7 @@ class NodeListGetAttributeQuery(Query):
|
|
|
606
606
|
|
|
607
607
|
async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: # noqa: ARG002
|
|
608
608
|
self.params["ids"] = self.ids
|
|
609
|
+
self.params["profile_relationship_name"] = PROFILE_NODE_RELATIONSHIP_IDENTIFIER
|
|
609
610
|
|
|
610
611
|
branch_filter, branch_params = self.branch.get_query_filter_path(
|
|
611
612
|
at=self.at, branch_agnostic=self.branch_agnostic
|
|
@@ -614,6 +615,7 @@ class NodeListGetAttributeQuery(Query):
|
|
|
614
615
|
|
|
615
616
|
query = """
|
|
616
617
|
MATCH (n:Node) WHERE n.uuid IN $ids
|
|
618
|
+
WITH n, exists((n)-[:IS_RELATED]-(:Relationship {name: $profile_relationship_name})) AS might_use_profile
|
|
617
619
|
MATCH (n)-[:HAS_ATTRIBUTE]-(a:Attribute)
|
|
618
620
|
"""
|
|
619
621
|
if self.fields:
|
|
@@ -626,15 +628,23 @@ class NodeListGetAttributeQuery(Query):
|
|
|
626
628
|
CALL (n, a) {
|
|
627
629
|
MATCH (n)-[r:HAS_ATTRIBUTE]-(a:Attribute)
|
|
628
630
|
WHERE %(branch_filter)s
|
|
629
|
-
RETURN
|
|
631
|
+
RETURN r AS r1
|
|
630
632
|
ORDER BY r.branch_level DESC, r.from DESC
|
|
631
633
|
LIMIT 1
|
|
632
634
|
}
|
|
633
|
-
WITH
|
|
635
|
+
WITH n, r1, a, might_use_profile
|
|
634
636
|
WHERE r1.status = "active"
|
|
635
|
-
WITH n, r1, a
|
|
637
|
+
WITH n, r1, a, might_use_profile
|
|
636
638
|
MATCH (a)-[r:HAS_VALUE]-(av:AttributeValue)
|
|
637
639
|
WHERE %(branch_filter)s
|
|
640
|
+
CALL (a, might_use_profile) {
|
|
641
|
+
OPTIONAL MATCH (a)-[r:HAS_SOURCE]->(:CoreProfile)
|
|
642
|
+
WHERE might_use_profile = TRUE AND %(branch_filter)s
|
|
643
|
+
RETURN r.status = "active" AS has_active_profile
|
|
644
|
+
ORDER BY r.branch_level DESC, r.from DESC, r.status ASC
|
|
645
|
+
LIMIT 1
|
|
646
|
+
}
|
|
647
|
+
WITH *, has_active_profile = TRUE AS is_from_profile
|
|
638
648
|
CALL (a, av) {
|
|
639
649
|
MATCH (a)-[r:HAS_VALUE]-(av:AttributeValue)
|
|
640
650
|
WHERE %(branch_filter)s
|
|
@@ -642,13 +652,13 @@ class NodeListGetAttributeQuery(Query):
|
|
|
642
652
|
ORDER BY r.branch_level DESC, r.from DESC
|
|
643
653
|
LIMIT 1
|
|
644
654
|
}
|
|
645
|
-
WITH n, r1, a1 as a, r2, av1 as av
|
|
655
|
+
WITH n, r1, a1 as a, r2, av1 as av, is_from_profile
|
|
646
656
|
WHERE r2.status = "active"
|
|
647
|
-
WITH n, a, av, r1, r2
|
|
657
|
+
WITH n, a, av, r1, r2, is_from_profile
|
|
648
658
|
""" % {"branch_filter": branch_filter}
|
|
649
659
|
self.add_to_query(query)
|
|
650
660
|
|
|
651
|
-
self.return_labels = ["n", "a", "av", "r1", "r2"]
|
|
661
|
+
self.return_labels = ["n", "a", "av", "r1", "r2", "is_from_profile"]
|
|
652
662
|
|
|
653
663
|
# Add Is_Protected and Is_visible
|
|
654
664
|
rel_isv_branch_filter, _ = self.branch.get_query_filter_path(
|
|
@@ -668,16 +678,32 @@ class NodeListGetAttributeQuery(Query):
|
|
|
668
678
|
|
|
669
679
|
if self.include_source:
|
|
670
680
|
query = """
|
|
671
|
-
|
|
672
|
-
|
|
681
|
+
CALL (a) {
|
|
682
|
+
OPTIONAL MATCH (a)-[rel_source:HAS_SOURCE]-(source)
|
|
683
|
+
WHERE all(r IN [rel_source] WHERE ( %(branch_filter)s ))
|
|
684
|
+
RETURN source, rel_source
|
|
685
|
+
ORDER BY rel_source.branch_level DESC, rel_source.from DESC, rel_source.status ASC
|
|
686
|
+
LIMIT 1
|
|
687
|
+
}
|
|
688
|
+
WITH *,
|
|
689
|
+
CASE WHEN rel_source.status = "active" THEN source ELSE NULL END AS source,
|
|
690
|
+
CASE WHEN rel_source.status = "active" THEN rel_source ELSE NULL END AS rel_source
|
|
673
691
|
""" % {"branch_filter": branch_filter}
|
|
674
692
|
self.add_to_query(query)
|
|
675
693
|
self.return_labels.extend(["source", "rel_source"])
|
|
676
694
|
|
|
677
695
|
if self.include_owner:
|
|
678
696
|
query = """
|
|
679
|
-
|
|
680
|
-
|
|
697
|
+
CALL (a) {
|
|
698
|
+
OPTIONAL MATCH (a)-[rel_owner:HAS_OWNER]-(owner)
|
|
699
|
+
WHERE all(r IN [rel_owner] WHERE ( %(branch_filter)s ))
|
|
700
|
+
RETURN owner, rel_owner
|
|
701
|
+
ORDER BY rel_owner.branch_level DESC, rel_owner.from DESC, rel_owner.status ASC
|
|
702
|
+
LIMIT 1
|
|
703
|
+
}
|
|
704
|
+
WITH *,
|
|
705
|
+
CASE WHEN rel_owner.status = "active" THEN owner ELSE NULL END AS owner,
|
|
706
|
+
CASE WHEN rel_owner.status = "active" THEN rel_owner ELSE NULL END AS rel_owner
|
|
681
707
|
""" % {"branch_filter": branch_filter}
|
|
682
708
|
self.add_to_query(query)
|
|
683
709
|
self.return_labels.extend(["owner", "rel_owner"])
|
|
@@ -708,6 +734,7 @@ class NodeListGetAttributeQuery(Query):
|
|
|
708
734
|
def _extract_attribute_data(self, result: QueryResult) -> AttributeFromDB:
|
|
709
735
|
attr = result.get_node("a")
|
|
710
736
|
attr_value = result.get_node("av")
|
|
737
|
+
is_from_profile = result.get_as_type(label="is_from_profile", return_type=bool)
|
|
711
738
|
|
|
712
739
|
data = AttributeFromDB(
|
|
713
740
|
name=attr.get("name"),
|
|
@@ -719,6 +746,7 @@ class NodeListGetAttributeQuery(Query):
|
|
|
719
746
|
updated_at=result.get_rel("r2").get("from"),
|
|
720
747
|
value=attr_value.get("value"),
|
|
721
748
|
is_default=attr_value.get("is_default"),
|
|
749
|
+
is_from_profile=is_from_profile,
|
|
722
750
|
content=attr_value._properties,
|
|
723
751
|
branch=self.branch.name,
|
|
724
752
|
flag_properties={
|
|
@@ -942,6 +970,7 @@ class NodeListGetInfoQuery(Query):
|
|
|
942
970
|
at=self.at, branch_agnostic=self.branch_agnostic
|
|
943
971
|
)
|
|
944
972
|
self.params.update(branch_params)
|
|
973
|
+
self.params["ids"] = self.ids
|
|
945
974
|
|
|
946
975
|
query = """
|
|
947
976
|
MATCH p = (root:Root)<-[:IS_PART_OF]-(n:Node)
|
|
@@ -955,16 +984,11 @@ class NodeListGetInfoQuery(Query):
|
|
|
955
984
|
}
|
|
956
985
|
WITH n1 as n, r1 as rb
|
|
957
986
|
WHERE rb.status = "active"
|
|
958
|
-
OPTIONAL MATCH profile_path = (n)-[:IS_RELATED]->(profile_r:Relationship)<-[:IS_RELATED]-(profile:Node)-[:IS_PART_OF]->(:Root)
|
|
959
|
-
WHERE profile_r.name = "node__profile"
|
|
960
|
-
AND profile.namespace = "Profile"
|
|
961
|
-
AND all(r in relationships(profile_path) WHERE %(branch_filter)s and r.status = "active")
|
|
962
987
|
""" % {"branch_filter": branch_filter}
|
|
963
988
|
|
|
964
989
|
self.add_to_query(query)
|
|
965
|
-
self.params["ids"] = self.ids
|
|
966
990
|
|
|
967
|
-
self.return_labels = ["
|
|
991
|
+
self.return_labels = ["n", "rb"]
|
|
968
992
|
|
|
969
993
|
async def get_nodes(self, db: InfrahubDatabase, duplicate: bool = False) -> AsyncIterator[NodeToProcess]:
|
|
970
994
|
"""Return all the node objects as NodeToProcess."""
|
|
@@ -978,24 +1002,11 @@ class NodeListGetInfoQuery(Query):
|
|
|
978
1002
|
schema=schema,
|
|
979
1003
|
node_id=result.get_node("n").element_id,
|
|
980
1004
|
node_uuid=result.get_node("n").get("uuid"),
|
|
981
|
-
profile_uuids=[str(puuid) for puuid in result.get("profile_uuids")],
|
|
982
1005
|
updated_at=result.get_rel("rb").get("from"),
|
|
983
1006
|
branch=node_branch,
|
|
984
1007
|
labels=list(result.get_node("n").labels),
|
|
985
1008
|
)
|
|
986
1009
|
|
|
987
|
-
def get_profile_ids_by_node_id(self) -> dict[str, list[str]]:
|
|
988
|
-
profile_id_map: dict[str, list[str]] = {}
|
|
989
|
-
for result in self.results:
|
|
990
|
-
node_id = result.get_node("n").get("uuid")
|
|
991
|
-
profile_ids = result.get("profile_uuids")
|
|
992
|
-
if not node_id or not profile_ids:
|
|
993
|
-
continue
|
|
994
|
-
if node_id not in profile_id_map:
|
|
995
|
-
profile_id_map[node_id] = []
|
|
996
|
-
profile_id_map[node_id].extend(profile_ids)
|
|
997
|
-
return profile_id_map
|
|
998
|
-
|
|
999
1010
|
|
|
1000
1011
|
class FieldAttributeRequirementType(Enum):
|
|
1001
1012
|
FILTER = "filter"
|
|
@@ -1012,7 +1023,7 @@ class FieldAttributeRequirement:
|
|
|
1012
1023
|
types: list[FieldAttributeRequirementType] = dataclass_field(default_factory=list)
|
|
1013
1024
|
|
|
1014
1025
|
@property
|
|
1015
|
-
def
|
|
1026
|
+
def is_attribute_value(self) -> bool:
|
|
1016
1027
|
return bool(self.field and self.field.is_attribute and self.field_attr_name in ("value", "values", "isnull"))
|
|
1017
1028
|
|
|
1018
1029
|
@property
|
|
@@ -1023,26 +1034,10 @@ class FieldAttributeRequirement:
|
|
|
1023
1034
|
def is_order(self) -> bool:
|
|
1024
1035
|
return FieldAttributeRequirementType.ORDER in self.types
|
|
1025
1036
|
|
|
1026
|
-
@property
|
|
1027
|
-
def is_default_query_variable(self) -> str:
|
|
1028
|
-
return f"attr{self.index}_is_default"
|
|
1029
|
-
|
|
1030
1037
|
@property
|
|
1031
1038
|
def node_value_query_variable(self) -> str:
|
|
1032
1039
|
return f"attr{self.index}_node_value"
|
|
1033
1040
|
|
|
1034
|
-
@property
|
|
1035
|
-
def profile_value_query_variable(self) -> str:
|
|
1036
|
-
return f"attr{self.index}_profile_value"
|
|
1037
|
-
|
|
1038
|
-
@property
|
|
1039
|
-
def profile_final_value_query_variable(self) -> str:
|
|
1040
|
-
return f"attr{self.index}_final_profile_value"
|
|
1041
|
-
|
|
1042
|
-
@property
|
|
1043
|
-
def final_value_query_variable(self) -> str:
|
|
1044
|
-
return f"attr{self.index}_final_value"
|
|
1045
|
-
|
|
1046
1041
|
@property
|
|
1047
1042
|
def comparison_operator(self) -> str:
|
|
1048
1043
|
if self.field_attr_name == "isnull":
|
|
@@ -1178,7 +1173,6 @@ class NodeGetListQuery(Query):
|
|
|
1178
1173
|
self.params["node_ids"] = self.filters["ids"]
|
|
1179
1174
|
|
|
1180
1175
|
field_attribute_requirements = self._get_field_requirements(disable_order=disable_order)
|
|
1181
|
-
use_profiles = any(far for far in field_attribute_requirements if far.supports_profile)
|
|
1182
1176
|
await self._add_node_filter_attributes(
|
|
1183
1177
|
db=db, field_attribute_requirements=field_attribute_requirements, branch_filter=branch_filter
|
|
1184
1178
|
)
|
|
@@ -1190,21 +1184,11 @@ class NodeGetListQuery(Query):
|
|
|
1190
1184
|
for far in field_attribute_requirements:
|
|
1191
1185
|
if not far.is_order:
|
|
1192
1186
|
continue
|
|
1193
|
-
if far.supports_profile:
|
|
1194
|
-
self.order_by.append(far.final_value_query_variable)
|
|
1195
|
-
continue
|
|
1196
1187
|
self.order_by.append(far.node_value_query_variable)
|
|
1197
1188
|
|
|
1198
1189
|
# Always order by uuid to guarantee pagination, see https://github.com/opsmill/infrahub/pull/4704.
|
|
1199
1190
|
self.order_by.append("n.uuid")
|
|
1200
1191
|
|
|
1201
|
-
if use_profiles:
|
|
1202
|
-
await self._add_profiles_per_node_query(db=db, branch_filter=branch_filter)
|
|
1203
|
-
await self._add_profile_attributes(
|
|
1204
|
-
db=db, field_attribute_requirements=field_attribute_requirements, branch_filter=branch_filter
|
|
1205
|
-
)
|
|
1206
|
-
await self._add_profile_rollups(field_attribute_requirements=field_attribute_requirements)
|
|
1207
|
-
|
|
1208
1192
|
self._add_final_filter(field_attribute_requirements=field_attribute_requirements)
|
|
1209
1193
|
|
|
1210
1194
|
async def _add_node_filter_attributes(
|
|
@@ -1222,8 +1206,6 @@ class NodeGetListQuery(Query):
|
|
|
1222
1206
|
|
|
1223
1207
|
for far in field_attribute_requirements:
|
|
1224
1208
|
extra_tail_properties = {far.node_value_query_variable: "value"}
|
|
1225
|
-
if far.supports_profile:
|
|
1226
|
-
extra_tail_properties[far.is_default_query_variable] = "is_default"
|
|
1227
1209
|
subquery, subquery_params, subquery_result_name = await build_subquery_filter(
|
|
1228
1210
|
db=db,
|
|
1229
1211
|
field=far.field,
|
|
@@ -1234,7 +1216,6 @@ class NodeGetListQuery(Query):
|
|
|
1234
1216
|
branch=self.branch,
|
|
1235
1217
|
subquery_idx=far.index,
|
|
1236
1218
|
partial_match=self.partial_match,
|
|
1237
|
-
support_profiles=far.supports_profile,
|
|
1238
1219
|
extra_tail_properties=extra_tail_properties,
|
|
1239
1220
|
)
|
|
1240
1221
|
for query_var in extra_tail_properties:
|
|
@@ -1274,9 +1255,6 @@ class NodeGetListQuery(Query):
|
|
|
1274
1255
|
for far in field_attribute_requirements:
|
|
1275
1256
|
if far.field is None:
|
|
1276
1257
|
continue
|
|
1277
|
-
extra_tail_properties = {}
|
|
1278
|
-
if far.supports_profile:
|
|
1279
|
-
extra_tail_properties[far.is_default_query_variable] = "is_default"
|
|
1280
1258
|
|
|
1281
1259
|
subquery, subquery_params, _ = await build_subquery_order(
|
|
1282
1260
|
db=db,
|
|
@@ -1287,11 +1265,7 @@ class NodeGetListQuery(Query):
|
|
|
1287
1265
|
branch=self.branch,
|
|
1288
1266
|
subquery_idx=far.index,
|
|
1289
1267
|
result_prefix=far.node_value_query_variable,
|
|
1290
|
-
support_profiles=far.supports_profile,
|
|
1291
|
-
extra_tail_properties=extra_tail_properties,
|
|
1292
1268
|
)
|
|
1293
|
-
for query_var in extra_tail_properties:
|
|
1294
|
-
self._track_variable(query_var)
|
|
1295
1269
|
self._track_variable(far.node_value_query_variable)
|
|
1296
1270
|
with_str = ", ".join(self._get_tracked_variables())
|
|
1297
1271
|
|
|
@@ -1305,122 +1279,11 @@ class NodeGetListQuery(Query):
|
|
|
1305
1279
|
self.add_to_query(sort_query)
|
|
1306
1280
|
self.params.update(sort_params)
|
|
1307
1281
|
|
|
1308
|
-
async def _add_profiles_per_node_query(self, db: InfrahubDatabase, branch_filter: str) -> None:
|
|
1309
|
-
with_str = ", ".join(self._get_tracked_variables())
|
|
1310
|
-
froms_str = db.render_list_comprehension(items="relationships(profile_path)", item_name="from")
|
|
1311
|
-
profiles_per_node_query = (
|
|
1312
|
-
"""
|
|
1313
|
-
CALL (n) {
|
|
1314
|
-
OPTIONAL MATCH profile_path = (n)-[:IS_RELATED]->(profile_r:Relationship)<-[:IS_RELATED]-(maybe_profile_n:Node)-[:IS_PART_OF]->(:Root)
|
|
1315
|
-
WHERE profile_r.name = "node__profile"
|
|
1316
|
-
AND all(r in relationships(profile_path) WHERE %(branch_filter)s)
|
|
1317
|
-
WITH
|
|
1318
|
-
maybe_profile_n,
|
|
1319
|
-
profile_path,
|
|
1320
|
-
reduce(br_lvl = 0, r in relationships(profile_path) | br_lvl + r.branch_level) AS branch_level,
|
|
1321
|
-
%(froms_str)s AS froms,
|
|
1322
|
-
all(r in relationships(profile_path) WHERE r.status = "active") AS is_active
|
|
1323
|
-
RETURN maybe_profile_n, is_active, branch_level, froms
|
|
1324
|
-
}
|
|
1325
|
-
WITH %(with_str)s, maybe_profile_n, branch_level, froms, is_active
|
|
1326
|
-
ORDER BY n.uuid, maybe_profile_n.uuid, branch_level DESC, froms[-1] DESC, froms[-2] DESC, froms[-3] DESC
|
|
1327
|
-
WITH %(with_str)s, maybe_profile_n, collect(is_active) as ordered_is_actives
|
|
1328
|
-
WITH %(with_str)s, CASE
|
|
1329
|
-
WHEN ordered_is_actives[0] = True THEN maybe_profile_n ELSE NULL
|
|
1330
|
-
END AS profile_n
|
|
1331
|
-
CALL (profile_n) {
|
|
1332
|
-
OPTIONAL MATCH profile_priority_path = (profile_n)-[pr1:HAS_ATTRIBUTE]->(a:Attribute)-[pr2:HAS_VALUE]->(av:AttributeValue)
|
|
1333
|
-
WHERE a.name = "profile_priority"
|
|
1334
|
-
AND all(r in relationships(profile_priority_path) WHERE %(branch_filter)s and r.status = "active")
|
|
1335
|
-
RETURN av.value as profile_priority
|
|
1336
|
-
ORDER BY pr1.branch_level + pr2.branch_level DESC, pr2.from DESC, pr1.from DESC
|
|
1337
|
-
LIMIT 1
|
|
1338
|
-
}
|
|
1339
|
-
WITH %(with_str)s, profile_n, profile_priority
|
|
1340
|
-
"""
|
|
1341
|
-
) % {"branch_filter": branch_filter, "with_str": with_str, "froms_str": froms_str}
|
|
1342
|
-
self.add_to_query(profiles_per_node_query)
|
|
1343
|
-
self._track_variable("profile_n")
|
|
1344
|
-
self._track_variable("profile_priority")
|
|
1345
|
-
|
|
1346
|
-
async def _add_profile_attributes(
|
|
1347
|
-
self, db: InfrahubDatabase, field_attribute_requirements: list[FieldAttributeRequirement], branch_filter: str
|
|
1348
|
-
) -> None:
|
|
1349
|
-
attributes_queries: list[str] = []
|
|
1350
|
-
attributes_params: dict[str, Any] = {}
|
|
1351
|
-
profile_attributes = [far for far in field_attribute_requirements if far.supports_profile]
|
|
1352
|
-
|
|
1353
|
-
for profile_attr in profile_attributes:
|
|
1354
|
-
if not profile_attr.field:
|
|
1355
|
-
continue
|
|
1356
|
-
subquery, subquery_params, _ = await build_subquery_order(
|
|
1357
|
-
db=db,
|
|
1358
|
-
field=profile_attr.field,
|
|
1359
|
-
node_alias="profile_n",
|
|
1360
|
-
name=profile_attr.field_name,
|
|
1361
|
-
order_by=profile_attr.field_attr_name,
|
|
1362
|
-
branch_filter=branch_filter,
|
|
1363
|
-
branch=self.branch,
|
|
1364
|
-
subquery_idx=profile_attr.index,
|
|
1365
|
-
result_prefix=profile_attr.profile_value_query_variable,
|
|
1366
|
-
support_profiles=False,
|
|
1367
|
-
)
|
|
1368
|
-
attributes_params.update(subquery_params)
|
|
1369
|
-
self._track_variable(profile_attr.profile_value_query_variable)
|
|
1370
|
-
with_str = ", ".join(self._get_tracked_variables())
|
|
1371
|
-
|
|
1372
|
-
attributes_queries.append("CALL (profile_n) {")
|
|
1373
|
-
attributes_queries.append(subquery)
|
|
1374
|
-
attributes_queries.append("}")
|
|
1375
|
-
attributes_queries.append(f"WITH {with_str}")
|
|
1376
|
-
|
|
1377
|
-
self.add_to_query(attributes_queries)
|
|
1378
|
-
self.params.update(attributes_params)
|
|
1379
|
-
|
|
1380
|
-
async def _add_profile_rollups(self, field_attribute_requirements: list[FieldAttributeRequirement]) -> None:
|
|
1381
|
-
profile_attributes = [far for far in field_attribute_requirements if far.supports_profile]
|
|
1382
|
-
profile_value_collects = []
|
|
1383
|
-
for profile_attr in profile_attributes:
|
|
1384
|
-
self._untrack_variable(profile_attr.profile_value_query_variable)
|
|
1385
|
-
profile_value_collects.append(
|
|
1386
|
-
f"""head(
|
|
1387
|
-
reduce(
|
|
1388
|
-
non_null_values = [], v in collect({profile_attr.profile_value_query_variable}) |
|
|
1389
|
-
CASE WHEN v IS NOT NULL AND v <> "NULL" THEN non_null_values + [v] ELSE non_null_values END
|
|
1390
|
-
)
|
|
1391
|
-
) as {profile_attr.profile_final_value_query_variable}"""
|
|
1392
|
-
)
|
|
1393
|
-
self._untrack_variable("profile_n")
|
|
1394
|
-
self._untrack_variable("profile_priority")
|
|
1395
|
-
profile_rollup_with_str = ", ".join(self._get_tracked_variables() + profile_value_collects)
|
|
1396
|
-
profile_rollup_query = f"""
|
|
1397
|
-
ORDER BY n.uuid, profile_priority ASC, profile_n.uuid ASC
|
|
1398
|
-
WITH {profile_rollup_with_str}
|
|
1399
|
-
"""
|
|
1400
|
-
self.add_to_query(profile_rollup_query)
|
|
1401
|
-
for profile_attr in profile_attributes:
|
|
1402
|
-
self._track_variable(profile_attr.profile_final_value_query_variable)
|
|
1403
|
-
|
|
1404
|
-
final_value_with = []
|
|
1405
|
-
for profile_attr in profile_attributes:
|
|
1406
|
-
final_value_with.append(f"""
|
|
1407
|
-
CASE
|
|
1408
|
-
WHEN {profile_attr.is_default_query_variable} AND {profile_attr.profile_final_value_query_variable} IS NOT NULL
|
|
1409
|
-
THEN {profile_attr.profile_final_value_query_variable}
|
|
1410
|
-
ELSE {profile_attr.node_value_query_variable}
|
|
1411
|
-
END AS {profile_attr.final_value_query_variable}
|
|
1412
|
-
""")
|
|
1413
|
-
self._untrack_variable(profile_attr.is_default_query_variable)
|
|
1414
|
-
self._untrack_variable(profile_attr.profile_final_value_query_variable)
|
|
1415
|
-
self._untrack_variable(profile_attr.node_value_query_variable)
|
|
1416
|
-
final_value_with_str = ", ".join(self._get_tracked_variables() + final_value_with)
|
|
1417
|
-
self.add_to_query(f"WITH {final_value_with_str}")
|
|
1418
|
-
|
|
1419
1282
|
def _add_final_filter(self, field_attribute_requirements: list[FieldAttributeRequirement]) -> None:
|
|
1420
1283
|
where_parts = []
|
|
1421
1284
|
where_str = ""
|
|
1422
1285
|
for far in field_attribute_requirements:
|
|
1423
|
-
if not far.is_filter or not far.
|
|
1286
|
+
if not far.is_filter or not far.is_attribute_value:
|
|
1424
1287
|
continue
|
|
1425
1288
|
var_name = f"final_attr_value{far.index}"
|
|
1426
1289
|
self.params[var_name] = far.field_attr_comparison_value
|
|
@@ -1429,11 +1292,11 @@ class NodeGetListQuery(Query):
|
|
|
1429
1292
|
# If the any filter is an array/list
|
|
1430
1293
|
var_array = f"{var_name}_array"
|
|
1431
1294
|
where_parts.append(
|
|
1432
|
-
f"any({var_array} IN ${var_name} WHERE toLower(toString({far.
|
|
1295
|
+
f"any({var_array} IN ${var_name} WHERE toLower(toString({far.node_value_query_variable})) CONTAINS toLower({var_array}))"
|
|
1433
1296
|
)
|
|
1434
1297
|
else:
|
|
1435
1298
|
where_parts.append(
|
|
1436
|
-
f"toLower(toString({far.
|
|
1299
|
+
f"toLower(toString({far.node_value_query_variable})) CONTAINS toLower(toString(${var_name}))"
|
|
1437
1300
|
)
|
|
1438
1301
|
continue
|
|
1439
1302
|
if far.field and isinstance(far.field, AttributeSchema) and far.field.kind == "List":
|
|
@@ -1442,10 +1305,10 @@ class NodeGetListQuery(Query):
|
|
|
1442
1305
|
else:
|
|
1443
1306
|
self.params[var_name] = build_regex_attrs(values=[far.field_attr_comparison_value])
|
|
1444
1307
|
|
|
1445
|
-
where_parts.append(f"toString({far.
|
|
1308
|
+
where_parts.append(f"toString({far.node_value_query_variable}) =~ ${var_name}")
|
|
1446
1309
|
continue
|
|
1447
1310
|
|
|
1448
|
-
where_parts.append(f"{far.
|
|
1311
|
+
where_parts.append(f"{far.node_value_query_variable} {far.comparison_operator} ${var_name}")
|
|
1449
1312
|
if where_parts:
|
|
1450
1313
|
where_str = "WHERE " + " AND ".join(where_parts)
|
|
1451
1314
|
self.add_to_query(where_str)
|
|
@@ -350,17 +350,20 @@ class RelationshipUpdatePropertyQuery(RelationshipQuery):
|
|
|
350
350
|
|
|
351
351
|
def __init__(
|
|
352
352
|
self,
|
|
353
|
-
|
|
354
|
-
|
|
353
|
+
rel_node_id: str,
|
|
354
|
+
flag_properties_to_update: dict[str, bool],
|
|
355
|
+
node_properties_to_update: dict[str, str],
|
|
355
356
|
**kwargs,
|
|
356
357
|
):
|
|
357
|
-
self.
|
|
358
|
-
|
|
359
|
-
|
|
358
|
+
self.rel_node_id = rel_node_id
|
|
359
|
+
if not flag_properties_to_update and not node_properties_to_update:
|
|
360
|
+
raise ValueError("Either flag_properties_to_update or node_properties_to_update must be set")
|
|
361
|
+
self.flag_properties_to_update = flag_properties_to_update
|
|
362
|
+
self.node_properties_to_update = node_properties_to_update
|
|
360
363
|
super().__init__(**kwargs)
|
|
361
364
|
|
|
362
365
|
async def query_init(self, db: InfrahubDatabase, **kwargs) -> None: # noqa: ARG002
|
|
363
|
-
self.params["rel_node_id"] = self.
|
|
366
|
+
self.params["rel_node_id"] = self.rel_node_id
|
|
364
367
|
self.params["branch"] = self.branch.name
|
|
365
368
|
self.params["branch_level"] = self.branch.hierarchy_level
|
|
366
369
|
self.params["at"] = self.at.to_string()
|
|
@@ -370,36 +373,51 @@ class RelationshipUpdatePropertyQuery(RelationshipQuery):
|
|
|
370
373
|
"""
|
|
371
374
|
self.add_to_query(query)
|
|
372
375
|
|
|
373
|
-
self.query_add_all_flag_property_merge()
|
|
374
376
|
self.query_add_all_node_property_merge()
|
|
377
|
+
self.query_add_all_flag_property_merge()
|
|
375
378
|
|
|
376
|
-
self.query_add_all_flag_property_create()
|
|
377
379
|
self.query_add_all_node_property_create()
|
|
380
|
+
self.query_add_all_flag_property_create()
|
|
378
381
|
|
|
379
382
|
def query_add_all_flag_property_merge(self) -> None:
|
|
380
|
-
for prop_name in self.
|
|
381
|
-
|
|
382
|
-
self.query_add_flag_property_merge(name=prop_name)
|
|
383
|
+
for prop_name, prop_value in self.flag_properties_to_update.items():
|
|
384
|
+
self.query_add_flag_property_merge(name=prop_name, value=prop_value)
|
|
383
385
|
|
|
384
|
-
def query_add_flag_property_merge(self, name: str) -> None:
|
|
386
|
+
def query_add_flag_property_merge(self, name: str, value: bool) -> None:
|
|
385
387
|
self.add_to_query("MERGE (prop_%s:Boolean { value: $prop_%s })" % (name, name))
|
|
386
|
-
self.params[f"prop_{name}"] =
|
|
388
|
+
self.params[f"prop_{name}"] = value
|
|
387
389
|
self.return_labels.append(f"prop_{name}")
|
|
388
390
|
|
|
389
391
|
def query_add_all_node_property_merge(self) -> None:
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
self.query_add_node_property_merge(name=prop_name)
|
|
392
|
+
branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at)
|
|
393
|
+
self.params.update(branch_params)
|
|
393
394
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
395
|
+
for prop_name, prop_value in self.node_properties_to_update.items():
|
|
396
|
+
self.params[f"prop_{prop_name}"] = prop_value
|
|
397
|
+
if self.branch.is_default or self.branch.is_global:
|
|
398
|
+
node_query = """
|
|
399
|
+
MATCH (prop_%(prop_name)s:Node {uuid: $prop_%(prop_name)s })-[r_%(prop_name)s:IS_PART_OF]->(:Root)
|
|
400
|
+
WHERE r_%(prop_name)s.branch IN $branch0
|
|
401
|
+
AND r_%(prop_name)s.status = "active"
|
|
402
|
+
AND r_%(prop_name)s.from <= $at AND (r_%(prop_name)s.to IS NULL OR r_%(prop_name)s.to > $at)
|
|
403
|
+
WITH *
|
|
404
|
+
LIMIT 1
|
|
405
|
+
""" % {"prop_name": prop_name}
|
|
406
|
+
else:
|
|
407
|
+
node_query = """
|
|
408
|
+
MATCH (prop_%(prop_name)s:Node {uuid: $prop_%(prop_name)s })-[r_%(prop_name)s:IS_PART_OF]->(:Root)
|
|
409
|
+
WHERE all(r in [r_%(prop_name)s] WHERE %(branch_filter)s)
|
|
410
|
+
ORDER BY r_%(prop_name)s.branch_level DESC, r_%(prop_name)s.from DESC, r_%(prop_name)s.status ASC
|
|
411
|
+
LIMIT 1
|
|
412
|
+
WITH *
|
|
413
|
+
WHERE r_%(prop_name)s.status = "active"
|
|
414
|
+
""" % {"branch_filter": branch_filter, "prop_name": prop_name}
|
|
415
|
+
self.add_to_query(node_query)
|
|
416
|
+
self.return_labels.append(f"prop_{prop_name}")
|
|
398
417
|
|
|
399
418
|
def query_add_all_flag_property_create(self) -> None:
|
|
400
|
-
for prop_name in self.
|
|
401
|
-
|
|
402
|
-
self.query_add_flag_property_create(name=prop_name)
|
|
419
|
+
for prop_name in self.flag_properties_to_update:
|
|
420
|
+
self.query_add_flag_property_create(name=prop_name)
|
|
403
421
|
|
|
404
422
|
def query_add_flag_property_create(self, name: str) -> None:
|
|
405
423
|
query = """
|
|
@@ -411,9 +429,8 @@ class RelationshipUpdatePropertyQuery(RelationshipQuery):
|
|
|
411
429
|
self.add_to_query(query)
|
|
412
430
|
|
|
413
431
|
def query_add_all_node_property_create(self) -> None:
|
|
414
|
-
for prop_name in self.
|
|
415
|
-
|
|
416
|
-
self.query_add_node_property_create(name=prop_name)
|
|
432
|
+
for prop_name in self.node_properties_to_update:
|
|
433
|
+
self.query_add_node_property_create(name=prop_name)
|
|
417
434
|
|
|
418
435
|
def query_add_node_property_create(self, name: str) -> None:
|
|
419
436
|
query = """
|
infrahub/core/query/subquery.py
CHANGED
|
@@ -25,12 +25,8 @@ async def build_subquery_filter(
|
|
|
25
25
|
partial_match: bool = False,
|
|
26
26
|
optional_match: bool = False,
|
|
27
27
|
result_prefix: str = "filter",
|
|
28
|
-
support_profiles: bool = False,
|
|
29
28
|
extra_tail_properties: dict[str, str] | None = None,
|
|
30
29
|
) -> tuple[str, dict[str, Any], str]:
|
|
31
|
-
support_profiles = (
|
|
32
|
-
support_profiles and field and field.is_attribute and filter_name in ("value", "values", "isnull")
|
|
33
|
-
)
|
|
34
30
|
params = {}
|
|
35
31
|
prefix = f"{result_prefix}{subquery_idx}"
|
|
36
32
|
|
|
@@ -52,7 +48,6 @@ async def build_subquery_filter(
|
|
|
52
48
|
param_prefix=prefix,
|
|
53
49
|
db=db,
|
|
54
50
|
partial_match=partial_match,
|
|
55
|
-
support_profiles=support_profiles,
|
|
56
51
|
)
|
|
57
52
|
params.update(field_params)
|
|
58
53
|
|
|
@@ -109,10 +104,8 @@ async def build_subquery_order(
|
|
|
109
104
|
branch: Branch = None,
|
|
110
105
|
subquery_idx: int = 1,
|
|
111
106
|
result_prefix: str | None = None,
|
|
112
|
-
support_profiles: bool = False,
|
|
113
107
|
extra_tail_properties: dict[str, str] | None = None,
|
|
114
108
|
) -> tuple[str, dict[str, Any], str]:
|
|
115
|
-
support_profiles = support_profiles and field and field.is_attribute and order_by in ("value", "values")
|
|
116
109
|
params = {}
|
|
117
110
|
prefix = result_prefix or f"order{subquery_idx}"
|
|
118
111
|
|
|
@@ -124,7 +117,6 @@ async def build_subquery_order(
|
|
|
124
117
|
filter_value=None,
|
|
125
118
|
branch=branch,
|
|
126
119
|
param_prefix=prefix,
|
|
127
|
-
support_profiles=support_profiles,
|
|
128
120
|
)
|
|
129
121
|
params.update(field_params)
|
|
130
122
|
|