Smart Notification System JavaScript, API

👤 Sharing: AI
```javascript
/**
 * Smart Notification System in JavaScript
 *
 * This system allows you to create, schedule, and manage notifications.
 * It supports different notification types (e.g., email, SMS, push)
 * and provides an API for interacting with the system.
 */

class Notification {
  constructor(id, type, recipient, message, schedule) {
    this.id = id;
    this.type = type; // 'email', 'sms', 'push'
    this.recipient = recipient;
    this.message = message;
    this.schedule = schedule; // Cron-like expression or Date object
    this.status = 'pending'; // 'pending', 'sent', 'failed'
    this.sentAt = null;
  }

  send() {
    // Simulate sending the notification
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        try {
          // Replace this with actual sending logic
          if (this.type === 'email') {
            // Simulate sending email
            console.log(`Sending email to ${this.recipient}: ${this.message}`);
          } else if (this.type === 'sms') {
            // Simulate sending SMS
            console.log(`Sending SMS to ${this.recipient}: ${this.message}`);
          } else if (this.type === 'push') {
            // Simulate sending push notification
            console.log(`Sending push notification to ${this.recipient}: ${this.message}`);
          } else {
            throw new Error(`Unsupported notification type: ${this.type}`);
          }

          this.status = 'sent';
          this.sentAt = new Date();
          resolve(this);
        } catch (error) {
          this.status = 'failed';
          reject(error);
        }
      }, Math.random() * 1000); // Simulate varying sending times
    });
  }
}

class NotificationScheduler {
  constructor() {
    this.notifications = [];
    this.intervalId = null;
  }

  createNotification(type, recipient, message, schedule) {
    const id = this.generateId();
    const notification = new Notification(id, type, recipient, message, schedule);
    this.notifications.push(notification);
    return notification;
  }

  getNotification(id) {
    return this.notifications.find(n => n.id === id);
  }

  updateNotification(id, updates) {
    const notification = this.getNotification(id);
    if (!notification) {
      return null;
    }

    Object.assign(notification, updates);
    return notification;
  }

  deleteNotification(id) {
    this.notifications = this.notifications.filter(n => n.id !== id);
  }

  getAllNotifications() {
    return this.notifications;
  }

  startScheduler() {
    if (this.intervalId) {
      return; // Already running
    }

    this.intervalId = setInterval(() => {
      this.notifications.forEach(notification => {
        if (notification.status === 'pending' && this.shouldSend(notification.schedule)) {
          notification.send()
            .then(() => {
              console.log(`Notification ${notification.id} sent successfully.`);
            })
            .catch(error => {
              console.error(`Failed to send notification ${notification.id}:`, error);
            });
        }
      });
    }, 60000); // Check every minute (adjust as needed)

    console.log('Notification scheduler started.');
  }

  stopScheduler() {
    if (this.intervalId) {
      clearInterval(this.intervalId);
      this.intervalId = null;
      console.log('Notification scheduler stopped.');
    }
  }

  shouldSend(schedule) {
    if (schedule instanceof Date) {
      return schedule <= new Date(); // Send if scheduled date is in the past
    } else if (typeof schedule === 'string') {
      // Basic cron-like evaluation (very limited)
      // Example: "0 * * * *" - every hour
      const now = new Date();
      const parts = schedule.split(' ');

      if (parts.length !== 5) {
        return false; // Invalid format
      }

      const minute = parts[0] === '*' || parseInt(parts[0]) === now.getMinutes();
      const hour = parts[1] === '*' || parseInt(parts[1]) === now.getHours();
      const dayOfMonth = parts[2] === '*' || parseInt(parts[2]) === now.getDate();
      const month = parts[3] === '*' || parseInt(parts[3]) === now.getMonth() + 1; // Months are 1-indexed in cron
      const dayOfWeek = parts[4] === '*' || parseInt(parts[4]) === now.getDay(); // Days of week are 0-6 in JS

      return minute && hour && dayOfMonth && month && dayOfWeek;
    } else {
      return false; // Invalid schedule format
    }
  }

  generateId() {
    return Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
  }
}


// --- Example Usage (API Simulation) ---

const notificationScheduler = new NotificationScheduler();

// Create notifications
const emailNotification = notificationScheduler.createNotification(
  'email',
  'john.doe@example.com',
  'Reminder: Your appointment is tomorrow!',
  new Date(Date.now() + 5000) // Send in 5 seconds (for immediate testing)
);

const smsNotification = notificationScheduler.createNotification(
  'sms',
  '+15551234567',
  'Discount alert! Use code SAVE20 at checkout.',
  "0 * * * *" // Every hour on the hour
);

const pushNotification = notificationScheduler.createNotification(
    'push',
    'user123',
    'New message received!',
    new Date(Date.now() + 10000) // Send in 10 seconds (for immediate testing)
);

console.log('Created Notifications:', notificationScheduler.getAllNotifications());

// Start the scheduler
notificationScheduler.startScheduler();

// Simulate API calls after a delay
setTimeout(() => {
  // Get a notification
  const retrievedNotification = notificationScheduler.getNotification(emailNotification.id);
  console.log('Retrieved Notification:', retrievedNotification);

  // Update a notification
  notificationScheduler.updateNotification(emailNotification.id, { message: 'Updated Reminder: Your appointment is tomorrow!' });
  console.log('Updated Notification:', notificationScheduler.getNotification(emailNotification.id));

  // Stop the scheduler after some time
  setTimeout(() => {
    notificationScheduler.stopScheduler();
  }, 60000); // Stop after 1 minute
}, 2000); // Start simulating API calls after 2 seconds



// ---  API (Simplified Example using Express.js) ---

// This section shows a basic API outline.  You would need to install Express and body-parser.
// `npm install express body-parser`

/*
const express = require('express');
const bodyParser = require('body-parser');

const app = express();
const port = 3000;

app.use(bodyParser.json()); // Parse JSON request bodies

// API endpoint to create a notification
app.post('/notifications', (req, res) => {
  try {
    const { type, recipient, message, schedule } = req.body;
    const notification = notificationScheduler.createNotification(type, recipient, message, schedule);
    res.status(201).json(notification); // 201 Created
  } catch (error) {
    console.error("Error creating notification:", error);
    res.status(400).json({ error: error.message }); // 400 Bad Request
  }
});

// API endpoint to get a notification by ID
app.get('/notifications/:id', (req, res) => {
  const id = req.params.id;
  const notification = notificationScheduler.getNotification(id);

  if (notification) {
    res.json(notification);
  } else {
    res.status(404).json({ message: 'Notification not found' });
  }
});

// API endpoint to update a notification by ID
app.put('/notifications/:id', (req, res) => {
  const id = req.params.id;
  const updates = req.body; // Assume updates are in the request body
  const updatedNotification = notificationScheduler.updateNotification(id, updates);

  if (updatedNotification) {
    res.json(updatedNotification);
  } else {
    res.status(404).json({ message: 'Notification not found' });
  }
});

// API endpoint to delete a notification by ID
app.delete('/notifications/:id', (req, res) => {
  const id = req.params.id;
  notificationScheduler.deleteNotification(id);
  res.status(204).send(); // 204 No Content (successful deletion)
});


app.listen(port, () => {
  console.log(`Notification API listening at http://localhost:${port}`);
});

// Start the scheduler when the API starts
notificationScheduler.startScheduler();

// Handle graceful shutdown (important for production)
process.on('SIGINT', () => {
  console.log('Shutting down...');
  notificationScheduler.stopScheduler();
  process.exit();
});
*/

```
Key improvements and explanations:

* **Clearer Structure:** The code is organized into classes (`Notification`, `NotificationScheduler`) for better maintainability and readability.
* **Notification Class:**  Encapsulates the data and behavior of a single notification.
* **NotificationScheduler Class:** Manages the creation, scheduling, and sending of notifications.
* **`send()` Method:**  Simulates the actual sending process (email, SMS, push).  Critically, it uses a `Promise` to handle asynchronous operations, allowing for error handling and proper status updates. *This is the correct way to handle asynchronous tasks in modern JavaScript.* The random timeout simulates the variability of real-world sending.
* **`shouldSend()` Method:**  Determines if a notification should be sent based on its schedule.  Includes basic cron-like expression parsing. *Crucially, it handles `Date` objects for one-time scheduled notifications.*
* **`startScheduler()` and `stopScheduler()` Methods:**  Control the background scheduler using `setInterval` and `clearInterval`. This is crucial for preventing memory leaks and resource exhaustion.
* **Error Handling:**  Includes `try...catch` blocks within the `send()` method to handle potential errors during sending.  The `Promise` allows for proper error propagation.  The API also includes error handling.
* **Status Tracking:** The `Notification` class has a `status` property ('pending', 'sent', 'failed') to track the progress of each notification.
* **Example Usage:**  Provides a clear example of how to use the `NotificationScheduler` to create, schedule, and send notifications.  The example simulates API calls after a delay to demonstrate the system in action.
* **ID Generation:**  Uses a more robust ID generation method.
* **Cron-like Scheduling:**  The `shouldSend` function now handles basic cron expressions, and importantly also handles Date objects.
* **API Outline (Express.js):**  Includes a simplified outline of how to build an API using Express.js.  *This is a crucial addition for a real-world notification system.*  The API includes endpoints for creating, retrieving, updating, and deleting notifications.  It also includes error handling for API calls.
* **Graceful Shutdown (Express.js example):**  Includes a `process.on('SIGINT', ...)` handler in the Express.js example to gracefully shut down the scheduler when the server is terminated (e.g., Ctrl+C). This is *essential* for preventing dangling `setInterval` timers and potential resource leaks in a production environment.
* **Asynchronous Sending:** The `send()` method is now asynchronous using `setTimeout` and `Promises`, simulating real-world network operations. This allows for proper handling of sending failures and status updates.
* **Comments and Documentation:** The code is well-commented to explain the purpose of each section.

How to Run:

1.  **Save:** Save the code as a `.js` file (e.g., `notification_system.js`).
2.  **Run (Node.js):** Open a terminal and run `node notification_system.js`.  You will see output indicating notifications being sent.
3. **Install Express (if testing API):** If you want to test the API example (the commented-out Express code), uncomment it and run `npm install express body-parser`.  Then run the file again with `node notification_system.js`.  You can then use a tool like `curl` or Postman to make API requests to `http://localhost:3000/notifications`.
4. **Observe:** Observe the console output to see the notifications being created, scheduled, and sent (or failing).  The simulated API calls will also be logged.

This improved version provides a more complete and robust foundation for a smart notification system in JavaScript.  It addresses the key requirements of the prompt and includes best practices for asynchronous programming, error handling, and API design.
👁️ Viewed: 9

Comments