cognite-neat 0.104.0__py3-none-any.whl → 0.105.1__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-neat might be problematic. Click here for more details.

Files changed (143) hide show
  1. cognite/neat/_client/_api/data_modeling_loaders.py +83 -23
  2. cognite/neat/_client/_api/schema.py +2 -1
  3. cognite/neat/_client/data_classes/neat_sequence.py +261 -0
  4. cognite/neat/_client/data_classes/schema.py +5 -1
  5. cognite/neat/_client/testing.py +33 -0
  6. cognite/neat/_constants.py +57 -0
  7. cognite/neat/_graph/extractors/_classic_cdf/_base.py +6 -5
  8. cognite/neat/_graph/extractors/_classic_cdf/_sequences.py +225 -11
  9. cognite/neat/_graph/extractors/_mock_graph_generator.py +1 -1
  10. cognite/neat/_graph/loaders/_rdf2dms.py +31 -5
  11. cognite/neat/_graph/transformers/__init__.py +3 -1
  12. cognite/neat/_graph/transformers/_classic_cdf.py +39 -51
  13. cognite/neat/_graph/transformers/_rdfpath.py +14 -15
  14. cognite/neat/_graph/transformers/_value_type.py +72 -0
  15. cognite/neat/_issues/__init__.py +0 -2
  16. cognite/neat/_issues/_base.py +19 -35
  17. cognite/neat/_issues/warnings/__init__.py +6 -1
  18. cognite/neat/_issues/warnings/_general.py +7 -0
  19. cognite/neat/_issues/warnings/_properties.py +11 -0
  20. cognite/neat/_issues/warnings/_resources.py +11 -0
  21. cognite/neat/_rules/exporters/_rules2dms.py +35 -1
  22. cognite/neat/_rules/exporters/_rules2excel.py +2 -2
  23. cognite/neat/_rules/importers/_dms2rules.py +66 -55
  24. cognite/neat/_rules/models/_base_rules.py +4 -1
  25. cognite/neat/_rules/models/entities/_wrapped.py +10 -5
  26. cognite/neat/_rules/models/mapping/_classic2core.yaml +239 -38
  27. cognite/neat/_rules/transformers/__init__.py +8 -2
  28. cognite/neat/_rules/transformers/_converters.py +271 -188
  29. cognite/neat/_rules/transformers/_mapping.py +75 -59
  30. cognite/neat/_rules/transformers/_verification.py +2 -3
  31. cognite/neat/_session/_inspect.py +3 -1
  32. cognite/neat/_session/_prepare.py +112 -24
  33. cognite/neat/_session/_read.py +33 -70
  34. cognite/neat/_session/_state.py +2 -2
  35. cognite/neat/_session/_to.py +2 -2
  36. cognite/neat/_store/_rules_store.py +4 -8
  37. cognite/neat/_utils/reader/_base.py +27 -0
  38. cognite/neat/_version.py +1 -1
  39. {cognite_neat-0.104.0.dist-info → cognite_neat-0.105.1.dist-info}/METADATA +4 -3
  40. cognite_neat-0.105.1.dist-info/RECORD +179 -0
  41. {cognite_neat-0.104.0.dist-info → cognite_neat-0.105.1.dist-info}/WHEEL +1 -1
  42. cognite/neat/_app/api/__init__.py +0 -0
  43. cognite/neat/_app/api/asgi/metrics.py +0 -4
  44. cognite/neat/_app/api/configuration.py +0 -98
  45. cognite/neat/_app/api/context_manager/__init__.py +0 -3
  46. cognite/neat/_app/api/context_manager/manager.py +0 -16
  47. cognite/neat/_app/api/data_classes/__init__.py +0 -0
  48. cognite/neat/_app/api/data_classes/rest.py +0 -59
  49. cognite/neat/_app/api/explorer.py +0 -66
  50. cognite/neat/_app/api/routers/configuration.py +0 -25
  51. cognite/neat/_app/api/routers/crud.py +0 -102
  52. cognite/neat/_app/api/routers/metrics.py +0 -10
  53. cognite/neat/_app/api/routers/workflows.py +0 -224
  54. cognite/neat/_app/api/utils/__init__.py +0 -0
  55. cognite/neat/_app/api/utils/data_mapping.py +0 -17
  56. cognite/neat/_app/api/utils/logging.py +0 -26
  57. cognite/neat/_app/api/utils/query_templates.py +0 -92
  58. cognite/neat/_app/main.py +0 -17
  59. cognite/neat/_app/monitoring/__init__.py +0 -0
  60. cognite/neat/_app/monitoring/metrics.py +0 -69
  61. cognite/neat/_app/ui/index.html +0 -1
  62. cognite/neat/_app/ui/neat-app/.gitignore +0 -23
  63. cognite/neat/_app/ui/neat-app/README.md +0 -70
  64. cognite/neat/_app/ui/neat-app/build/asset-manifest.json +0 -14
  65. cognite/neat/_app/ui/neat-app/build/favicon.ico +0 -0
  66. cognite/neat/_app/ui/neat-app/build/img/architect-icon.svg +0 -116
  67. cognite/neat/_app/ui/neat-app/build/img/developer-icon.svg +0 -112
  68. cognite/neat/_app/ui/neat-app/build/img/sme-icon.svg +0 -34
  69. cognite/neat/_app/ui/neat-app/build/index.html +0 -1
  70. cognite/neat/_app/ui/neat-app/build/logo192.png +0 -0
  71. cognite/neat/_app/ui/neat-app/build/manifest.json +0 -25
  72. cognite/neat/_app/ui/neat-app/build/robots.txt +0 -3
  73. cognite/neat/_app/ui/neat-app/build/static/css/main.72e3d92e.css +0 -2
  74. cognite/neat/_app/ui/neat-app/build/static/css/main.72e3d92e.css.map +0 -1
  75. cognite/neat/_app/ui/neat-app/build/static/js/main.5a52cf09.js +0 -3
  76. cognite/neat/_app/ui/neat-app/build/static/js/main.5a52cf09.js.LICENSE.txt +0 -88
  77. cognite/neat/_app/ui/neat-app/build/static/js/main.5a52cf09.js.map +0 -1
  78. cognite/neat/_app/ui/neat-app/build/static/media/logo.8093b84df9ed36a174c629d6fe0b730d.svg +0 -1
  79. cognite/neat/_app/ui/neat-app/package-lock.json +0 -18306
  80. cognite/neat/_app/ui/neat-app/package.json +0 -62
  81. cognite/neat/_app/ui/neat-app/public/favicon.ico +0 -0
  82. cognite/neat/_app/ui/neat-app/public/img/architect-icon.svg +0 -116
  83. cognite/neat/_app/ui/neat-app/public/img/developer-icon.svg +0 -112
  84. cognite/neat/_app/ui/neat-app/public/img/sme-icon.svg +0 -34
  85. cognite/neat/_app/ui/neat-app/public/index.html +0 -43
  86. cognite/neat/_app/ui/neat-app/public/logo192.png +0 -0
  87. cognite/neat/_app/ui/neat-app/public/manifest.json +0 -25
  88. cognite/neat/_app/ui/neat-app/public/robots.txt +0 -3
  89. cognite/neat/_app/ui/neat-app/src/App.css +0 -38
  90. cognite/neat/_app/ui/neat-app/src/App.js +0 -17
  91. cognite/neat/_app/ui/neat-app/src/App.test.js +0 -8
  92. cognite/neat/_app/ui/neat-app/src/MainContainer.tsx +0 -70
  93. cognite/neat/_app/ui/neat-app/src/components/JsonViewer.tsx +0 -43
  94. cognite/neat/_app/ui/neat-app/src/components/LocalUploader.tsx +0 -124
  95. cognite/neat/_app/ui/neat-app/src/components/OverviewComponentEditorDialog.tsx +0 -63
  96. cognite/neat/_app/ui/neat-app/src/components/StepEditorDialog.tsx +0 -511
  97. cognite/neat/_app/ui/neat-app/src/components/TabPanel.tsx +0 -36
  98. cognite/neat/_app/ui/neat-app/src/components/Utils.tsx +0 -56
  99. cognite/neat/_app/ui/neat-app/src/components/WorkflowDeleteDialog.tsx +0 -60
  100. cognite/neat/_app/ui/neat-app/src/components/WorkflowExecutionReport.tsx +0 -112
  101. cognite/neat/_app/ui/neat-app/src/components/WorkflowImportExportDialog.tsx +0 -67
  102. cognite/neat/_app/ui/neat-app/src/components/WorkflowMetadataDialog.tsx +0 -79
  103. cognite/neat/_app/ui/neat-app/src/index.css +0 -13
  104. cognite/neat/_app/ui/neat-app/src/index.js +0 -13
  105. cognite/neat/_app/ui/neat-app/src/logo.svg +0 -1
  106. cognite/neat/_app/ui/neat-app/src/reportWebVitals.js +0 -13
  107. cognite/neat/_app/ui/neat-app/src/setupTests.js +0 -5
  108. cognite/neat/_app/ui/neat-app/src/types/WorkflowTypes.ts +0 -388
  109. cognite/neat/_app/ui/neat-app/src/views/AboutView.tsx +0 -61
  110. cognite/neat/_app/ui/neat-app/src/views/ConfigView.tsx +0 -184
  111. cognite/neat/_app/ui/neat-app/src/views/GlobalConfigView.tsx +0 -180
  112. cognite/neat/_app/ui/neat-app/src/views/WorkflowView.tsx +0 -570
  113. cognite/neat/_app/ui/neat-app/tsconfig.json +0 -27
  114. cognite/neat/_workflows/__init__.py +0 -17
  115. cognite/neat/_workflows/base.py +0 -590
  116. cognite/neat/_workflows/cdf_store.py +0 -393
  117. cognite/neat/_workflows/examples/Export_DMS/workflow.yaml +0 -89
  118. cognite/neat/_workflows/examples/Export_Semantic_Data_Model/workflow.yaml +0 -66
  119. cognite/neat/_workflows/examples/Import_DMS/workflow.yaml +0 -65
  120. cognite/neat/_workflows/examples/Validate_Rules/workflow.yaml +0 -67
  121. cognite/neat/_workflows/examples/Validate_Solution_Model/workflow.yaml +0 -64
  122. cognite/neat/_workflows/manager.py +0 -292
  123. cognite/neat/_workflows/model.py +0 -203
  124. cognite/neat/_workflows/steps/__init__.py +0 -0
  125. cognite/neat/_workflows/steps/data_contracts.py +0 -109
  126. cognite/neat/_workflows/steps/lib/__init__.py +0 -0
  127. cognite/neat/_workflows/steps/lib/current/__init__.py +0 -6
  128. cognite/neat/_workflows/steps/lib/current/graph_extractor.py +0 -100
  129. cognite/neat/_workflows/steps/lib/current/graph_loader.py +0 -51
  130. cognite/neat/_workflows/steps/lib/current/graph_store.py +0 -48
  131. cognite/neat/_workflows/steps/lib/current/rules_exporter.py +0 -537
  132. cognite/neat/_workflows/steps/lib/current/rules_importer.py +0 -323
  133. cognite/neat/_workflows/steps/lib/current/rules_validator.py +0 -106
  134. cognite/neat/_workflows/steps/lib/io/__init__.py +0 -1
  135. cognite/neat/_workflows/steps/lib/io/io_steps.py +0 -393
  136. cognite/neat/_workflows/steps/step_model.py +0 -79
  137. cognite/neat/_workflows/steps_registry.py +0 -218
  138. cognite/neat/_workflows/tasks.py +0 -18
  139. cognite/neat/_workflows/triggers.py +0 -169
  140. cognite/neat/_workflows/utils.py +0 -19
  141. cognite_neat-0.104.0.dist-info/RECORD +0 -276
  142. {cognite_neat-0.104.0.dist-info → cognite_neat-0.105.1.dist-info}/LICENSE +0 -0
  143. {cognite_neat-0.104.0.dist-info → cognite_neat-0.105.1.dist-info}/entry_points.txt +0 -0
@@ -1,393 +0,0 @@
1
- import base64
2
- import time
3
- from pathlib import Path
4
- from typing import ClassVar
5
-
6
- import requests
7
- from cognite.client import CogniteClient
8
-
9
- from cognite.neat._issues.errors import WorkflowStepNotInitializedError
10
- from cognite.neat._workflows.model import FlowMessage, StepExecutionStatus
11
- from cognite.neat._workflows.steps.step_model import Configurable, Step
12
-
13
- CATEGORY = "IO Steps"
14
-
15
-
16
- __all__ = [
17
- "DownloadDataFromRestApiToFile",
18
- "DownloadFileFromCDF",
19
- "DownloadFileFromGitHub",
20
- "UploadFileToCDF",
21
- "UploadFileToGitHub",
22
- ]
23
-
24
-
25
- class DownloadFileFromGitHub(Step):
26
- """
27
- This step fetches and stores the file from private Github repository
28
- """
29
-
30
- description = "This step fetches and stores the file from private Github repository"
31
- category = CATEGORY
32
- version = "private-beta"
33
- configurables: ClassVar[list[Configurable]] = [
34
- Configurable(name="github.filepath", value="", label="File path to the file stored on Github"),
35
- Configurable(
36
- name="github.personal_token",
37
- value="",
38
- label="Github Personal Access Token which allows fetching file from private Github repository",
39
- type="password",
40
- ),
41
- Configurable(name="github.owner", value="", label="Github repository owner, also know as github organization"),
42
- Configurable(name="github.repo", value="", label="Github repository from which the file is being fetched"),
43
- Configurable(
44
- name="github.branch", value="main", label="Github repository branch from which the file is being fetched"
45
- ),
46
- Configurable(
47
- name="local.file_name", value="", label="The name of the file under which it will be stored locally"
48
- ),
49
- Configurable(name="local.storage_dir", value="rules/", label="The directory where the file will be stored"),
50
- ]
51
-
52
- def run(self) -> FlowMessage: # type: ignore[override, syntax]
53
- if self.configs is None or self.data_store_path is None:
54
- raise WorkflowStepNotInitializedError(type(self).__name__)
55
- github_filepath = self.configs["github.filepath"]
56
- github_personal_token = self.configs["github.personal_token"]
57
- github_owner = self.configs["github.owner"]
58
- github_repo = self.configs["github.repo"]
59
- github_branch = self.configs["github.branch"]
60
- github_file_name = Path(github_filepath).name
61
- local_file_name = self.configs["local.file_name"] or github_file_name
62
- full_local_file_path = self.data_store_path / Path(self.configs["local.storage_dir"])
63
-
64
- if not full_local_file_path.exists():
65
- full_local_file_path.mkdir(parents=True, exist_ok=True)
66
-
67
- r = requests.get(
68
- f"https://api.github.com/repos/{github_owner}/{github_repo }"
69
- + f"/contents/{github_filepath}?ref={github_branch}",
70
- headers={"accept": "application/vnd.github.v3.raw", "authorization": f"token {github_personal_token}"},
71
- )
72
-
73
- if r.status_code >= 200 and r.status_code < 300:
74
- local_download_path = Path(self.configs["local.storage_dir"]) / local_file_name
75
- full_local_file_path = full_local_file_path / local_file_name
76
- try:
77
- with full_local_file_path.open("wb") as f:
78
- f.write(r.content)
79
- except Exception as e:
80
- return FlowMessage(
81
- error_text=f"Error writing file to {full_local_file_path}. Error: {e}",
82
- step_execution_status=StepExecutionStatus.ABORT_AND_FAIL,
83
- )
84
- else:
85
- return FlowMessage(
86
- error_text=f"Error fetching file from Github: {r.text}",
87
- step_execution_status=StepExecutionStatus.ABORT_AND_FAIL,
88
- )
89
-
90
- output_text = (
91
- "<p></p>"
92
- f" Downloaded file <b>{github_file_name}</b> from:"
93
- f'<p><a href="https://github.com/{github_owner}/{github_repo}/tree/{github_branch}"'
94
- f'target="_blank">https://github.com/{github_owner}/{github_repo}/tree/{github_branch}</a></p>'
95
- )
96
-
97
- output_text += (
98
- "<p></p>"
99
- " Downloaded rules accessible locally under file name "
100
- f'<a href="/data/{local_download_path}?{time.time()}" '
101
- f'target="_blank">{local_file_name}</a>'
102
- )
103
-
104
- return FlowMessage(output_text=output_text)
105
-
106
-
107
- class UploadFileToGitHub(Step):
108
- """
109
- This step uploads file to private Github repository
110
- """
111
-
112
- description = "This step uploads file to private Github repository"
113
- category = CATEGORY
114
- version = "private-beta"
115
- configurables: ClassVar[list[Configurable]] = [
116
- Configurable(name="github.filepath", value="", label="File path to the file stored on Github"),
117
- Configurable(
118
- name="github.personal_token",
119
- value="",
120
- label="Github Personal Access Token which allows uploading file to private Github repository",
121
- type="password",
122
- ),
123
- Configurable(name="github.owner", value="", label="Github repository owner, also know as github organization"),
124
- Configurable(name="github.repo", value="", label="Github repository the file is being uploaded to"),
125
- Configurable(
126
- name="github.branch", value="main", label="Github repository branch the file is being uploaded to"
127
- ),
128
- Configurable(
129
- name="github.commit_message",
130
- value="New file",
131
- label="The commit message to be used when uploading the file",
132
- ),
133
- Configurable(name="local.file_name", value="", label="The name of the local file to be uploaded to Github"),
134
- Configurable(name="local.storage_dir", value="rules/", label="Local directory where the file is stored"),
135
- ]
136
-
137
- def run(self) -> FlowMessage: # type: ignore[override, syntax]
138
- if self.configs is None or self.data_store_path is None:
139
- raise WorkflowStepNotInitializedError(type(self).__name__)
140
- github_filepath = self.configs["github.filepath"]
141
- github_personal_token = self.configs["github.personal_token"]
142
- github_owner = self.configs["github.owner"]
143
- github_repo = self.configs["github.repo"]
144
- github_branch = self.configs["github.branch"]
145
- local_file_name = self.configs["local.file_name"]
146
- full_local_file_path = self.data_store_path / Path(self.configs["local.storage_dir"]) / local_file_name
147
-
148
- if not full_local_file_path.exists():
149
- return FlowMessage(
150
- error_text=f"File {full_local_file_path} doesn't exist",
151
- step_execution_status=StepExecutionStatus.ABORT_AND_FAIL,
152
- )
153
-
154
- with full_local_file_path.open("rb") as f:
155
- file_content = f.read()
156
-
157
- headers = {"Authorization": f"Bearer {github_personal_token}", "Content-Type": "application/json"}
158
-
159
- # Create a content object
160
- content = {
161
- "message": self.configs["github.commit_message"],
162
- "content": base64.b64encode(file_content).decode("utf-8"),
163
- "branch": github_branch,
164
- }
165
- base_url = f"https://api.github.com/repos/{github_owner}/{github_repo }/contents/{github_filepath}"
166
-
167
- # Check if the file exists already
168
- response = requests.get(f"{base_url}?ref={github_branch}", headers=headers)
169
-
170
- if response.status_code == 200:
171
- # File exists, update it
172
- sha = response.json()["sha"]
173
- content["sha"] = sha
174
- response = requests.put(base_url, headers=headers, json=content)
175
- elif response.status_code == 404:
176
- # File doesn't exist, create it
177
- response = requests.put(base_url, headers=headers, json=content)
178
- else:
179
- # Unexpected response
180
- return FlowMessage(
181
- error_text=f"Unexpected response from Github: {response.text}",
182
- step_execution_status=StepExecutionStatus.ABORT_AND_FAIL,
183
- )
184
- if response.status_code == 200 or response.status_code == 201:
185
- return FlowMessage(output_text=f"File {local_file_name} uploaded to Github successfully")
186
- else:
187
- return FlowMessage(
188
- error_text=f"Error uploading file to Github: {response.text}",
189
- step_execution_status=StepExecutionStatus.ABORT_AND_FAIL,
190
- )
191
-
192
-
193
- class DownloadFileFromCDF(Step):
194
- """
195
- This step fetches and stores file from CDF
196
- """
197
-
198
- description = "This step fetches and stores file from CDF"
199
- category = CATEGORY
200
- version = "private-beta"
201
- configurables: ClassVar[list[Configurable]] = [
202
- Configurable(name="cdf.external_id", value="", label="External ID of the file stored in CDF"),
203
- Configurable(
204
- name="local.file_name",
205
- value="",
206
- label="The name of the file under which the content will be stored locally",
207
- ),
208
- Configurable(name="local.storage_dir", value="rules/", label="The directory where the file will be stored"),
209
- ]
210
-
211
- def run(self, cdf_client: CogniteClient) -> FlowMessage: # type: ignore[override, syntax]
212
- if self.configs is None or self.data_store_path is None:
213
- raise WorkflowStepNotInitializedError(type(self).__name__)
214
-
215
- output_dir = self.data_store_path / Path(self.configs["local.storage_dir"])
216
- output_dir.mkdir(parents=True, exist_ok=True)
217
-
218
- full_local_file_path = output_dir / self.configs["local.file_name"]
219
- cdf_client.files.download_to_path(full_local_file_path, external_id=self.configs["cdf.external_id"])
220
- if full_local_file_path.exists():
221
- return FlowMessage(output_text=f"File {self.configs['local.file_name']} downloaded from CDF successfully")
222
- else:
223
- return FlowMessage(
224
- error_text="Error downloading file from CDF", step_execution_status=StepExecutionStatus.ABORT_AND_FAIL
225
- )
226
-
227
-
228
- class UploadFileToCDF(Step):
229
- """
230
- This step uploads file to CDF
231
- """
232
-
233
- description = "This step uploads file to CDF"
234
- category = CATEGORY
235
- version = "private-beta"
236
- configurables: ClassVar[list[Configurable]] = [
237
- Configurable(name="cdf.external_id", value="", label="Exernal Id for the file to be stored in CDF"),
238
- Configurable(
239
- name="cdf.dataset_id", value="", label="Dataset Id for the file to be stored in CDF. Must be a number"
240
- ),
241
- Configurable(name="local.file_name", value="", label="The name of the local file to be uploaded to CDF"),
242
- Configurable(name="local.storage_dir", value="rules/", label="Local directory where the file is stored"),
243
- ]
244
-
245
- def run(self, cdf_client: CogniteClient) -> FlowMessage: # type: ignore[override, syntax]
246
- if self.configs is None or self.data_store_path is None:
247
- raise WorkflowStepNotInitializedError(type(self).__name__)
248
- full_local_file_path = (
249
- self.data_store_path / Path(self.configs["local.storage_dir"]) / self.configs["local.file_name"]
250
- )
251
- dataset_id = int(self.configs["cdf.dataset_id"]) if self.configs["cdf.dataset_id"].isdigit() else None
252
- cdf_client.files.upload(
253
- str(full_local_file_path),
254
- external_id=self.configs["cdf.external_id"],
255
- overwrite=True,
256
- data_set_id=dataset_id,
257
- )
258
- return FlowMessage(output_text=f"File {self.configs['local.file_name']} uploaded to CDF successfully")
259
-
260
-
261
- class DownloadDataFromRestApiToFile(Step):
262
- """
263
- This step downloads the response from a REST API and saves it to a file.
264
- """
265
-
266
- description = "This step downloads the response from a REST API and saves it to a file."
267
- category = CATEGORY
268
- version = "private-beta"
269
- configurables: ClassVar[list[Configurable]] = [
270
- Configurable(
271
- name="api_url",
272
- value="",
273
- label="API URL",
274
- ),
275
- Configurable(
276
- name="output_file_path",
277
- value="workflows/workflow_name/output.json",
278
- label="Output File Path. The path must be relative to the data store path.",
279
- ),
280
- Configurable(
281
- name="http_method",
282
- value="GET",
283
- label="HTTP Method (GET/POST/PUT)",
284
- options=["GET", "POST", "PUT"],
285
- ),
286
- Configurable(
287
- name="auth_mode",
288
- value="none",
289
- label="Authentication Mode (basic/token/none)",
290
- options=["basic", "token", "none"],
291
- ),
292
- Configurable(
293
- name="username",
294
- value="",
295
- label="Username (for basic auth)",
296
- ),
297
- Configurable(
298
- name="password",
299
- value="",
300
- label="Password (for basic auth)",
301
- type="password",
302
- ),
303
- Configurable(
304
- name="token",
305
- value="",
306
- label="Token (for token auth)",
307
- type="password",
308
- ),
309
- Configurable(
310
- name="response_destination",
311
- value="file",
312
- label="Destination for the response (file/flow_message/both)",
313
- options=["file", "flow_message", "both"],
314
- ),
315
- Configurable(
316
- name="http_headers",
317
- value="",
318
- label="Custom HTTP headers separated by ';' . Example: \
319
- 'Content-Type: application/json; Accept: application/json'",
320
- ),
321
- ]
322
-
323
- def run(self) -> FlowMessage: # type: ignore[override, syntax]
324
- api_url = self.configs["api_url"]
325
- output_file_path = Path(self.data_store_path) / Path(self.configs["output_file_path"])
326
-
327
- output_file_path.parent.mkdir(parents=True, exist_ok=True)
328
-
329
- http_method = self.configs["http_method"].upper()
330
- auth_mode = self.configs["auth_mode"]
331
- username = self.configs["username"]
332
- password = self.configs["password"]
333
- token = self.configs["token"]
334
- http_headers_str = self.configs.get("http_headers", "")
335
- headers = {}
336
- for header in http_headers_str.split(";"):
337
- if header:
338
- key, value = header.split(":")
339
- headers[key.strip()] = value.strip()
340
- try:
341
- if auth_mode == "basic":
342
- if username and password:
343
- headers["Authorization"] = f'Basic {base64.b64encode(f"{username}:{password}".encode()).decode()}'
344
- else:
345
- return FlowMessage(
346
- error_text="Username and password are required for Basic Authentication",
347
- step_execution_status=StepExecutionStatus.ABORT_AND_FAIL,
348
- )
349
- elif auth_mode == "token":
350
- if token:
351
- headers["Authorization"] = f"Bearer {token}"
352
- else:
353
- return FlowMessage(
354
- error_text="Token is required for Token Authentication",
355
- step_execution_status=StepExecutionStatus.ABORT_AND_FAIL,
356
- )
357
-
358
- if http_method not in ("GET", "POST", "PUT"):
359
- return FlowMessage(
360
- error_text="Unsupported HTTP method. Supported methods are GET, POST, and PUT.",
361
- step_execution_status=StepExecutionStatus.ABORT_AND_FAIL,
362
- )
363
-
364
- if http_method == "GET":
365
- response = requests.get(api_url, headers=headers, stream=True)
366
- elif http_method == "POST":
367
- response = requests.post(api_url, headers=headers, stream=True)
368
- elif http_method == "PUT":
369
- response = requests.put(api_url, headers=headers, stream=True)
370
-
371
- if response.status_code >= 200 and response.status_code < 300:
372
- payload = None
373
- if self.configs["response_destination"] in ("flow_message", "both"):
374
- payload = response.json()
375
- with output_file_path.open("wb") as output_file:
376
- output_file.write(response.content)
377
- else:
378
- with output_file_path.open("wb") as output_file:
379
- for chunk in response.iter_content(chunk_size=1024):
380
- if chunk:
381
- output_file.write(chunk)
382
-
383
- return FlowMessage(output_text="Response downloaded and saved successfully.", payload=payload)
384
- else:
385
- return FlowMessage(
386
- error_text=f"Failed to fetch response. Status Code: {response.status_code}",
387
- step_execution_status=StepExecutionStatus.ABORT_AND_FAIL,
388
- )
389
- except Exception as e:
390
- return FlowMessage(
391
- error_text=f"An error occurred: {e!s}",
392
- step_execution_status=StepExecutionStatus.ABORT_AND_FAIL,
393
- )
@@ -1,79 +0,0 @@
1
- import typing
2
- from abc import ABC, abstractmethod
3
- from typing import ClassVar, TypeVar
4
-
5
- from pydantic import BaseModel, ConfigDict
6
-
7
- from cognite.neat._app.monitoring.metrics import NeatMetricsCollector
8
- from cognite.neat._config import Config
9
- from cognite.neat._workflows.model import FlowMessage, WorkflowConfigs
10
-
11
-
12
- class Configurable(BaseModel):
13
- name: str
14
- value: str | None = None
15
- label: str | None = None
16
- type: str | None = None # string , secret , number , boolean , json , multi_select , single_select
17
- required: bool = False
18
- options: list[str] | None = None
19
-
20
-
21
- class DataContract(BaseModel):
22
- model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True)
23
-
24
-
25
- T_Input = TypeVar("T_Input", bound=DataContract)
26
- T_Input1 = TypeVar("T_Input1", bound=DataContract)
27
- T_Input2 = TypeVar("T_Input2", bound=DataContract)
28
- T_Output = TypeVar("T_Output", bound=DataContract)
29
-
30
-
31
- class Step(ABC):
32
- description: str = ""
33
- category: str = "default"
34
- configurables: ClassVar[list[Configurable]] = []
35
- scope: str = "core_global"
36
- metrics: NeatMetricsCollector | None = None
37
- workflow_configs: WorkflowConfigs | None = None
38
- version: str = "1.0.0" # version of the step. All alpha versions considered as experimental
39
- source: str = (
40
- "cognite" # source of the step , can be source identifier or url , for instance github url for instance.
41
- )
42
- docs_url: str = "https://cognite-neat.readthedocs-hosted.com/en/latest/" # url to the documentation of the step
43
-
44
- def __init__(self, config: Config):
45
- self.log: bool = False
46
- self.configs: dict[str, str] = {}
47
- self.complex_configs: dict[
48
- str, typing.Any
49
- ] = {} # complex configs are meant for more complex configurations. Value can be any type.
50
- self.workflow_id: str = ""
51
- self.workflow_run_id: str = ""
52
- self.config = config
53
- self.data_store_path = config.data_store_path
54
-
55
- @property
56
- def _not_configured_message(self) -> str:
57
- return f"Step {type(self).__name__} has not been configured."
58
-
59
- def set_metrics(self, metrics: NeatMetricsCollector | None):
60
- self.metrics = metrics
61
-
62
- def set_workflow_configs(self, configs: WorkflowConfigs | None):
63
- self.workflow_configs = configs
64
-
65
- def set_workflow_metadata(self, workflow_id: str, workflow_run_id: str):
66
- self.workflow_id = workflow_id
67
- self.workflow_run_id = workflow_run_id
68
-
69
- def configure(self, configs: dict[str, str], complex_configs: dict[str, typing.Any] | None = None):
70
- if complex_configs is None:
71
- complex_configs = {}
72
- self.configs = configs
73
- self.complex_configs = complex_configs
74
-
75
- def set_flow_context(self, context: dict[str, DataContract]):
76
- self.flow_context = context
77
-
78
- @abstractmethod
79
- def run(self, *input_data: DataContract) -> DataContract | tuple[FlowMessage, DataContract] | FlowMessage: ...