Calendar/src/storage/SyncPlugin.ts

91 lines
2.9 KiB
TypeScript
Raw Normal View History

import { ISync, SyncStatus, EntityType } from '../types/CalendarTypes';
/**
* SyncPlugin<T extends ISync> - Pluggable sync functionality for entity services
*
* COMPOSITION PATTERN:
* - Encapsulates all sync-related logic in separate class
* - Composed into BaseEntityService (not inheritance)
* - Allows sync functionality to be swapped/mocked for testing
* - Single Responsibility: Only handles sync status management
*
* DESIGN:
* - Takes reference to BaseEntityService for calling get/save
* - Implements sync methods that delegate to service's CRUD
* - Uses IndexedDB syncStatus index for efficient queries
*/
export class SyncPlugin<T extends ISync> {
/**
* @param service - Reference to BaseEntityService for CRUD operations
*/
constructor(private service: any) {
// Type is 'any' to avoid circular dependency at compile time
// Runtime: service is BaseEntityService<T>
}
/**
* Mark entity as successfully synced
* Sets syncStatus = 'synced' and persists to IndexedDB
*
* @param id - Entity ID
*/
async markAsSynced(id: string): Promise<void> {
const entity = await this.service.get(id);
if (entity) {
entity.syncStatus = 'synced';
await this.service.save(entity);
}
}
/**
* Mark entity as sync error (max retries exceeded)
* Sets syncStatus = 'error' and persists to IndexedDB
*
* @param id - Entity ID
*/
async markAsError(id: string): Promise<void> {
const entity = await this.service.get(id);
if (entity) {
entity.syncStatus = 'error';
await this.service.save(entity);
}
}
/**
* Get current sync status for an entity
*
* @param id - Entity ID
* @returns SyncStatus or null if entity not found
*/
async getSyncStatus(id: string): Promise<SyncStatus | null> {
const entity = await this.service.get(id);
return entity ? entity.syncStatus : null;
}
/**
* Get entities by sync status
* Uses IndexedDB syncStatus index for efficient querying
*
* @param syncStatus - Sync status ('synced', 'pending', 'error')
* @returns Array of entities with this sync status
*/
async getBySyncStatus(syncStatus: string): Promise<T[]> {
return new Promise((resolve, reject) => {
const transaction = this.service.db.transaction([this.service.storeName], 'readonly');
const store = transaction.objectStore(this.service.storeName);
const index = store.index('syncStatus');
const request = index.getAll(syncStatus);
request.onsuccess = () => {
const data = request.result as any[];
const entities = data.map(item => this.service.deserialize(item));
resolve(entities);
};
request.onerror = () => {
reject(new Error(`Failed to get ${this.service.entityType}s by sync status ${syncStatus}: ${request.error}`));
};
});
}
}