Source: routers/choir.js

const express = require('express');
const auth = require('../middleware/authenticator.js');
const verify = require('../middleware/verifier.js');
const mysql = require("../db/mysql.js");
const router = express.Router();
const accessCode = require("../util/accessCode.js");

/**
 * @typedef {object} ChoirGetPackage
 * @property {ChoirGetReturnPackage[]} choirs Information on each choir retrieved
 */

/**
 * @typedef {object} ChoirGetReturnPackage
 * @property {string} choir_id The ID of the choir
 * @property {string} choir_name The name of the choir
 * @property {string} description A description of the choir
 */

/**
 * Route for getting all choir information
 * @name choir/get
 * @function
 * @inner
 * @param {*} req
 * @param {*} res
 * @returns {ChoirGetPackage|string} When successful, returns information about the choirs retrieved. Otherwise an error message
 * @async
 */
router.get('/', auth.token, async(req,res) => {
  let client = await mysql.getClient();
  const errorMessage = "Unable to get choirs";
  client.query("SELECT hex(choir.choir_id) AS choir_id, choir_name, description FROM tma.choir INNER JOIN tma.member ON choir.choir_id = member.choir_id WHERE person_id = ? AND member_status='verified'", [res.locals.uid], function(error, results, fields) {
    if (error) {
      res.status(503).send(errorMessage);
    } else {
      let retObj = {"choirs":results};
      res.status(200).json(retObj);
    }
  });
});

/**
 * @typedef {ChoirGetMembersReturnPackage[]} ChoirGetMembersPackage Information about the retrieved members from the choir
 */

/**
 * @typedef {object} ChoirGetMembersReturnPackage
 * @property {string} first_name The first name of the member
 * @property {string} last_name The last name of the member
 * @property {string} member_role The role of the member
 * @property {string} person_id The ID of the person attached to the member
 * @property {boolean} has_picture Whether the given member is attached to a person with a profile picture or not
 */

/**
 * Route for getting all the members of a given choir if the user is verified to be in that choir
 * @name choir/get/members
 * @function
 * @inner
 * @param {*} req
 * @param {string} req.query.choirId The choir ID to be accessed
 * @param {*} res
 * @returns {ChoirGetMembersPackage|string} When successful, returns information about the members in the choir. Otherwise an error message
 * @async
 */
router.get('/members', auth.token, async (req,res) => {
  verify.userIsVerified(req,res).then((isVerified) => {
    if (!isVerified) {
      res.status(403).send('Unauthorized');
    } else {
      let choirId = req.query.choirId;
      const errorMessage = "Unable to get choir";
      if (choirId) {
        mysql.getClient().then((client) => {
          client.query("SELECT first_name, last_name, member_type, member_role, tma.member.person_id AS person_id, has_picture FROM tma.member INNER JOIN tma.person ON member.person_id = person.person_id WHERE choir_id = unhex(?)", [choirId], function(error, results, fields) {
            if (error) {
              res.status(503).send(errorMessage);
            } else if (results.length === 0) {
              res.status(204).send(errorMessage);
            } else {
              res.status(200).json(results);
            }
          });
        })
      } else {
        res.status(400).send(errorMessage);
      }
    }
  }).catch((error) => {
    res.status(403).send('Unauthorized');
  });
});

/**
 * @typedef {object} ChoirPostPackage
 * @property {string} accessCode The access code of the generated choir
 */

/**
 * Route for adding a choir and sets the current user as the admin of that choir
 * @name choir/post
 * @function
 * @inner
 * @param {*} req
 * @param {string} req.body.choirName The name of the choir to be created
 * @param {string} req.body.description A description of the choir to be created. Can't be undefined but can be null
 * @param {string} req.body.memberType The type of the member to be created as the initial member into the choir
 * @param {string} req.body.memberRole The role of the member to be created as the initial member into the choir
 * @param {*} res
 * @returns {ChoirPostPackage|string} When successful, the access code of the generated choir. Otherwise, an error message
 * @async
 */
router.post('/', auth.token, async (req,res) => {
  let name = req.body.choirName;
  let description = req.body.description;
  let type = req.body.memberType;
  let role = req.body.memberRole;
  const errorMessage = "Unable to add choir";

  if (name && description !== undefined && type && role) {
    let client = await mysql.getClient();
    client.beginTransaction(function(error) {
      if (error) {
        res.status(503).send(errorMessage);
      } else {
        client.query("SELECT uuid()", function(error, results, fields) {
          if (error) {
            res.status(503).send(errorMessage);
          } else {
            res.locals.generated_uuid = results[0]["uuid()"];
            accessCode.getAccessCode().then((generatedAccessCode) => {
              client.query("insert into tma.choir (choir_id, choir_name, description, access_code) values(unhex(replace(?,'-','')), ?, ?, ?)",[res.locals.generated_uuid,name,description,generatedAccessCode], function (error, results, fields) {
                if (error) {
                  client.rollback(function() {
                    res.status(503).send(errorMessage);
                  });
                } else {
                  client.query(
"insert into tma.member (member_id, member_type, member_role, choir_id, person_id, member_status, gets_feedback) values(unhex(replace(uuid(),'-','')), ?, ?, unhex(replace(?,'-','')), ?, 'verified', 1)",[type,role,res.locals.generated_uuid,res.locals.uid], function (error, results, fields) {
                    if (error) {
                       client.rollback(function() {
                        res.status(503).send(errorMessage);
                      });
                    } else {
                      client.commit(function(error) {
                        if (error) {
                          client.rollback(function() {
                            res.status(503).send(errorMessage);
                          });
                        } else {
                          res.status(200).json({"accessCode":generatedAccessCode});
                        }
                      });
                    }
                  });
                }
              });
            }).catch((error) => {
              res.status(503).send(errorMessage);
            });
          }
        });
      }
    });
  } else {
    res.status(400).send(errorMessage);
  }
});

module.exports = router;