cognite-toolkit 0.6.83__py3-none-any.whl → 0.6.85__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of cognite-toolkit might be problematic. Click here for more details.

@@ -17,7 +17,7 @@ from cognite_toolkit._cdf_tk.storageio.selectors import (
17
17
  )
18
18
  from cognite_toolkit._cdf_tk.utils.auth import EnvironmentVariables
19
19
  from cognite_toolkit._cdf_tk.utils.cli_args import parse_view_str
20
- from cognite_toolkit._cdf_tk.utils.interactive_select import DataModelingSelect
20
+ from cognite_toolkit._cdf_tk.utils.interactive_select import AssetInteractiveSelect, DataModelingSelect
21
21
 
22
22
 
23
23
  class InstanceTypeEnum(str, Enum):
@@ -39,8 +39,8 @@ class PurgeApp(typer.Typer):
39
39
  if ctx.invoked_subcommand is None:
40
40
  print("Use [bold yellow]cdf purge --help[/] for more information.")
41
41
 
42
+ @staticmethod
42
43
  def purge_dataset(
43
- self,
44
44
  ctx: typer.Context,
45
45
  external_id: Annotated[
46
46
  str | None,
@@ -53,7 +53,46 @@ class PurgeApp(typer.Typer):
53
53
  typer.Option(
54
54
  "--include-dataset",
55
55
  "-i",
56
- help="Include dataset in the purge. This will also archive the dataset.",
56
+ help="Whether to archive the dataset itself after purging its contents.",
57
+ hidden=Flags.v07.is_enabled(),
58
+ ),
59
+ ] = False,
60
+ archive_dataset: Annotated[
61
+ bool,
62
+ typer.Option(
63
+ "--archive-dataset",
64
+ help="Whether to archive the dataset itself after purging its contents.",
65
+ hidden=not Flags.v07.is_enabled(),
66
+ ),
67
+ ] = False,
68
+ skip_data: Annotated[
69
+ bool,
70
+ typer.Option(
71
+ "--skip-data",
72
+ "-s",
73
+ help="Skip deleting the data in the dataset, only delete configurations. The resources that are "
74
+ "considered data are: time series, event, files, assets, sequences, relationships, "
75
+ "labels, and 3D Models",
76
+ hidden=not Flags.v07.is_enabled(),
77
+ ),
78
+ ] = False,
79
+ include_configurations: Annotated[
80
+ bool,
81
+ typer.Option(
82
+ "--include-configurations",
83
+ "-c",
84
+ help="Include configurations, workflows, extraction pipelines and transformations in the purge.",
85
+ hidden=not Flags.v07.is_enabled(),
86
+ ),
87
+ ] = False,
88
+ asset_recursive: Annotated[
89
+ bool,
90
+ typer.Option(
91
+ "--asset-recursive",
92
+ "-a",
93
+ help="When deleting assets, delete all child assets recursively. CAVEAT: This can lead to assets"
94
+ " not in the selected dataset being deleted if they are children of assets in the dataset.",
95
+ hidden=not Flags.v07.is_enabled(),
57
96
  ),
58
97
  ] = False,
59
98
  dry_run: Annotated[
@@ -84,11 +123,52 @@ class PurgeApp(typer.Typer):
84
123
  """This command will delete the contents of the specified dataset"""
85
124
  cmd = PurgeCommand()
86
125
  client = EnvironmentVariables.create_from_environment().get_client()
126
+
127
+ if external_id is None:
128
+ # Is Interactive
129
+ interactive = AssetInteractiveSelect(client, operation="purge")
130
+ external_id = interactive.select_data_set(allow_empty=False)
131
+ if Flags.v07.is_enabled():
132
+ skip_data = not questionary.confirm(
133
+ "Delete data in the dataset (time series, events, files, assets, sequences, relationships, labels, 3D models)?",
134
+ default=True,
135
+ ).ask()
136
+ include_configurations = questionary.confirm(
137
+ "Delete configurations (workflows, extraction pipelines and transformations) in the dataset?",
138
+ default=False,
139
+ ).ask()
140
+ asset_recursive = questionary.confirm(
141
+ "When deleting assets, delete all child assets recursively? (WARNING: This can lead "
142
+ "to assets not in the selected dataset being deleted if they are children of assets in the dataset.)",
143
+ default=False,
144
+ ).ask()
145
+ archive_dataset = questionary.confirm("Archive the dataset itself after purging?", default=False).ask()
146
+ dry_run = questionary.confirm("Dry run?", default=True).ask()
147
+ verbose = questionary.confirm("Verbose?", default=True).ask()
148
+
149
+ user_options = [archive_dataset, dry_run, verbose]
150
+ if Flags.v07.is_enabled():
151
+ user_options.extend([skip_data, include_configurations, asset_recursive])
152
+
153
+ if any(selected is None for selected in user_options):
154
+ raise typer.Abort("Aborted by user.")
155
+
156
+ else:
157
+ archive_dataset = archive_dataset if Flags.v07.is_enabled() else include_dataset
158
+
159
+ if not Flags.v07.is_enabled():
160
+ skip_data = False
161
+ include_configurations = True
162
+ asset_recursive = False
163
+
87
164
  cmd.run(
88
165
  lambda: cmd.dataset(
89
166
  client,
90
167
  external_id,
91
- include_dataset,
168
+ archive_dataset,
169
+ not skip_data,
170
+ include_configurations,
171
+ asset_recursive,
92
172
  dry_run,
93
173
  auto_yes,
94
174
  verbose,
@@ -1,6 +1,7 @@
1
1
  from typing import cast
2
2
 
3
3
  from cognite.client import CogniteClient
4
+ from rich.console import Console
4
5
 
5
6
  from .api.canvas import CanvasAPI
6
7
  from .api.charts import ChartsAPI
@@ -24,11 +25,12 @@ class ToolkitClient(CogniteClient):
24
25
  def __init__(self, config: ToolkitClientConfig | None = None, enable_set_pending_ids: bool = False) -> None:
25
26
  super().__init__(config=config)
26
27
  toolkit_config = ToolkitClientConfig.from_client_config(self.config)
28
+ self.console = Console()
27
29
  self.search = SearchAPI(self._config, self._API_VERSION, self)
28
30
  self.robotics = RoboticsAPI(self._config, self._API_VERSION, self)
29
31
  self.dml = DMLAPI(self._config, self._API_VERSION, self)
30
32
  self.verify = VerifyAPI(self._config, self._API_VERSION, self)
31
- self.lookup = LookUpGroup(self._config, self._API_VERSION, self)
33
+ self.lookup = LookUpGroup(self._config, self._API_VERSION, self, self.console)
32
34
  self.functions: ExtendedFunctionsAPI = ExtendedFunctionsAPI(toolkit_config, self._API_VERSION, self)
33
35
  self.data_modeling: ExtendedDataModelingAPI = ExtendedDataModelingAPI(self._config, self._API_VERSION, self)
34
36
  if enable_set_pending_ids:
@@ -17,11 +17,12 @@ from cognite.client.data_classes.capabilities import (
17
17
  )
18
18
  from cognite.client.exceptions import CogniteAPIError
19
19
  from cognite.client.utils.useful_types import SequenceNotStr
20
+ from rich.console import Console
20
21
 
21
22
  from cognite_toolkit._cdf_tk.client.api_client import ToolkitAPI
22
23
  from cognite_toolkit._cdf_tk.constants import DRY_RUN_ID
23
- from cognite_toolkit._cdf_tk.exceptions import ResourceRetrievalError
24
24
  from cognite_toolkit._cdf_tk.tk_warnings import MediumSeverityWarning
25
+ from cognite_toolkit._cdf_tk.utils import humanize_collection
25
26
 
26
27
  if TYPE_CHECKING:
27
28
  from cognite_toolkit._cdf_tk.client._toolkit_client import ToolkitClient
@@ -30,17 +31,20 @@ if TYPE_CHECKING:
30
31
  class LookUpAPI(ToolkitAPI, ABC):
31
32
  dry_run_id: int = DRY_RUN_ID
32
33
 
33
- def __init__(self, config: ClientConfig, api_version: str | None, cognite_client: "ToolkitClient") -> None:
34
+ def __init__(
35
+ self, config: ClientConfig, api_version: str | None, cognite_client: "ToolkitClient", console: Console
36
+ ) -> None:
34
37
  super().__init__(config, api_version, cognite_client)
35
- self._cache: dict[str, int] = {}
36
- self._reverse_cache: dict[int, str] = {}
38
+ self._console = console
39
+ self._cache: dict[str, int | None] = {}
40
+ self._reverse_cache: dict[int, str | None] = {}
37
41
 
38
42
  @property
39
43
  def resource_name(self) -> str:
40
44
  return type(self).__name__.removesuffix("LookUpAPI")
41
45
 
42
46
  @overload
43
- def id(self, external_id: str, is_dry_run: bool = False, allow_empty: bool = False) -> int: ...
47
+ def id(self, external_id: str, is_dry_run: bool = False, allow_empty: bool = False) -> int | None: ...
44
48
 
45
49
  @overload
46
50
  def id(
@@ -49,39 +53,61 @@ class LookUpAPI(ToolkitAPI, ABC):
49
53
 
50
54
  def id(
51
55
  self, external_id: str | SequenceNotStr[str], is_dry_run: bool = False, allow_empty: bool = False
52
- ) -> int | list[int]:
56
+ ) -> int | None | list[int]:
57
+ """Lookup internal IDs for given external IDs.
58
+
59
+ Args:
60
+ external_id: A string external ID or a sequence of string external IDs to look up.
61
+ is_dry_run: If True, return a predefined dry run ID for missing external IDs instead of raising an error.
62
+ allow_empty: If True, allow empty string external IDs and return 0 for them.
63
+
64
+ Returns:
65
+ The corresponding internal ID as an integer if a single external ID is provided,
66
+ or a list of internal IDs if a sequence of external IDs is provided.
67
+ """
53
68
  ids = [external_id] if isinstance(external_id, str) else external_id
54
- missing = [id for id in ids if id not in self._cache]
55
- if allow_empty and "" in missing:
69
+ need_lookup = [id for id in ids if id not in self._cache]
70
+ if allow_empty and "" in need_lookup:
56
71
  # Note we do not want to put empty string in the cache. It is a special case that
57
72
  # as of 01/02/2025 only applies to LocationFilters
58
- missing.remove("")
59
- if missing:
60
- try:
61
- lookup = self._id(missing)
62
- except CogniteAPIError as e:
63
- if 400 <= e.code < 500:
64
- missing_capabilities = self._toolkit_client.verify.authorization(self._read_acl())
65
- if missing_capabilities:
66
- raise self._toolkit_client.verify.create_error(
67
- missing_capabilities,
68
- f"lookup {self.resource_name} with external_id {missing}",
69
- )
70
- # Raise the original error if it's not a 400 or the user has access to read the resource.from
71
- raise
72
- self._cache.update(lookup)
73
- self._reverse_cache.update({v: k for k, v in lookup.items()})
74
- if len(missing) != len(lookup) and not is_dry_run:
75
- raise ResourceRetrievalError(
76
- f"Failed to retrieve {self.resource_name} with external_id {missing}. Have you created it?", missing
77
- )
78
- return (
79
- self._get_id_from_cache(external_id, is_dry_run, allow_empty)
80
- if isinstance(external_id, str)
81
- else [self._get_id_from_cache(id, is_dry_run, allow_empty) for id in ids]
82
- )
73
+ need_lookup.remove("")
74
+ if need_lookup:
75
+ self._do_lookup_external_ids(need_lookup, is_dry_run)
83
76
 
84
- def _get_id_from_cache(self, external_id: str, is_dry_run: bool = False, allow_empty: bool = False) -> int:
77
+ if isinstance(external_id, str):
78
+ return self._get_id_from_cache(external_id, is_dry_run, allow_empty)
79
+ else:
80
+ internal_ids = (
81
+ self._get_id_from_cache(external_id, is_dry_run, allow_empty) for external_id in external_id
82
+ )
83
+ return [id_ for id_ in internal_ids if id_ is not None]
84
+
85
+ def _do_lookup_external_ids(self, external_ids: list[str], is_dry_run: bool) -> None:
86
+ try:
87
+ ids_by_external_id = self._id(external_ids)
88
+ except CogniteAPIError as e:
89
+ if 400 <= e.code < 500:
90
+ missing_capabilities = self._toolkit_client.verify.authorization(self._read_acl())
91
+ if missing_capabilities:
92
+ raise self._toolkit_client.verify.create_error(
93
+ missing_capabilities,
94
+ f"lookup {self.resource_name} with external_id {external_ids}",
95
+ )
96
+ # Raise the original error if it's not a 400 or the user has access to read the resource.from
97
+ raise
98
+ self._cache.update(ids_by_external_id)
99
+ self._reverse_cache.update({v: k for k, v in ids_by_external_id.items()})
100
+ missing_external_ids = [ext_id for ext_id in external_ids if ext_id not in ids_by_external_id]
101
+ if missing_external_ids and not is_dry_run:
102
+ plural = "s" if len(missing_external_ids) > 1 else ""
103
+ plural2 = "They do" if len(missing_external_ids) > 1 else "It does"
104
+ MediumSeverityWarning(
105
+ f"Failed to retrieve {self.resource_name} with external_id{plural} "
106
+ f"{humanize_collection(missing_external_ids)}. {plural2} not exist in CDF"
107
+ ).print_warning(console=self._console)
108
+ self._cache.update({ext_id: None for ext_id in missing_external_ids})
109
+
110
+ def _get_id_from_cache(self, external_id: str, is_dry_run: bool = False, allow_empty: bool = False) -> int | None:
85
111
  if allow_empty and external_id == "":
86
112
  return 0
87
113
  elif is_dry_run:
@@ -96,34 +122,56 @@ class LookUpAPI(ToolkitAPI, ABC):
96
122
  def external_id(self, id: Sequence[int]) -> list[str]: ...
97
123
 
98
124
  def external_id(self, id: int | Sequence[int]) -> str | None | list[str]:
125
+ """Lookup external IDs for given internal IDs.
126
+
127
+ Args:
128
+ id: An integer ID or a sequence of integer IDs to look up. Note that an ID of 0 corresponds
129
+ to an empty string external ID.
130
+
131
+ Returns:
132
+ The corresponding external ID as a string if a single ID is provided,
133
+ or a list of external IDs if a sequence of IDs is provided.
134
+ If an ID does not exist, None is returned for that ID.
135
+
136
+ """
99
137
  ids = [id] if isinstance(id, int) else id
100
- missing = [id_ for id_ in ids if id_ not in self._reverse_cache]
101
- if 0 in missing:
102
- missing.remove(0)
103
- if missing:
104
- try:
105
- lookup = self._external_id(missing)
106
- except CogniteAPIError as e:
107
- if 400 <= e.code < 500:
108
- missing_capabilities = self._toolkit_client.verify.authorization(self._read_acl())
109
- if missing_capabilities:
110
- raise self._toolkit_client.verify.create_error(
111
- missing_capabilities,
112
- f"lookup {self.resource_name} with id {missing}",
113
- )
114
- # Raise the original error if it's not a 400 or the user has access to read the resource.from
115
- raise
116
- self._reverse_cache.update(lookup)
117
- self._cache.update({v: k for k, v in lookup.items()})
118
- if len(missing) != len(lookup):
119
- MediumSeverityWarning(
120
- f"Failed to retrieve {self.resource_name} with id {missing}. It does not exist in CDF"
121
- ).print_warning()
138
+ need_lookup = [id_ for id_ in ids if id_ not in self._reverse_cache if id_ != 0]
139
+ if need_lookup:
140
+ self._do_lookup_internal_ids(need_lookup)
141
+
122
142
  if isinstance(id, int):
123
143
  return self._get_external_id_from_cache(id)
124
144
  else:
125
- external_ids = (self._get_external_id_from_cache(id) for id in ids)
126
- return [id for id in external_ids if id is not None]
145
+ external_ids = (self._get_external_id_from_cache(id_) for id_ in ids)
146
+ return [id_ for id_ in external_ids if id_ is not None]
147
+
148
+ def _do_lookup_internal_ids(self, ids: list[int]) -> None:
149
+ try:
150
+ found_by_id = self._external_id(ids)
151
+ except CogniteAPIError as e:
152
+ if 400 <= e.code < 500:
153
+ missing_capabilities = self._toolkit_client.verify.authorization(self._read_acl())
154
+ if missing_capabilities:
155
+ raise self._toolkit_client.verify.create_error(
156
+ missing_capabilities,
157
+ f"lookup {self.resource_name} with id {ids}",
158
+ )
159
+ # Raise the original error if it's not a 400 or the user has access to read the resource.from
160
+ raise
161
+ self._reverse_cache.update(found_by_id)
162
+ self._cache.update({v: k for k, v in found_by_id.items()})
163
+ missing_ids = [id for id in ids if id not in found_by_id]
164
+ if not missing_ids:
165
+ return None
166
+ plural = "s" if len(missing_ids) > 1 else ""
167
+ plural2 = "They do" if len(missing_ids) > 1 else "It does"
168
+ MediumSeverityWarning(
169
+ f"Failed to retrieve {self.resource_name} with id{plural} "
170
+ f"{humanize_collection(missing_ids)}. {plural2} not exist in CDF"
171
+ ).print_warning(console=self._console)
172
+ # Cache the missing IDs with None to avoid repeated lookups
173
+ self._reverse_cache.update({missing_id: None for missing_id in missing_ids})
174
+ return None
127
175
 
128
176
  def _get_external_id_from_cache(self, id: int) -> str | None:
129
177
  if id == 0:
@@ -311,8 +359,10 @@ class FunctionLookUpAPI(LookUpAPI):
311
359
 
312
360
 
313
361
  class AllLookUpAPI(LookUpAPI, ABC):
314
- def __init__(self, config: ClientConfig, api_version: str | None, cognite_client: "ToolkitClient") -> None:
315
- super().__init__(config, api_version, cognite_client)
362
+ def __init__(
363
+ self, config: ClientConfig, api_version: str | None, cognite_client: "ToolkitClient", console: Console
364
+ ) -> None:
365
+ super().__init__(config, api_version, cognite_client, console)
316
366
  self._has_looked_up = False
317
367
 
318
368
  @abstractmethod
@@ -322,13 +372,15 @@ class AllLookUpAPI(LookUpAPI, ABC):
322
372
  def _id(self, external_id: SequenceNotStr[str]) -> dict[str, int]:
323
373
  if not self._has_looked_up:
324
374
  self._lookup()
325
- return {external_id: self._cache[external_id] for external_id in external_id if external_id in self._cache}
375
+ found_pairs = ((ext_id, self._cache[ext_id]) for ext_id in external_id if ext_id in self._cache)
376
+ return {k: v for k, v in found_pairs if v is not None}
326
377
 
327
378
  def _external_id(self, id: Sequence[int]) -> dict[int, str]:
328
379
  if not self._has_looked_up:
329
380
  self._lookup()
330
381
  self._has_looked_up = True
331
- return {id: self._reverse_cache[id] for id in id}
382
+ found_pairs = ((id_, self._reverse_cache[id_]) for id_ in id if id_ in self._reverse_cache)
383
+ return {k: v for k, v in found_pairs if v is not None}
332
384
 
333
385
 
334
386
  class SecurityCategoriesLookUpAPI(AllLookUpAPI):
@@ -363,18 +415,21 @@ class LocationFiltersLookUpAPI(AllLookUpAPI):
363
415
  def _id(self, external_id: SequenceNotStr[str]) -> dict[str, int]:
364
416
  if not self._has_looked_up:
365
417
  self._lookup()
366
- return {external_id: self._cache[external_id] for external_id in external_id if external_id in self._cache}
418
+ found_pairs = ((ext_id, self._cache[ext_id]) for ext_id in external_id if ext_id in self._cache)
419
+ return {k: v for k, v in found_pairs if v is not None}
367
420
 
368
421
 
369
422
  class LookUpGroup(ToolkitAPI):
370
- def __init__(self, config: ClientConfig, api_version: str | None, cognite_client: "ToolkitClient") -> None:
423
+ def __init__(
424
+ self, config: ClientConfig, api_version: str | None, cognite_client: "ToolkitClient", console: Console
425
+ ) -> None:
371
426
  super().__init__(config, api_version, cognite_client)
372
- self.data_sets = DataSetLookUpAPI(config, api_version, cognite_client)
373
- self.assets = AssetLookUpAPI(config, api_version, cognite_client)
374
- self.time_series = TimeSeriesLookUpAPI(config, api_version, cognite_client)
375
- self.files = FileMetadataLookUpAPI(config, api_version, cognite_client)
376
- self.events = EventLookUpAPI(config, api_version, cognite_client)
377
- self.security_categories = SecurityCategoriesLookUpAPI(config, api_version, cognite_client)
378
- self.location_filters = LocationFiltersLookUpAPI(config, api_version, cognite_client)
379
- self.extraction_pipelines = ExtractionPipelineLookUpAPI(config, api_version, cognite_client)
380
- self.functions = FunctionLookUpAPI(config, api_version, cognite_client)
427
+ self.data_sets = DataSetLookUpAPI(config, api_version, cognite_client, console)
428
+ self.assets = AssetLookUpAPI(config, api_version, cognite_client, console)
429
+ self.time_series = TimeSeriesLookUpAPI(config, api_version, cognite_client, console)
430
+ self.files = FileMetadataLookUpAPI(config, api_version, cognite_client, console)
431
+ self.events = EventLookUpAPI(config, api_version, cognite_client, console)
432
+ self.security_categories = SecurityCategoriesLookUpAPI(config, api_version, cognite_client, console)
433
+ self.location_filters = LocationFiltersLookUpAPI(config, api_version, cognite_client, console)
434
+ self.extraction_pipelines = ExtractionPipelineLookUpAPI(config, api_version, cognite_client, console)
435
+ self.functions = FunctionLookUpAPI(config, api_version, cognite_client, console)
@@ -9,6 +9,7 @@ from cognite.client._api.functions import FunctionCallsAPI, FunctionSchedulesAPI
9
9
  from cognite.client._api.raw import RawDatabasesAPI, RawRowsAPI, RawTablesAPI
10
10
  from cognite.client._api.synthetic_time_series import SyntheticDatapointsAPI
11
11
  from cognite.client.testing import CogniteClientMock
12
+ from rich.console import Console
12
13
 
13
14
  from cognite_toolkit._cdf_tk.client._toolkit_client import ToolkitClient
14
15
 
@@ -58,6 +59,7 @@ class ToolkitClientMock(CogniteClientMock):
58
59
  super().__init__(*args, **kwargs)
59
60
  return None
60
61
  super().__init__(*args, **kwargs)
62
+ self.console = Console()
61
63
  # Developer note:
62
64
  # - Please add your mocked APIs in chronological order
63
65
  # - For nested APIs:
@@ -3,7 +3,6 @@ from functools import partial
3
3
  from pathlib import Path
4
4
 
5
5
  from cognite.client.data_classes._base import T_CogniteResource
6
- from rich.console import Console
7
6
 
8
7
  from cognite_toolkit._cdf_tk.constants import DATA_MANIFEST_STEM, DATA_RESOURCE_DIR
9
8
  from cognite_toolkit._cdf_tk.exceptions import ToolkitValueError
@@ -41,7 +40,7 @@ class DownloadCommand(ToolkitCommand):
41
40
  """
42
41
  compression_cls = Compression.from_name(compression)
43
42
 
44
- console = Console()
43
+ console = io.client.console
45
44
  for selector in selectors:
46
45
  target_dir = output_dir / selector.group
47
46
  if verbose:
@@ -22,7 +22,7 @@ from cognite.client.data_classes.events import EventProperty
22
22
  from cognite_toolkit._cdf_tk.client import ToolkitClient
23
23
  from cognite_toolkit._cdf_tk.client.data_classes.apm_config_v1 import APMConfig, APMConfigList
24
24
  from cognite_toolkit._cdf_tk.cruds import NodeCRUD, ResourceCRUD, SpaceCRUD
25
- from cognite_toolkit._cdf_tk.exceptions import ToolkitRequiredValueError
25
+ from cognite_toolkit._cdf_tk.exceptions import ToolkitMissingResourceError, ToolkitRequiredValueError
26
26
  from cognite_toolkit._cdf_tk.utils import humanize_collection
27
27
 
28
28
  from .data_model import CREATED_SOURCE_SYSTEM_VIEW_ID, SPACE, SPACE_SOURCE_VIEW_ID
@@ -196,9 +196,10 @@ class SourceSystemCreator(MigrationCreator[NodeApplyList]):
196
196
  @cached_property
197
197
  def _advanced_filter(self) -> filters.Filter:
198
198
  if self.data_set_external_id is not None:
199
- return filters.Equals(
200
- SourceFileProperty.data_set_id, self.client.lookup.data_sets.id(self.data_set_external_id)
201
- )
199
+ data_set_id = self.client.lookup.data_sets.id(self.data_set_external_id)
200
+ if data_set_id is None:
201
+ raise ToolkitMissingResourceError(f"Data set with external ID '{self.data_set_external_id}' not found.")
202
+ return filters.Equals(SourceFileProperty.data_set_id, data_set_id)
202
203
  if self.hierarchy is not None:
203
204
  return filters.InAssetSubtree(SourceFileProperty.asset_external_ids, [self.hierarchy])
204
205
  else: