snapctl 0.32.0__py3-none-any.whl → 0.32.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of snapctl might be problematic. Click here for more details.

@@ -2,7 +2,7 @@
2
2
  Constants used by snapctl
3
3
  """
4
4
  COMPANY_NAME = 'Snapser'
5
- VERSION = '0.32.0'
5
+ VERSION = '0.32.1'
6
6
  CONFIG_FILE_MAC = '~/.snapser/config'
7
7
  CONFIG_FILE_WIN = '%homepath%\\.snapser\\config'
8
8
 
@@ -12,16 +12,80 @@ URL_KEY = 'SNAPSER_URL_KEY'
12
12
  CONFIG_PATH_KEY = 'SNAPSER_CONFIG_PATH'
13
13
  SERVER_CALL_TIMEOUT = 30
14
14
 
15
- SNAPCTL_SUCCESS = 0
16
- SNAPCTL_ERROR = 1
17
-
18
15
  # HTTP codes
19
16
  HTTP_UNAUTHORIZED = 401
20
17
  HTTP_FORBIDDEN = 403
21
18
  HTTP_NOT_FOUND = 404
22
19
  HTTP_CONFLICT = 409
23
20
 
24
- # Error codes
25
- ERROR_SERVICE_VERSION_EXISTS = 542
26
- ERROR_TAG_NOT_AVAILABLE = 544
27
- ERROR_ADD_ON_NOT_ENABLED = 547
21
+ # HTTP Error codes
22
+ HTTP_ERROR_SERVICE_VERSION_EXISTS = 542
23
+ HTTP_ERROR_TAG_NOT_AVAILABLE = 544
24
+ HTTP_ERROR_ADD_ON_NOT_ENABLED = 547
25
+ HTTP_ERROR_DUPLICATE_GAME_NAME = 2
26
+
27
+ # CLI Return Codes
28
+ SNAPCTL_SUCCESS = 0
29
+ SNAPCTL_ERROR = 1
30
+ SNAPCTL_INPUT_ERROR = 2
31
+
32
+ # Configuration Errors
33
+ SNAPCTL_CONFIGURATION_INCORRECT = 10
34
+ SNAPCTL_CONFIGURATION_ERROR = 11
35
+ SNAPCTL_DEPENDENCY_MISSING = 12
36
+
37
+ # BYOGS Errors
38
+ SNAPCTL_BYOGS_GENERIC_ERROR = 20
39
+ SNAPCTL_BYOGS_DEPENDENCY_MISSING = 21
40
+ SNAPCTL_BYOGS_ECR_LOGIN_ERROR = 22
41
+ SNAPCTL_BYOGS_BUILD_ERROR = 23
42
+ SNAPCTL_BYOGS_TAG_ERROR = 24
43
+ SNAPCTL_BYOGS_PUBLISH_ERROR = 25
44
+ SNAPCTL_BYOGS_PUBLISH_PERMISSION_ERROR = 26
45
+ SNAPCTL_BYOGS_PUBLISH_DUPLICATE_TAG_ERROR = 27
46
+
47
+ # BYOSNAP Errors
48
+ SNAPCTL_BYOSNAP_GENERIC_ERROR = 30
49
+ SNAPCTL_BYOSNAP_DEPENDENCY_MISSING = 31
50
+ SNAPCTL_BYOSNAP_ECR_LOGIN_ERROR = 32
51
+ SNAPCTL_BYOSNAP_BUILD_ERROR = 33
52
+ SNAPCTL_BYOSNAP_TAG_ERROR = 34
53
+ SNAPCTL_BYOSNAP_PUBLISH_IMAGE_ERROR = 35
54
+ SNAPCTL_BYOSNAP_PUBLISH_IMAGE_PERMISSION_ERROR = 36
55
+ SNAPCTL_BYOSNAP_PUBLISH_IMAGE_DUPLICATE_TAG_ERROR = 37
56
+ SNAPCTL_BYOSNAP_CREATE_ERROR = 38
57
+ SNAPCTL_BYOSNAP_CREATE_PERMISSION_ERROR = 39
58
+ SNAPCTL_BYOSNAP_CREATE_DUPLICATE_NAME_ERROR = 40
59
+ SNAPCTL_BYOSNAP_PUBLISH_VERSION_ERROR = 41
60
+ SNAPCTL_BYOSNAP_PUBLISH_VERSION_PERMISSION_ERROR = 42
61
+ SNAPCTL_BYOSNAP_PUBLISH_VERSION_DUPLICATE_VERSION_ERROR = 43
62
+ SNAPCTL_BYOSNAP_PUBLISH_VERSION_DUPLICATE_TAG_ERROR = 44
63
+
64
+ # Game Errors
65
+ SNAPCTL_GAME_GENERIC_ERROR = 50
66
+ SNAPCTL_GAME_CREATE_ERROR = 51
67
+ SNAPCTL_GAME_CREATE_PERMISSION_ERROR = 52
68
+ SNAPCTL_GAME_CREATE_DUPLICATE_NAME_ERROR = 53
69
+ SNAPCTL_GAME_ENUMERATE_ERROR = 54
70
+
71
+ # Snapend Errors
72
+ SNAPCTL_SNAPEND_GENERIC_ERROR = 60
73
+ SNAPCTL_SNAPEND_ENUMERATE_ERROR = 61
74
+ SNAPCTL_SNAPEND_CLONE_ERROR = 62
75
+ SNAPCTL_SNAPEND_CLONE_SERVER_ERROR = 63
76
+ SNAPCTL_SNAPEND_CLONE_TIMEOUT_ERROR = 64
77
+ SNAPCTL_SNAPEND_APPLY_ERROR = 65
78
+ SNAPCTL_SNAPEND_APPLY_SERVER_ERROR = 66
79
+ SNAPCTL_SNAPEND_APPLY_TIMEOUT_ERROR = 67
80
+ SNAPCTL_SNAPEND_PROMOTE_ERROR = 68
81
+ SNAPCTL_SNAPEND_PROMOTE_SERVER_ERROR = 69
82
+ SNAPCTL_SNAPEND_PROMOTE_TIMEOUT_ERROR = 70
83
+ SNAPCTL_SNAPEND_DOWNLOAD_ERROR = 71
84
+ SNAPCTL_SNAPEND_UPDATE_ERROR = 72
85
+ SNAPCTL_SNAPEND_UPDATE_SERVER_ERROR = 73
86
+ SNAPCTL_SNAPEND_UPDATE_TIMEOUT_ERROR = 74
87
+ SNAPCTL_SNAPEND_STATE_ERROR = 75
88
+
89
+ # Generate Errors
90
+ SNAPCTL_GENERATE_GENERIC_ERROR = 80
91
+ SNAPCTL_GENERATE_BYOSNAP_PROFILE_ERROR = 81
snapctl/main.py CHANGED
@@ -4,7 +4,7 @@
4
4
  import configparser
5
5
  import os
6
6
  from sys import platform
7
- from typing import Union, Callable
7
+ from typing import Union
8
8
  import typer
9
9
  import pyfiglet
10
10
 
@@ -14,12 +14,13 @@ from snapctl.commands.game import Game
14
14
  from snapctl.commands.generate import Generate
15
15
  from snapctl.commands.snapend import Snapend
16
16
  from snapctl.config.constants import COMPANY_NAME, API_KEY, URL_KEY, CONFIG_FILE_MAC, \
17
- CONFIG_FILE_WIN, DEFAULT_PROFILE, VERSION, SNAPCTL_SUCCESS, SNAPCTL_ERROR, CONFIG_PATH_KEY
17
+ CONFIG_FILE_WIN, DEFAULT_PROFILE, VERSION, SNAPCTL_SUCCESS, CONFIG_PATH_KEY, \
18
+ SNAPCTL_CONFIGURATION_INCORRECT
18
19
  from snapctl.config.endpoints import END_POINTS
19
20
  from snapctl.config.hashes import CLIENT_SDK_TYPES, SERVER_SDK_TYPES, PROTOS_TYPES, SERVICE_IDS, \
20
21
  SNAPEND_MANIFEST_TYPES
21
- from snapctl.types.definitions import ResponseType
22
22
  from snapctl.utils.echo import error, success, info
23
+ from snapctl.utils.helper import validate_api_key
23
24
 
24
25
  ######### Globals #########
25
26
 
@@ -66,20 +67,22 @@ def extract_config(extract_key: str, profile: str | None = None) -> object:
66
67
  config_file_path = os.path.expandvars(CONFIG_FILE_WIN)
67
68
  else:
68
69
  config_file_path = os.path.expanduser(CONFIG_FILE_MAC)
69
- result['location'] = f'config-file:{config_file_path}'
70
+ result['location'] = f'{config_file_path}'
70
71
  if os.path.isfile(config_file_path):
71
72
  config = configparser.ConfigParser()
72
73
  config.read(config_file_path, encoding=encoding)
73
74
  config_profile: str = DEFAULT_PROFILE
74
75
  if profile is not None and profile != '' and profile != DEFAULT_PROFILE:
75
- result['location'] = f'{config_file_path}:profile {profile}'
76
+ result['location'] = f'"{config_file_path}:profile {profile}"'
76
77
  config_profile = f'profile {profile}'
77
- info(f"Trying to extract API KEY from profile {profile}")
78
+ info(
79
+ f'Trying to extract API KEY from "{config_file_path}:profile {profile}"'
80
+ )
78
81
  result['value'] = config.get(
79
82
  config_profile, extract_key, fallback=None, raw=True
80
83
  )
81
84
  else:
82
- error(
85
+ info(
83
86
  f'Config file on platform {platform} not found at {config_file_path}')
84
87
  return result
85
88
 
@@ -111,14 +114,9 @@ def validate_command_context(
111
114
  Validator to confirm if the context has been set properly
112
115
  """
113
116
  if ctx.obj['api_key'] is None or ctx.obj['base_url'] == '':
114
- error(
115
- "Unable to set command context. "
116
- f"API Key:{ctx.obj['api_key']} "
117
- f"API Key Category:{ctx.obj['api_key_location']} "
118
- f"Base URL:{ctx.obj['base_url']}"
119
- )
120
- raise typer.Exit(SNAPCTL_ERROR)
121
- info(f"Using API Key from {ctx.obj['api_key_location']}")
117
+ error("Snapctl Configuration Incorrect. Unable to extract API Key",
118
+ SNAPCTL_CONFIGURATION_INCORRECT)
119
+ raise typer.Exit(code=SNAPCTL_CONFIGURATION_INCORRECT)
122
120
 
123
121
  ######### CALLBACKS #########
124
122
 
@@ -174,16 +172,16 @@ def profile_context_callback(
174
172
  # Ensure ctx object is instantiated
175
173
  ctx.ensure_object(dict)
176
174
  api_key_obj = extract_config(API_KEY, profile)
177
- if api_key_obj['value'] is None and profile is not None and profile != '':
178
- conf_file = ''
179
- if platform == 'win32':
180
- conf_file = os.path.expandvars(CONFIG_FILE_WIN)
181
- else:
182
- conf_file = os.path.expanduser(CONFIG_FILE_MAC)
183
- error(
184
- f'Invalid profile input {profile}. '
185
- f'Please check your snap config file at {conf_file}'
186
- )
175
+ # if api_key_obj['value'] is None and profile is not None and profile != '':
176
+ # conf_file = ''
177
+ # if platform == 'win32':
178
+ # conf_file = os.path.expandvars(CONFIG_FILE_WIN)
179
+ # else:
180
+ # conf_file = os.path.expanduser(CONFIG_FILE_MAC)
181
+ # error(
182
+ # f'Invalid profile input {profile}. '
183
+ # f'Please check your snap config file at {conf_file}'
184
+ # )
187
185
  ctx.obj['version'] = VERSION
188
186
  ctx.obj['api_key'] = api_key_obj['value']
189
187
  ctx.obj['api_key_location'] = api_key_obj['location']
@@ -198,7 +196,7 @@ def version_callback(value: bool = True):
198
196
  """
199
197
  if value:
200
198
  success(f"Snapctl version: {VERSION}")
201
- raise typer.Exit(SNAPCTL_SUCCESS)
199
+ raise typer.Exit(code=SNAPCTL_SUCCESS)
202
200
 
203
201
 
204
202
  @app.callback()
@@ -227,12 +225,57 @@ def validate(
227
225
  profile: Union[str, None] = typer.Option(
228
226
  None, "--profile", help="Profile to use.", callback=profile_context_callback
229
227
  ),
230
- ):
228
+ ) -> None:
231
229
  """
232
230
  Validate your Snapctl setup
233
231
  """
234
232
  validate_command_context(ctx)
233
+ validate_api_key(ctx.obj['base_url'], ctx.obj['api_key'])
235
234
  success("Setup is valid")
235
+ raise typer.Exit(code=SNAPCTL_SUCCESS)
236
+
237
+
238
+ @app.command()
239
+ def byogs(
240
+ ctx: typer.Context,
241
+ # Required fields
242
+ subcommand: str = typer.Argument(
243
+ ..., help="BYOGs Subcommands: " + ", ".join(ByoGs.SUBCOMMANDS) + "."
244
+ ),
245
+ # sid: str = typer.Argument(
246
+ # ByoGs.SID, help="Game Server Id. Should start with byogs"
247
+ # ),
248
+ # publish, publish-image and publish-version
249
+ tag: str = typer.Option(
250
+ None, "--tag",
251
+ help="(req: build, push, publish) Tag for your snap"
252
+ ),
253
+ # publish and publish-image
254
+ path: Union[str, None] = typer.Option(
255
+ None, "--path", help="(req: build, publish) Path to your snap code"
256
+ ),
257
+ docker_file: str = typer.Option(
258
+ "Dockerfile", help="Dockerfile name to use"
259
+ ),
260
+ # overrides
261
+ api_key: Union[str, None] = typer.Option(
262
+ None, "--api-key", help="API Key override.", callback=api_key_context_callback
263
+ ),
264
+ profile: Union[str, None] = typer.Option(
265
+ None, "--profile", help="Profile to use.", callback=profile_context_callback
266
+ ),
267
+ ) -> None:
268
+ """
269
+ Bring your own game server commands
270
+ """
271
+ validate_command_context(ctx)
272
+ byogs_obj: ByoGs = ByoGs(
273
+ subcommand, ctx.obj['base_url'], ctx.obj['api_key'],
274
+ tag, path, docker_file,
275
+ )
276
+ getattr(byogs_obj, subcommand.replace('-', '_'))()
277
+ success(f"BYOGs {subcommand} complete")
278
+ raise typer.Exit(code=SNAPCTL_SUCCESS)
236
279
 
237
280
 
238
281
  @app.command()
@@ -306,39 +349,22 @@ def byosnap(
306
349
  name, desc, platform_type, language, tag, path, docker_file,
307
350
  prefix, version, http_port, byosnap_profile
308
351
  )
309
- validate_input_response: ResponseType = byosnap_obj.validate_input()
310
- if validate_input_response['error']:
311
- error(validate_input_response['msg'])
312
- raise typer.Exit(SNAPCTL_ERROR)
313
- command_method = subcommand.replace('-', '_')
314
- method: Callable[..., bool] = getattr(byosnap_obj, command_method)
315
- if not method():
316
- error(f"BYOSnap {subcommand} failed")
317
- raise typer.Exit(SNAPCTL_ERROR)
352
+ getattr(byosnap_obj, subcommand.replace('-', '_'))()
318
353
  success(f"BYOSnap {subcommand} complete")
354
+ raise typer.Exit(code=SNAPCTL_SUCCESS)
319
355
 
320
356
 
321
357
  @app.command()
322
- def byogs(
358
+ def game(
323
359
  ctx: typer.Context,
324
360
  # Required fields
325
361
  subcommand: str = typer.Argument(
326
- ..., help="BYOGs Subcommands: " + ", ".join(ByoGs.SUBCOMMANDS) + "."
327
- ),
328
- # sid: str = typer.Argument(
329
- # ByoGs.SID, help="Game Server Id. Should start with byogs"
330
- # ),
331
- # publish, publish-image and publish-version
332
- tag: str = typer.Option(
333
- None, "--tag",
334
- help="(req: build, push, publish) Tag for your snap"
335
- ),
336
- # publish and publish-image
337
- path: Union[str, None] = typer.Option(
338
- None, "--path", help="(req: build, publish) Path to your snap code"
362
+ ..., help="Game Subcommands: " + ", ".join(Game.SUBCOMMANDS) + "."
339
363
  ),
340
- docker_file: str = typer.Option(
341
- "Dockerfile", help="Dockerfile name to use"
364
+ # name
365
+ name: str = typer.Option(
366
+ None, "--name",
367
+ help=("(req: create) Name of your game: ")
342
368
  ),
343
369
  # overrides
344
370
  api_key: Union[str, None] = typer.Option(
@@ -349,36 +375,26 @@ def byogs(
349
375
  ),
350
376
  ) -> None:
351
377
  """
352
- Bring your own game server commands
378
+ Game commands
353
379
  """
354
380
  validate_command_context(ctx)
355
- byogs_obj: ByoGs = ByoGs(
356
- subcommand, ctx.obj['base_url'], ctx.obj['api_key'],
357
- tag, path, docker_file,
358
- )
359
- validate_input_response: ResponseType = byogs_obj.validate_input()
360
- if validate_input_response['error']:
361
- error(validate_input_response['msg'])
362
- raise typer.Exit(SNAPCTL_ERROR)
363
- command_method = subcommand.replace('-', '_')
364
- method: Callable[..., bool] = getattr(byogs_obj, command_method)
365
- if not method():
366
- error(f"BYOGs {subcommand} failed")
367
- raise typer.Exit(SNAPCTL_ERROR)
368
- success(f"BYOGs {subcommand} complete")
381
+ game_obj: Game = Game(
382
+ subcommand, ctx.obj['base_url'], ctx.obj['api_key'], name)
383
+ getattr(game_obj, subcommand.replace('-', '_'))()
384
+ success(f"Game {subcommand} complete")
385
+ raise typer.Exit(code=SNAPCTL_SUCCESS)
369
386
 
370
387
 
371
388
  @app.command()
372
- def game(
389
+ def generate(
373
390
  ctx: typer.Context,
374
391
  # Required fields
375
392
  subcommand: str = typer.Argument(
376
- ..., help="Game Subcommands: " + ", ".join(Game.SUBCOMMANDS) + "."
393
+ ..., help="Generate Subcommands: " + ", ".join(Generate.SUBCOMMANDS) + "."
377
394
  ),
378
- # name
379
- name: str = typer.Option(
380
- None, "--name",
381
- help=("(req: create) Name of your game: ")
395
+ # byosnap-profile
396
+ out_path: Union[str, None] = typer.Option(
397
+ None, "--out-path", help="(req: byosnap-profile) Path to output the byosnap profile"
382
398
  ),
383
399
  # overrides
384
400
  api_key: Union[str, None] = typer.Option(
@@ -389,21 +405,16 @@ def game(
389
405
  ),
390
406
  ) -> None:
391
407
  """
392
- Game commands
408
+ Generate files to be used by other commands
393
409
  """
394
410
  validate_command_context(ctx)
395
- game_obj: Game = Game(
396
- subcommand, ctx.obj['base_url'], ctx.obj['api_key'], name)
397
- validate_input_response: ResponseType = game_obj.validate_input()
398
- if validate_input_response['error']:
399
- error(validate_input_response['msg'])
400
- raise typer.Exit(SNAPCTL_ERROR)
401
- command_method = subcommand.replace('-', '_')
402
- method: Callable[..., bool] = getattr(game_obj, command_method)
403
- if not method():
404
- error(f"Game {subcommand} failed")
405
- raise typer.Exit(SNAPCTL_ERROR)
406
- success(f"Game {subcommand} complete")
411
+ generate_obj: Generate = Generate(
412
+ subcommand, ctx.obj['base_url'], ctx.obj['api_key'],
413
+ out_path,
414
+ )
415
+ getattr(generate_obj, subcommand.replace('-', '_'))()
416
+ success(f"Generate {subcommand} complete")
417
+ raise typer.Exit(code=SNAPCTL_SUCCESS)
407
418
 
408
419
 
409
420
  @app.command()
@@ -416,7 +427,7 @@ def snapend(
416
427
  # snapend_id: str = typer.Argument(..., help="Snapend Id"),
417
428
  snapend_id: str = typer.Option(
418
429
  None, "--snapend-id",
419
- help=("(req: update, download) Snapend Id")
430
+ help=("(req: state, update, download) Snapend Id")
420
431
  ),
421
432
  # enumerate
422
433
  game_id: str = typer.Option(
@@ -530,52 +541,6 @@ def snapend(
530
541
  # Update
531
542
  byosnaps, byogs, blocking
532
543
  )
533
- validate_input_response: ResponseType = snapend_obj.validate_input()
534
- if validate_input_response['error']:
535
- error(validate_input_response['msg'])
536
- raise typer.Exit(SNAPCTL_ERROR)
537
- command_method = subcommand.replace('-', '_')
538
- method: Callable[..., bool] = getattr(snapend_obj, command_method)
539
- if not method():
540
- error(f"Snapend {subcommand} failed")
541
- raise typer.Exit(SNAPCTL_ERROR)
544
+ getattr(snapend_obj, subcommand.replace('-', '_'))()
542
545
  success(f"Snapend {subcommand} complete")
543
-
544
-
545
- @app.command()
546
- def generate(
547
- ctx: typer.Context,
548
- # Required fields
549
- subcommand: str = typer.Argument(
550
- ..., help="Generate Subcommands: " + ", ".join(Generate.SUBCOMMANDS) + "."
551
- ),
552
- # byosnap-profile
553
- out_path: Union[str, None] = typer.Option(
554
- None, "--out-path", help="(req: byosnap-profile) Path to output the byosnap profile"
555
- ),
556
- # overrides
557
- api_key: Union[str, None] = typer.Option(
558
- None, "--api-key", help="API Key override.", callback=api_key_context_callback
559
- ),
560
- profile: Union[str, None] = typer.Option(
561
- None, "--profile", help="Profile to use.", callback=profile_context_callback
562
- ),
563
- ) -> None:
564
- """
565
- Generate files to be used by other commands
566
- """
567
- validate_command_context(ctx)
568
- generate_obj: Generate = Generate(
569
- subcommand, ctx.obj['base_url'], ctx.obj['api_key'],
570
- out_path,
571
- )
572
- validate_input_response: ResponseType = generate_obj.validate_input()
573
- if validate_input_response['error']:
574
- error(validate_input_response['msg'])
575
- raise typer.Exit(SNAPCTL_ERROR)
576
- command_method = subcommand.replace('-', '_')
577
- method: Callable[..., bool] = getattr(generate_obj, command_method)
578
- if not method():
579
- error(f"Generate {subcommand} failed")
580
- raise typer.Exit(SNAPCTL_ERROR)
581
- success(f"Generate {subcommand} complete")
546
+ raise typer.Exit(code=SNAPCTL_SUCCESS)
@@ -1,12 +1,28 @@
1
1
  """
2
2
  This file contains the definitions of the types used in the snapctl package.
3
3
  """
4
+ from typing import List, Union
4
5
 
5
6
 
6
- class ResponseType():
7
+ class ErrorResponse:
7
8
  """
8
9
  This class represents the response type of the Snapser API.
9
10
  """
10
- error: bool
11
- msg: str
12
- data: list
11
+
12
+ def __init__(self, error: bool, code: int, msg: str, data: Union[List, str]):
13
+ self.error = error
14
+ self.code = code
15
+ self.msg = msg
16
+ self.data = data
17
+
18
+ def to_dict(self):
19
+ '''
20
+ Convert the object to a dictionary
21
+
22
+ '''
23
+ return {
24
+ 'error': self.error,
25
+ 'code': self.code,
26
+ 'msg': self.msg,
27
+ 'data': self.data
28
+ }
snapctl/utils/echo.py CHANGED
@@ -1,15 +1,23 @@
1
1
  """
2
2
  This module contains functions to print messages to the console.
3
3
  """
4
+ import json
5
+ import typer
4
6
  from rich import print
7
+ from snapctl.config.constants import SNAPCTL_ERROR
8
+ from snapctl.types.definitions import ErrorResponse
5
9
  # Run `python -m rich.emoji` to get a list of all emojis that are supported
6
10
 
7
11
 
8
- def error(msg: str) -> None:
12
+ def error(msg: str, code: int = SNAPCTL_ERROR, data: object = None) -> None:
9
13
  """
10
14
  Prints an error message to the console.
11
15
  """
16
+ error_response = ErrorResponse(
17
+ error=True, code=code, msg=msg, data=data if data else ''
18
+ )
12
19
  print(f"[bold red]Error[/bold red] {msg}")
20
+ typer.echo(json.dumps(error_response.to_dict()), err=True)
13
21
 
14
22
 
15
23
  def info(msg: str) -> None:
@@ -23,4 +31,4 @@ def success(msg: str) -> None:
23
31
  """
24
32
  Prints a success message to the console.
25
33
  """
26
- print(f"[green]Success[/green] {msg}")
34
+ print(f"[bold green]Success[/bold green] {msg}")
snapctl/utils/helper.py CHANGED
@@ -3,11 +3,46 @@ Helper functions for snapctl
3
3
  """
4
4
  import requests
5
5
  import typer
6
+ from requests.exceptions import RequestException
7
+ from rich.progress import Progress
6
8
  from snapctl.config.constants import HTTP_NOT_FOUND, HTTP_FORBIDDEN, HTTP_UNAUTHORIZED, \
7
- SERVER_CALL_TIMEOUT
9
+ SERVER_CALL_TIMEOUT, SNAPCTL_CONFIGURATION_ERROR, SNAPCTL_SUCCESS
8
10
  from snapctl.utils.echo import error, success
9
11
 
10
12
 
13
+ def validate_api_key(base_url: str, api_key: str | None):
14
+ """
15
+ This function validates the API Key
16
+ """
17
+ try:
18
+ url = f"{base_url}/v1/snapser-api/games"
19
+ res = requests.get(
20
+ url, headers={'api-key': api_key},
21
+ timeout=SERVER_CALL_TIMEOUT
22
+ )
23
+ if res.ok:
24
+ success('API Key validated')
25
+ return True
26
+ if res.status_code == HTTP_NOT_FOUND:
27
+ error('Service ID is invalid.', SNAPCTL_CONFIGURATION_ERROR)
28
+ elif res.status_code == HTTP_UNAUTHORIZED:
29
+ error(
30
+ 'API Key verification failed. Your API Key is either invalid or may have expired. ',
31
+ SNAPCTL_CONFIGURATION_ERROR
32
+ )
33
+ elif res.status_code == HTTP_FORBIDDEN:
34
+ error(
35
+ 'Permission denied. Your role has been revoked. Please contact your administrator.',
36
+ SNAPCTL_CONFIGURATION_ERROR
37
+ )
38
+ else:
39
+ error('Failed to validate API Key. Error:',
40
+ SNAPCTL_CONFIGURATION_ERROR)
41
+ except RequestException as e:
42
+ error(f"Exception: Unable to update your snapend {e}")
43
+ raise typer.Exit(code=SNAPCTL_CONFIGURATION_ERROR)
44
+
45
+
11
46
  def get_composite_token(base_url: str, api_key: str | None, action: str, params: object) -> str:
12
47
  """
13
48
  This function exchanges the api_key for a composite token.
@@ -30,9 +65,31 @@ def get_composite_token(base_url: str, api_key: str | None, action: str, params:
30
65
  )
31
66
  elif res.status_code == HTTP_FORBIDDEN:
32
67
  error(
33
- 'Permission denied. Your role has been revoked. Please contact your administrator.')
68
+ 'Permission denied. Your role has been revoked. Please contact your administrator.'
69
+ )
34
70
  else:
35
71
  error(f'Failed to validate API Key. Error: {res.text}')
36
- raise typer.Exit()
72
+ raise typer.Exit(code=SNAPCTL_CONFIGURATION_ERROR)
37
73
  success('API Key validated')
38
74
  return res.json()['token']
75
+
76
+
77
+ def snapctl_success(message: str, progress: Progress | None = None, no_exit: bool = False):
78
+ """
79
+ This function exits the snapctl
80
+ """
81
+ if progress:
82
+ progress.stop()
83
+ success(message)
84
+ if not no_exit:
85
+ raise typer.Exit(code=SNAPCTL_SUCCESS)
86
+
87
+
88
+ def snapctl_error(message: str, code: int, progress: Progress | None = None):
89
+ """
90
+ This function exits the snapctl
91
+ """
92
+ if progress:
93
+ progress.stop()
94
+ error(message, code)
95
+ raise typer.Exit(code=code)