opensecureconf-client 2.3.1__py3-none-any.whl → 3.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,6 @@
1
+ opensecureconf_client.py,sha256=pnMDS-UW5pr8xxJS6gaePIdn2vQX6INlLeRHU4PTjTY,34396
2
+ opensecureconf_client-3.0.0.dist-info/licenses/LICENSE,sha256=mvMdzinneV_-L01ddrHOBgbutNS8tjT1m7loT7VTWbI,1073
3
+ opensecureconf_client-3.0.0.dist-info/METADATA,sha256=SPhMNSD9P9BjwNoaH2067MimKgb1K5DBTaiOLyept1Q,42145
4
+ opensecureconf_client-3.0.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
5
+ opensecureconf_client-3.0.0.dist-info/top_level.txt,sha256=J7NP3hD92OUdqseJLlbzgPuG_ovqkURRyw7iBJJeDVE,22
6
+ opensecureconf_client-3.0.0.dist-info/RECORD,,
opensecureconf_client.py CHANGED
@@ -13,7 +13,8 @@ Enhanced Features:
13
13
  - Enhanced input validation
14
14
  - Health check utilities
15
15
  - Support for multiple value types (dict, str, int, bool, list)
16
- - Category and environment support
16
+ - Multi-environment support (same key in different environments)
17
+ - Environment-based configuration isolation
17
18
  """
18
19
 
19
20
  from typing import Any, Dict, List, Optional, Union
@@ -24,29 +25,37 @@ from requests.adapters import HTTPAdapter
24
25
  from requests.exceptions import Timeout, RequestException
25
26
  from urllib3.util.retry import Retry
26
27
 
28
+
27
29
  # ============================================================================
28
30
  # EXCEPTIONS
29
31
  # ============================================================================
30
32
 
33
+
31
34
  class OpenSecureConfError(Exception):
32
35
  """Base exception for OpenSecureConf client errors."""
33
36
 
37
+
34
38
  class AuthenticationError(OpenSecureConfError):
35
39
  """Raised when authentication fails (invalid or missing user key)."""
36
40
 
41
+
37
42
  class ConfigurationNotFoundError(OpenSecureConfError):
38
43
  """Raised when a requested configuration key does not exist."""
39
44
 
45
+
40
46
  class ConfigurationExistsError(OpenSecureConfError):
41
47
  """Raised when attempting to create a configuration that already exists."""
42
48
 
49
+
43
50
  class ClusterError(OpenSecureConfError):
44
51
  """Raised when cluster operations fail."""
45
52
 
53
+
46
54
  # ============================================================================
47
55
  # CLIENT
48
56
  # ============================================================================
49
57
 
58
+
50
59
  class OpenSecureConfClient:
51
60
  """
52
61
  Enhanced client for interacting with the OpenSecureConf API.
@@ -69,12 +78,11 @@ class OpenSecureConfClient:
69
78
  ... enable_retry=True,
70
79
  ... log_level="INFO"
71
80
  ... )
72
- >>> # Dict value with category and environment
73
- >>> config = client.create("database", {"host": "localhost", "port": 5432},
74
- ... category="config", environment="production")
75
- >>> # String value
76
- >>> config = client.create("api_token", "secret-token-123",
77
- ... category="auth", environment="staging")
81
+ >>> # Same key in different environments
82
+ >>> prod_config = client.create("database", {"host": "db.prod.com", "port": 5432},
83
+ ... "production", "config")
84
+ >>> staging_config = client.create("database", {"host": "db.staging.com", "port": 5432},
85
+ ... "staging", "config")
78
86
  """
79
87
 
80
88
  def __init__(
@@ -349,143 +357,163 @@ class OpenSecureConfClient:
349
357
  self,
350
358
  key: str,
351
359
  value: Union[Dict[str, Any], str, int, bool, list],
352
- category: Optional[str] = None,
353
- environment: Optional[str] = None
360
+ environment: str,
361
+ category: Optional[str] = None
354
362
  ) -> Dict[str, Any]:
355
363
  """
356
364
  Create a new encrypted configuration entry.
357
365
 
358
366
  Args:
359
- key: Unique configuration key (1-255 characters)
367
+ key: Configuration key (1-255 characters)
360
368
  value: Configuration data (dict, string, int, bool, or list - will be encrypted)
369
+ environment: Environment identifier (REQUIRED, max 100 characters)
361
370
  category: Optional category for grouping (max 100 characters)
362
- environment: Optional environment identifier (max 100 characters)
363
371
 
364
372
  Returns:
365
373
  Dictionary containing the created configuration with fields:
366
374
  - id: Configuration ID
367
375
  - key: Configuration key
368
376
  - value: Configuration value (decrypted)
377
+ - environment: Environment identifier
369
378
  - category: Configuration category (if set)
370
- - environment: Environment identifier (if set)
371
379
 
372
380
  Raises:
373
- ConfigurationExistsError: If configuration key already exists
374
- ValueError: If key is invalid
381
+ ConfigurationExistsError: If configuration (key, environment) already exists
382
+ ValueError: If key or environment is invalid
375
383
 
376
384
  Example:
377
- >>> # Dict value with category and environment
378
- >>> config = client.create("database", {"host": "localhost", "port": 5432},
379
- ... category="config", environment="production")
385
+ >>> # Same key in different environments
386
+ >>> prod_config = client.create("database", {"host": "db.prod.com", "port": 5432},
387
+ ... "production", "config")
388
+ >>> staging_config = client.create("database", {"host": "db.staging.com", "port": 5432},
389
+ ... "staging", "config")
380
390
  >>> # String value
381
- >>> config = client.create("api_token", "secret-123",
382
- ... category="auth", environment="staging")
391
+ >>> client.create("api_token", "secret-123", "production", "auth")
383
392
  >>> # Integer value
384
- >>> config = client.create("max_retries", 3, environment="production")
393
+ >>> client.create("max_retries", 3, "production")
385
394
  >>> # Boolean value
386
- >>> config = client.create("debug", False, category="settings", environment="dev")
395
+ >>> client.create("debug", False, "development", "settings")
387
396
  """
388
397
  # Enhanced validation
389
398
  if not key or not isinstance(key, str):
390
399
  raise ValueError("Key must be a non-empty string")
391
400
  if len(key) > 255:
392
401
  raise ValueError("Key must be between 1 and 255 characters")
402
+ if not environment or not isinstance(environment, str):
403
+ raise ValueError("Environment is required and must be a non-empty string")
404
+ if len(environment) > 100:
405
+ raise ValueError("Environment must be max 100 characters")
393
406
  if category and len(category) > 100:
394
407
  raise ValueError("Category must be max 100 characters")
395
- if environment and len(environment) > 100:
396
- raise ValueError("Environment must be max 100 characters")
397
408
 
398
- payload = {"key": key, "value": value, "category": category, "environment": environment}
409
+ payload = {
410
+ "key": key,
411
+ "value": value,
412
+ "environment": environment,
413
+ "category": category
414
+ }
399
415
  return self._make_request("POST", "/configs", json=payload)
400
416
 
401
- def read(self, key: str) -> Dict[str, Any]:
417
+ def read(self, key: str, environment: str) -> Dict[str, Any]:
402
418
  """
403
- Read and decrypt a configuration entry by key.
419
+ Read and decrypt a configuration entry by key and environment.
404
420
 
405
421
  Args:
406
422
  key: Configuration key to retrieve
423
+ environment: Environment identifier (REQUIRED)
407
424
 
408
425
  Returns:
409
426
  Dictionary containing the configuration with decrypted value
410
427
  The value can be dict, str, int, bool, or list depending on what was stored
411
428
 
412
429
  Raises:
413
- ConfigurationNotFoundError: If configuration key does not exist
414
- ValueError: If key is invalid
430
+ ConfigurationNotFoundError: If configuration (key, environment) does not exist
431
+ ValueError: If key or environment is invalid
415
432
 
416
433
  Example:
417
- >>> config = client.read("database")
418
- >>> print(config["value"]) # Could be any supported type
419
- >>> print(config["environment"]) # e.g., "production"
434
+ >>> prod_config = client.read("database", "production")
435
+ >>> staging_config = client.read("database", "staging")
436
+ >>> print(prod_config["value"]) # Different from staging
437
+ >>> print(prod_config["environment"]) # "production"
420
438
  """
421
439
  if not key or not isinstance(key, str):
422
440
  raise ValueError("Key must be a non-empty string")
441
+ if not environment or not isinstance(environment, str):
442
+ raise ValueError("Environment is required and must be a non-empty string")
423
443
 
424
- return self._make_request("GET", f"/configs/{key}")
444
+ params = {"environment": environment}
445
+ return self._make_request("GET", f"/configs/{key}", params=params)
425
446
 
426
447
  def update(
427
448
  self,
428
449
  key: str,
450
+ environment: str,
429
451
  value: Union[Dict[str, Any], str, int, bool, list],
430
- category: Optional[str] = None,
431
- environment: Optional[str] = None
452
+ category: Optional[str] = None
432
453
  ) -> Dict[str, Any]:
433
454
  """
434
455
  Update an existing configuration entry with new encrypted value.
435
456
 
436
457
  Args:
437
458
  key: Configuration key to update
459
+ environment: Environment identifier (REQUIRED, cannot be changed)
438
460
  value: New configuration data (dict, string, int, bool, or list - will be encrypted)
439
461
  category: Optional new category
440
- environment: Optional new environment
441
462
 
442
463
  Returns:
443
464
  Dictionary containing the updated configuration with decrypted value
444
465
 
445
466
  Raises:
446
- ConfigurationNotFoundError: If configuration key does not exist
447
- ValueError: If key is invalid
467
+ ConfigurationNotFoundError: If configuration (key, environment) does not exist
468
+ ValueError: If key or environment is invalid
448
469
 
449
470
  Example:
450
- >>> # Update with dict
451
- >>> config = client.update("database", {"host": "db.example.com", "port": 5432},
452
- ... environment="production")
453
- >>> # Update with string and change environment
454
- >>> config = client.update("api_token", "new-token-456", environment="staging")
471
+ >>> # Update production config only
472
+ >>> config = client.update("database", "production",
473
+ ... {"host": "db-new.prod.com", "port": 5432})
474
+ >>> # Update with string and category
475
+ >>> config = client.update("api_token", "staging", "new-token-456", "auth")
455
476
  """
456
477
  if not key or not isinstance(key, str):
457
478
  raise ValueError("Key must be a non-empty string")
479
+ if not environment or not isinstance(environment, str):
480
+ raise ValueError("Environment is required and must be a non-empty string")
458
481
  if category and len(category) > 100:
459
482
  raise ValueError("Category must be max 100 characters")
460
- if environment and len(environment) > 100:
461
- raise ValueError("Environment must be max 100 characters")
462
483
 
463
- payload = {"value": value, "category": category, "environment": environment}
464
- return self._make_request("PUT", f"/configs/{key}", json=payload)
484
+ payload = {"value": value, "category": category}
485
+ params = {"environment": environment}
486
+ return self._make_request("PUT", f"/configs/{key}", json=payload, params=params)
465
487
 
466
- def delete(self, key: str) -> Dict[str, str]:
488
+ def delete(self, key: str, environment: str) -> Dict[str, str]:
467
489
  """
468
- Delete a configuration entry permanently.
490
+ Delete a configuration entry permanently from specific environment.
469
491
 
470
492
  Args:
471
493
  key: Configuration key to delete
494
+ environment: Environment identifier (REQUIRED)
472
495
 
473
496
  Returns:
474
497
  Dictionary with success message
475
498
 
476
499
  Raises:
477
- ConfigurationNotFoundError: If configuration key does not exist
478
- ValueError: If key is invalid
500
+ ConfigurationNotFoundError: If configuration (key, environment) does not exist
501
+ ValueError: If key or environment is invalid
479
502
 
480
503
  Example:
481
- >>> result = client.delete("database")
504
+ >>> # Delete from staging only
505
+ >>> result = client.delete("database", "staging")
506
+ >>> # Production and development environments remain untouched
482
507
  >>> print(result["message"])
483
508
  Configuration 'database' deleted successfully
484
509
  """
485
510
  if not key or not isinstance(key, str):
486
511
  raise ValueError("Key must be a non-empty string")
512
+ if not environment or not isinstance(environment, str):
513
+ raise ValueError("Environment is required and must be a non-empty string")
487
514
 
488
- return self._make_request("DELETE", f"/configs/{key}")
515
+ params = {"environment": environment}
516
+ return self._make_request("DELETE", f"/configs/{key}", params=params)
489
517
 
490
518
  def list_all(self, category: Optional[str] = None, environment: Optional[str] = None) -> List[Dict[str, Any]]:
491
519
  """
@@ -503,10 +531,10 @@ class OpenSecureConfClient:
503
531
  Example:
504
532
  >>> # List all
505
533
  >>> configs = client.list_all()
506
- >>> # Filter by category
507
- >>> prod_configs = client.list_all(category="production")
508
534
  >>> # Filter by environment
509
- >>> staging_configs = client.list_all(environment="staging")
535
+ >>> prod_configs = client.list_all(environment="production")
536
+ >>> # Filter by category
537
+ >>> db_configs = client.list_all(category="database")
510
538
  >>> # Filter by both
511
539
  >>> configs = client.list_all(category="database", environment="production")
512
540
  """
@@ -532,7 +560,7 @@ class OpenSecureConfClient:
532
560
 
533
561
  Args:
534
562
  configs: List of configuration dictionaries with 'key', 'value',
535
- and optional 'category' and 'environment'
563
+ 'environment' (REQUIRED), and optional 'category'
536
564
  Value can be dict, str, int, bool, or list
537
565
  ignore_errors: If True, continue on errors and return partial results
538
566
 
@@ -540,17 +568,17 @@ class OpenSecureConfClient:
540
568
  List of created configuration dictionaries
541
569
 
542
570
  Raises:
543
- ValueError: If configs format is invalid
571
+ ValueError: If configs format is invalid or environment is missing
544
572
  OpenSecureConfError: If creation fails and ignore_errors is False
545
573
 
546
574
  Example:
547
575
  >>> configs = [
548
- ... {"key": "db1", "value": {"host": "localhost"},
549
- ... "category": "db", "environment": "production"},
576
+ ... {"key": "db", "value": {"host": "localhost"},
577
+ ... "environment": "production", "category": "config"},
578
+ ... {"key": "db", "value": {"host": "localhost"},
579
+ ... "environment": "staging", "category": "config"},
550
580
  ... {"key": "token", "value": "secret-123",
551
- ... "category": "auth", "environment": "staging"},
552
- ... {"key": "retries", "value": 3,
553
- ... "category": "config", "environment": "production"}
581
+ ... "environment": "production", "category": "auth"}
554
582
  ... ]
555
583
  >>> results = client.bulk_create(configs)
556
584
  >>> print(f"Created {len(results)} configurations")
@@ -566,20 +594,28 @@ class OpenSecureConfClient:
566
594
  raise ValueError(f"Config at index {i} must be a dictionary")
567
595
  if "key" not in config or "value" not in config:
568
596
  raise ValueError(f"Config at index {i} missing required 'key' or 'value'")
597
+ if "environment" not in config:
598
+ raise ValueError(f"Config at index {i} missing required 'environment'")
569
599
 
570
600
  try:
571
601
  result = self.create(
572
602
  key=config["key"],
573
603
  value=config["value"],
574
- category=config.get("category"),
575
- environment=config.get("environment")
604
+ environment=config["environment"],
605
+ category=config.get("category")
576
606
  )
577
607
  results.append(result)
578
- self.logger.info(f"Bulk create: created '{config['key']}'")
608
+ self.logger.info(
609
+ f"Bulk create: created '{config['key']}' in '{config['environment']}'"
610
+ )
579
611
  except Exception as e:
580
- error_msg = f"Failed to create '{config['key']}': {str(e)}"
612
+ error_msg = f"Failed to create '{config['key']}' in '{config['environment']}': {str(e)}"
581
613
  self.logger.error(error_msg)
582
- errors.append({"key": config["key"], "error": str(e)})
614
+ errors.append({
615
+ "key": config["key"],
616
+ "environment": config["environment"],
617
+ "error": str(e)
618
+ })
583
619
  if not ignore_errors:
584
620
  raise OpenSecureConfError(error_msg) from e
585
621
 
@@ -590,41 +626,66 @@ class OpenSecureConfClient:
590
626
 
591
627
  def bulk_read(
592
628
  self,
593
- keys: List[str],
629
+ items: List[Dict[str, str]],
594
630
  ignore_errors: bool = False
595
631
  ) -> List[Dict[str, Any]]:
596
632
  """
597
633
  Read multiple configurations in batch.
598
634
 
599
635
  Args:
600
- keys: List of configuration keys to retrieve
636
+ items: List of dictionaries with 'key' and 'environment' fields
601
637
  ignore_errors: If True, skip missing keys and return partial results
602
638
 
603
639
  Returns:
604
640
  List of configuration dictionaries
605
641
 
642
+ Raises:
643
+ ValueError: If items format is invalid
644
+
606
645
  Example:
607
- >>> configs = client.bulk_read(["db1", "token", "retries"])
646
+ >>> items = [
647
+ ... {"key": "database", "environment": "production"},
648
+ ... {"key": "database", "environment": "staging"},
649
+ ... {"key": "api_token", "environment": "production"}
650
+ ... ]
651
+ >>> configs = client.bulk_read(items)
608
652
  >>> print(f"Retrieved {len(configs)} configurations")
609
653
  """
610
- if not isinstance(keys, list):
611
- raise ValueError("keys must be a list")
654
+ if not isinstance(items, list):
655
+ raise ValueError("items must be a list")
612
656
 
613
657
  results = []
614
658
  errors = []
615
659
 
616
- for key in keys:
660
+ for i, item in enumerate(items):
661
+ if not isinstance(item, dict):
662
+ raise ValueError(f"Item at index {i} must be a dictionary")
663
+ if "key" not in item or "environment" not in item:
664
+ raise ValueError(f"Item at index {i} missing required 'key' or 'environment'")
665
+
617
666
  try:
618
- result = self.read(key)
667
+ result = self.read(item["key"], item["environment"])
619
668
  results.append(result)
620
669
  except ConfigurationNotFoundError as e:
621
- self.logger.warning(f"Bulk read: key '{key}' not found")
622
- errors.append({"key": key, "error": str(e)})
670
+ self.logger.warning(
671
+ f"Bulk read: key '{item['key']}' not found in '{item['environment']}'"
672
+ )
673
+ errors.append({
674
+ "key": item["key"],
675
+ "environment": item["environment"],
676
+ "error": str(e)
677
+ })
623
678
  if not ignore_errors:
624
679
  raise
625
680
  except Exception as e:
626
- self.logger.error(f"Bulk read: failed to read '{key}': {str(e)}")
627
- errors.append({"key": key, "error": str(e)})
681
+ self.logger.error(
682
+ f"Bulk read: failed to read '{item['key']}' from '{item['environment']}': {str(e)}"
683
+ )
684
+ errors.append({
685
+ "key": item["key"],
686
+ "environment": item["environment"],
687
+ "error": str(e)
688
+ })
628
689
  if not ignore_errors:
629
690
  raise
630
691
 
@@ -632,37 +693,61 @@ class OpenSecureConfClient:
632
693
 
633
694
  def bulk_delete(
634
695
  self,
635
- keys: List[str],
696
+ items: List[Dict[str, str]],
636
697
  ignore_errors: bool = False
637
698
  ) -> Dict[str, Any]:
638
699
  """
639
700
  Delete multiple configurations in batch.
640
701
 
641
702
  Args:
642
- keys: List of configuration keys to delete
703
+ items: List of dictionaries with 'key' and 'environment' fields
643
704
  ignore_errors: If True, continue on errors
644
705
 
645
706
  Returns:
646
- Dictionary with summary: {"deleted": [...], "failed": [...]}
707
+ Dictionary with summary: {
708
+ "deleted": [{"key": "...", "environment": "..."}],
709
+ "failed": [{"key": "...", "environment": "...", "error": "..."}]
710
+ }
711
+
712
+ Raises:
713
+ ValueError: If items format is invalid
647
714
 
648
715
  Example:
649
- >>> result = client.bulk_delete(["temp1", "temp2", "temp3"])
716
+ >>> items = [
717
+ ... {"key": "temp1", "environment": "staging"},
718
+ ... {"key": "temp2", "environment": "staging"},
719
+ ... {"key": "temp3", "environment": "development"}
720
+ ... ]
721
+ >>> result = client.bulk_delete(items)
650
722
  >>> print(f"Deleted: {len(result['deleted'])}, Failed: {len(result['failed'])}")
651
723
  """
652
- if not isinstance(keys, list):
653
- raise ValueError("keys must be a list")
724
+ if not isinstance(items, list):
725
+ raise ValueError("items must be a list")
654
726
 
655
727
  deleted = []
656
728
  failed = []
657
729
 
658
- for key in keys:
730
+ for i, item in enumerate(items):
731
+ if not isinstance(item, dict):
732
+ raise ValueError(f"Item at index {i} must be a dictionary")
733
+ if "key" not in item or "environment" not in item:
734
+ raise ValueError(f"Item at index {i} missing required 'key' or 'environment'")
735
+
659
736
  try:
660
- self.delete(key)
661
- deleted.append(key)
662
- self.logger.info(f"Bulk delete: deleted '{key}'")
737
+ self.delete(item["key"], item["environment"])
738
+ deleted.append({"key": item["key"], "environment": item["environment"]})
739
+ self.logger.info(
740
+ f"Bulk delete: deleted '{item['key']}' from '{item['environment']}'"
741
+ )
663
742
  except Exception as e:
664
- self.logger.error(f"Bulk delete: failed to delete '{key}': {str(e)}")
665
- failed.append({"key": key, "error": str(e)})
743
+ self.logger.error(
744
+ f"Bulk delete: failed to delete '{item['key']}' from '{item['environment']}': {str(e)}"
745
+ )
746
+ failed.append({
747
+ "key": item["key"],
748
+ "environment": item["environment"],
749
+ "error": str(e)
750
+ })
666
751
  if not ignore_errors:
667
752
  raise
668
753
 
@@ -672,22 +757,25 @@ class OpenSecureConfClient:
672
757
  # UTILITY METHODS
673
758
  # ========================================================================
674
759
 
675
- def exists(self, key: str) -> bool:
760
+ def exists(self, key: str, environment: str) -> bool:
676
761
  """
677
- Check if a configuration key exists.
762
+ Check if a configuration key exists in specific environment.
678
763
 
679
764
  Args:
680
765
  key: Configuration key to check
766
+ environment: Environment identifier (REQUIRED)
681
767
 
682
768
  Returns:
683
- True if key exists, False otherwise
769
+ True if key exists in the specified environment, False otherwise
684
770
 
685
771
  Example:
686
- >>> if client.exists("database"):
687
- ... print("Configuration exists")
772
+ >>> if client.exists("database", "production"):
773
+ ... print("Configuration exists in production")
774
+ >>> if not client.exists("database", "development"):
775
+ ... print("Configuration does not exist in development")
688
776
  """
689
777
  try:
690
- self.read(key)
778
+ self.read(key, environment)
691
779
  return True
692
780
  except ConfigurationNotFoundError:
693
781
  return False
@@ -695,6 +783,7 @@ class OpenSecureConfClient:
695
783
  def get_or_default(
696
784
  self,
697
785
  key: str,
786
+ environment: str,
698
787
  default: Union[Dict[str, Any], str, int, bool, list]
699
788
  ) -> Dict[str, Any]:
700
789
  """
@@ -702,6 +791,7 @@ class OpenSecureConfClient:
702
791
 
703
792
  Args:
704
793
  key: Configuration key to retrieve
794
+ environment: Environment identifier (REQUIRED)
705
795
  default: Default value to return if key not found (any supported type)
706
796
 
707
797
  Returns:
@@ -709,14 +799,21 @@ class OpenSecureConfClient:
709
799
 
710
800
  Example:
711
801
  >>> # Dict default
712
- >>> config = client.get_or_default("database", {"host": "localhost", "port": 5432})
802
+ >>> config = client.get_or_default(
803
+ ... "database", "production", {"host": "localhost", "port": 5432}
804
+ ... )
713
805
  >>> # String default
714
- >>> config = client.get_or_default("token", "default-token")
806
+ >>> config = client.get_or_default("token", "staging", "default-token")
715
807
  """
716
808
  try:
717
- return self.read(key)
809
+ return self.read(key, environment)
718
810
  except ConfigurationNotFoundError:
719
- return {"key": key, "value": default, "category": None, "environment": None}
811
+ return {
812
+ "key": key,
813
+ "value": default,
814
+ "environment": environment,
815
+ "category": None
816
+ }
720
817
 
721
818
  def count(self, category: Optional[str] = None, environment: Optional[str] = None) -> int:
722
819
  """
@@ -766,7 +863,7 @@ class OpenSecureConfClient:
766
863
  Example:
767
864
  >>> environments = client.list_environments()
768
865
  >>> print(f"Environments: {', '.join(environments)}")
769
- Environments: production, staging, development
866
+ Environments: development, production, staging
770
867
  """
771
868
  configs = self.list_all()
772
869
  environments = set()
@@ -1,6 +0,0 @@
1
- opensecureconf_client.py,sha256=YReSxnFPu_7CDhqjBxcOTrJxYaWMTihiyECnWVmVgCw,30028
2
- opensecureconf_client-2.3.1.dist-info/licenses/LICENSE,sha256=mvMdzinneV_-L01ddrHOBgbutNS8tjT1m7loT7VTWbI,1073
3
- opensecureconf_client-2.3.1.dist-info/METADATA,sha256=tYJjAbvyIAKchDQ2BUQv4KyrIv-whJCAZpxPDkYGr78,33865
4
- opensecureconf_client-2.3.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
5
- opensecureconf_client-2.3.1.dist-info/top_level.txt,sha256=J7NP3hD92OUdqseJLlbzgPuG_ovqkURRyw7iBJJeDVE,22
6
- opensecureconf_client-2.3.1.dist-info/RECORD,,