diff --git a/backend/infrahub/core/constants/__init__.py b/backend/infrahub/core/constants/__init__.py index e66b96315d..f4e097974f 100644 --- a/backend/infrahub/core/constants/__init__.py +++ b/backend/infrahub/core/constants/__init__.py @@ -146,6 +146,11 @@ class AllowOverrideType(InfrahubStringEnum): ANY = "any" +class RepositoryObjects(InfrahubStringEnum): + OBJECT = "object" + MENU = "menu" + + class ContentType(InfrahubStringEnum): APPLICATION_JSON = "application/json" APPLICATION_YAML = "application/yaml" diff --git a/backend/infrahub/core/constants/infrahubkind.py b/backend/infrahub/core/constants/infrahubkind.py index 4762374108..57c1f0dcfb 100644 --- a/backend/infrahub/core/constants/infrahubkind.py +++ b/backend/infrahub/core/constants/infrahubkind.py @@ -58,6 +58,7 @@ PROPOSEDCHANGE = "CoreProposedChange" REFRESHTOKEN = "InternalRefreshToken" REPOSITORY = "CoreRepository" +REPOSITORYGROUP = "CoreRepositoryGroup" RESOURCEPOOL = "CoreResourcePool" GENERICREPOSITORY = "CoreGenericRepository" READONLYREPOSITORY = "CoreReadOnlyRepository" diff --git a/backend/infrahub/core/protocols.py b/backend/infrahub/core/protocols.py index c7aa17f2fd..1654d9c1cf 100644 --- a/backend/infrahub/core/protocols.py +++ b/backend/infrahub/core/protocols.py @@ -497,6 +497,11 @@ class CoreRepository(LineageOwner, LineageSource, CoreGenericRepository, CoreTas commit: StringOptional +class CoreRepositoryGroup(CoreGroup): + content: Dropdown + repository: RelationshipManager + + class CoreRepositoryValidator(CoreValidator): repository: RelationshipManager diff --git a/backend/infrahub/core/schema/definitions/core/__init__.py b/backend/infrahub/core/schema/definitions/core/__init__.py index c681e21027..094c6c8ada 100644 --- a/backend/infrahub/core/schema/definitions/core/__init__.py +++ b/backend/infrahub/core/schema/definitions/core/__init__.py @@ -28,7 +28,13 @@ from .core import core_node, core_task_target from .generator import core_generator_definition, core_generator_instance from .graphql_query import core_graphql_query -from .group import core_generator_group, core_graphql_query_group, core_group, core_standard_group +from .group import ( + core_generator_group, + core_graphql_query_group, + core_group, + core_repository_group, + core_standard_group, +) from .ipam import builtin_ip_address, builtin_ip_prefix, builtin_ipam, core_ipam_namespace from .lineage import lineage_owner, lineage_source from .menu import generic_menu_item, menu_item @@ -116,6 +122,7 @@ core_standard_group, core_generator_group, core_graphql_query_group, + core_repository_group, builtin_tag, core_account, core_account_token, diff --git a/backend/infrahub/core/schema/definitions/core/group.py b/backend/infrahub/core/schema/definitions/core/group.py index da86ec190a..f825d22952 100644 --- a/backend/infrahub/core/schema/definitions/core/group.py +++ b/backend/infrahub/core/schema/definitions/core/group.py @@ -1,9 +1,11 @@ from infrahub.core.constants import ( BranchSupportType, InfrahubKind, + RepositoryObjects, ) from infrahub.core.constants import RelationshipCardinality as Cardinality from infrahub.core.constants import RelationshipKind as RelKind +from infrahub.core.schema.dropdown import DropdownChoice from ...attribute_schema import AttributeSchema as Attr from ...generic_schema import GenericSchema @@ -80,6 +82,7 @@ generate_profile=False, ) + core_graphql_query_group = NodeSchema( name="GraphQLQueryGroup", namespace="Core", @@ -106,3 +109,45 @@ ), ], ) + + +core_repository_group = NodeSchema( + name="RepositoryGroup", + namespace="Core", + description="Group of nodes associated with a given repository.", + include_in_menu=False, + icon="mdi:account-group", + label="Repository Group", + default_filter="name__value", + order_by=["name__value"], + display_labels=["name__value"], + branch=BranchSupportType.LOCAL, + inherit_from=[InfrahubKind.GENERICGROUP], + generate_profile=False, + attributes=[ + Attr( + name="content", + kind="Dropdown", + description="Type of data to load, can be either `object` or `menu`", + choices=[ + DropdownChoice( + name=RepositoryObjects.OBJECT.value, + label="Objects", + ), + DropdownChoice( + name=RepositoryObjects.MENU.value, + label="Menus", + ), + ], + optional=False, + ), + ], + relationships=[ + Rel( + name="repository", + peer=InfrahubKind.GENERICREPOSITORY, + optional=False, + cardinality=Cardinality.ONE, + ), + ], +) diff --git a/backend/infrahub/git/base.py b/backend/infrahub/git/base.py index f875308411..3f8b2b6c03 100644 --- a/backend/infrahub/git/base.py +++ b/backend/infrahub/git/base.py @@ -162,6 +162,11 @@ class InfrahubRepositoryBase(BaseModel, ABC): infrahub_branch_name: str | None = Field(None, description="Infrahub branch on which to sync the remote repository") model_config = ConfigDict(arbitrary_types_allowed=True, ignored_types=(Flow, Task)) + def get_client(self) -> InfrahubClient: + if self.client is None: + raise ValueError("Client is not set") + return self.client + @property def sdk(self) -> InfrahubClient: if self.client: @@ -445,9 +450,6 @@ def get_worktrees(self) -> list[Worktree]: return [Worktree.init(response) for response in responses] - def get_client(self) -> InfrahubClient: - return self.sdk - def get_location(self) -> str: if self.location: return self.location diff --git a/backend/infrahub/git/integrator.py b/backend/infrahub/git/integrator.py index 268d2c007f..f593388e6f 100644 --- a/backend/infrahub/git/integrator.py +++ b/backend/infrahub/git/integrator.py @@ -29,10 +29,12 @@ InfrahubPythonTransformConfig, InfrahubRepositoryConfig, ) +from infrahub_sdk.spec.menu import MenuFile +from infrahub_sdk.spec.object import ObjectFile from infrahub_sdk.template import Jinja2Template from infrahub_sdk.template.exceptions import JinjaTemplateError from infrahub_sdk.utils import compare_lists -from infrahub_sdk.yaml import SchemaFile +from infrahub_sdk.yaml import InfrahubFile, SchemaFile from prefect import flow, task from prefect.cache_policies import NONE from prefect.logging import get_run_logger @@ -40,7 +42,7 @@ from pydantic import ValidationError as PydanticValidationError from typing_extensions import Self -from infrahub.core.constants import ArtifactStatus, ContentType, InfrahubKind, RepositorySyncStatus +from infrahub.core.constants import ArtifactStatus, ContentType, InfrahubKind, RepositoryObjects, RepositorySyncStatus from infrahub.core.registry import registry from infrahub.events.artifact_action import ArtifactCreatedEvent, ArtifactUpdatedEvent from infrahub.events.models import EventMeta @@ -54,6 +56,7 @@ import types from infrahub_sdk.checks import InfrahubCheck + from infrahub_sdk.ctl.utils import YamlFileVar from infrahub_sdk.schema.repository import InfrahubRepositoryArtifactDefinitionConfig from infrahub_sdk.transforms import InfrahubTransform @@ -159,7 +162,7 @@ async def init(cls, service: InfrahubServices, commit: str | None = None, **kwar async def ensure_location_is_defined(self) -> None: if self.location: return - client = self.get_client() + client = self.sdk repo = await client.get( kind=CoreGenericRepository, name__value=self.name, exclude=["tags", "credential"], raise_when_missing=True ) @@ -179,6 +182,7 @@ async def import_objects_from_files( config_file = await self.get_repository_config(branch_name=infrahub_branch_name, commit=commit) # type: ignore[misc] sync_status = RepositorySyncStatus.IN_SYNC if config_file else RepositorySyncStatus.ERROR_IMPORT + error: Exception | None = None try: @@ -189,6 +193,17 @@ async def import_objects_from_files( branch_name=infrahub_branch_name, commit=commit, config_file=config_file ) # type: ignore[misc] + await self.import_objects( + branch_name=infrahub_branch_name, + commit=commit, + config_file=config_file, + ) # type: ignore[misc] + await self.import_objects( + branch_name=infrahub_branch_name, + commit=commit, + config_file=config_file, + ) # type: ignore[misc] + await self.import_all_python_files( # type: ignore[call-overload] branch_name=infrahub_branch_name, commit=commit, config_file=config_file ) # type: ignore[misc] @@ -815,6 +830,80 @@ async def import_python_transforms( log.info(f"TransformPython {transform_name!r} not found locally, deleting") await transform_definition_in_graph[transform_name].delete() + async def _load_yamlfile_from_disk(self, paths: list[Path], file_type: type[YamlFileVar]) -> list[YamlFileVar]: + data_files = file_type.load_from_disk(paths=paths) + + for data_file in data_files: + if not data_file.valid or not data_file.content: + raise ValueError(f"{data_file.error_message} ({data_file.location})") + + return data_files + + async def _load_objects( + self, + paths: list[Path], + branch: str, + file_type: type[InfrahubFile], + ) -> None: + """Load one or multiple objects files into Infrahub.""" + + log = get_run_logger() + files = await self._load_yamlfile_from_disk(paths=paths, file_type=file_type) + + for file in files: + await file.validate_format(client=self.sdk, branch=branch) + schema = await self.sdk.schema.get(kind=file.spec.kind, branch=branch) + if not schema.human_friendly_id and not schema.default_filter: + raise ValueError( + f"Schemas of objects or menus defined within {file.location} " + "should have a `human_friendly_id` defined to avoid creating duplicated objects." + ) + + for file in files: + log.info(f"Loading objects defined in {file.location}") + await file.process(client=self.sdk, branch=branch) + + async def _import_file_paths( + self, branch_name: str, commit: str, files_pathes: list[Path], object_type: RepositoryObjects + ) -> None: + branch_wt = self.get_worktree(identifier=commit or branch_name) + file_pathes = [branch_wt.directory / file_path for file_path in files_pathes] + + # We currently assume there can't be concurrent imports, but if so, we might need to clone the client before tracking here. + async with self.sdk.start_tracking( + identifier=f"group-repo-{object_type.value}-{self.id}", + delete_unused_nodes=True, + branch=branch_name, + group_type="CoreRepositoryGroup", + group_params={"content": object_type.value, "repository": str(self.id)}, + ): + file_type = repo_object_type_to_file_type(object_type) + await self._load_objects( + paths=file_pathes, + branch=branch_name, + file_type=file_type, + ) + + @task(name="import-objects", task_run_name="Import Objects", cache_policy=NONE) # type: ignore[arg-type] + async def import_objects( + self, + branch_name: str, + commit: str, + config_file: InfrahubRepositoryConfig, + ) -> None: + await self._import_file_paths( + branch_name=branch_name, + commit=commit, + files_pathes=config_file.objects, + object_type=RepositoryObjects.OBJECT, + ) + await self._import_file_paths( + branch_name=branch_name, + commit=commit, + files_pathes=config_file.menus, + object_type=RepositoryObjects.MENU, + ) + @task(name="check-definition-get", task_run_name="Get Check Definition", cache_policy=NONE) # type: ignore[arg-type] async def get_check_definition( self, @@ -1342,3 +1431,13 @@ async def render_artifact( await self.service.event.send(event=event) return ArtifactGenerateResult(changed=True, checksum=checksum, storage_id=storage_id, artifact_id=artifact.id) + + +def repo_object_type_to_file_type(repo_object: RepositoryObjects) -> type[InfrahubFile]: + match repo_object: + case RepositoryObjects.OBJECT: + return ObjectFile + case RepositoryObjects.MENU: + return MenuFile + case _: + raise ValueError(f"Unknown repository object type: {repo_object}") diff --git a/backend/tests/fixtures/repos/car-dealership/initial__main/.infrahub.yml b/backend/tests/fixtures/repos/car-dealership/initial__main/.infrahub.yml index 40ef4352be..5bbfec9701 100644 --- a/backend/tests/fixtures/repos/car-dealership/initial__main/.infrahub.yml +++ b/backend/tests/fixtures/repos/car-dealership/initial__main/.infrahub.yml @@ -98,3 +98,11 @@ queries: file_path: "generators/cartags.gql" - name: person_with_cars file_path: "templates/person_with_cars.gql" + +objects: + - "objects/persons.yml" + - "objects/manufacturers.yml" + +menus: + - "menus/person_base.yml" + - "menus/manufacturer_base.yml" diff --git a/backend/tests/fixtures/repos/car-dealership/initial__main/menus/manufacturer_base.yml b/backend/tests/fixtures/repos/car-dealership/initial__main/menus/manufacturer_base.yml new file mode 100644 index 0000000000..73b35e2f9d --- /dev/null +++ b/backend/tests/fixtures/repos/car-dealership/initial__main/menus/manufacturer_base.yml @@ -0,0 +1,15 @@ +--- +apiVersion: infrahub.app/v1 +kind: Menu +spec: + data: + - namespace: Testing + name: Manufacturer + label: Manufacturer + kind: TestingManufacturer + children: + data: + - namespace: Testing + name: Car + label: Car + kind: TestingCar diff --git a/backend/tests/fixtures/repos/car-dealership/initial__main/menus/person_base.yml b/backend/tests/fixtures/repos/car-dealership/initial__main/menus/person_base.yml new file mode 100644 index 0000000000..342f685b0f --- /dev/null +++ b/backend/tests/fixtures/repos/car-dealership/initial__main/menus/person_base.yml @@ -0,0 +1,15 @@ +--- +apiVersion: infrahub.app/v1 +kind: Menu +spec: + data: + - namespace: Testing + name: Person + label: Person + kind: TestingPerson + children: + data: + - namespace: Testing + name: Manufacturer + label: Manufacturer + kind: TestingManufacturer diff --git a/backend/tests/fixtures/repos/car-dealership/initial__main/objects/manufacturers.yml b/backend/tests/fixtures/repos/car-dealership/initial__main/objects/manufacturers.yml new file mode 100644 index 0000000000..c7b3fbaa01 --- /dev/null +++ b/backend/tests/fixtures/repos/car-dealership/initial__main/objects/manufacturers.yml @@ -0,0 +1,12 @@ +--- +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: TestingManufacturer + data: + - name: Mercedes + customers: + - "Ethan Carter" + - name: Ford + customers: + - "Olivia Bennett" diff --git a/backend/tests/fixtures/repos/car-dealership/initial__main/objects/persons.yml b/backend/tests/fixtures/repos/car-dealership/initial__main/objects/persons.yml new file mode 100644 index 0000000000..788424ba64 --- /dev/null +++ b/backend/tests/fixtures/repos/car-dealership/initial__main/objects/persons.yml @@ -0,0 +1,10 @@ +--- +apiVersion: infrahub.app/v1 +kind: Object +spec: + kind: TestingPerson + data: + - name: Ethan Carter + height: 180 + - name: Olivia Bennett + height: 170 diff --git a/backend/tests/integration/git/test_repository.py b/backend/tests/integration/git/test_repository.py index 0077666108..8aa7a46f7e 100644 --- a/backend/tests/integration/git/test_repository.py +++ b/backend/tests/integration/git/test_repository.py @@ -15,6 +15,7 @@ from tests.helpers.file_repo import FileRepo from tests.helpers.schema import CAR_SCHEMA, load_schema from tests.helpers.test_app import TestInfrahubApp +from tests.integration.git.utils import check_repo_correctly_created if TYPE_CHECKING: from pathlib import Path @@ -50,6 +51,7 @@ async def test_create_repository( initial_dataset: None, git_repos_source_dir_module_scope: Path, client: InfrahubClient, + default_branch, ) -> None: """Validate that we can create a repository, that it gets updated with the commit id and that objects are created.""" client_repository = await client.create( @@ -59,18 +61,24 @@ async def test_create_repository( await client_repository.save() repository: CoreRepository = await NodeManager.get_one( - db=db, id=client_repository.id, kind=InfrahubKind.REPOSITORY, raise_on_error=True + db=db, + id=client_repository.id, + kind=InfrahubKind.REPOSITORY, + raise_on_error=True, ) - check_definition: CoreCheckDefinition = await NodeManager.get_one_by_default_filter( - db=db, id="car_description_check", kind=InfrahubKind.CHECKDEFINITION, raise_on_error=True + db=db, + id="car_description_check", + kind=InfrahubKind.CHECKDEFINITION, + raise_on_error=True, ) - assert repository.commit.value - assert repository.internal_status.value == "active" + assert repository.internal_status.value == "active", f"{repository.internal_status.value=}" assert repository.operational_status.value == "online" assert check_definition.file_path.value == "checks/car_overview.py" + await check_repo_correctly_created(repo_id=client_repository.id, db=db, branch_name=default_branch.name) + @pytest.mark.parametrize( "stderr,expected_operational_status", [ diff --git a/backend/tests/integration/git/test_repository_branch.py b/backend/tests/integration/git/test_repository_branch.py index 5d5e653a97..bbef562de7 100644 --- a/backend/tests/integration/git/test_repository_branch.py +++ b/backend/tests/integration/git/test_repository_branch.py @@ -11,13 +11,14 @@ from tests.helpers.file_repo import FileRepo from tests.helpers.schema import CAR_SCHEMA, load_schema from tests.helpers.test_app import TestInfrahubApp +from tests.integration.git.utils import check_repo_correctly_created if TYPE_CHECKING: from pathlib import Path from infrahub_sdk import InfrahubClient - from infrahub.core.protocols import CoreCheckDefinition, CoreRepository + from infrahub.core.protocols import CoreRepository from infrahub.database import InfrahubDatabase BRANCH_NAME = "branch2" @@ -48,38 +49,17 @@ async def test_create_repository( git_repos_source_dir_module_scope: Path, client: InfrahubClient, ) -> None: + """Validate that we can create a repository, that it gets updated with the commit id and that objects are created.""" + branch = await client.branch.create(branch_name=BRANCH_NAME) - """Validate that we can create a repository, that it gets updated with the commit id and that objects are created.""" client_repository = await client.create( kind=InfrahubKind.REPOSITORY, branch=branch.name, data={"name": "car-dealership", "location": f"{git_repos_source_dir_module_scope}/car-dealership"}, ) await client_repository.save() - - repository_branch: CoreRepository = await NodeManager.get_one( - db=db, id=client_repository.id, kind=InfrahubKind.REPOSITORY, branch=branch.name, raise_on_error=True - ) - - check_definition: CoreCheckDefinition = await NodeManager.get_one_by_default_filter( - db=db, - id="car_description_check", - kind=InfrahubKind.CHECKDEFINITION, - branch=branch.name, - raise_on_error=True, - ) - - assert repository_branch.commit.value - assert repository_branch.internal_status.value == RepositoryInternalStatus.STAGING.value - assert check_definition.file_path.value == "checks/car_overview.py" - - repository_main: CoreRepository = await NodeManager.get_one( - db=db, id=client_repository.id, kind=InfrahubKind.REPOSITORY, raise_on_error=True - ) - - assert repository_main.commit.value is None - assert repository_main.internal_status.value == RepositoryInternalStatus.INACTIVE.value + await check_repo_correctly_created(client_repository.id, db, branch_name=branch.name) async def test_merge_branch( self, diff --git a/backend/tests/integration/git/utils.py b/backend/tests/integration/git/utils.py new file mode 100644 index 0000000000..ff4f97a7c4 --- /dev/null +++ b/backend/tests/integration/git/utils.py @@ -0,0 +1,89 @@ +from __future__ import annotations + +from infrahub.core.constants import RepositoryObjects +from infrahub.core.manager import NodeManager + + +async def check_repo_correctly_created(repo_id, db, branch_name: str): + # Check persons have been correctly loaded + person_ethan = await NodeManager.get_one_by_default_filter( + db=db, id="Ethan Carter", kind="TestingPerson", raise_on_error=True, branch=branch_name + ) + assert person_ethan.name.value == "Ethan Carter" + assert person_ethan.height.value == 180 + + person_olivia = await NodeManager.get_one_by_default_filter( + db=db, id="Olivia Bennett", kind="TestingPerson", raise_on_error=True, branch=branch_name + ) + assert person_olivia.name.value == "Olivia Bennett" + assert person_olivia.height.value == 170 + + # Check manufacturers have been correctly loaded + manufacturer_mercedes = await NodeManager.get_one_by_default_filter( + db=db, + id="Mercedes", + kind="TestingManufacturer", + raise_on_error=True, + prefetch_relationships=True, + branch=branch_name, + ) + assert manufacturer_mercedes.name.value == "Mercedes" + assert list((await manufacturer_mercedes.customers.get_peers(db=db)).values())[0].name.value == "Ethan Carter" + + manufacturer_ford = await NodeManager.get_one_by_default_filter( + db=db, + id="Ford", + kind="TestingManufacturer", + raise_on_error=True, + prefetch_relationships=True, + branch=branch_name, + ) + assert manufacturer_ford.name.value == "Ford" + assert list((await manufacturer_ford.customers.get_peers(db=db)).values())[0].name.value == "Olivia Bennett" + + # Check repository groups have been correctly created + repository_group = await NodeManager.get_one_by_default_filter( + db=db, + id=f"group-repo-{RepositoryObjects.OBJECT.value}-{repo_id}", + kind="CoreRepositoryGroup", + raise_on_error=True, + prefetch_relationships=True, + branch=branch_name, + ) + assert repository_group.content.value == RepositoryObjects.OBJECT.value + assert (await repository_group.repository.get_peer(db=db)).id == repo_id + members = (await repository_group.members.get_peers(db=db)).values() + assert len(members) == 4 + assert {m.id for m in members} == { + manufacturer_ford.id, + manufacturer_mercedes.id, + person_ethan.id, + person_olivia.id, + } + + repository_group_menus = await NodeManager.get_one_by_default_filter( + db=db, + id=f"group-repo-{RepositoryObjects.MENU.value}-{repo_id}", + kind="CoreRepositoryGroup", + raise_on_error=True, + prefetch_relationships=True, + branch=branch_name, + ) + assert repository_group_menus.content.value == RepositoryObjects.MENU.value + assert (await repository_group_menus.repository.get_peer(db=db)).id == repo_id + _ = await NodeManager.get_one_by_hfid( + db=db, + hfid=["Testing", "Manufacturer"], + kind="CoreMenu", + raise_on_error=True, + prefetch_relationships=True, + branch=branch_name, + ) + _ = await NodeManager.get_one_by_hfid( + db=db, + hfid=["Testing", "Person"], + kind="CoreMenu", + raise_on_error=True, + prefetch_relationships=True, + branch=branch_name, + ) diff --git a/backend/tests/unit/graphql/test_graphql_utils.py b/backend/tests/unit/graphql/test_graphql_utils.py index ee231aac63..b08ed3abb8 100644 --- a/backend/tests/unit/graphql/test_graphql_utils.py +++ b/backend/tests/unit/graphql/test_graphql_utils.py @@ -58,6 +58,7 @@ async def test_schema_models_generics( InfrahubKind.GENERICGROUP, InfrahubKind.STANDARDGROUP, InfrahubKind.ACCOUNTGROUP, + InfrahubKind.REPOSITORYGROUP, "EdgedTestPerson", "NestedEdgedCoreGroup", "NestedEdgedTestCar", diff --git a/backend/tests/unit/graphql/test_query_analyzer.py b/backend/tests/unit/graphql/test_query_analyzer.py index b2b88b80ac..12b1c56495 100644 --- a/backend/tests/unit/graphql/test_query_analyzer.py +++ b/backend/tests/unit/graphql/test_query_analyzer.py @@ -147,6 +147,7 @@ async def test_get_models_in_use( InfrahubKind.GENERICGROUP, InfrahubKind.STANDARDGROUP, InfrahubKind.ACCOUNTGROUP, + InfrahubKind.REPOSITORYGROUP, "TestCar", "TestElectricCar", "TestGazCar", @@ -157,6 +158,7 @@ async def test_get_models_in_use( InfrahubKind.GENERATORGROUP, InfrahubKind.GRAPHQLQUERYGROUP, InfrahubKind.GENERICGROUP, + InfrahubKind.REPOSITORYGROUP, InfrahubKind.STANDARDGROUP, "TestCar", "TestElectricCar", diff --git a/backend/tests/unit/message_bus/operations/requests/test_proposed_change.py b/backend/tests/unit/message_bus/operations/requests/test_proposed_change.py index a837e14533..9af9bd3427 100644 --- a/backend/tests/unit/message_bus/operations/requests/test_proposed_change.py +++ b/backend/tests/unit/message_bus/operations/requests/test_proposed_change.py @@ -141,8 +141,8 @@ async def test_get_proposed_change_schema_integrity_constraints( ) non_generate_profile_constraints = [c for c in constraints if c.constraint_name != "node.generate_profile.update"] # should be updated/removed when ConstraintValidatorDeterminer is updated (#2592) - assert len(constraints) == 206 - assert len(non_generate_profile_constraints) == 122 + assert len(constraints) == 211 + assert len(non_generate_profile_constraints) == 126 dumped_constraints = [c.model_dump() for c in non_generate_profile_constraints] assert { "constraint_name": "relationship.optional.update", diff --git a/docs/docs/reference/dotinfrahub.mdx b/docs/docs/reference/dotinfrahub.mdx index 7db5e8a5fb..359d5d2c9a 100644 --- a/docs/docs/reference/dotinfrahub.mdx +++ b/docs/docs/reference/dotinfrahub.mdx @@ -129,3 +129,23 @@ To help with the development process of a repository configuration file, you can | -------- | ---- | ----------- | --------- | | name | string | The name of the GraphQL Query | True | | file_path | string | The file within the repository with the query code. | True | + + +## Objects + + +**Description**: Objects
+**Key**: objects
+**Type**: array
+**Item type**: string
+**Item format**: path
+ + +## Menus + + +**Description**: Menus
+**Key**: menus
+**Type**: array
+**Item type**: string
+**Item format**: path
diff --git a/python_sdk b/python_sdk index 7bda36c299..f41402791b 160000 --- a/python_sdk +++ b/python_sdk @@ -1 +1 @@ -Subproject commit 7bda36c299a8df36c85ed57ac0ce05778ee17970 +Subproject commit f41402791b0d519b2deddd631abb1d14d08d8f4c