opensecureconf-client 1.0.2__py3-none-any.whl → 2.0.2__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,1289 @@
1
+ Metadata-Version: 2.4
2
+ Name: opensecureconf-client
3
+ Version: 2.0.2
4
+ Summary: Python client library for OpenSecureConf encrypted configuration management API with clustering support
5
+ Author-email: Alessandro Pioli <alessandro.pioli+apioli-pypi@gmail.com>
6
+ Maintainer-email: Alessandro Pioli <alessandro.pioli+apioli-pypi@gmail.com>
7
+ License: MIT
8
+ Project-URL: Homepage, https://github.com/lordraw77/OpenSecureConf
9
+ Project-URL: Documentation, https://github.com/lordraw77/OpenSecureConf/blob/main/client/README.md
10
+ Project-URL: Repository, https://github.com/lordraw77/OpenSecureConf/tree/main/client
11
+ Project-URL: Issues, https://github.com/lordraw77/OpenSecureConf/issues
12
+ Keywords: configuration,encryption,api-client,secure,config-management,clustering,distributed-config,secrets-management
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
24
+ Classifier: Topic :: Security :: Cryptography
25
+ Classifier: Topic :: System :: Distributed Computing
26
+ Requires-Python: >=3.8
27
+ Description-Content-Type: text/markdown
28
+ License-File: LICENSE
29
+ Requires-Dist: requests>=2.28.0
30
+ Requires-Dist: urllib3>=1.26.0
31
+ Provides-Extra: dev
32
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
33
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
34
+ Requires-Dist: black>=23.0.0; extra == "dev"
35
+ Requires-Dist: flake8>=6.0.0; extra == "dev"
36
+ Requires-Dist: mypy>=1.0.0; extra == "dev"
37
+ Requires-Dist: pylint>=2.17.0; extra == "dev"
38
+ Requires-Dist: isort>=5.12.0; extra == "dev"
39
+ Provides-Extra: enhanced
40
+ Requires-Dist: urllib3>=2.0.0; extra == "enhanced"
41
+ Dynamic: license-file
42
+
43
+ # OpenSecureConf Python Client
44
+
45
+ [![PyPI version](https://badge.fury.io/py/opensecureconf-client.svg)](https://badge.fury.io/py/opensecureconf-client)
46
+ [![Python](https://img.shields.io/pypi/pyversions/opensecureconf-client.svg)](https://pypi.org/project/opensecureconf-client/)
47
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
48
+
49
+ A powerful Python client library for interacting with the [OpenSecureConf API](https://github.com/lordraw77/OpenSecureConf), providing encrypted configuration management with clustering support, automatic retry logic, and comprehensive monitoring capabilities.
50
+
51
+ ## 📋 Table of Contents
52
+
53
+ - [Features](#features)
54
+ - [Installation](#installation)
55
+ - [Quick Start](#quick-start)
56
+ - [Core Features](#core-features)
57
+ - [Basic CRUD Operations](#basic-crud-operations)
58
+ - [Cluster Awareness](#cluster-awareness)
59
+ - [Batch Operations](#batch-operations)
60
+ - [Retry Logic](#retry-logic)
61
+ - [Health Checks](#health-checks)
62
+ - [Utility Methods](#utility-methods)
63
+ - [Advanced Usage](#advanced-usage)
64
+ - [API Reference](#api-reference)
65
+ - [Error Handling](#error-handling)
66
+ - [Configuration Options](#configuration-options)
67
+ - [Best Practices](#best-practices)
68
+ - [Development](#development)
69
+ - [Contributing](#contributing)
70
+ - [License](#license)
71
+
72
+ ## ✨ Features
73
+
74
+ ### Core Capabilities
75
+ - 🔐 **Encrypted Configuration Management**: Securely store and retrieve encrypted configurations using PBKDF2 + Fernet
76
+ - 🚀 **Simple & Intuitive API**: Clean interface for CRUD operations
77
+ - 🛡️ **Type-Safe**: Fully typed with comprehensive error handling
78
+ - 📦 **Lightweight**: Minimal dependencies (only `requests` and `urllib3`)
79
+
80
+ ### Enhanced Features
81
+ - 🔄 **Automatic Retry Logic**: Exponential backoff for transient failures
82
+ - 🌐 **Cluster Awareness**: Monitor and interact with clustered deployments
83
+ - ⚡ **Connection Pooling**: Optimized HTTP connection management
84
+ - 📊 **Structured Logging**: Built-in logging for debugging and monitoring
85
+ - 🔢 **Batch Operations**: Efficient bulk create, read, and delete operations
86
+ - 🏥 **Health Checks**: Built-in connectivity and cluster health monitoring
87
+ - 🎯 **Utility Methods**: Convenient helpers for common operations
88
+ - 🔌 **Context Manager**: Automatic resource cleanup
89
+
90
+ ## 📦 Installation
91
+
92
+ ### Standard Installation
93
+
94
+ ```bash
95
+ pip install opensecureconf-client
96
+ ```
97
+
98
+ ### Development Installation
99
+
100
+ ```bash
101
+ pip install opensecureconf-client[dev]
102
+ ```
103
+
104
+ ### From Source
105
+
106
+ ```bash
107
+ git clone https://github.com/lordraw77/OpenSecureConf.git
108
+ cd OpenSecureConf/client
109
+ pip install -e .
110
+ ```
111
+
112
+ ## 🚀 Quick Start
113
+
114
+ ### Basic Usage
115
+
116
+ ```python
117
+ from opensecureconf_client import OpenSecureConfClient
118
+
119
+ # Initialize the client
120
+ client = OpenSecureConfClient(
121
+ base_url="http://localhost:9000",
122
+ user_key="my-secure-key-min-8-chars",
123
+ api_key="cluster-secret-key-123" # Optional: if API key authentication is enabled
124
+ )
125
+
126
+ # Create a configuration
127
+ config = client.create(
128
+ key="database",
129
+ value={"host": "localhost", "port": 5432, "username": "admin", "password": "secret"},
130
+ category="production"
131
+ )
132
+ print(f"Created: {config['key']}")
133
+
134
+ # Read a configuration
135
+ db_config = client.read("database")
136
+ print(f"Host: {db_config['value']['host']}")
137
+
138
+ # Update a configuration
139
+ updated = client.update(
140
+ key="database",
141
+ value={"host": "db.example.com", "port": 5432, "username": "admin", "password": "secret"}
142
+ )
143
+
144
+ # List all configurations
145
+ configs = client.list_all(category="production")
146
+ for cfg in configs:
147
+ print(f"- {cfg['key']}: {cfg['category']}")
148
+
149
+ # Delete a configuration
150
+ client.delete("database")
151
+
152
+ # Close the client
153
+ client.close()
154
+ ```
155
+
156
+ ### Using Context Manager (Recommended)
157
+
158
+ ```python
159
+ from opensecureconf_client import OpenSecureConfClient
160
+
161
+ with OpenSecureConfClient(
162
+ base_url="http://localhost:9000",
163
+ user_key="my-secure-key-min-8-chars"
164
+ ) as client:
165
+ # Create and use configurations
166
+ config = client.create("app", {"version": "1.0.0", "debug": False})
167
+ print(config)
168
+ # Session automatically closed when exiting context
169
+ ```
170
+
171
+ ### Enhanced Client with All Features
172
+
173
+ ```python
174
+ from opensecureconf_client_enhanced import OpenSecureConfClient
175
+
176
+ # Initialize with advanced features
177
+ client = OpenSecureConfClient(
178
+ base_url="http://localhost:9000",
179
+ user_key="my-secure-key-min-8-chars",
180
+ api_key="cluster-secret-key-123",
181
+ enable_retry=True, # Enable automatic retry
182
+ max_retries=3, # Max retry attempts
183
+ backoff_factor=1.0, # Exponential backoff factor
184
+ pool_connections=20, # Connection pool size
185
+ pool_maxsize=50, # Max pool size
186
+ log_level="INFO" # Logging level
187
+ )
188
+
189
+ # Use all the enhanced features...
190
+ ```
191
+
192
+ ## 🎯 Core Features
193
+
194
+ ### Basic CRUD Operations
195
+
196
+ #### Create Configuration
197
+
198
+ ```python
199
+ # Create a simple configuration
200
+ config = client.create(
201
+ key="api_settings",
202
+ value={"base_url": "https://api.example.com", "timeout": 30, "retries": 3},
203
+ category="production"
204
+ )
205
+
206
+ # Create with validation
207
+ if not client.exists("api_settings"):
208
+ config = client.create("api_settings", {"base_url": "https://api.example.com"})
209
+ ```
210
+
211
+ #### Read Configuration
212
+
213
+ ```python
214
+ # Read a specific configuration
215
+ config = client.read("api_settings")
216
+ print(f"API URL: {config['value']['base_url']}")
217
+
218
+ # Safe read with default value
219
+ config = client.get_or_default(
220
+ "optional_setting",
221
+ default={"enabled": False, "timeout": 30}
222
+ )
223
+ ```
224
+
225
+ #### Update Configuration
226
+
227
+ ```python
228
+ # Update existing configuration
229
+ updated = client.update(
230
+ key="api_settings",
231
+ value={"base_url": "https://api.example.com", "timeout": 60, "retries": 5},
232
+ category="production"
233
+ )
234
+ ```
235
+
236
+ #### Delete Configuration
237
+
238
+ ```python
239
+ # Delete a single configuration
240
+ result = client.delete("api_settings")
241
+ print(result["message"])
242
+
243
+ # Conditional delete
244
+ if client.exists("temporary_config"):
245
+ client.delete("temporary_config")
246
+ ```
247
+
248
+ #### List Configurations
249
+
250
+ ```python
251
+ # List all configurations
252
+ all_configs = client.list_all()
253
+ print(f"Total configurations: {len(all_configs)}")
254
+
255
+ # List by category
256
+ prod_configs = client.list_all(category="production")
257
+ for config in prod_configs:
258
+ print(f"- {config['key']}: {config['value']}")
259
+
260
+ # Get count
261
+ total = client.count()
262
+ prod_count = client.count(category="production")
263
+ print(f"Production configs: {prod_count}/{total}")
264
+ ```
265
+
266
+ ### Cluster Awareness
267
+
268
+ Monitor and interact with OpenSecureConf clusters:
269
+
270
+ ```python
271
+ # Check cluster status
272
+ status = client.get_cluster_status()
273
+ if status['enabled']:
274
+ print(f"Cluster Mode: {status['mode']}") # REPLICA or FEDERATED
275
+ print(f"Node ID: {status['node_id']}")
276
+ print(f"Healthy Nodes: {status['healthy_nodes']}/{status['total_nodes']}")
277
+ else:
278
+ print("Clustering is disabled")
279
+
280
+ # Check node health
281
+ health = client.get_cluster_health()
282
+ print(f"Node Status: {health['status']}")
283
+ ```
284
+
285
+ **Cluster Modes:**
286
+ - **REPLICA**: Active-active replication with automatic synchronization
287
+ - **FEDERATED**: Distributed storage with cross-node queries
288
+
289
+ ### Batch Operations
290
+
291
+ Perform multiple operations efficiently:
292
+
293
+ #### Bulk Create
294
+
295
+ ```python
296
+ # Create multiple configurations at once
297
+ configs_to_create = [
298
+ {
299
+ "key": "service1",
300
+ "value": {"url": "http://service1.local", "timeout": 30},
301
+ "category": "microservices"
302
+ },
303
+ {
304
+ "key": "service2",
305
+ "value": {"url": "http://service2.local", "timeout": 60},
306
+ "category": "microservices"
307
+ },
308
+ {
309
+ "key": "service3",
310
+ "value": {"url": "http://service3.local", "timeout": 45},
311
+ "category": "microservices"
312
+ }
313
+ ]
314
+
315
+ # Create all configurations (stop on first error)
316
+ results = client.bulk_create(configs_to_create)
317
+ print(f"Created {len(results)} configurations")
318
+
319
+ # Create all configurations (continue on errors)
320
+ results = client.bulk_create(configs_to_create, ignore_errors=True)
321
+ print(f"Created {len(results)} configurations")
322
+ ```
323
+
324
+ #### Bulk Read
325
+
326
+ ```python
327
+ # Read multiple configurations at once
328
+ keys = ["service1", "service2", "service3", "api_settings"]
329
+
330
+ # Read all (stop on first error)
331
+ configs = client.bulk_read(keys)
332
+
333
+ # Read all (skip missing keys)
334
+ configs = client.bulk_read(keys, ignore_errors=True)
335
+ for config in configs:
336
+ print(f"{config['key']}: {config['value']['url']}")
337
+ ```
338
+
339
+ #### Bulk Delete
340
+
341
+ ```python
342
+ # Delete multiple configurations
343
+ keys_to_delete = ["temp1", "temp2", "temp3"]
344
+
345
+ # Delete all (stop on first error)
346
+ result = client.bulk_delete(keys_to_delete)
347
+
348
+ # Delete all (continue on errors)
349
+ result = client.bulk_delete(keys_to_delete, ignore_errors=True)
350
+ print(f"Deleted: {len(result['deleted'])}")
351
+ print(f"Failed: {len(result['failed'])}")
352
+
353
+ # Inspect failures
354
+ for failure in result['failed']:
355
+ print(f"Failed to delete '{failure['key']}': {failure['error']}")
356
+ ```
357
+
358
+ ### Retry Logic
359
+
360
+ The enhanced client includes automatic retry with exponential backoff:
361
+
362
+ ```python
363
+ # Configure retry behavior
364
+ client = OpenSecureConfClient(
365
+ base_url="http://localhost:9000",
366
+ user_key="my-key",
367
+ enable_retry=True,
368
+ max_retries=5, # Retry up to 5 times
369
+ backoff_factor=2.0, # 2^n seconds between retries
370
+ timeout=30
371
+ )
372
+
373
+ # Automatic retry on transient failures (429, 500, 502, 503, 504)
374
+ try:
375
+ config = client.read("my_config")
376
+ except Exception as e:
377
+ print(f"Failed after {client.max_retries} retries: {e}")
378
+ ```
379
+
380
+ **Retry Strategy:**
381
+ - Status codes: `429, 500, 502, 503, 504`
382
+ - HTTP methods: All methods including POST
383
+ - Backoff: `backoff_factor * (2 ^ retry_count)` seconds
384
+
385
+ ### Health Checks
386
+
387
+ Built-in health monitoring:
388
+
389
+ ```python
390
+ # Simple ping check
391
+ if client.ping():
392
+ print("✓ Server is healthy and reachable")
393
+ else:
394
+ print("✗ Server is not reachable")
395
+
396
+ # Get detailed service information
397
+ info = client.get_service_info()
398
+ print(f"Service: {info['service']}")
399
+ print(f"Version: {info['version']}")
400
+ print(f"Features: {', '.join(info['features'])}")
401
+ print(f"Cluster Enabled: {info['cluster_enabled']}")
402
+
403
+ # Monitor cluster health
404
+ if info['cluster_enabled']:
405
+ health = client.get_cluster_health()
406
+ print(f"Cluster Health: {health['status']}")
407
+ ```
408
+
409
+ ### Utility Methods
410
+
411
+ Convenient helper methods:
412
+
413
+ #### Check Existence
414
+
415
+ ```python
416
+ # Check if a key exists
417
+ if client.exists("database"):
418
+ print("Configuration exists")
419
+ config = client.read("database")
420
+ else:
421
+ print("Configuration does not exist")
422
+ config = client.create("database", {"host": "localhost"})
423
+ ```
424
+
425
+ #### Get with Default
426
+
427
+ ```python
428
+ # Get configuration or return default
429
+ config = client.get_or_default(
430
+ "optional_feature",
431
+ default={"enabled": False, "timeout": 30}
432
+ )
433
+
434
+ # Use the configuration
435
+ if config['value']['enabled']:
436
+ timeout = config['value']['timeout']
437
+ ```
438
+
439
+ #### Count Configurations
440
+
441
+ ```python
442
+ # Count all configurations
443
+ total = client.count()
444
+ print(f"Total configurations: {total}")
445
+
446
+ # Count by category
447
+ prod_count = client.count(category="production")
448
+ dev_count = client.count(category="development")
449
+ print(f"Production: {prod_count}, Development: {dev_count}")
450
+ ```
451
+
452
+ #### List Categories
453
+
454
+ ```python
455
+ # Get all unique categories
456
+ categories = client.list_categories()
457
+ print(f"Available categories: {', '.join(categories)}")
458
+
459
+ # Process by category
460
+ for category in categories:
461
+ count = client.count(category=category)
462
+ print(f"{category}: {count} configurations")
463
+ ```
464
+
465
+ ## 🔧 Advanced Usage
466
+
467
+ ### Custom Connection Pooling
468
+
469
+ ```python
470
+ from opensecureconf_client_enhanced import OpenSecureConfClient
471
+
472
+ # Configure connection pooling for high-traffic scenarios
473
+ client = OpenSecureConfClient(
474
+ base_url="http://localhost:9000",
475
+ user_key="my-key",
476
+ pool_connections=50, # Number of connection pools
477
+ pool_maxsize=100, # Maximum pool size
478
+ timeout=60 # Request timeout
479
+ )
480
+ ```
481
+
482
+ ### Structured Logging
483
+
484
+ ```python
485
+ import logging
486
+
487
+ # Enable detailed logging
488
+ logging.basicConfig(
489
+ level=logging.DEBUG,
490
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
491
+ )
492
+
493
+ client = OpenSecureConfClient(
494
+ base_url="http://localhost:9000",
495
+ user_key="my-key",
496
+ log_level="DEBUG" # DEBUG, INFO, WARNING, ERROR
497
+ )
498
+
499
+ # All operations will be logged
500
+ config = client.create("test", {"data": "value"})
501
+ # Output: 2026-01-14 17:00:00 - opensecureconf_client - INFO - POST /configs - Status: 201 - Duration: 0.045s
502
+ ```
503
+
504
+ ### SSL Configuration
505
+
506
+ ```python
507
+ # Disable SSL verification (not recommended for production)
508
+ client = OpenSecureConfClient(
509
+ base_url="https://localhost:9000",
510
+ user_key="my-key",
511
+ verify_ssl=False
512
+ )
513
+
514
+ # Use custom SSL certificate
515
+ import requests
516
+ session = requests.Session()
517
+ session.verify = '/path/to/ca-bundle.crt'
518
+
519
+ client = OpenSecureConfClient(
520
+ base_url="https://localhost:9000",
521
+ user_key="my-key",
522
+ verify_ssl=True
523
+ )
524
+ client._session = session
525
+ ```
526
+
527
+ ### Environment-Based Configuration
528
+
529
+ ```python
530
+ import os
531
+ from opensecureconf_client_enhanced import OpenSecureConfClient
532
+
533
+ # Load from environment variables
534
+ client = OpenSecureConfClient(
535
+ base_url=os.getenv("OSC_URL", "http://localhost:9000"),
536
+ user_key=os.getenv("OSC_USER_KEY"),
537
+ api_key=os.getenv("OSC_API_KEY"),
538
+ enable_retry=os.getenv("OSC_RETRY", "true").lower() == "true",
539
+ max_retries=int(os.getenv("OSC_MAX_RETRIES", "3")),
540
+ log_level=os.getenv("OSC_LOG_LEVEL", "INFO")
541
+ )
542
+ ```
543
+
544
+ ### Working with Clusters
545
+
546
+ ```python
547
+ from opensecureconf_client_enhanced import OpenSecureConfClient
548
+
549
+ # Connect to any node in the cluster
550
+ client = OpenSecureConfClient(
551
+ base_url="http://node1.example.com:9000", # Can be any node
552
+ user_key="my-key",
553
+ api_key="cluster-secret-key"
554
+ )
555
+
556
+ # Check cluster topology
557
+ status = client.get_cluster_status()
558
+ print(f"Connected to: {status['node_id']}")
559
+ print(f"Cluster mode: {status['mode']}")
560
+
561
+ # In REPLICA mode: writes are automatically replicated
562
+ config = client.create("shared_config", {"data": "value"})
563
+ # This configuration is now available on all nodes
564
+
565
+ # In FEDERATED mode: reads check all nodes
566
+ config = client.read("distributed_config")
567
+ # This searches across all federated nodes
568
+
569
+ # Monitor cluster health
570
+ if status['healthy_nodes'] < status['total_nodes']:
571
+ print(f"Warning: {status['total_nodes'] - status['healthy_nodes']} nodes are down")
572
+ ```
573
+
574
+ ## 📚 API Reference
575
+
576
+ ### OpenSecureConfClient
577
+
578
+ Main client class for interacting with OpenSecureConf API.
579
+
580
+ #### Constructor
581
+
582
+ ```python
583
+ OpenSecureConfClient(
584
+ base_url: str,
585
+ user_key: str,
586
+ api_key: Optional[str] = None,
587
+ timeout: int = 30,
588
+ verify_ssl: bool = True,
589
+ enable_retry: bool = True,
590
+ max_retries: int = 3,
591
+ backoff_factor: float = 1.0,
592
+ pool_connections: int = 10,
593
+ pool_maxsize: int = 20,
594
+ log_level: str = "WARNING"
595
+ )
596
+ ```
597
+
598
+ **Parameters:**
599
+
600
+ | Parameter | Type | Default | Description |
601
+ |-----------|------|---------|-------------|
602
+ | `base_url` | `str` | Required | Base URL of the OpenSecureConf API server |
603
+ | `user_key` | `str` | Required | User encryption key (minimum 8 characters) |
604
+ | `api_key` | `Optional[str]` | `None` | API key for authentication (if enabled on server) |
605
+ | `timeout` | `int` | `30` | Request timeout in seconds |
606
+ | `verify_ssl` | `bool` | `True` | Verify SSL certificates |
607
+ | `enable_retry` | `bool` | `True` | Enable automatic retry with exponential backoff |
608
+ | `max_retries` | `int` | `3` | Maximum number of retry attempts |
609
+ | `backoff_factor` | `float` | `1.0` | Exponential backoff multiplier |
610
+ | `pool_connections` | `int` | `10` | Number of connection pools to cache |
611
+ | `pool_maxsize` | `int` | `20` | Maximum size of the connection pool |
612
+ | `log_level` | `str` | `"WARNING"` | Logging level (DEBUG, INFO, WARNING, ERROR) |
613
+
614
+ #### Methods
615
+
616
+ ##### Health & Status
617
+
618
+ ###### `ping() -> bool`
619
+
620
+ Check if the API server is reachable.
621
+
622
+ **Returns:** `True` if server is healthy, `False` otherwise
623
+
624
+ **Example:**
625
+ ```python
626
+ if client.ping():
627
+ print("Server is online")
628
+ ```
629
+
630
+ ###### `get_service_info() -> Dict[str, Any]`
631
+
632
+ Get information about the OpenSecureConf service.
633
+
634
+ **Returns:** Dictionary with service metadata
635
+ - `service`: Service name
636
+ - `version`: API version
637
+ - `features`: List of enabled features
638
+ - `cluster_enabled`: Whether clustering is enabled
639
+ - `cluster_mode`: Cluster mode (if enabled)
640
+
641
+ **Example:**
642
+ ```python
643
+ info = client.get_service_info()
644
+ print(f"Version: {info['version']}")
645
+ ```
646
+
647
+ ##### Cluster Operations
648
+
649
+ ###### `get_cluster_status() -> Dict[str, Any]`
650
+
651
+ Get cluster status and node information.
652
+
653
+ **Returns:** Dictionary with cluster status
654
+ - `enabled`: Whether clustering is enabled
655
+ - `mode`: Cluster mode (replica or federated)
656
+ - `node_id`: Current node identifier
657
+ - `total_nodes`: Total number of nodes
658
+ - `healthy_nodes`: Number of healthy nodes
659
+
660
+ **Raises:** `ClusterError` if cluster status cannot be retrieved
661
+
662
+ **Example:**
663
+ ```python
664
+ status = client.get_cluster_status()
665
+ print(f"Healthy: {status['healthy_nodes']}/{status['total_nodes']}")
666
+ ```
667
+
668
+ ###### `get_cluster_health() -> Dict[str, Any]`
669
+
670
+ Check cluster node health.
671
+
672
+ **Returns:** Dictionary with health status
673
+
674
+ **Example:**
675
+ ```python
676
+ health = client.get_cluster_health()
677
+ print(f"Status: {health['status']}")
678
+ ```
679
+
680
+ ##### Configuration CRUD
681
+
682
+ ###### `create(key: str, value: Dict[str, Any], category: Optional[str] = None) -> Dict[str, Any]`
683
+
684
+ Create a new encrypted configuration entry.
685
+
686
+ **Parameters:**
687
+ - `key`: Unique configuration key (1-255 characters)
688
+ - `value`: Configuration data as dictionary
689
+ - `category`: Optional category for grouping (max 100 characters)
690
+
691
+ **Returns:** Dictionary with created configuration
692
+
693
+ **Raises:**
694
+ - `ConfigurationExistsError`: If key already exists
695
+ - `ValueError`: If parameters are invalid
696
+
697
+ **Example:**
698
+ ```python
699
+ config = client.create(
700
+ "database",
701
+ {"host": "localhost", "port": 5432},
702
+ category="production"
703
+ )
704
+ ```
705
+
706
+ ###### `read(key: str) -> Dict[str, Any]`
707
+
708
+ Read and decrypt a configuration entry.
709
+
710
+ **Parameters:**
711
+ - `key`: Configuration key to retrieve
712
+
713
+ **Returns:** Dictionary with configuration
714
+
715
+ **Raises:**
716
+ - `ConfigurationNotFoundError`: If key does not exist
717
+ - `ValueError`: If key is invalid
718
+
719
+ **Example:**
720
+ ```python
721
+ config = client.read("database")
722
+ print(config['value'])
723
+ ```
724
+
725
+ ###### `update(key: str, value: Dict[str, Any], category: Optional[str] = None) -> Dict[str, Any]`
726
+
727
+ Update an existing configuration entry.
728
+
729
+ **Parameters:**
730
+ - `key`: Configuration key to update
731
+ - `value`: New configuration data
732
+ - `category`: Optional new category
733
+
734
+ **Returns:** Dictionary with updated configuration
735
+
736
+ **Raises:**
737
+ - `ConfigurationNotFoundError`: If key does not exist
738
+ - `ValueError`: If parameters are invalid
739
+
740
+ **Example:**
741
+ ```python
742
+ updated = client.update("database", {"host": "db.example.com", "port": 5432})
743
+ ```
744
+
745
+ ###### `delete(key: str) -> Dict[str, str]`
746
+
747
+ Delete a configuration entry permanently.
748
+
749
+ **Parameters:**
750
+ - `key`: Configuration key to delete
751
+
752
+ **Returns:** Dictionary with success message
753
+
754
+ **Raises:**
755
+ - `ConfigurationNotFoundError`: If key does not exist
756
+ - `ValueError`: If key is invalid
757
+
758
+ **Example:**
759
+ ```python
760
+ result = client.delete("database")
761
+ print(result['message'])
762
+ ```
763
+
764
+ ###### `list_all(category: Optional[str] = None) -> List[Dict[str, Any]]`
765
+
766
+ List all configurations with optional category filter.
767
+
768
+ **Parameters:**
769
+ - `category`: Optional category filter
770
+
771
+ **Returns:** List of configuration dictionaries
772
+
773
+ **Example:**
774
+ ```python
775
+ configs = client.list_all(category="production")
776
+ ```
777
+
778
+ ##### Batch Operations
779
+
780
+ ###### `bulk_create(configs: List[Dict[str, Any]], ignore_errors: bool = False) -> List[Dict[str, Any]]`
781
+
782
+ Create multiple configurations in batch.
783
+
784
+ **Parameters:**
785
+ - `configs`: List of configuration dictionaries
786
+ - `ignore_errors`: Continue on errors and return partial results
787
+
788
+ **Returns:** List of created configurations
789
+
790
+ **Raises:**
791
+ - `ValueError`: If configs format is invalid
792
+ - `OpenSecureConfError`: If creation fails and ignore_errors is False
793
+
794
+ **Example:**
795
+ ```python
796
+ configs = [
797
+ {"key": "db1", "value": {"host": "localhost"}, "category": "prod"},
798
+ {"key": "db2", "value": {"host": "remote"}, "category": "prod"}
799
+ ]
800
+ results = client.bulk_create(configs)
801
+ ```
802
+
803
+ ###### `bulk_read(keys: List[str], ignore_errors: bool = False) -> List[Dict[str, Any]]`
804
+
805
+ Read multiple configurations in batch.
806
+
807
+ **Parameters:**
808
+ - `keys`: List of configuration keys
809
+ - `ignore_errors`: Skip missing keys and return partial results
810
+
811
+ **Returns:** List of configuration dictionaries
812
+
813
+ **Example:**
814
+ ```python
815
+ configs = client.bulk_read(["db1", "db2", "api"])
816
+ ```
817
+
818
+ ###### `bulk_delete(keys: List[str], ignore_errors: bool = False) -> Dict[str, Any]`
819
+
820
+ Delete multiple configurations in batch.
821
+
822
+ **Parameters:**
823
+ - `keys`: List of configuration keys
824
+ - `ignore_errors`: Continue on errors
825
+
826
+ **Returns:** Dictionary with summary: `{"deleted": [...], "failed": [...]}`
827
+
828
+ **Example:**
829
+ ```python
830
+ result = client.bulk_delete(["temp1", "temp2", "temp3"])
831
+ print(f"Deleted: {len(result['deleted'])}")
832
+ ```
833
+
834
+ ##### Utility Methods
835
+
836
+ ###### `exists(key: str) -> bool`
837
+
838
+ Check if a configuration key exists.
839
+
840
+ **Parameters:**
841
+ - `key`: Configuration key to check
842
+
843
+ **Returns:** `True` if key exists, `False` otherwise
844
+
845
+ **Example:**
846
+ ```python
847
+ if client.exists("database"):
848
+ config = client.read("database")
849
+ ```
850
+
851
+ ###### `get_or_default(key: str, default: Dict[str, Any]) -> Dict[str, Any]`
852
+
853
+ Get configuration or return default if not found.
854
+
855
+ **Parameters:**
856
+ - `key`: Configuration key to retrieve
857
+ - `default`: Default value to return if not found
858
+
859
+ **Returns:** Configuration dictionary or default
860
+
861
+ **Example:**
862
+ ```python
863
+ config = client.get_or_default("optional", {"enabled": False})
864
+ ```
865
+
866
+ ###### `count(category: Optional[str] = None) -> int`
867
+
868
+ Count configurations, optionally filtered by category.
869
+
870
+ **Parameters:**
871
+ - `category`: Optional category filter
872
+
873
+ **Returns:** Number of configurations
874
+
875
+ **Example:**
876
+ ```python
877
+ total = client.count()
878
+ prod_count = client.count(category="production")
879
+ ```
880
+
881
+ ###### `list_categories() -> List[str]`
882
+
883
+ Get list of all unique categories.
884
+
885
+ **Returns:** Sorted list of category names
886
+
887
+ **Example:**
888
+ ```python
889
+ categories = client.list_categories()
890
+ print(f"Categories: {', '.join(categories)}")
891
+ ```
892
+
893
+ ##### Session Management
894
+
895
+ ###### `close()`
896
+
897
+ Close the underlying HTTP session.
898
+
899
+ **Example:**
900
+ ```python
901
+ client.close()
902
+ ```
903
+
904
+ ###### Context Manager
905
+
906
+ The client supports context manager protocol for automatic cleanup.
907
+
908
+ **Example:**
909
+ ```python
910
+ with OpenSecureConfClient(base_url="...", user_key="...") as client:
911
+ config = client.create("key", {"value": "data"})
912
+ # Session automatically closed
913
+ ```
914
+
915
+ ## ⚠️ Error Handling
916
+
917
+ ### Exception Hierarchy
918
+
919
+ ```
920
+ OpenSecureConfError (base exception)
921
+ ├── AuthenticationError # Invalid or missing credentials
922
+ ├── ConfigurationNotFoundError # Configuration key does not exist
923
+ ├── ConfigurationExistsError # Configuration key already exists
924
+ └── ClusterError # Cluster operation failed
925
+ ```
926
+
927
+ ### Handling Errors
928
+
929
+ ```python
930
+ from opensecureconf_client import (
931
+ OpenSecureConfClient,
932
+ AuthenticationError,
933
+ ConfigurationNotFoundError,
934
+ ConfigurationExistsError,
935
+ ClusterError,
936
+ OpenSecureConfError
937
+ )
938
+
939
+ try:
940
+ config = client.create("mykey", {"data": "value"})
941
+ except AuthenticationError:
942
+ print("Authentication failed - check your user_key and api_key")
943
+ except ConfigurationExistsError:
944
+ print("Configuration already exists - use update() instead")
945
+ config = client.update("mykey", {"data": "new_value"})
946
+ except ConfigurationNotFoundError:
947
+ print("Configuration not found")
948
+ except ClusterError as e:
949
+ print(f"Cluster error: {e}")
950
+ except OpenSecureConfError as e:
951
+ print(f"API error: {e}")
952
+ except ConnectionError as e:
953
+ print(f"Connection error: {e}")
954
+ ```
955
+
956
+ ### Best Practices for Error Handling
957
+
958
+ ```python
959
+ # 1. Use specific exceptions first
960
+ try:
961
+ config = client.read("important_config")
962
+ except ConfigurationNotFoundError:
963
+ # Create with defaults if not found
964
+ config = client.create("important_config", {"default": "value"})
965
+
966
+ # 2. Use exists() to avoid exceptions
967
+ if not client.exists("optional_config"):
968
+ client.create("optional_config", {"data": "value"})
969
+
970
+ # 3. Use get_or_default() for optional configurations
971
+ config = client.get_or_default("optional", {"enabled": False})
972
+
973
+ # 4. Handle bulk operation failures
974
+ result = client.bulk_delete(keys, ignore_errors=True)
975
+ if result['failed']:
976
+ print(f"Some deletions failed: {result['failed']}")
977
+ ```
978
+
979
+ ## ⚙️ Configuration Options
980
+
981
+ ### Production Configuration
982
+
983
+ ```python
984
+ from opensecureconf_client_enhanced import OpenSecureConfClient
985
+ import os
986
+
987
+ # Production-ready configuration
988
+ client = OpenSecureConfClient(
989
+ base_url=os.getenv("OSC_URL"),
990
+ user_key=os.getenv("OSC_USER_KEY"),
991
+ api_key=os.getenv("OSC_API_KEY"),
992
+ timeout=60, # Longer timeout for production
993
+ verify_ssl=True, # Always verify SSL in production
994
+ enable_retry=True,
995
+ max_retries=5, # More retries for reliability
996
+ backoff_factor=2.0, # Aggressive backoff
997
+ pool_connections=50, # Large pool for high traffic
998
+ pool_maxsize=100,
999
+ log_level="INFO" # Moderate logging
1000
+ )
1001
+ ```
1002
+
1003
+ ### Development Configuration
1004
+
1005
+ ```python
1006
+ # Development-friendly configuration
1007
+ client = OpenSecureConfClient(
1008
+ base_url="http://localhost:9000",
1009
+ user_key="dev-key-12345678",
1010
+ api_key="dev-api-key",
1011
+ timeout=30,
1012
+ verify_ssl=False, # Self-signed certs in dev
1013
+ enable_retry=False, # Fail fast in development
1014
+ pool_connections=5, # Smaller pool
1015
+ pool_maxsize=10,
1016
+ log_level="DEBUG" # Verbose logging for debugging
1017
+ )
1018
+ ```
1019
+
1020
+ ### Environment Variables
1021
+
1022
+ Recommended environment variables:
1023
+
1024
+ ```bash
1025
+ # Required
1026
+ export OSC_URL="http://localhost:9000"
1027
+ export OSC_USER_KEY="your-secure-key-min-8-chars"
1028
+
1029
+ # Optional
1030
+ export OSC_API_KEY="cluster-secret-key-123"
1031
+ export OSC_TIMEOUT="30"
1032
+ export OSC_VERIFY_SSL="true"
1033
+ export OSC_RETRY="true"
1034
+ export OSC_MAX_RETRIES="3"
1035
+ export OSC_BACKOFF_FACTOR="1.0"
1036
+ export OSC_POOL_CONNECTIONS="10"
1037
+ export OSC_POOL_MAXSIZE="20"
1038
+ export OSC_LOG_LEVEL="INFO"
1039
+ ```
1040
+
1041
+ ## 💡 Best Practices
1042
+
1043
+ ### 1. Use Context Managers
1044
+
1045
+ ```python
1046
+ # ✅ Good: Automatic cleanup
1047
+ with OpenSecureConfClient(base_url="...", user_key="...") as client:
1048
+ config = client.create("key", {"data": "value"})
1049
+
1050
+ # ❌ Avoid: Manual cleanup required
1051
+ client = OpenSecureConfClient(base_url="...", user_key="...")
1052
+ config = client.create("key", {"data": "value"})
1053
+ client.close() # Easy to forget
1054
+ ```
1055
+
1056
+ ### 2. Check Existence Before Operations
1057
+
1058
+ ```python
1059
+ # ✅ Good: Avoid exceptions
1060
+ if not client.exists("config"):
1061
+ client.create("config", {"data": "value"})
1062
+
1063
+ # ❌ Avoid: Exception handling for flow control
1064
+ try:
1065
+ client.create("config", {"data": "value"})
1066
+ except ConfigurationExistsError:
1067
+ pass
1068
+ ```
1069
+
1070
+ ### 3. Use Batch Operations for Multiple Items
1071
+
1072
+ ```python
1073
+ # ✅ Good: Single batch operation
1074
+ keys = ["config1", "config2", "config3"]
1075
+ configs = client.bulk_read(keys, ignore_errors=True)
1076
+
1077
+ # ❌ Avoid: Multiple individual requests
1078
+ configs = []
1079
+ for key in keys:
1080
+ try:
1081
+ configs.append(client.read(key))
1082
+ except:
1083
+ pass
1084
+ ```
1085
+
1086
+ ### 4. Enable Retry for Production
1087
+
1088
+ ```python
1089
+ # ✅ Good: Resilient to transient failures
1090
+ client = OpenSecureConfClient(
1091
+ base_url="...",
1092
+ user_key="...",
1093
+ enable_retry=True,
1094
+ max_retries=3
1095
+ )
1096
+
1097
+ # ❌ Avoid: No retry, fails on transient errors
1098
+ client = OpenSecureConfClient(
1099
+ base_url="...",
1100
+ user_key="...",
1101
+ enable_retry=False
1102
+ )
1103
+ ```
1104
+
1105
+ ### 5. Use Structured Logging
1106
+
1107
+ ```python
1108
+ # ✅ Good: Enable logging for production monitoring
1109
+ client = OpenSecureConfClient(
1110
+ base_url="...",
1111
+ user_key="...",
1112
+ log_level="INFO"
1113
+ )
1114
+
1115
+ # ❌ Avoid: Silent failures in production
1116
+ client = OpenSecureConfClient(
1117
+ base_url="...",
1118
+ user_key="...",
1119
+ log_level="ERROR" # Misses important info
1120
+ )
1121
+ ```
1122
+
1123
+ ### 6. Secure Credential Management
1124
+
1125
+ ```python
1126
+ # ✅ Good: Load from environment or secrets manager
1127
+ import os
1128
+ from opensecureconf_client import OpenSecureConfClient
1129
+
1130
+ client = OpenSecureConfClient(
1131
+ base_url=os.getenv("OSC_URL"),
1132
+ user_key=os.getenv("OSC_USER_KEY"),
1133
+ api_key=os.getenv("OSC_API_KEY")
1134
+ )
1135
+
1136
+ # ❌ Avoid: Hardcoded credentials
1137
+ client = OpenSecureConfClient(
1138
+ base_url="http://localhost:9000",
1139
+ user_key="hardcoded-key-12345", # Never do this!
1140
+ api_key="hardcoded-api-key"
1141
+ )
1142
+ ```
1143
+
1144
+ ### 7. Monitor Cluster Health
1145
+
1146
+ ```python
1147
+ # ✅ Good: Regular health checks
1148
+ if client.ping():
1149
+ status = client.get_cluster_status()
1150
+ if status['enabled']:
1151
+ if status['healthy_nodes'] < status['total_nodes']:
1152
+ logger.warning(f"Cluster degraded: {status['healthy_nodes']}/{status['total_nodes']}")
1153
+
1154
+ # ❌ Avoid: No health monitoring
1155
+ config = client.read("config") # Might fail silently in degraded cluster
1156
+ ```
1157
+
1158
+ ## 🔨 Development
1159
+
1160
+ ### Setting Up Development Environment
1161
+
1162
+ ```bash
1163
+ # Clone the repository
1164
+ git clone https://github.com/lordraw77/OpenSecureConf.git
1165
+ cd OpenSecureConf/client
1166
+
1167
+ # Create virtual environment
1168
+ python -m venv venv
1169
+ source venv/bin/activate # On Windows: venv\Scripts\activate
1170
+
1171
+ # Install in development mode with all dependencies
1172
+ pip install -e ".[dev]"
1173
+ ```
1174
+
1175
+ ### Running Tests
1176
+
1177
+ ```bash
1178
+ # Run all tests
1179
+ pytest
1180
+
1181
+ # Run with coverage
1182
+ pytest --cov=opensecureconf_client --cov-report=html
1183
+
1184
+ # Run specific test file
1185
+ pytest tests/test_client.py
1186
+
1187
+ # Run with verbose output
1188
+ pytest -v
1189
+
1190
+ # Run with logging
1191
+ pytest -s
1192
+ ```
1193
+
1194
+ ### Code Quality
1195
+
1196
+ ```bash
1197
+ # Format code
1198
+ black opensecureconf_client.py
1199
+ black tests/
1200
+
1201
+ # Sort imports
1202
+ isort opensecureconf_client.py tests/
1203
+
1204
+ # Lint code
1205
+ flake8 opensecureconf_client.py
1206
+ pylint opensecureconf_client.py
1207
+
1208
+ # Type checking
1209
+ mypy opensecureconf_client.py
1210
+
1211
+ # Security check
1212
+ bandit -r opensecureconf_client.py
1213
+ ```
1214
+
1215
+ ### Building Distribution
1216
+
1217
+ ```bash
1218
+ # Build package
1219
+ python -m build
1220
+
1221
+ # Check distribution
1222
+ twine check dist/*
1223
+
1224
+ # Upload to TestPyPI
1225
+ twine upload --repository testpypi dist/*
1226
+
1227
+ # Upload to PyPI
1228
+ twine upload dist/*
1229
+ ```
1230
+
1231
+ ## 🤝 Contributing
1232
+
1233
+ Contributions are welcome! Please follow these guidelines:
1234
+
1235
+ ### Reporting Issues
1236
+
1237
+ - Use the [GitHub issue tracker](https://github.com/lordraw77/OpenSecureConf/issues)
1238
+ - Include minimal reproducible example
1239
+ - Specify Python version and client version
1240
+ - Include relevant logs (with sensitive data removed)
1241
+
1242
+ ### Pull Requests
1243
+
1244
+ 1. Fork the repository
1245
+ 2. Create a feature branch (`git checkout -b feature/amazing-feature`)
1246
+ 3. Make your changes
1247
+ 4. Add tests for new functionality
1248
+ 5. Ensure all tests pass (`pytest`)
1249
+ 6. Format code (`black`, `isort`)
1250
+ 7. Lint code (`flake8`, `pylint`)
1251
+ 8. Commit your changes (`git commit -m 'Add amazing feature'`)
1252
+ 9. Push to the branch (`git push origin feature/amazing-feature`)
1253
+ 10. Open a Pull Request
1254
+
1255
+ ### Code Style
1256
+
1257
+ - Follow [PEP 8](https://peps.python.org/pep-0008/)
1258
+ - Use type hints
1259
+ - Write docstrings for all public methods
1260
+ - Maintain test coverage above 90%
1261
+ - Add examples for new features
1262
+
1263
+ ## 📄 License
1264
+
1265
+ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
1266
+
1267
+ ## 🔗 Links
1268
+
1269
+ - [OpenSecureConf Server](https://github.com/lordraw77/OpenSecureConf)
1270
+ - [PyPI Package](https://pypi.org/project/opensecureconf-client/)
1271
+ - [Issue Tracker](https://github.com/lordraw77/OpenSecureConf/issues)
1272
+ - [Documentation](https://github.com/lordraw77/OpenSecureConf/tree/main/docs)
1273
+ - [Changelog](https://github.com/lordraw77/OpenSecureConf/blob/main/CHANGELOG.md)
1274
+
1275
+ ## 📮 Support
1276
+
1277
+ - 📧 Email: support@opensecureconf.dev
1278
+ - 💬 GitHub Discussions: [opensecureconf/discussions](https://github.com/lordraw77/OpenSecureConf/discussions)
1279
+ - 🐛 Bug Reports: [opensecureconf/issues](https://github.com/lordraw77/OpenSecureConf/issues)
1280
+
1281
+ ## 🙏 Acknowledgments
1282
+
1283
+ - Built with [FastAPI](https://fastapi.tiangolo.com/)
1284
+ - Encryption powered by [cryptography](https://cryptography.io/)
1285
+ - HTTP client using [requests](https://requests.readthedocs.io/)
1286
+
1287
+ ---
1288
+
1289
+ **Made with ❤️ by the OpenSecureConf Team**