peak-sdk 1.6.0__py3-none-any.whl → 1.8.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 +78 -58
- peak/_version.py +1 -1
- peak/callbacks.py +22 -2
- peak/cli/args.py +19 -0
- peak/cli/helpers.py +12 -8
- peak/cli/press/apps/deployments.py +72 -18
- peak/cli/press/apps/specs.py +29 -11
- peak/cli/press/blocks/deployments.py +71 -18
- peak/cli/press/blocks/specs.py +95 -35
- peak/cli/press/deployments.py +40 -1
- peak/cli/press/specs.py +2 -2
- peak/cli/resources/alerts/emails.py +4 -5
- peak/cli/resources/artifacts.py +9 -9
- peak/cli/resources/images.py +29 -18
- peak/cli/resources/services.py +6 -7
- peak/cli/resources/tenants.py +4 -2
- peak/cli/resources/users.py +3 -3
- peak/cli/resources/webapps.py +6 -6
- peak/cli/resources/workflows.py +24 -25
- peak/compression.py +28 -13
- peak/exceptions.py +15 -1
- peak/handler.py +5 -1
- peak/helpers.py +38 -0
- peak/output.py +13 -6
- peak/press/apps.py +43 -3
- peak/press/blocks.py +450 -138
- peak/press/deployments.py +25 -0
- peak/resources/images.py +309 -86
- peak/sample_yaml/press/apps/specs/create_app_spec.yaml +2 -0
- peak/sample_yaml/press/apps/specs/create_app_spec_release.yaml +2 -0
- peak/sample_yaml/press/blocks/specs/service/api/create_block_spec.yaml +102 -0
- peak/sample_yaml/press/blocks/specs/service/api/create_block_spec_release.yaml +88 -0
- peak/sample_yaml/press/blocks/specs/service/webapp/create_block_spec.yaml +103 -0
- peak/sample_yaml/press/blocks/specs/service/webapp/create_block_spec_release.yaml +89 -0
- peak/sample_yaml/press/blocks/specs/{create_block_spec.yaml → workflow/create_block_spec.yaml} +20 -1
- peak/sample_yaml/press/blocks/specs/{create_block_spec_release.yaml → workflow/create_block_spec_release.yaml} +20 -1
- peak/sample_yaml/resources/images/dockerfile/create_image.yaml +3 -0
- peak/sample_yaml/resources/images/dockerfile/create_image_version.yaml +3 -0
- peak/sample_yaml/resources/images/dockerfile/update_version.yaml +3 -0
- peak/sample_yaml/resources/images/github/create_image.yaml +3 -0
- peak/sample_yaml/resources/images/github/create_image_version.yaml +3 -0
- peak/sample_yaml/resources/images/github/update_version.yaml +3 -0
- peak/sample_yaml/resources/images/upload/create_image.yaml +3 -0
- peak/sample_yaml/resources/images/upload/create_image_version.yaml +3 -0
- peak/sample_yaml/resources/images/upload/create_or_update_image.yaml +3 -0
- peak/sample_yaml/resources/images/upload/update_version.yaml +3 -0
- peak/sample_yaml/resources/workflows/create_or_update_workflow.yaml +9 -1
- peak/sample_yaml/resources/workflows/create_workflow.yaml +9 -1
- peak/sample_yaml/resources/workflows/patch_workflow.yaml +9 -1
- peak/sample_yaml/resources/workflows/update_workflow.yaml +9 -1
- peak/session.py +1 -1
- peak/template.py +21 -2
- {peak_sdk-1.6.0.dist-info → peak_sdk-1.8.0.dist-info}/METADATA +18 -18
- {peak_sdk-1.6.0.dist-info → peak_sdk-1.8.0.dist-info}/RECORD +57 -53
- {peak_sdk-1.6.0.dist-info → peak_sdk-1.8.0.dist-info}/LICENSE +0 -0
- {peak_sdk-1.6.0.dist-info → peak_sdk-1.8.0.dist-info}/WHEEL +0 -0
- {peak_sdk-1.6.0.dist-info → peak_sdk-1.8.0.dist-info}/entry_points.txt +0 -0
peak/cli/resources/services.py
CHANGED
@@ -107,7 +107,7 @@ MAPPING = {
|
|
107
107
|
}
|
108
108
|
|
109
109
|
|
110
|
-
@app.command("list", short_help="List services."
|
110
|
+
@app.command("list", short_help="List services.")
|
111
111
|
def list_services(
|
112
112
|
ctx: typer.Context,
|
113
113
|
page_size: Optional[int] = args.PAGE_SIZE,
|
@@ -155,7 +155,7 @@ def list_services(
|
|
155
155
|
writer.write(response)
|
156
156
|
|
157
157
|
|
158
|
-
@app.command(short_help="Create a new service."
|
158
|
+
@app.command(short_help="Create a new service.")
|
159
159
|
def create(
|
160
160
|
ctx: typer.Context,
|
161
161
|
file: Annotated[Optional[str], typer.Argument(..., help=FILE_HELP_STRING)] = None,
|
@@ -267,7 +267,7 @@ def create(
|
|
267
267
|
writer.write(response)
|
268
268
|
|
269
269
|
|
270
|
-
@app.command(short_help="Update an existing service."
|
270
|
+
@app.command(short_help="Update an existing service.")
|
271
271
|
def update(
|
272
272
|
ctx: typer.Context,
|
273
273
|
service_id: str = _SERVICE_ID,
|
@@ -383,7 +383,6 @@ def update(
|
|
383
383
|
|
384
384
|
@app.command(
|
385
385
|
short_help="Create a new service or Update an existing service.",
|
386
|
-
options_metavar="create_or_update_service",
|
387
386
|
)
|
388
387
|
def create_or_update(
|
389
388
|
ctx: typer.Context,
|
@@ -502,7 +501,7 @@ def create_or_update(
|
|
502
501
|
writer.write(response)
|
503
502
|
|
504
503
|
|
505
|
-
@app.command(short_help="Delete an existing service."
|
504
|
+
@app.command(short_help="Delete an existing service.")
|
506
505
|
def delete(
|
507
506
|
ctx: typer.Context,
|
508
507
|
service_id: str = _SERVICE_ID,
|
@@ -536,7 +535,7 @@ def delete(
|
|
536
535
|
writer.write(response)
|
537
536
|
|
538
537
|
|
539
|
-
@app.command(short_help="Describe details of a service."
|
538
|
+
@app.command(short_help="Describe details of a service.")
|
540
539
|
def describe(
|
541
540
|
ctx: typer.Context,
|
542
541
|
service_id: str = _SERVICE_ID,
|
@@ -588,7 +587,7 @@ def describe(
|
|
588
587
|
writer.write(response)
|
589
588
|
|
590
589
|
|
591
|
-
@app.command(short_help="Test API type service"
|
590
|
+
@app.command(short_help="Test API type service")
|
592
591
|
def test(
|
593
592
|
ctx: typer.Context,
|
594
593
|
service_name: str = _SERVICE_NAME,
|
peak/cli/resources/tenants.py
CHANGED
@@ -32,13 +32,15 @@ app = typer.Typer(
|
|
32
32
|
short_help="Create and manage Tenant Settings.",
|
33
33
|
)
|
34
34
|
|
35
|
-
_ENTITY_TYPE = typer.Option(
|
35
|
+
_ENTITY_TYPE = typer.Option(
|
36
|
+
...,
|
37
|
+
help="Entity type to be used in this operation (e.g. - `workflow`, `webapp`, `api-deployment`).",
|
38
|
+
)
|
36
39
|
|
37
40
|
|
38
41
|
@app.command(
|
39
42
|
"list-instance-options",
|
40
43
|
short_help="List tenant instance options.",
|
41
|
-
options_metavar="list_tenant_instance_options",
|
42
44
|
)
|
43
45
|
def list_instance_options(
|
44
46
|
ctx: typer.Context,
|
peak/cli/resources/users.py
CHANGED
@@ -24,7 +24,7 @@ from typing import Optional
|
|
24
24
|
import typer
|
25
25
|
from peak import Session
|
26
26
|
from peak.cli.args import OUTPUT_TYPES, PAGING
|
27
|
-
from peak.constants import OutputTypesNoTable
|
27
|
+
from peak.constants import OutputTypes, OutputTypesNoTable
|
28
28
|
from peak.output import Writer
|
29
29
|
from peak.resources import users
|
30
30
|
|
@@ -41,7 +41,7 @@ _AUTH_TOKEN = typer.Option(
|
|
41
41
|
)
|
42
42
|
|
43
43
|
|
44
|
-
@app.command(short_help="Check user permission for a specific feature."
|
44
|
+
@app.command(short_help="Check user permission for a specific feature.")
|
45
45
|
def check_permission(
|
46
46
|
ctx: typer.Context,
|
47
47
|
feature: str = _FEATURE,
|
@@ -68,4 +68,4 @@ def check_permission(
|
|
68
68
|
|
69
69
|
with writer.pager():
|
70
70
|
response = user_client.check_permissions({feature: action})
|
71
|
-
writer.write(response.get(feature, False))
|
71
|
+
writer.write(response.get(feature, False), output_type=OutputTypes.json)
|
peak/cli/resources/webapps.py
CHANGED
@@ -84,7 +84,7 @@ MAPPING = {"imageId": "imageDetails", "versionId": "imageDetails", "instanceType
|
|
84
84
|
DEPRECATION_MESSAGE = "The Web App commands are deprecated and will eventually be removed. We recommend using the Service feature which offers a more comprehensive and flexible solution for managing web applications and API deployments.\n"
|
85
85
|
|
86
86
|
|
87
|
-
@app.command("list", short_help="List webapps."
|
87
|
+
@app.command("list", short_help="List webapps.")
|
88
88
|
def list_webapps(
|
89
89
|
ctx: typer.Context,
|
90
90
|
page_size: Optional[int] = args.PAGE_SIZE,
|
@@ -138,7 +138,7 @@ def list_webapps(
|
|
138
138
|
writer.write(response, deprecation_message=DEPRECATION_MESSAGE)
|
139
139
|
|
140
140
|
|
141
|
-
@app.command(short_help="Create a new webapp."
|
141
|
+
@app.command(short_help="Create a new webapp.")
|
142
142
|
def create(
|
143
143
|
ctx: typer.Context,
|
144
144
|
file: Annotated[Optional[str], typer.Argument(..., help=FILE_HELP_STRING)] = None,
|
@@ -229,7 +229,7 @@ def create(
|
|
229
229
|
writer.write(response, deprecation_message=DEPRECATION_MESSAGE)
|
230
230
|
|
231
231
|
|
232
|
-
@app.command(short_help="Update an existing webapp."
|
232
|
+
@app.command(short_help="Update an existing webapp.")
|
233
233
|
def update(
|
234
234
|
ctx: typer.Context,
|
235
235
|
webapp_id: str = _WEBAPP_ID,
|
@@ -322,7 +322,7 @@ def update(
|
|
322
322
|
writer.write(response, deprecation_message=DEPRECATION_MESSAGE)
|
323
323
|
|
324
324
|
|
325
|
-
@app.command(short_help="Create a new webapp or Update an existing webapp."
|
325
|
+
@app.command(short_help="Create a new webapp or Update an existing webapp.")
|
326
326
|
def create_or_update(
|
327
327
|
ctx: typer.Context,
|
328
328
|
file: Annotated[Optional[str], typer.Argument(..., help=FILE_HELP_STRING)] = None,
|
@@ -415,7 +415,7 @@ def create_or_update(
|
|
415
415
|
writer.write(response, deprecation_message=DEPRECATION_MESSAGE)
|
416
416
|
|
417
417
|
|
418
|
-
@app.command(short_help="Delete an existing webapp."
|
418
|
+
@app.command(short_help="Delete an existing webapp.")
|
419
419
|
def delete(
|
420
420
|
ctx: typer.Context,
|
421
421
|
webapp_id: str = _WEBAPP_ID,
|
@@ -457,7 +457,7 @@ def delete(
|
|
457
457
|
writer.write(response, deprecation_message=DEPRECATION_MESSAGE)
|
458
458
|
|
459
459
|
|
460
|
-
@app.command(short_help="Describe details of a webapp."
|
460
|
+
@app.command(short_help="Describe details of a webapp.")
|
461
461
|
def describe(
|
462
462
|
ctx: typer.Context,
|
463
463
|
webapp_id: str = _WEBAPP_ID,
|
peak/cli/resources/workflows.py
CHANGED
@@ -93,7 +93,7 @@ _COUNT = typer.Option(
|
|
93
93
|
)
|
94
94
|
|
95
95
|
|
96
|
-
@app.command(short_help="Create a new workflow."
|
96
|
+
@app.command(short_help="Create a new workflow.")
|
97
97
|
def create(
|
98
98
|
ctx: typer.Context,
|
99
99
|
file: str = args.TEMPLATE_PATH,
|
@@ -119,12 +119,12 @@ def create(
|
|
119
119
|
watchers (list(map) | required: false):
|
120
120
|
- events (map):
|
121
121
|
success (boolean | required: false): Whether to call event on success.
|
122
|
-
fail (boolean | required: false): Whether to call event on
|
122
|
+
fail (boolean | required: false): Whether to call event on failure.
|
123
123
|
runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
|
124
124
|
user (string): User to be notified.
|
125
125
|
- events (map):
|
126
126
|
success (boolean | required: false): Whether to call event on success.
|
127
|
-
fail (boolean | required: false): Whether to call event on
|
127
|
+
fail (boolean | required: false): Whether to call event on failure.
|
128
128
|
runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
|
129
129
|
webhook (map):
|
130
130
|
name (string): Name of the webhook.
|
@@ -132,7 +132,7 @@ def create(
|
|
132
132
|
payload (string): Webhook payload.
|
133
133
|
- events (map):
|
134
134
|
success (boolean | required: false): Whether to call event on success.
|
135
|
-
fail (boolean | required: false): Whether to call event on
|
135
|
+
fail (boolean | required: false): Whether to call event on failure.
|
136
136
|
runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
|
137
137
|
email (map):
|
138
138
|
name (string): Name of the email watcher.
|
@@ -216,7 +216,7 @@ def create(
|
|
216
216
|
writer.write(response)
|
217
217
|
|
218
218
|
|
219
|
-
@app.command(short_help="Update an existing workflow."
|
219
|
+
@app.command(short_help="Update an existing workflow.")
|
220
220
|
def update(
|
221
221
|
ctx: typer.Context,
|
222
222
|
workflow_id: int = _WORKFLOW_ID,
|
@@ -243,12 +243,12 @@ def update(
|
|
243
243
|
watchers (list(map) | required: false):
|
244
244
|
- events (map):
|
245
245
|
success (boolean | required: false): Whether to call event on success.
|
246
|
-
fail (boolean | required: false): Whether to call event on
|
246
|
+
fail (boolean | required: false): Whether to call event on failure.
|
247
247
|
runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
|
248
248
|
user (string): User to be notified.
|
249
249
|
- events (map):
|
250
250
|
success (boolean | required: false): Whether to call event on success.
|
251
|
-
fail (boolean | required: false): Whether to call event on
|
251
|
+
fail (boolean | required: false): Whether to call event on failure.
|
252
252
|
runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
|
253
253
|
webhook (map):
|
254
254
|
name (string): Name of the webhook.
|
@@ -256,7 +256,7 @@ def update(
|
|
256
256
|
payload (string): Webhook payload.
|
257
257
|
- events (map):
|
258
258
|
success (boolean | required: false): Whether to call event on success.
|
259
|
-
fail (boolean | required: false): Whether to call event on
|
259
|
+
fail (boolean | required: false): Whether to call event on failure.
|
260
260
|
runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
|
261
261
|
email (map):
|
262
262
|
name (string): Name of the email watcher.
|
@@ -342,7 +342,6 @@ def update(
|
|
342
342
|
|
343
343
|
@app.command(
|
344
344
|
short_help="Create a new workflow or Update an existing workflow.",
|
345
|
-
options_metavar="create_or_update_workflow",
|
346
345
|
)
|
347
346
|
def create_or_update(
|
348
347
|
ctx: typer.Context,
|
@@ -367,12 +366,12 @@ def create_or_update(
|
|
367
366
|
watchers (list(map) | required: false):
|
368
367
|
- events (map):
|
369
368
|
success (boolean | required: false): Whether to call event on success.
|
370
|
-
fail (boolean | required: false): Whether to call event on
|
369
|
+
fail (boolean | required: false): Whether to call event on failure.
|
371
370
|
runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
|
372
371
|
user (string): User to be notified.
|
373
372
|
- events (map):
|
374
373
|
success (boolean | required: false): Whether to call event on success.
|
375
|
-
fail (boolean | required: false): Whether to call event on
|
374
|
+
fail (boolean | required: false): Whether to call event on failure.
|
376
375
|
runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
|
377
376
|
webhook (map):
|
378
377
|
name (string): Name of the webhook.
|
@@ -380,7 +379,7 @@ def create_or_update(
|
|
380
379
|
payload (string): Webhook payload.
|
381
380
|
- events (map):
|
382
381
|
success (boolean | required: false): Whether to call event on success.
|
383
|
-
fail (boolean | required: false): Whether to call event on
|
382
|
+
fail (boolean | required: false): Whether to call event on failure.
|
384
383
|
runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
|
385
384
|
email (map):
|
386
385
|
name (string): Name of the email watcher.
|
@@ -464,7 +463,7 @@ def create_or_update(
|
|
464
463
|
writer.write(response)
|
465
464
|
|
466
465
|
|
467
|
-
@app.command(short_help="Update required fields of an existing workflow."
|
466
|
+
@app.command(short_help="Update required fields of an existing workflow.")
|
468
467
|
def patch(
|
469
468
|
ctx: typer.Context,
|
470
469
|
workflow_id: int = _WORKFLOW_ID,
|
@@ -521,12 +520,12 @@ def patch(
|
|
521
520
|
watchers (list(map) | required: false):
|
522
521
|
- events (map):
|
523
522
|
success (boolean | required: false): Whether to call event on success.
|
524
|
-
fail (boolean | required: false): Whether to call event on
|
523
|
+
fail (boolean | required: false): Whether to call event on failure.
|
525
524
|
runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
|
526
525
|
user (string): User to be notified.
|
527
526
|
- events (map):
|
528
527
|
success (boolean | required: false): Whether to call event on success.
|
529
|
-
fail (boolean | required: false): Whether to call event on
|
528
|
+
fail (boolean | required: false): Whether to call event on failure.
|
530
529
|
runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
|
531
530
|
webhook (map):
|
532
531
|
name (string): Name of the webhook.
|
@@ -534,7 +533,7 @@ def patch(
|
|
534
533
|
payload (string): Webhook payload.
|
535
534
|
- events (map):
|
536
535
|
success (boolean | required: false): Whether to call event on success.
|
537
|
-
fail (boolean | required: false): Whether to call event on
|
536
|
+
fail (boolean | required: false): Whether to call event on failure.
|
538
537
|
runtimeExceeded (int | required: false): The runtime in minutes after which event is called.
|
539
538
|
email (map):
|
540
539
|
name (string): Name of the email watcher.
|
@@ -640,7 +639,7 @@ def patch(
|
|
640
639
|
writer.write(response)
|
641
640
|
|
642
641
|
|
643
|
-
@app.command("list", short_help="List workflows."
|
642
|
+
@app.command("list", short_help="List workflows.")
|
644
643
|
def list_workflows(
|
645
644
|
ctx: typer.Context,
|
646
645
|
page_size: Optional[int] = args.PAGE_SIZE,
|
@@ -690,7 +689,7 @@ def list_workflows(
|
|
690
689
|
writer.write(response)
|
691
690
|
|
692
691
|
|
693
|
-
@app.command(short_help="Describe details of a workflow."
|
692
|
+
@app.command(short_help="Describe details of a workflow.")
|
694
693
|
def describe(
|
695
694
|
ctx: typer.Context,
|
696
695
|
workflow_id: int = _WORKFLOW_ID,
|
@@ -734,7 +733,7 @@ def describe(
|
|
734
733
|
writer.write(response)
|
735
734
|
|
736
735
|
|
737
|
-
@app.command(short_help="Delete a workflow."
|
736
|
+
@app.command(short_help="Delete a workflow.")
|
738
737
|
def delete(
|
739
738
|
ctx: typer.Context,
|
740
739
|
workflow_id: int = _WORKFLOW_ID,
|
@@ -766,7 +765,7 @@ def delete(
|
|
766
765
|
writer.write(response)
|
767
766
|
|
768
767
|
|
769
|
-
@app.command(short_help="Start a workflow run."
|
768
|
+
@app.command(short_help="Start a workflow run.")
|
770
769
|
def execute(
|
771
770
|
ctx: typer.Context,
|
772
771
|
workflow_id: int = _WORKFLOW_ID,
|
@@ -828,7 +827,7 @@ def execute(
|
|
828
827
|
writer.write(response)
|
829
828
|
|
830
829
|
|
831
|
-
@app.command(short_help="List all available resources."
|
830
|
+
@app.command(short_help="List all available resources.")
|
832
831
|
def list_resources(
|
833
832
|
ctx: typer.Context,
|
834
833
|
paging: Optional[bool] = PAGING, # noqa: ARG001
|
@@ -866,7 +865,7 @@ def list_resources(
|
|
866
865
|
writer.write(response)
|
867
866
|
|
868
867
|
|
869
|
-
@app.command(short_help="List default resources."
|
868
|
+
@app.command(short_help="List default resources.")
|
870
869
|
def list_default_resources(
|
871
870
|
ctx: typer.Context,
|
872
871
|
paging: Optional[bool] = PAGING, # noqa: ARG001
|
@@ -899,7 +898,7 @@ def list_default_resources(
|
|
899
898
|
writer.write(response)
|
900
899
|
|
901
900
|
|
902
|
-
@app.command(short_help="List executions for the given workflow."
|
901
|
+
@app.command(short_help="List executions for the given workflow.")
|
903
902
|
def list_executions(
|
904
903
|
ctx: typer.Context,
|
905
904
|
workflow_id: int = _WORKFLOW_ID,
|
@@ -951,7 +950,7 @@ def list_executions(
|
|
951
950
|
writer.write(response)
|
952
951
|
|
953
952
|
|
954
|
-
@app.command(short_help="Get workflow execution logs."
|
953
|
+
@app.command(short_help="Get workflow execution logs.")
|
955
954
|
def get_execution_logs(
|
956
955
|
ctx: typer.Context,
|
957
956
|
workflow_id: int = _WORKFLOW_ID_OPTION,
|
@@ -1039,7 +1038,7 @@ def get_execution_logs(
|
|
1039
1038
|
next_token = response.get("nextToken", None)
|
1040
1039
|
|
1041
1040
|
|
1042
|
-
@app.command(short_help="Get workflow execution details."
|
1041
|
+
@app.command(short_help="Get workflow execution details.")
|
1043
1042
|
def get_execution_details(
|
1044
1043
|
ctx: typer.Context,
|
1045
1044
|
workflow_id: int = _WORKFLOW_ID_OPTION,
|
peak/compression.py
CHANGED
@@ -19,6 +19,7 @@
|
|
19
19
|
# # along with this program. If not, see <https://apache.org/licenses/LICENSE-2.0>
|
20
20
|
#
|
21
21
|
"""Compression module to create zip file to be used as artifact."""
|
22
|
+
|
22
23
|
from __future__ import annotations
|
23
24
|
|
24
25
|
import contextlib
|
@@ -26,7 +27,7 @@ import os
|
|
26
27
|
import tempfile
|
27
28
|
import zipfile
|
28
29
|
from pathlib import Path
|
29
|
-
from typing import Iterable, Iterator, Optional, Set
|
30
|
+
from typing import Any, Dict, Iterable, Iterator, Optional, Set
|
30
31
|
|
31
32
|
from pathspec import PathSpec
|
32
33
|
|
@@ -163,7 +164,7 @@ def _load_ignore_patterns(path_obj: Path, ignore_files: Optional[list[str]]) ->
|
|
163
164
|
|
164
165
|
|
165
166
|
def print_file_tree(files: Iterable[str]) -> None:
|
166
|
-
"""Prints list of files in tree format.
|
167
|
+
"""Prints list of files in tree format with specific limits per level.
|
167
168
|
|
168
169
|
Args:
|
169
170
|
files (list[str]): List of file paths
|
@@ -171,33 +172,47 @@ def print_file_tree(files: Iterable[str]) -> None:
|
|
171
172
|
writer = output.Writer(ignore_debug_mode=True)
|
172
173
|
files_dict = _build_files_dict(files)
|
173
174
|
|
174
|
-
|
175
|
+
limits = {1: 100, 2: 100, 3: 50}
|
176
|
+
default_limit = 25
|
177
|
+
|
178
|
+
def _print_tree(files_dict: Dict[str, Any], indent: str, level: int) -> None:
|
179
|
+
limit = limits.get(level, default_limit)
|
180
|
+
count = 0
|
181
|
+
|
182
|
+
for key, value in files_dict.items():
|
183
|
+
if isinstance(value, dict):
|
184
|
+
writer.write(f"{indent}{key}/")
|
185
|
+
new_indent = indent + ("| " if indent else "├── ")
|
186
|
+
_print_tree(value, new_indent, level + 1)
|
187
|
+
|
175
188
|
for key, value in files_dict.items():
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
189
|
+
if not isinstance(value, dict):
|
190
|
+
if count >= limit:
|
191
|
+
writer.write(f"{indent}...")
|
192
|
+
break
|
193
|
+
writer.write(f"{indent}{key}")
|
194
|
+
count += 1
|
180
195
|
|
181
|
-
_print_tree(files_dict, "")
|
196
|
+
_print_tree(files_dict, "", 1)
|
182
197
|
|
183
198
|
|
184
|
-
def _build_files_dict(files: Iterable[str]) ->
|
199
|
+
def _build_files_dict(files: Iterable[str]) -> Dict[str, Any]:
|
185
200
|
"""Builds a nested dictionary from list of files.
|
186
201
|
|
187
202
|
Args:
|
188
203
|
files (list[str]): List of file paths to process.
|
189
204
|
|
190
205
|
Returns:
|
191
|
-
dict[str,
|
206
|
+
dict[str, Any]: Nested dict file tree structure.
|
192
207
|
"""
|
193
|
-
files_dict:
|
208
|
+
files_dict: Dict[str, Any] = {}
|
194
209
|
for f in files:
|
195
210
|
components = Path(os.path.normpath(f)).parts
|
196
211
|
current_dir = files_dict
|
197
212
|
for directory in components[:-1]:
|
198
|
-
if directory not in current_dir:
|
213
|
+
if directory not in current_dir or current_dir[directory] is None:
|
199
214
|
current_dir[directory] = {}
|
200
215
|
current_dir = current_dir[directory]
|
201
216
|
if components[-1] not in current_dir:
|
202
|
-
current_dir[components[-1]] =
|
217
|
+
current_dir[components[-1]] = None
|
203
218
|
return files_dict
|
peak/exceptions.py
CHANGED
@@ -19,6 +19,7 @@
|
|
19
19
|
# # along with this program. If not, see <https://apache.org/licenses/LICENSE-2.0>
|
20
20
|
#
|
21
21
|
"""Exceptions for the Peak API."""
|
22
|
+
|
22
23
|
from __future__ import annotations
|
23
24
|
|
24
25
|
from collections import defaultdict
|
@@ -103,6 +104,17 @@ class PayloadTooLargeException(BaseHttpException):
|
|
103
104
|
|
104
105
|
STATUS_CODE = 413
|
105
106
|
|
107
|
+
def __init__(self, message: str = "") -> None:
|
108
|
+
"""Throw exception with custom message.
|
109
|
+
|
110
|
+
Args:
|
111
|
+
message (str): Additional message to add to exception.
|
112
|
+
"""
|
113
|
+
error_message: str = (
|
114
|
+
"Please use '--dry-run' with your command to preview the file tree and optimize the file contents."
|
115
|
+
)
|
116
|
+
super().__init__(f"{message}. {error_message}")
|
117
|
+
|
106
118
|
|
107
119
|
class UnprocessableEntityException(BaseHttpException):
|
108
120
|
"""The server understands the request, but it was unable to process the contained instructions."""
|
@@ -154,7 +166,9 @@ class FileLimitExceededException(PeakBaseException):
|
|
154
166
|
message (str): Additional message to add to exception.
|
155
167
|
units (str): Units of the maximum size.
|
156
168
|
"""
|
157
|
-
error_message: str =
|
169
|
+
error_message: str = (
|
170
|
+
f"Compressed directory size is over {max_size}{units}. Please use '--dry-run' with your command to preview the file tree and optimize the file contents."
|
171
|
+
)
|
158
172
|
super().__init__(f"{error_message} {message}")
|
159
173
|
|
160
174
|
|
peak/handler.py
CHANGED
@@ -19,6 +19,7 @@
|
|
19
19
|
# # along with this program. If not, see <https://apache.org/licenses/LICENSE-2.0>
|
20
20
|
#
|
21
21
|
"""Handler for sending requests to the API."""
|
22
|
+
|
22
23
|
from __future__ import annotations
|
23
24
|
|
24
25
|
import contextlib
|
@@ -35,7 +36,7 @@ from urllib3.util import Retry
|
|
35
36
|
import peak.config
|
36
37
|
from peak.compression import compress, get_files_to_include, print_file_tree
|
37
38
|
from peak.constants import ContentType, HttpMethods
|
38
|
-
from peak.exceptions import BaseHttpException
|
39
|
+
from peak.exceptions import BaseHttpException, PayloadTooLargeException
|
39
40
|
from peak.output import Writer
|
40
41
|
from peak.telemetry import telemetry
|
41
42
|
from peak.validators import check_file_size
|
@@ -200,6 +201,9 @@ class HandlerUtils(AuthRetrySession):
|
|
200
201
|
if 200 <= response.status_code < 300: # noqa: PLR2004
|
201
202
|
return response
|
202
203
|
|
204
|
+
if response.status_code == 413: # noqa: PLR2004
|
205
|
+
raise PayloadTooLargeException(response.json().get("detail", ""))
|
206
|
+
|
203
207
|
raise BaseHttpException.REGISTRY[response.status_code](response.json())
|
204
208
|
|
205
209
|
|
peak/helpers.py
CHANGED
@@ -20,10 +20,12 @@
|
|
20
20
|
#
|
21
21
|
|
22
22
|
"""Collection of basic helper functions."""
|
23
|
+
|
23
24
|
from __future__ import annotations
|
24
25
|
|
25
26
|
import inspect
|
26
27
|
import json
|
28
|
+
import re
|
27
29
|
from datetime import datetime, timezone
|
28
30
|
from types import FrameType
|
29
31
|
from typing import Any, Dict, List, Optional
|
@@ -205,6 +207,42 @@ def remove_none_values(data: Any) -> Dict[str, Any]:
|
|
205
207
|
return data # type: ignore[no-any-return]
|
206
208
|
|
207
209
|
|
210
|
+
def convert_to_snake_case(s: str) -> str:
|
211
|
+
"""Converts a given string from camelCase or TitleCase to snake case.
|
212
|
+
|
213
|
+
Args:
|
214
|
+
s (str): The string to be converted.
|
215
|
+
|
216
|
+
Returns:
|
217
|
+
str: The converted string in snake case.
|
218
|
+
"""
|
219
|
+
return re.sub(r"(?<!^)(?=[A-Z])", "_", s).lower()
|
220
|
+
|
221
|
+
|
222
|
+
def convert_keys_to_snake_case(data: Dict[str, Any], *, convert_nested: bool = False) -> Dict[str, Any]:
|
223
|
+
"""Converts keys of a dictionary to snake case.
|
224
|
+
|
225
|
+
Args:
|
226
|
+
data (Dict[str, Any]): Dictionary to be converted.
|
227
|
+
convert_nested (bool): Whether to convert nested keys as well. Default is False.
|
228
|
+
|
229
|
+
Returns:
|
230
|
+
Dict[str, Any]: Dictionary with keys converted to snake case.
|
231
|
+
"""
|
232
|
+
|
233
|
+
def convert_dict(d: Dict[str, Any]) -> Dict[str, Any]:
|
234
|
+
new_dict = {}
|
235
|
+
for k, v in d.items():
|
236
|
+
new_key = convert_to_snake_case(k)
|
237
|
+
if convert_nested and isinstance(v, dict):
|
238
|
+
new_dict[new_key] = convert_dict(v)
|
239
|
+
else:
|
240
|
+
new_dict[new_key] = v
|
241
|
+
return new_dict
|
242
|
+
|
243
|
+
return convert_dict(data)
|
244
|
+
|
245
|
+
|
208
246
|
def format_date(timestamp: str, time_format: str = "%Y/%m/%d %H:%M:%S") -> str:
|
209
247
|
"""Format a timestamp to a given format.
|
210
248
|
|
peak/output.py
CHANGED
@@ -56,6 +56,7 @@ class Writer:
|
|
56
56
|
self,
|
57
57
|
data: Any,
|
58
58
|
deprecation_message: str | None = None,
|
59
|
+
output_type: OutputTypes | None = None,
|
59
60
|
) -> None:
|
60
61
|
"""Write logs to the terminal.
|
61
62
|
|
@@ -63,27 +64,33 @@ class Writer:
|
|
63
64
|
data (Any): Data to be printed on the terminal.
|
64
65
|
This handles dry-run, debug mode and exit code for the CLI.
|
65
66
|
deprecation_message (str, optional): Deprecation message to be printed on the terminal.
|
67
|
+
output_type (OutputTypes, optional): Override for the output type set in the config.
|
66
68
|
"""
|
67
|
-
|
69
|
+
output_type_parsed = output_type or config.OUTPUT_TYPE
|
68
70
|
table_params = config.TABLE_PARAMS
|
69
71
|
|
70
72
|
if not config.DEBUG_MODE or self.ignore_debug_mode:
|
71
73
|
if deprecation_message:
|
72
74
|
self._print_deprecation_warning(deprecation_message)
|
73
|
-
if
|
75
|
+
if output_type_parsed == OutputTypes.yaml.value:
|
74
76
|
self.__yaml(data)
|
75
|
-
elif
|
77
|
+
elif output_type_parsed == OutputTypes.table.value:
|
76
78
|
self.__table(data, table_params)
|
77
79
|
else:
|
78
|
-
self.__json(data)
|
80
|
+
self.__json(data, output_type)
|
79
81
|
|
80
|
-
def __json(self, data: Any) -> None:
|
82
|
+
def __json(self, data: Any, output_type: OutputTypes | None = None) -> None:
|
81
83
|
"""Write logs to the terminal in JSON format.
|
82
84
|
|
83
85
|
Args:
|
84
86
|
data (Any): Data to be printed on the terminal.
|
87
|
+
output_type (OutputTypes): If passed, JSON parser would be used to print output
|
88
|
+
even if the data is not a dictionary.
|
85
89
|
"""
|
86
|
-
|
90
|
+
if isinstance(data, dict) or output_type == OutputTypes.json:
|
91
|
+
console.print_json(data=data)
|
92
|
+
else:
|
93
|
+
console.print(data)
|
87
94
|
|
88
95
|
def __table(self, data: dict[Any, Any], params: dict[str, Any]) -> None:
|
89
96
|
"""Write logs to the terminal in a tabular format.
|