Source: controllers/SpineImageController.js

/** @module controllers */

const BaseDatabaseController = require("./BaseDatabaseController");

/** Default metadata for spine images */
const spineImagesDefaultMeta = {
    "crowdApproved": false,
    "crowdConfirmations": 0,
    "crowdRejections": 0,
};

/** Class representing a spine image controller. */
class SpineImageController extends BaseDatabaseController {
    /** Create a spine image controller. */
    constructor() {
        super();
    }

    /**
     * Get a spine image by id.
     * 
     * @param {number} id
     * @returns {Promise<object>}
     */
    async byId(id) {
        const spineImageResponse = await this._query('SELECT * FROM spine_images WHERE id = ?', id);
        if (spineImageResponse.length !== 1) {
            return null;
        }

        return spineImageResponse[0];
    }

    /**
     * Get spine images by edition.
     * 
     * @param {number} editionId
     * @returns {Promise<object[]>}
     */
    async byEdition(editionId) {
        const spineImagesResponse = await this._query('SELECT * FROM spine_images WHERE edition_id = ?', editionId);

        return spineImagesResponse;
    }

    /**
     * Get spine images that have been crowd approved.
     * 
     * @param {number} amount
     * @param {boolean} approved
     * @returns {Promise<object[]>}
     */
    async byCrowdApproved(amount = 5, approved = true) {
        const spineImagesResponse = await this._query('SELECT si.* FROM spine_images AS si JOIN spine_images_meta AS sim WHERE name = "crowdApproved" AND value = ? AND sim.image_id = si.id ORDER BY RAND() LIMIT ?', approved, parseInt(amount));
        
        return spineImagesResponse;
    }

    /**
     * Insert a spine image.
     * 
     * @param {number} editionId
     * @param {string} filepath
     * @returns {Promise<object>}
     */
    async insert(editionId, filepath) {
        const dbInsertion = await this._query('INSERT INTO spine_images (edition_id, filepath) VALUES (?, ?)', editionId, filepath);
        if (!dbInsertion.insertId) {
            throw new Error('Failed to save spine image');
        }

        for (const key in spineImagesDefaultMeta) {
            await this._query('INSERT INTO spine_images_meta (image_id, name, value) VALUES (?, ?, ?)', dbInsertion.insertId, key, spineImagesDefaultMeta[key]);
        }

        return await this.byId(dbInsertion.insertId);
    }

    /**
     * Set a meta value for a spine image.
     * 
     * @param {number} imageId
     * @param {string} name
     * @param {any} value
     * @returns {Promise<void>}
     */
    async setMeta(imageId, name, value) {
        const existsCheck = await this._query('SELECT * FROM spine_images_meta WHERE image_id = ? AND name = ?', imageId, name);
        if (!existsCheck) {
            await this._query('INSERT INTO spine_images_meta (image_id, name, value) VALUES (?, ?, ?)', imageId, name, value);
        } else {
            await this._query('UPDATE spine_images_meta SET value = ? WHERE image_id = ? AND name = ?', value, imageId, name);
        }
    }

    /**
     * Get a meta value for a spine image.
     * 
     * @param {number} imageId
     * @param {string} name
     * @returns {Promise<any>}
     */
    async getMeta(imageId, name) {
        const meta = await this._query('SELECT * FROM spine_images_meta WHERE image_id = ? AND name = ?', imageId, name);
        if (meta.length === 0) {
            return null;
        }

        return meta[0].value;
    }

    /**
     * Delete a spine image.
     * 
     * @param {number} id
     * @returns {Promise<void>}
     */
    async delete(id) {
        await this._query('DELETE FROM spine_images WHERE id = ?', id);

        // Delete all contributions to this spine image
        await this._query('DELETE uc FROM user_contributions AS uc JOIN contribution_types AS ct WHERE uc.contribution_type_id = ct.id AND ct.code = "ADD_SPINE_IMAGE" AND uc.contribution_record_id = ?', id);
    }
}

module.exports = SpineImageController;