Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CDF-23593] 😃 Refactor dump method #1313

Open
wants to merge 43 commits into
base: refactor-tool-config
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
0423a5b
Ãrefactor: new signature
doctrino Dec 20, 2024
d5c349c
refactor: reorder and clean signature
doctrino Dec 20, 2024
d4fd4c0
refactor: new signature again
doctrino Dec 20, 2024
cd88c86
refactor: draft new deploy method
doctrino Dec 20, 2024
1f774ec
refactor: draft of pull and clean
doctrino Dec 20, 2024
c42c7a5
refactor: introduced resource worker
doctrino Dec 20, 2024
3ca09b6
refactor: cleanup
doctrino Dec 20, 2024
98efae2
refactor: use worker in clean/deploy
doctrino Dec 20, 2024
ecae89f
refactor; updated load and dump for Resource Containers
doctrino Dec 20, 2024
49e21e7
refactor; updating
doctrino Dec 20, 2024
0fb18d6
refactor; two more
doctrino Dec 20, 2024
025fc51
refactor: finished workflow loaders
doctrino Dec 20, 2024
855c4a6
refactor: happier mypy
doctrino Dec 20, 2024
d22d04f
refactor: happier mypy
doctrino Dec 20, 2024
a8a5907
refactor: update signatures
doctrino Dec 21, 2024
b764b88
refactor: function and location filter
doctrino Dec 21, 2024
7333be7
refactor: extraction pipelines
doctrino Dec 21, 2024
965ab31
refactor; classic resources
doctrino Dec 21, 2024
f2c6a7a
refactor; finished transformation loaders
doctrino Dec 21, 2024
987163d
refactor; only group loader left
doctrino Dec 21, 2024
a92f448
refactor: elegant solution to group
doctrino Dec 21, 2024
44c0301
style: ruff happy
doctrino Dec 21, 2024
9c3de17
style: happy mypy
doctrino Dec 21, 2024
29fa12b
style: cleanup unsued ignore
doctrino Dec 21, 2024
7e494b8
refactor: handle special gorup case
doctrino Dec 21, 2024
30562c2
tests: update snapshot manually
doctrino Dec 21, 2024
3de7daf
fix: bug in transformation load
doctrino Dec 21, 2024
fca8cdd
fix: introduced bug
doctrino Dec 21, 2024
92210b3
tests: updated test data
doctrino Dec 21, 2024
b0252db
refactor: remove use of _is_equals
doctrino Dec 21, 2024
678acb6
docs; one step closer
doctrino Dec 21, 2024
07e43d5
refactor: simplify load resource
doctrino Dec 21, 2024
50155bb
tests: updated loaders
doctrino Dec 21, 2024
2873c44
tests: group and function loaders
doctrino Dec 21, 2024
35f4d7d
tests: closer
doctrino Dec 21, 2024
be54c2c
refactor; base loaders left
doctrino Dec 21, 2024
9590193
tests: updated base loaders
doctrino Dec 21, 2024
f92ad69
fix: tiny bug
doctrino Dec 21, 2024
7106805
fix: import
doctrino Dec 21, 2024
f475670
tests: remove tests
doctrino Dec 21, 2024
50df2bd
refactor: cleanup
doctrino Dec 21, 2024
ab071be
refactor: cleanup
doctrino Dec 21, 2024
2cce96a
refactor: delete are equal
doctrino Dec 21, 2024
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
1 change: 1 addition & 0 deletions CHANGELOG.cdf-tk.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Changes are grouped as follows:
### Fixed

- No more warning about missing `.env` file when running in `Google Cloud Build`.
- When deploying a `Sequence` resource, Cognite Toolkit now replaces `dataSetExternalId` with `dataSetId`.
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found this bug in the refactoring.


## [0.3.23] - 2024-12-13

Expand Down
5 changes: 3 additions & 2 deletions cognite_toolkit/_cdf_tk/client/api/lookup.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@
from cognite.client.utils.useful_types import SequenceNotStr

from cognite_toolkit._cdf_tk.client.api_client import ToolkitAPI
from cognite_toolkit._cdf_tk.constants import DRY_RUN_ID
from cognite_toolkit._cdf_tk.exceptions import ResourceRetrievalError

if TYPE_CHECKING:
from cognite_toolkit._cdf_tk.client._toolkit_client import ToolkitClient


class LookUpAPI(ToolkitAPI, ABC):
dry_run_id: int = -1
dry_run_id: int = DRY_RUN_ID

def __init__(self, config: ClientConfig, api_version: str | None, cognite_client: "ToolkitClient") -> None:
super().__init__(config, api_version, cognite_client)
Expand Down Expand Up @@ -82,7 +83,7 @@ def external_id(
id: int | Sequence[int],
) -> str | list[str]:
ids = [id] if isinstance(id, int) else id
missing = [id_ for id_ in ids if id not in self._reverse_cache]
missing = [id_ for id_ in ids if id_ not in self._reverse_cache]
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another bug introduced in last refactoring.

if missing:
try:
lookup = self._external_id(missing)
Expand Down
26 changes: 22 additions & 4 deletions cognite_toolkit/_cdf_tk/client/data_classes/streamlit_.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def __init__(
theme: Literal["Light", "Dark"] = "Light",
thumbnail: str | None = None,
data_set_id: int | None = None,
cognite_toolkit_app_hash: str = "MISSING",
) -> None:
self.external_id = external_id
self.name = name
Expand All @@ -33,6 +34,7 @@ def __init__(
self.theme = theme
self.thumbnail = thumbnail
self.data_set_id = data_set_id
self.cognite_toolkit_app_hash = cognite_toolkit_app_hash

def _as_file_args(self) -> dict[str, Any]:
metadata = {
Expand All @@ -42,6 +44,7 @@ def _as_file_args(self) -> dict[str, Any]:
"published": self.published,
"theme": self.theme,
"entrypoint": self.entrypoint,
"cdf-toolkit-app-hash": self.cognite_toolkit_app_hash,
}
if self.thumbnail:
metadata["thumbnail"] = self.thumbnail
Expand Down Expand Up @@ -70,6 +73,8 @@ def _load(cls, resource: dict[str, Any], cognite_client: CogniteClient | None =
for key in ["published", "theme"]:
if key in resource:
args[key] = resource[key]
if "cogniteToolkitAppHash" in resource:
args["cognite_toolkit_app_hash"] = resource["cogniteToolkitAppHash"]
return cls(**args)

def as_write(self) -> "StreamlitWrite":
Expand All @@ -88,15 +93,25 @@ def __init__(
entrypoint: str,
created_time: int,
last_updated_time: int,
app_hash: str = "MISSING",
description: str | None = None,
published: bool = False,
theme: Literal["Light", "Dark"] = "Light",
thumbnail: str | None = None,
data_set_id: int | None = None,
cognite_toolkit_app_hash: str = "MISSING",
) -> None:
super().__init__(external_id, name, creator, entrypoint, description, published, theme, thumbnail, data_set_id)
self.app_hash = app_hash
super().__init__(
external_id,
name,
creator,
entrypoint,
description,
published,
theme,
thumbnail,
data_set_id,
cognite_toolkit_app_hash,
)
self.created_time = created_time
self.last_updated_time = last_updated_time

Expand All @@ -117,6 +132,8 @@ def _load(cls, resource: dict[str, Any], cognite_client: CogniteClient | None =
for key in ["theme", "app_hash"]:
if key in resource:
args[key] = resource[key]
if "cogniteToolkitAppHash" in resource:
args["cognite_toolkit_app_hash"] = resource["cogniteToolkitAppHash"]
if "published" in resource:
if isinstance(resource["published"], str):
args["published"] = resource["published"].strip().lower() == "true"
Expand All @@ -130,7 +147,7 @@ def from_file(cls, file: FileMetadata) -> "Streamlit":
if "metadata" in dumped:
dumped.update(dumped.pop("metadata"))
if "cdf-toolkit-app-hash" in dumped:
dumped["app_hash"] = dumped.pop("cdf-toolkit-app-hash")
dumped["cogniteToolkitAppHash"] = dumped.pop("cdf-toolkit-app-hash")
if "entrypoint" not in dumped:
dumped["entrypoint"] = "MISSING"
return cls._load(dumped)
Expand All @@ -146,6 +163,7 @@ def as_write(self) -> StreamlitWrite:
theme=self.theme,
thumbnail=self.thumbnail,
data_set_id=self.data_set_id,
cognite_toolkit_app_hash=self.cognite_toolkit_app_hash,
)

def as_file(self) -> FileMetadata:
Expand Down
47 changes: 0 additions & 47 deletions cognite_toolkit/_cdf_tk/commands/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,15 @@

import sys
from collections.abc import Callable
from pathlib import Path
from typing import Any

from cognite.client.data_classes._base import T_CogniteResourceList, T_WritableCogniteResource, T_WriteClass
from rich import print

from cognite_toolkit._cdf_tk.exceptions import ToolkitRequiredValueError, ToolkitTypeError
from cognite_toolkit._cdf_tk.loaders import (
ResourceLoader,
)
from cognite_toolkit._cdf_tk.loaders._base_loaders import T_ID, T_WritableCogniteResourceList
from cognite_toolkit._cdf_tk.tk_warnings import (
LowSeverityWarning,
ToolkitWarning,
WarningList,
)
from cognite_toolkit._cdf_tk.tracker import Tracker
from cognite_toolkit._cdf_tk.utils import (
CDFToolConfig,
)

_HAS_PRINTED_COLLECT_MESSAGE = False

Expand Down Expand Up @@ -75,39 +64,3 @@ def warn(self, warning: ToolkitWarning) -> None:
def console(self, message: str, prefix: str = "[bold green]INFO:[/] ") -> None:
if not self.silent:
print(f"{prefix}{message}")

def _load_files(
self,
loader: ResourceLoader[
T_ID, T_WriteClass, T_WritableCogniteResource, T_CogniteResourceList, T_WritableCogniteResourceList
],
filepaths: list[Path],
ToolGlobals: CDFToolConfig,
skip_validation: bool,
) -> T_CogniteResourceList:
loaded_resources = loader.list_write_cls([])
for filepath in filepaths:
try:
resource = loader.load_resource_file(filepath, ToolGlobals, skip_validation)
except KeyError as e:
# KeyError means that we are missing a required field in the yaml file.
raise ToolkitRequiredValueError(
f"Failed to load {filepath.name} with {loader.display_name}. Missing required field: {e}."
f"\nPlease compare with the API specification at {loader.doc_url()}."
)
except TypeError as e:
raise ToolkitTypeError(
f"Failed to load {filepath.name} with {loader.display_name}. Wrong type {e!r}"
) from e
if resource is None:
# This is intentional. It is, for example, used by the AuthLoader to skip groups with resource scopes.
continue
if isinstance(resource, loader.list_write_cls) and not resource:
self.warn(LowSeverityWarning(f"Skipping {filepath.name}. No data to load."))
continue

if isinstance(resource, loader.list_write_cls):
loaded_resources.extend(resource)
else:
loaded_resources.append(resource)
return loaded_resources
39 changes: 17 additions & 22 deletions cognite_toolkit/_cdf_tk/commands/clean.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
ResourceContainerDeployResult,
ResourceDeployResult,
)
from cognite_toolkit._cdf_tk.data_classes._module_directories import ReadModule
from cognite_toolkit._cdf_tk.exceptions import (
ToolkitCleanResourceError,
ToolkitNotADirectoryError,
Expand All @@ -34,6 +35,7 @@
RawDatabaseLoader,
ResourceContainerLoader,
ResourceLoader,
ResourceWorker,
)
from cognite_toolkit._cdf_tk.loaders._base_loaders import T_ID, Loader, T_WritableCogniteResourceList
from cognite_toolkit._cdf_tk.tk_warnings import (
Expand All @@ -48,7 +50,7 @@
read_yaml_file,
)

from ._utils import _print_ids_or_length, _remove_duplicates
from ._utils import _print_ids_or_length


class CleanCommand(ToolkitCommand):
Expand All @@ -58,6 +60,7 @@ def clean_resources(
T_ID, T_WriteClass, T_WritableCogniteResource, T_CogniteResourceList, T_WritableCogniteResourceList
],
ToolGlobals: CDFToolConfig,
read_modules: list[ReadModule],
dry_run: bool = False,
drop: bool = True,
drop_data: bool = False,
Expand All @@ -77,25 +80,16 @@ def clean_resources(
)
return ResourceContainerDeployResult(name=loader.display_name, item_name=loader.item_name)

filepaths = loader.find_files()

worker = ResourceWorker(loader)
files = worker.load_files(read_modules=read_modules)
# Since we do a clean, we do not want to verify that everything exists wrt data sets, spaces etc.
loaded_resources = self._load_files(loader, filepaths, ToolGlobals, skip_validation=True)

# Duplicates are warned in the build step, but the use might continue, so we
# need to check for duplicates here as well.
loaded_resources, duplicates = _remove_duplicates(loaded_resources, loader)

capabilities = loader.get_required_capability(loaded_resources, read_only=dry_run)

if capabilities and (missing := ToolGlobals.toolkit_client.verify.authorization(capabilities)):
raise ToolGlobals.toolkit_client.verify.create_error(missing, action=f"clean {loader.display_name}")

nr_of_items = len(loaded_resources)
if nr_of_items == 0:
return ResourceDeployResult(name=loader.display_name)

existing_resources = loader.retrieve(loader.get_ids(loaded_resources))
existing_resources, duplicated = worker.load_resources(
filepaths=files,
return_existing=True,
environment_variables=ToolGlobals.environment_variables(),
is_dry_run=True,
verbose=verbose,
)
nr_of_existing = len(existing_resources)

if drop:
Expand All @@ -106,7 +100,7 @@ def clean_resources(
with_data = ""
print(f"[bold]{prefix} {nr_of_existing} {loader.display_name} {with_data}from CDF...[/]")
if not isinstance(loader, RawDatabaseLoader):
for duplicate in duplicates:
for duplicate in duplicated:
self.warn(LowSeverityWarning(f"Duplicate {loader.display_name} {duplicate}."))

# Deleting resources.
Expand All @@ -121,15 +115,15 @@ def clean_resources(
return ResourceContainerDeployResult(
name=loader.display_name,
deleted=nr_of_deleted,
total=nr_of_items,
total=nr_of_existing,
dropped_datapoints=nr_of_dropped_datapoints,
item_name=loader.item_name,
)
elif not isinstance(self, ResourceContainerLoader) and drop:
nr_of_deleted = self._delete_resources(existing_resources, loader, dry_run, verbose)
if verbose:
print("")
return ResourceDeployResult(name=loader.display_name, deleted=nr_of_deleted, total=nr_of_items)
return ResourceDeployResult(name=loader.display_name, deleted=nr_of_deleted, total=nr_of_existing)
else:
return ResourceDeployResult(name=loader.display_name)

Expand Down Expand Up @@ -275,6 +269,7 @@ def execute(
result = self.clean_resources(
loader,
ToolGlobals,
read_modules=clean_state.read_modules,
drop=True,
dry_run=dry_run,
drop_data=True,
Expand Down
Loading
Loading