Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 92 additions & 0 deletions cognite_toolkit/_cdf_tk/resource_classes/infield_cdmv1.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
from typing import Any

from .base import BaseModelResource, ToolkitResource


class ObservationFeatureToggles(BaseModelResource):
"""Feature toggles for observations."""

is_enabled: bool | None = None
is_write_back_enabled: bool | None = None
notifications_endpoint_external_id: str | None = None
attachments_endpoint_external_id: str | None = None


class FeatureToggles(BaseModelResource):
"""Feature toggles for InField location configuration."""

three_d: bool | None = None
trends: bool | None = None
documents: bool | None = None
workorders: bool | None = None
notifications: bool | None = None
media: bool | None = None
template_checklist_flow: bool | None = None
workorder_checklist_flow: bool | None = None
observations: ObservationFeatureToggles | None = None


class AccessManagement(BaseModelResource):
"""Access management configuration."""

template_admins: list[str] | None = None # list of CDF group external IDs
checklist_admins: list[str] | None = None # list of CDF group external IDs


class ResourceFilters(BaseModelResource):
"""Resource filters."""

spaces: list[str] | None = None


class RootLocationDataFilters(BaseModelResource):
"""Data filters for root location."""

general: ResourceFilters | None = None
assets: ResourceFilters | None = None
files: ResourceFilters | None = None
timeseries: ResourceFilters | None = None


class DataExplorationConfig(BaseModelResource):
"""Properties for DataExplorationConfig node.

Contains configuration for data exploration features:
- observations: Observations feature configuration
- activities: Activities configuration
- documents: Document configuration
- notifications: Notifications configuration
- assets: Asset page configuration
"""

external_id: str

observations: dict[str, Any] | None = None # ObservationsConfigFeature
activities: dict[str, Any] | None = None # ActivitiesConfiguration
documents: dict[str, Any] | None = None # DocumentConfiguration
notifications: dict[str, Any] | None = None # NotificationsConfiguration
assets: dict[str, Any] | None = None # AssetPageConfiguration


class InfieldLocationConfigYAML(ToolkitResource):
"""Properties for InFieldLocationConfig node.

Currently migrated fields:
- root_location_external_id: Reference to the LocationFilterDTO external ID
- feature_toggles: Feature toggles migrated from old configuration
- rootAsset: Direct relation to the root asset (space and externalId)
- app_instance_space: Application instance space from appDataInstanceSpace
- access_management: Template and checklist admin groups (from templateAdmins and checklistAdmins)
- disciplines: List of disciplines (from disciplines in FeatureConfiguration)
- data_filters: Data filters for general, assets, files, and timeseries (from dataFilters in old configuration)
- data_exploration_config: Direct relation to the DataExplorationConfig node (shared across all locations)
"""

external_id: str

root_location_external_id: str | None = None
feature_toggles: FeatureToggles | None = None
app_instance_space: str | None = None
access_management: AccessManagement | None = None
data_filters: RootLocationDataFilters | None = None
data_exploration_config: DataExplorationConfig | None = None
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
externalId: my_location_config_001

rootLocationExternalId: location_filter_main_facility

featureToggles:
threeD: true
trends: true
documents: true
workorders: true
notifications: true
media: true
templateChecklistFlow: true
workorderChecklistFlow: false

appInstanceSpace: my_app_instance_space

accessManagement:
templateAdmins:
- cdf_group_template_admins
- cdf_group_superusers
checklistAdmins:
- cdf_group_checklist_admins
- cdf_group_superusers

dataFilters:
general:
spaces:
- my_space_001
- my_space_002

dataExplorationConfig:
externalId: data_exploration_config_shared
observations:
enabled: true
defaultView: list
activities:
enabled: true
showCompleted: true
documents:
enabled: true
supportedFormats:
- pdf
- docx
notifications:
enabled: true
pushEnabled: false
assets:
enabled: true
showHierarchy: true
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
from collections.abc import Iterable
from pathlib import Path
from typing import Any

import pytest

from cognite_toolkit._cdf_tk.resource_classes.infield_cdmv1 import InfieldLocationConfigYAML
from cognite_toolkit._cdf_tk.tk_warnings.fileread import ResourceFormatWarning
from cognite_toolkit._cdf_tk.validation import validate_resource_yaml_pydantic
from tests.data import COMPLETE_ORG_ALPHA_FLAGS
from tests.test_unit.utils import find_resources


def invalid_test_cases() -> Iterable:
yield pytest.param(
{"rootLocationExternalId": "myLocation"},
{"Missing required field: 'externalId'"},
id="Missing required field: externalId",
)
yield pytest.param(
{
"externalId": "my_config",
"unknownField": "invalid_value",
"anotherUnknownField": 123,
"featureToggles": {
"threeD": True,
"invalidToggle": "bad_value",
},
},
{
"In featureToggles unused field: 'invalidToggle'",
"Unused field: 'anotherUnknownField'",
"Unused field: 'unknownField'",
},
id="Multiple extra fields at different levels",
)
yield pytest.param(
{
"externalId": "my_config",
"featureToggles": {
"threeD": "not_a_boolean",
"trends": 123,
"observations": {
"isEnabled": "not_a_boolean",
"isWriteBackEnabled": ["invalid_type"],
},
},
"accessManagement": {
"templateAdmins": "should_be_a_list",
"checklistAdmins": 456,
},
},
{
"In accessManagement.checklistAdmins input should be a valid list. Got 456.",
"In accessManagement.templateAdmins input should be a valid list. Got 'should_be_a_list'.",
"In featureToggles.observations.isEnabled input should be a valid boolean. "
"Got 'not_a_boolean' of type str.",
"In featureToggles.observations.isWriteBackEnabled input should be a valid "
"boolean. Got ['invalid_type'] of type list.",
"In featureToggles.threeD input should be a valid boolean. Got 'not_a_boolean' of type str.",
"In featureToggles.trends input should be a valid boolean. Got 123 of type int.",
},
id="Multiple type mismatches across nested structures",
)


class TestInfieldCDMv1YAML:
@pytest.mark.parametrize(
"data", list(find_resources("InfieldCDMv1", resource_dir="cdf_applications", base=COMPLETE_ORG_ALPHA_FLAGS))
)
def test_load_valid(self, data: dict[str, Any]) -> None:
loaded = InfieldLocationConfigYAML.model_validate(data)

dumped = loaded.model_dump(exclude_unset=True, by_alias=True)
assert dumped == data

@pytest.mark.parametrize("data, expected_errors", list(invalid_test_cases()))
def test_invalid_error_messages(self, data: dict[str, Any], expected_errors: set[str]) -> None:
warning_list = validate_resource_yaml_pydantic(data, InfieldLocationConfigYAML, Path("some_file.yaml"))
assert len(warning_list) == 1
format_warning = warning_list[0]
assert isinstance(format_warning, ResourceFormatWarning)

assert set(format_warning.errors) == expected_errors
Loading