-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #5116 from opsmill/dga-20241202-fix-5106
Add validator when adding a new attribute / relationship to the schema
- Loading branch information
Showing
25 changed files
with
616 additions
and
15 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
from __future__ import annotations | ||
|
||
from typing import TYPE_CHECKING, Optional | ||
|
||
from ..interface import ConstraintCheckerInterface | ||
from ..query import NodeNotPresentValidatorQuery | ||
|
||
if TYPE_CHECKING: | ||
from infrahub.core.branch import Branch | ||
from infrahub.core.path import GroupedDataPaths | ||
from infrahub.database import InfrahubDatabase | ||
|
||
from ..model import SchemaConstraintValidatorRequest | ||
|
||
|
||
class NodeAttributeAddChecker(ConstraintCheckerInterface): | ||
query_classes = [NodeNotPresentValidatorQuery] | ||
|
||
def __init__(self, db: InfrahubDatabase, branch: Optional[Branch] = None): | ||
self.db = db | ||
self.branch = branch | ||
|
||
@property | ||
def name(self) -> str: | ||
return "node.attribute.add" | ||
|
||
def supports(self, request: SchemaConstraintValidatorRequest) -> bool: | ||
return request.constraint_name == self.name | ||
|
||
async def check(self, request: SchemaConstraintValidatorRequest) -> list[GroupedDataPaths]: | ||
grouped_data_paths_list: list[GroupedDataPaths] = [] | ||
if not request.schema_path.field_name: | ||
raise ValueError("field_name is not defined") | ||
attribute_schema = request.node_schema.get_attribute(name=request.schema_path.field_name) | ||
if attribute_schema.optional is True or attribute_schema.default_value is not None: | ||
return grouped_data_paths_list | ||
|
||
for query_class in self.query_classes: | ||
# TODO add exception handling | ||
query = await query_class.init( | ||
db=self.db, branch=self.branch, node_schema=request.node_schema, schema_path=request.schema_path | ||
) | ||
await query.execute(db=self.db) | ||
grouped_data_paths_list.append(await query.get_paths()) | ||
return grouped_data_paths_list |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
from __future__ import annotations | ||
|
||
from typing import TYPE_CHECKING, Optional | ||
|
||
from ..interface import ConstraintCheckerInterface | ||
from ..query import NodeNotPresentValidatorQuery | ||
|
||
if TYPE_CHECKING: | ||
from infrahub.core.branch import Branch | ||
from infrahub.core.path import GroupedDataPaths | ||
from infrahub.database import InfrahubDatabase | ||
|
||
from ..model import SchemaConstraintValidatorRequest | ||
|
||
|
||
class NodeRelationshipAddChecker(ConstraintCheckerInterface): | ||
query_classes = [NodeNotPresentValidatorQuery] | ||
|
||
def __init__(self, db: InfrahubDatabase, branch: Optional[Branch] = None): | ||
self.db = db | ||
self.branch = branch | ||
|
||
@property | ||
def name(self) -> str: | ||
return "node.relationship.add" | ||
|
||
def supports(self, request: SchemaConstraintValidatorRequest) -> bool: | ||
return request.constraint_name == self.name | ||
|
||
async def check(self, request: SchemaConstraintValidatorRequest) -> list[GroupedDataPaths]: | ||
grouped_data_paths_list: list[GroupedDataPaths] = [] | ||
if not request.schema_path.field_name: | ||
raise ValueError("field_name is not defined") | ||
rel_schema = request.node_schema.get_relationship(name=request.schema_path.field_name) | ||
if rel_schema.optional is True: | ||
return grouped_data_paths_list | ||
|
||
for query_class in self.query_classes: | ||
# TODO add exception handling | ||
query = await query_class.init( | ||
db=self.db, branch=self.branch, node_schema=request.node_schema, schema_path=request.schema_path | ||
) | ||
await query.execute(db=self.db) | ||
grouped_data_paths_list.append(await query.get_paths()) | ||
return grouped_data_paths_list |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
from __future__ import annotations | ||
|
||
from typing import TYPE_CHECKING, Any | ||
|
||
from infrahub.core.constants import PathType | ||
from infrahub.core.path import DataPath, GroupedDataPaths | ||
|
||
from .shared import SchemaValidatorQuery | ||
|
||
if TYPE_CHECKING: | ||
from infrahub.database import InfrahubDatabase | ||
|
||
|
||
class NodeNotPresentValidatorQuery(SchemaValidatorQuery): | ||
name: str = "node_not_present_validator" | ||
|
||
async def query_init(self, db: InfrahubDatabase, **kwargs: dict[str, Any]) -> None: | ||
branch_filter, branch_params = self.branch.get_query_filter_path(at=self.at.to_string()) | ||
self.params.update(branch_params) | ||
|
||
query = """ | ||
MATCH (n:%(node_kind)s) | ||
CALL { | ||
WITH n | ||
MATCH path = (root:Root)<-[rr:IS_PART_OF]-(n) | ||
WHERE all( | ||
r in relationships(path) | ||
WHERE %(branch_filter)s | ||
) | ||
RETURN path as full_path, n as node, rr as root_relationship | ||
ORDER BY rr.branch_level DESC, rr.from DESC | ||
LIMIT 1 | ||
} | ||
WITH full_path, node, root_relationship | ||
WITH full_path, node, root_relationship | ||
WHERE all(r in relationships(full_path) WHERE r.status = "active") | ||
""" % {"branch_filter": branch_filter, "node_kind": self.node_schema.kind} | ||
|
||
self.add_to_query(query) | ||
self.return_labels = ["node.uuid", "root_relationship"] | ||
|
||
async def get_paths(self) -> GroupedDataPaths: | ||
grouped_data_paths = GroupedDataPaths() | ||
for result in self.results: | ||
grouped_data_paths.add_data_path( | ||
DataPath( | ||
branch=str(result.get("root_relationship").get("branch")), | ||
path_type=PathType.NODE, | ||
node_id=str(result.get("node.uuid")), | ||
kind=self.node_schema.kind, | ||
), | ||
) | ||
|
||
return grouped_data_paths |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
8 changes: 8 additions & 0 deletions
8
backend/infrahub/dependencies/builder/constraint/schema/node_attribute.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
from infrahub.core.validators.node.attribute import NodeAttributeAddChecker | ||
from infrahub.dependencies.interface import DependencyBuilder, DependencyBuilderContext | ||
|
||
|
||
class SchemaNodeAttributeAddConstraintDependency(DependencyBuilder[NodeAttributeAddChecker]): | ||
@classmethod | ||
def build(cls, context: DependencyBuilderContext) -> NodeAttributeAddChecker: | ||
return NodeAttributeAddChecker(db=context.db, branch=context.branch) |
8 changes: 8 additions & 0 deletions
8
backend/infrahub/dependencies/builder/constraint/schema/node_relationship.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
from infrahub.core.validators.node.relationship import NodeRelationshipAddChecker | ||
from infrahub.dependencies.interface import DependencyBuilder, DependencyBuilderContext | ||
|
||
|
||
class SchemaNodeRelationshipAddConstraintDependency(DependencyBuilder[NodeRelationshipAddChecker]): | ||
@classmethod | ||
def build(cls, context: DependencyBuilderContext) -> NodeRelationshipAddChecker: | ||
return NodeRelationshipAddChecker(db=context.db, branch=context.branch) |
Oops, something went wrong.