peak-sdk 1.4.0__py3-none-any.whl → 1.6.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.
- peak/_metadata.py +58 -3
- peak/_version.py +1 -1
- peak/cli/cli.py +4 -2
- peak/cli/helpers.py +2 -0
- peak/cli/press/blocks/specs.py +2 -2
- peak/cli/press/specs.py +4 -2
- peak/cli/resources/alerts/__init__.py +35 -0
- peak/cli/resources/alerts/emails.py +360 -0
- peak/cli/resources/images.py +1 -1
- peak/cli/resources/services.py +23 -0
- peak/cli/resources/users.py +71 -0
- peak/cli/resources/workflows.py +77 -15
- peak/cli/ruff.toml +5 -3
- peak/compression.py +2 -2
- peak/exceptions.py +4 -6
- peak/handler.py +3 -5
- peak/helpers.py +35 -9
- peak/output.py +2 -2
- peak/press/apps.py +8 -16
- peak/press/blocks.py +8 -16
- peak/press/deployments.py +2 -4
- peak/press/specs.py +12 -14
- peak/resources/__init__.py +3 -2
- peak/resources/alerts.py +309 -0
- peak/resources/artifacts.py +2 -4
- peak/resources/images.py +8 -14
- peak/resources/services.py +7 -6
- peak/resources/users.py +93 -0
- peak/resources/webapps.py +3 -5
- peak/resources/workflows.py +106 -16
- peak/sample_yaml/resources/emails/send_email.yaml +15 -0
- peak/sample_yaml/resources/services/create_or_update_service.yaml +1 -0
- peak/sample_yaml/resources/services/create_service.yaml +1 -0
- peak/sample_yaml/resources/services/update_service.yaml +1 -0
- peak/sample_yaml/resources/workflows/create_or_update_workflow.yaml +28 -0
- peak/sample_yaml/resources/workflows/create_workflow.yaml +10 -0
- peak/sample_yaml/resources/workflows/patch_workflow.yaml +28 -0
- peak/sample_yaml/resources/workflows/update_workflow.yaml +28 -0
- peak/session.py +7 -4
- peak/telemetry.py +1 -1
- peak/template.py +6 -4
- peak/tools/logging/__init__.py +26 -268
- peak/tools/logging/log_level.py +35 -3
- peak/tools/logging/logger.py +389 -0
- peak/validators.py +34 -2
- {peak_sdk-1.4.0.dist-info → peak_sdk-1.6.0.dist-info}/METADATA +11 -12
- {peak_sdk-1.4.0.dist-info → peak_sdk-1.6.0.dist-info}/RECORD +50 -43
- {peak_sdk-1.4.0.dist-info → peak_sdk-1.6.0.dist-info}/WHEEL +1 -1
- {peak_sdk-1.4.0.dist-info → peak_sdk-1.6.0.dist-info}/LICENSE +0 -0
- {peak_sdk-1.4.0.dist-info → peak_sdk-1.6.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,71 @@
|
|
1
|
+
#
|
2
|
+
# # Copyright © 2024 Peak AI Limited. or its affiliates. All Rights Reserved.
|
3
|
+
# #
|
4
|
+
# # Licensed under the Apache License, Version 2.0 (the "License"). You
|
5
|
+
# # may not use this file except in compliance with the License. A copy of
|
6
|
+
# # the License is located at:
|
7
|
+
# #
|
8
|
+
# # https://github.com/PeakBI/peak-sdk/blob/main/LICENSE
|
9
|
+
# #
|
10
|
+
# # or in the "license" file accompanying this file. This file is
|
11
|
+
# # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
12
|
+
# # ANY KIND, either express or implied. See the License for the specific
|
13
|
+
# # language governing permissions and limitations under the License.
|
14
|
+
# #
|
15
|
+
# # This file is part of the peak-sdk.
|
16
|
+
# # see (https://github.com/PeakBI/peak-sdk)
|
17
|
+
# #
|
18
|
+
# # You should have received a copy of the APACHE LICENSE, VERSION 2.0
|
19
|
+
# # along with this program. If not, see <https://apache.org/licenses/LICENSE-2.0>
|
20
|
+
#
|
21
|
+
"""Peak Users commands."""
|
22
|
+
from typing import Optional
|
23
|
+
|
24
|
+
import typer
|
25
|
+
from peak import Session
|
26
|
+
from peak.cli.args import OUTPUT_TYPES, PAGING
|
27
|
+
from peak.constants import OutputTypesNoTable
|
28
|
+
from peak.output import Writer
|
29
|
+
from peak.resources import users
|
30
|
+
|
31
|
+
app = typer.Typer(
|
32
|
+
help="User management and permission checking.",
|
33
|
+
short_help="Manage User Permissions.",
|
34
|
+
)
|
35
|
+
|
36
|
+
_FEATURE = typer.Option(..., help="The feature path to check permissions for, e.g., 'PRICING.GUARDRAILS'.")
|
37
|
+
_ACTION = typer.Option(..., help="The action to check for the feature path, e.g., 'read' or 'write'.")
|
38
|
+
_AUTH_TOKEN = typer.Option(
|
39
|
+
None,
|
40
|
+
help="Authentication token for the user. If not provided, the token from the environment will be used.",
|
41
|
+
)
|
42
|
+
|
43
|
+
|
44
|
+
@app.command(short_help="Check user permission for a specific feature.", options_metavar="check_permission")
|
45
|
+
def check_permission(
|
46
|
+
ctx: typer.Context,
|
47
|
+
feature: str = _FEATURE,
|
48
|
+
action: str = _ACTION,
|
49
|
+
auth_token: Optional[str] = _AUTH_TOKEN,
|
50
|
+
paging: Optional[bool] = PAGING, # noqa: ARG001
|
51
|
+
output_type: Optional[OutputTypesNoTable] = OUTPUT_TYPES, # noqa: ARG001
|
52
|
+
) -> None:
|
53
|
+
"""Check if the user has the specified permission for the given feature path.
|
54
|
+
|
55
|
+
\b
|
56
|
+
📝 ***Example usage:***
|
57
|
+
```bash
|
58
|
+
peak user check-permission --feature "PRICING.GUARDRAILS" --action "read" --auth-token <your-auth-token>
|
59
|
+
```
|
60
|
+
|
61
|
+
\b
|
62
|
+
🆗 ***Response:***
|
63
|
+
True if the user has the permission, False otherwise.
|
64
|
+
"""
|
65
|
+
user_session = Session(auth_token=auth_token)
|
66
|
+
user_client = users.get_client(session=user_session)
|
67
|
+
writer: Writer = ctx.obj["writer"]
|
68
|
+
|
69
|
+
with writer.pager():
|
70
|
+
response = user_client.check_permissions({feature: action})
|
71
|
+
writer.write(response.get(feature, False))
|
peak/cli/resources/workflows.py
CHANGED
@@ -82,6 +82,16 @@ _CLEAR_IMAGE_CACHE = typer.Option(None, help="Whether to clear image cache on wo
|
|
82
82
|
|
83
83
|
_STEP_NAMES = typer.Option(None, help="List of step names to be updated.")
|
84
84
|
|
85
|
+
_EXECUTION_STATUS = typer.Option(
|
86
|
+
None,
|
87
|
+
help="List of workflow execution statuses. Valid values are `Success`, `Running`, `Stopped`, `Stopping` and `Failed`.",
|
88
|
+
)
|
89
|
+
|
90
|
+
_COUNT = typer.Option(
|
91
|
+
None,
|
92
|
+
help="Number of workflow executions required in the provided time range or 90 days. If not provided, all executions are returned.",
|
93
|
+
)
|
94
|
+
|
85
95
|
|
86
96
|
@app.command(short_help="Create a new workflow.", options_metavar="create_workflow")
|
87
97
|
def create(
|
@@ -110,12 +120,24 @@ def create(
|
|
110
120
|
- events (map):
|
111
121
|
success (boolean | required: false): Whether to call event on success.
|
112
122
|
fail (boolean | required: false): Whether to call event on success.
|
113
|
-
runtimeExceeded (int | required: false): The runtime after which event is called.
|
114
|
-
user (string
|
115
|
-
|
123
|
+
runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
|
124
|
+
user (string): User to be notified.
|
125
|
+
- events (map):
|
126
|
+
success (boolean | required: false): Whether to call event on success.
|
127
|
+
fail (boolean | required: false): Whether to call event on success.
|
128
|
+
runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
|
129
|
+
webhook (map):
|
116
130
|
name (string): Name of the webhook.
|
117
131
|
url (string): URL of the webhook.
|
118
132
|
payload (string): Webhook payload.
|
133
|
+
- events (map):
|
134
|
+
success (boolean | required: false): Whether to call event on success.
|
135
|
+
fail (boolean | required: false): Whether to call event on success.
|
136
|
+
runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
|
137
|
+
email (map):
|
138
|
+
name (string): Name of the email watcher.
|
139
|
+
recipients (map):
|
140
|
+
to (list(str)): List of email addresses to send the email to. Email can be sent only to the users who are added in the tenant.
|
119
141
|
retryOptions (map | required: false): # Workflow level retry options which will be applied to all steps.
|
120
142
|
duration (int | required: false): Duration in seconds after which the step is retried if it fails. Default is 5 seconds and maximum is 120 seconds.
|
121
143
|
exitCodes (list(int) | required: false): List of exit codes for which the step is retried. If not provided, the step is retried for all exit codes.
|
@@ -222,12 +244,24 @@ def update(
|
|
222
244
|
- events (map):
|
223
245
|
success (boolean | required: false): Whether to call event on success.
|
224
246
|
fail (boolean | required: false): Whether to call event on success.
|
225
|
-
runtimeExceeded (int | required: false): The runtime after which event is called.
|
226
|
-
user (string
|
227
|
-
|
247
|
+
runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
|
248
|
+
user (string): User to be notified.
|
249
|
+
- events (map):
|
250
|
+
success (boolean | required: false): Whether to call event on success.
|
251
|
+
fail (boolean | required: false): Whether to call event on success.
|
252
|
+
runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
|
253
|
+
webhook (map):
|
228
254
|
name (string): Name of the webhook.
|
229
255
|
url (string): URL of the webhook.
|
230
256
|
payload (string): Webhook payload.
|
257
|
+
- events (map):
|
258
|
+
success (boolean | required: false): Whether to call event on success.
|
259
|
+
fail (boolean | required: false): Whether to call event on success.
|
260
|
+
runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
|
261
|
+
email (map):
|
262
|
+
name (string): Name of the email watcher.
|
263
|
+
recipients (map):
|
264
|
+
to (list(str)): List of email addresses to send the email to. Email can be sent only to the users who are added in the tenant.
|
231
265
|
retryOptions (map | required: false): # Workflow level retry options which will be applied to all steps.
|
232
266
|
duration (int | required: false): Duration in seconds after which the step is retried if it fails. Default is 5 seconds and maximum is 120 seconds.
|
233
267
|
exitCodes (list(int) | required: false): List of exit codes for which the step is retried. If not provided, the step is retried for all exit codes.
|
@@ -334,12 +368,24 @@ def create_or_update(
|
|
334
368
|
- events (map):
|
335
369
|
success (boolean | required: false): Whether to call event on success.
|
336
370
|
fail (boolean | required: false): Whether to call event on success.
|
337
|
-
runtimeExceeded (int | required: false): The runtime after which event is called.
|
338
|
-
user (string
|
339
|
-
|
371
|
+
runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
|
372
|
+
user (string): User to be notified.
|
373
|
+
- events (map):
|
374
|
+
success (boolean | required: false): Whether to call event on success.
|
375
|
+
fail (boolean | required: false): Whether to call event on success.
|
376
|
+
runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
|
377
|
+
webhook (map):
|
340
378
|
name (string): Name of the webhook.
|
341
379
|
url (string): URL of the webhook.
|
342
380
|
payload (string): Webhook payload.
|
381
|
+
- events (map):
|
382
|
+
success (boolean | required: false): Whether to call event on success.
|
383
|
+
fail (boolean | required: false): Whether to call event on success.
|
384
|
+
runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
|
385
|
+
email (map):
|
386
|
+
name (string): Name of the email watcher.
|
387
|
+
recipients (map):
|
388
|
+
to (list(str)): List of email addresses to send the email to. Email can be sent only to the users who are added in the tenant.
|
343
389
|
retryOptions (map | required: false): # Workflow level retry options which will be applied to all steps.
|
344
390
|
duration (int | required: false): Duration in seconds after which the step is retried if it fails. Default is 5 seconds and maximum is 120 seconds.
|
345
391
|
exitCodes (list(int) | required: false): List of exit codes for which the step is retried. If not provided, the step is retried for all exit codes.
|
@@ -476,12 +522,24 @@ def patch(
|
|
476
522
|
- events (map):
|
477
523
|
success (boolean | required: false): Whether to call event on success.
|
478
524
|
fail (boolean | required: false): Whether to call event on success.
|
479
|
-
runtimeExceeded (int | required: false): The runtime after which event is called.
|
480
|
-
user (string
|
481
|
-
|
525
|
+
runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
|
526
|
+
user (string): User to be notified.
|
527
|
+
- events (map):
|
528
|
+
success (boolean | required: false): Whether to call event on success.
|
529
|
+
fail (boolean | required: false): Whether to call event on success.
|
530
|
+
runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
|
531
|
+
webhook (map):
|
482
532
|
name (string): Name of the webhook.
|
483
533
|
url (string): URL of the webhook.
|
484
534
|
payload (string): Webhook payload.
|
535
|
+
- events (map):
|
536
|
+
success (boolean | required: false): Whether to call event on success.
|
537
|
+
fail (boolean | required: false): Whether to call event on success.
|
538
|
+
runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
|
539
|
+
email (map):
|
540
|
+
name (string): Name of the email watcher.
|
541
|
+
recipients (map):
|
542
|
+
to (list(str)): List of email addresses to send the email to. Email can be sent only to the users who are added in the tenant.
|
485
543
|
retryOptions (map | required: false): # Workflow level retry options which will be applied to all steps.
|
486
544
|
duration (int | required: false): Duration in seconds after which the step is retried if it fails. Default is 5 seconds and maximum is 120 seconds.
|
487
545
|
exitCodes (list(int) | required: false): List of exit codes for which the step is retried. If not provided, the step is retried for all exit codes.
|
@@ -599,7 +657,7 @@ def list_workflows(
|
|
599
657
|
\b
|
600
658
|
📝 ***Example usage:***<br/>
|
601
659
|
```bash
|
602
|
-
peak workflows list --page-size 10 --page-number 1 --
|
660
|
+
peak workflows list --page-size 10 --page-number 1 --workflow-status "Draft" --last-execution-status "Success" --last-modified-by "abc@peak.ai" --name "test"
|
603
661
|
```
|
604
662
|
|
605
663
|
\b
|
@@ -847,6 +905,8 @@ def list_executions(
|
|
847
905
|
workflow_id: int = _WORKFLOW_ID,
|
848
906
|
date_from: Optional[str] = args.DATE_FROM,
|
849
907
|
date_to: Optional[str] = args.DATE_TO,
|
908
|
+
status: Optional[List[str]] = _EXECUTION_STATUS,
|
909
|
+
count: Optional[int] = _COUNT,
|
850
910
|
page_size: Optional[int] = args.PAGE_SIZE,
|
851
911
|
page_number: Optional[int] = args.PAGE_NUMBER,
|
852
912
|
paging: Optional[bool] = PAGING, # noqa: ARG001
|
@@ -857,7 +917,7 @@ def list_executions(
|
|
857
917
|
\b
|
858
918
|
📝 ***Example usage:***<br/>
|
859
919
|
```bash
|
860
|
-
peak workflows list-executions 9999 --page-size 10 --page-number 1
|
920
|
+
peak workflows list-executions 9999 --page-size 10 --page-number 1 --status "Running" --status "Failed"
|
861
921
|
```
|
862
922
|
|
863
923
|
\b
|
@@ -882,6 +942,8 @@ def list_executions(
|
|
882
942
|
workflow_id=workflow_id,
|
883
943
|
date_from=date_from,
|
884
944
|
date_to=date_to,
|
945
|
+
status=parse_list_of_strings(status),
|
946
|
+
count=count,
|
885
947
|
page_size=page_size,
|
886
948
|
page_number=page_number,
|
887
949
|
return_iterator=False,
|
@@ -974,7 +1036,7 @@ def get_execution_logs(
|
|
974
1036
|
writer.write(response)
|
975
1037
|
break
|
976
1038
|
|
977
|
-
next_token = response
|
1039
|
+
next_token = response.get("nextToken", None)
|
978
1040
|
|
979
1041
|
|
980
1042
|
@app.command(short_help="Get workflow execution details.", options_metavar="get_execution_details")
|
peak/cli/ruff.toml
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
extend = '../../pyproject.toml'
|
2
2
|
src = ["."]
|
3
|
-
ignore = [
|
3
|
+
lint.ignore = [
|
4
4
|
"UP007", # typer is not PEP-585 and PEP-604 compliant
|
5
5
|
"D208",
|
6
6
|
"FBT001",
|
7
7
|
"FBT002",
|
8
8
|
"FBT003",
|
9
|
-
"RSE102"
|
9
|
+
"RSE102",
|
10
|
+
"FA100"
|
11
|
+
# TODO: remove this once we are supporting > 3.10 properly
|
10
12
|
]
|
11
|
-
unfixable = []
|
13
|
+
lint.unfixable = []
|
peak/compression.py
CHANGED
@@ -65,7 +65,7 @@ def compress(path: str, ignore_files: Optional[list[str]] = None) -> Iterator[te
|
|
65
65
|
raise exceptions.FileLimitExceededException(constants.MAX_ARTIFACT_SIZE_MB)
|
66
66
|
|
67
67
|
# include directories as API backend need directories explicitly included
|
68
|
-
relative_root_path = Path(
|
68
|
+
relative_root_path = Path()
|
69
69
|
if relative_root_path in parent_directories:
|
70
70
|
parent_directories.remove(relative_root_path)
|
71
71
|
for directory in parent_directories:
|
@@ -192,7 +192,7 @@ def _build_files_dict(files: Iterable[str]) -> dict[str, dict]: # type: ignore[
|
|
192
192
|
"""
|
193
193
|
files_dict: dict[str, dict] = {} # type: ignore[type-arg]
|
194
194
|
for f in files:
|
195
|
-
components = os.path.normpath(f).
|
195
|
+
components = Path(os.path.normpath(f)).parts
|
196
196
|
current_dir = files_dict
|
197
197
|
for directory in components[:-1]:
|
198
198
|
if directory not in current_dir:
|
peak/exceptions.py
CHANGED
@@ -29,13 +29,11 @@ from typing import Any, ClassVar, Dict, List, Optional, Tuple, Type
|
|
29
29
|
class PeakBaseException(Exception):
|
30
30
|
"""Base exception class for the Peak SDK."""
|
31
31
|
|
32
|
-
...
|
33
|
-
|
34
32
|
|
35
33
|
class HttpExceptionsRegistryMeta(type):
|
36
34
|
"""Registry metaclass."""
|
37
35
|
|
38
|
-
REGISTRY: Dict[int, Any] = defaultdict(lambda: Exception)
|
36
|
+
REGISTRY: ClassVar[Dict[int, Any]] = defaultdict(lambda: Exception)
|
39
37
|
|
40
38
|
def __new__(
|
41
39
|
cls: "Type[HttpExceptionsRegistryMeta]",
|
@@ -58,7 +56,7 @@ class HttpExceptionsRegistryMeta(type):
|
|
58
56
|
HttpExceptionsRegistryMeta: the child class itself, forward annotated for type checking
|
59
57
|
"""
|
60
58
|
new_cls: "HttpExceptionsRegistryMeta" = type.__new__(cls, name, bases, attrs)
|
61
|
-
status_code: Optional[int] = attrs.get("STATUS_CODE"
|
59
|
+
status_code: Optional[int] = attrs.get("STATUS_CODE")
|
62
60
|
if status_code:
|
63
61
|
cls.REGISTRY[status_code] = new_cls
|
64
62
|
return new_cls
|
@@ -148,11 +146,11 @@ class MissingEnvironmentVariableException(PeakBaseException):
|
|
148
146
|
class FileLimitExceededException(PeakBaseException):
|
149
147
|
"""Limits on the file are exceeded."""
|
150
148
|
|
151
|
-
def __init__(self, max_size:
|
149
|
+
def __init__(self, max_size: float, *, message: str = "", units: str = "MB") -> None:
|
152
150
|
"""Throw exception with custom message.
|
153
151
|
|
154
152
|
Args:
|
155
|
-
max_size (
|
153
|
+
max_size (float): Maximum size of the file.
|
156
154
|
message (str): Additional message to add to exception.
|
157
155
|
units (str): Units of the maximum size.
|
158
156
|
"""
|
peak/handler.py
CHANGED
@@ -51,7 +51,7 @@ writer = Writer(ignore_debug_mode=True)
|
|
51
51
|
class HandlerRegistryMeta(type):
|
52
52
|
"""Metaclass for registering all types of Handler classes."""
|
53
53
|
|
54
|
-
REGISTRY: Dict[ContentType, BaseHandler] = {}
|
54
|
+
REGISTRY: ClassVar[Dict[ContentType, BaseHandler]] = {}
|
55
55
|
|
56
56
|
def __new__(
|
57
57
|
cls: "Type[HandlerRegistryMeta]",
|
@@ -79,7 +79,7 @@ class HandlerRegistryMeta(type):
|
|
79
79
|
error_invalid_content_type: str = f"Invalid content type for {name} handler"
|
80
80
|
new_cls: "HandlerRegistryMeta" = type.__new__(cls, name, bases, attrs)
|
81
81
|
|
82
|
-
content_type: Optional[ContentType] = attrs.get("CONTENT_TYPE"
|
82
|
+
content_type: Optional[ContentType] = attrs.get("CONTENT_TYPE")
|
83
83
|
try:
|
84
84
|
if content_type and ContentType(content_type):
|
85
85
|
cls.REGISTRY[content_type] = new_cls()
|
@@ -92,14 +92,12 @@ class HandlerRegistryMeta(type):
|
|
92
92
|
class _CombinedMeta(HandlerRegistryMeta, ABCMeta):
|
93
93
|
"""Utility class for combining multiple meta-classes."""
|
94
94
|
|
95
|
-
...
|
96
|
-
|
97
95
|
|
98
96
|
class AuthRetrySession(requests.Session):
|
99
97
|
"""Session with extra sugar attached."""
|
100
98
|
|
101
99
|
# used in testing, so we can modify the backoff_factor etc. to speed up the tests
|
102
|
-
_DEFAULT_RETRY_CONFIG: Dict[str, Any] = {
|
100
|
+
_DEFAULT_RETRY_CONFIG: ClassVar[Dict[str, Any]] = {
|
103
101
|
"backoff_factor": 2,
|
104
102
|
"total": 5,
|
105
103
|
"status_forcelist": [500, 502, 503, 504],
|
peak/helpers.py
CHANGED
@@ -44,7 +44,7 @@ def parse_body_for_multipart_request(body: Dict[str, Any]) -> Dict[str, str]:
|
|
44
44
|
Returns:
|
45
45
|
Dict[str, str]: the parsed object
|
46
46
|
"""
|
47
|
-
return {key: (value if
|
47
|
+
return {key: (value if isinstance(value, str) else json.dumps(value)) for (key, value) in body.items()}
|
48
48
|
|
49
49
|
|
50
50
|
def remove_keys(body: Dict[str, Any], keys: List[str]) -> Dict[str, Any]:
|
@@ -76,8 +76,7 @@ def get_base_domain(stage: str, subdomain: Optional[str] = "service") -> str:
|
|
76
76
|
stage = "dev"
|
77
77
|
|
78
78
|
domain: str = f"https://{subdomain}.{stage}.peak.ai"
|
79
|
-
|
80
|
-
return domain
|
79
|
+
return domain.replace("..", ".") # for prod domain
|
81
80
|
|
82
81
|
|
83
82
|
def parse_list_of_strings(param: List[str] | None) -> List[str] | None:
|
@@ -113,7 +112,7 @@ def snake_case_to_lower_camel_case(snake_case_string: str) -> str:
|
|
113
112
|
|
114
113
|
|
115
114
|
def variables_to_dict(*args: Any, frame: FrameType | None = None) -> Dict[str, str]:
|
116
|
-
"""Converts
|
115
|
+
"""Converts arbitrary variables to a dictionary.
|
117
116
|
|
118
117
|
Args:
|
119
118
|
args (str|int): tuple of string|int variables
|
@@ -143,11 +142,11 @@ def combine_dictionaries(
|
|
143
142
|
dict2: Dict[str, Any],
|
144
143
|
nested_keys_to_skip: Optional[List[str]] = [], # noqa: B006
|
145
144
|
) -> Dict[str, Any]:
|
146
|
-
"""Combines two
|
145
|
+
"""Combines two dictionaries. Values for second dictionary have higher precedence.
|
147
146
|
|
148
147
|
Args:
|
149
148
|
dict1 (Dict[str, Any]): dictionary 1
|
150
|
-
dict2 (Dict[str, Any]):
|
149
|
+
dict2 (Dict[str, Any]): dictionary 2
|
151
150
|
nested_keys_to_skip (List[str] | None): Keys for which nested combining is not required.
|
152
151
|
|
153
152
|
Returns:
|
@@ -158,7 +157,7 @@ def combine_dictionaries(
|
|
158
157
|
|
159
158
|
combined_dict = dict(dict1)
|
160
159
|
for key in dict2:
|
161
|
-
if key in combined_dict and
|
160
|
+
if key in combined_dict and isinstance(combined_dict[key], dict) and key not in (nested_keys_to_skip or []):
|
162
161
|
combined_dict[key] = combine_dictionaries(combined_dict[key], dict2[key])
|
163
162
|
else:
|
164
163
|
combined_dict[key] = dict2[key]
|
@@ -178,12 +177,12 @@ def map_user_options(
|
|
178
177
|
dict_type_keys (List[str]): List of keys which have json type values
|
179
178
|
|
180
179
|
Returns:
|
181
|
-
Dict[str, str]:
|
180
|
+
Dict[str, str]: Mapped dictionary
|
182
181
|
"""
|
183
182
|
result: Dict[str, Any] = {}
|
184
183
|
for key in user_options:
|
185
184
|
if key in mapping:
|
186
|
-
nested_dict = result
|
185
|
+
nested_dict = result.get(mapping[key], {})
|
187
186
|
nested_dict[key] = json.loads(user_options[key]) if key in dict_type_keys else user_options[key]
|
188
187
|
result[mapping[key]] = nested_dict
|
189
188
|
else:
|
@@ -239,3 +238,30 @@ def download_logs_helper(
|
|
239
238
|
if config.SOURCE == Sources.CLI:
|
240
239
|
writer = Writer()
|
241
240
|
writer.write(f"\nLog contents have been saved to: {file_name}")
|
241
|
+
|
242
|
+
|
243
|
+
def search_action(current_path: str, current_dict: Dict[str, Any], action: str) -> Dict[str, bool]:
|
244
|
+
"""Search for a specified action within a nested dictionary structure and check if deeper levels exist.
|
245
|
+
|
246
|
+
Args:
|
247
|
+
current_path (str): The dot-separated path representing the feature hierarchy.
|
248
|
+
current_dict (Dict[str, Any]): The nested dictionary representing the permissions structure.
|
249
|
+
action (str): The action to search for (e.g., "read" or "write").
|
250
|
+
|
251
|
+
Returns:
|
252
|
+
bool: A dictionary with keys 'has_permission' indicating if the action is found and
|
253
|
+
'deeper_levels' indicating if there are deeper levels that need specification.
|
254
|
+
"""
|
255
|
+
keys = current_path.split(".")
|
256
|
+
for key in keys:
|
257
|
+
if key in current_dict:
|
258
|
+
current_dict = current_dict[key]
|
259
|
+
else:
|
260
|
+
return {"has_permission": False, "deeper_levels": False}
|
261
|
+
|
262
|
+
if "actions" in current_dict:
|
263
|
+
actions = current_dict["actions"]
|
264
|
+
has_permission = "*" in actions or action in actions or ("write" in actions and action == "read")
|
265
|
+
return {"has_permission": has_permission, "deeper_levels": False}
|
266
|
+
|
267
|
+
return {"has_permission": False, "deeper_levels": True}
|
peak/output.py
CHANGED
@@ -105,7 +105,7 @@ class Writer:
|
|
105
105
|
title = params["title"]
|
106
106
|
|
107
107
|
if "subheader_key" in params and params["subheader_key"] in data:
|
108
|
-
subheader_title = "
|
108
|
+
subheader_title = params.get("subheader_title", "Total")
|
109
109
|
title = f'{title} ({subheader_title} = {data[params["subheader_key"]]})'
|
110
110
|
|
111
111
|
table = Table(
|
@@ -135,7 +135,7 @@ class Writer:
|
|
135
135
|
elif "parser" in key_details[1]:
|
136
136
|
v = key_details[1]["parser"](v)
|
137
137
|
|
138
|
-
if isinstance(v, (dict, list)):
|
138
|
+
if isinstance(v, (dict, list)):
|
139
139
|
parsed_value = json_highlighter(Text(json.dumps(v, indent=2), overflow="fold"))
|
140
140
|
else:
|
141
141
|
parsed_value = Text(str(v), overflow="fold")
|
peak/press/apps.py
CHANGED
@@ -48,8 +48,7 @@ class App(BaseClient):
|
|
48
48
|
page_number: Optional[int] = None,
|
49
49
|
*,
|
50
50
|
return_iterator: Literal[False],
|
51
|
-
) -> Dict[str, Any]:
|
52
|
-
...
|
51
|
+
) -> Dict[str, Any]: ...
|
53
52
|
|
54
53
|
@overload
|
55
54
|
def list_specs(
|
@@ -64,8 +63,7 @@ class App(BaseClient):
|
|
64
63
|
page_number: Optional[int] = None,
|
65
64
|
*,
|
66
65
|
return_iterator: Literal[True] = True,
|
67
|
-
) -> Iterator[Dict[str, Any]]:
|
68
|
-
...
|
66
|
+
) -> Iterator[Dict[str, Any]]: ...
|
69
67
|
|
70
68
|
def list_specs(
|
71
69
|
self,
|
@@ -440,8 +438,7 @@ class App(BaseClient):
|
|
440
438
|
page_number: Optional[int] = None,
|
441
439
|
*,
|
442
440
|
return_iterator: Literal[False],
|
443
|
-
) -> Dict[str, Any]:
|
444
|
-
...
|
441
|
+
) -> Dict[str, Any]: ...
|
445
442
|
|
446
443
|
@overload
|
447
444
|
def list_spec_releases(
|
@@ -452,8 +449,7 @@ class App(BaseClient):
|
|
452
449
|
page_number: Optional[int] = None,
|
453
450
|
*,
|
454
451
|
return_iterator: Literal[True] = True,
|
455
|
-
) -> Iterator[Dict[str, Any]]:
|
456
|
-
...
|
452
|
+
) -> Iterator[Dict[str, Any]]: ...
|
457
453
|
|
458
454
|
def list_spec_releases(
|
459
455
|
self,
|
@@ -595,8 +591,7 @@ class App(BaseClient):
|
|
595
591
|
page_number: Optional[int] = None,
|
596
592
|
*,
|
597
593
|
return_iterator: Literal[False],
|
598
|
-
) -> Dict[str, Any]:
|
599
|
-
...
|
594
|
+
) -> Dict[str, Any]: ...
|
600
595
|
|
601
596
|
@overload
|
602
597
|
def list_deployments(
|
@@ -608,8 +603,7 @@ class App(BaseClient):
|
|
608
603
|
page_number: Optional[int] = None,
|
609
604
|
*,
|
610
605
|
return_iterator: Literal[True] = True,
|
611
|
-
) -> Iterator[Dict[str, Any]]:
|
612
|
-
...
|
606
|
+
) -> Iterator[Dict[str, Any]]: ...
|
613
607
|
|
614
608
|
def list_deployments(
|
615
609
|
self,
|
@@ -844,8 +838,7 @@ class App(BaseClient):
|
|
844
838
|
page_number: Optional[int] = None,
|
845
839
|
*,
|
846
840
|
return_iterator: Literal[False],
|
847
|
-
) -> Dict[str, Any]:
|
848
|
-
...
|
841
|
+
) -> Dict[str, Any]: ...
|
849
842
|
|
850
843
|
@overload
|
851
844
|
def list_deployment_revisions(
|
@@ -857,8 +850,7 @@ class App(BaseClient):
|
|
857
850
|
page_number: Optional[int] = None,
|
858
851
|
*,
|
859
852
|
return_iterator: Literal[True] = True,
|
860
|
-
) -> Iterator[Dict[str, Any]]:
|
861
|
-
...
|
853
|
+
) -> Iterator[Dict[str, Any]]: ...
|
862
854
|
|
863
855
|
def list_deployment_revisions(
|
864
856
|
self,
|
peak/press/blocks.py
CHANGED
@@ -52,8 +52,7 @@ class Block(BaseClient):
|
|
52
52
|
page_number: Optional[int] = None,
|
53
53
|
*,
|
54
54
|
return_iterator: Literal[False],
|
55
|
-
) -> Dict[str, Any]:
|
56
|
-
...
|
55
|
+
) -> Dict[str, Any]: ...
|
57
56
|
|
58
57
|
@overload
|
59
58
|
def list_specs(
|
@@ -68,8 +67,7 @@ class Block(BaseClient):
|
|
68
67
|
page_number: Optional[int] = None,
|
69
68
|
*,
|
70
69
|
return_iterator: Literal[True] = True,
|
71
|
-
) -> Iterator[Dict[str, Any]]:
|
72
|
-
...
|
70
|
+
) -> Iterator[Dict[str, Any]]: ...
|
73
71
|
|
74
72
|
def list_specs(
|
75
73
|
self,
|
@@ -582,8 +580,7 @@ class Block(BaseClient):
|
|
582
580
|
page_number: Optional[int] = None,
|
583
581
|
*,
|
584
582
|
return_iterator: Literal[False],
|
585
|
-
) -> Dict[str, Any]:
|
586
|
-
...
|
583
|
+
) -> Dict[str, Any]: ...
|
587
584
|
|
588
585
|
@overload
|
589
586
|
def list_spec_releases(
|
@@ -594,8 +591,7 @@ class Block(BaseClient):
|
|
594
591
|
page_number: Optional[int] = None,
|
595
592
|
*,
|
596
593
|
return_iterator: Literal[True] = True,
|
597
|
-
) -> Iterator[Dict[str, Any]]:
|
598
|
-
...
|
594
|
+
) -> Iterator[Dict[str, Any]]: ...
|
599
595
|
|
600
596
|
def list_spec_releases(
|
601
597
|
self,
|
@@ -774,8 +770,7 @@ class Block(BaseClient):
|
|
774
770
|
page_number: Optional[int] = None,
|
775
771
|
*,
|
776
772
|
return_iterator: Literal[False],
|
777
|
-
) -> Dict[str, Any]:
|
778
|
-
...
|
773
|
+
) -> Dict[str, Any]: ...
|
779
774
|
|
780
775
|
@overload
|
781
776
|
def list_deployments(
|
@@ -788,8 +783,7 @@ class Block(BaseClient):
|
|
788
783
|
page_number: Optional[int] = None,
|
789
784
|
*,
|
790
785
|
return_iterator: Literal[True] = True,
|
791
|
-
) -> Iterator[Dict[str, Any]]:
|
792
|
-
...
|
786
|
+
) -> Iterator[Dict[str, Any]]: ...
|
793
787
|
|
794
788
|
def list_deployments(
|
795
789
|
self,
|
@@ -1070,8 +1064,7 @@ class Block(BaseClient):
|
|
1070
1064
|
page_number: Optional[int] = None,
|
1071
1065
|
*,
|
1072
1066
|
return_iterator: Literal[False],
|
1073
|
-
) -> Dict[str, Any]:
|
1074
|
-
...
|
1067
|
+
) -> Dict[str, Any]: ...
|
1075
1068
|
|
1076
1069
|
@overload
|
1077
1070
|
def list_deployment_revisions(
|
@@ -1083,8 +1076,7 @@ class Block(BaseClient):
|
|
1083
1076
|
page_number: Optional[int] = None,
|
1084
1077
|
*,
|
1085
1078
|
return_iterator: Literal[True] = True,
|
1086
|
-
) -> Iterator[Dict[str, Any]]:
|
1087
|
-
...
|
1079
|
+
) -> Iterator[Dict[str, Any]]: ...
|
1088
1080
|
|
1089
1081
|
def list_deployment_revisions(
|
1090
1082
|
self,
|
peak/press/deployments.py
CHANGED
@@ -44,8 +44,7 @@ class Deployment(BaseClient):
|
|
44
44
|
page_number: Optional[int] = None,
|
45
45
|
*,
|
46
46
|
return_iterator: Literal[False],
|
47
|
-
) -> Dict[str, Any]:
|
48
|
-
...
|
47
|
+
) -> Dict[str, Any]: ...
|
49
48
|
|
50
49
|
@overload
|
51
50
|
def list_deployments(
|
@@ -58,8 +57,7 @@ class Deployment(BaseClient):
|
|
58
57
|
page_number: Optional[int] = None,
|
59
58
|
*,
|
60
59
|
return_iterator: Literal[True] = True,
|
61
|
-
) -> Iterator[Dict[str, Any]]:
|
62
|
-
...
|
60
|
+
) -> Iterator[Dict[str, Any]]: ...
|
63
61
|
|
64
62
|
def list_deployments(
|
65
63
|
self,
|