peak-sdk 1.2.1__py3-none-any.whl → 1.4.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.
@@ -0,0 +1,632 @@
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
+
22
+ #
23
+ """Peak Services service commands."""
24
+
25
+ import json
26
+ from typing import Any, Dict, List, Optional
27
+
28
+ import typer
29
+ from peak.cli import args, helpers
30
+ from peak.cli.args import DRY_RUN, GENERATE_YAML, OUTPUT_TYPES, PAGING
31
+ from peak.constants import OutputTypes, OutputTypesNoTable
32
+ from peak.helpers import combine_dictionaries, map_user_options, parse_list_of_strings, variables_to_dict
33
+ from peak.output import Writer
34
+ from peak.resources.services import Service
35
+ from typing_extensions import Annotated
36
+
37
+ app = typer.Typer(
38
+ help="Develop web services to deliver insights, suggestions and recommendations.",
39
+ short_help="Create and Manage Services.",
40
+ )
41
+
42
+ _SERVICE_ID = typer.Argument(..., help="ID of the service to be used in this operation.")
43
+
44
+ _SERVICE_NAME = typer.Argument(..., help="Name of the service to be used in this operation.")
45
+
46
+ _LIST_STATUS = typer.Option(
47
+ None,
48
+ help="A list of service status to filter the list by. Valid values are `CREATING`, `DEPLOYING`, `AVAILABLE`, `DELETING`, `CREATE_FAILED`, `DELETE_FAILED`.",
49
+ )
50
+
51
+ _SERVICE_TYPE = typer.Option(
52
+ None,
53
+ help="A list of service types to filter the list by. Valid values are `api`, `web-app` and `shiny`.",
54
+ )
55
+
56
+ _LIST_NAME = typer.Option(None, help="Service name to search for.")
57
+
58
+ FILE_HELP_STRING = (
59
+ "Path to the file that defines the body for this operation, supports both `yaml` file or a `jinja` template."
60
+ )
61
+
62
+ NAME = typer.Option(None, help="Name of the service")
63
+
64
+ DESCRIPTION = typer.Option(None, help="Description of the service")
65
+
66
+ TITLE = typer.Option(None, help="Title of the service")
67
+
68
+ IMAGE_ID = typer.Option(None, help="ID of the image to be used in this operation.")
69
+
70
+ VERSION_ID = typer.Option(
71
+ None,
72
+ help="ID of the version to be used in this operation. If version-id is not passed, service will be created using latest ready version of the image.",
73
+ )
74
+
75
+ INSTANCE_TYPE_ID = typer.Option(None, help="The ID of the instance type to be used in this operation.")
76
+
77
+ SESSION_STICKINESS = typer.Option(
78
+ None,
79
+ help="Enable session stickiness for the service. Not required for API type services.",
80
+ )
81
+
82
+ _ENVS = typer.Option(None, help="List of plain text environment variables in the format arg1=value1.")
83
+
84
+ _SECRETS = typer.Option(None, help="List of secret names to be passed as environment variables.")
85
+
86
+ _ENTRYPOINT = typer.Option(None, help="Entrypoint for the service.")
87
+
88
+ _HEALTH_CHECK_URL = typer.Option(None, help="Endpoint to monitor service's operational status.")
89
+
90
+ _HTTP_METHOD = typer.Option(None, help="HTTP method to be used to test the service.")
91
+
92
+ _PATH = typer.Option(None, help="Path to be used to test the service.")
93
+
94
+ _PAYLOAD = typer.Option(None, help="Payload to be used to test the service. To be passed in stringified json format.")
95
+
96
+ MAPPING = {
97
+ "imageId": "imageDetails",
98
+ "versionId": "imageDetails",
99
+ "instanceTypeId": "resources",
100
+ "env": "parameters",
101
+ "secrets": "parameters", # pragma: allowlist secret
102
+ }
103
+
104
+
105
+ @app.command("list", short_help="List services.", options_metavar="list_services")
106
+ def list_services(
107
+ ctx: typer.Context,
108
+ page_size: Optional[int] = args.PAGE_SIZE,
109
+ page_number: Optional[int] = args.PAGE_NUMBER,
110
+ status: Optional[List[str]] = _LIST_STATUS,
111
+ name: Optional[str] = _LIST_NAME,
112
+ service_type: Optional[List[str]] = _SERVICE_TYPE,
113
+ paging: Optional[bool] = PAGING, # noqa: ARG001
114
+ output_type: Optional[OutputTypes] = OUTPUT_TYPES, # noqa: ARG001
115
+ ) -> None:
116
+ """***List*** all services that exist for the tenant.
117
+
118
+ \b
119
+ 📝 ***Example usage:***<br/>
120
+ ```bash
121
+ peak services list --page-size 10 --page-number 1 --status CREATING --name test --service-type web-app
122
+ ```
123
+
124
+ \b
125
+ 🆗 ***Response:***
126
+ ```
127
+ {
128
+ "servicesCount": 1,
129
+ "services": [...],
130
+ "pageCount": 1,
131
+ "pageNumber": 1,
132
+ "pageSize": 25
133
+ }
134
+ ```
135
+
136
+ 🔗 [**API Documentation**](https://service.peak.ai/webapps/api-docs/index.htm#/Services/list-service)
137
+ """
138
+ service_client: Service = ctx.obj["client"]
139
+ writer: Writer = ctx.obj["writer"]
140
+
141
+ with writer.pager():
142
+ response = service_client.list_services(
143
+ page_size=page_size,
144
+ page_number=page_number,
145
+ return_iterator=False,
146
+ status=parse_list_of_strings(status),
147
+ name=name,
148
+ service_type=service_type,
149
+ )
150
+ writer.write(response)
151
+
152
+
153
+ @app.command(short_help="Create a new service.", options_metavar="create_service")
154
+ def create(
155
+ ctx: typer.Context,
156
+ file: Annotated[Optional[str], typer.Argument(..., help=FILE_HELP_STRING)] = None,
157
+ params_file: str = args.TEMPLATE_PARAMS_FILE,
158
+ params: List[str] = args.TEMPLATE_PARAMS,
159
+ name: str = NAME,
160
+ title: str = TITLE,
161
+ description: str = DESCRIPTION,
162
+ image_id: int = IMAGE_ID,
163
+ version_id: int = VERSION_ID,
164
+ instance_type_id: int = INSTANCE_TYPE_ID,
165
+ session_stickiness: bool = SESSION_STICKINESS,
166
+ service_type: Optional[str] = _SERVICE_TYPE,
167
+ env: Optional[List[str]] = _ENVS,
168
+ secrets: Optional[List[str]] = _SECRETS,
169
+ entrypoint: Optional[str] = _ENTRYPOINT,
170
+ health_check_url: Optional[str] = _HEALTH_CHECK_URL,
171
+ dry_run: Optional[bool] = DRY_RUN, # noqa: ARG001
172
+ paging: Optional[bool] = PAGING, # noqa: ARG001
173
+ output_type: Optional[OutputTypesNoTable] = OUTPUT_TYPES, # noqa: ARG001
174
+ generate: Optional[bool] = GENERATE_YAML, # noqa: ARG001
175
+ ) -> None:
176
+ """***Create*** a new service and start its deployment.
177
+
178
+ \b
179
+ 🧩 ***Input file schema(yaml):***<br/>
180
+ ```yaml
181
+ body (map):
182
+ name (string): Name of the service.
183
+ title (string | required: false): Title of the service.
184
+ description (string | required: false): Description of the service.
185
+ serviceType (string | required: false): Type of the service. Valid values are `api`, `web-app` and `shiny`. Default value is `web-app`.
186
+ imageDetails (map):
187
+ imageId (number): ID of the image.
188
+ versionId (number | required: false): ID of the image version. If versionId is not passed, service will be created using latest ready version of the image.
189
+ resources (map | required: false):
190
+ instanceTypeId (number): ID of the instance type.
191
+ parameters (map | required: false):
192
+ env (map | required: false): Key-Value pair where key is the name of the env.
193
+ secrets (list(str) | required: false): List of secret names to be passed.
194
+ sessionStickiness (boolean | required: false): Enable session stickiness for the service. Default value is false. Enabling session stickiness will tie each user to a specific server for all their requests. Not required for API type services.
195
+ entrypoint (string | required: false): Entrypoint for the service.
196
+ healthCheckURL (string | required: false): Endpoint to monitor service's operational status.
197
+ ```
198
+
199
+ \b
200
+ 📝 ***Example usage:***
201
+ ```bash
202
+ peak services create '/path/to/file.yml' -v '/path/to/params.yml'
203
+ ```
204
+
205
+ \b
206
+ 📝 ***Example usage without yaml:***
207
+ ```bash
208
+ peak services create --name <name> --title <title> --description <description> --service-type web-app --image-id <image-id> --version-id <version-id> --instance-type-id <instance-type-id> --session-stickiness
209
+ ```
210
+
211
+ \b
212
+ 🆗 ***Response:***
213
+ ```json
214
+ {
215
+ "id": "db88c21d-1add-45dd-a72e-8c6b83b68dee"
216
+ }
217
+ ```
218
+
219
+ 🔗 [**API Documentation**](https://service.peak.ai/webapps/api-docs/index.htm#/Services/create-service)
220
+ """
221
+ service_client: Service = ctx.obj["client"]
222
+ writer: Writer = ctx.obj["writer"]
223
+
224
+ user_options: Dict[str, Any] = variables_to_dict(
225
+ name,
226
+ description,
227
+ title,
228
+ image_id,
229
+ version_id,
230
+ instance_type_id,
231
+ session_stickiness,
232
+ service_type,
233
+ env,
234
+ secrets,
235
+ entrypoint,
236
+ )
237
+ user_options = map_user_options(user_options, MAPPING)
238
+
239
+ body: Dict[str, Any] = {}
240
+ if file:
241
+ body = helpers.template_handler(file=file, params_file=params_file, params=params)
242
+ body = helpers.remove_unknown_args(body, service_client.create_service)
243
+
244
+ updated_body = combine_dictionaries(body.get("body") or {}, user_options)
245
+
246
+ if env:
247
+ updated_body["parameters"]["env"] = helpers.parse_envs(env)
248
+
249
+ if secrets:
250
+ updated_body["parameters"]["secrets"] = parse_list_of_strings(secrets)
251
+
252
+ if health_check_url:
253
+ updated_body["healthCheckURL"] = health_check_url
254
+
255
+ with writer.pager():
256
+ response = service_client.create_service(body=updated_body)
257
+ writer.write(response)
258
+
259
+
260
+ @app.command(short_help="Update an existing service.", options_metavar="update_service")
261
+ def update(
262
+ ctx: typer.Context,
263
+ service_id: str = _SERVICE_ID,
264
+ file: Annotated[Optional[str], typer.Argument(..., help=FILE_HELP_STRING)] = None,
265
+ params_file: str = args.TEMPLATE_PARAMS_FILE,
266
+ params: List[str] = args.TEMPLATE_PARAMS,
267
+ title: str = TITLE,
268
+ description: str = DESCRIPTION,
269
+ image_id: int = IMAGE_ID,
270
+ version_id: int = VERSION_ID,
271
+ instance_type_id: int = INSTANCE_TYPE_ID,
272
+ session_stickiness: bool = SESSION_STICKINESS,
273
+ env: Optional[List[str]] = _ENVS,
274
+ secrets: Optional[List[str]] = _SECRETS,
275
+ entrypoint: Optional[str] = _ENTRYPOINT,
276
+ health_check_url: Optional[str] = _HEALTH_CHECK_URL,
277
+ dry_run: Optional[bool] = DRY_RUN, # noqa: ARG001
278
+ paging: Optional[bool] = PAGING, # noqa: ARG001
279
+ output_type: Optional[OutputTypesNoTable] = OUTPUT_TYPES, # noqa: ARG001
280
+ generate: Optional[bool] = GENERATE_YAML, # noqa: ARG001
281
+ ) -> None:
282
+ """***Update*** an existing service.
283
+
284
+ \b
285
+ When updating the service, it will trigger a redeployment only under specific conditions.
286
+ Redeployment is triggered if you make changes to any of the following parameters: imageId, versionId, instanceTypeId, env, secrets or sessionStickiness.
287
+ However, only modifying the title or description will not trigger a redeployment.
288
+
289
+ With the help of this operation, we can just update the required fields (except name and serviceType) and keep the rest of the fields as it is.
290
+
291
+ \b
292
+ 🧩 ***Input file schema(yaml):***<br/>
293
+ ```yaml
294
+ body (map):
295
+ title (string | required: false): Title of the service.
296
+ description (string | required: false): Description of the service.
297
+ imageDetails (map | required: false):
298
+ imageId (number): ID of the image.
299
+ versionId (number | required: false): ID of the image version. If versionId is not passed, service will be created using latest ready version of the image.
300
+ resources (map | required: false):
301
+ instanceTypeId (number): ID of the instance type.
302
+ parameters (map | required: false):
303
+ env (map | required: false): Key-Value pair where key is the name of the env.
304
+ secrets (list(str) | required: false): List of secret names to be passed.
305
+ sessionStickiness (boolean | required: false): Enable session stickiness for the service. Default value is false. Enabling session stickiness will tie each user to a specific server for all their requests. Not required for API type services.
306
+ entrypoint (string | required: false): Entrypoint for the service.
307
+ healthCheckURL (string | required: false): Endpoint to monitor service's operational status.
308
+ ```
309
+
310
+ \b
311
+ 📝 ***Example usage:***
312
+ ```bash
313
+ peak services update <service-id> '/path/to/file.yml' -v '/path/to/params.yml'
314
+ ```
315
+
316
+ \b
317
+ 📝 ***Example usage without yaml:***
318
+ ```bash
319
+ peak services update <service-id> --title <title> --description <description> --image-id <image-id> --version-id <version-id> --instance-type-id <instance-type-id> --session-stickiness
320
+ ```
321
+
322
+ \b
323
+ 🆗 ***Response:***
324
+ ```json
325
+ {
326
+ "id": "ab11c21d-1add-45dd-a72e-8c6b83b68dee"
327
+ }
328
+ ```
329
+
330
+ 🔗 [**API Documentation**](https://service.peak.ai/webapps/api-docs/index.htm#/Services/update-service)
331
+ """
332
+ service_client: Service = ctx.obj["client"]
333
+ writer: Writer = ctx.obj["writer"]
334
+
335
+ user_options: Dict[str, Any] = variables_to_dict(
336
+ description,
337
+ title,
338
+ image_id,
339
+ version_id,
340
+ instance_type_id,
341
+ session_stickiness,
342
+ env,
343
+ secrets,
344
+ entrypoint,
345
+ )
346
+ user_options = map_user_options(user_options, MAPPING)
347
+
348
+ body: Dict[str, Any] = {}
349
+ if file:
350
+ body = helpers.template_handler(file=file, params_file=params_file, params=params)
351
+ body = helpers.remove_unknown_args(body, service_client.update_service)
352
+
353
+ updated_body = combine_dictionaries(body.get("body") or {}, user_options)
354
+
355
+ if env:
356
+ updated_body["parameters"]["env"] = helpers.parse_envs(env)
357
+
358
+ if secrets:
359
+ updated_body["parameters"]["secrets"] = parse_list_of_strings(secrets)
360
+
361
+ if health_check_url:
362
+ updated_body["healthCheckURL"] = health_check_url
363
+
364
+ with writer.pager():
365
+ response = service_client.update_service(service_id=service_id, body=updated_body)
366
+ writer.write(response)
367
+
368
+
369
+ @app.command(
370
+ short_help="Create a new service or Update an existing service.",
371
+ options_metavar="create_or_update_service",
372
+ )
373
+ def create_or_update(
374
+ ctx: typer.Context,
375
+ file: Annotated[Optional[str], typer.Argument(..., help=FILE_HELP_STRING)] = None,
376
+ params_file: str = args.TEMPLATE_PARAMS_FILE,
377
+ params: List[str] = args.TEMPLATE_PARAMS,
378
+ name: str = NAME,
379
+ title: str = TITLE,
380
+ description: str = DESCRIPTION,
381
+ image_id: int = IMAGE_ID,
382
+ version_id: int = VERSION_ID,
383
+ instance_type_id: int = INSTANCE_TYPE_ID,
384
+ session_stickiness: bool = SESSION_STICKINESS,
385
+ service_type: Optional[str] = _SERVICE_TYPE,
386
+ env: Optional[List[str]] = _ENVS,
387
+ secrets: Optional[List[str]] = _SECRETS,
388
+ entrypoint: Optional[str] = _ENTRYPOINT,
389
+ health_check_url: Optional[str] = _HEALTH_CHECK_URL,
390
+ dry_run: Optional[bool] = DRY_RUN, # noqa: ARG001
391
+ paging: Optional[bool] = PAGING, # noqa: ARG001
392
+ output_type: Optional[OutputTypesNoTable] = OUTPUT_TYPES, # noqa: ARG001
393
+ generate: Optional[bool] = GENERATE_YAML, # noqa: ARG001
394
+ ) -> None:
395
+ """***Create*** a new service or ***Update*** an existing service based on service name and start its deployment.
396
+
397
+ \b
398
+ When updating the service, it will trigger a redeployment only under specific conditions.
399
+ Redeployment is triggered if you make changes to any of the following parameters: imageId, versionId, instanceTypeId, env, secrets or sessionStickiness.
400
+ However, only modifying the title or description will not trigger a redeployment.
401
+
402
+ \b
403
+ 🧩 ***Input file schema(yaml):***<br/>
404
+ ```yaml
405
+ body (map):
406
+ name (string): Name of the service.
407
+ title (string | required: false): Title of the service.
408
+ description (string | required: false): Description of the service.
409
+ serviceType (string | required: false): Type of the service. Valid values are `api`, `web-app` and `shiny`. Default value is `web-app`.
410
+ imageDetails (map):
411
+ imageId (number): ID of the image.
412
+ versionId (number | required: false): ID of the image version. If versionId is not passed, service will be created using latest ready version of the image.
413
+ resources (map | required: false):
414
+ instanceTypeId (number): ID of the instance type.
415
+ parameters (map | required: false):
416
+ env (map | required: false): Key-Value pair where key is the name of the env.
417
+ secrets (list(str) | required: false): List of secret names to be passed.
418
+ sessionStickiness (boolean | required: false): Enable session stickiness for the service. Default value is false. Enabling session stickiness will tie each user to a specific server for all their requests. Not required for API type services.
419
+ entrypoint (string | required: false): Entrypoint for the service.
420
+ healthCheckURL (string | required: false): Endpoint to monitor service's operational status.
421
+ ```
422
+
423
+ \b
424
+ 📝 ***Example usage:***
425
+ ```bash
426
+ peak services create-or-update '/path/to/file.yml' -v '/path/to/params.yml'
427
+ ```
428
+
429
+ \b
430
+ 📝 ***Example usage without yaml:***
431
+ ```bash
432
+ peak services create-or-update --name <name> --title <title> --description <description> --image-id <image-id> --version-id <version-id> --instance-type-id <instance-type-id> --session-stickiness
433
+ ```
434
+
435
+ \b
436
+ 🆗 ***Response:***
437
+ ```json
438
+ {
439
+ "id": "db88c21d-1add-45dd-a72e-8c6b83b68dee"
440
+ }
441
+ ```
442
+
443
+ 🔗 [**API Documentation for creating a new service**](https://service.peak.ai/webapps/api-docs/index.htm#/Services/create-service)
444
+
445
+ 🔗 [**API Documentation for updating an existing service**](https://service.peak.ai/webapps/api-docs/index.htm#/Services/update-service)
446
+ """
447
+ service_client: Service = ctx.obj["client"]
448
+ writer: Writer = ctx.obj["writer"]
449
+
450
+ user_options: Dict[str, Any] = variables_to_dict(
451
+ name,
452
+ description,
453
+ title,
454
+ image_id,
455
+ version_id,
456
+ instance_type_id,
457
+ session_stickiness,
458
+ service_type,
459
+ env,
460
+ secrets,
461
+ entrypoint,
462
+ )
463
+ user_options = map_user_options(user_options, MAPPING)
464
+
465
+ body: Dict[str, Any] = {}
466
+ if file:
467
+ body = helpers.template_handler(file=file, params_file=params_file, params=params)
468
+ body = helpers.remove_unknown_args(body, service_client.create_or_update_service)
469
+
470
+ updated_body = combine_dictionaries(body.get("body") or {}, user_options)
471
+
472
+ if env:
473
+ updated_body["parameters"]["env"] = helpers.parse_envs(env)
474
+
475
+ if secrets:
476
+ updated_body["parameters"]["secrets"] = parse_list_of_strings(secrets)
477
+
478
+ if health_check_url:
479
+ updated_body["healthCheckURL"] = health_check_url
480
+
481
+ response = service_client.create_or_update_service(body=updated_body)
482
+ writer.write(response)
483
+
484
+
485
+ @app.command(short_help="Delete an existing service.", options_metavar="delete_service")
486
+ def delete(
487
+ ctx: typer.Context,
488
+ service_id: str = _SERVICE_ID,
489
+ dry_run: Optional[bool] = DRY_RUN, # noqa: ARG001
490
+ paging: Optional[bool] = PAGING, # noqa: ARG001
491
+ output_type: Optional[OutputTypesNoTable] = OUTPUT_TYPES, # noqa: ARG001
492
+ ) -> None:
493
+ """***Delete*** an existing service.
494
+
495
+ \b
496
+ 📝 ***Example usage:***<br/>
497
+ ```bash
498
+ peak services delete <service-id>
499
+ ```
500
+
501
+ \b
502
+ 🆗 ***Response:***
503
+ ```json
504
+ {
505
+ "id": "ab11c21d-1add-45dd-a72e-8c6b83b68dee"
506
+ }
507
+ ```
508
+
509
+ 🔗 [**API Documentation**](https://service.peak.ai/webapps/api-docs/index.htm#/Services/delete-service)
510
+ """
511
+ service_client: Service = ctx.obj["client"]
512
+ writer: Writer = ctx.obj["writer"]
513
+
514
+ with writer.pager():
515
+ response = service_client.delete_service(service_id=service_id)
516
+ writer.write(response)
517
+
518
+
519
+ @app.command(short_help="Describe details of a service.", options_metavar="describe_service")
520
+ def describe(
521
+ ctx: typer.Context,
522
+ service_id: str = _SERVICE_ID,
523
+ paging: Optional[bool] = PAGING, # noqa: ARG001
524
+ output_type: Optional[OutputTypesNoTable] = OUTPUT_TYPES, # noqa: ARG001
525
+ ) -> None:
526
+ """***Describe*** details for the specific service.
527
+
528
+ \b
529
+ 📝 ***Example usage:***<br/>
530
+ ```bash
531
+ peak services describe <service-id>
532
+ ```
533
+
534
+ \b
535
+ 🆗 ***Response:***
536
+ ```
537
+ {
538
+ "id": "ab11c21d-1add-45dd-a72e-8c6b83b68dee",
539
+ "name": "awesome-service",
540
+ "status": "AVAILABLE",
541
+ "serviceType": "web-app",
542
+ "imageDetails": {
543
+ "imageId": 1,
544
+ "versionId": 1
545
+ },
546
+ "resources": {
547
+ "instanceTypeId": 1
548
+ },
549
+ "sessionStickiness": false,
550
+ "createdAt": "2020-01-01T18:00:00.000Z",
551
+ "createdBy": "someone@peak.ai",
552
+ "updatedAt": "2020-01-01T18:00:00.000Z",
553
+ "updatedBy": "someone@peak.ai",
554
+ "tags": [...]
555
+ }
556
+ ```
557
+
558
+ 🔗 [**API Documentation**](https://service.peak.ai/webapps/api-docs/index.htm#/Services/get-service)
559
+ """
560
+ service_client: Service = ctx.obj["client"]
561
+ writer: Writer = ctx.obj["writer"]
562
+
563
+ with writer.pager():
564
+ response = service_client.describe_service(service_id=service_id)
565
+ writer.write(response)
566
+
567
+
568
+ @app.command(short_help="Test API type service", options_metavar="test_service")
569
+ def test(
570
+ ctx: typer.Context,
571
+ service_name: str = _SERVICE_NAME,
572
+ file: Annotated[Optional[str], typer.Argument(..., help=FILE_HELP_STRING)] = None,
573
+ params_file: str = args.TEMPLATE_PARAMS_FILE,
574
+ params: List[str] = args.TEMPLATE_PARAMS,
575
+ http_method: str = _HTTP_METHOD,
576
+ path: Optional[str] = _PATH,
577
+ payload: Optional[str] = _PAYLOAD,
578
+ dry_run: Optional[bool] = DRY_RUN, # noqa: ARG001
579
+ paging: Optional[bool] = PAGING, # noqa: ARG001
580
+ output_type: Optional[OutputTypesNoTable] = OUTPUT_TYPES, # noqa: ARG001
581
+ generate: Optional[bool] = GENERATE_YAML, # noqa: ARG001
582
+ ) -> None:
583
+ """***Test*** an API type service.
584
+
585
+ \b
586
+ 🧩 ***Input file schema(yaml):***<br/>
587
+ ```yaml
588
+ payload (map): payload to be used to test the service.
589
+ ```
590
+
591
+ \b
592
+ 📝 ***Example usage:***
593
+ ```bash
594
+ peak services test <service-name> '/path/to/file.yml' -v '/path/to/params.yml'
595
+ ```
596
+
597
+ \b
598
+ 📝 ***Example usage without yaml:***<br/>
599
+ ```bash
600
+ peak services test <service-name> --http-method 'get' --path '/'
601
+ ```
602
+
603
+ \b
604
+ 🆗 ***Response:***
605
+ ```
606
+ {
607
+ "responseBody": "{"status": "OK"}",
608
+ "responseStatus": 200,
609
+ "reqStartTime": "2024-01-01T10:00:01.064Z",
610
+ "reqEndTime": "2024-01-01T10:00:05.064Z",
611
+ "responseSize": "1KB"
612
+ }
613
+ ```
614
+
615
+ 🔗 [**API Documentation**](https://service.peak.ai/webapps/api-docs/index.htm#/Services/test-api-service)
616
+ """
617
+ service_client: Service = ctx.obj["client"]
618
+ writer: Writer = ctx.obj["writer"]
619
+
620
+ body: Dict[str, Any] = {}
621
+ if file:
622
+ body = helpers.template_handler(file=file, params_file=params_file, params=params)
623
+ body = helpers.remove_unknown_args(body, service_client.test_service)
624
+
625
+ with writer.pager():
626
+ response = service_client.test_service(
627
+ service_name=service_name,
628
+ http_method=http_method,
629
+ path=path,
630
+ payload=json.loads(payload) if payload else body.get("payload"),
631
+ )
632
+ writer.write(response)