objection.js icon indicating copy to clipboard operation
objection.js copied to clipboard

Added $formatJson options that are passed to children/relations

Open amit-meshbey opened this issue 3 years ago • 4 comments

PR for https://github.com/Vincit/objection.js/issues/1374

amit-meshbey avatar Apr 11 '21 07:04 amit-meshbey

Coverage Status

Coverage increased (+0.002%) to 97.001% when pulling c92ff221a611d8a934af19183b964d455e2cfed9 on amit-meshbey:master into 260b284a1cbfb044991894c5a3cf3dedc8ce7267 on Vincit:master.

coveralls avatar Apr 11 '21 07:04 coveralls

@koskimas any chance you can have a look at it?

It can really help my project as well.

adamgen avatar Sep 22 '22 11:09 adamgen

@koskimas is this something that can be merged soon?

oxodesign avatar Oct 23 '22 11:10 oxodesign

@oxodesign in case it helps in our project we monkey patched it by requiring this file:

// objection.patch.js
/**
 * This file monkey patches the objection toJson method to pass down the options to the $formatJson.
 * Can be closed when this PR is merged https://github.com/Vincit/objection.js/pull/2033
 */
"use strict";
const EMPTY_ARRAY = [];

const { isInternalProp } = require("objection/lib/utils/internalPropUtils");
const { isObject, cloneDeep, isFunction } = require("objection/lib/utils/objectUtils");
const modelToJson = require("objection/lib/model/modelToJson.js");

modelToJson.toJson = function toJson(model, optIn) {
    const modelClass = model.constructor;

    const opt = {
        virtuals: getVirtuals(optIn),
        shallow: isShallow(optIn),
        omit: getOmit(optIn, modelClass),
        pick: null,
        omitFromJson: model.$omitFromJson() || null,
        cloneObjects: modelClass.cloneObjectAttributes,
        format: optIn && optIn.format
    };

    let json = toExternalJsonImpl(model, opt);
    json = model.$formatJson(json, opt.format);

    return json;
};

function getVirtuals(opt) {
    if (!opt) {
        return true;
    } else if (Array.isArray(opt.virtuals)) {
        return opt.virtuals;
    } else {
        return opt.virtuals !== false;
    }
}

function isShallow(opt) {
    return !!opt && !!opt.shallow;
}

function getOmit(opt, modelClass) {
    return isShallow(opt) ? modelClass.getRelationNames() : null;
}

function toExternalJsonImpl(model, opt) {
    const json = {};
    const keys = Object.keys(model);
    const vAttr = getVirtualAttributes(model, opt);

    for (let i = 0, l = keys.length; i < l; ++i) {
        const key = keys[i];
        const value = model[key];

        assignJsonValue(json, key, value, opt);
    }

    if (vAttr.length !== 0) {
        assignVirtualAttributes(json, model, vAttr, opt);
    }

    return json;
}

function getVirtualAttributes(model, opt) {
    if (Array.isArray(opt.virtuals)) {
        return opt.virtuals;
    } else if (opt.virtuals === true) {
        return model.constructor.getVirtualAttributes();
    } else {
        return EMPTY_ARRAY;
    }
}

function assignJsonValue(json, key, value, opt) {
    const type = typeof value;

    if (type !== "function" && type !== "undefined" && !isInternalProp(key) && !shouldOmit(opt, key) && shouldPick(opt, key)) {
        if (isObject(value)) {
            json[key] = toJsonObject(value, opt);
        } else {
            json[key] = value;
        }
    }
}

function shouldOmit(opt, key) {
    return (opt.omit !== null && opt.omit.includes(key)) || (opt.omitFromJson !== null && opt.omitFromJson.includes(key));
}

function shouldPick(opt, key) {
    return opt.pick === null || key in opt.pick;
}

function assignVirtualAttributes(json, model, vAttr, opt) {
    for (let i = 0, l = vAttr.length; i < l; ++i) {
        const key = vAttr[i];
        let value = model[key];

        if (isFunction(value)) {
            value = value.call(model);
        }

        assignJsonValue(json, key, value, opt);
    }
}

function toJsonObject(value, opt) {
    if (Array.isArray(value)) {
        return toJsonArray(value, opt);
    } else if (value.$isObjectionModel) {
        // No branch for $toDatabaseJson here since there is never a need
        // to have nested models in database rows.
        return value.$toJson(opt);
    } else if (Buffer.isBuffer(value)) {
        return value;
    } else if (opt.cloneObjects) {
        return cloneDeep(value);
    } else {
        return value;
    }
}

function toJsonArray(value, opt) {
    const ret = new Array(value.length);

    for (let i = 0, l = ret.length; i < l; ++i) {
        const item = value[i];

        if (isObject(item)) {
            ret[i] = toJsonObject(item, opt);
        } else {
            ret[i] = item;
        }
    }

    return ret;
}

adamgen avatar Oct 23 '22 18:10 adamgen