infrahub-server 1.6.3__py3-none-any.whl → 1.7.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- infrahub/actions/tasks.py +4 -2
- infrahub/api/exceptions.py +2 -2
- infrahub/api/schema.py +3 -1
- infrahub/artifacts/tasks.py +1 -0
- infrahub/auth.py +2 -2
- infrahub/cli/db.py +54 -28
- infrahub/computed_attribute/gather.py +3 -4
- infrahub/computed_attribute/tasks.py +23 -6
- infrahub/config.py +8 -0
- infrahub/constants/enums.py +12 -0
- infrahub/core/account.py +4 -4
- infrahub/core/attribute.py +106 -108
- infrahub/core/branch/models.py +44 -71
- infrahub/core/branch/tasks.py +5 -3
- infrahub/core/changelog/diff.py +1 -20
- infrahub/core/changelog/models.py +0 -7
- infrahub/core/constants/__init__.py +17 -0
- infrahub/core/constants/database.py +0 -1
- infrahub/core/constants/schema.py +0 -1
- infrahub/core/convert_object_type/repository_conversion.py +3 -4
- infrahub/core/diff/branch_differ.py +1 -1
- infrahub/core/diff/conflict_transferer.py +1 -1
- infrahub/core/diff/data_check_synchronizer.py +4 -3
- infrahub/core/diff/enricher/cardinality_one.py +2 -2
- infrahub/core/diff/enricher/hierarchy.py +1 -1
- infrahub/core/diff/enricher/labels.py +1 -1
- infrahub/core/diff/merger/merger.py +28 -2
- infrahub/core/diff/merger/serializer.py +3 -10
- infrahub/core/diff/model/diff.py +1 -1
- infrahub/core/diff/query/merge.py +376 -135
- infrahub/core/diff/repository/repository.py +3 -1
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/graph/constraints.py +3 -3
- infrahub/core/graph/schema.py +2 -12
- infrahub/core/ipam/reconciler.py +8 -6
- infrahub/core/ipam/utilization.py +8 -15
- infrahub/core/manager.py +133 -152
- infrahub/core/merge.py +1 -1
- infrahub/core/metadata/__init__.py +0 -0
- infrahub/core/metadata/interface.py +37 -0
- infrahub/core/metadata/model.py +31 -0
- infrahub/core/metadata/query/__init__.py +0 -0
- infrahub/core/metadata/query/node_metadata.py +301 -0
- infrahub/core/migrations/graph/__init__.py +4 -0
- infrahub/core/migrations/graph/m012_convert_account_generic.py +12 -12
- infrahub/core/migrations/graph/m013_convert_git_password_credential.py +7 -12
- infrahub/core/migrations/graph/m017_add_core_profile.py +5 -2
- infrahub/core/migrations/graph/m018_uniqueness_nulls.py +2 -1
- infrahub/core/migrations/graph/m019_restore_rels_to_time.py +0 -10
- infrahub/core/migrations/graph/m020_duplicate_edges.py +0 -8
- infrahub/core/migrations/graph/m025_uniqueness_nulls.py +2 -1
- infrahub/core/migrations/graph/m026_0000_prefix_fix.py +2 -1
- infrahub/core/migrations/graph/m029_duplicates_cleanup.py +0 -1
- infrahub/core/migrations/graph/m031_check_number_attributes.py +2 -2
- infrahub/core/migrations/graph/m038_redo_0000_prefix_fix.py +2 -1
- infrahub/core/migrations/graph/m041_deleted_dup_edges.py +1 -1
- infrahub/core/migrations/graph/m049_remove_is_visible_relationship.py +53 -0
- infrahub/core/migrations/graph/m050_backfill_vertex_metadata.py +168 -0
- infrahub/core/migrations/query/__init__.py +2 -2
- infrahub/core/migrations/query/attribute_add.py +17 -6
- infrahub/core/migrations/query/attribute_remove.py +19 -5
- infrahub/core/migrations/query/attribute_rename.py +21 -5
- infrahub/core/migrations/query/node_duplicate.py +19 -4
- infrahub/core/migrations/query/schema_attribute_update.py +1 -1
- infrahub/core/migrations/schema/attribute_kind_update.py +21 -2
- infrahub/core/migrations/schema/attribute_name_update.py +1 -1
- infrahub/core/migrations/schema/attribute_supports_profile.py +5 -3
- infrahub/core/migrations/schema/models.py +3 -0
- infrahub/core/migrations/schema/node_attribute_add.py +5 -2
- infrahub/core/migrations/schema/node_attribute_remove.py +1 -1
- infrahub/core/migrations/schema/node_kind_update.py +1 -1
- infrahub/core/migrations/schema/node_remove.py +24 -2
- infrahub/core/migrations/schema/tasks.py +4 -1
- infrahub/core/migrations/shared.py +13 -6
- infrahub/core/models.py +6 -6
- infrahub/core/node/__init__.py +157 -58
- infrahub/core/node/base.py +9 -5
- infrahub/core/node/create.py +7 -3
- infrahub/core/node/delete_validator.py +1 -1
- infrahub/core/node/standard.py +100 -14
- infrahub/core/order.py +30 -0
- infrahub/core/property.py +0 -1
- infrahub/core/protocols.py +1 -0
- infrahub/core/protocols_base.py +10 -2
- infrahub/core/query/__init__.py +5 -3
- infrahub/core/query/attribute.py +164 -49
- infrahub/core/query/branch.py +58 -70
- infrahub/core/query/delete.py +1 -1
- infrahub/core/query/diff.py +7 -7
- infrahub/core/query/ipam.py +104 -43
- infrahub/core/query/node.py +1072 -281
- infrahub/core/query/relationship.py +531 -325
- infrahub/core/query/resource_manager.py +107 -18
- infrahub/core/query/standard_node.py +25 -5
- infrahub/core/query/utils.py +2 -4
- infrahub/core/relationship/constraints/count.py +1 -1
- infrahub/core/relationship/constraints/peer_kind.py +1 -1
- infrahub/core/relationship/constraints/peer_parent.py +1 -1
- infrahub/core/relationship/constraints/peer_relatives.py +1 -1
- infrahub/core/relationship/constraints/profiles_kind.py +1 -1
- infrahub/core/relationship/constraints/profiles_removal.py +168 -0
- infrahub/core/relationship/model.py +293 -139
- infrahub/core/schema/attribute_schema.py +2 -2
- infrahub/core/schema/basenode_schema.py +3 -0
- infrahub/core/schema/definitions/core/__init__.py +8 -2
- infrahub/core/schema/definitions/core/account.py +10 -10
- infrahub/core/schema/definitions/core/artifact.py +14 -8
- infrahub/core/schema/definitions/core/check.py +10 -4
- infrahub/core/schema/definitions/core/generator.py +26 -6
- infrahub/core/schema/definitions/core/graphql_query.py +1 -1
- infrahub/core/schema/definitions/core/group.py +9 -2
- infrahub/core/schema/definitions/core/ipam.py +80 -10
- infrahub/core/schema/definitions/core/menu.py +41 -7
- infrahub/core/schema/definitions/core/permission.py +16 -2
- infrahub/core/schema/definitions/core/profile.py +16 -2
- infrahub/core/schema/definitions/core/propose_change.py +24 -4
- infrahub/core/schema/definitions/core/propose_change_comment.py +23 -11
- infrahub/core/schema/definitions/core/propose_change_validator.py +50 -21
- infrahub/core/schema/definitions/core/repository.py +10 -0
- infrahub/core/schema/definitions/core/resource_pool.py +8 -1
- infrahub/core/schema/definitions/core/template.py +19 -2
- infrahub/core/schema/definitions/core/transform.py +11 -5
- infrahub/core/schema/definitions/core/webhook.py +27 -9
- infrahub/core/schema/manager.py +63 -43
- infrahub/core/schema/relationship_schema.py +6 -2
- infrahub/core/schema/schema_branch.py +48 -10
- infrahub/core/task/task.py +4 -2
- infrahub/core/utils.py +3 -25
- infrahub/core/validators/aggregated_checker.py +1 -1
- infrahub/core/validators/attribute/choices.py +1 -1
- infrahub/core/validators/attribute/enum.py +1 -1
- infrahub/core/validators/attribute/kind.py +1 -1
- infrahub/core/validators/attribute/length.py +1 -1
- infrahub/core/validators/attribute/min_max.py +1 -1
- infrahub/core/validators/attribute/number_pool.py +1 -1
- infrahub/core/validators/attribute/optional.py +1 -1
- infrahub/core/validators/attribute/regex.py +1 -1
- infrahub/core/validators/determiner.py +3 -3
- infrahub/core/validators/node/attribute.py +1 -1
- infrahub/core/validators/node/relationship.py +1 -1
- infrahub/core/validators/relationship/peer.py +1 -1
- infrahub/database/__init__.py +4 -4
- infrahub/dependencies/builder/constraint/grouped/node_runner.py +2 -0
- infrahub/dependencies/builder/constraint/relationship_manager/profiles_removal.py +8 -0
- infrahub/dependencies/registry.py +2 -0
- infrahub/display_labels/tasks.py +12 -3
- infrahub/git/integrator.py +18 -18
- infrahub/git/tasks.py +1 -1
- infrahub/git/utils.py +1 -1
- infrahub/graphql/constants.py +3 -0
- infrahub/graphql/context.py +1 -1
- infrahub/graphql/field_extractor.py +1 -1
- infrahub/graphql/initialization.py +11 -0
- infrahub/graphql/loaders/account.py +134 -0
- infrahub/graphql/loaders/node.py +5 -12
- infrahub/graphql/loaders/peers.py +5 -7
- infrahub/graphql/manager.py +175 -21
- infrahub/graphql/metadata.py +91 -0
- infrahub/graphql/mutations/account.py +6 -6
- infrahub/graphql/mutations/attribute.py +0 -2
- infrahub/graphql/mutations/branch.py +9 -5
- infrahub/graphql/mutations/computed_attribute.py +1 -1
- infrahub/graphql/mutations/display_label.py +1 -1
- infrahub/graphql/mutations/hfid.py +1 -1
- infrahub/graphql/mutations/ipam.py +4 -6
- infrahub/graphql/mutations/main.py +9 -4
- infrahub/graphql/mutations/profile.py +16 -22
- infrahub/graphql/mutations/proposed_change.py +4 -4
- infrahub/graphql/mutations/relationship.py +40 -10
- infrahub/graphql/mutations/repository.py +14 -12
- infrahub/graphql/mutations/schema.py +2 -2
- infrahub/graphql/order.py +14 -0
- infrahub/graphql/queries/branch.py +62 -6
- infrahub/graphql/queries/resource_manager.py +25 -24
- infrahub/graphql/resolvers/account_metadata.py +84 -0
- infrahub/graphql/resolvers/ipam.py +6 -8
- infrahub/graphql/resolvers/many_relationship.py +77 -35
- infrahub/graphql/resolvers/resolver.py +59 -14
- infrahub/graphql/resolvers/single_relationship.py +87 -23
- infrahub/graphql/subscription/graphql_query.py +2 -0
- infrahub/graphql/types/__init__.py +0 -1
- infrahub/graphql/types/attribute.py +10 -5
- infrahub/graphql/types/branch.py +40 -53
- infrahub/graphql/types/enums.py +3 -0
- infrahub/graphql/types/metadata.py +28 -0
- infrahub/graphql/types/node.py +22 -2
- infrahub/graphql/types/relationship.py +10 -2
- infrahub/graphql/types/standard_node.py +12 -7
- infrahub/hfid/tasks.py +12 -3
- infrahub/lock.py +7 -0
- infrahub/menu/repository.py +1 -1
- infrahub/patch/queries/base.py +1 -1
- infrahub/pools/number.py +1 -8
- infrahub/profiles/gather.py +56 -0
- infrahub/profiles/mandatory_fields_checker.py +116 -0
- infrahub/profiles/models.py +66 -0
- infrahub/profiles/node_applier.py +154 -13
- infrahub/profiles/queries/get_profile_data.py +143 -31
- infrahub/profiles/tasks.py +79 -27
- infrahub/profiles/triggers.py +22 -0
- infrahub/proposed_change/action_checker.py +1 -1
- infrahub/proposed_change/tasks.py +4 -1
- infrahub/services/__init__.py +1 -1
- infrahub/services/adapters/cache/nats.py +1 -1
- infrahub/services/adapters/cache/redis.py +7 -0
- infrahub/tasks/artifact.py +1 -0
- infrahub/transformations/tasks.py +2 -2
- infrahub/trigger/catalogue.py +2 -0
- infrahub/trigger/models.py +1 -0
- infrahub/trigger/setup.py +3 -3
- infrahub/trigger/tasks.py +3 -0
- infrahub/validators/tasks.py +1 -0
- infrahub/webhook/gather.py +1 -1
- infrahub/webhook/models.py +1 -1
- infrahub/webhook/tasks.py +23 -7
- infrahub/workers/dependencies.py +9 -3
- infrahub/workers/infrahub_async.py +13 -4
- infrahub/workflows/catalogue.py +19 -0
- infrahub_sdk/analyzer.py +2 -2
- infrahub_sdk/branch.py +12 -39
- infrahub_sdk/checks.py +4 -4
- infrahub_sdk/client.py +36 -0
- infrahub_sdk/ctl/cli_commands.py +2 -1
- infrahub_sdk/ctl/graphql.py +15 -4
- infrahub_sdk/ctl/utils.py +2 -2
- infrahub_sdk/enums.py +6 -0
- infrahub_sdk/graphql/renderers.py +21 -0
- infrahub_sdk/graphql/utils.py +85 -0
- infrahub_sdk/node/attribute.py +12 -2
- infrahub_sdk/node/constants.py +12 -0
- infrahub_sdk/node/metadata.py +69 -0
- infrahub_sdk/node/node.py +65 -14
- infrahub_sdk/node/property.py +3 -0
- infrahub_sdk/node/related_node.py +37 -5
- infrahub_sdk/node/relationship.py +18 -1
- infrahub_sdk/operation.py +2 -2
- infrahub_sdk/schema/repository.py +1 -2
- infrahub_sdk/transforms.py +2 -2
- infrahub_sdk/types.py +18 -2
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0.dist-info}/METADATA +17 -16
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0.dist-info}/RECORD +249 -228
- infrahub_testcontainers/container.py +3 -3
- infrahub_testcontainers/docker-compose-cluster.test.yml +7 -7
- infrahub_testcontainers/docker-compose.test.yml +13 -5
- infrahub_testcontainers/models.py +3 -3
- infrahub_testcontainers/performance_test.py +1 -1
- infrahub/graphql/models.py +0 -6
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0.dist-info}/WHEEL +0 -0
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0.dist-info}/entry_points.txt +0 -0
- {infrahub_server-1.6.3.dist-info → infrahub_server-1.7.0.dist-info}/licenses/LICENSE.txt +0 -0
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import TYPE_CHECKING, Any
|
|
4
4
|
|
|
5
|
+
from infrahub.core.constants import GLOBAL_BRANCH_NAME
|
|
5
6
|
from infrahub.core.query import Query, QueryType
|
|
6
7
|
|
|
7
8
|
if TYPE_CHECKING:
|
|
@@ -14,6 +15,7 @@ class DiffMergeQuery(Query):
|
|
|
14
15
|
name = "diff_merge"
|
|
15
16
|
type = QueryType.WRITE
|
|
16
17
|
insert_return = False
|
|
18
|
+
raise_error_if_empty = False
|
|
17
19
|
|
|
18
20
|
def __init__(
|
|
19
21
|
self,
|
|
@@ -56,7 +58,7 @@ END AS node_db_id
|
|
|
56
58
|
// ------------------------------
|
|
57
59
|
CALL (node_diff_map, node_db_id) {
|
|
58
60
|
MATCH (n:Node {uuid: node_diff_map.uuid})-[n_is_part_of:IS_PART_OF]->(:Root)
|
|
59
|
-
WHERE node_db_id IS NULL OR
|
|
61
|
+
WHERE node_db_id IS NULL OR elementId(n) = node_db_id
|
|
60
62
|
AND n_is_part_of.branch IN [$source_branch, $target_branch]
|
|
61
63
|
RETURN n
|
|
62
64
|
ORDER BY n_is_part_of.branch_level DESC, n_is_part_of.from DESC, n_is_part_of.status ASC
|
|
@@ -79,65 +81,80 @@ CALL (n, node_diff_map, is_node_kind_migration) {
|
|
|
79
81
|
AND is_node_kind_migration = FALSE
|
|
80
82
|
MATCH (root:Root)
|
|
81
83
|
// ------------------------------
|
|
84
|
+
// get from_user_id from source branch edge for creating new edges
|
|
85
|
+
// and to_user_id from source branch deleted edge for closing edges
|
|
86
|
+
// ------------------------------
|
|
87
|
+
CALL (n, root, node_rel_status) {
|
|
88
|
+
OPTIONAL MATCH (n)-[source_is_part_of:IS_PART_OF {branch: $source_branch, status: node_rel_status}]->(root)
|
|
89
|
+
WHERE source_is_part_of.from <= $at AND source_is_part_of.to IS NULL
|
|
90
|
+
RETURN source_is_part_of.from_user_id AS source_from_user_id
|
|
91
|
+
ORDER BY source_is_part_of.from DESC
|
|
92
|
+
LIMIT 1
|
|
93
|
+
}
|
|
94
|
+
// ------------------------------
|
|
82
95
|
// set IS_PART_OF.to, optionally, target branch
|
|
83
96
|
// ------------------------------
|
|
84
|
-
WITH root, n, node_rel_status
|
|
85
|
-
CALL (root, n, node_rel_status) {
|
|
97
|
+
WITH root, n, node_rel_status, source_from_user_id
|
|
98
|
+
CALL (root, n, node_rel_status, source_from_user_id) {
|
|
86
99
|
OPTIONAL MATCH (root)<-[target_r_root:IS_PART_OF {branch: $target_branch, status: "active"}]-(n)
|
|
87
100
|
WHERE node_rel_status = "deleted"
|
|
88
101
|
AND target_r_root.from <= $at AND target_r_root.to IS NULL
|
|
89
|
-
SET target_r_root.to = $at
|
|
102
|
+
SET target_r_root.to = $at, target_r_root.to_user_id = source_from_user_id
|
|
90
103
|
}
|
|
91
104
|
// ------------------------------
|
|
92
105
|
// create new IS_PART_OF relationship on target_branch
|
|
106
|
+
// also set created_at/created_by on Node vertex when adding
|
|
93
107
|
// ------------------------------
|
|
94
|
-
WITH root, n, node_rel_status
|
|
95
|
-
CALL (root, n, node_rel_status) {
|
|
108
|
+
WITH root, n, node_rel_status, source_from_user_id
|
|
109
|
+
CALL (root, n, node_rel_status, source_from_user_id) {
|
|
96
110
|
OPTIONAL MATCH (root)<-[r_root:IS_PART_OF {branch: $target_branch}]-(n)
|
|
97
111
|
WHERE r_root.status = node_rel_status
|
|
98
112
|
AND r_root.from <= $at
|
|
99
113
|
AND (r_root.to >= $at OR r_root.to IS NULL)
|
|
100
|
-
WITH r_root
|
|
114
|
+
WITH n, r_root, source_from_user_id
|
|
101
115
|
WHERE r_root IS NULL
|
|
102
116
|
CREATE (root)
|
|
103
|
-
<-[:IS_PART_OF { branch: $target_branch, branch_level: $branch_level, from: $at, status: node_rel_status }]
|
|
117
|
+
<-[:IS_PART_OF { branch: $target_branch, branch_level: $branch_level, from: $at, status: node_rel_status, from_user_id: source_from_user_id }]
|
|
104
118
|
-(n)
|
|
119
|
+
WITH node_rel_status
|
|
120
|
+
WHERE node_rel_status = "active"
|
|
121
|
+
SET n.created_at = $at, n.created_by = source_from_user_id
|
|
105
122
|
}
|
|
106
123
|
// ------------------------------
|
|
107
124
|
// shortcut to delete all attributes and relationships for this node if the node is deleted
|
|
108
125
|
// ------------------------------
|
|
109
|
-
CALL (n, node_rel_status) {
|
|
110
|
-
WITH n, node_rel_status
|
|
126
|
+
CALL (n, node_rel_status, source_from_user_id) {
|
|
127
|
+
WITH n, node_rel_status, source_from_user_id
|
|
111
128
|
WHERE node_rel_status = "deleted"
|
|
112
129
|
CALL (n) {
|
|
113
|
-
OPTIONAL MATCH (n)-[rel1:IS_RELATED]-(:Relationship)-[rel2]-(p)
|
|
130
|
+
OPTIONAL MATCH (n)-[rel1:IS_RELATED]-(attr_rel:Relationship)-[rel2]-(p)
|
|
114
131
|
WHERE (p.uuid IS NULL OR n.uuid <> p.uuid)
|
|
115
132
|
AND rel1.branch = $target_branch
|
|
116
133
|
AND rel2.branch = $target_branch
|
|
117
134
|
AND rel1.status = "active"
|
|
118
135
|
AND rel2.status = "active"
|
|
119
|
-
RETURN rel1, rel2
|
|
136
|
+
RETURN rel1, rel2, attr_rel
|
|
120
137
|
UNION
|
|
121
|
-
OPTIONAL MATCH (n)-[rel1:HAS_ATTRIBUTE]->(:Attribute)-[rel2]->()
|
|
138
|
+
OPTIONAL MATCH (n)-[rel1:HAS_ATTRIBUTE]->(attr_rel:Attribute)-[rel2]->()
|
|
122
139
|
WHERE type(rel2) <> "HAS_ATTRIBUTE"
|
|
123
140
|
AND rel1.branch = $target_branch
|
|
124
141
|
AND rel2.branch = $target_branch
|
|
125
142
|
AND rel1.status = "active"
|
|
126
143
|
AND rel2.status = "active"
|
|
127
|
-
RETURN rel1, rel2
|
|
144
|
+
RETURN rel1, rel2, attr_rel
|
|
128
145
|
}
|
|
129
|
-
WITH rel1, rel2
|
|
146
|
+
WITH rel1, rel2, attr_rel, source_from_user_id
|
|
130
147
|
WHERE rel1.to IS NULL
|
|
131
148
|
AND rel2.to IS NULL
|
|
132
149
|
AND rel1.from <= $at
|
|
133
150
|
AND rel2.from <= $at
|
|
134
|
-
SET rel1.to = $at
|
|
135
|
-
SET rel2.to = $at
|
|
151
|
+
SET rel1.to = $at, rel1.to_user_id = source_from_user_id
|
|
152
|
+
SET rel2.to = $at, rel2.to_user_id = source_from_user_id
|
|
136
153
|
// ------------------------------
|
|
137
154
|
// and delete HAS_OWNER and HAS_SOURCE edges to this node if the node is deleted
|
|
138
155
|
// ------------------------------
|
|
139
|
-
WITH n
|
|
140
|
-
CALL (n) {
|
|
156
|
+
WITH n, source_from_user_id
|
|
157
|
+
CALL (n, source_from_user_id) {
|
|
141
158
|
CALL (n) {
|
|
142
159
|
MATCH (n)<-[rel:HAS_OWNER]-()
|
|
143
160
|
WHERE rel.branch = $target_branch
|
|
@@ -153,7 +170,7 @@ CALL (n, node_diff_map, is_node_kind_migration) {
|
|
|
153
170
|
AND rel.to IS NULL
|
|
154
171
|
RETURN rel
|
|
155
172
|
}
|
|
156
|
-
SET rel.to = $at
|
|
173
|
+
SET rel.to = $at, rel.to_user_id = source_from_user_id
|
|
157
174
|
}
|
|
158
175
|
}
|
|
159
176
|
}
|
|
@@ -180,32 +197,48 @@ CALL (n, node_diff_map, is_node_kind_migration) {
|
|
|
180
197
|
ORDER BY has_attr.from DESC
|
|
181
198
|
LIMIT 1
|
|
182
199
|
}
|
|
183
|
-
|
|
200
|
+
// ------------------------------
|
|
201
|
+
// get from_user_id from source branch edge
|
|
202
|
+
// ------------------------------
|
|
203
|
+
CALL (n, a, attr_rel_status) {
|
|
204
|
+
OPTIONAL MATCH (n)-[source_has_attr:HAS_ATTRIBUTE {branch: $source_branch, status: attr_rel_status}]->(a)
|
|
205
|
+
WHERE source_has_attr.from <= $at AND source_has_attr.to IS NULL
|
|
206
|
+
RETURN source_has_attr.from_user_id AS attr_source_from_user_id
|
|
207
|
+
ORDER BY source_has_attr.from DESC
|
|
208
|
+
LIMIT 1
|
|
209
|
+
}
|
|
210
|
+
WITH n, attr_rel_status, a, attr_source_from_user_id
|
|
184
211
|
// ------------------------------
|
|
185
212
|
// set HAS_ATTRIBUTE.to on target branch if necessary
|
|
213
|
+
// set Attribute.created_at/by vertex when adding
|
|
186
214
|
// ------------------------------
|
|
187
|
-
CALL (n, attr_rel_status, a) {
|
|
215
|
+
CALL (n, attr_rel_status, a, attr_source_from_user_id) {
|
|
188
216
|
OPTIONAL MATCH (n)
|
|
189
217
|
-[target_r_attr:HAS_ATTRIBUTE {branch: $target_branch, status: "active"}]
|
|
190
218
|
->(a)
|
|
191
219
|
WHERE attr_rel_status = "deleted"
|
|
192
220
|
AND target_r_attr.from <= $at AND target_r_attr.to IS NULL
|
|
193
|
-
SET target_r_attr.to = $at
|
|
221
|
+
SET target_r_attr.to = $at, target_r_attr.to_user_id = attr_source_from_user_id
|
|
194
222
|
}
|
|
195
|
-
WITH n, attr_rel_status, a
|
|
223
|
+
WITH n, attr_rel_status, a, attr_source_from_user_id
|
|
196
224
|
// ------------------------------
|
|
197
225
|
// conditionally create new HAS_ATTRIBUTE relationship on target_branch, if necessary
|
|
226
|
+
// also set created_at/created_by on Attribute vertex when adding
|
|
198
227
|
// ------------------------------
|
|
199
|
-
CALL (n, attr_rel_status, a) {
|
|
200
|
-
WITH n, attr_rel_status, a
|
|
228
|
+
CALL (n, attr_rel_status, a, attr_source_from_user_id) {
|
|
229
|
+
WITH n, attr_rel_status, a, attr_source_from_user_id
|
|
201
230
|
WHERE a IS NOT NULL
|
|
202
231
|
OPTIONAL MATCH (n)-[r_attr:HAS_ATTRIBUTE {branch: $target_branch}]->(a)
|
|
203
232
|
WHERE r_attr.status = attr_rel_status
|
|
204
233
|
AND r_attr.from <= $at
|
|
205
234
|
AND (r_attr.to >= $at OR r_attr.to IS NULL)
|
|
206
|
-
WITH r_attr
|
|
235
|
+
WITH a, r_attr, attr_source_from_user_id
|
|
207
236
|
WHERE r_attr IS NULL
|
|
208
|
-
CREATE (n)-[:HAS_ATTRIBUTE { branch: $target_branch, branch_level: $branch_level, from: $at, status: attr_rel_status }]->(a)
|
|
237
|
+
CREATE (n)-[:HAS_ATTRIBUTE { branch: $target_branch, branch_level: $branch_level, from: $at, status: attr_rel_status, from_user_id: attr_source_from_user_id }]->(a)
|
|
238
|
+
WITH attr_rel_status
|
|
239
|
+
WHERE attr_rel_status = "active"
|
|
240
|
+
AND a.created_at IS NULL
|
|
241
|
+
SET a.created_at = $at, a.created_by = attr_source_from_user_id
|
|
209
242
|
}
|
|
210
243
|
RETURN 1 AS done
|
|
211
244
|
}
|
|
@@ -236,7 +269,7 @@ CALL (n, node_diff_map, is_node_kind_migration) {
|
|
|
236
269
|
// ------------------------------
|
|
237
270
|
CALL (rel_peer_id, rel_peer_db_id) {
|
|
238
271
|
MATCH (rel_peer:Node {uuid: rel_peer_id})-[target_is_part_of:IS_PART_OF]->(:Root)
|
|
239
|
-
WHERE (rel_peer_db_id IS NULL OR
|
|
272
|
+
WHERE (rel_peer_db_id IS NULL OR elementId(rel_peer) = rel_peer_db_id)
|
|
240
273
|
AND target_is_part_of.branch IN [$source_branch, $target_branch]
|
|
241
274
|
RETURN rel_peer
|
|
242
275
|
ORDER BY target_is_part_of.branch_level DESC, target_is_part_of.from DESC, target_is_part_of.status ASC
|
|
@@ -244,7 +277,7 @@ CALL (n, node_diff_map, is_node_kind_migration) {
|
|
|
244
277
|
}
|
|
245
278
|
WITH rel_name, related_rel_status, rel_peer
|
|
246
279
|
// ------------------------------
|
|
247
|
-
// determine the directions of each IS_RELATED
|
|
280
|
+
// determine the directions of each IS_RELATED and get from_user_id from source branch
|
|
248
281
|
// ------------------------------
|
|
249
282
|
CALL (n, rel_name, rel_peer, related_rel_status) {
|
|
250
283
|
MATCH (n)
|
|
@@ -268,26 +301,32 @@ CALL (n, node_diff_map, is_node_kind_migration) {
|
|
|
268
301
|
ELSE "l"
|
|
269
302
|
END AS r2_dir,
|
|
270
303
|
source_r_rel_1.hierarchy AS r1_hierarchy,
|
|
271
|
-
source_r_rel_2.hierarchy AS r2_hierarchy
|
|
304
|
+
source_r_rel_2.hierarchy AS r2_hierarchy,
|
|
305
|
+
source_r_rel_1.from_user_id AS r1_from_user_id,
|
|
306
|
+
source_r_rel_2.from_user_id AS r2_from_user_id
|
|
272
307
|
}
|
|
273
|
-
WITH n, r, r1_dir, r2_dir, r1_hierarchy, r2_hierarchy, rel_name, rel_peer, related_rel_status
|
|
274
|
-
|
|
308
|
+
WITH n, r, r1_dir, r2_dir, r1_hierarchy, r2_hierarchy, r1_from_user_id, r2_from_user_id, rel_name, rel_peer, related_rel_status
|
|
309
|
+
// ------------------------------
|
|
310
|
+
// set IS_RELATED.to on target branch when deleting relationship
|
|
311
|
+
// ------------------------------
|
|
312
|
+
CALL (n, r, rel_name, rel_peer, related_rel_status, r1_from_user_id, r2_from_user_id) {
|
|
275
313
|
OPTIONAL MATCH (n)
|
|
276
314
|
-[target_r_rel_1:IS_RELATED {branch: $target_branch, status: "active"}]
|
|
277
|
-
-(:Relationship {name: rel_name})
|
|
315
|
+
-(r:Relationship {name: rel_name})
|
|
278
316
|
-[target_r_rel_2:IS_RELATED {branch: $target_branch, status: "active"}]
|
|
279
317
|
-(rel_peer)
|
|
280
318
|
WHERE related_rel_status = "deleted"
|
|
281
319
|
AND target_r_rel_1.from <= $at AND target_r_rel_1.to IS NULL
|
|
282
320
|
AND target_r_rel_2.from <= $at AND target_r_rel_2.to IS NULL
|
|
283
|
-
SET target_r_rel_1.to = $at
|
|
284
|
-
SET target_r_rel_2.to = $at
|
|
321
|
+
SET target_r_rel_1.to = $at, target_r_rel_1.to_user_id = r1_from_user_id
|
|
322
|
+
SET target_r_rel_2.to = $at, target_r_rel_2.to_user_id = r2_from_user_id
|
|
285
323
|
}
|
|
286
|
-
WITH n, r, r1_dir, r2_dir, r1_hierarchy, r2_hierarchy, rel_name, rel_peer, related_rel_status
|
|
324
|
+
WITH n, r, r1_dir, r2_dir, r1_hierarchy, r2_hierarchy, r1_from_user_id, r2_from_user_id, rel_name, rel_peer, related_rel_status
|
|
287
325
|
// ------------------------------
|
|
288
326
|
// conditionally create new IS_RELATED relationships on target_branch, if necessary
|
|
327
|
+
// also set created_at/created_by on Relationship vertex when adding
|
|
289
328
|
// ------------------------------
|
|
290
|
-
CALL (n, r, r1_dir, r2_dir, r1_hierarchy, r2_hierarchy, rel_name, rel_peer, related_rel_status) {
|
|
329
|
+
CALL (n, r, r1_dir, r2_dir, r1_hierarchy, r2_hierarchy, r1_from_user_id, r2_from_user_id, rel_name, rel_peer, related_rel_status) {
|
|
291
330
|
OPTIONAL MATCH (n)
|
|
292
331
|
-[r_rel_1:IS_RELATED {branch: $target_branch, status: related_rel_status}]
|
|
293
332
|
-(:Relationship {name: rel_name})
|
|
@@ -297,46 +336,50 @@ CALL (n, node_diff_map, is_node_kind_migration) {
|
|
|
297
336
|
AND (r_rel_1.to >= $at OR r_rel_1.to IS NULL)
|
|
298
337
|
AND r_rel_2.from <= $at
|
|
299
338
|
AND (r_rel_2.to >= $at OR r_rel_2.to IS NULL)
|
|
300
|
-
WITH rel_peer, r_rel_1, r_rel_2
|
|
339
|
+
WITH r, rel_peer, r_rel_1, r_rel_2, r1_from_user_id, r2_from_user_id
|
|
301
340
|
WHERE r_rel_1 IS NULL
|
|
302
341
|
AND r_rel_2 IS NULL
|
|
342
|
+
WITH n, r, r1_dir, r2_dir, r1_hierarchy, r2_hierarchy, related_rel_status, r1_from_user_id, r2_from_user_id
|
|
303
343
|
// ------------------------------
|
|
304
344
|
// create IS_RELATED relationships with directions maintained from source
|
|
305
345
|
// ------------------------------
|
|
306
|
-
CALL (n, r, r1_dir, r1_hierarchy, related_rel_status) {
|
|
307
|
-
WITH n, r, r1_dir, r1_hierarchy, related_rel_status
|
|
346
|
+
CALL (n, r, r1_dir, r1_hierarchy, related_rel_status, r1_from_user_id) {
|
|
347
|
+
WITH n, r, r1_dir, r1_hierarchy, related_rel_status, r1_from_user_id
|
|
308
348
|
WHERE r1_dir = "r"
|
|
309
349
|
CREATE (n)
|
|
310
|
-
-[:IS_RELATED {branch: $target_branch, branch_level: $branch_level, from: $at, status: related_rel_status, hierarchy: r1_hierarchy}]
|
|
350
|
+
-[:IS_RELATED {branch: $target_branch, branch_level: $branch_level, from: $at, status: related_rel_status, hierarchy: r1_hierarchy, from_user_id: r1_from_user_id}]
|
|
311
351
|
->(r)
|
|
312
352
|
}
|
|
313
|
-
CALL (n, r, r1_dir, r1_hierarchy, related_rel_status) {
|
|
314
|
-
WITH n, r, r1_dir, r1_hierarchy, related_rel_status
|
|
353
|
+
CALL (n, r, r1_dir, r1_hierarchy, related_rel_status, r1_from_user_id) {
|
|
354
|
+
WITH n, r, r1_dir, r1_hierarchy, related_rel_status, r1_from_user_id
|
|
315
355
|
WHERE r1_dir = "l"
|
|
316
356
|
CREATE (n)
|
|
317
|
-
<-[:IS_RELATED {branch: $target_branch, branch_level: $branch_level, from: $at, status: related_rel_status, hierarchy: r1_hierarchy}]
|
|
357
|
+
<-[:IS_RELATED {branch: $target_branch, branch_level: $branch_level, from: $at, status: related_rel_status, hierarchy: r1_hierarchy, from_user_id: r1_from_user_id}]
|
|
318
358
|
-(r)
|
|
319
359
|
}
|
|
320
|
-
CALL (r, rel_peer, r2_dir, r2_hierarchy, related_rel_status) {
|
|
321
|
-
WITH r, rel_peer, r2_dir, r2_hierarchy, related_rel_status
|
|
360
|
+
CALL (r, rel_peer, r2_dir, r2_hierarchy, related_rel_status, r2_from_user_id) {
|
|
361
|
+
WITH r, rel_peer, r2_dir, r2_hierarchy, related_rel_status, r2_from_user_id
|
|
322
362
|
WHERE r2_dir = "r"
|
|
323
363
|
CREATE (r)
|
|
324
|
-
-[:IS_RELATED {branch: $target_branch, branch_level: $branch_level, from: $at, status: related_rel_status, hierarchy: r2_hierarchy}]
|
|
364
|
+
-[:IS_RELATED {branch: $target_branch, branch_level: $branch_level, from: $at, status: related_rel_status, hierarchy: r2_hierarchy, from_user_id: r2_from_user_id}]
|
|
325
365
|
->(rel_peer)
|
|
326
366
|
}
|
|
327
|
-
CALL (r, rel_peer, r2_dir, r2_hierarchy, related_rel_status) {
|
|
328
|
-
WITH r, rel_peer, r2_dir, r2_hierarchy, related_rel_status
|
|
367
|
+
CALL (r, rel_peer, r2_dir, r2_hierarchy, related_rel_status, r2_from_user_id) {
|
|
368
|
+
WITH r, rel_peer, r2_dir, r2_hierarchy, related_rel_status, r2_from_user_id
|
|
329
369
|
WHERE r2_dir = "l"
|
|
330
370
|
CREATE (r)
|
|
331
|
-
<-[:IS_RELATED {branch: $target_branch, branch_level: $branch_level, from: $at, status: related_rel_status, hierarchy: r2_hierarchy}]
|
|
371
|
+
<-[:IS_RELATED {branch: $target_branch, branch_level: $branch_level, from: $at, status: related_rel_status, hierarchy: r2_hierarchy, from_user_id: r2_from_user_id}]
|
|
332
372
|
-(rel_peer)
|
|
333
373
|
}
|
|
374
|
+
// set Relationship vertex metadata when adding
|
|
375
|
+
WITH r
|
|
376
|
+
WHERE r.created_at IS NULL
|
|
377
|
+
SET r.created_at = $at, r.created_by = r1_from_user_id
|
|
334
378
|
}
|
|
335
379
|
}
|
|
336
380
|
}
|
|
337
381
|
}
|
|
338
|
-
|
|
339
|
-
""" % {"id_func": db.get_id_function_name()}
|
|
382
|
+
"""
|
|
340
383
|
self.add_to_query(query=query)
|
|
341
384
|
|
|
342
385
|
|
|
@@ -391,7 +434,7 @@ CALL (attr_rel_prop_diff, node_db_id, peer_db_id) {
|
|
|
391
434
|
-[has_attr:HAS_ATTRIBUTE]
|
|
392
435
|
->(attr:Attribute {name: attr_rel_prop_diff.attribute_name})
|
|
393
436
|
WHERE attr_rel_prop_diff.attribute_name IS NOT NULL
|
|
394
|
-
AND (node_db_id IS NULL OR
|
|
437
|
+
AND (node_db_id IS NULL OR elementId(n) = node_db_id)
|
|
395
438
|
AND has_attr.branch IN [$source_branch, $target_branch]
|
|
396
439
|
RETURN attr
|
|
397
440
|
ORDER BY has_attr.from DESC
|
|
@@ -404,8 +447,8 @@ CALL (attr_rel_prop_diff, node_db_id, peer_db_id) {
|
|
|
404
447
|
-[r2:IS_RELATED]
|
|
405
448
|
-(rel_peer:Node {uuid: attr_rel_prop_diff.peer_uuid})
|
|
406
449
|
WHERE attr_rel_prop_diff.relationship_id IS NOT NULL
|
|
407
|
-
AND (node_db_id IS NULL OR
|
|
408
|
-
AND (peer_db_id IS NULL OR
|
|
450
|
+
AND (node_db_id IS NULL OR elementId(n) = node_db_id)
|
|
451
|
+
AND (peer_db_id IS NULL OR elementId(rel_peer) = peer_db_id)
|
|
409
452
|
AND r1.branch IN [$source_branch, $target_branch]
|
|
410
453
|
AND r2.branch IN [$source_branch, $target_branch]
|
|
411
454
|
RETURN rel
|
|
@@ -425,14 +468,14 @@ CALL (attr_rel_prop_diff, node_db_id, peer_db_id) {
|
|
|
425
468
|
CALL (attr_rel, property_diff, peer_db_id) {
|
|
426
469
|
OPTIONAL MATCH (peer:Node {uuid: property_diff.value})
|
|
427
470
|
WHERE property_diff.property_type IN ["HAS_SOURCE", "HAS_OWNER"]
|
|
428
|
-
AND (peer_db_id IS NULL OR
|
|
471
|
+
AND (peer_db_id IS NULL OR elementId(peer) = peer_db_id)
|
|
429
472
|
// ------------------------------
|
|
430
|
-
// the serialized diff might not include the values for
|
|
473
|
+
// the serialized diff might not include the values for IS_PROTECTED in
|
|
431
474
|
// some cases, so we need to figure them out here
|
|
432
475
|
// ------------------------------
|
|
433
476
|
CALL (attr_rel, property_diff) {
|
|
434
477
|
OPTIONAL MATCH (attr_rel)-[r_vis_pro]->(bool:Boolean)
|
|
435
|
-
WHERE property_diff.property_type
|
|
478
|
+
WHERE property_diff.property_type = "IS_PROTECTED"
|
|
436
479
|
AND r_vis_pro.branch IN [$source_branch, $target_branch]
|
|
437
480
|
AND type(r_vis_pro) = property_diff.property_type
|
|
438
481
|
AND (property_diff.value IS NULL OR bool.value = property_diff.value)
|
|
@@ -464,15 +507,25 @@ CALL (attr_rel_prop_diff, node_db_id, peer_db_id) {
|
|
|
464
507
|
ELSE NULL
|
|
465
508
|
END as prop_rel_status
|
|
466
509
|
// ------------------------------
|
|
510
|
+
// get from_user_id from source branch property edge
|
|
511
|
+
// ------------------------------
|
|
512
|
+
CALL (attr_rel, prop_type) {
|
|
513
|
+
OPTIONAL MATCH (attr_rel)-[source_prop_edge {branch: $source_branch}]->()
|
|
514
|
+
WHERE type(source_prop_edge) = prop_type
|
|
515
|
+
AND source_prop_edge.from <= $at AND source_prop_edge.to IS NULL
|
|
516
|
+
RETURN source_prop_edge.from_user_id AS prop_source_from_user_id
|
|
517
|
+
ORDER BY source_prop_edge.from DESC
|
|
518
|
+
LIMIT 1
|
|
519
|
+
}
|
|
520
|
+
// ------------------------------
|
|
467
521
|
// set property edge.to, optionally, on target branch
|
|
468
522
|
// ------------------------------
|
|
469
|
-
CALL (attr_rel,
|
|
470
|
-
|
|
471
|
-
-[target_r_prop {branch: $target_branch}]
|
|
472
|
-
->()
|
|
523
|
+
CALL (attr_rel, prop_type, prop_node, prop_source_from_user_id) {
|
|
524
|
+
MATCH (attr_rel)-[target_r_prop {branch: $target_branch}]->(target_prop)
|
|
473
525
|
WHERE type(target_r_prop) = prop_type
|
|
526
|
+
AND target_prop <> prop_node
|
|
474
527
|
AND target_r_prop.from < $at AND target_r_prop.to IS NULL
|
|
475
|
-
SET target_r_prop.to = $at
|
|
528
|
+
SET target_r_prop.to = $at, target_r_prop.to_user_id = prop_source_from_user_id
|
|
476
529
|
}
|
|
477
530
|
// ------------------------------
|
|
478
531
|
// check for existing edge on target_branch
|
|
@@ -485,40 +538,36 @@ CALL (attr_rel_prop_diff, node_db_id, peer_db_id) {
|
|
|
485
538
|
AND (r_prop.to > $at OR r_prop.to IS NULL)
|
|
486
539
|
RETURN r_prop
|
|
487
540
|
}
|
|
488
|
-
WITH attr_rel,prop_rel_status, prop_type, prop_node, r_prop
|
|
541
|
+
WITH attr_rel, prop_rel_status, prop_type, prop_node, r_prop, prop_source_from_user_id
|
|
489
542
|
WHERE r_prop IS NULL
|
|
543
|
+
WITH attr_rel, prop_rel_status, prop_type, prop_node, prop_source_from_user_id
|
|
490
544
|
// ------------------------------
|
|
491
545
|
// create new edge to prop_node on target_branch, if necessary
|
|
492
546
|
// one subquery per possible edge type b/c edge type cannot be a variable
|
|
493
547
|
// ------------------------------
|
|
494
|
-
CALL (attr_rel, prop_rel_status, prop_type, prop_node) {
|
|
495
|
-
WITH attr_rel, prop_rel_status, prop_type, prop_node
|
|
548
|
+
CALL (attr_rel, prop_rel_status, prop_type, prop_node, prop_source_from_user_id) {
|
|
549
|
+
WITH attr_rel, prop_rel_status, prop_type, prop_node, prop_source_from_user_id
|
|
496
550
|
WHERE prop_type = "HAS_VALUE"
|
|
497
|
-
CREATE (attr_rel)-[:HAS_VALUE { branch: $target_branch, branch_level: $branch_level, from: $at, status: prop_rel_status }]->(prop_node)
|
|
551
|
+
CREATE (attr_rel)-[:HAS_VALUE { branch: $target_branch, branch_level: $branch_level, from: $at, status: prop_rel_status, from_user_id: prop_source_from_user_id }]->(prop_node)
|
|
498
552
|
}
|
|
499
|
-
CALL (attr_rel, prop_rel_status, prop_type, prop_node) {
|
|
500
|
-
WITH attr_rel, prop_rel_status, prop_type, prop_node
|
|
553
|
+
CALL (attr_rel, prop_rel_status, prop_type, prop_node, prop_source_from_user_id) {
|
|
554
|
+
WITH attr_rel, prop_rel_status, prop_type, prop_node, prop_source_from_user_id
|
|
501
555
|
WHERE prop_type = "HAS_SOURCE"
|
|
502
|
-
CREATE (attr_rel)-[:HAS_SOURCE { branch: $target_branch, branch_level: $branch_level, from: $at, status: prop_rel_status }]->(prop_node)
|
|
556
|
+
CREATE (attr_rel)-[:HAS_SOURCE { branch: $target_branch, branch_level: $branch_level, from: $at, status: prop_rel_status, from_user_id: prop_source_from_user_id }]->(prop_node)
|
|
503
557
|
}
|
|
504
|
-
CALL (attr_rel, prop_rel_status, prop_type, prop_node) {
|
|
505
|
-
WITH attr_rel, prop_rel_status, prop_type, prop_node
|
|
558
|
+
CALL (attr_rel, prop_rel_status, prop_type, prop_node, prop_source_from_user_id) {
|
|
559
|
+
WITH attr_rel, prop_rel_status, prop_type, prop_node, prop_source_from_user_id
|
|
506
560
|
WHERE prop_type = "HAS_OWNER"
|
|
507
|
-
CREATE (attr_rel)-[:HAS_OWNER { branch: $target_branch, branch_level: $branch_level, from: $at, status: prop_rel_status }]->(prop_node)
|
|
508
|
-
}
|
|
509
|
-
CALL (attr_rel, prop_rel_status, prop_type, prop_node) {
|
|
510
|
-
WITH attr_rel, prop_rel_status, prop_type, prop_node
|
|
511
|
-
WHERE prop_type = "IS_VISIBLE"
|
|
512
|
-
CREATE (attr_rel)-[:IS_VISIBLE { branch: $target_branch, branch_level: $branch_level, from: $at, status: prop_rel_status }]->(prop_node)
|
|
561
|
+
CREATE (attr_rel)-[:HAS_OWNER { branch: $target_branch, branch_level: $branch_level, from: $at, status: prop_rel_status, from_user_id: prop_source_from_user_id }]->(prop_node)
|
|
513
562
|
}
|
|
514
|
-
CALL (attr_rel, prop_rel_status, prop_type, prop_node) {
|
|
515
|
-
WITH attr_rel, prop_rel_status, prop_type, prop_node
|
|
563
|
+
CALL (attr_rel, prop_rel_status, prop_type, prop_node, prop_source_from_user_id) {
|
|
564
|
+
WITH attr_rel, prop_rel_status, prop_type, prop_node, prop_source_from_user_id
|
|
516
565
|
WHERE prop_type = "IS_PROTECTED"
|
|
517
|
-
CREATE (attr_rel)-[:IS_PROTECTED { branch: $target_branch, branch_level: $branch_level, from: $at, status: prop_rel_status }]->(prop_node)
|
|
566
|
+
CREATE (attr_rel)-[:IS_PROTECTED { branch: $target_branch, branch_level: $branch_level, from: $at, status: prop_rel_status, from_user_id: prop_source_from_user_id }]->(prop_node)
|
|
518
567
|
}
|
|
519
568
|
}
|
|
520
569
|
}
|
|
521
|
-
"""
|
|
570
|
+
"""
|
|
522
571
|
self.add_to_query(query=query)
|
|
523
572
|
|
|
524
573
|
|
|
@@ -582,47 +631,62 @@ CALL (n) {
|
|
|
582
631
|
// --------------
|
|
583
632
|
// ignore edges of this type that already have the correct status on the target branch
|
|
584
633
|
// --------------
|
|
585
|
-
WITH n, peer, edge_type, latest_source_edge, latest_target_edge
|
|
634
|
+
WITH n, peer, edge_type, latest_source_edge, latest_target_edge, latest_source_edge.from_user_id AS user_id
|
|
586
635
|
WHERE (latest_target_edge IS NULL AND latest_source_edge.status = "active")
|
|
587
636
|
OR latest_source_edge.status <> latest_target_edge.status
|
|
588
|
-
CALL (latest_source_edge, latest_target_edge) {
|
|
637
|
+
CALL (n, latest_source_edge, latest_target_edge, user_id) {
|
|
589
638
|
// --------------
|
|
590
639
|
// set the to time on active target branch edges that we are setting to deleted
|
|
640
|
+
// to_user_id comes from the source edge's from_user_id (user who deleted it on source branch)
|
|
591
641
|
// --------------
|
|
592
|
-
WITH latest_target_edge WHERE latest_target_edge IS NOT NULL
|
|
642
|
+
WITH n, latest_target_edge, user_id WHERE latest_target_edge IS NOT NULL
|
|
593
643
|
AND latest_source_edge.status = "deleted"
|
|
594
644
|
AND latest_target_edge.status = "active"
|
|
595
645
|
AND latest_target_edge.to IS NULL
|
|
596
|
-
SET latest_target_edge.to = $at
|
|
646
|
+
SET latest_target_edge.to = $at, latest_target_edge.to_user_id = user_id
|
|
597
647
|
}
|
|
598
648
|
// --------------
|
|
649
|
+
// get the earliest created time to handle migrated kind/inheritance Nodes
|
|
650
|
+
// --------------
|
|
651
|
+
CALL (n) {
|
|
652
|
+
MATCH (earliest_n:Node {uuid: n.uuid})
|
|
653
|
+
RETURN earliest_n.created_at AS node_created_at, earliest_n.created_by AS node_created_by
|
|
654
|
+
ORDER BY earliest_n.created_at ASC
|
|
655
|
+
LIMIT 1
|
|
656
|
+
}
|
|
657
|
+
WITH *, COALESCE(node_created_at, $at) AS node_created_at, COALESCE(node_created_by, user_id) AS node_created_by
|
|
658
|
+
// --------------
|
|
599
659
|
// create the outbound edges on the target branch, one subquery per possible type
|
|
660
|
+
// from_user_id is copied from source edge via properties(latest_source_edge)
|
|
600
661
|
// --------------
|
|
601
|
-
CALL (n, latest_source_edge, peer, edge_type) {
|
|
662
|
+
CALL (n, latest_source_edge, peer, edge_type, node_created_at, node_created_by) {
|
|
602
663
|
WITH edge_type WHERE edge_type = "IS_PART_OF"
|
|
603
664
|
CREATE (n)-[new_edge:IS_PART_OF]->(peer)
|
|
604
665
|
SET new_edge = properties(latest_source_edge)
|
|
605
|
-
SET new_edge.from = $at
|
|
606
|
-
|
|
607
|
-
|
|
666
|
+
SET new_edge.from = $at, new_edge.branch_level = $branch_level, new_edge.branch = $target_branch
|
|
667
|
+
WITH n
|
|
668
|
+
WHERE n.created_at IS NULL
|
|
669
|
+
SET n.created_at = node_created_at, n.created_by = node_created_by
|
|
608
670
|
}
|
|
609
|
-
CALL (n, latest_source_edge, peer, edge_type) {
|
|
610
|
-
WITH edge_type
|
|
671
|
+
CALL (n, latest_source_edge, peer, edge_type, user_id) {
|
|
672
|
+
WITH peer, user_id, edge_type
|
|
611
673
|
WHERE edge_type = "IS_RELATED"
|
|
612
674
|
CREATE (n)-[new_edge:IS_RELATED]->(peer)
|
|
613
675
|
SET new_edge = properties(latest_source_edge)
|
|
614
|
-
SET new_edge.from = $at
|
|
615
|
-
|
|
616
|
-
|
|
676
|
+
SET new_edge.from = $at, new_edge.branch_level = $branch_level, new_edge.branch = $target_branch
|
|
677
|
+
WITH peer
|
|
678
|
+
WHERE peer.created_at IS NULL
|
|
679
|
+
SET peer.created_at = $at, peer.created_by = user_id
|
|
617
680
|
}
|
|
618
|
-
CALL (n, latest_source_edge, peer, edge_type) {
|
|
619
|
-
WITH edge_type
|
|
681
|
+
CALL (n, latest_source_edge, peer, edge_type, user_id) {
|
|
682
|
+
WITH peer, user_id, edge_type
|
|
620
683
|
WHERE edge_type = "HAS_ATTRIBUTE"
|
|
621
684
|
CREATE (n)-[new_edge:HAS_ATTRIBUTE]->(peer)
|
|
622
685
|
SET new_edge = properties(latest_source_edge)
|
|
623
|
-
SET new_edge.from = $at
|
|
624
|
-
|
|
625
|
-
|
|
686
|
+
SET new_edge.from = $at, new_edge.branch_level = $branch_level, new_edge.branch = $target_branch
|
|
687
|
+
WITH peer
|
|
688
|
+
WHERE peer.created_at IS NULL
|
|
689
|
+
SET peer.created_at = $at, peer.created_by = user_id
|
|
626
690
|
}
|
|
627
691
|
// --------------
|
|
628
692
|
// do all of this again for inbound edges
|
|
@@ -652,54 +716,202 @@ CALL (n) {
|
|
|
652
716
|
// --------------
|
|
653
717
|
// ignore edges of this type that already have the correct status on the target branch
|
|
654
718
|
// --------------
|
|
655
|
-
WITH n, peer, edge_type, latest_source_edge, latest_target_edge
|
|
719
|
+
WITH n, peer, edge_type, latest_source_edge, latest_target_edge, latest_source_edge.from_user_id AS user_id
|
|
656
720
|
WHERE latest_target_edge IS NULL OR latest_source_edge.status <> latest_target_edge.status
|
|
657
|
-
CALL (latest_source_edge, latest_target_edge) {
|
|
721
|
+
CALL (latest_source_edge, latest_target_edge, user_id, peer) {
|
|
658
722
|
// --------------
|
|
659
723
|
// set the to time on active target branch edges that we are setting to deleted
|
|
724
|
+
// to_user_id comes from the source edge's from_user_id (user who deleted it on source branch)
|
|
660
725
|
// --------------
|
|
661
|
-
WITH latest_target_edge
|
|
726
|
+
WITH latest_target_edge, user_id, peer
|
|
662
727
|
WHERE latest_target_edge IS NOT NULL
|
|
663
728
|
AND latest_source_edge.status = "deleted"
|
|
664
729
|
AND latest_target_edge.status = "active"
|
|
665
730
|
AND latest_target_edge.to IS NULL
|
|
666
|
-
SET latest_target_edge.to = $at
|
|
731
|
+
SET latest_target_edge.to = $at, latest_target_edge.to_user_id = user_id
|
|
667
732
|
}
|
|
668
733
|
// --------------
|
|
669
|
-
// create the
|
|
734
|
+
// create the inbound edges on the target branch, one subquery per possible type
|
|
735
|
+
// from_user_id is copied from source edge via properties(latest_source_edge)
|
|
670
736
|
// --------------
|
|
671
|
-
CALL (n, latest_source_edge, peer, edge_type) {
|
|
672
|
-
WITH edge_type
|
|
737
|
+
CALL (n, latest_source_edge, peer, edge_type, user_id) {
|
|
738
|
+
WITH peer, user_id, edge_type
|
|
673
739
|
WHERE edge_type = "IS_RELATED"
|
|
674
740
|
CREATE (n)<-[new_edge:IS_RELATED]-(peer)
|
|
675
741
|
SET new_edge = properties(latest_source_edge)
|
|
676
|
-
SET new_edge.from = $at
|
|
677
|
-
|
|
678
|
-
|
|
742
|
+
SET new_edge.from = $at, new_edge.branch_level = $branch_level, new_edge.branch = $target_branch
|
|
743
|
+
WITH peer
|
|
744
|
+
WHERE peer.created_at IS NULL
|
|
745
|
+
SET peer.created_at = $at, peer.created_by = user_id
|
|
679
746
|
}
|
|
680
|
-
CALL (n, latest_source_edge, peer, edge_type) {
|
|
681
|
-
WITH edge_type
|
|
747
|
+
CALL (n, latest_source_edge, peer, edge_type, user_id) {
|
|
748
|
+
WITH peer, user_id, edge_type
|
|
682
749
|
WHERE edge_type = "HAS_OWNER"
|
|
683
750
|
CREATE (n)<-[new_edge:HAS_OWNER]-(peer)
|
|
684
751
|
SET new_edge = properties(latest_source_edge)
|
|
685
|
-
SET new_edge.from = $at
|
|
686
|
-
SET new_edge.branch_level = $branch_level
|
|
687
|
-
SET new_edge.branch = $target_branch
|
|
752
|
+
SET new_edge.from = $at, new_edge.branch_level = $branch_level, new_edge.branch = $target_branch
|
|
688
753
|
}
|
|
689
|
-
CALL (n, latest_source_edge, peer, edge_type) {
|
|
690
|
-
WITH edge_type
|
|
754
|
+
CALL (n, latest_source_edge, peer, edge_type, user_id) {
|
|
755
|
+
WITH peer, user_id, edge_type
|
|
691
756
|
WHERE edge_type = "HAS_SOURCE"
|
|
692
757
|
CREATE (n)<-[new_edge:HAS_SOURCE]-(peer)
|
|
693
758
|
SET new_edge = properties(latest_source_edge)
|
|
694
|
-
SET new_edge.from = $at
|
|
695
|
-
SET new_edge.branch_level = $branch_level
|
|
696
|
-
SET new_edge.branch = $target_branch
|
|
759
|
+
SET new_edge.from = $at, new_edge.branch_level = $branch_level, new_edge.branch = $target_branch
|
|
697
760
|
}
|
|
698
761
|
}
|
|
699
762
|
"""
|
|
700
763
|
self.add_to_query(query)
|
|
701
764
|
|
|
702
765
|
|
|
766
|
+
class DiffMergeMetadataQuery(Query):
|
|
767
|
+
"""Set metadata properties on Nodes, Attributes, and Relationships included in this merge
|
|
768
|
+
|
|
769
|
+
Each Node, Attribute, and Relationship should have its current updated_at/by (if it exists) saved in the
|
|
770
|
+
previous_updated_at/by properties to support a rollback.
|
|
771
|
+
For Attributes and Relationships (let's call them fields), set updated_by to the user_id of the latest change on
|
|
772
|
+
the field on the branch being merged
|
|
773
|
+
For Nodes, set the updated_by to the user_id of the latest change on any associated fields on this branch
|
|
774
|
+
For Nodes, Attributes, and Relationships, set updated_at to the merge time.
|
|
775
|
+
|
|
776
|
+
The logic in pseudocode
|
|
777
|
+
For each Node we care about:
|
|
778
|
+
a. For each Attribute/Relationship (let's call them "fields") linked to the Node
|
|
779
|
+
i. filter to only fields updated on the source branch
|
|
780
|
+
ii. previous_updated_at/by = updated_at/by
|
|
781
|
+
iii. identify the latest update and set updated_by = the associated user_id
|
|
782
|
+
iv. set updated_at = $at
|
|
783
|
+
b. set Node.updated_by to the user_id associated with the latest change of ALL the Node's fields
|
|
784
|
+
c. set Node.updated_at = $at
|
|
785
|
+
"""
|
|
786
|
+
|
|
787
|
+
name = "merge_metadata"
|
|
788
|
+
type = QueryType.WRITE
|
|
789
|
+
insert_return = False
|
|
790
|
+
|
|
791
|
+
def __init__(
|
|
792
|
+
self,
|
|
793
|
+
node_uuids: list[str],
|
|
794
|
+
at: Timestamp,
|
|
795
|
+
target_branch: Branch,
|
|
796
|
+
**kwargs: Any,
|
|
797
|
+
) -> None:
|
|
798
|
+
super().__init__(**kwargs)
|
|
799
|
+
self.node_uuids = node_uuids
|
|
800
|
+
self.at = at
|
|
801
|
+
self.target_branch = target_branch
|
|
802
|
+
|
|
803
|
+
async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
|
|
804
|
+
self.params = {
|
|
805
|
+
"node_uuids": self.node_uuids,
|
|
806
|
+
"at": self.at.to_string(),
|
|
807
|
+
"target_branch": self.target_branch.name,
|
|
808
|
+
"source_branch": self.branch.name,
|
|
809
|
+
"global_branch": GLOBAL_BRANCH_NAME,
|
|
810
|
+
"branched_from": self.branch.get_branched_from(),
|
|
811
|
+
}
|
|
812
|
+
# ruff: noqa: E501
|
|
813
|
+
query = """
|
|
814
|
+
// --------------------
|
|
815
|
+
// Match all affected nodes, accounting for nodes with migrated kind
|
|
816
|
+
// for each UUID, get the latest Node that was active on this branch
|
|
817
|
+
// --------------------
|
|
818
|
+
UNWIND $node_uuids AS node_uuid
|
|
819
|
+
CALL (node_uuid) {
|
|
820
|
+
MATCH (n:Node {uuid: node_uuid})-[e:IS_PART_OF]->(:Root)
|
|
821
|
+
WHERE e.branch IN [$target_branch, $global_branch]
|
|
822
|
+
AND (
|
|
823
|
+
(e.branch = $target_branch AND (e.from <= $branched_from OR e.from = $at))
|
|
824
|
+
OR (e.branch = $global_branch AND e.from <= $at)
|
|
825
|
+
)
|
|
826
|
+
RETURN n, e AS is_part_of_e
|
|
827
|
+
ORDER BY e.from DESC, e.status ASC
|
|
828
|
+
LIMIT 1
|
|
829
|
+
}
|
|
830
|
+
// --------------------
|
|
831
|
+
// Special handling for the new version of a migrated kind/inheritance Node
|
|
832
|
+
// set updated_at/by to the time/user that created the new version of the Node
|
|
833
|
+
// --------------------
|
|
834
|
+
CALL (n, is_part_of_e) {
|
|
835
|
+
WITH n, is_part_of_e
|
|
836
|
+
WHERE n.updated_at IS NULL
|
|
837
|
+
SET n.updated_at = is_part_of_e.from, n.updated_by = is_part_of_e.from_user_id
|
|
838
|
+
}
|
|
839
|
+
// --------------------
|
|
840
|
+
// Get all the Attributes and Relationships for this Node that were active on this branch at some point
|
|
841
|
+
// --------------------
|
|
842
|
+
MATCH (n)-[e:HAS_ATTRIBUTE|IS_RELATED]-(field:Attribute|Relationship)
|
|
843
|
+
WHERE e.branch IN [$source_branch, $target_branch, $global_branch]
|
|
844
|
+
AND (
|
|
845
|
+
(e.branch = $target_branch AND e.from <= $branched_from)
|
|
846
|
+
OR (e.branch IN [$source_branch, $global_branch] AND e.from <= $at)
|
|
847
|
+
)
|
|
848
|
+
|
|
849
|
+
// --------------------
|
|
850
|
+
// For each field, only include it if it has an update at $at on the target branch
|
|
851
|
+
// to prevent updating metadata for conflicts in which the base branch version was accepted
|
|
852
|
+
// --------------------
|
|
853
|
+
WITH DISTINCT n, field
|
|
854
|
+
WHERE exists((field)-[{branch: $target_branch, from: $at}]-())
|
|
855
|
+
OR exists((field)-[{branch: $target_branch, to: $at}]-())
|
|
856
|
+
|
|
857
|
+
// --------------------
|
|
858
|
+
// For each changed field, find the latest time and user_id that updated it on the source branch
|
|
859
|
+
// Check both from (creation) and to (deletion) timestamps
|
|
860
|
+
// Prefer non-system users (those not starting with "__")
|
|
861
|
+
// --------------------
|
|
862
|
+
CALL (field) {
|
|
863
|
+
// ignore HAS_ATTRIBUTE and IS_RELATED b/c these show when an Attribute/Relationship was created/deleted
|
|
864
|
+
// not when it was updated
|
|
865
|
+
MATCH ()-[edge:!HAS_ATTRIBUTE&!IS_RELATED {branch: $source_branch}]-(field)
|
|
866
|
+
WHERE edge.from <= $at
|
|
867
|
+
// Collect both from and to timestamps as potential "change times"
|
|
868
|
+
WITH edge,
|
|
869
|
+
edge.from AS from_time,
|
|
870
|
+
edge.from_user_id AS from_user,
|
|
871
|
+
edge.to AS to_time,
|
|
872
|
+
edge.to_user_id AS to_user
|
|
873
|
+
// Create rows for each type of change
|
|
874
|
+
UNWIND [
|
|
875
|
+
CASE WHEN from_time <= $at THEN {time: from_time, user_id: from_user} ELSE NULL END,
|
|
876
|
+
CASE WHEN to_time IS NOT NULL AND to_time <= $at THEN {time: to_time, user_id: to_user} ELSE NULL END
|
|
877
|
+
] AS change
|
|
878
|
+
WITH change WHERE change IS NOT NULL AND change.user_id IS NOT NULL
|
|
879
|
+
// Sort by time DESC, then prefer non-system users (is_system ASC puts false before true)
|
|
880
|
+
WITH change.user_id AS change_user_id, change.time AS change_time, change.user_id STARTS WITH "__" AS is_system
|
|
881
|
+
RETURN change_user_id, change_time
|
|
882
|
+
ORDER BY change_time DESC, is_system ASC
|
|
883
|
+
LIMIT 1
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
// Save current field values for rollback, then update field metadata
|
|
887
|
+
WITH n, field, change_user_id, change_time
|
|
888
|
+
WHERE change_user_id IS NOT NULL
|
|
889
|
+
// --------------------
|
|
890
|
+
// make sure not to set the previous_updated_at multiple times
|
|
891
|
+
// --------------------
|
|
892
|
+
CALL (field, change_user_id) {
|
|
893
|
+
WITH field
|
|
894
|
+
WHERE field.updated_at <> $at OR field.updated_at IS NULL
|
|
895
|
+
SET field.previous_updated_at = field.updated_at, field.previous_updated_by = field.updated_by
|
|
896
|
+
SET field.updated_at = $at, field.updated_by = change_user_id
|
|
897
|
+
}
|
|
898
|
+
|
|
899
|
+
// Aggregate to find the latest change across all fields for Node-level metadata
|
|
900
|
+
WITH n, change_user_id, change_time, change_user_id STARTS WITH "__" AS is_system
|
|
901
|
+
ORDER BY change_time DESC, is_system ASC
|
|
902
|
+
WITH n, collect(change_user_id)[0] AS node_updated_by
|
|
903
|
+
|
|
904
|
+
// ------------------------------------
|
|
905
|
+
// Update Node metadata with latest change across all its fields
|
|
906
|
+
// ------------------------------------
|
|
907
|
+
WITH n, node_updated_by
|
|
908
|
+
WHERE node_updated_by IS NOT NULL
|
|
909
|
+
SET n.previous_updated_at = n.updated_at, n.previous_updated_by = n.updated_by
|
|
910
|
+
SET n.updated_at = $at, n.updated_by = node_updated_by
|
|
911
|
+
"""
|
|
912
|
+
self.add_to_query(query=query)
|
|
913
|
+
|
|
914
|
+
|
|
703
915
|
class DiffMergeRollbackQuery(Query):
|
|
704
916
|
name = "diff_merge_rollback"
|
|
705
917
|
type = QueryType.WRITE
|
|
@@ -709,33 +921,62 @@ class DiffMergeRollbackQuery(Query):
|
|
|
709
921
|
self,
|
|
710
922
|
at: Timestamp,
|
|
711
923
|
target_branch: Branch,
|
|
924
|
+
node_uuids: list[str],
|
|
712
925
|
**kwargs: Any,
|
|
713
926
|
) -> None:
|
|
714
927
|
super().__init__(**kwargs)
|
|
715
928
|
self.at = at
|
|
716
929
|
self.target_branch = target_branch
|
|
717
930
|
self.source_branch_name = self.branch.name
|
|
931
|
+
self.node_uuids = node_uuids
|
|
718
932
|
|
|
719
933
|
async def query_init(self, db: InfrahubDatabase, **kwargs: Any) -> None: # noqa: ARG002
|
|
720
934
|
self.params = {
|
|
721
935
|
"at": self.at.to_string(),
|
|
722
936
|
"target_branch": self.target_branch.name,
|
|
723
937
|
"source_branch": self.source_branch_name,
|
|
938
|
+
"node_uuids": self.node_uuids,
|
|
724
939
|
}
|
|
725
940
|
query = """
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
941
|
+
// ---------------------------
|
|
942
|
+
// Restore updated_at/by from previous_* on affected Node vertices
|
|
943
|
+
// ---------------------------
|
|
944
|
+
MATCH (n:Node)
|
|
945
|
+
WHERE n.uuid IN $node_uuids
|
|
946
|
+
CALL (n) {
|
|
947
|
+
WITH n
|
|
948
|
+
WHERE n.previous_updated_at IS NOT NULL
|
|
949
|
+
SET n.updated_at = n.previous_updated_at, n.updated_by = n.previous_updated_by
|
|
950
|
+
SET n.previous_updated_at = NULL, n.previous_updated_by = NULL
|
|
951
|
+
}
|
|
952
|
+
// ---------------------------
|
|
953
|
+
// Restore updated_at/by from previous_* on affected Attribute/Relationship vertices
|
|
954
|
+
// ---------------------------
|
|
955
|
+
CALL (n) {
|
|
956
|
+
MATCH (n)-[:HAS_ATTRIBUTE|IS_RELATED {branch: $target_branch}]-(attr_rel:Attribute|Relationship)
|
|
957
|
+
WHERE attr_rel.previous_updated_at IS NOT NULL
|
|
958
|
+
WITH DISTINCT attr_rel
|
|
959
|
+
SET attr_rel.updated_at = attr_rel.previous_updated_at, attr_rel.updated_by = attr_rel.previous_updated_by
|
|
960
|
+
SET attr_rel.previous_updated_at = NULL, attr_rel.previous_updated_by = NULL
|
|
961
|
+
}
|
|
962
|
+
// ---------------------------
|
|
963
|
+
// Limit results to 1 row
|
|
964
|
+
// ---------------------------
|
|
965
|
+
WITH 1 AS one
|
|
966
|
+
LIMIT 1
|
|
967
|
+
// ---------------------------
|
|
968
|
+
// reset to times on target branch
|
|
969
|
+
// ---------------------------
|
|
970
|
+
CALL () {
|
|
971
|
+
OPTIONAL MATCH (v)-[r_to {to: $at, branch: $target_branch}]-()
|
|
972
|
+
SET r_to.to = NULL, r_to.to_user_id = NULL
|
|
973
|
+
}
|
|
974
|
+
// ---------------------------
|
|
975
|
+
// reset from times on target branch
|
|
976
|
+
// ---------------------------
|
|
977
|
+
CALL () {
|
|
978
|
+
OPTIONAL MATCH (v)-[r_from {from: $at, branch: $target_branch}]-()
|
|
979
|
+
DELETE r_from
|
|
980
|
+
}
|
|
740
981
|
"""
|
|
741
982
|
self.add_to_query(query=query)
|