lib/request-resolver.js
function getAttribute(path, attrNode, context) {
    path.forEach((attributeName, i) => {
        if (!(attrNode.attributes && attrNode.attributes[attributeName])) {
            if (attrNode._origNodes) {
                let subAttrNode = null;
                attrNode._origNodes.forEach((origNode, inheritDepth) => {
                    if (!origNode || !origNode.attributes || !origNode.attributes[attributeName]) return;

                    let origSubAttrNode = origNode.attributes[attributeName];

                    if (subAttrNode) {
                        if (subAttrNode.inherit === 'inherit') {
                            // just add/merge options from sub-resource below
                        } else if (subAttrNode.inherit === 'replace') {
                            return; // just ignore options from sub-resource
                        } else {
                            let attrPath = context.attrPath.join('.');
                            throw new ImplementationError(
                                `Cannot overwrite attribute "${attributeName}" in "${attrPath}" (maybe use "inherit"?)`
                            );
                        }
                    } else {
                        subAttrNode = {};
                    }

                    Object.keys(origSubAttrNode).forEach(optionName => {
                        if (subAttrNode.hasOwnProperty(optionName)) return; // for inherit

                        if (optionName === 'attributes') {
                            subAttrNode[optionName] = {};
                        } else if (optionName === 'dataSources') {
                            // DataSources are handled/cloned later in resolveResourceTree():
                            subAttrNode[optionName] = origSubAttrNode[optionName];
                        } else if (typeof origSubAttrNode[optionName] === 'object') {
                            subAttrNode[optionName] = cloneDeep(origSubAttrNode[optionName]);
                        } else {
                            subAttrNode[optionName] = origSubAttrNode[optionName];
                        }
                    });

                    // keep the inherit-depth (array length) from parent:
                    subAttrNode._origNodes = subAttrNode._origNodes || Array(attrNode._origNodes.length);
                    subAttrNode._origNodes[inheritDepth] = origSubAttrNode;

                    attrNode.attributes[attributeName] = subAttrNode;
                });
            }

            if (!(attrNode.attributes && attrNode.attributes[attributeName])) {
                throw new RequestError(
                    'Unknown attribute ' + `"${context.attrPath.concat(path.slice(0, i + 1)).join('.')}"`
                );
            }
        }

        attrNode = attrNode.attributes[attributeName];

        if (attrNode.resource) {
            const subContext = Object.assign({}, context);
            subContext.attrPath = subContext.attrPath.concat(path.slice(0, i + 1));
            resolveIncludes(attrNode, subContext);
        }
    });

    return attrNode;
}
function getAttribute(path, attrNode, context) {
    path.forEach((attributeName, i) => {
        if (!(attrNode.attributes && attrNode.attributes[attributeName])) {
            if (attrNode._origNodes) {
                let subAttrNode = null;
                attrNode._origNodes.forEach((origNode, inheritDepth) => {
                    if (!origNode || !origNode.attributes || !origNode.attributes[attributeName]) return;

                    let origSubAttrNode = origNode.attributes[attributeName];

                    if (subAttrNode) {
                        if (subAttrNode.inherit === 'inherit') {
                            // just add/merge options from sub-resource below
                        } else if (subAttrNode.inherit === 'replace') {
                            return; // just ignore options from sub-resource
                        } else {
                            let attrPath = context.attrPath.join('.');
                            throw new ImplementationError(
                                `Cannot overwrite attribute "${attributeName}" in "${attrPath}" (maybe use "inherit"?)`
                            );
                        }
                    } else {
                        subAttrNode = {};
                    }

                    Object.keys(origSubAttrNode).forEach(optionName => {
                        if (subAttrNode.hasOwnProperty(optionName)) return; // for inherit

                        if (optionName === 'attributes') {
                            subAttrNode[optionName] = {};
                        } else if (optionName === 'dataSources') {
                            // DataSources are handled/cloned later in resolveResourceTree():
                            subAttrNode[optionName] = origSubAttrNode[optionName];
                        } else if (typeof origSubAttrNode[optionName] === 'object') {
                            subAttrNode[optionName] = cloneDeep(origSubAttrNode[optionName]);
                        } else {
                            subAttrNode[optionName] = origSubAttrNode[optionName];
                        }
                    });

                    // keep the inherit-depth (array length) from parent:
                    subAttrNode._origNodes = subAttrNode._origNodes || Array(attrNode._origNodes.length);
                    subAttrNode._origNodes[inheritDepth] = origSubAttrNode;

                    attrNode.attributes[attributeName] = subAttrNode;
                });
            }

            if (!(attrNode.attributes && attrNode.attributes[attributeName])) {
                throw new RequestError(
                    'Unknown attribute ' + `"${context.attrPath.concat(path.slice(0, i + 1)).join('.')}"`
                );
            }
        }

        attrNode = attrNode.attributes[attributeName];

        if (attrNode.resource) {
            const subContext = Object.assign({}, context);
            subContext.attrPath = subContext.attrPath.concat(path.slice(0, i + 1));
            resolveIncludes(attrNode, subContext);
        }
    });

    return attrNode;
}
function getAttribute ( path , attrNode , context ) { path . forEach ( ( attributeName , i ) => { if ( ! ( attrNode . attributes && attrNode . attributes [ attributeName ] ) ) { if ( attrNode . _origNodes ) { let subAttrNode = null ; attrNode . _origNodes . forEach ( ( origNode , inheritDepth ) => { if ( ! origNode || ! origNode . attributes || ! origNode . attributes [ attributeName ] ) return ; let origSubAttrNode = origNode . attributes [ attributeName ] ; if ( subAttrNode ) { if ( subAttrNode . inherit === 'inherit' ) { // just add/merge options from sub-resource below } else if ( subAttrNode . inherit === 'replace' ) { return ; // just ignore options from sub-resource } else { let attrPath = context . attrPath . join ( '.' ) ; throw new ImplementationError ( ` ${ attributeName } ${ attrPath } ` ) ; } } else { subAttrNode = { } ; } Object . keys ( origSubAttrNode ) . forEach ( optionName => { if ( subAttrNode . hasOwnProperty ( optionName ) ) return ; // for inherit if ( optionName === 'attributes' ) { subAttrNode [ optionName ] = { } ; } else if ( optionName === 'dataSources' ) { // DataSources are handled/cloned later in resolveResourceTree(): subAttrNode [ optionName ] = origSubAttrNode [ optionName ] ; } else if ( typeof origSubAttrNode [ optionName ] === 'object' ) { subAttrNode [ optionName ] = cloneDeep ( origSubAttrNode [ optionName ] ) ; } else { subAttrNode [ optionName ] = origSubAttrNode [ optionName ] ; } } ) ; // keep the inherit-depth (array length) from parent: subAttrNode . _origNodes = subAttrNode . _origNodes || Array ( attrNode . _origNodes . length ) ; subAttrNode . _origNodes [ inheritDepth ] = origSubAttrNode ; attrNode . attributes [ attributeName ] = subAttrNode ; } ) ; } if ( ! ( attrNode . attributes && attrNode . attributes [ attributeName ] ) ) { throw new RequestError ( 'Unknown attribute ' + ` ${ context . attrPath . concat ( path . slice ( 0 , i + 1 ) ) . join ( '.' ) } ` ) ; } } attrNode = attrNode . attributes [ attributeName ] ; if ( attrNode . resource ) { const subContext = Object . assign ( { } , context ) ; subContext . attrPath = subContext . attrPath . concat ( path . slice ( 0 , i + 1 ) ) ; resolveIncludes ( attrNode , subContext ) ; } } ) ; return attrNode ; }
Resolve attribute path relative to attrNode, handle included resources
and return child attrNode

@param path - Array of attribute-names representing the path
@param attrNode - Root node where to start resolving
@param context - "Global" things and context for better error-handling
@private
Resolve attribute path relative to attrNode handle included resources and return child attrNode