Building Real-Time Task Notification System for maintenance.queenofsandiego.com
What Was Done
We implemented a complete task notification pipeline for the JADA maintenance tool, enabling real-time alerts when new maintenance tasks are added. The system intelligently routes notifications based on task criticality and includes a daily digest capability. This required creating three new Google Apps Script files, modifying existing routing logic, updating the staging HTML interface, and deploying Lambda infrastructure to persist task state.
Problem Statement
The maintenance.queenofsandiego.com tool had no mechanism to surface newly added tasks or notify stakeholders (Sergio and the operations team) when maintenance work was created. Travis could add tasks via the UI, but without visibility into what changed, the team operated reactively rather than proactively. Additionally, there was no audit trail or state persistence for tasks between sessions.
Architecture Overview
The solution follows a multi-layer approach:
- Frontend Layer: HTML5 interface at
/tools/maintenance/staging-index.htmlcaptures new tasks and sends them via POST to the GAS application - GAS Routing Layer:
BookingAutomation.gshandles the incoming request and routes to appropriate handlers based on action type - Persistence Layer: New
MaintenancePersistence.gsmanages state via DynamoDB through Lambda - Notification Layer: New
MaintenanceCalendar.gshandles email notifications and calendar event creation - Storage Layer: AWS Lambda and DynamoDB for durable state outside Google's ecosystem
File Changes and Implementation Details
1. Main GAS Files Modified
BookingAutomation.gs - Added maintenance routing in the doPost handler:
if (action === 'log_maintenance') {
return MaintenancePersistence.logNewTask(
request.parameter.task_name,
request.parameter.criticality,
request.parameter.description,
Session.getActiveUser().getEmail()
);
}
This routes maintenance-specific POST requests to the persistence layer rather than the booking system.
MaintenancePersistence.gs (newly created) - Handles all DynamoDB interactions:
- Function
logNewTask(name, criticality, description, createdBy)- Writes new task to DynamoDB with timestamp and UUID - Function
getTasks()- Retrieves all tasks from DynamoDB with sorting by date - Function
notifyStakeholders(task, criticality)- Calls notification Lambda based on task severity - Uses UrlFetchApp to invoke Lambda function at
maintenance-notification-handler
MaintenanceCalendar.gs (newly created) - Manages notifications and calendar events:
- Function
sendImmediateNotification(task)- For critical tasks (P0/P1), sends immediate email tojadasailing@gmail.com - Function
queueForDigest(task)- For lower priority tasks, adds to daily digest queue - Function
createCalendarEvent(task)- Creates event on "Jada Maintenance" calendar in the jadasailing@gmail.com account - Uses CalendarApp to interact with Google Calendar API
2. Frontend Changes
Modified /tools/maintenance/staging-index.html:
- Updated the task submission form to capture
criticalityfield (dropdown: P0/P1/P2/P3) - Added JavaScript to POST new tasks to the
log_maintenanceaction - Implemented client-side validation to ensure required fields before submission
- Added visual indicator showing last sync time with backend persistence layer
Infrastructure and Deployment
AWS Lambda Function
Created Lambda function maintenance-notification-handler:
- Runtime: Node.js 18.x (following existing tips-box Lambda pattern)
- IAM Role:
arn:aws:iam::ACCOUNT_ID:role/lambda-maintenance-execution - Permissions: DynamoDB read/write to
maintenance-taskstable, SES for email sending - Environment Variables:
DYNAMODB_TABLE=maintenance-tasks,NOTIFICATION_EMAIL=jadasailing@gmail.com
Handler logic invokes based on criticality:
const criticality = event.criticality;
if (criticality === 'P0' || criticality === 'P1') {
// Immediate SES email
await sendEmailViaSES(task, process.env.NOTIFICATION_EMAIL);
} else {
// Queue to DynamoDB digest table for end-of-day processing
await queueForDigest(task);
}
DynamoDB Tables
maintenance-tasks- Primary table for task storage- Partition Key:
taskId(UUID) - Sort Key:
createdAt(ISO 8601 timestamp) - TTL Attribute:
expiresAt(90 days from creation)
- Partition Key:
maintenance-digests- Queue for daily digest emails- Partition Key:
digestDate(YYYY-MM-DD) - Sort Key:
taskId
- Partition Key:
CloudFront and S3
- Staging HTML deployed to S3 bucket:
staging-maintenance-tools-queenofsandiego - CloudFront distribution:
E3ABC123DEF456(invalidated with path/tools/maintenance/*) - Live version remains at original S3 location until staging is validated
Key Technical Decisions
Why Lambda + DynamoDB for Persistence?
Google Apps Script has no native state persistence between executions. By using AWS Lambda with DynamoDB, we:
- Maintain a durable audit trail of all maintenance tasks
- Enable querying across sessions without parsing spreadsheet data
- Implement TTL-based cleanup without manual overhead
- Scale independently of the GAS app's quotas
Why Criticality-Based Notification Strategy?
Industry best practices (informed by DevOps alert fatigue research) recommend:
- P0/P1 (Critical): Immediate email notifications - these impact operations immediately
- P2/P3 (Standard/Low): Daily