docs: add TimeSafari PWA request pattern adoption guide and example
- Add comprehensive guide for adopting existing TimeSafari PWA request patterns - Show direct integration of loadNewStarredProjectChanges() and getStarredProjectsWithChanges() - Provide Vue.js component integration examples - Include migration strategy and testing approach - Add practical example showing exact configuration needed - Demonstrate parallel testing and performance comparison - Show how to maintain existing TimeSafari interfaces while adding plugin features This enables seamless adoption of the plugin into existing TimeSafari PWA codebase while maintaining the same developer experience and user interface.
This commit is contained in:
682
docs/timesafari-request-adoption-guide.md
Normal file
682
docs/timesafari-request-adoption-guide.md
Normal file
@@ -0,0 +1,682 @@
|
||||
# TimeSafari Daily Notification Plugin - Request Pattern Adoption Guide
|
||||
|
||||
**Author**: Matthew Raymer
|
||||
**Version**: 1.0.0
|
||||
**Created**: 2025-10-08 06:24:57 UTC
|
||||
|
||||
## Overview
|
||||
|
||||
This guide shows how to directly adopt the existing TimeSafari PWA request patterns (like `loadNewStarredProjectChanges()` and `getStarredProjectsWithChanges()`) into the Daily Notification Plugin configuration. The plugin is designed to work seamlessly with TimeSafari's existing axios-based request architecture.
|
||||
|
||||
## Current TimeSafari PWA Pattern Analysis
|
||||
|
||||
### Original TimeSafari PWA Code
|
||||
```typescript
|
||||
// TimeSafari PWA - HomeView.vue
|
||||
private async loadNewStarredProjectChanges() {
|
||||
if (this.activeDid && this.starredPlanHandleIds.length > 0) {
|
||||
try {
|
||||
const starredProjectChanges = await getStarredProjectsWithChanges(
|
||||
this.axios,
|
||||
this.apiServer,
|
||||
this.activeDid,
|
||||
this.starredPlanHandleIds,
|
||||
this.lastAckedStarredPlanChangesJwtId,
|
||||
);
|
||||
this.numNewStarredProjectChanges = starredProjectChanges.data.length;
|
||||
this.newStarredProjectChangesHitLimit = starredProjectChanges.hitLimit;
|
||||
} catch (error) {
|
||||
logger.warn("[HomeView] Failed to load starred project changes:", error);
|
||||
this.numNewStarredProjectChanges = 0;
|
||||
this.newStarredProjectChangesHitLimit = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TimeSafari PWA - API function
|
||||
export async function getStarredProjectsWithChanges(
|
||||
axios: Axios,
|
||||
apiServer: string,
|
||||
activeDid: string,
|
||||
starredPlanHandleIds: string[],
|
||||
afterId?: string,
|
||||
): Promise<{ data: Array<PlanSummaryAndPreviousClaim>; hitLimit: boolean }> {
|
||||
const url = `${apiServer}/api/v2/report/plansLastUpdatedBetween`;
|
||||
const headers = await getHeaders(activeDid);
|
||||
|
||||
const requestBody = {
|
||||
planIds: starredPlanHandleIds,
|
||||
afterId: afterId,
|
||||
};
|
||||
|
||||
const response = await axios.post(url, requestBody, { headers });
|
||||
return response.data;
|
||||
}
|
||||
```
|
||||
|
||||
## Plugin Configuration Adoption
|
||||
|
||||
### 1. Direct Axios Integration Pattern
|
||||
|
||||
The plugin can be configured to use the host's existing axios instance and request patterns:
|
||||
|
||||
```typescript
|
||||
// In TimeSafari PWA - Plugin Configuration
|
||||
import { DailyNotification } from '@timesafari/daily-notification-plugin';
|
||||
import { TimeSafariIntegrationService } from '@timesafari/daily-notification-plugin';
|
||||
|
||||
// Configure plugin with existing TimeSafari patterns
|
||||
await DailyNotification.configure({
|
||||
// TimeSafari-specific configuration
|
||||
timesafariConfig: {
|
||||
activeDid: this.activeDid,
|
||||
|
||||
// Use existing TimeSafari API endpoints
|
||||
endpoints: {
|
||||
offersToPerson: `${this.apiServer}/api/v2/offers/person`,
|
||||
offersToPlans: `${this.apiServer}/api/v2/offers/plans`,
|
||||
projectsLastUpdated: `${this.apiServer}/api/v2/report/plansLastUpdatedBetween`
|
||||
},
|
||||
|
||||
// Configure starred projects fetching
|
||||
starredProjectsConfig: {
|
||||
enabled: true,
|
||||
starredPlanHandleIds: this.starredPlanHandleIds,
|
||||
lastAckedJwtId: this.lastAckedStarredPlanChangesJwtId,
|
||||
fetchInterval: '0 8 * * *', // Daily at 8 AM
|
||||
maxResults: 50,
|
||||
hitLimitHandling: 'warn' // 'warn' | 'error' | 'ignore'
|
||||
},
|
||||
|
||||
// Sync configuration matching TimeSafari patterns
|
||||
syncConfig: {
|
||||
enableParallel: true,
|
||||
maxConcurrent: 3,
|
||||
batchSize: 10,
|
||||
timeout: 30000,
|
||||
retryAttempts: 3
|
||||
}
|
||||
},
|
||||
|
||||
// Network configuration using existing patterns
|
||||
networkConfig: {
|
||||
// Use existing axios instance
|
||||
httpClient: this.axios,
|
||||
baseURL: this.apiServer,
|
||||
timeout: 30000,
|
||||
retryAttempts: 3,
|
||||
retryDelay: 1000,
|
||||
|
||||
// Headers matching TimeSafari pattern
|
||||
defaultHeaders: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
},
|
||||
|
||||
// Content fetch configuration
|
||||
contentFetch: {
|
||||
enabled: true,
|
||||
schedule: '0 8 * * *', // Daily at 8 AM
|
||||
|
||||
// Use existing TimeSafari request pattern
|
||||
requestConfig: {
|
||||
method: 'POST',
|
||||
url: '${apiServer}/api/v2/report/plansLastUpdatedBetween',
|
||||
headers: {
|
||||
'Authorization': 'Bearer ${jwt}',
|
||||
'X-User-DID': '${activeDid}'
|
||||
},
|
||||
body: {
|
||||
planIds: '${starredPlanHandleIds}',
|
||||
afterId: '${lastAckedJwtId}'
|
||||
}
|
||||
},
|
||||
|
||||
// Callbacks matching TimeSafari error handling
|
||||
callbacks: {
|
||||
onSuccess: async (data: { data: Array<PlanSummaryAndPreviousClaim>; hitLimit: boolean }) => {
|
||||
// Handle successful fetch - same as TimeSafari PWA
|
||||
this.numNewStarredProjectChanges = data.data.length;
|
||||
this.newStarredProjectChangesHitLimit = data.hitLimit;
|
||||
|
||||
// Update UI
|
||||
this.updateStarredProjectsUI(data);
|
||||
},
|
||||
|
||||
onError: async (error: Error) => {
|
||||
// Handle error - same as TimeSafari PWA
|
||||
logger.warn("[DailyNotification] Failed to load starred project changes:", error);
|
||||
this.numNewStarredProjectChanges = 0;
|
||||
this.newStarredProjectChangesHitLimit = false;
|
||||
},
|
||||
|
||||
onComplete: async (result: ContentFetchResult) => {
|
||||
// Handle completion
|
||||
logger.info("[DailyNotification] Starred projects fetch completed", {
|
||||
success: result.success,
|
||||
dataCount: result.data?.data?.length || 0,
|
||||
hitLimit: result.data?.hitLimit || false
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 2. Enhanced TimeSafari Integration Service
|
||||
|
||||
The plugin provides an enhanced integration service that mirrors TimeSafari's patterns:
|
||||
|
||||
```typescript
|
||||
// In TimeSafari PWA - Enhanced Integration
|
||||
import { TimeSafariIntegrationService } from '@timesafari/daily-notification-plugin';
|
||||
|
||||
// Initialize with existing TimeSafari configuration
|
||||
const integrationService = TimeSafariIntegrationService.getInstance();
|
||||
|
||||
await integrationService.initialize({
|
||||
activeDid: this.activeDid,
|
||||
storageAdapter: this.timeSafariStorageAdapter,
|
||||
endorserApiBaseUrl: this.apiServer,
|
||||
|
||||
// Use existing TimeSafari request patterns
|
||||
requestConfig: {
|
||||
httpClient: this.axios,
|
||||
baseURL: this.apiServer,
|
||||
timeout: 30000,
|
||||
retryAttempts: 3
|
||||
},
|
||||
|
||||
// Configure starred projects fetching
|
||||
starredProjectsConfig: {
|
||||
enabled: true,
|
||||
starredPlanHandleIds: this.starredPlanHandleIds,
|
||||
lastAckedJwtId: this.lastAckedStarredPlanChangesJwtId,
|
||||
fetchInterval: '0 8 * * *',
|
||||
maxResults: 50
|
||||
}
|
||||
});
|
||||
|
||||
// Use the service to fetch starred projects (same pattern as TimeSafari PWA)
|
||||
const fetchStarredProjects = async () => {
|
||||
try {
|
||||
const starredProjectChanges = await integrationService.getStarredProjectsWithChanges(
|
||||
this.activeDid,
|
||||
this.starredPlanHandleIds,
|
||||
this.lastAckedStarredPlanChangesJwtId
|
||||
);
|
||||
|
||||
// Same handling as TimeSafari PWA
|
||||
this.numNewStarredProjectChanges = starredProjectChanges.data.length;
|
||||
this.newStarredProjectChangesHitLimit = starredProjectChanges.hitLimit;
|
||||
|
||||
} catch (error) {
|
||||
// Same error handling as TimeSafari PWA
|
||||
logger.warn("[DailyNotification] Failed to load starred project changes:", error);
|
||||
this.numNewStarredProjectChanges = 0;
|
||||
this.newStarredProjectChangesHitLimit = false;
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 3. Vue.js Component Integration
|
||||
|
||||
Direct integration into existing TimeSafari Vue components:
|
||||
|
||||
```typescript
|
||||
// In TimeSafari PWA - HomeView.vue (enhanced)
|
||||
import { defineComponent } from 'vue';
|
||||
import { DailyNotification } from '@timesafari/daily-notification-plugin';
|
||||
import { TimeSafariIntegrationService } from '@timesafari/daily-notification-plugin';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'HomeView',
|
||||
|
||||
data() {
|
||||
return {
|
||||
// Existing TimeSafari data
|
||||
activeDid: '',
|
||||
starredPlanHandleIds: [] as string[],
|
||||
lastAckedStarredPlanChangesJwtId: '',
|
||||
numNewStarredProjectChanges: 0,
|
||||
newStarredProjectChangesHitLimit: false,
|
||||
|
||||
// Plugin integration
|
||||
dailyNotificationService: null as DailyNotificationService | null,
|
||||
integrationService: null as TimeSafariIntegrationService | null
|
||||
};
|
||||
},
|
||||
|
||||
async mounted() {
|
||||
// Initialize plugin with existing TimeSafari configuration
|
||||
await this.initializeDailyNotifications();
|
||||
},
|
||||
|
||||
methods: {
|
||||
async initializeDailyNotifications() {
|
||||
try {
|
||||
// Configure plugin with existing TimeSafari patterns
|
||||
await DailyNotification.configure({
|
||||
timesafariConfig: {
|
||||
activeDid: this.activeDid,
|
||||
endpoints: {
|
||||
projectsLastUpdated: `${this.apiServer}/api/v2/report/plansLastUpdatedBetween`
|
||||
},
|
||||
starredProjectsConfig: {
|
||||
enabled: true,
|
||||
starredPlanHandleIds: this.starredPlanHandleIds,
|
||||
lastAckedJwtId: this.lastAckedStarredPlanChangesJwtId,
|
||||
fetchInterval: '0 8 * * *'
|
||||
}
|
||||
},
|
||||
|
||||
networkConfig: {
|
||||
httpClient: this.axios,
|
||||
baseURL: this.apiServer,
|
||||
timeout: 30000
|
||||
},
|
||||
|
||||
contentFetch: {
|
||||
enabled: true,
|
||||
schedule: '0 8 * * *',
|
||||
callbacks: {
|
||||
onSuccess: this.handleStarredProjectsSuccess,
|
||||
onError: this.handleStarredProjectsError
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Initialize integration service
|
||||
this.integrationService = TimeSafariIntegrationService.getInstance();
|
||||
await this.integrationService.initialize({
|
||||
activeDid: this.activeDid,
|
||||
storageAdapter: this.timeSafariStorageAdapter,
|
||||
endorserApiBaseUrl: this.apiServer
|
||||
});
|
||||
|
||||
// Replace existing method with plugin-enhanced version
|
||||
this.loadNewStarredProjectChanges = this.loadNewStarredProjectChangesEnhanced;
|
||||
|
||||
} catch (error) {
|
||||
logger.error("[HomeView] Failed to initialize daily notifications:", error);
|
||||
}
|
||||
},
|
||||
|
||||
// Enhanced version of existing method
|
||||
async loadNewStarredProjectChangesEnhanced() {
|
||||
if (this.activeDid && this.starredPlanHandleIds.length > 0) {
|
||||
try {
|
||||
// Use plugin's enhanced fetching with same interface
|
||||
const starredProjectChanges = await this.integrationService!.getStarredProjectsWithChanges(
|
||||
this.activeDid,
|
||||
this.starredPlanHandleIds,
|
||||
this.lastAckedStarredPlanChangesJwtId
|
||||
);
|
||||
|
||||
// Same handling as original TimeSafari PWA
|
||||
this.numNewStarredProjectChanges = starredProjectChanges.data.length;
|
||||
this.newStarredProjectChangesHitLimit = starredProjectChanges.hitLimit;
|
||||
|
||||
} catch (error) {
|
||||
// Same error handling as original TimeSafari PWA
|
||||
logger.warn("[HomeView] Failed to load starred project changes:", error);
|
||||
this.numNewStarredProjectChanges = 0;
|
||||
this.newStarredProjectChangesHitLimit = false;
|
||||
}
|
||||
} else {
|
||||
this.numNewStarredProjectChanges = 0;
|
||||
this.newStarredProjectChangesHitLimit = false;
|
||||
}
|
||||
},
|
||||
|
||||
// Callback handlers matching TimeSafari patterns
|
||||
async handleStarredProjectsSuccess(data: { data: Array<PlanSummaryAndPreviousClaim>; hitLimit: boolean }) {
|
||||
// Same handling as TimeSafari PWA
|
||||
this.numNewStarredProjectChanges = data.data.length;
|
||||
this.newStarredProjectChangesHitLimit = data.hitLimit;
|
||||
|
||||
// Update UI
|
||||
this.updateStarredProjectsUI(data);
|
||||
},
|
||||
|
||||
async handleStarredProjectsError(error: Error) {
|
||||
// Same error handling as TimeSafari PWA
|
||||
logger.warn("[HomeView] Failed to load starred project changes:", error);
|
||||
this.numNewStarredProjectChanges = 0;
|
||||
this.newStarredProjectChangesHitLimit = false;
|
||||
},
|
||||
|
||||
// Existing TimeSafari methods (unchanged)
|
||||
updateStarredProjectsUI(data: { data: Array<PlanSummaryAndPreviousClaim>; hitLimit: boolean }) {
|
||||
// Existing TimeSafari UI update logic
|
||||
this.$emit('starred-projects-updated', data);
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Plugin Implementation Details
|
||||
|
||||
### 1. Enhanced TimeSafari Integration Service
|
||||
|
||||
The plugin provides an enhanced version of TimeSafari's request patterns:
|
||||
|
||||
```typescript
|
||||
// Plugin implementation - Enhanced TimeSafari Integration Service
|
||||
export class TimeSafariIntegrationService {
|
||||
private static instance: TimeSafariIntegrationService;
|
||||
private httpClient: AxiosInstance | null = null;
|
||||
private baseURL: string = '';
|
||||
private activeDid: string = '';
|
||||
|
||||
static getInstance(): TimeSafariIntegrationService {
|
||||
if (!TimeSafariIntegrationService.instance) {
|
||||
TimeSafariIntegrationService.instance = new TimeSafariIntegrationService();
|
||||
}
|
||||
return TimeSafariIntegrationService.instance;
|
||||
}
|
||||
|
||||
async initialize(config: {
|
||||
activeDid: string;
|
||||
httpClient: AxiosInstance;
|
||||
baseURL: string;
|
||||
storageAdapter: TimeSafariStorageAdapter;
|
||||
endorserApiBaseUrl: string;
|
||||
}): Promise<void> {
|
||||
this.activeDid = config.activeDid;
|
||||
this.httpClient = config.httpClient;
|
||||
this.baseURL = config.baseURL;
|
||||
|
||||
// Initialize with existing TimeSafari patterns
|
||||
await this.initializeStorage(config.storageAdapter);
|
||||
await this.initializeAuthentication(config.endorserApiBaseUrl);
|
||||
}
|
||||
|
||||
// Enhanced version of TimeSafari's getStarredProjectsWithChanges
|
||||
async getStarredProjectsWithChanges(
|
||||
activeDid: string,
|
||||
starredPlanHandleIds: string[],
|
||||
afterId?: string
|
||||
): Promise<{ data: Array<PlanSummaryAndPreviousClaim>; hitLimit: boolean }> {
|
||||
if (!starredPlanHandleIds || starredPlanHandleIds.length === 0) {
|
||||
return { data: [], hitLimit: false };
|
||||
}
|
||||
|
||||
if (!afterId) {
|
||||
return { data: [], hitLimit: false };
|
||||
}
|
||||
|
||||
try {
|
||||
// Use existing TimeSafari request pattern
|
||||
const url = `${this.baseURL}/api/v2/report/plansLastUpdatedBetween`;
|
||||
const headers = await this.getHeaders(activeDid);
|
||||
|
||||
const requestBody = {
|
||||
planIds: starredPlanHandleIds,
|
||||
afterId: afterId,
|
||||
};
|
||||
|
||||
// Use host's axios instance
|
||||
const response = await this.httpClient!.post(url, requestBody, { headers });
|
||||
|
||||
// Log with structured logging
|
||||
observability.logEvent('INFO', EVENT_CODES.FETCH_SUCCESS, 'Starred projects fetched successfully', {
|
||||
activeDid,
|
||||
planCount: starredPlanHandleIds.length,
|
||||
resultCount: response.data.data.length,
|
||||
hitLimit: response.data.hitLimit
|
||||
});
|
||||
|
||||
return response.data;
|
||||
|
||||
} catch (error) {
|
||||
// Enhanced error handling with structured logging
|
||||
observability.logEvent('ERROR', EVENT_CODES.FETCH_FAILURE, 'Failed to fetch starred projects', {
|
||||
activeDid,
|
||||
planCount: starredPlanHandleIds.length,
|
||||
error: (error as Error).message
|
||||
});
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
// Enhanced version of TimeSafari's getHeaders
|
||||
private async getHeaders(activeDid: string): Promise<Record<string, string>> {
|
||||
// Use existing TimeSafari authentication pattern
|
||||
const jwt = await this.getJWTToken(activeDid);
|
||||
|
||||
return {
|
||||
'Authorization': `Bearer ${jwt}`,
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'X-User-DID': activeDid
|
||||
};
|
||||
}
|
||||
|
||||
// Enhanced JWT token management
|
||||
private async getJWTToken(activeDid: string): Promise<string> {
|
||||
// Use existing TimeSafari JWT pattern
|
||||
const cachedToken = await this.getCachedToken(activeDid);
|
||||
if (cachedToken && !this.isTokenExpired(cachedToken)) {
|
||||
return cachedToken;
|
||||
}
|
||||
|
||||
// Generate new token using existing TimeSafari pattern
|
||||
const newToken = await this.generateJWTToken(activeDid);
|
||||
await this.cacheToken(activeDid, newToken);
|
||||
|
||||
return newToken;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Plugin Configuration Extensions
|
||||
|
||||
The plugin extends the configuration to support TimeSafari's existing patterns:
|
||||
|
||||
```typescript
|
||||
// Plugin configuration extensions
|
||||
export interface TimeSafariConfig {
|
||||
activeDid: string;
|
||||
|
||||
// Existing TimeSafari endpoints
|
||||
endpoints?: {
|
||||
offersToPerson?: string;
|
||||
offersToPlans?: string;
|
||||
projectsLastUpdated?: string;
|
||||
};
|
||||
|
||||
// Enhanced starred projects configuration
|
||||
starredProjectsConfig?: {
|
||||
enabled: boolean;
|
||||
starredPlanHandleIds: string[];
|
||||
lastAckedJwtId: string;
|
||||
fetchInterval: string; // Cron expression
|
||||
maxResults?: number;
|
||||
hitLimitHandling?: 'warn' | 'error' | 'ignore';
|
||||
};
|
||||
|
||||
// Existing TimeSafari sync configuration
|
||||
syncConfig?: {
|
||||
enableParallel?: boolean;
|
||||
maxConcurrent?: number;
|
||||
batchSize?: number;
|
||||
timeout?: number;
|
||||
retryAttempts?: number;
|
||||
};
|
||||
|
||||
// Enhanced error policy
|
||||
errorPolicy?: {
|
||||
maxRetries?: number;
|
||||
backoffMultiplier?: number;
|
||||
activeDidChangeRetries?: number;
|
||||
starredProjectsRetries?: number; // New
|
||||
};
|
||||
}
|
||||
|
||||
export interface NetworkConfig {
|
||||
// Use existing TimeSafari axios instance
|
||||
httpClient?: AxiosInstance;
|
||||
baseURL?: string;
|
||||
timeout?: number;
|
||||
retryAttempts?: number;
|
||||
retryDelay?: number;
|
||||
|
||||
// Existing TimeSafari headers
|
||||
defaultHeaders?: Record<string, string>;
|
||||
|
||||
// Enhanced request configuration
|
||||
requestConfig?: {
|
||||
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
|
||||
url: string;
|
||||
headers?: Record<string, string>;
|
||||
body?: Record<string, unknown>;
|
||||
params?: Record<string, string>;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Migration Strategy
|
||||
|
||||
### Phase 1: Parallel Implementation
|
||||
1. **Keep existing TimeSafari PWA code unchanged**
|
||||
2. **Add plugin configuration alongside existing code**
|
||||
3. **Test plugin functionality in parallel**
|
||||
4. **Compare results between existing and plugin implementations**
|
||||
|
||||
### Phase 2: Gradual Migration
|
||||
1. **Replace individual request methods one by one**
|
||||
2. **Use plugin's enhanced error handling and logging**
|
||||
3. **Maintain existing UI and user experience**
|
||||
4. **Add plugin-specific features (background fetching, etc.)**
|
||||
|
||||
### Phase 3: Full Integration
|
||||
1. **Replace all TimeSafari request patterns with plugin**
|
||||
2. **Remove duplicate code**
|
||||
3. **Leverage plugin's advanced features**
|
||||
4. **Optimize performance with plugin's caching and batching**
|
||||
|
||||
## Benefits of Plugin Adoption
|
||||
|
||||
### 1. Enhanced Error Handling
|
||||
```typescript
|
||||
// Existing TimeSafari PWA
|
||||
catch (error) {
|
||||
logger.warn("[HomeView] Failed to load starred project changes:", error);
|
||||
this.numNewStarredProjectChanges = 0;
|
||||
}
|
||||
|
||||
// Plugin-enhanced version
|
||||
catch (error) {
|
||||
// Structured logging with event IDs
|
||||
observability.logEvent('WARN', EVENT_CODES.FETCH_FAILURE, 'Failed to load starred project changes', {
|
||||
eventId: this.generateEventId(),
|
||||
activeDid: this.activeDid,
|
||||
planCount: this.starredPlanHandleIds.length,
|
||||
error: error.message,
|
||||
retryCount: this.retryCount
|
||||
});
|
||||
|
||||
// Enhanced fallback handling
|
||||
await this.handleStarredProjectsFallback(error);
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Background Fetching
|
||||
```typescript
|
||||
// Plugin provides background fetching
|
||||
await DailyNotification.configure({
|
||||
contentFetch: {
|
||||
enabled: true,
|
||||
schedule: '0 8 * * *', // Daily at 8 AM
|
||||
backgroundFetch: true, // Fetch in background
|
||||
cachePolicy: {
|
||||
maxAge: 3600, // 1 hour cache
|
||||
staleWhileRevalidate: 1800 // 30 minutes stale
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### 3. Enhanced Observability
|
||||
```typescript
|
||||
// Plugin provides comprehensive metrics
|
||||
const metrics = await DailyNotification.getMetrics();
|
||||
console.log('Starred Projects Metrics:', {
|
||||
fetchSuccessRate: metrics.starredProjects.fetchSuccessRate,
|
||||
averageResponseTime: metrics.starredProjects.averageResponseTime,
|
||||
cacheHitRate: metrics.starredProjects.cacheHitRate,
|
||||
errorRate: metrics.starredProjects.errorRate
|
||||
});
|
||||
```
|
||||
|
||||
## Testing Strategy
|
||||
|
||||
### 1. Parallel Testing
|
||||
```typescript
|
||||
// Test both implementations in parallel
|
||||
const testStarredProjectsFetch = async () => {
|
||||
// Existing TimeSafari PWA implementation
|
||||
const existingResult = await getStarredProjectsWithChanges(
|
||||
this.axios,
|
||||
this.apiServer,
|
||||
this.activeDid,
|
||||
this.starredPlanHandleIds,
|
||||
this.lastAckedStarredPlanChangesJwtId
|
||||
);
|
||||
|
||||
// Plugin implementation
|
||||
const pluginResult = await this.integrationService.getStarredProjectsWithChanges(
|
||||
this.activeDid,
|
||||
this.starredPlanHandleIds,
|
||||
this.lastAckedStarredPlanChangesJwtId
|
||||
);
|
||||
|
||||
// Compare results
|
||||
assert.deepEqual(existingResult, pluginResult);
|
||||
};
|
||||
```
|
||||
|
||||
### 2. Performance Testing
|
||||
```typescript
|
||||
// Compare performance
|
||||
const performanceTest = async () => {
|
||||
const start = Date.now();
|
||||
|
||||
// Existing implementation
|
||||
await getStarredProjectsWithChanges(...);
|
||||
const existingTime = Date.now() - start;
|
||||
|
||||
const pluginStart = Date.now();
|
||||
// Plugin implementation
|
||||
await this.integrationService.getStarredProjectsWithChanges(...);
|
||||
const pluginTime = Date.now() - pluginStart;
|
||||
|
||||
console.log('Performance Comparison:', {
|
||||
existing: existingTime,
|
||||
plugin: pluginTime,
|
||||
improvement: ((existingTime - pluginTime) / existingTime * 100).toFixed(2) + '%'
|
||||
});
|
||||
};
|
||||
```
|
||||
|
||||
## Conclusion
|
||||
|
||||
The Daily Notification Plugin is designed to seamlessly adopt TimeSafari's existing request patterns while providing enhanced functionality:
|
||||
|
||||
- **Same Interface**: Plugin methods match existing TimeSafari patterns
|
||||
- **Enhanced Features**: Background fetching, structured logging, metrics
|
||||
- **Gradual Migration**: Can be adopted incrementally
|
||||
- **Backward Compatibility**: Existing code continues to work
|
||||
- **Performance Improvements**: Caching, batching, and optimization
|
||||
|
||||
The plugin transforms TimeSafari's existing `loadNewStarredProjectChanges()` pattern into a more robust, observable, and efficient system while maintaining the same developer experience and user interface.
|
||||
|
||||
---
|
||||
|
||||
**Next Steps**:
|
||||
1. Implement parallel testing with existing TimeSafari PWA code
|
||||
2. Gradually migrate individual request methods
|
||||
3. Leverage plugin's advanced features for enhanced user experience
|
||||
Reference in New Issue
Block a user