crud/CrudController.js

/**
 * @module flitter-crud/CrudController
 */

const status = require('http-status')
const Controller = require('libflitter/controller/Controller')
const ImplementationError = require('libflitter/errors/ImplementationError')

/**
 * A controller with built-in create, read, update, delete methods that references a specific model.
 * @extends module:libflitter/controller/Controller
 */
class CrudController extends Controller {
    /**
     * Defines the services required by this controller.
     * @returns {Array<string>}
     */
    static get services() {
        return [...super.services, 'models', 'forms']
    }

    /**
     * Get the name of the resource managed by this controller. Should be a Flitter canonical name.
     * @returns {string|null} null
     */
    name(){
        throw new ImplementationError()
    }

    /**
     * Get the registered model CLASS with the name of this controller's resource.
     * @returns {module:flitter-orm/src/model/Model~Model}
     */
    model(){
        return this.models.get(this.name())
    }

    /**
     * Get the registered validator with the name of this controller's resource.
     * @returns {module:flitter-forms/Validator~Validator}
     */
    form(){
        return this.forms.get(this.name())
    }

    /**
     * Send a JSON formatted, API-style response.
     * @param {Express/Response} res - the Express response
     * @param {object} [body = {}] - the response information. Accepts the following fields, optionally: "status", "message", and "data"
     */
    send(res, body = {}){
        const code = body.status ? body.status : 200
        res.status(code)
        res.send({
            status: code,
            message: body.messageTest ? body.message : status[code],
            data: body.data ? body.data : {}
        })
    }

    /**
     * Create a new instance of the resource managed by this controller. Input taken from req.body is validated
     * before the resource is created.
     * @param {Express/Request} req - the Express request
     * @param {Express/Response} res - the Express response
     * @param {function} next - the error handler
     */
    create(req, res, next){
        this.form().validate(req.body, (failed, data) => {
            if ( failed ){
                this.send(res, {
                    status: 400,
                    message: "Invalid input for model: "+this.name(),
                    data
                })
            }
            else {
                console.log('validation success!')
                const instance = new (this.model())(data)
                instance.save().then(() => {
                    this.send(res, {data: instance})
                })
            }
        })
    }

    /**
     * Retrieve an instance of the resource managed by this controller with the ID "req.params.id".
     * @param {Express/Request} req - the Express request
     * @param {Express/Response} res - the Express response
     * @param {function} next - the error handler
     */
    read(req, res, next){
        const id = req.params.id
        this.model().findById(id, (error, instance) => {
            if ( error ){
                this.send(res, {
                    status: 500,
                    data:error,
                })
            }
            else if ( instance ){
                this.send(res, {data:instance})
            }
            else {
                this.send(res, {status:404})
            }
        })
    }

    /**
     * Delete an instance of the resource managed by this controller with the ID "req.params.id".
     * @param {Express/Request} req - the Express request
     * @param {Express/Response} res - the Express response
     * @param {function} next - the error handler
     */
    delete(req, res, next){
        const id = req.params.id
        this.model().findById(id, (error, instance) => {
            if ( error ){
                this.send(res, {
                    status: 500,
                    data:error,
                })
            }
            else if ( instance ){
                instance.delete().then(() => {
                    this.send(res, {status:200, message: "Deleted"})
                })
            }
            else {
                this.send(res, {status:404})
            }
        })
    }

    /**
     * Update an instance of the resource managed by this controller with the ID "req.params.id". Any input taken from
     * req.body is validated before updating.
     * @param {Express/Request} req - the Express request
     * @param {Express/Response} res - the Express response
     * @param {function} next - the error handler
     */
    update(req, res, next){
        const id = req.params.id
        this.model().findById(id, (error, instance) => {
            if ( error ) this.send(res, {status:500, data:error})
            else if ( !instance ) this.send(res, {status:404})
            else {
                this.form().validate({...instance.toObject(), ...req.body}, (failed, data) => {
                    if ( failed ) this.send(res, {status:400, message:"Invalid input data to update "+this.name(), data})
                    else {
                        instance.update(data).then(() => {
                            this.send(res, {message: "Updated"})
                        })
                    }
                })
            }
        })
    }
}

module.exports = exports = CrudController