Browse Source

feat: Implement dual scheduling API design and interfaces

- Add comprehensive dual scheduling interfaces to definitions.ts
- Implement ContentFetchConfig, UserNotificationConfig, and DualScheduleConfiguration
- Add new plugin methods for dual scheduling, content management, and callbacks
- Update web implementations with mock functionality for all new methods
- Fix all test files to include new dual scheduling method mocks
- Ensure TypeScript compilation and all tests pass successfully

Resolves: Plugin API design for dual scheduling system implementation
research/notification-plugin-enhancement
Matthew Raymer 1 day ago
parent
commit
9f8a8e60a9
  1. 143
      src/definitions.ts
  2. 79
      src/web.ts
  3. 161
      src/web/index.ts
  4. 20
      tests/advanced-scenarios.test.ts
  5. 20
      tests/daily-notification.test.ts
  6. 20
      tests/edge-cases.test.ts
  7. 20
      tests/enterprise-scenarios.test.ts

143
src/definitions.ts

@ -35,19 +35,7 @@ export interface ContentHandler {
(response?: any): Promise<{ title: string; body: string; data?: any }>; (response?: any): Promise<{ title: string; body: string; data?: any }>;
} }
export interface DailyNotificationPlugin {
scheduleDailyNotification(options: NotificationOptions | ScheduleOptions): Promise<void>;
getLastNotification(): Promise<NotificationResponse | null>;
cancelAllNotifications(): Promise<void>;
getNotificationStatus(): Promise<NotificationStatus>;
updateSettings(settings: NotificationSettings): Promise<void>;
getBatteryStatus(): Promise<BatteryStatus>;
requestBatteryOptimizationExemption(): Promise<void>;
setAdaptiveScheduling(options: { enabled: boolean }): Promise<void>;
getPowerState(): Promise<PowerState>;
checkPermissions(): Promise<PermissionStatus>;
requestPermissions(): Promise<PermissionStatus>;
}
export interface ScheduleOptions { export interface ScheduleOptions {
url?: string; url?: string;
@ -164,3 +152,132 @@ export interface SchedulingConfig {
}; };
timezone: string; timezone: string;
} }
// Dual Scheduling System Interfaces
export interface ContentFetchConfig {
enabled: boolean;
schedule: string; // Cron expression
url?: string;
headers?: Record<string, string>;
timeout?: number;
retryAttempts?: number;
retryDelay?: number;
callbacks: {
apiService?: string;
database?: string;
reporting?: string;
onSuccess?: (data: any) => Promise<void>;
onError?: (error: Error) => Promise<void>;
onComplete?: (result: ContentFetchResult) => Promise<void>;
};
contentHandler?: ContentHandler;
cachePolicy?: CachePolicy;
networkConfig?: NetworkConfig;
}
export interface UserNotificationConfig {
enabled: boolean;
schedule: string; // Cron expression
title?: string;
body?: string;
sound?: boolean;
vibration?: boolean;
priority?: 'low' | 'normal' | 'high';
badge?: boolean;
actions?: NotificationAction[];
category?: string;
userInfo?: Record<string, any>;
}
export interface NotificationAction {
id: string;
title: string;
icon?: string;
destructive?: boolean;
authenticationRequired?: boolean;
}
export interface DualScheduleConfiguration {
contentFetch: ContentFetchConfig;
userNotification: UserNotificationConfig;
relationship?: {
autoLink: boolean; // Automatically link content to notification
contentTimeout: number; // How long to wait for content before notification
fallbackBehavior: 'skip' | 'show_default' | 'retry';
};
}
export interface ContentFetchResult {
success: boolean;
data?: any;
timestamp: number;
contentAge: number;
error?: string;
retryCount: number;
metadata?: Record<string, any>;
}
export interface DualScheduleStatus {
contentFetch: {
isEnabled: boolean;
isScheduled: boolean;
lastFetchTime?: number;
nextFetchTime?: number;
lastFetchResult?: ContentFetchResult;
pendingFetches: number;
};
userNotification: {
isEnabled: boolean;
isScheduled: boolean;
lastNotificationTime?: number;
nextNotificationTime?: number;
pendingNotifications: number;
};
relationship: {
isLinked: boolean;
contentAvailable: boolean;
lastLinkTime?: number;
};
overall: {
isActive: boolean;
lastActivity: number;
errorCount: number;
successRate: number;
};
}
// Enhanced DailyNotificationPlugin interface with dual scheduling
export interface DailyNotificationPlugin {
// Existing methods
scheduleDailyNotification(options: NotificationOptions | ScheduleOptions): Promise<void>;
getLastNotification(): Promise<NotificationResponse | null>;
cancelAllNotifications(): Promise<void>;
getNotificationStatus(): Promise<NotificationStatus>;
updateSettings(settings: NotificationSettings): Promise<void>;
getBatteryStatus(): Promise<BatteryStatus>;
requestBatteryOptimizationExemption(): Promise<void>;
setAdaptiveScheduling(options: { enabled: boolean }): Promise<void>;
getPowerState(): Promise<PowerState>;
checkPermissions(): Promise<PermissionStatus>;
requestPermissions(): Promise<PermissionStatus>;
// New dual scheduling methods
scheduleContentFetch(config: ContentFetchConfig): Promise<void>;
scheduleUserNotification(config: UserNotificationConfig): Promise<void>;
scheduleDualNotification(config: DualScheduleConfiguration): Promise<void>;
getDualScheduleStatus(): Promise<DualScheduleStatus>;
updateDualScheduleConfig(config: DualScheduleConfiguration): Promise<void>;
cancelDualSchedule(): Promise<void>;
pauseDualSchedule(): Promise<void>;
resumeDualSchedule(): Promise<void>;
// Content management methods
getContentCache(): Promise<Record<string, any>>;
clearContentCache(): Promise<void>;
getContentHistory(): Promise<ContentFetchResult[]>;
// Callback management methods
registerCallback(name: string, callback: Function): Promise<void>;
unregisterCallback(name: string): Promise<void>;
getRegisteredCallbacks(): Promise<string[]>;
}

79
src/web.ts

@ -88,4 +88,83 @@ export class DailyNotificationWeb extends WebPlugin implements DailyNotification
carPlay: false carPlay: false
}; };
} }
// Dual Scheduling Methods Implementation
async scheduleContentFetch(_config: any): Promise<void> {
console.log('Schedule content fetch called on web platform');
}
async scheduleUserNotification(_config: any): Promise<void> {
console.log('Schedule user notification called on web platform');
}
async scheduleDualNotification(_config: any): Promise<void> {
console.log('Schedule dual notification called on web platform');
}
async getDualScheduleStatus(): Promise<any> {
return {
contentFetch: {
isEnabled: false,
isScheduled: false,
pendingFetches: 0
},
userNotification: {
isEnabled: false,
isScheduled: false,
pendingNotifications: 0
},
relationship: {
isLinked: false,
contentAvailable: false
},
overall: {
isActive: false,
lastActivity: Date.now(),
errorCount: 0,
successRate: 1.0
}
};
}
async updateDualScheduleConfig(_config: any): Promise<void> {
console.log('Update dual schedule config called on web platform');
}
async cancelDualSchedule(): Promise<void> {
console.log('Cancel dual schedule called on web platform');
}
async pauseDualSchedule(): Promise<void> {
console.log('Pause dual schedule called on web platform');
}
async resumeDualSchedule(): Promise<void> {
console.log('Resume dual schedule called on web platform');
}
async getContentCache(): Promise<Record<string, any>> {
return {};
}
async clearContentCache(): Promise<void> {
console.log('Clear content cache called on web platform');
}
async getContentHistory(): Promise<any[]> {
return [];
}
async registerCallback(_name: string, _callback: Function): Promise<void> {
console.log('Register callback called on web platform');
}
async unregisterCallback(_name: string): Promise<void> {
console.log('Unregister callback called on web platform');
}
async getRegisteredCallbacks(): Promise<string[]> {
return [];
}
} }

161
src/web/index.ts

@ -100,14 +100,14 @@ export class DailyNotificationWeb implements DailyNotificationPlugin {
*/ */
async updateSettings(settings: NotificationSettings): Promise<void> { async updateSettings(settings: NotificationSettings): Promise<void> {
this.settings = { ...this.settings, ...settings }; this.settings = { ...this.settings, ...settings };
console.log('Web notification settings updated:', this.settings); console.log('Settings updated:', this.settings);
} }
/** /**
* Get battery status (mock implementation) * Get battery status (mock implementation for web)
*/ */
async getBatteryStatus(): Promise<BatteryStatus> { async getBatteryStatus(): Promise<BatteryStatus> {
// Mock battery status for web // Mock implementation for web
return { return {
level: 100, level: 100,
isCharging: false, isCharging: false,
@ -117,21 +117,21 @@ export class DailyNotificationWeb implements DailyNotificationPlugin {
} }
/** /**
* Request battery optimization exemption (web not applicable) * Request battery optimization exemption (mock for web)
*/ */
async requestBatteryOptimizationExemption(): Promise<void> { async requestBatteryOptimizationExemption(): Promise<void> {
console.log('Battery optimization exemption not applicable on web'); console.log('Battery optimization exemption requested (web mock)');
} }
/** /**
* Set adaptive scheduling (web not applicable) * Set adaptive scheduling (mock for web)
*/ */
async setAdaptiveScheduling(options: { enabled: boolean }): Promise<void> { async setAdaptiveScheduling(options: { enabled: boolean }): Promise<void> {
console.log('Adaptive scheduling not applicable on web:', options); console.log('Adaptive scheduling set:', options.enabled);
} }
/** /**
* Get power state (mock implementation) * Get power state (mock for web)
*/ */
async getPowerState(): Promise<PowerState> { async getPowerState(): Promise<PowerState> {
return { return {
@ -141,13 +141,11 @@ export class DailyNotificationWeb implements DailyNotificationPlugin {
} }
/** /**
* Check permissions * Check permissions (web implementation)
*/ */
async checkPermissions(): Promise<PermissionStatus> { async checkPermissions(): Promise<PermissionStatus> {
if (!('Notification' in window)) { if (!('Notification' in window)) {
return { return {
status: 'denied',
granted: false,
notifications: 'denied', notifications: 'denied',
alert: false, alert: false,
badge: false, badge: false,
@ -161,7 +159,8 @@ export class DailyNotificationWeb implements DailyNotificationPlugin {
return { return {
status: permission, status: permission,
granted: permission === 'granted', granted: permission === 'granted',
notifications: permission as any, notifications: permission === 'granted' ? 'granted' :
permission === 'denied' ? 'denied' : 'prompt',
alert: permission === 'granted', alert: permission === 'granted',
badge: permission === 'granted', badge: permission === 'granted',
sound: permission === 'granted', sound: permission === 'granted',
@ -171,29 +170,139 @@ export class DailyNotificationWeb implements DailyNotificationPlugin {
} }
/** /**
* Request permissions * Request permissions (web implementation)
*/ */
async requestPermissions(): Promise<PermissionStatus> { async requestPermissions(): Promise<PermissionStatus> {
if (!('Notification' in window)) { if (!('Notification' in window)) {
throw new Error('Notifications not supported in this browser'); throw new Error('Notifications not supported in this browser');
} }
try { await Notification.requestPermission();
const permission = await Notification.requestPermission(); return this.checkPermissions();
}
// Dual Scheduling Methods Implementation
/**
* Schedule content fetch (web implementation)
*/
async scheduleContentFetch(config: any): Promise<void> {
console.log('Content fetch scheduled (web mock):', config);
// Mock implementation - in real app would use Service Worker
}
/**
* Schedule user notification (web implementation)
*/
async scheduleUserNotification(config: any): Promise<void> {
console.log('User notification scheduled (web mock):', config);
// Mock implementation - in real app would use browser notifications
}
/**
* Schedule dual notification (web implementation)
*/
async scheduleDualNotification(config: any): Promise<void> {
console.log('Dual notification scheduled (web mock):', config);
// Mock implementation combining content fetch and user notification
}
/**
* Get dual schedule status (web implementation)
*/
async getDualScheduleStatus(): Promise<any> {
return { return {
status: permission, contentFetch: {
granted: permission === 'granted', isEnabled: false,
notifications: permission as any, isScheduled: false,
alert: permission === 'granted', pendingFetches: 0
badge: permission === 'granted', },
sound: permission === 'granted', userNotification: {
lockScreen: permission === 'granted', isEnabled: false,
carPlay: false isScheduled: false,
pendingNotifications: 0
},
relationship: {
isLinked: false,
contentAvailable: false
},
overall: {
isActive: false,
lastActivity: Date.now(),
errorCount: 0,
successRate: 1.0
}
}; };
} catch (error) {
console.error('Error requesting notification permissions:', error);
throw error;
} }
/**
* Update dual schedule configuration (web implementation)
*/
async updateDualScheduleConfig(config: any): Promise<void> {
console.log('Dual schedule config updated (web mock):', config);
}
/**
* Cancel dual schedule (web implementation)
*/
async cancelDualSchedule(): Promise<void> {
console.log('Dual schedule cancelled (web mock)');
}
/**
* Pause dual schedule (web implementation)
*/
async pauseDualSchedule(): Promise<void> {
console.log('Dual schedule paused (web mock)');
}
/**
* Resume dual schedule (web implementation)
*/
async resumeDualSchedule(): Promise<void> {
console.log('Dual schedule resumed (web mock)');
}
/**
* Get content cache (web implementation)
*/
async getContentCache(): Promise<Record<string, any>> {
return {}; // Mock empty cache
}
/**
* Clear content cache (web implementation)
*/
async clearContentCache(): Promise<void> {
console.log('Content cache cleared (web mock)');
}
/**
* Get content history (web implementation)
*/
async getContentHistory(): Promise<any[]> {
return []; // Mock empty history
}
/**
* Register callback (web implementation)
*/
async registerCallback(name: string, _callback: Function): Promise<void> {
console.log('Callback registered (web mock):', name);
}
/**
* Unregister callback (web implementation)
*/
async unregisterCallback(name: string): Promise<void> {
console.log('Callback unregistered (web mock):', name);
}
/**
* Get registered callbacks (web implementation)
*/
async getRegisteredCallbacks(): Promise<string[]> {
return []; // Mock empty callback list
} }
/** /**

20
tests/advanced-scenarios.test.ts

@ -19,6 +19,26 @@ describe('DailyNotification Advanced Scenarios', () => {
getPowerState: jest.fn(), getPowerState: jest.fn(),
checkPermissions: jest.fn(), checkPermissions: jest.fn(),
requestPermissions: jest.fn(), requestPermissions: jest.fn(),
// Dual scheduling methods
scheduleContentFetch: jest.fn(),
scheduleUserNotification: jest.fn(),
scheduleDualNotification: jest.fn(),
getDualScheduleStatus: jest.fn(),
updateDualScheduleConfig: jest.fn(),
cancelDualSchedule: jest.fn(),
pauseDualSchedule: jest.fn(),
resumeDualSchedule: jest.fn(),
// Content management methods
getContentCache: jest.fn(),
clearContentCache: jest.fn(),
getContentHistory: jest.fn(),
// Callback management methods
registerCallback: jest.fn(),
unregisterCallback: jest.fn(),
getRegisteredCallbacks: jest.fn(),
}; };
plugin = new DailyNotification(mockPlugin); plugin = new DailyNotification(mockPlugin);
}); });

20
tests/daily-notification.test.ts

@ -33,6 +33,26 @@ describe('DailyNotification Plugin', () => {
getPowerState: jest.fn(), getPowerState: jest.fn(),
checkPermissions: jest.fn(), checkPermissions: jest.fn(),
requestPermissions: jest.fn(), requestPermissions: jest.fn(),
// Dual scheduling methods
scheduleContentFetch: jest.fn(),
scheduleUserNotification: jest.fn(),
scheduleDualNotification: jest.fn(),
getDualScheduleStatus: jest.fn(),
updateDualScheduleConfig: jest.fn(),
cancelDualSchedule: jest.fn(),
pauseDualSchedule: jest.fn(),
resumeDualSchedule: jest.fn(),
// Content management methods
getContentCache: jest.fn(),
clearContentCache: jest.fn(),
getContentHistory: jest.fn(),
// Callback management methods
registerCallback: jest.fn(),
unregisterCallback: jest.fn(),
getRegisteredCallbacks: jest.fn(),
}; };
// Create plugin instance with mock // Create plugin instance with mock

20
tests/edge-cases.test.ts

@ -24,6 +24,26 @@ describe('DailyNotification Edge Cases', () => {
getPowerState: jest.fn(), getPowerState: jest.fn(),
checkPermissions: jest.fn(), checkPermissions: jest.fn(),
requestPermissions: jest.fn(), requestPermissions: jest.fn(),
// Dual scheduling methods
scheduleContentFetch: jest.fn(),
scheduleUserNotification: jest.fn(),
scheduleDualNotification: jest.fn(),
getDualScheduleStatus: jest.fn(),
updateDualScheduleConfig: jest.fn(),
cancelDualSchedule: jest.fn(),
pauseDualSchedule: jest.fn(),
resumeDualSchedule: jest.fn(),
// Content management methods
getContentCache: jest.fn(),
clearContentCache: jest.fn(),
getContentHistory: jest.fn(),
// Callback management methods
registerCallback: jest.fn(),
unregisterCallback: jest.fn(),
getRegisteredCallbacks: jest.fn(),
}; };
plugin = new DailyNotification(mockPlugin); plugin = new DailyNotification(mockPlugin);
}); });

20
tests/enterprise-scenarios.test.ts

@ -23,6 +23,26 @@ describe('DailyNotification Enterprise Scenarios', () => {
getPowerState: jest.fn(), getPowerState: jest.fn(),
checkPermissions: jest.fn(), checkPermissions: jest.fn(),
requestPermissions: jest.fn(), requestPermissions: jest.fn(),
// Dual scheduling methods
scheduleContentFetch: jest.fn(),
scheduleUserNotification: jest.fn(),
scheduleDualNotification: jest.fn(),
getDualScheduleStatus: jest.fn(),
updateDualScheduleConfig: jest.fn(),
cancelDualSchedule: jest.fn(),
pauseDualSchedule: jest.fn(),
resumeDualSchedule: jest.fn(),
// Content management methods
getContentCache: jest.fn(),
clearContentCache: jest.fn(),
getContentHistory: jest.fn(),
// Callback management methods
registerCallback: jest.fn(),
unregisterCallback: jest.fn(),
getRegisteredCallbacks: jest.fn(),
}; };
plugin = new DailyNotification(mockPlugin); plugin = new DailyNotification(mockPlugin);
}); });

Loading…
Cancel
Save