snapctl 0.31.1__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.31.1'
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/config/hashes.py CHANGED
@@ -155,3 +155,17 @@ SERVICE_IDS = [
155
155
  'relay', 'remote-config', 'scheduler', 'sequencer', 'social-graph', 'statistics', 'storage',
156
156
  'trackables', 'xp'
157
157
  ]
158
+
159
+ DEFAULT_BYOSNAP_TEMPLATE = {
160
+ 'cpu': 100,
161
+ 'memory': 0.125,
162
+ 'cmd': '',
163
+ 'args': [],
164
+ 'env_params': []
165
+ }
166
+
167
+ BYOSNAP_TEMPLATE = {
168
+ 'dev_template': DEFAULT_BYOSNAP_TEMPLATE,
169
+ 'stage_template': DEFAULT_BYOSNAP_TEMPLATE,
170
+ 'prod_template': DEFAULT_BYOSNAP_TEMPLATE
171
+ }
snapctl/main.py CHANGED
@@ -4,21 +4,23 @@
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
 
11
11
  from snapctl.commands.byosnap import ByoSnap
12
12
  from snapctl.commands.byogs import ByoGs
13
13
  from snapctl.commands.game import Game
14
+ from snapctl.commands.generate import Generate
14
15
  from snapctl.commands.snapend import Snapend
15
16
  from snapctl.config.constants import COMPANY_NAME, API_KEY, URL_KEY, CONFIG_FILE_MAC, \
16
- 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
17
19
  from snapctl.config.endpoints import END_POINTS
18
20
  from snapctl.config.hashes import CLIENT_SDK_TYPES, SERVER_SDK_TYPES, PROTOS_TYPES, SERVICE_IDS, \
19
21
  SNAPEND_MANIFEST_TYPES
20
- from snapctl.types.definitions import ResponseType
21
22
  from snapctl.utils.echo import error, success, info
23
+ from snapctl.utils.helper import validate_api_key
22
24
 
23
25
  ######### Globals #########
24
26
 
@@ -65,20 +67,22 @@ def extract_config(extract_key: str, profile: str | None = None) -> object:
65
67
  config_file_path = os.path.expandvars(CONFIG_FILE_WIN)
66
68
  else:
67
69
  config_file_path = os.path.expanduser(CONFIG_FILE_MAC)
68
- result['location'] = f'config-file:{config_file_path}'
70
+ result['location'] = f'{config_file_path}'
69
71
  if os.path.isfile(config_file_path):
70
72
  config = configparser.ConfigParser()
71
73
  config.read(config_file_path, encoding=encoding)
72
74
  config_profile: str = DEFAULT_PROFILE
73
75
  if profile is not None and profile != '' and profile != DEFAULT_PROFILE:
74
- result['location'] = f'{config_file_path}:profile {profile}'
76
+ result['location'] = f'"{config_file_path}:profile {profile}"'
75
77
  config_profile = f'profile {profile}'
76
- 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
+ )
77
81
  result['value'] = config.get(
78
82
  config_profile, extract_key, fallback=None, raw=True
79
83
  )
80
84
  else:
81
- error(
85
+ info(
82
86
  f'Config file on platform {platform} not found at {config_file_path}')
83
87
  return result
84
88
 
@@ -110,14 +114,9 @@ def validate_command_context(
110
114
  Validator to confirm if the context has been set properly
111
115
  """
112
116
  if ctx.obj['api_key'] is None or ctx.obj['base_url'] == '':
113
- error(
114
- "Unable to set command context. "
115
- f"API Key:{ctx.obj['api_key']} "
116
- f"API Key Category:{ctx.obj['api_key_location']} "
117
- f"Base URL:{ctx.obj['base_url']}"
118
- )
119
- raise typer.Exit(SNAPCTL_ERROR)
120
- 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)
121
120
 
122
121
  ######### CALLBACKS #########
123
122
 
@@ -173,16 +172,16 @@ def profile_context_callback(
173
172
  # Ensure ctx object is instantiated
174
173
  ctx.ensure_object(dict)
175
174
  api_key_obj = extract_config(API_KEY, profile)
176
- if api_key_obj['value'] is None and profile is not None and profile != '':
177
- conf_file = ''
178
- if platform == 'win32':
179
- conf_file = os.path.expandvars(CONFIG_FILE_WIN)
180
- else:
181
- conf_file = os.path.expanduser(CONFIG_FILE_MAC)
182
- error(
183
- f'Invalid profile input {profile}. '
184
- f'Please check your snap config file at {conf_file}'
185
- )
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
+ # )
186
185
  ctx.obj['version'] = VERSION
187
186
  ctx.obj['api_key'] = api_key_obj['value']
188
187
  ctx.obj['api_key_location'] = api_key_obj['location']
@@ -197,7 +196,7 @@ def version_callback(value: bool = True):
197
196
  """
198
197
  if value:
199
198
  success(f"Snapctl version: {VERSION}")
200
- raise typer.Exit(SNAPCTL_SUCCESS)
199
+ raise typer.Exit(code=SNAPCTL_SUCCESS)
201
200
 
202
201
 
203
202
  @app.callback()
@@ -226,12 +225,57 @@ def validate(
226
225
  profile: Union[str, None] = typer.Option(
227
226
  None, "--profile", help="Profile to use.", callback=profile_context_callback
228
227
  ),
229
- ):
228
+ ) -> None:
230
229
  """
231
230
  Validate your Snapctl setup
232
231
  """
233
232
  validate_command_context(ctx)
233
+ validate_api_key(ctx.obj['base_url'], ctx.obj['api_key'])
234
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)
235
279
 
236
280
 
237
281
  @app.command()
@@ -283,6 +327,11 @@ def byosnap(
283
327
  http_port: Union[str, None] = typer.Option(
284
328
  None, "--http-port", help="(req: publish-version) Ingress HTTP port version"
285
329
  ),
330
+ byosnap_profile: Union[str, None] = typer.Option(
331
+ None, "--byosnap-profile", help=(
332
+ "(req: publish-version) Path to your byosnap-profile JSON file"
333
+ )
334
+ ),
286
335
  # overrides
287
336
  api_key: Union[str, None] = typer.Option(
288
337
  None, "--api-key", help="API Key override.", callback=api_key_context_callback
@@ -298,68 +347,24 @@ def byosnap(
298
347
  byosnap_obj: ByoSnap = ByoSnap(
299
348
  subcommand, ctx.obj['base_url'], ctx.obj['api_key'], sid,
300
349
  name, desc, platform_type, language, tag, path, docker_file,
301
- prefix, version, http_port
350
+ prefix, version, http_port, byosnap_profile
302
351
  )
303
- validate_input_response: ResponseType = byosnap_obj.validate_input()
304
- if validate_input_response['error']:
305
- error(validate_input_response['msg'])
306
- raise typer.Exit(SNAPCTL_ERROR)
307
- command_method = subcommand.replace('-', '_')
308
- method: Callable[..., bool] = getattr(byosnap_obj, command_method)
309
- if not method():
310
- error(f"BYOSnap {subcommand} failed")
311
- raise typer.Exit(SNAPCTL_ERROR)
352
+ getattr(byosnap_obj, subcommand.replace('-', '_'))()
312
353
  success(f"BYOSnap {subcommand} complete")
354
+ raise typer.Exit(code=SNAPCTL_SUCCESS)
313
355
 
314
356
 
315
357
  @app.command()
316
- def byogs(
358
+ def game(
317
359
  ctx: typer.Context,
318
360
  # Required fields
319
361
  subcommand: str = typer.Argument(
320
- ..., help="BYOGs Subcommands: " + ", ".join(ByoGs.SUBCOMMANDS) + "."
321
- ),
322
- sid: str = typer.Argument(
323
- ByoGs.SID, help="Game Server Id. Should start with byogs"
362
+ ..., help="Game Subcommands: " + ", ".join(Game.SUBCOMMANDS) + "."
324
363
  ),
325
- # create
364
+ # name
326
365
  name: str = typer.Option(
327
- None, "--name", help="(req: create) Name for your snap"
328
- ),
329
- desc: str = typer.Option(
330
- None, "--desc", help="(req: create) Description for your snap"
331
- ),
332
- platform_type: str = typer.Option(
333
- None, "--platform",
334
- help="(req: create) Platform for your snap - " + \
335
- ", ".join(ByoGs.PLATFORMS) + "."
336
- ),
337
- language: str = typer.Option(
338
- None, "--language",
339
- help="(req: create) Language of your snap - " + \
340
- ", ".join(ByoGs.LANGUAGES) + "."
341
- ),
342
- # publish, publish-image and publish-version
343
- tag: str = typer.Option(
344
- None, "--tag",
345
- help="(req: build, push, publish, publish-image and publish-version) Tag for your snap"
346
- ),
347
- # publish and publish-image
348
- path: Union[str, None] = typer.Option(
349
- None, "--path", help="(req: build, publish, publish-image, upload-docs) Path to your snap code"
350
- ),
351
- docker_file: str = typer.Option(
352
- "Dockerfile", help="Dockerfile name to use"
353
- ),
354
- # publish-version
355
- version: Union[str, None] = typer.Option(
356
- None, "--version", help="(req: publish-version) Snap version"
357
- ),
358
- http_port: Union[str, None] = typer.Option(
359
- None, "--http-port", help="(req: publish-version) Ingress HTTP port version"
360
- ),
361
- debug_port: Union[str, None] = typer.Option(
362
- None, "--debug-port", help="(optional: publish-version) Debug HTTP port version"
366
+ None, "--name",
367
+ help=("(req: create) Name of your game: ")
363
368
  ),
364
369
  # overrides
365
370
  api_key: Union[str, None] = typer.Option(
@@ -370,37 +375,26 @@ def byogs(
370
375
  ),
371
376
  ) -> None:
372
377
  """
373
- Bring your own game server commands
378
+ Game commands
374
379
  """
375
380
  validate_command_context(ctx)
376
- byogs_obj: ByoGs = ByoGs(
377
- subcommand, ctx.obj['base_url'], ctx.obj['api_key'], sid,
378
- name, desc, platform_type, language, tag, path, docker_file,
379
- version, http_port, debug_port
380
- )
381
- validate_input_response: ResponseType = byogs_obj.validate_input()
382
- if validate_input_response['error']:
383
- error(validate_input_response['msg'])
384
- raise typer.Exit(SNAPCTL_ERROR)
385
- command_method = subcommand.replace('-', '_')
386
- method: Callable[..., bool] = getattr(byogs_obj, command_method)
387
- if not method():
388
- error(f"BYOGs {subcommand} failed")
389
- raise typer.Exit(SNAPCTL_ERROR)
390
- 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)
391
386
 
392
387
 
393
388
  @app.command()
394
- def game(
389
+ def generate(
395
390
  ctx: typer.Context,
396
391
  # Required fields
397
392
  subcommand: str = typer.Argument(
398
- ..., help="Game Subcommands: " + ", ".join(Game.SUBCOMMANDS) + "."
393
+ ..., help="Generate Subcommands: " + ", ".join(Generate.SUBCOMMANDS) + "."
399
394
  ),
400
- # name
401
- name: str = typer.Option(
402
- None, "--name",
403
- 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"
404
398
  ),
405
399
  # overrides
406
400
  api_key: Union[str, None] = typer.Option(
@@ -411,21 +405,16 @@ def game(
411
405
  ),
412
406
  ) -> None:
413
407
  """
414
- Game commands
408
+ Generate files to be used by other commands
415
409
  """
416
410
  validate_command_context(ctx)
417
- game_obj: Game = Game(
418
- subcommand, ctx.obj['base_url'], ctx.obj['api_key'], name)
419
- validate_input_response: ResponseType = game_obj.validate_input()
420
- if validate_input_response['error']:
421
- error(validate_input_response['msg'])
422
- raise typer.Exit(SNAPCTL_ERROR)
423
- command_method = subcommand.replace('-', '_')
424
- method: Callable[..., bool] = getattr(game_obj, command_method)
425
- if not method():
426
- error(f"Game {subcommand} failed")
427
- raise typer.Exit(SNAPCTL_ERROR)
428
- 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)
429
418
 
430
419
 
431
420
  @app.command()
@@ -438,7 +427,7 @@ def snapend(
438
427
  # snapend_id: str = typer.Argument(..., help="Snapend Id"),
439
428
  snapend_id: str = typer.Option(
440
429
  None, "--snapend-id",
441
- help=("(req: update, download) Snapend Id")
430
+ help=("(req: state, update, download) Snapend Id")
442
431
  ),
443
432
  # enumerate
444
433
  game_id: str = typer.Option(
@@ -552,13 +541,6 @@ def snapend(
552
541
  # Update
553
542
  byosnaps, byogs, blocking
554
543
  )
555
- validate_input_response: ResponseType = snapend_obj.validate_input()
556
- if validate_input_response['error']:
557
- error(validate_input_response['msg'])
558
- raise typer.Exit(SNAPCTL_ERROR)
559
- command_method = subcommand.replace('-', '_')
560
- method: Callable[..., bool] = getattr(snapend_obj, command_method)
561
- if not method():
562
- error(f"Snapend {subcommand} failed")
563
- raise typer.Exit(SNAPCTL_ERROR)
544
+ getattr(snapend_obj, subcommand.replace('-', '_'))()
564
545
  success(f"Snapend {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)
@@ -0,0 +1,29 @@
1
+ Snapser Inc. Proprietary License
2
+
3
+ This CLI tool, its source code, and any accompanying documentation (collectively referred to as the "Software") are the property of Snapser Inc. ("Snapser"). The Software is provided under the following license terms and conditions:
4
+
5
+ 1. License Grant:
6
+ Snapser grants you a non-exclusive, non-transferable, revocable license to use the Software solely for your internal business purposes. You may not distribute, sublicense, rent, lease, sell, or otherwise transfer the Software or any portion thereof.
7
+
8
+ 2. Restrictions:
9
+ You may not modify, adapt, translate, reverse engineer, decompile, disassemble, or create derivative works based on the Software. You may not remove or alter any copyright, trademark, or other proprietary rights notices contained in the Software.
10
+
11
+ 3. Ownership:
12
+ The Software and all intellectual property rights therein are and shall remain the exclusive property of Snapser Inc. Nothing in this license agreement shall be construed to transfer ownership of the Software to you.
13
+
14
+ 4. Term and Termination:
15
+ This license is effective until terminated by either party. Snapser may terminate this license at any time if you breach any of its terms and conditions. Upon termination, you must immediately cease all use of the Software and destroy all copies thereof.
16
+
17
+ 5. Disclaimer of Warranty:
18
+ THE SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED. TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, SNAPSER DISCLAIMS ALL WARRANTIES, INCLUDING BUT NOT LIMITED TO IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. SNAPSER DOES NOT WARRANT THAT THE FUNCTIONS CONTAINED IN THE SOFTWARE WILL MEET YOUR REQUIREMENTS OR THAT THE OPERATION OF THE SOFTWARE WILL BE UNINTERRUPTED OR ERROR-FREE.
19
+
20
+ 6. Limitation of Liability:
21
+ IN NO EVENT SHALL SNAPSER BE LIABLE FOR ANY SPECIAL, INCIDENTAL, INDIRECT, OR CONSEQUENTIAL DAMAGES WHATSOEVER (INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF PROFITS, BUSINESS INTERRUPTION, LOSS OF INFORMATION, OR ANY OTHER PECUNIARY LOSS) ARISING OUT OF THE USE OF OR INABILITY TO USE THE SOFTWARE, EVEN IF SNAPSER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
22
+
23
+ 7. Governing Law:
24
+ This license agreement shall be governed by and construed in accordance with the laws of the United States of America, excluding its conflicts of law principles. Any disputes arising out of or in connection with this agreement shall be subject to the exclusive jurisdiction of the courts located in the United States of America.
25
+
26
+ By using the Software, you agree to be bound by the terms and conditions of this license agreement. If you do not agree to these terms and conditions, do not use the Software.
27
+
28
+ Snapser Inc.
29
+ June 4, 2024