Source: models/BookingsModel.mjs

import { DatabaseModel } from "./DatabaseModel.mjs";

/**
 * Represents a booking stored in the bookings table.
 */
export class BookingsModel extends DatabaseModel {
  /**
   * @param {number|null} id Booking identifier.
   * @param {string} sessionId Session identifier.
   * @param {number} userId User identifier.
   * @param {string|Date} created Booking creation timestamp.
   */
  constructor(id, sessionId, userId, created) {
    super();
    this.id = id;
    this.session_id = sessionId;
    this.user_id = userId;
    this.created = created;
  }

  /**
   * Convert a database row into a BookingsModel instance.
   * @param {Object} row Database row.
   * @returns {BookingsModel} Mapped booking.
   */
  static tableToModel(row) {
    return new BookingsModel(
      Number(row.id),
      row.session_id,
      Number(row.user_id),
      row.created,
    );
  }

  /**
   * Retrieve all bookings.
   * @returns {Promise<Array<BookingsModel>>} Stored bookings.
   */
  static async getAll() {
    return this.query("SELECT * FROM bookings").then((result) =>
      result.map((row) => this.tableToModel(row.bookings)),
    );
  }

  /**
   * Retrieve bookings belonging to one user.
   * @param {number} userId User identifier.
   * @returns {Promise<Array<BookingsModel>>} The user's bookings.
   */
  static async getByUserId(userId) {
    return this.query("SELECT * FROM bookings WHERE user_id = ?", [
      userId,
    ]).then((result) => result.map((row) => this.tableToModel(row.bookings)));
  }

  /**
   * Retrieve bookings belonging to one session.
   * @param {number} sessionId Session identifier.
   * @returns {Promise<Array<BookingsModel>>} The session's bookings.
   */
  static async getBySessionId(sessionId) {
    return this.query("SELECT * FROM bookings WHERE session_id = ?", [
      sessionId,
    ]).then((result) => result.map((row) => this.tableToModel(row.bookings)));
  }

  /**
   * Delete every booking belonging to one session.
   * @param {number} sessionId Session identifier.
   * @returns {Promise<OkPacket>} Database result.
   */
  static deleteBySessionId(sessionId) {
    return this.query("DELETE FROM bookings WHERE session_id = ?", [sessionId]);
  }

  /**
   * Retrieve a booking by its identifier.
   * @param {number} id Booking identifier.
   * @returns {Promise<BookingsModel>} Matching booking.
   * @throws {string} "not found" when no booking matches the identifier.
   */
  static async getById(id) {
    const result = await this.query("SELECT * FROM bookings WHERE id = ?", [
      id,
    ]);
    return result.length > 0
      ? this.tableToModel(result[0].bookings)
      : Promise.reject("not found");
  }

  /**
   * Check whether a user already booked a session.
   * @param {number|string} sessionId Session identifier.
   * @param {number} userId User identifier.
   * @returns {Promise<boolean>} Whether the matching booking exists.
   */
  static async existsForSessionUser(sessionId, userId) {
    const result = await this.query(
      "SELECT id FROM bookings WHERE session_id = ? AND user_id = ? LIMIT 1",
      [sessionId, userId],
    );
    return result.length > 0;
  }

  /**
   * Update an existing booking.
   * @param {BookingsModel} booking Booking to update.
   * @returns {Promise<OkPacket>} Database result.
   */
  static update(booking) {
    return this.query(
      `
            UPDATE bookings
            SET session_id = ?, user_id = ?
            WHERE id = ?
        `,
      [booking.session_id, booking.user_id, booking.id],
    );
  }

  /**
   * Create a booking with a generated identifier.
   * @param {BookingsModel} booking Booking to create.
   * @returns {Promise<OkPacket | {affectedRows: 0, duplicate: true}>} Insert result or duplicate marker.
   */
  static async create(booking) {
    if (await this.existsForSessionUser(booking.session_id, booking.user_id)) {
      return { affectedRows: 0, duplicate: true };
    }

    return this.query(
      `
            INSERT INTO bookings (session_id, user_id, created)
            VALUES (?, ?, ?)
        `,
      [booking.session_id, booking.user_id, booking.created],
    );
  }

  /**
   * Create a booking with a caller-provided identifier.
   * @param {BookingsModel} booking Booking to create.
   * @returns {Promise<OkPacket>} Database result.
   */
  static createWithExistingID(booking) {
    return this.query(
      `
            INSERT INTO bookings (id, session_id, user_id, created)
            VALUES (?, ?, ?, ?)
        `,
      [booking.id, booking.session_id, booking.user_id, booking.created],
    );
  }

  /**
   * Delete a booking by its identifier.
   * @param {number} id Booking identifier.
   * @returns {Promise<OkPacket>} Database result.
   */
  static delete(id) {
    return this.query("DELETE FROM bookings WHERE id = ?", [id]);
  }
}