Custom Adapter Example
This example demonstrates how to create custom storage adapters for file-keeper.
Overview
This script shows:
- How to create a SQLite-based storage adapter
- How to create an encrypted file storage adapter
- Best practices for adapter development
- Minimal adapter implementation example
Prerequisites
- Python 3.10+
- file-keeper library
- cryptography library (for encrypted adapter)
Installation
pip install file-keeper cryptography
Usage
Run the custom adapter examples:
python custom_adapter_example.py
This will:
- Demonstrate the SQLite storage adapter
- Demonstrate the encrypted storage adapter
- Show best practices for adapter development
- Provide a minimal adapter example
Key Concepts
1. Adapter Structure
Every adapter consists of:
- Settings: Configuration class inheriting from
fk.Settings - Uploader: Handles file uploads (inherits from
fk.Uploader) - Manager: Handles file management (inherits from
fk.Manager) - Reader: Handles file reading (inherits from
fk.Reader) - Storage: Main class combining all components (inherits from
fk.Storage)
2. Capability System
Adapters declare their capabilities by setting the capabilities attribute:
class MyUploader(Uploader):
capabilities = Capability.CREATE
class MyManager(Manager):
capabilities = Capability.EXISTS | Capability.ANALYZE | Capability.REMOVE
class MyReader(Reader):
capabilities = Capability.STREAM | Capability.RANGE
3. SQLite Adapter Example
class SQLiteStorage(Storage):
SettingsFactory = SQLiteSettings
UploaderFactory = SQLiteUploader
ManagerFactory = SQLiteManager
ReaderFactory = SQLiteReader
def __init__(self, settings):
super().__init__(settings)
# Initialize database connection
self.db = sqlite3.connect(settings.db_path)
Creating Your Own Adapter
Step 1: Define Settings
from file_keeper import Settings
import dataclasses
@dataclasses.dataclass
class MyCustomSettings(Settings):
host: str = "localhost"
port: int = 8080
username: str = ""
password: str = ""
_required_options = ["host"] # Required fields
Step 2: Implement Services
from file_keeper import Uploader, Manager, Reader, Capability
class MyUploader(Uploader):
capabilities = Capability.CREATE
def upload(self, location, upload, extras):
# Implement upload logic
pass
class MyManager(Manager):
capabilities = Capability.EXISTS | Capability.ANALYZE | Capability.REMOVE
def exists(self, data, extras):
# Implement existence check
pass
def analyze(self, location, extras):
pass
def remove(self, data, extras):
pass
class MyReader(Reader):
capabilities = Capability.STREAM
def stream(self, data, extras):
# Implement streaming logic
pass
Step 3: Create Storage Class
from file_keeper import Storage
class MyCustomStorage(Storage):
SettingsFactory = MyCustomSettings
UploaderFactory = MyUploader
ManagerFactory = MyManager
ReaderFactory = MyReader
def __init__(self, settings):
super().__init__(settings)
# Initialize your backend connection
self.client = self.initialize_backend()
def initialize_backend(self):
# Initialize your storage backend
pass
Step 4: Register the Adapter
import file_keeper as fk
# initialize adapter registry. This is not required when storages
# are registered via package entry-points.
fk.ext.setup()
# Register your adapter
fk.adapters.register("file_keeper:myadapter", MyCustomStorage)
# Now you can use it
storage = fk.make_storage("my_storage", {
"type": "file_keeper:myadapter",
"host": "myserver.com",
"port": 9000
})
Best Practices
- Define appropriate settings: Include validation and required fields
- Implement only supported capabilities: Don't claim capabilities your backend doesn't support
- Handle errors properly: Use file-keeper's exception hierarchy
- Respect security: Validate paths and implement proper access controls
- Test thoroughly: Test with the standard test suite
- Document limitations: Clearly document what your adapter supports
- Follow interface contracts: Maintain compatibility with file-keeper interfaces
- Consider thread safety: If your backend supports concurrent access
Security Considerations
When creating custom adapters, pay attention to: - Path traversal prevention - Authentication and authorization - Data encryption at rest - Secure credential handling - Input validation
Performance Considerations
- Minimize network calls when possible
- Implement efficient streaming for large files
- Use appropriate caching strategies
- Consider connection pooling for network backends
- Optimize for your specific use case
Testing Your Adapter
Create tests that verify:
- All claimed capabilities work correctly
- Error conditions are handled properly
- Security measures are effective
- Performance meets requirements
- Edge cases are handled