cognite-neat 0.104.0__py3-none-any.whl → 0.105.0__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.
- cognite/neat/_client/_api/data_modeling_loaders.py +83 -23
- cognite/neat/_client/_api/schema.py +2 -1
- cognite/neat/_client/data_classes/neat_sequence.py +261 -0
- cognite/neat/_client/data_classes/schema.py +5 -1
- cognite/neat/_client/testing.py +33 -0
- cognite/neat/_constants.py +56 -0
- cognite/neat/_graph/extractors/_classic_cdf/_base.py +6 -5
- cognite/neat/_graph/extractors/_classic_cdf/_sequences.py +225 -11
- cognite/neat/_graph/extractors/_mock_graph_generator.py +1 -1
- cognite/neat/_graph/loaders/_rdf2dms.py +13 -2
- cognite/neat/_graph/transformers/__init__.py +3 -1
- cognite/neat/_graph/transformers/_classic_cdf.py +2 -1
- cognite/neat/_graph/transformers/_value_type.py +72 -0
- cognite/neat/_issues/__init__.py +0 -2
- cognite/neat/_issues/_base.py +19 -35
- cognite/neat/_issues/warnings/__init__.py +4 -1
- cognite/neat/_issues/warnings/_general.py +7 -0
- cognite/neat/_issues/warnings/_resources.py +11 -0
- cognite/neat/_rules/exporters/_rules2dms.py +35 -1
- cognite/neat/_rules/exporters/_rules2excel.py +2 -2
- cognite/neat/_rules/importers/_dms2rules.py +66 -55
- cognite/neat/_rules/models/_base_rules.py +4 -1
- cognite/neat/_rules/models/entities/_wrapped.py +10 -5
- cognite/neat/_rules/models/mapping/_classic2core.yaml +239 -38
- cognite/neat/_rules/transformers/__init__.py +8 -2
- cognite/neat/_rules/transformers/_converters.py +271 -188
- cognite/neat/_rules/transformers/_mapping.py +75 -59
- cognite/neat/_rules/transformers/_verification.py +2 -3
- cognite/neat/_session/_inspect.py +3 -1
- cognite/neat/_session/_prepare.py +112 -24
- cognite/neat/_session/_read.py +33 -70
- cognite/neat/_session/_state.py +2 -2
- cognite/neat/_session/_to.py +2 -2
- cognite/neat/_store/_rules_store.py +4 -8
- cognite/neat/_utils/reader/_base.py +27 -0
- cognite/neat/_version.py +1 -1
- {cognite_neat-0.104.0.dist-info → cognite_neat-0.105.0.dist-info}/METADATA +3 -2
- cognite_neat-0.105.0.dist-info/RECORD +179 -0
- {cognite_neat-0.104.0.dist-info → cognite_neat-0.105.0.dist-info}/WHEEL +1 -1
- cognite/neat/_app/api/__init__.py +0 -0
- cognite/neat/_app/api/asgi/metrics.py +0 -4
- cognite/neat/_app/api/configuration.py +0 -98
- cognite/neat/_app/api/context_manager/__init__.py +0 -3
- cognite/neat/_app/api/context_manager/manager.py +0 -16
- cognite/neat/_app/api/data_classes/__init__.py +0 -0
- cognite/neat/_app/api/data_classes/rest.py +0 -59
- cognite/neat/_app/api/explorer.py +0 -66
- cognite/neat/_app/api/routers/configuration.py +0 -25
- cognite/neat/_app/api/routers/crud.py +0 -102
- cognite/neat/_app/api/routers/metrics.py +0 -10
- cognite/neat/_app/api/routers/workflows.py +0 -224
- cognite/neat/_app/api/utils/__init__.py +0 -0
- cognite/neat/_app/api/utils/data_mapping.py +0 -17
- cognite/neat/_app/api/utils/logging.py +0 -26
- cognite/neat/_app/api/utils/query_templates.py +0 -92
- cognite/neat/_app/main.py +0 -17
- cognite/neat/_app/monitoring/__init__.py +0 -0
- cognite/neat/_app/monitoring/metrics.py +0 -69
- cognite/neat/_app/ui/index.html +0 -1
- cognite/neat/_app/ui/neat-app/.gitignore +0 -23
- cognite/neat/_app/ui/neat-app/README.md +0 -70
- cognite/neat/_app/ui/neat-app/build/asset-manifest.json +0 -14
- cognite/neat/_app/ui/neat-app/build/favicon.ico +0 -0
- cognite/neat/_app/ui/neat-app/build/img/architect-icon.svg +0 -116
- cognite/neat/_app/ui/neat-app/build/img/developer-icon.svg +0 -112
- cognite/neat/_app/ui/neat-app/build/img/sme-icon.svg +0 -34
- cognite/neat/_app/ui/neat-app/build/index.html +0 -1
- cognite/neat/_app/ui/neat-app/build/logo192.png +0 -0
- cognite/neat/_app/ui/neat-app/build/manifest.json +0 -25
- cognite/neat/_app/ui/neat-app/build/robots.txt +0 -3
- cognite/neat/_app/ui/neat-app/build/static/css/main.72e3d92e.css +0 -2
- cognite/neat/_app/ui/neat-app/build/static/css/main.72e3d92e.css.map +0 -1
- cognite/neat/_app/ui/neat-app/build/static/js/main.5a52cf09.js +0 -3
- cognite/neat/_app/ui/neat-app/build/static/js/main.5a52cf09.js.LICENSE.txt +0 -88
- cognite/neat/_app/ui/neat-app/build/static/js/main.5a52cf09.js.map +0 -1
- cognite/neat/_app/ui/neat-app/build/static/media/logo.8093b84df9ed36a174c629d6fe0b730d.svg +0 -1
- cognite/neat/_app/ui/neat-app/package-lock.json +0 -18306
- cognite/neat/_app/ui/neat-app/package.json +0 -62
- cognite/neat/_app/ui/neat-app/public/favicon.ico +0 -0
- cognite/neat/_app/ui/neat-app/public/img/architect-icon.svg +0 -116
- cognite/neat/_app/ui/neat-app/public/img/developer-icon.svg +0 -112
- cognite/neat/_app/ui/neat-app/public/img/sme-icon.svg +0 -34
- cognite/neat/_app/ui/neat-app/public/index.html +0 -43
- cognite/neat/_app/ui/neat-app/public/logo192.png +0 -0
- cognite/neat/_app/ui/neat-app/public/manifest.json +0 -25
- cognite/neat/_app/ui/neat-app/public/robots.txt +0 -3
- cognite/neat/_app/ui/neat-app/src/App.css +0 -38
- cognite/neat/_app/ui/neat-app/src/App.js +0 -17
- cognite/neat/_app/ui/neat-app/src/App.test.js +0 -8
- cognite/neat/_app/ui/neat-app/src/MainContainer.tsx +0 -70
- cognite/neat/_app/ui/neat-app/src/components/JsonViewer.tsx +0 -43
- cognite/neat/_app/ui/neat-app/src/components/LocalUploader.tsx +0 -124
- cognite/neat/_app/ui/neat-app/src/components/OverviewComponentEditorDialog.tsx +0 -63
- cognite/neat/_app/ui/neat-app/src/components/StepEditorDialog.tsx +0 -511
- cognite/neat/_app/ui/neat-app/src/components/TabPanel.tsx +0 -36
- cognite/neat/_app/ui/neat-app/src/components/Utils.tsx +0 -56
- cognite/neat/_app/ui/neat-app/src/components/WorkflowDeleteDialog.tsx +0 -60
- cognite/neat/_app/ui/neat-app/src/components/WorkflowExecutionReport.tsx +0 -112
- cognite/neat/_app/ui/neat-app/src/components/WorkflowImportExportDialog.tsx +0 -67
- cognite/neat/_app/ui/neat-app/src/components/WorkflowMetadataDialog.tsx +0 -79
- cognite/neat/_app/ui/neat-app/src/index.css +0 -13
- cognite/neat/_app/ui/neat-app/src/index.js +0 -13
- cognite/neat/_app/ui/neat-app/src/logo.svg +0 -1
- cognite/neat/_app/ui/neat-app/src/reportWebVitals.js +0 -13
- cognite/neat/_app/ui/neat-app/src/setupTests.js +0 -5
- cognite/neat/_app/ui/neat-app/src/types/WorkflowTypes.ts +0 -388
- cognite/neat/_app/ui/neat-app/src/views/AboutView.tsx +0 -61
- cognite/neat/_app/ui/neat-app/src/views/ConfigView.tsx +0 -184
- cognite/neat/_app/ui/neat-app/src/views/GlobalConfigView.tsx +0 -180
- cognite/neat/_app/ui/neat-app/src/views/WorkflowView.tsx +0 -570
- cognite/neat/_app/ui/neat-app/tsconfig.json +0 -27
- cognite/neat/_workflows/__init__.py +0 -17
- cognite/neat/_workflows/base.py +0 -590
- cognite/neat/_workflows/cdf_store.py +0 -393
- cognite/neat/_workflows/examples/Export_DMS/workflow.yaml +0 -89
- cognite/neat/_workflows/examples/Export_Semantic_Data_Model/workflow.yaml +0 -66
- cognite/neat/_workflows/examples/Import_DMS/workflow.yaml +0 -65
- cognite/neat/_workflows/examples/Validate_Rules/workflow.yaml +0 -67
- cognite/neat/_workflows/examples/Validate_Solution_Model/workflow.yaml +0 -64
- cognite/neat/_workflows/manager.py +0 -292
- cognite/neat/_workflows/model.py +0 -203
- cognite/neat/_workflows/steps/__init__.py +0 -0
- cognite/neat/_workflows/steps/data_contracts.py +0 -109
- cognite/neat/_workflows/steps/lib/__init__.py +0 -0
- cognite/neat/_workflows/steps/lib/current/__init__.py +0 -6
- cognite/neat/_workflows/steps/lib/current/graph_extractor.py +0 -100
- cognite/neat/_workflows/steps/lib/current/graph_loader.py +0 -51
- cognite/neat/_workflows/steps/lib/current/graph_store.py +0 -48
- cognite/neat/_workflows/steps/lib/current/rules_exporter.py +0 -537
- cognite/neat/_workflows/steps/lib/current/rules_importer.py +0 -323
- cognite/neat/_workflows/steps/lib/current/rules_validator.py +0 -106
- cognite/neat/_workflows/steps/lib/io/__init__.py +0 -1
- cognite/neat/_workflows/steps/lib/io/io_steps.py +0 -393
- cognite/neat/_workflows/steps/step_model.py +0 -79
- cognite/neat/_workflows/steps_registry.py +0 -218
- cognite/neat/_workflows/tasks.py +0 -18
- cognite/neat/_workflows/triggers.py +0 -169
- cognite/neat/_workflows/utils.py +0 -19
- cognite_neat-0.104.0.dist-info/RECORD +0 -276
- {cognite_neat-0.104.0.dist-info → cognite_neat-0.105.0.dist-info}/LICENSE +0 -0
- {cognite_neat-0.104.0.dist-info → cognite_neat-0.105.0.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: ...
|