lang/View.js

const { _ } = require('@kitmi/utils');
const { generateDisplayName, deepCloneField, Clonable } = require('./XemlUtils');

const Dataset = require('./Dataset');

/**
 * Geml view class.
 * @class View
 */
class View extends Clonable {
    isList = false;

    /**
     * @param {Linker} linker
     * @param {string} name - View name
     * @param {object} xemlModule - Source ool module
     * @param {object} info - View info
     */
    constructor(linker, name, xemlModule, info) {
        super();

        /**
         * Linker to process this view
         * @member {Linker}
         */
        this.linker = linker;

        /**
         * Name of this view
         * @member {string}
         */
        this.name = name;

        /**
         * Owner geml module
         * @member {object}
         */
        this.xemlModule = xemlModule;

        /**
         * Raw metadata
         * @member {Object}
         */
        this.info = info;
    }

    /**
     * Start linking this view
     * @returns {View}
     */
    link() {
        pre: !this.linked;

        if (this.info.dataset) {
            this.dataset = this.linker.loadDoc(this.xemlModule, this.info.dataset);
        } else {
            assert: this.info.entity, 'Invalid view syntax!';

            let mainEntity = this.linker.getReferencedEntity(this.xemlModule, this.info.entity);

            this.dataset = new Dataset(this.linker, mainEntity.name, this.xemlModule, { mainEntity: mainEntity.name });
            this.dataset.link();
        }

        if (this.info.isList) {
            this.isList = true;
        }

        if (!_.isEmpty(this.info.accept)) {
            this.params = this.info.accept.concat();
        }

        if (!_.isEmpty(this.info.selectBy)) {
            this.selectBy = this.info.selectBy.concat();
        }

        if (!_.isEmpty(this.info.groupBy)) {
            this.groupBy = this.info.groupBy.concat();
        }

        if (!_.isEmpty(this.info.orderBy)) {
            this.orderBy = this.info.orderBy.concat();
        }

        if (this.info.skip) {
            this.skip = _.isPlainObject(this.info.skip) ? Object.assign({}, this.info.skip) : this.info.skip;
        }

        if (this.info.limit) {
            this.limit = _.isPlainObject(this.info.limit) ? Object.assign({}, this.info.limit) : this.info.limit;
        }

        this.linked = true;

        return this;
    }

    inferTypeInfo(inSchema) {
        if (!_.isEmpty(this.params)) {
            let inferredParams = [];

            this.params.forEach((param) => {
                if (GemlUtils.isMemberAccess(param.type)) {
                    let [entityName, fieldName] = param.type.split('.');

                    if (!inSchema.hasEntity(entityName)) {
                        throw new Error(
                            `Parameter "${param.name}" references to an entity "${entityName}" which is not belong to the schema.`
                        );
                    }

                    let entity = inSchema.entities[entityName];
                    //console.dir(entity.toJSON(), {depth: 8, colors: true});

                    let field = entity.getEntityAttribute(fieldName);
                    inferredParams.push(
                        Object.assign(_.omit(_.toPlainObject(field), ['isReference', 'optional', 'displayName']), {
                            name: param.name,
                        })
                    );
                } else {
                    const [typeInfo, baseInfo] = this.linker.trackBackType(this.xemlModule, param);
                    inferredParams.push(typeInfo);
                }
            });

            this.params = inferredParams;
        }
    }

    getDocumentHierarchy(inSchema) {
        return inSchema.getDocumentHierachy(this.xemlModule, this.dataset.name);
    }

    /**
     * Clone the view
     * @returns {View}
     */
    clone() {
        super.clone();

        let view = new View(this.linker, this.name, this.xemlModule, this.info);

        deepCloneField(this, view, 'dataset');
        deepCloneField(this, view, 'params');
        deepCloneField(this, view, 'selectBy');
        deepCloneField(this, view, 'groupBy');
        deepCloneField(this, view, 'orderBy');
        deepCloneField(this, view, 'skip');
        deepCloneField(this, view, 'limit');

        view.isList = this.isList;
        view.linked = true;

        return view;
    }

    /**
     * Translate the view into a plain JSON object
     * @returns {object}
     */
    toJSON() {
        return {
            name: this.name,
            dataset: this.dataset.toJSON(),
            isList: this.isList,
            params: this.params,
            selectBy: this.selectBy,
            groupBy: this.groupBy,
            orderBy: this.orderBy,
            skip: this.skip,
            limit: this.limit,
        };
    }
}

module.exports = View;