cognite-toolkit 0.6.97__py3-none-any.whl → 0.7.30__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.
Files changed (136) hide show
  1. cognite_toolkit/_cdf.py +16 -17
  2. cognite_toolkit/_cdf_tk/apps/__init__.py +2 -0
  3. cognite_toolkit/_cdf_tk/apps/_core_app.py +13 -5
  4. cognite_toolkit/_cdf_tk/apps/_data_app.py +1 -1
  5. cognite_toolkit/_cdf_tk/apps/_dev_app.py +86 -0
  6. cognite_toolkit/_cdf_tk/apps/_download_app.py +692 -24
  7. cognite_toolkit/_cdf_tk/apps/_dump_app.py +43 -101
  8. cognite_toolkit/_cdf_tk/apps/_landing_app.py +18 -4
  9. cognite_toolkit/_cdf_tk/apps/_migrate_app.py +249 -9
  10. cognite_toolkit/_cdf_tk/apps/_modules_app.py +0 -3
  11. cognite_toolkit/_cdf_tk/apps/_purge.py +15 -43
  12. cognite_toolkit/_cdf_tk/apps/_run.py +11 -0
  13. cognite_toolkit/_cdf_tk/apps/_upload_app.py +45 -6
  14. cognite_toolkit/_cdf_tk/builders/__init__.py +2 -2
  15. cognite_toolkit/_cdf_tk/builders/_base.py +28 -42
  16. cognite_toolkit/_cdf_tk/cdf_toml.py +20 -1
  17. cognite_toolkit/_cdf_tk/client/_toolkit_client.py +23 -3
  18. cognite_toolkit/_cdf_tk/client/api/extended_functions.py +6 -9
  19. cognite_toolkit/_cdf_tk/client/api/infield.py +93 -1
  20. cognite_toolkit/_cdf_tk/client/api/migration.py +175 -1
  21. cognite_toolkit/_cdf_tk/client/api/streams.py +84 -0
  22. cognite_toolkit/_cdf_tk/client/api/three_d.py +50 -0
  23. cognite_toolkit/_cdf_tk/client/data_classes/base.py +25 -1
  24. cognite_toolkit/_cdf_tk/client/data_classes/canvas.py +46 -3
  25. cognite_toolkit/_cdf_tk/client/data_classes/charts.py +3 -3
  26. cognite_toolkit/_cdf_tk/client/data_classes/charts_data.py +95 -213
  27. cognite_toolkit/_cdf_tk/client/data_classes/infield.py +32 -18
  28. cognite_toolkit/_cdf_tk/client/data_classes/migration.py +10 -2
  29. cognite_toolkit/_cdf_tk/client/data_classes/streams.py +90 -0
  30. cognite_toolkit/_cdf_tk/client/data_classes/three_d.py +47 -0
  31. cognite_toolkit/_cdf_tk/client/testing.py +18 -2
  32. cognite_toolkit/_cdf_tk/commands/__init__.py +6 -6
  33. cognite_toolkit/_cdf_tk/commands/_changes.py +3 -42
  34. cognite_toolkit/_cdf_tk/commands/_download.py +21 -11
  35. cognite_toolkit/_cdf_tk/commands/_migrate/__init__.py +0 -2
  36. cognite_toolkit/_cdf_tk/commands/_migrate/command.py +22 -20
  37. cognite_toolkit/_cdf_tk/commands/_migrate/conversion.py +133 -91
  38. cognite_toolkit/_cdf_tk/commands/_migrate/data_classes.py +73 -22
  39. cognite_toolkit/_cdf_tk/commands/_migrate/data_mapper.py +311 -43
  40. cognite_toolkit/_cdf_tk/commands/_migrate/default_mappings.py +5 -5
  41. cognite_toolkit/_cdf_tk/commands/_migrate/issues.py +33 -0
  42. cognite_toolkit/_cdf_tk/commands/_migrate/migration_io.py +157 -8
  43. cognite_toolkit/_cdf_tk/commands/_migrate/selectors.py +9 -4
  44. cognite_toolkit/_cdf_tk/commands/_purge.py +27 -28
  45. cognite_toolkit/_cdf_tk/commands/_questionary_style.py +16 -0
  46. cognite_toolkit/_cdf_tk/commands/_upload.py +109 -86
  47. cognite_toolkit/_cdf_tk/commands/about.py +221 -0
  48. cognite_toolkit/_cdf_tk/commands/auth.py +19 -12
  49. cognite_toolkit/_cdf_tk/commands/build_cmd.py +15 -61
  50. cognite_toolkit/_cdf_tk/commands/clean.py +63 -16
  51. cognite_toolkit/_cdf_tk/commands/deploy.py +20 -17
  52. cognite_toolkit/_cdf_tk/commands/dump_resource.py +6 -4
  53. cognite_toolkit/_cdf_tk/commands/init.py +225 -3
  54. cognite_toolkit/_cdf_tk/commands/modules.py +20 -44
  55. cognite_toolkit/_cdf_tk/commands/pull.py +6 -19
  56. cognite_toolkit/_cdf_tk/commands/resources.py +179 -0
  57. cognite_toolkit/_cdf_tk/constants.py +20 -1
  58. cognite_toolkit/_cdf_tk/cruds/__init__.py +19 -5
  59. cognite_toolkit/_cdf_tk/cruds/_base_cruds.py +14 -70
  60. cognite_toolkit/_cdf_tk/cruds/_data_cruds.py +8 -17
  61. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/__init__.py +4 -1
  62. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/agent.py +11 -9
  63. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/auth.py +4 -14
  64. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/classic.py +44 -43
  65. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/configuration.py +4 -11
  66. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/data_organization.py +4 -13
  67. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/datamodel.py +205 -66
  68. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/extraction_pipeline.py +5 -17
  69. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/fieldops.py +116 -27
  70. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/file.py +6 -27
  71. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/function.py +9 -28
  72. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/hosted_extractors.py +12 -30
  73. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/industrial_tool.py +3 -7
  74. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/location.py +3 -15
  75. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/migration.py +4 -12
  76. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/raw.py +4 -10
  77. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/relationship.py +3 -8
  78. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/robotics.py +15 -44
  79. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/streams.py +94 -0
  80. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/three_d_model.py +3 -7
  81. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/timeseries.py +5 -15
  82. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/transformation.py +39 -31
  83. cognite_toolkit/_cdf_tk/cruds/_resource_cruds/workflow.py +20 -40
  84. cognite_toolkit/_cdf_tk/cruds/_worker.py +24 -36
  85. cognite_toolkit/_cdf_tk/feature_flags.py +16 -36
  86. cognite_toolkit/_cdf_tk/plugins.py +2 -1
  87. cognite_toolkit/_cdf_tk/resource_classes/__init__.py +4 -0
  88. cognite_toolkit/_cdf_tk/resource_classes/capabilities.py +12 -0
  89. cognite_toolkit/_cdf_tk/resource_classes/functions.py +3 -1
  90. cognite_toolkit/_cdf_tk/resource_classes/infield_cdm_location_config.py +109 -0
  91. cognite_toolkit/_cdf_tk/resource_classes/migration.py +8 -17
  92. cognite_toolkit/_cdf_tk/resource_classes/streams.py +29 -0
  93. cognite_toolkit/_cdf_tk/storageio/__init__.py +9 -21
  94. cognite_toolkit/_cdf_tk/storageio/_annotations.py +19 -16
  95. cognite_toolkit/_cdf_tk/storageio/_applications.py +338 -26
  96. cognite_toolkit/_cdf_tk/storageio/_asset_centric.py +67 -104
  97. cognite_toolkit/_cdf_tk/storageio/_base.py +61 -29
  98. cognite_toolkit/_cdf_tk/storageio/_datapoints.py +276 -20
  99. cognite_toolkit/_cdf_tk/storageio/_file_content.py +436 -0
  100. cognite_toolkit/_cdf_tk/storageio/_instances.py +34 -2
  101. cognite_toolkit/_cdf_tk/storageio/_raw.py +26 -0
  102. cognite_toolkit/_cdf_tk/storageio/selectors/__init__.py +62 -4
  103. cognite_toolkit/_cdf_tk/storageio/selectors/_base.py +14 -2
  104. cognite_toolkit/_cdf_tk/storageio/selectors/_canvas.py +14 -0
  105. cognite_toolkit/_cdf_tk/storageio/selectors/_charts.py +14 -0
  106. cognite_toolkit/_cdf_tk/storageio/selectors/_datapoints.py +23 -3
  107. cognite_toolkit/_cdf_tk/storageio/selectors/_file_content.py +164 -0
  108. cognite_toolkit/_cdf_tk/tk_warnings/other.py +4 -0
  109. cognite_toolkit/_cdf_tk/tracker.py +2 -2
  110. cognite_toolkit/_cdf_tk/utils/dtype_conversion.py +9 -3
  111. cognite_toolkit/_cdf_tk/utils/fileio/__init__.py +2 -0
  112. cognite_toolkit/_cdf_tk/utils/fileio/_base.py +5 -1
  113. cognite_toolkit/_cdf_tk/utils/fileio/_readers.py +112 -20
  114. cognite_toolkit/_cdf_tk/utils/fileio/_writers.py +15 -15
  115. cognite_toolkit/_cdf_tk/utils/http_client/_client.py +284 -18
  116. cognite_toolkit/_cdf_tk/utils/http_client/_data_classes.py +50 -4
  117. cognite_toolkit/_cdf_tk/utils/http_client/_data_classes2.py +187 -0
  118. cognite_toolkit/_cdf_tk/utils/interactive_select.py +9 -14
  119. cognite_toolkit/_cdf_tk/utils/sql_parser.py +2 -3
  120. cognite_toolkit/_cdf_tk/utils/useful_types.py +6 -2
  121. cognite_toolkit/_cdf_tk/validation.py +79 -1
  122. cognite_toolkit/_repo_files/GitHub/.github/workflows/deploy.yaml +1 -1
  123. cognite_toolkit/_repo_files/GitHub/.github/workflows/dry-run.yaml +1 -1
  124. cognite_toolkit/_resources/cdf.toml +5 -4
  125. cognite_toolkit/_version.py +1 -1
  126. cognite_toolkit/config.dev.yaml +13 -0
  127. {cognite_toolkit-0.6.97.dist-info → cognite_toolkit-0.7.30.dist-info}/METADATA +24 -24
  128. {cognite_toolkit-0.6.97.dist-info → cognite_toolkit-0.7.30.dist-info}/RECORD +153 -143
  129. cognite_toolkit-0.7.30.dist-info/WHEEL +4 -0
  130. {cognite_toolkit-0.6.97.dist-info → cognite_toolkit-0.7.30.dist-info}/entry_points.txt +1 -0
  131. cognite_toolkit/_cdf_tk/commands/_migrate/canvas.py +0 -201
  132. cognite_toolkit/_cdf_tk/commands/dump_data.py +0 -489
  133. cognite_toolkit/_cdf_tk/commands/featureflag.py +0 -27
  134. cognite_toolkit/_cdf_tk/utils/table_writers.py +0 -434
  135. cognite_toolkit-0.6.97.dist-info/WHEEL +0 -4
  136. cognite_toolkit-0.6.97.dist-info/licenses/LICENSE +0 -18
@@ -0,0 +1,221 @@
1
+ import platform
2
+ import sys
3
+ from pathlib import Path
4
+
5
+ from rich import print
6
+ from rich.table import Table
7
+
8
+ from cognite_toolkit._cdf_tk.cdf_toml import CDFToml, _read_toml
9
+ from cognite_toolkit._cdf_tk.commands._base import ToolkitCommand
10
+ from cognite_toolkit._cdf_tk.constants import clean_name
11
+ from cognite_toolkit._cdf_tk.feature_flags import Flags
12
+ from cognite_toolkit._cdf_tk.plugins import Plugins
13
+ from cognite_toolkit._cdf_tk.tk_warnings import LowSeverityWarning, MediumSeverityWarning
14
+ from cognite_toolkit._version import __version__
15
+
16
+
17
+ class AboutCommand(ToolkitCommand):
18
+ def execute(self, cwd: Path) -> None:
19
+ # Version information
20
+ print(f"\n[bold cyan]Cognite Toolkit[/bold cyan] version: [yellow]{__version__}[/yellow]")
21
+ print(f"Python version: {sys.version.split()[0]}")
22
+ print(f"Platform: {platform.system()} {platform.release()}")
23
+
24
+ # Check for cdf.toml in the current directory
25
+ cdf_toml_path = cwd / CDFToml.file_name
26
+
27
+ if cdf_toml_path.exists():
28
+ print(f"\n[bold green]Configuration file found:[/bold green] {cdf_toml_path}")
29
+
30
+ cdf_toml = CDFToml.load(cwd)
31
+
32
+ # We need to read the raw TOML to get original key names for plugins and alpha flags
33
+ raw_toml = _read_toml(cdf_toml_path)
34
+
35
+ self._check_unrecognized_sections(raw_toml)
36
+ self._display_plugins(cdf_toml, raw_toml)
37
+ self._display_alpha_flags(cdf_toml, raw_toml)
38
+ self._display_additional_config(cdf_toml)
39
+
40
+ else:
41
+ # Search for cdf.toml in subdirectories
42
+ found_files = self._search_cdf_toml(cwd)
43
+
44
+ if found_files:
45
+ print(f"\n[bold yellow]No cdf.toml found in current directory:[/bold yellow] {cwd}")
46
+ print("\n[bold]Found cdf.toml files in subdirectories:[/bold]")
47
+ for file in found_files:
48
+ rel_path = file.relative_to(cwd)
49
+ print(f" • {rel_path}")
50
+ print(f"\n[bold cyan]Hint:[/bold cyan] Move one of these files to {cwd} or navigate to its directory.")
51
+ else:
52
+ print("\n[bold yellow]No cdf.toml found[/bold yellow] in current directory or subdirectories.")
53
+ print(f"Current directory: {cwd}")
54
+ print("\n[bold cyan]Hint:[/bold cyan] Run [yellow]cdf init[/yellow] to create a new project.")
55
+
56
+ def _check_unrecognized_sections(self, raw_toml: dict) -> None:
57
+ """Check for unrecognized tables in cdf.toml and warn about them."""
58
+ # Valid top-level tables in cdf.toml
59
+ valid_tables = {"cdf", "modules", "alpha_flags", "feature_flags", "plugins", "library"}
60
+
61
+ # Filter out empty keys, whitespace-only keys, and check for unrecognized tables
62
+ unrecognized_tables = [key for key in raw_toml.keys() if key and key.strip() and key not in valid_tables]
63
+
64
+ if unrecognized_tables:
65
+ print()
66
+
67
+ for table in unrecognized_tables:
68
+ # Try to find a matching valid table by stripping non-alphabetical characters
69
+ suggestion = self._find_similar_table(table, valid_tables)
70
+
71
+ message = f"Table '{table}' in cdf.toml is not recognized and will have no effect."
72
+ if suggestion:
73
+ message += f" Did you mean '{suggestion}'?"
74
+
75
+ self.warn(MediumSeverityWarning(message))
76
+
77
+ @staticmethod
78
+ def _find_similar_table(unrecognized: str, valid_tables: set[str]) -> str | None:
79
+ """Find a similar valid table by comparing alphabetical characters only.
80
+
81
+ Returns None if the unrecognized table is already valid or if no similar match is found.
82
+ """
83
+ # If it's already a valid table, return None (no suggestion needed)
84
+ if unrecognized in valid_tables:
85
+ return None
86
+
87
+ # Keep only alphabetical characters and lowercase
88
+ normalized_unrecognized = "".join(c for c in unrecognized if c.isalpha()).lower()
89
+
90
+ # First, try exact match (after normalization)
91
+ for valid in valid_tables:
92
+ normalized_valid = "".join(c for c in valid if c.isalpha()).lower()
93
+ if normalized_unrecognized == normalized_valid:
94
+ return valid
95
+
96
+ # If no match, check for singular/plural variations (missing 's')
97
+ for valid in valid_tables:
98
+ normalized_valid = "".join(c for c in valid if c.isalpha()).lower()
99
+
100
+ # Check if adding 's' to unrecognized matches valid (e.g., "plugin" -> "plugins")
101
+ if normalized_unrecognized + "s" == normalized_valid:
102
+ return valid
103
+
104
+ return None
105
+
106
+ def _display_plugins(self, cdf_toml: CDFToml, raw_toml: dict) -> None:
107
+ """Display all available plugins and their status."""
108
+ table = Table(title="Plugins", show_header=True)
109
+ table.add_column("Plugin", justify="left", style="cyan")
110
+ table.add_column("Status", justify="center")
111
+ table.add_column("Description", justify="left")
112
+
113
+ # Track which plugins we've seen
114
+ seen_plugins = set()
115
+
116
+ # Show all plugins from the enum
117
+ for plugin in Plugins:
118
+ plugin_name = plugin.value.name
119
+ cleaned_key = clean_name(plugin_name)
120
+ seen_plugins.add(cleaned_key)
121
+
122
+ is_enabled = cdf_toml.plugins.get(cleaned_key, False)
123
+ if is_enabled:
124
+ status = "[green]✓ enabled[/green]"
125
+ else:
126
+ status = "[dim]○ disabled[/dim]"
127
+
128
+ table.add_row(plugin_name, status, plugin.value.description)
129
+
130
+ print()
131
+ print(table)
132
+
133
+ # Show any unrecognized plugins from cdf.toml using original key names
134
+ raw_plugins = raw_toml.get("plugins", {})
135
+ unrecognized = []
136
+ for original_key, value in raw_plugins.items():
137
+ cleaned_key = clean_name(original_key)
138
+ if cleaned_key not in seen_plugins:
139
+ unrecognized.append((original_key, value))
140
+
141
+ for original_key, is_enabled in unrecognized:
142
+ status = "enabled" if is_enabled else "disabled"
143
+ self.warn(
144
+ LowSeverityWarning(f"Plugin '{original_key}' in cdf.toml is not recognized and will have no effect.")
145
+ )
146
+
147
+ def _display_alpha_flags(self, cdf_toml: CDFToml, raw_toml: dict) -> None:
148
+ """Display available alpha flags and their status."""
149
+ table = Table(title="Alpha Flags", show_header=True)
150
+ table.add_column("Flag", justify="left", style="yellow")
151
+ table.add_column("Status", justify="center")
152
+ table.add_column("Description", justify="left")
153
+
154
+ # Track which flags we've seen
155
+ seen_flags = set()
156
+
157
+ # Show flags from the enum that are either enabled or visible
158
+ for flag in Flags:
159
+ cleaned_key = clean_name(flag.name)
160
+ seen_flags.add(cleaned_key)
161
+
162
+ is_enabled = cdf_toml.alpha_flags.get(cleaned_key, False)
163
+
164
+ # Only show if enabled or visible
165
+ if is_enabled or flag.value.visible:
166
+ # Convert enum name to kebab-case for display
167
+ display_name = flag.name.lower().replace("_", "-")
168
+
169
+ if is_enabled:
170
+ status = "[green]✓ enabled[/green]"
171
+ else:
172
+ status = "[dim]○ disabled[/dim]"
173
+
174
+ table.add_row(display_name, status, flag.value.description)
175
+
176
+ print()
177
+ print(table)
178
+
179
+ # Show any unrecognized flags from cdf.toml using original key names
180
+ raw_flags = raw_toml.get("alpha_flags", {})
181
+ unrecognized = []
182
+ for original_key, value in raw_flags.items():
183
+ cleaned_key = clean_name(original_key)
184
+ if cleaned_key not in seen_flags:
185
+ unrecognized.append((original_key, value))
186
+
187
+ for original_key, is_enabled in unrecognized:
188
+ status = "enabled" if is_enabled else "disabled"
189
+ self.warn(
190
+ LowSeverityWarning(
191
+ f"Alpha flag '{original_key}' in cdf.toml is not recognized and will have no effect."
192
+ )
193
+ )
194
+
195
+ def _display_additional_config(self, cdf_toml: CDFToml) -> None:
196
+ """Display additional configuration information."""
197
+ print("\n[bold]Additional Configuration:[/bold]")
198
+
199
+ print(f" Default environment: [cyan]{cdf_toml.cdf.default_env}[/cyan]")
200
+
201
+ if cdf_toml.cdf.has_user_set_default_org:
202
+ print(f" Default organization dir: [cyan]{cdf_toml.cdf.default_organization_dir}[/cyan]")
203
+
204
+ if cdf_toml.cdf.file_encoding:
205
+ print(f" File encoding: [cyan]{cdf_toml.cdf.file_encoding}[/cyan]")
206
+
207
+ print(f" Modules version: [cyan]{cdf_toml.modules.version}[/cyan]")
208
+
209
+ if cdf_toml.libraries:
210
+ print(f" Configured libraries: [cyan]{len(cdf_toml.libraries)}[/cyan]")
211
+ for lib_name, lib_config in cdf_toml.libraries.items():
212
+ print(f" • {lib_name}: [dim]{lib_config.url}[/dim]")
213
+
214
+ def _search_cdf_toml(self, cwd: Path) -> list[Path]:
215
+ """Search for cdf.toml files in immediate subdirectories (one level down)."""
216
+ try:
217
+ return sorted(
218
+ [potential_file for potential_file in cwd.glob(f"*/{CDFToml.file_name}") if potential_file.is_file()]
219
+ )
220
+ except PermissionError:
221
+ return []
@@ -26,6 +26,7 @@ import questionary
26
26
  from cognite.client.data_classes.capabilities import (
27
27
  AssetsAcl,
28
28
  Capability,
29
+ ExtractionConfigsAcl,
29
30
  FunctionsAcl,
30
31
  GroupsAcl,
31
32
  ProjectsAcl,
@@ -46,6 +47,7 @@ from cognite_toolkit._cdf_tk.constants import (
46
47
  TOOLKIT_DEMO_GROUP_NAME,
47
48
  TOOLKIT_SERVICE_PRINCIPAL_GROUP_NAME,
48
49
  )
50
+ from cognite_toolkit._cdf_tk.cruds import ExtractionPipelineConfigCRUD
49
51
  from cognite_toolkit._cdf_tk.exceptions import (
50
52
  AuthenticationError,
51
53
  AuthorizationError,
@@ -95,16 +97,6 @@ class AuthCommand(ToolkitCommand):
95
97
  raise AuthenticationError(f"Unable to verify the credentials.\n{e}")
96
98
 
97
99
  print("[green]The credentials are valid.[/green]")
98
- if no_verify:
99
- return
100
- print(
101
- Panel(
102
- "Running verification, 'cdf auth verify'...",
103
- title="",
104
- expand=False,
105
- )
106
- )
107
- self.verify(client, dry_run)
108
100
 
109
101
  def _store_dotenv(self, env_vars: EnvironmentVariables) -> None:
110
102
  new_env_file = env_vars.create_dotenv_file()
@@ -444,8 +436,23 @@ class AuthCommand(ToolkitCommand):
444
436
  crud = crud_cls.create_loader(client)
445
437
  if crud.prerequisite_warning() is not None:
446
438
  continue
447
- capability = crud_cls.get_required_capability(None, read_only=False)
448
- capabilities = capability if isinstance(capability, list) else [capability]
439
+ if isinstance(crud, ExtractionPipelineConfigCRUD):
440
+ # The Extraction Pipeline Config CRUD requires special handling.
441
+ # The .get_required_capability is used in the DeployCommand as well. Since, there is no way to no
442
+ # the extraction pipeline ID or dataSetId from an ExtractionPipelineConfigWrite object, we do not
443
+ # check those there. If we returned the full capability, it would always have to be all scoped.
444
+ # That is too restrictive in the deploy command, so we return an empty list, essentially not checking
445
+ # anything there. Here, we want to add the all scoped capability, so that the Toolkit group gets the
446
+ # correct capability.
447
+ capabilities: list[Capability] = [
448
+ ExtractionConfigsAcl(
449
+ [ExtractionConfigsAcl.Action.Read, ExtractionConfigsAcl.Action.Write],
450
+ ExtractionConfigsAcl.Scope.All(),
451
+ )
452
+ ]
453
+ else:
454
+ capability = crud_cls.get_required_capability(None, read_only=False)
455
+ capabilities = capability if isinstance(capability, list) else [capability]
449
456
  for cap in capabilities:
450
457
  if project_type == "DATA_MODELING_ONLY" and isinstance(cap, AssetsAcl | RelationshipsAcl):
451
458
  continue
@@ -20,7 +20,6 @@ from cognite_toolkit._cdf_tk.constants import (
20
20
  _RUNNING_IN_BROWSER,
21
21
  BUILD_FOLDER_ENCODING,
22
22
  DEFAULT_ENV,
23
- DEV_ONLY_MODULES,
24
23
  HINT_LEAD_TEXT,
25
24
  ROOT_MODULES,
26
25
  TEMPLATE_VARS_FILE_SUFFIXES,
@@ -59,10 +58,7 @@ from cognite_toolkit._cdf_tk.data_classes import (
59
58
  SourceLocationLazy,
60
59
  )
61
60
  from cognite_toolkit._cdf_tk.exceptions import (
62
- ToolkitDuplicatedModuleError,
63
- ToolkitEnvError,
64
61
  ToolkitError,
65
- ToolkitMissingModuleError,
66
62
  ToolkitYAMLFormatError,
67
63
  )
68
64
  from cognite_toolkit._cdf_tk.hints import Hint, ModuleDefinition, verify_module_directory
@@ -89,6 +85,7 @@ from cognite_toolkit._cdf_tk.utils.file import safe_rmtree
89
85
  from cognite_toolkit._cdf_tk.utils.modules import parse_user_selected_modules
90
86
  from cognite_toolkit._cdf_tk.validation import (
91
87
  validate_data_set_is_set,
88
+ validate_module_selection,
92
89
  validate_modules_variables,
93
90
  validate_resource_yaml_pydantic,
94
91
  )
@@ -207,7 +204,11 @@ class BuildCommand(ToolkitCommand):
207
204
 
208
205
  user_selected_modules = config.environment.get_selected_modules(packages)
209
206
  modules = ModuleDirectories.load(organization_dir, user_selected_modules)
210
- self._validate_modules(modules, config, packages, user_selected_modules, organization_dir)
207
+ module_warnings = validate_module_selection(modules, config, packages, user_selected_modules, organization_dir)
208
+ if module_warnings:
209
+ self.warning_list.extend(module_warnings)
210
+ if self.print_warning:
211
+ print(str(module_warnings))
211
212
 
212
213
  if verbose:
213
214
  self.console("Selected packages:")
@@ -358,6 +359,15 @@ class BuildCommand(ToolkitCommand):
358
359
  for resource_name, resource_files in module.source_paths_by_resource_folder.items():
359
360
  source_files = self._replace_variables(resource_files, module_variables, resource_name, module.dir, verbose)
360
361
 
362
+ if resource_name == "data_models":
363
+ resource_name = "data_modeling"
364
+ self.warn(
365
+ MediumSeverityWarning(
366
+ "The resource folder 'data_models' is deprecated and will be removed in v1.0. "
367
+ "Please rename the folder to 'data_modeling'."
368
+ )
369
+ )
370
+
361
371
  builder = self._get_builder(build_dir, resource_name)
362
372
 
363
373
  built_resources = BuiltResourceList[Hashable]()
@@ -413,62 +423,6 @@ class BuildCommand(ToolkitCommand):
413
423
  builder = self._builder_by_resource_folder[resource_name]
414
424
  return builder
415
425
 
416
- def _validate_modules(
417
- self,
418
- modules: ModuleDirectories,
419
- config: BuildConfigYAML,
420
- packages: dict[str, list[str]],
421
- selected_modules: set[str | Path],
422
- organization_dir: Path,
423
- ) -> None:
424
- # Validations: Ambiguous selection.
425
- selected_names = {s for s in config.environment.selected if isinstance(s, str)}
426
- if duplicate_modules := {
427
- module_name: paths
428
- for module_name, paths in modules.as_path_by_name().items()
429
- if len(paths) > 1 and module_name in selected_names
430
- }:
431
- # If the user has selected a module by name, and there are multiple modules with that name, raise an error.
432
- # Note, if the user uses a path to select a module, this error will not be raised.
433
- raise ToolkitDuplicatedModuleError(
434
- f"Ambiguous module selected in config.{config.environment.name}.yaml:", duplicate_modules
435
- )
436
- # Package Referenced Modules Exists
437
- for package, package_modules in packages.items():
438
- if package not in selected_names:
439
- # We do not check packages that are not selected.
440
- # Typically, the user will delete the modules that are irrelevant for them;
441
- # thus we only check the selected packages.
442
- continue
443
- if missing_packages := set(package_modules) - modules.available_names:
444
- ToolkitMissingModuleError(
445
- f"Package {package} defined in {CDFToml.file_name!s} is referring "
446
- f"the following missing modules {missing_packages}."
447
- )
448
-
449
- # Selected modules does not exists
450
- if missing_modules := set(selected_modules) - modules.available:
451
- hint = ModuleDefinition.long(missing_modules, organization_dir)
452
- raise ToolkitMissingModuleError(
453
- f"The following selected modules are missing, please check path: {missing_modules}.\n{hint}"
454
- )
455
-
456
- # Nothing is Selected
457
- if not modules.selected:
458
- raise ToolkitEnvError(
459
- f"No selected modules specified in {config.filepath!s}, have you configured "
460
- f"the environment ({config.environment.name})?"
461
- )
462
-
463
- dev_modules = modules.available_names & DEV_ONLY_MODULES
464
- if dev_modules and config.environment.validation_type != "dev":
465
- self.warn(
466
- MediumSeverityWarning(
467
- "The following modules should [bold]only[/bold] be used a in CDF Projects designated as dev (development): "
468
- f"{humanize_collection(dev_modules)!r}",
469
- )
470
- )
471
-
472
426
  def _replace_variables(
473
427
  self,
474
428
  resource_files: Sequence[Path],
@@ -1,7 +1,9 @@
1
1
  import traceback
2
+ from collections.abc import Sequence
2
3
  from graphlib import TopologicalSorter
3
4
  from pathlib import Path
4
5
 
6
+ import questionary
5
7
  from cognite.client.exceptions import CogniteAPIError, CogniteNotFoundError
6
8
  from cognite.client.utils.useful_types import SequenceNotStr
7
9
  from rich import print
@@ -23,14 +25,7 @@ from cognite_toolkit._cdf_tk.cruds import (
23
25
  ResourceCRUD,
24
26
  ResourceWorker,
25
27
  )
26
- from cognite_toolkit._cdf_tk.cruds._base_cruds import (
27
- T_ID,
28
- Loader,
29
- T_ResourceRequest,
30
- T_ResourceRequestList,
31
- T_ResourceResponse,
32
- T_ResourceResponseList,
33
- )
28
+ from cognite_toolkit._cdf_tk.cruds._base_cruds import Loader
34
29
  from cognite_toolkit._cdf_tk.data_classes import (
35
30
  BuildEnvironment,
36
31
  DeployResults,
@@ -40,9 +35,12 @@ from cognite_toolkit._cdf_tk.data_classes import (
40
35
  from cognite_toolkit._cdf_tk.data_classes._module_directories import ReadModule
41
36
  from cognite_toolkit._cdf_tk.exceptions import (
42
37
  ToolkitCleanResourceError,
38
+ ToolkitMissingModuleError,
43
39
  ToolkitNotADirectoryError,
44
40
  ToolkitValidationError,
41
+ ToolkitValueError,
45
42
  )
43
+ from cognite_toolkit._cdf_tk.protocols import T_ResourceRequest, T_ResourceResponse
46
44
  from cognite_toolkit._cdf_tk.tk_warnings import (
47
45
  LowSeverityWarning,
48
46
  MediumSeverityWarning,
@@ -54,6 +52,7 @@ from cognite_toolkit._cdf_tk.utils import (
54
52
  read_yaml_file,
55
53
  )
56
54
  from cognite_toolkit._cdf_tk.utils.auth import EnvironmentVariables
55
+ from cognite_toolkit._cdf_tk.utils.useful_types import T_ID
57
56
 
58
57
  from ._utils import _print_ids_or_length
59
58
 
@@ -63,9 +62,7 @@ AVAILABLE_DATA_TYPES: tuple[str, ...] = tuple(CRUDS_BY_FOLDER_NAME)
63
62
  class CleanCommand(ToolkitCommand):
64
63
  def clean_resources(
65
64
  self,
66
- loader: ResourceCRUD[
67
- T_ID, T_ResourceRequest, T_ResourceResponse, T_ResourceRequestList, T_ResourceResponseList
68
- ],
65
+ loader: ResourceCRUD[T_ID, T_ResourceRequest, T_ResourceResponse],
69
66
  env_vars: EnvironmentVariables,
70
67
  read_modules: list[ReadModule],
71
68
  dry_run: bool = False,
@@ -136,7 +133,7 @@ class CleanCommand(ToolkitCommand):
136
133
  return ResourceDeployResult(name=loader.display_name)
137
134
 
138
135
  def _delete_resources(
139
- self, loaded_resources: T_ResourceResponseList, loader: ResourceCRUD, dry_run: bool, verbose: bool
136
+ self, loaded_resources: Sequence[T_ResourceResponse], loader: ResourceCRUD, dry_run: bool, verbose: bool
140
137
  ) -> int:
141
138
  nr_of_deleted = 0
142
139
  resource_ids = loader.get_ids(loaded_resources)
@@ -161,7 +158,11 @@ class CleanCommand(ToolkitCommand):
161
158
  return nr_of_deleted
162
159
 
163
160
  def _drop_data(
164
- self, loaded_resources: T_ResourceResponseList, loader: ResourceContainerCRUD, dry_run: bool, verbose: bool
161
+ self,
162
+ loaded_resources: Sequence[T_ResourceResponse],
163
+ loader: ResourceContainerCRUD,
164
+ dry_run: bool,
165
+ verbose: bool,
165
166
  ) -> int:
166
167
  nr_of_dropped = 0
167
168
  resource_ids = loader.get_ids(loaded_resources)
@@ -185,6 +186,23 @@ class CleanCommand(ToolkitCommand):
185
186
  self._verbose_print_drop(resource_drop_count, resource_ids, loader, dry_run)
186
187
  return nr_of_dropped
187
188
 
189
+ def _interactive_module_selection(self, built_modules: list[ReadModule] | None) -> list[ReadModule] | None:
190
+ if not built_modules:
191
+ return None
192
+ choices = [
193
+ questionary.Choice(title=built_module.dir.name, value=built_module) for built_module in built_modules
194
+ ]
195
+
196
+ selected_modules = questionary.checkbox(
197
+ "Which modules would you like to clean?",
198
+ instruction="Use arrow up/down, press space to select item(s) and enter to save",
199
+ choices=choices,
200
+ ).ask()
201
+
202
+ if not selected_modules:
203
+ return None
204
+ return selected_modules
205
+
188
206
  def _verbose_print_drop(
189
207
  self, drop_count: int, resource_ids: SequenceNotStr[T_ID], loader: ResourceContainerCRUD, dry_run: bool
190
208
  ) -> None:
@@ -204,6 +222,11 @@ class CleanCommand(ToolkitCommand):
204
222
  # Count is not supported
205
223
  print(f" {prefix} all {loader.item_name} from {loader.display_name}: {_print_ids_or_length(resource_ids)}.")
206
224
 
225
+ def _select_modules(self, clean_state: BuildEnvironment, module_str: str | None) -> list[ReadModule] | None:
226
+ if module_str:
227
+ return [module for module in clean_state.read_modules if module.dir.name == module_str]
228
+ return self._interactive_module_selection(clean_state.read_modules)
229
+
207
230
  def execute(
208
231
  self,
209
232
  env_vars: EnvironmentVariables,
@@ -211,7 +234,9 @@ class CleanCommand(ToolkitCommand):
211
234
  build_env_name: str | None,
212
235
  dry_run: bool,
213
236
  include: list[str] | None,
237
+ module_str: str | None,
214
238
  verbose: bool,
239
+ all_modules: bool = False,
215
240
  ) -> None:
216
241
  if not build_dir.exists():
217
242
  raise ToolkitNotADirectoryError(
@@ -245,9 +270,31 @@ class CleanCommand(ToolkitCommand):
245
270
  )
246
271
 
247
272
  if not build_dir.is_dir():
248
- raise ToolkitNotADirectoryError(f"'{build_dir}'. Did you forget to run `cdf-tk build` first?")
273
+ raise ToolkitNotADirectoryError(f"'{build_dir}'. Did you forget to run `cdf build` first?")
274
+
275
+ selected_modules: list[ReadModule]
276
+ if all_modules:
277
+ selected_modules = clean_state.read_modules or []
278
+ if not selected_modules:
279
+ raise ToolkitValueError("No modules available to clean.")
280
+ elif module_str:
281
+ selected_modules = [module for module in clean_state.read_modules if module.dir.name == module_str]
282
+ if not selected_modules:
283
+ available_module_names = {module.dir.name for module in clean_state.read_modules}
284
+ raise ToolkitMissingModuleError(
285
+ f"No modules matched the selection: {module_str}. Available modules: {sorted(available_module_names)}"
286
+ )
287
+ else:
288
+ selected_modules = self._interactive_module_selection(clean_state.read_modules) or []
289
+ if not selected_modules:
290
+ raise ToolkitValueError(
291
+ "No module specified with the --module option and no modules selected interactively."
292
+ )
249
293
 
250
- selected_loaders = self.get_selected_loaders(build_dir, clean_state.read_resource_folders, include)
294
+ selected_resource_folders = {
295
+ resource_folder for module in selected_modules for resource_folder in module.resource_directories
296
+ }
297
+ selected_loaders = self.get_selected_loaders(build_dir, selected_resource_folders, include)
251
298
 
252
299
  results = DeployResults([], "clean", dry_run=dry_run)
253
300
 
@@ -274,7 +321,7 @@ class CleanCommand(ToolkitCommand):
274
321
  result = self.clean_resources(
275
322
  loader,
276
323
  env_vars=env_vars,
277
- read_modules=clean_state.read_modules,
324
+ read_modules=selected_modules,
278
325
  drop=True,
279
326
  dry_run=dry_run,
280
327
  drop_data=True,
@@ -1,4 +1,4 @@
1
- from collections.abc import Hashable
1
+ from collections.abc import Hashable, Sequence
2
2
  from graphlib import TopologicalSorter
3
3
  from pathlib import Path
4
4
  from typing import overload
@@ -15,6 +15,7 @@ from cognite_toolkit._cdf_tk.commands.clean import CleanCommand
15
15
  from cognite_toolkit._cdf_tk.constants import (
16
16
  _RUNNING_IN_BROWSER,
17
17
  BUILD_ENVIRONMENT_FILE,
18
+ DATA_UPLOAD_URL,
18
19
  HINT_LEAD_TEXT,
19
20
  )
20
21
  from cognite_toolkit._cdf_tk.cruds import (
@@ -45,13 +46,12 @@ from cognite_toolkit._cdf_tk.exceptions import (
45
46
  )
46
47
  from cognite_toolkit._cdf_tk.protocols import (
47
48
  T_ResourceRequest,
48
- T_ResourceRequestList,
49
49
  T_ResourceResponse,
50
- T_ResourceResponseList,
51
50
  )
52
51
  from cognite_toolkit._cdf_tk.tk_warnings import EnvironmentVariableMissingWarning
53
52
  from cognite_toolkit._cdf_tk.tk_warnings.base import WarningList, catch_warnings
54
53
  from cognite_toolkit._cdf_tk.tk_warnings.other import (
54
+ HighSeverityWarning,
55
55
  LowSeverityWarning,
56
56
  ToolkitDependenciesIncludedWarning,
57
57
  )
@@ -292,6 +292,15 @@ class DeployCommand(ToolkitCommand):
292
292
  read_modules = build.read_modules
293
293
  output_results = DeployResults([], "deploy", dry_run=dry_run) if results is None else results
294
294
  for loader_cls in ordered_loaders:
295
+ if issubclass(loader_cls, DataCRUD):
296
+ self.warn(
297
+ HighSeverityWarning(
298
+ f"Uploading {loader_cls.kind} data is deprecated and will be removed in v0.8. "
299
+ f"Use the `cdf data upload dir` command instead. See [{DATA_UPLOAD_URL}]({DATA_UPLOAD_URL}) for more information about "
300
+ "the upload command."
301
+ )
302
+ )
303
+
295
304
  loader = loader_cls.create_loader(client, build_dir)
296
305
  resource_result: DeployResult | None
297
306
  if isinstance(loader, ResourceCRUD):
@@ -327,9 +336,7 @@ class DeployCommand(ToolkitCommand):
327
336
 
328
337
  def deploy_resource_type(
329
338
  self,
330
- loader: ResourceCRUD[
331
- T_ID, T_ResourceRequest, T_ResourceResponse, T_ResourceRequestList, T_ResourceResponseList
332
- ],
339
+ loader: ResourceCRUD[T_ID, T_ResourceRequest, T_ResourceResponse],
333
340
  env_vars: EnvironmentVariables,
334
341
  read_modules: list[ReadModule] | None = None,
335
342
  dry_run: bool = False,
@@ -391,10 +398,8 @@ class DeployCommand(ToolkitCommand):
391
398
 
392
399
  def actual_deploy(
393
400
  self,
394
- resources: CategorizedResources[T_ID, T_ResourceResponseList],
395
- loader: ResourceCRUD[
396
- T_ID, T_ResourceRequest, T_ResourceResponse, T_ResourceRequestList, T_ResourceResponseList
397
- ],
401
+ resources: CategorizedResources[T_ID, T_ResourceRequest],
402
+ loader: ResourceCRUD[T_ID, T_ResourceRequest, T_ResourceResponse],
398
403
  env_var_warnings: WarningList | None = None,
399
404
  ) -> ResourceDeployResult:
400
405
  environment_variable_warning_by_id = {
@@ -425,10 +430,8 @@ class DeployCommand(ToolkitCommand):
425
430
 
426
431
  @staticmethod
427
432
  def dry_run_deploy(
428
- resources: CategorizedResources[T_ID, T_ResourceResponseList],
429
- loader: ResourceCRUD[
430
- T_ID, T_ResourceRequest, T_ResourceResponse, T_ResourceRequestList, T_ResourceResponseList
431
- ],
433
+ resources: CategorizedResources[T_ID, T_ResourceRequest],
434
+ loader: ResourceCRUD[T_ID, T_ResourceRequest, T_ResourceResponse],
432
435
  has_done_drop: bool,
433
436
  has_dropped_data: bool,
434
437
  ) -> ResourceDeployResult:
@@ -459,7 +462,7 @@ class DeployCommand(ToolkitCommand):
459
462
 
460
463
  @staticmethod
461
464
  def _verbose_print(
462
- resources: CategorizedResources[T_ID, T_ResourceResponseList],
465
+ resources: CategorizedResources[T_ID, T_ResourceRequest],
463
466
  loader: ResourceCRUD,
464
467
  dry_run: bool,
465
468
  ) -> None:
@@ -483,7 +486,7 @@ class DeployCommand(ToolkitCommand):
483
486
 
484
487
  def _create_resources(
485
488
  self,
486
- resources: T_ResourceResponseList,
489
+ resources: Sequence[T_ResourceRequest],
487
490
  loader: ResourceCRUD,
488
491
  environment_variable_warning_by_id: dict[Hashable, EnvironmentVariableMissingWarning],
489
492
  ) -> int:
@@ -506,7 +509,7 @@ class DeployCommand(ToolkitCommand):
506
509
 
507
510
  def _update_resources(
508
511
  self,
509
- resources: T_ResourceResponseList,
512
+ resources: Sequence[T_ResourceRequest],
510
513
  loader: ResourceCRUD,
511
514
  environment_variable_warning_by_id: dict[Hashable, EnvironmentVariableMissingWarning],
512
515
  ) -> int: