Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added $formatJson options that are passed to children/relations #2033

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

amit-meshbey
Copy link

PR for #1374

@amit-meshbey amit-meshbey marked this pull request as ready for review April 11, 2021 07:31
@coveralls
Copy link

Coverage Status

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

@adamgen
Copy link

adamgen commented Sep 22, 2022

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

It can really help my project as well.

@oxodesign
Copy link

@koskimas is this something that can be merged soon?

@adamgen
Copy link

adamgen commented Oct 23, 2022

@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;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants