NGPIris 5.4.3.dev1__tar.gz → 5.5.0__tar.gz

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.
Files changed (55) hide show
  1. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/NGPIris/cli/__init__.py +189 -88
  2. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/NGPIris/hcp/hcp.py +136 -84
  3. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0/NGPIris.egg-info}/PKG-INFO +4 -3
  4. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/NGPIris.egg-info/requires.txt +3 -2
  5. {ngpiris-5.4.3.dev1/NGPIris.egg-info → ngpiris-5.5.0}/PKG-INFO +4 -3
  6. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/pyproject.toml +4 -3
  7. ngpiris-5.5.0/tests/test_hci.py +80 -0
  8. ngpiris-5.4.3.dev1/tests/test_hci.py +0 -73
  9. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/LICENSE +0 -0
  10. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/MANIFEST.in +0 -0
  11. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/NGPIris/__init__.py +0 -0
  12. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/NGPIris/hci/__init__.py +0 -0
  13. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/NGPIris/hci/exceptions.py +0 -0
  14. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/NGPIris/hci/hci.py +0 -0
  15. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/NGPIris/hci/helpers.py +0 -0
  16. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/NGPIris/hcp/__init__.py +0 -0
  17. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/NGPIris/hcp/exceptions.py +0 -0
  18. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/NGPIris/hcp/helpers.py +0 -0
  19. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/NGPIris/hcp/statistics.py +0 -0
  20. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/NGPIris/parse_credentials/__init__.py +0 -0
  21. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/NGPIris/parse_credentials/parse_credentials.py +0 -0
  22. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/NGPIris/utils/__init__.py +0 -0
  23. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/NGPIris/utils/utils.py +0 -0
  24. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/NGPIris.egg-info/SOURCES.txt +0 -0
  25. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/NGPIris.egg-info/dependency_links.txt +0 -0
  26. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/NGPIris.egg-info/entry_points.txt +0 -0
  27. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/NGPIris.egg-info/top_level.txt +0 -0
  28. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/README.md +0 -0
  29. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/docs/IRIS_5_changelog.md +0 -0
  30. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/docs/Makefile +0 -0
  31. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/docs/NGPIris.cli.rst +0 -0
  32. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/docs/NGPIris.hci.rst +0 -0
  33. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/docs/NGPIris.hcp.rst +0 -0
  34. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/docs/NGPIris.parse_credentials.rst +0 -0
  35. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/docs/NGPIris.rst +0 -0
  36. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/docs/NGPIris.utils.rst +0 -0
  37. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/docs/Tutorial.md +0 -0
  38. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/docs/conf.py +0 -0
  39. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/docs/index.rst +0 -0
  40. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/docs/make.bat +0 -0
  41. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/docs/modules.rst +0 -0
  42. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/setup.cfg +0 -0
  43. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/tests/data/80MB_test_file +0 -0
  44. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/tests/data/a folder of data/80MB_test_file_0 +0 -0
  45. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/tests/data/a folder of data/80MB_test_file_1 +0 -0
  46. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/tests/data/complete_creds_0.json +0 -0
  47. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/tests/data/complete_creds_1.json +0 -0
  48. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/tests/data/complete_creds_2.json +0 -0
  49. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/tests/data/incomplete_creds_0.json +0 -0
  50. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/tests/data/incomplete_creds_1.json +0 -0
  51. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/tests/data/incomplete_creds_2.json +0 -0
  52. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/tests/data/incomplete_creds_3.json +0 -0
  53. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/tests/data/incomplete_creds_4.json +0 -0
  54. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/tests/test_hcp.py +0 -0
  55. {ngpiris-5.4.3.dev1 → ngpiris-5.5.0}/tests/test_parse_credentials.py +0 -0
@@ -6,37 +6,73 @@ from pathlib import Path
6
6
  from boto3 import set_stream_logger
7
7
  from typing import Any, Generator
8
8
  from bitmath import Byte, TiB
9
+ import lazy_table as lt
9
10
  import sys
11
+ import os
10
12
 
11
13
  from NGPIris.hcp import HCPHandler
12
14
 
13
- def get_HCPHandler(context : Context) -> HCPHandler:
14
- return context.obj["hcph"]
15
-
16
- def format_list(list_of_things : list) -> str:
17
- list_of_buckets = list(map(lambda s : s + "\n", list_of_things))
18
- return "".join(list_of_buckets).strip("\n")
19
-
20
- def _list_objects_generator(hcph : HCPHandler, path : str, name_only : bool, files_only : bool) -> Generator[str, Any, None]:
21
- """
22
- Handle object list as a paginator that `click` can handle. It works slightly
23
- different from `list_objects` in `hcp.py` in order to make the output
24
- printable in a terminal
25
- """
26
- objects = hcph.list_objects(path, name_only, files_only)
27
- for obj in objects:
28
- yield str(obj) + "\n"
29
-
30
- def object_is_folder(object_path : str, hcph : HCPHandler) -> bool:
31
- return (object_path[-1] == "/") and (hcph.get_object(object_path)["ContentLength"] == 0)
32
-
33
15
  def add_trailing_slash(path : str) -> str:
34
16
  if not path[-1] == "/":
35
17
  path += "/"
36
18
  return path
37
19
 
20
+ def create_HCPHandler(context : Context) -> HCPHandler:
21
+ # hcp_credentials : str | dict[str, str], debug : bool, transfer_config : str
22
+
23
+ if context.parent:
24
+ parent_context = context.parent
25
+ else:
26
+ # Should never happen
27
+ click.echo("Something went wrong with the subcommand and parent command relation", err = True)
28
+ sys.exit(1)
29
+
30
+ credentials : str | None = parent_context.params.get("credentials")
31
+
32
+ if credentials:
33
+ hcp_credentials = credentials
34
+ elif os.environ.get("NGPIRIS_CREDENTIALS_PATH", None):
35
+ hcp_credentials = os.environ["NGPIRIS_CREDENTIALS_PATH"]
36
+ else:
37
+ endpoint : str = click.prompt(
38
+ "Please enter your tenant endpoint"
39
+ )
40
+
41
+ aws_access_key_id : str = click.prompt(
42
+ "Please enter your base64 hashed aws_access_key_id"
43
+ )
44
+
45
+ aws_secret_access_key : str = click.prompt(
46
+ "Please enter your md5 hashed aws_secret_access_key",
47
+ hide_input = True,
48
+ confirmation_prompt = True
49
+ )
50
+
51
+ hcp_credentials = {
52
+ "endpoint" : endpoint,
53
+ "aws_access_key_id" : aws_access_key_id,
54
+ "aws_secret_access_key" : aws_secret_access_key
55
+ }
56
+
57
+ debug : bool | None = parent_context.params.get("debug")
58
+ transfer_config : str | None = parent_context.params.get("transfer_config")
59
+ if transfer_config:
60
+ hcp_h = HCPHandler(hcp_credentials, custom_config_path = transfer_config)
61
+ else:
62
+ hcp_h = HCPHandler(hcp_credentials)
63
+
64
+ if debug:
65
+ set_stream_logger(name="")
66
+ click.echo(hcp_h.transfer_config.__dict__)
67
+
68
+ return hcp_h
69
+
38
70
  @click.group()
39
- @click.argument("credentials")
71
+ @click.option(
72
+ "-c",
73
+ "--credentials",
74
+ help = "Path to a JSON file with credentials",
75
+ )
40
76
  @click.option(
41
77
  "--debug",
42
78
  help = "Get the debug log for running a command",
@@ -45,7 +81,7 @@ def add_trailing_slash(path : str) -> str:
45
81
  @click.option(
46
82
  "-tc",
47
83
  "--transfer_config",
48
- help = "Use a custom transfer config for uploads or downloads",
84
+ help = "Path for using a custom transfer config for uploads or downloads",
49
85
  )
50
86
  @click.version_option(package_name = "NGPIris")
51
87
  @click.pass_context
@@ -56,18 +92,47 @@ def cli(context : Context, credentials : str, debug : bool, transfer_config : st
56
92
  CREDENTIALS refers to the path to the JSON credentials file.
57
93
  """
58
94
 
59
- if transfer_config:
60
- context.ensure_object(dict)
61
- context.obj["hcph"] = HCPHandler(credentials, custom_config_path = transfer_config)
62
- else:
63
- context.ensure_object(dict)
64
- context.obj["hcph"] = HCPHandler(credentials)
95
+ @cli.command(short_help = "This command returns a shell command that sets the `NGPIRIS_CREDENTIALS_PATH` enviroment variable depending on your shell")
96
+ @click.argument(
97
+ "credentials_path",
98
+ required = False
99
+ )
100
+ @click.option(
101
+ "-s",
102
+ "--shell",
103
+ type = click.Choice(
104
+ ["bash", "fish", "zsh"],
105
+ case_sensitive = False
106
+ ),
107
+ help = "Allows for selection of shell that the produced command should support",
108
+ default = "bash"
109
+ )
110
+ @click.pass_context
111
+ def shell_env(context : Context, credentials_path : str, shell : str):
112
+ """
113
+ NGP IRIS will look for an enviroment variable called
114
+ `NGPIRIS_CREDENTIALS_PATH` when authenicating. This command returns a
115
+ shell command that sets the `NGPIRIS_CREDENTIALS_PATH` enviroment variable
116
+ depending on your shell.
117
+
118
+ NOTE: The enviroment variable will only last for this
119
+ shell session, but you could set the value of `NGPIRIS_CREDENTIALS_PATH`
120
+ permanently via other commands if you wish to do so.
65
121
 
66
- if debug:
67
- set_stream_logger(name="")
68
- click.echo(
69
- context.obj["hcph"].transfer_config.__dict__
70
- )
122
+ CREDENTIALS PATH is the either absolute or relative path to your credentials
123
+ JSON file
124
+ """
125
+ if not credentials_path:
126
+ click.prompt("Please enter the path to your credentials file")
127
+
128
+ click.echo("Copy and paste the following command in order to set your environment variable:")
129
+ match shell:
130
+ case "bash":
131
+ click.echo("export NGPIRIS_CREDENTIALS_PATH=" + credentials_path)
132
+ case "fish":
133
+ click.echo("set -x NGPIRIS_CREDENTIALS_PATH " + credentials_path)
134
+ case "zsh":
135
+ click.echo("export NGPIRIS_CREDENTIALS_PATH=" + credentials_path)
71
136
 
72
137
  @cli.command()
73
138
  @click.argument("bucket")
@@ -114,7 +179,7 @@ def upload(context : Context, bucket : str, source : str, destination : str, dry
114
179
 
115
180
  upload_mode_choice = HCPHandler.UploadMode(upload_mode.lower())
116
181
 
117
- hcph : HCPHandler = get_HCPHandler(context)
182
+ hcph : HCPHandler = create_HCPHandler(context)
118
183
  hcph.mount_bucket(bucket)
119
184
  destination = add_trailing_slash(destination)
120
185
  if Path(source).is_dir():
@@ -131,7 +196,7 @@ def upload(context : Context, bucket : str, source : str, destination : str, dry
131
196
  else:
132
197
  hcph.upload_file(source, destination, upload_mode = upload_mode_choice, equal_parts = equal_parts)
133
198
 
134
- @cli.command()
199
+ @cli.command(short_help = "Download a file or folder from an HCP bucket/namespace.")
135
200
  @click.argument("bucket")
136
201
  @click.argument("source")
137
202
  @click.argument("destination")
@@ -164,7 +229,10 @@ def download(context : Context, bucket : str, source : str, destination : str, f
164
229
 
165
230
  DESTINATION is the folder where the downloaded object or object folder is to be stored locally.
166
231
  """
167
- hcph : HCPHandler = get_HCPHandler(context)
232
+ def object_is_folder(object_path : str, hcph : HCPHandler) -> bool:
233
+ return (object_path[-1] == "/") and (hcph.get_object(object_path)["ContentLength"] == 0)
234
+
235
+ hcph : HCPHandler = create_HCPHandler(context)
168
236
  hcph.mount_bucket(bucket)
169
237
  if not Path(destination).exists():
170
238
  Path(destination).mkdir()
@@ -227,15 +295,14 @@ def delete_object(context : Context, bucket : str, object : str, dry_run : bool)
227
295
 
228
296
  OBJECT is the name of the object to be deleted.
229
297
  """
230
- hcph : HCPHandler = get_HCPHandler(context)
298
+ hcph : HCPHandler = create_HCPHandler(context)
231
299
  hcph.mount_bucket(bucket)
232
300
  if not dry_run:
233
- hcph.delete_object(object)
301
+ click.echo(hcph.delete_object(object))
234
302
  else:
235
303
  click.echo("This command would delete:")
236
304
  click.echo(list(hcph.list_objects(object))[0])
237
305
 
238
-
239
306
  @cli.command()
240
307
  @click.argument("bucket")
241
308
  @click.argument("folder")
@@ -254,34 +321,47 @@ def delete_folder(context : Context, bucket : str, folder : str, dry_run : bool)
254
321
 
255
322
  FOLDER is the name of the folder to be deleted.
256
323
  """
257
- hcph : HCPHandler = get_HCPHandler(context)
324
+ hcph : HCPHandler = create_HCPHandler(context)
258
325
  hcph.mount_bucket(bucket)
259
326
  if not dry_run:
260
- hcph.delete_folder(folder)
327
+ click.echo(hcph.delete_folder(folder))
261
328
  else:
262
329
  click.echo("By deleting \"" + folder + "\", the following objects would have been deleted (not including objects in sub-folders):")
263
330
  for obj in hcph.list_objects(folder):
264
331
  click.echo(obj)
265
332
 
333
+ @cli.command()
334
+ @click.argument("bucket")
335
+ @click.option(
336
+ "-dr",
337
+ "--dry_run",
338
+ help = "Simulate the command execution without making actual changes. Useful for testing and verification",
339
+ is_flag = True
340
+ )
341
+ @click.pass_context
342
+ def delete_bucket(context : Context, bucket : str, dry_run : bool):
343
+ hcph : HCPHandler = create_HCPHandler(context)
344
+ if not dry_run:
345
+ click.echo(hcph.delete_bucket(bucket))
346
+ else:
347
+ click.echo("This command would have deleted the bucket called \"" + bucket + "\"")
348
+
266
349
  @cli.command()
267
350
  @click.pass_context
268
351
  def list_buckets(context : Context):
269
352
  """
270
353
  List the available buckets/namespaces on the HCP.
271
354
  """
272
- hcph : HCPHandler = get_HCPHandler(context)
273
- click.echo(format_list(hcph.list_buckets()))
355
+ hcph : HCPHandler = create_HCPHandler(context)
356
+ click.echo(
357
+ "".join(
358
+ list(map(lambda s : s + "\n", hcph.list_buckets()))
359
+ ).strip("\n")
360
+ )
274
361
 
275
- @cli.command()
362
+ @cli.command(short_help = "List the objects in a certain bucket/namespace on the HCP.")
276
363
  @click.argument("bucket")
277
364
  @click.argument("path", required = False)
278
- @click.option(
279
- "-no",
280
- "--name-only",
281
- help = "Output only the name of the objects instead of all the associated metadata",
282
- default = False,
283
- is_flag = True
284
- )
285
365
  @click.option(
286
366
  "-p",
287
367
  "--pagination",
@@ -296,8 +376,15 @@ def list_buckets(context : Context):
296
376
  default = False,
297
377
  is_flag = True
298
378
  )
379
+ @click.option(
380
+ "-e",
381
+ "--extended-information",
382
+ help = "Output the fully exteded information for each object",
383
+ default = False,
384
+ is_flag = True
385
+ )
299
386
  @click.pass_context
300
- def list_objects(context : Context, bucket : str, path : str, name_only : bool, pagination : bool, files_only : bool):
387
+ def list_objects(context : Context, bucket : str, path : str, pagination : bool, files_only : bool, extended_information : bool):
301
388
  """
302
389
  List the objects in a certain bucket/namespace on the HCP.
303
390
 
@@ -305,8 +392,23 @@ def list_objects(context : Context, bucket : str, path : str, name_only : bool,
305
392
 
306
393
  PATH is an optional argument for where to list the objects
307
394
  """
308
- hcph : HCPHandler = get_HCPHandler(context)
395
+ def list_objects_generator(hcph : HCPHandler, path : str, files_only : bool, output_mode : HCPHandler.ListObjectsOutputMode) -> Generator[str, Any, None]:
396
+ """
397
+ Handle object list as a paginator that `click` can handle. It works slightly
398
+ different from `list_objects` in `hcp.py` in order to make the output
399
+ printable in a terminal
400
+ """
401
+ objects = hcph.list_objects(path, output_mode = output_mode, files_only = files_only)
402
+ for obj in objects:
403
+ yield str(obj) + "\n"
404
+
405
+ hcph : HCPHandler = create_HCPHandler(context)
309
406
  hcph.mount_bucket(bucket)
407
+ output_mode = (
408
+ HCPHandler.ListObjectsOutputMode.EXTENDED if extended_information
409
+ else HCPHandler.ListObjectsOutputMode.SIMPLE
410
+ )
411
+
310
412
  if path:
311
413
  path_with_slash = add_trailing_slash(path)
312
414
 
@@ -316,12 +418,25 @@ def list_objects(context : Context, bucket : str, path : str, name_only : bool,
316
418
  path_with_slash = ""
317
419
 
318
420
  if pagination:
319
- click.echo_via_pager(_list_objects_generator(hcph, path_with_slash, name_only, files_only))
421
+ click.echo_via_pager(
422
+ list_objects_generator(
423
+ hcph,
424
+ path_with_slash,
425
+ files_only,
426
+ output_mode
427
+ )
428
+ )
320
429
  else:
321
- for obj in hcph.list_objects(path_with_slash, name_only, files_only):
322
- click.echo(obj)
430
+ lt.stream(
431
+ hcph.list_objects(
432
+ path_with_slash,
433
+ output_mode = output_mode,
434
+ files_only = files_only
435
+ ),
436
+ headers = "keys"
437
+ )
323
438
 
324
- @cli.command()
439
+ @cli.command(short_help = "Make simple search using substrings in a bucket/namespace on the HCP.")
325
440
  @click.argument("bucket")
326
441
  @click.argument("search_string")
327
442
  @click.option(
@@ -331,15 +446,8 @@ def list_objects(context : Context, bucket : str, path : str, name_only : bool,
331
446
  default = False,
332
447
  is_flag = True
333
448
  )
334
- @click.option(
335
- "-v",
336
- "--verbose",
337
- help = "Get a verbose output of files. Default value is False, since it might be slower",
338
- default = False,
339
- is_flag = True
340
- )
341
449
  @click.pass_context
342
- def simple_search(context : Context, bucket : str, search_string : str, case_sensitive : bool, verbose : bool):
450
+ def simple_search(context : Context, bucket : str, search_string : str, case_sensitive : bool):
343
451
  """
344
452
  Make simple search using substrings in a bucket/namespace on the HCP.
345
453
 
@@ -350,18 +458,19 @@ def simple_search(context : Context, bucket : str, search_string : str, case_sen
350
458
 
351
459
  SEARCH_STRING is any string that is to be used for the search.
352
460
  """
353
- hcph : HCPHandler = get_HCPHandler(context)
461
+ hcph : HCPHandler = create_HCPHandler(context)
354
462
  hcph.mount_bucket(bucket)
355
463
  list_of_results = hcph.search_in_bucket(
356
- search_string,
357
- name_only = (not verbose),
464
+ search_string,
358
465
  case_sensitive = case_sensitive
359
466
  )
360
467
  click.echo("Search results:")
361
- for result in list_of_results:
362
- click.echo(result)
468
+ lt.stream(
469
+ list_of_results,
470
+ headers = "keys"
471
+ )
363
472
 
364
- @cli.command()
473
+ @cli.command(short_help = "Make a fuzzy search using a search string in a bucket/namespace on the HCP.")
365
474
  @click.argument("bucket")
366
475
  @click.argument("search_string")
367
476
  @click.option(
@@ -371,13 +480,6 @@ def simple_search(context : Context, bucket : str, search_string : str, case_sen
371
480
  default = False,
372
481
  is_flag = True
373
482
  )
374
- @click.option(
375
- "-v",
376
- "--verbose",
377
- help = "Get a verbose output of files. Default value is False, since it might be slower",
378
- default = False,
379
- is_flag = True
380
- )
381
483
  @click.option(
382
484
  "-t",
383
485
  "--threshold",
@@ -385,29 +487,30 @@ def simple_search(context : Context, bucket : str, search_string : str, case_sen
385
487
  default = 80
386
488
  )
387
489
  @click.pass_context
388
- def fuzzy_search(context : Context, bucket : str, search_string : str, case_sensitive : bool, verbose : bool, threshold : int):
490
+ def fuzzy_search(context : Context, bucket : str, search_string : str, case_sensitive : bool, threshold : int):
389
491
  """
390
492
  Make a fuzzy search using a search string in a bucket/namespace on the HCP.
391
493
 
392
494
  NOTE: This command does not use the HCI. Instead, it uses the RapidFuzz
393
- library in order to find objects in the HCP. As such, this search might
394
- be slow.
495
+ library in order to find objects in the HCP. As such, this search might be
496
+ slow.
395
497
 
396
498
  BUCKET is the name of the bucket in which to make the search.
397
499
 
398
500
  SEARCH_STRING is any string that is to be used for the search.
399
501
  """
400
- hcph : HCPHandler = get_HCPHandler(context)
502
+ hcph : HCPHandler = create_HCPHandler(context)
401
503
  hcph.mount_bucket(bucket)
402
504
  list_of_results = hcph.fuzzy_search_in_bucket(
403
505
  search_string,
404
- name_only = (not verbose),
405
506
  case_sensitive = case_sensitive,
406
507
  threshold = threshold
407
508
  )
408
509
  click.echo("Search results:")
409
- for result in list_of_results:
410
- click.echo(result)
510
+ lt.stream(
511
+ list_of_results,
512
+ headers = "keys"
513
+ )
411
514
 
412
515
  @cli.command()
413
516
  @click.argument("bucket")
@@ -418,7 +521,7 @@ def test_connection(context : Context, bucket : str):
418
521
 
419
522
  BUCKET is the name of the bucket for which a connection test should be made.
420
523
  """
421
- hcph : HCPHandler = get_HCPHandler(context)
524
+ hcph : HCPHandler = create_HCPHandler(context)
422
525
  click.echo(hcph.test_connection(bucket))
423
526
 
424
527
  @click.command()
@@ -470,5 +573,3 @@ def iris_generate_credentials_file(path : str, name : str):
470
573
 
471
574
  with open(file_path, "w") as f:
472
575
  dump(credentials_dict, f, indent = 4)
473
-
474
-
@@ -20,8 +20,9 @@ from botocore.paginate import PageIterator, Paginator
20
20
  from botocore.exceptions import EndpointConnectionError, ClientError
21
21
  from boto3.s3.transfer import TransferConfig
22
22
  from configparser import ConfigParser
23
-
24
23
  from pathlib import Path
24
+ from itertools import islice
25
+ from more_itertools import peekable
25
26
 
26
27
  from os import (
27
28
  stat,
@@ -43,18 +44,18 @@ from tqdm import tqdm
43
44
  from bitmath import TiB, Byte
44
45
 
45
46
  from enum import Enum
46
- from typing import Generator
47
+ from typing import Any, Generator
47
48
 
48
49
  _KB = 1024
49
50
  _MB = _KB * _KB
50
51
 
51
52
  class HCPHandler:
52
- def __init__(self, credentials_path : str, use_ssl : bool = False, proxy_path : str = "", custom_config_path : str = "") -> None:
53
+ def __init__(self, credentials : str | dict[str, str], use_ssl : bool = False, proxy_path : str = "", custom_config_path : str = "") -> None:
53
54
  """
54
55
  Class for handling HCP requests.
55
56
 
56
- :param credentials_path: Path to the JSON credentials file
57
- :type credentials_path: str
57
+ :param credentials: If `credentials` is a `str`, then it will be interpreted as a path to the JSON credentials file. If `credentials` is a `dict`, then a dictionary with the appropriate HCP credentials is expected: ```{"endpoint" : "", "aws_access_key_id" : "", "aws_secret_access_key" : "" }```
58
+ :type credentials: str | dict
58
59
 
59
60
  :param use_ssl: Boolean choice between using SSL, defaults to False
60
61
  :type use_ssl: bool, optional
@@ -62,9 +63,18 @@ class HCPHandler:
62
63
  :param custom_config_path: Path to a .ini file for customs settings regarding download and upload
63
64
  :type custom_config_path: str, optional
64
65
  """
65
- credentials_handler = CredentialsHandler(credentials_path)
66
- self.hcp = credentials_handler.hcp
67
- self.endpoint = "https://" + self.hcp["endpoint"]
66
+ if type(credentials) is str:
67
+ credentials_handler = CredentialsHandler(credentials)
68
+ self.hcp = credentials_handler.hcp
69
+
70
+ self.endpoint = "https://" + self.hcp["endpoint"]
71
+ self.aws_access_key_id = self.hcp["aws_access_key_id"]
72
+ self.aws_secret_access_key = self.hcp["aws_secret_access_key"]
73
+ elif type(credentials) is dict:
74
+ self.endpoint = "https://" + credentials["endpoint"]
75
+ self.aws_access_key_id = credentials["aws_access_key_id"]
76
+ self.aws_secret_access_key = credentials["aws_secret_access_key"]
77
+
68
78
 
69
79
  # A lookup table for GMC names to HCP tenant names
70
80
  gmc_tenant_map = {
@@ -98,8 +108,6 @@ class HCPHandler:
98
108
  if not self.tenant:
99
109
  raise RuntimeError("Unable to parse endpoint, \"" + self.endpoint + "\". Make sure that you have entered the correct endpoint in your credentials JSON file. Hints:\n - The endpoint should *not* contain \"https://\" or port numbers\n - Is the endpoint spelled correctly?")
100
110
  self.base_request_url = self.endpoint + ":9090/mapi/tenants/" + self.tenant
101
- self.aws_access_key_id = self.hcp["aws_access_key_id"]
102
- self.aws_secret_access_key = self.hcp["aws_secret_access_key"]
103
111
  self.token = self.aws_access_key_id + ":" + self.aws_secret_access_key
104
112
  self.bucket_name = None
105
113
  self.use_ssl = use_ssl
@@ -204,8 +212,7 @@ class HCPHandler:
204
212
  try:
205
213
  response = dict(self.s3_client.head_bucket(Bucket = bucket_name))
206
214
  except EndpointConnectionError as e: # pragma: no cover
207
- print(e)
208
- raise VPNConnectionError("Please check your connection and that you have your VPN enabled")
215
+ raise e
209
216
  except ClientError as e:
210
217
  status_code = e.response["ResponseMetadata"].get("HTTPStatusCode", -1)
211
218
  match status_code:
@@ -256,26 +263,36 @@ class HCPHandler:
256
263
  list_of_buckets : list[str] = response["name"]
257
264
  return list_of_buckets
258
265
 
266
+ class ListObjectsOutputMode(Enum):
267
+ SIMPLE = "simple"
268
+ EXTENDED = "extended"
269
+ NAME_ONLY = "name_only"
270
+
259
271
  @check_mounted
260
272
  def list_objects(
261
273
  self,
262
274
  path_key : str = "",
263
- list_all_bucket_objects : bool = False,
264
- name_only : bool = False,
265
- files_only : bool = False
266
- ) -> Generator:
275
+ output_mode : ListObjectsOutputMode = ListObjectsOutputMode.EXTENDED,
276
+ files_only : bool = False,
277
+ list_all_bucket_objects : bool = False
278
+ ) -> Generator[dict[str, Any], Any, None]:
267
279
  """
268
280
  List all objects in the mounted bucket as a generator. If one wishes to
269
281
  get the result as a list, use :py:function:`list` to type cast the generator
270
282
 
271
283
  :param path_key: Filter string for which keys to list, specifically for finding objects in certain folders. Defaults to \"the root\" of the bucket
272
284
  :type path_key: str, optional
273
- :param list_all_bucket_objects: If True, the value of `path_key` will be ignored and instead will list all objects in the bucket. Defaults to False
274
- :type list_all_bucket_objects: bool, optional
275
- :param name_only: If True, yield only a the object names. If False, yield the full metadata about each object. Defaults to False.
276
- :type name_only: bool, optional
285
+ :param output_mode:
286
+ The upload mode of the transfer is any of the following:\n
287
+ HCPHandler.ListObjectsOutputMode.SIMPLE,\n
288
+ HCPHandler.ListObjectsOutputMode.EXTENDED,\n
289
+ HCPHandler.ListObjectsOutputMode.NAME_ONLY\n
290
+ Default is EXTENDED
291
+ :type output_mode: ListObjectsOutputMode, optional
277
292
  :param files_only: If True, only yield file objects. Defaults to False
278
293
  :type files_only: bool, optional
294
+ :param list_all_bucket_objects: If True, the value of `path_key` will be ignored and instead will list all objects in the bucket. Defaults to False
295
+ :type list_all_bucket_objects: bool, optional
279
296
  :yield: A generator of all objects in specified folder in a bucket
280
297
  :rtype: Generator
281
298
  """
@@ -291,39 +308,45 @@ class HCPHandler:
291
308
  if not page:
292
309
  break
293
310
 
294
- if files_only:
295
- for file_object in page.get("Contents", []):
296
- file_object : dict
297
- if file_object["Key"] != path_key:
298
- file_object["IsFile"] = True
299
- if name_only:
300
- yield file_object["Key"]
301
- else:
302
- yield file_object
303
- else:
311
+ if not files_only: # Hide folder objects when flag `files_only` is True
312
+ # Handle folder objects before file objects
304
313
  for folder_object in page.get("CommonPrefixes", []):
305
314
  folder_object : dict
306
315
  folder_object_metadata = self.get_object(folder_object["Prefix"])
307
-
308
- if name_only:
309
- yield folder_object["Prefix"]
310
- else:
311
- yield {
312
- "Key" : folder_object["Prefix"],
313
- "LastModified" : folder_object_metadata["LastModified"],
314
- "ETag" : folder_object_metadata["ETag"],
315
- "IsFile" : False,
316
- }
317
-
318
- for file_object in page.get("Contents", []):
319
- file_object : dict
320
- if file_object["Key"] != path_key:
321
- file_object["IsFile"] = True
322
- if name_only:
323
- yield file_object["Key"]
324
- else:
316
+ match output_mode:
317
+ case HCPHandler.ListObjectsOutputMode.EXTENDED:
318
+ yield {
319
+ "Key" : folder_object["Prefix"],
320
+ "LastModified" : folder_object_metadata["LastModified"],
321
+ "ETag" : folder_object_metadata["ETag"],
322
+ "IsFile" : False,
323
+ }
324
+ case HCPHandler.ListObjectsOutputMode.SIMPLE:
325
+ yield {
326
+ "Key" : folder_object["Prefix"],
327
+ "LastModified" : folder_object_metadata["LastModified"],
328
+ "IsFile" : False,
329
+ }
330
+ case HCPHandler.ListObjectsOutputMode.NAME_ONLY:
331
+ yield {"Key" : folder_object["Prefix"]}
332
+
333
+ # Handle file objects
334
+ for file_object in page.get("Contents", []):
335
+ file_object : dict
336
+ if file_object["Key"] != path_key:
337
+ file_object["IsFile"] = True
338
+ match output_mode:
339
+ case HCPHandler.ListObjectsOutputMode.EXTENDED:
325
340
  yield file_object
326
-
341
+ case HCPHandler.ListObjectsOutputMode.SIMPLE:
342
+ yield {
343
+ "Key" : file_object["Key"],
344
+ "LastModified" : file_object["LastModified"],
345
+ "Size" : file_object["Size"],
346
+ "IsFile" : file_object["IsFile"]
347
+ }
348
+ case HCPHandler.ListObjectsOutputMode.NAME_ONLY:
349
+ yield {"Key" : file_object["Key"]}
327
350
 
328
351
  @check_mounted
329
352
  def get_object(self, key : str) -> dict:
@@ -406,7 +429,6 @@ class HCPHandler:
406
429
  Config = self.transfer_config,
407
430
  )
408
431
  except ClientError as e0:
409
- print(str(e0))
410
432
  raise e0
411
433
  except Exception as e: # pragma: no cover
412
434
  raise Exception(e)
@@ -490,10 +512,11 @@ class HCPHandler:
490
512
  :type show_progress_bar: bool, optional
491
513
 
492
514
  :param upload_mode:
493
- The upload mode of the transfer is any of the following:
494
- HCPHandler.UploadMode.STANDARD,
495
- HCPHandler.UploadMode.SIMPLE,
515
+ The upload mode of the transfer is any of the following:\n
516
+ HCPHandler.UploadMode.STANDARD,\n
517
+ HCPHandler.UploadMode.SIMPLE,\n
496
518
  HCPHandler.UploadMode.EQUAL_PARTS\n
519
+ Default is STANDARD
497
520
  :type upload_mode: UploadMode, optional
498
521
 
499
522
  :param equal_parts: The number of equal parts that each file should be divided into when using the HCPHandler.UploadMode.EQUAL_PARTS mode. Default is 5
@@ -587,14 +610,15 @@ class HCPHandler:
587
610
  )
588
611
 
589
612
  @check_mounted
590
- def delete_objects(self, keys : list[str], verbose : bool = True) -> None:
591
- """Delete a list of objects on the mounted bucket
613
+ def delete_objects(self, keys : list[str]) -> str:
614
+ """
615
+ Delete a list of objects on the mounted bucket
592
616
 
593
617
  :param keys: List of object names to be deleted
594
618
  :type keys: list[str]
595
619
 
596
- :param verbose: Print the result of the deletion. Defaults to True
597
- :type verbose: bool, optional
620
+ :return: The result of the deletion
621
+ :rtype: str
598
622
  """
599
623
  object_list = []
600
624
  does_not_exist = []
@@ -604,45 +628,58 @@ class HCPHandler:
604
628
  else:
605
629
  does_not_exist.append(key)
606
630
 
631
+ result = ""
607
632
  if object_list:
608
633
  deletion_dict = {"Objects": object_list}
609
634
  response : dict = self.s3_client.delete_objects(
610
635
  Bucket = self.bucket_name,
611
636
  Delete = deletion_dict
612
637
  )
613
- if verbose:
614
- print(dumps(response, indent=4))
615
638
 
616
- if verbose and does_not_exist:
617
- print("The following could not be deleted because they didn't exist: \n" + "\n".join(does_not_exist))
639
+ deleted_files = list(d["Key"] for d in response["Deleted"])
640
+ result += "The following was successfully deleted: \n" + "\n".join(deleted_files)
641
+
642
+ if does_not_exist:
643
+ result += "The following could not be deleted because they didn't exist: \n" + "\n".join(does_not_exist)
644
+
645
+ return result
618
646
 
619
647
  @check_mounted
620
- def delete_object(self, key : str, verbose : bool = True) -> None:
648
+ def delete_object(self, key : str) -> str:
621
649
  """
622
650
  Delete a single object in the mounted bucket
623
651
 
624
652
  :param key: The object to be deleted
625
653
  :type key: str
626
- :param verbose: Print the result of the deletion. Defaults to True
627
- :type verbose: bool, optional
654
+
655
+ :return: The result of the deletion
656
+ :rtype: str
628
657
  """
629
- self.delete_objects([key], verbose = verbose)
658
+ return self.delete_objects([key])
630
659
 
631
660
  @check_mounted
632
- def delete_folder(self, key : str, verbose : bool = True) -> None:
661
+ def delete_folder(self, key : str) -> str:
633
662
  """
634
663
  Delete a folder of objects in the mounted bucket. If there are subfolders, a RuntimeError is raised
635
664
 
636
665
  :param key: The folder of objects to be deleted
637
666
  :type key: str
638
- :param verbose: Print the result of the deletion. defaults to True
639
- :type verbose: bool, optional
667
+
640
668
  :raises RuntimeError: If there are subfolders, a RuntimeError is raised
669
+
670
+ :return: The result of the deletion
671
+ :rtype: str
641
672
  """
642
673
  if key[-1] != "/":
643
674
  key += "/"
644
675
 
645
- objects : list[str] = list(self.list_objects(key, name_only = True))
676
+ objects : list[str] = list(
677
+ obj["Key"] for obj in
678
+ self.list_objects(
679
+ key,
680
+ output_mode = HCPHandler.ListObjectsOutputMode.NAME_ONLY
681
+ )
682
+ )
646
683
  objects.append(key) # Include the object "folder" path to be deleted
647
684
 
648
685
  if not objects:
@@ -651,13 +688,28 @@ class HCPHandler:
651
688
  for object_path in objects:
652
689
  if (object_path[-1] == "/") and (not object_path == key): # `objects` might contain key, in which case everything is fine
653
690
  raise RuntimeError("There are subfolders in this folder. Please remove these first, before deleting this one")
654
- self.delete_objects(objects, verbose = verbose)
691
+
692
+ return self.delete_objects(objects)
693
+
694
+ def delete_bucket(self, bucket : str) -> str:
695
+ """
696
+ Delete a specified bucket
697
+
698
+ :param bucket: The bucket to be deleted
699
+ :type bucket: str
700
+ :return: The result of the deletion
701
+ :rtype: str
702
+ """
703
+ self.s3_client.delete_bucket(
704
+ Bucket = bucket
705
+ )
706
+ # If the deletion was not successful, `self.s3_client.delete_bucket` would have thrown an error
707
+ return bucket + " was successfully deleted"
655
708
 
656
709
  @check_mounted
657
710
  def search_in_bucket(
658
711
  self,
659
712
  search_string : str,
660
- name_only : bool = True,
661
713
  case_sensitive : bool = False
662
714
  ) -> Generator:
663
715
  """
@@ -667,22 +719,18 @@ class HCPHandler:
667
719
  :param search_string: Substring to be used in the search
668
720
  :type search_string: str
669
721
 
670
- :param name_only: If True, yield only a the object names. If False, yield the full metadata about each object. Defaults to False.
671
- :type name_only: bool, optional
672
-
673
722
  :param case_sensitive: Case sensitivity. Defaults to False
674
723
  :type case_sensitive: bool, optional
675
724
 
676
725
  :return: A generator of objects based on the search string
677
726
  :rtype: Generator
678
727
  """
679
- return self.fuzzy_search_in_bucket(search_string, name_only, case_sensitive, 100)
728
+ return self.fuzzy_search_in_bucket(search_string, case_sensitive, 100)
680
729
 
681
730
  @check_mounted
682
731
  def fuzzy_search_in_bucket(
683
732
  self,
684
733
  search_string : str,
685
- name_only : bool = True,
686
734
  case_sensitive : bool = False,
687
735
  threshold : int = 80
688
736
  ) -> Generator:
@@ -710,20 +758,24 @@ class HCPHandler:
710
758
  else:
711
759
  processor = utils.default_process
712
760
 
713
- if not name_only:
714
- full_list = list(self.list_objects(list_all_bucket_objects = True))
761
+ full_list = peekable(self.list_objects(list_all_bucket_objects = True))
715
762
 
716
- for item, score, index in process.extract_iter(
763
+ full_list_names_only = peekable(
764
+ obj["Key"] for obj in
765
+ self.list_objects(
766
+ output_mode = HCPHandler.ListObjectsOutputMode.NAME_ONLY,
767
+ list_all_bucket_objects = True
768
+ )
769
+ )
770
+
771
+ for _, score, index in process.extract_iter(
717
772
  search_string,
718
- self.list_objects(list_all_bucket_objects = True, name_only = True),
773
+ full_list_names_only,
719
774
  scorer = fuzz.partial_ratio,
720
775
  processor = processor
721
776
  ):
722
777
  if score >= threshold:
723
- if name_only:
724
- yield item
725
- else:
726
- yield full_list[index] # type: ignore
778
+ yield full_list[index]
727
779
 
728
780
  @check_mounted
729
781
  def get_object_acl(self, key : str) -> dict:
@@ -1,18 +1,19 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: NGPIris
3
- Version: 5.4.3.dev1
3
+ Version: 5.5.0
4
4
  Author-email: Erik Brink <erik.brink@gu.se>, Isak Sylvin <isak.sylvin@gu.se>
5
5
  Description-Content-Type: text/markdown
6
6
  License-File: LICENSE
7
7
  Requires-Dist: requests>=2.31.0
8
- Requires-Dist: urllib3==1.26.19
8
+ Requires-Dist: urllib3==2.5.0
9
9
  Requires-Dist: boto3==1.35.81
10
10
  Requires-Dist: parse>=1.19.1
11
11
  Requires-Dist: RapidFuzz>=3.10.1
12
12
  Requires-Dist: tqdm>=4.66.2
13
13
  Requires-Dist: click>=8.1.7
14
14
  Requires-Dist: bitmath==1.3.3.1
15
- Requires-Dist: tabulate==0.9.0
15
+ Requires-Dist: lazy_table==0.2.1
16
+ Requires-Dist: more-itertools==10.7.0
16
17
  Dynamic: license-file
17
18
 
18
19
  # NGP IRIS 👀
@@ -1,9 +1,10 @@
1
1
  requests>=2.31.0
2
- urllib3==1.26.19
2
+ urllib3==2.5.0
3
3
  boto3==1.35.81
4
4
  parse>=1.19.1
5
5
  RapidFuzz>=3.10.1
6
6
  tqdm>=4.66.2
7
7
  click>=8.1.7
8
8
  bitmath==1.3.3.1
9
- tabulate==0.9.0
9
+ lazy_table==0.2.1
10
+ more-itertools==10.7.0
@@ -1,18 +1,19 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: NGPIris
3
- Version: 5.4.3.dev1
3
+ Version: 5.5.0
4
4
  Author-email: Erik Brink <erik.brink@gu.se>, Isak Sylvin <isak.sylvin@gu.se>
5
5
  Description-Content-Type: text/markdown
6
6
  License-File: LICENSE
7
7
  Requires-Dist: requests>=2.31.0
8
- Requires-Dist: urllib3==1.26.19
8
+ Requires-Dist: urllib3==2.5.0
9
9
  Requires-Dist: boto3==1.35.81
10
10
  Requires-Dist: parse>=1.19.1
11
11
  Requires-Dist: RapidFuzz>=3.10.1
12
12
  Requires-Dist: tqdm>=4.66.2
13
13
  Requires-Dist: click>=8.1.7
14
14
  Requires-Dist: bitmath==1.3.3.1
15
- Requires-Dist: tabulate==0.9.0
15
+ Requires-Dist: lazy_table==0.2.1
16
+ Requires-Dist: more-itertools==10.7.0
16
17
  Dynamic: license-file
17
18
 
18
19
  # NGP IRIS 👀
@@ -1,17 +1,18 @@
1
1
  [project]
2
2
  name = "NGPIris"
3
- version = "5.4.3.dev1"
3
+ version = "5.5.0"
4
4
  readme = "README.md"
5
5
  dependencies = [
6
6
  "requests >= 2.31.0",
7
- "urllib3 == 1.26.19",
7
+ "urllib3 == 2.5.0",
8
8
  "boto3 == 1.35.81",
9
9
  "parse >= 1.19.1",
10
10
  "RapidFuzz >= 3.10.1",
11
11
  "tqdm >= 4.66.2",
12
12
  "click >= 8.1.7",
13
13
  "bitmath == 1.3.3.1",
14
- "tabulate == 0.9.0"
14
+ "lazy_table == 0.2.1",
15
+ "more-itertools == 10.7.0"
15
16
  ]
16
17
  authors = [
17
18
  {name = "Erik Brink", email = "erik.brink@gu.se"},
@@ -0,0 +1,80 @@
1
+
2
+ from conftest import CustomConfig
3
+ from random import randint
4
+
5
+ def test_list_index_names_type(custom_config : CustomConfig) -> None:
6
+ custom_config.hci_h.request_token()
7
+ list_of_indexes = custom_config.hci_h.list_index_names()
8
+ assert type(list_of_indexes) is list
9
+
10
+ def test_look_up_all_indexes(custom_config : CustomConfig) -> None:
11
+ custom_config.hci_h.request_token()
12
+ list_of_indexes = custom_config.hci_h.list_index_names()
13
+ for index in list_of_indexes:
14
+ assert custom_config.hci_h.look_up_index(index)
15
+
16
+ def test_fail_index_look_up(custom_config : CustomConfig) -> None:
17
+ custom_config.hci_h.request_token()
18
+ assert not custom_config.hci_h.look_up_index("anIndexThatDoesNotExist")
19
+
20
+ def test_make_simple_raw_query(custom_config : CustomConfig) -> None:
21
+ custom_config.hci_h.request_token()
22
+ list_of_indexes = custom_config.hci_h.list_index_names()
23
+ arbitrary_index = list_of_indexes[randint(0, len(list_of_indexes) - 1)]
24
+ result = custom_config.hci_h.raw_query(
25
+ {
26
+ "indexName" : arbitrary_index
27
+ }
28
+ )
29
+ assert result["indexName"] == arbitrary_index
30
+
31
+ def test_fail_raw_query(custom_config : CustomConfig) -> None:
32
+ custom_config.hci_h.request_token()
33
+ query = {}
34
+ try:
35
+ custom_config.hci_h.raw_query(query)
36
+ except:
37
+ assert True
38
+ else: # pragma: no cover
39
+ assert False
40
+
41
+ def test_make_query(custom_config : CustomConfig) -> None:
42
+ custom_config.hci_h.request_token()
43
+ custom_config.hci_h.query(
44
+ custom_config.test_index
45
+ )
46
+
47
+ def test_make_query_with_query_string(custom_config : CustomConfig) -> None:
48
+ custom_config.hci_h.request_token()
49
+ custom_config.hci_h.query(
50
+ custom_config.test_index,
51
+ custom_config.query_string
52
+ )
53
+
54
+ def test_make_query_with_query_string_and_facet(custom_config : CustomConfig) -> None:
55
+ custom_config.hci_h.request_token()
56
+ if custom_config.test_facet:
57
+ custom_config.hci_h.query(
58
+ custom_config.test_index,
59
+ custom_config.query_string,
60
+ [custom_config.test_facet]
61
+ )
62
+ else:
63
+ custom_config.hci_h.query(
64
+ custom_config.test_index,
65
+ custom_config.query_string,
66
+ []
67
+ )
68
+
69
+ def test_make_query_with_facet(custom_config : CustomConfig) -> None:
70
+ custom_config.hci_h.request_token()
71
+ if custom_config.test_facet:
72
+ custom_config.hci_h.query(
73
+ custom_config.test_index,
74
+ facets = [custom_config.test_facet]
75
+ )
76
+ else:
77
+ custom_config.hci_h.query(
78
+ custom_config.test_index,
79
+ facets = []
80
+ )
@@ -1,73 +0,0 @@
1
-
2
- from configparser import ConfigParser
3
- from NGPIris.hci import HCIHandler
4
- from random import randint
5
- from json import dump
6
- from os import remove
7
-
8
- ini_config = ConfigParser()
9
- ini_config.read("tests/test_conf.ini")
10
-
11
- hci_h = HCIHandler(ini_config.get("General", "credentials_path"))
12
- hci_h.request_token()
13
-
14
- def test_list_index_names_type() -> None:
15
- list_of_indexes = hci_h.list_index_names()
16
- assert type(list_of_indexes) is list
17
-
18
- def test_look_up_all_indexes() -> None:
19
- list_of_indexes = hci_h.list_index_names()
20
- for index in list_of_indexes:
21
- assert hci_h.look_up_index(index)
22
-
23
- def test_fail_index_look_up() -> None:
24
- assert not hci_h.look_up_index("anIndexThatDoesNotExist")
25
-
26
- def test_make_simple_raw_query() -> None:
27
- list_of_indexes = hci_h.list_index_names()
28
- arbitrary_index = list_of_indexes[randint(0, len(list_of_indexes) - 1)]
29
- result = hci_h.raw_query(
30
- {
31
- "indexName" : arbitrary_index
32
- }
33
- )
34
- assert result["indexName"] == arbitrary_index
35
-
36
- def test_raw_query_with_results() -> None:
37
- query = {
38
- "indexName" : "Parsed_VCF_Index",
39
- "facetRequests" : [
40
- {
41
- "fieldName" : "ref"
42
- },
43
- {
44
- "fieldName" : "alt"
45
- }
46
- ]
47
- }
48
- parsed_VCF_index_result : list = hci_h.raw_query(query)["facets"]
49
- for i in range(len(parsed_VCF_index_result)):
50
- assert parsed_VCF_index_result[i]["termCounts"] # Assert if the result is not empty
51
-
52
- def test_fail_raw_query() -> None:
53
- query = {}
54
- try:
55
- hci_h.raw_query(query)
56
- except:
57
- assert True
58
- else: # pragma: no cover
59
- assert False
60
-
61
- def test_make_simple_raw_query_from_JSON() -> None:
62
- list_of_indexes = hci_h.list_index_names()
63
- arbitrary_index = list_of_indexes[randint(0, len(list_of_indexes) - 1)]
64
- path = "tests/data/json_test_query.json"
65
- with open(path, "w") as f:
66
- query = {
67
- "indexName": arbitrary_index
68
- }
69
- dump(query, f, indent = 4)
70
- result = hci_h.raw_query_from_JSON(path)
71
- assert result["indexName"] == arbitrary_index
72
- remove(path)
73
-
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes