api/v1/coachings/controller.js

import get from "lodash/get"

import Calendly from "api/v1/calendly/model"
import BaseController from "api/BaseController"

import CoachingModel from "./model"
import { prepQuery } from "./helpers"

/**
 * Controller for the @namespace /coachings
 *
 * @class CoachingsController
 * @extends {BaseController}
 */
class CoachingsController extends BaseController {
    constructor() {
        super()

        this.index = this.index.bind(this)
        this.show = this.show.bind(this)
        this.create = this.create.bind(this)
        this.update = this.update.bind(this)
        this.delete = this.delete.bind(this)
    }

    /**
   * GET / Fetches all coaching by user email address
   *
   * @param {int} emailAddress | Identifiers reference to the user instance
   * @returns [{CoachingModel}]
   * @memberof CoachingsController
   */
    async index(req, res) {
        const { success, unprocessableEntity } = this.codes
        const { emailAddress } = req.query

        if (!emailAddress) {
            res.status(unprocessableEntity).send({
                success: "false",
                message: "emailAddress_is_required",
                error: "ERROR: emailAddress is required in the request query",
            })
        }

        const query = prepQuery(req.query)

        const coaching = new CoachingModel()
        const result = await coaching.find(query).catch((error) => {
            res.status(unprocessableEntity).send({
                success: "false",
                message: "coaching_error",
                error,
            })

            throw error
        })

        res.status(success).send({
            success: "true",
            message: "coachings_retrieved_successfully",
            coachings: get(result, "rows", []),
        })
    }

    /**
   * GET /:id Fetch specific user exercise by id
   *
   * @param {int} id
   * @returns {CoachingModel}
   * @memberof CoachingsController
   */
    async show(req, res) {
        const { success, notFound, unprocessableEntity } = this.codes
        const { id } = req.params

        const coaching = new CoachingModel()
        const result = await coaching.find({ id }).catch((error) => {
            res.status(unprocessableEntity).send({
                success: "false",
                message: "coaching_error",
                error,
            })

            throw error
        })

        coaching.set(get(result, "rows[0]"))

        if (coaching.error) {
            res.status(notFound).send({
                success: "false",
                message: "coaching_not_found",
                error: coaching.error,
            })

            return
        }

        res.status(success).send({
            success: "true",
            message: "coaching_retrieved_successfully",
            coaching: coaching.props,
        })
    }

    /**
   * POST / Create an instance of coaching from a Calendly event
   *
   * @param {object} payload | payload from the event_created endpoint in the Calendly API
   * @returns {CoachingModel}
   * @memberof CoachingsController
   */
    async create(req, res) {
        const { success, unprocessableEntity, conflict } = this.codes
        const { event_type: eventType, event, invitee } = req.body?.payload || {}
        const { uuid: eventId } = eventType
        const { email: inviteeEmail } = invitee
        const {
            uuid: calendlyId,
            location,
            canceled,
            canceler_name: cancelerName,
            cancel_reason: cancelReason,
            canceled_at: canceledAt,
            start_time: startTime,
            created_at: createdAt,
        } = event

        if (!Calendly.eventTypeExists(eventId)) {
            res.status(unprocessableEntity).send({
                success: "false",
                message: "event_type_does_not_exists",
                error: Calendly.error,
            })
            return
        }

        const coaching = new CoachingModel({
            eventType: eventId,
            inviteeEmail,
            location,
            canceled,
            cancelerName,
            cancelReason,
            canceledAt: canceledAt ? new Date(canceledAt) : null,
            startTime: startTime ? new Date(startTime) : null,
            createdAt: createdAt ? new Date(createdAt) : null,
            calendlyId,
        })

        const doesCoachingExists = await coaching.exists({ calendlyId })

        if (doesCoachingExists) {
            res.status(conflict).send({
                success: "false",
                message: "coaching_already_exists",
                error: coaching.error,
            })
            return
        }

        if (coaching.error) {
            res.status(unprocessableEntity).send({
                success: "false",
                message: "coaching_create_failed",
                error: coaching.error,
            })
            return
        }

        const result = await coaching.create()
            .catch((error) => {
                res.status(unprocessableEntity).send({
                    success: "false",
                    message: "coaching_create_failed",
                    error,
                })

                throw error
            })

        const createCoaching = get(result, "rows[0]")

        if (createCoaching) {
            res.status(success).send({
                success: "true",
                message: "coaching_created_successfully",
                coaching: createCoaching,
            })
        }
    }

    /**
   * PUT / Update an instance of coaching
   *
   * @param {boolean} completed
   * @param {int} progress
   * @param {boolean} like
   * @param {date} doneAt
   * @param {date} lastVisit
   * @returns {CoachingModel}
   * @memberof CoachingsController
   */
    async update(req, res) {
        const { success, unprocessableEntity, notFound } = this.codes
        const { id } = req.params
        const { ...newProps } = req.body

        const coaching = new CoachingModel()
        const result = await coaching.find({ id }).catch((error) => {
            res.status(unprocessableEntity).send({
                success: "false",
                message: "coaching_update_failed",
                error,
            })

            throw error
        })

        if (coaching.error) {
            res.status(notFound).send({
                success: "false",
                message: "coaching_not_found",
                error: coaching.error,
            })

            return
        }

        const oldCoaching = get(result, "rows[0]")
        coaching.set({ ...oldCoaching, ...newProps })

        if (coaching.error) {
            res.status(unprocessableEntity).send({
                success: "false",
                message: "coaching_update_failed",
                error: coaching.error,
            })

            return
        }

        const updatedCoaching = await coaching.update().catch((error) => {
            res.status(unprocessableEntity).send({
                success: "false",
                message: "user_update_failed",
                error,
            })

            throw error
        })

        if (get(updatedCoaching, "rows[0]")) {
            res.status(success).send({
                success: "true",
                message: "coaching_updated_successfully",
                coaching: coaching.props,
            })
        }
    }

    /**
   * DELETE / Delete an instance of coaching
   *
   * @param {int} id
   * @returns {CoachingModel}
   * @memberof CoachingsController
   */
    async delete(req, res) {
        const { success, unprocessableEntity, notFound } = this.codes
        const { id } = req.params

        const coaching = new CoachingModel()
        const result = await coaching.find({ id }).catch((error) => {
            res.status(unprocessableEntity).send({
                success: "false",
                message: "coaching_delete_failed",
                error,
            })

            throw error
        })

        if (coaching.error) {
            res.status(notFound).send({
                success: "false",
                message: "coaching_not_found",
                error: coaching.error,
            })

            return
        }

        const data = get(result, "rows[0]")
        coaching.set({ ...data })

        if (coaching.error) {
            res.status(unprocessableEntity).send({
                success: "false",
                message: "coaching_delete_failed",
                error: coaching.error,
            })

            return
        }

        const deletedCoaching = await coaching.delete().catch((error) => {
            res.status(unprocessableEntity).send({
                success: "false",
                message: "user_delete_failed",
                error,
            })

            throw error
        })

        if (get(deletedCoaching, "rows[0]")) {
            res.status(success).send({
                success: "true",
                message: "coaching_deleted_successfully",
                coaching: coaching.props,
            })
        }
    }
}

export default CoachingsController