genediazjr/knexshelf

View on GitHub
lib/queries.js

Summary

Maintainability
D
1 day
Test Coverage
/* eslint complexity: 0 */
'use strict';

const lib = require('.');


exports.defaults = function (schema) {

    if (!lib.internals.models[schema.schema.name]) {
        lib.internals.models[schema.schema.name] = {};
    }

    const defaults = {};

    const runFixer = async (payload, opts) => {

        if (payload) {

            payload = await lib.internals.formatters.fixer(payload);
            payload = await schema.formatters.fixer(payload);

            if (opts.fixer) {
                payload = await opts.fixer(payload);
            }
        }

        return payload;
    };

    const getFormatter = (opts, method) => {

        return opts.formatter || schema.formatters[method];
    };

    const getCache = (opts) => {

        return opts.cache ||
            lib.internals.models[schema.schema.name].cache ||
            lib.internals.cache;
    };

    const props = Object.keys(schema.protoProps);
    /* $lab:coverage:off$ */
    const r1 = props.indexOf('tableName');
    if (r1 > -1) {
        props.splice(r1, 1);
    }
    /* $lab:coverage:on$ */
    const r2 = props.indexOf('withUpdatedAt');
    if (r2 > -1) {
        props.splice(r2, 1);
    }

    const r3 = props.indexOf('hasTimestamps');
    if (r3 > -1) {
        props.splice(r3, 1);
    }

    schema.references = Object.assign({}, {
        browse: props,
        obtain: props
    }, schema.references);

    schema.formatters = Object.assign({}, lib.internals.formatters, schema.formatters);

    defaults.browse = async function (query, opts) {

        query = query || {};
        opts = opts || {};

        let res;
        const formatter = getFormatter(opts, 'browse');
        let formatted = await lib.internals.formatters.browse(query, schema);
        formatted = await formatter(formatted.query, schema);
        query = formatted.query;
        query = Object.assign({ sort: 'id', order: 'asc' }, query);

        const key = `${schema.schema.name}:${
            JSON.stringify(query.perPage || '')}:${
            JSON.stringify(query.page || '')}:${
            JSON.stringify(query.custom || '')}`;

        const cache = getCache(opts);
        const cached = await cache.get(key);

        if (cached) {

            return cached;
        }

        const fetchOpts = Object.assign({
            withRelated: schema.references.browse
        }, opts);

        const fetch = schema.model.forge();

        if (!query.noSort) {
            fetch.orderBy(query.sort, query.order);
        }

        if (query.custom) {
            fetch.query(query.custom);
        }

        if (!query.all && query.perPage && query.page) {
            fetchOpts.pageSize = query.perPage;
            fetchOpts.page = query.page;

            try {
                res = await fetch.fetchPage(fetchOpts);

            }
            catch (e) {
                if (Object.keys(lib.internals.subModels).length) {
                    return await exports.defaults(lib.internals.subModels[`${schema.schema.name}`]).browse(query, opts);
                }
                throw e;
            }

        }
        else {
            try {
                res = await fetch.fetchAll(fetchOpts);
            }
            catch (e) {
                if (Object.keys(lib.internals.subModels).length) {
                    return await exports.defaults(lib.internals.subModels[`${schema.schema.name}`]).browse(query, opts);
                }
                throw e;
            }
        }

        const result = {
            payload: res.toJSON(),
            pagination: res.pagination || {
                page: 0,
                pageSize: 0,
                rowCount: 0,
                pageCount: 0
            }
        };

        const furnished = [];
        for (let r = 0; r < result.payload.length; ++r) {
            const fixres = await runFixer(result.payload[r], opts);
            if (fixres) {
                furnished.push(fixres);
            }
        }

        result.payload = furnished;

        await cache.set(key, result);

        return result;
    };

    defaults.obtain = async function (params, opts) {

        if (!params) {

            return null;
        }

        opts = opts || {};

        let res;
        const formatter = getFormatter(opts, 'obtain');
        let formatted = await lib.internals.formatters.obtain(params, schema);
        formatted = await formatter(formatted.params, schema);
        params = formatted.params;

        const key = `${schema.schema.name}:${JSON.stringify(params)}`;
        const cache = getCache(opts);
        const cached = await cache.get(key);

        if (cached) {

            return cached;
        }
        try {
            res = await schema.model
                .where(params)
                .fetchAll(Object.assign({
                    withRelated: schema.references.obtain
                }, opts));
        }
        catch (e) {
            if (Object.keys(lib.internals.subModels).length) {
                return await exports.defaults(lib.internals.subModels[`${schema.schema.name}`]).obtain(params, opts);
            }
            throw e;
        }

        res = res.toJSON();

        if (res.length > 1) {

            throw new Error('Obtain params found more than one row. Use browse instead');
        }

        const result = await runFixer(res[0] || null, opts);

        await cache.set(key, result);

        return result;
    };

    defaults.create = async function (payload, opts) {

        payload = payload || {};
        opts = opts || {};

        const formatter = getFormatter(opts, 'create');
        let formatted = await lib.internals.formatters.create(payload, schema);
        formatted = await formatter(formatted.payload, schema);

        const res = await schema.model
            .forge(formatted.payload)
            .save(null, opts);

        const result = res.toJSON();
        await lib.internals.broadcast(schema.schema.name, {
            method: 'create',
            payload: result
        });

        return result;
    };

    defaults.update = async function (params, payload, opts) {

        params = params || {};
        payload = payload || {};
        opts = opts || {};

        let res;
        const formatter = getFormatter(opts, 'update');
        let formatted = await lib.internals.formatters.update(payload, params, schema);
        formatted = await formatter(formatted.payload, formatted.params, schema);
        params = formatted.params;
        payload = formatted.payload;

        const key = `${schema.schema.name}:${JSON.stringify(params)}`;
        const cache = getCache(opts);
        await cache.del(key);

        if (!params.id &&
            Object.keys(params).length) {

            if (opts.multiple) {

                res = await schema.model
                    .where(params)
                    .save(payload, Object.assign({
                        method: 'update',
                        patch: true
                    }, opts));

                const result = res.toJSON();
                await lib.internals.broadcast(schema.schema.name, {
                    method: 'update',
                    payload: result
                });

                return result;
            }

            res = await schema.model
                .where(params)
                .fetchAll();

            res = res.toJSON();

            if (res.length > 1) {

                throw new Error('Update params found more than one row. Use multiple option');
            }

            /* $lab:coverage:off$ */
            params = { id: res[0] && res[0].id || 0 };
            /* $lab:coverage:on$ */
        }

        if (!Object.keys(params).length) {
            params = { id: 0 };
        }

        res = await schema.model
            .forge(params)
            .save(payload, Object.assign({
                patch: true
            }, opts));

        const result = res.toJSON();
        await lib.internals.broadcast(schema.schema.name, {
            method: 'update',
            payload: result
        });

        return result;
    };

    defaults.delete = async function (params, opts) {

        params = params || {};
        opts = opts || {};

        let res;
        const formatter = getFormatter(opts, 'delete');
        let formatted = await lib.internals.formatters.delete(params, schema);
        formatted = await formatter(formatted.params, schema);
        params = formatted.params;

        const key = `${schema.schema.name}:${JSON.stringify(params)}`;
        const cache = getCache(opts);
        await cache.del(key);

        if (!params.id &&
            Object.keys(params).length) {

            if (opts.multiple) {

                res = await schema.model
                    .query((qb) => {

                        qb.where(params);
                    }).destroy(opts);

                const result = res.toJSON();
                await lib.internals.broadcast(schema.schema.name, {
                    method: 'delete',
                    id: result.id
                });

                return result;
            }

            res = await schema.model
                .where(params)
                .fetchAll();

            res = res.toJSON();

            if (res.length > 1) {

                throw new Error('Delete params found more than one row. Use multiple option');
            }

            /* $lab:coverage:off$ */
            params = { id: res[0] && res[0].id || 0 };
            /* $lab:coverage:on$ */
        }

        if (!Object.keys(params).length) {
            params = { id: 0 };
        }

        res = await schema.model
            .forge(params)
            .destroy(opts);

        const result = res.toJSON();
        await lib.internals.broadcast(schema.schema.name, {
            method: 'delete',
            id: result.id
        });

        return result;
    };

    defaults.scrimp = async function (payload, opts) {

        payload = payload || {};
        opts = opts || {};

        const formatter = getFormatter(opts, 'scrimp');
        let formatted = await lib.internals.formatters.scrimp(payload, schema);
        formatted = await formatter(formatted.payload, schema);

        if (formatted.payload.id) {

            return await defaults.update({ id: formatted.payload.id }, formatted.payload, opts);
        }

        return await defaults.create(formatted.payload, opts);
    };

    return defaults;
};