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