JavaScript Extension Development API for Oracle Visual Builder Cloud Service - Classic Applications

Source: bop/js/api/resource/Resource.js

define(['core/js/api/utils/StringUtils'], function (StringUtils) {

    'use strict';

    /**
     * An object representing a single Resource so that it is possible to relate
     * a given REST request to a particularly entity in the model. For example
     * this is used by the {@link bop/js/spi/operation/BOPAuthenticator BOPAuthenticator}
     * on the server side to provide a whitelist of all resources exposed by
     * a particular BOP.
     *
     * @version 17.1.1
     * @AbcsExtension stable
     * @exports bop/js/api/resource/Resource
     *
     * @constructor
     * @private
     **/
    var Resource = function (data) {

        this._id = data.id;
        this._template = data.template;
        this._entityId = data.entity;
        this._parent_resource = data.parent_resource;
        this._headers = data.headers;
    };

    /**
     * @returns {String} The id if this resource
     */
    Resource.prototype.getId = function () {
        return this._id;
    };

    /**
     * @returns {String} The URI template for this resource relative to the
     *   root of the service
     */
    Resource.prototype.getTemplate = function () {
        return this._template;
    };

    /**
     * @returns {String} The entity that is represented by this resource
     */
    Resource.prototype.getEntityId = function () {
        return this._entityId;
    };

    /**
     * @returns {String} The parent resource, normally a collection of self
     */
    Resource.prototype.getParentResourceId = function () {
        return this._parent_resource;
    };

    /**
     * Returns the object that specifies the headers that can be sent to
     * the REST API. The keys of the object are the name of the headers
     * (in lowercase) and the value of each key is the expected value for
     * the header, which can be expressed either has a string or as a
     * regular expression. Headers that don't match this description
     * are not sent to the REST API.
     *
     * @returns {Object} Undefined or the object that specifies the headers that can be
     *   sent to the REST API. The keys of the object are the name of the headers
     *   and the value of each key is the expected value for
     *   the header, which can be expressed either has a string or as a
     *   regular expression.
     *
     * @example
     * <caption>
     *  Headers can be specified during the resouce creation. This example shows how to
     *  specify 3 headers that can be sent to the REST API: 'h1' whose value
     *  is the exact string 'http://www.example.com?summary', 'h2' whose value can be either
     *  'test' or 'production', and 'h3' whose value is a phone number that matches the specified
     *  regular expression.
     * </caption>
     *
     * var parent = Resource.create({
     *      id: 'employee_collection',
     *      template: '/employee',
     *      entity: 'my.custom.bop.Employee',
     *      headers: {
     *        h1: 'http://www.example.com?summary',
     *        h2: /test|production/,
     *        h3: /(?:\d{3}|\(\d{3}\))([-\/\.])\d{3}\1\d{4}/
     *      }
     *  });
     *
     */
    Resource.prototype.getHeaders = function () {
        return this._headers;
    };

    /**
     * An internal method to validate the basic data object.
     *
     * @param {type} data
     */
    Resource._validateData = function (data) {
        var mandatoryFields = [
            'id',
            'template',
            'entity'
        ];
        var supportedFields = mandatoryFields.concat([
            'headers'
        ]);
        AbcsLib.checkObjectLiteral(data, supportedFields, mandatoryFields);
    };

    /**
     * A internal version of the factory method that doesn't check
     * constrain the contents of data so that the nornal and child methods
     * can pass in slightly different information
     */
    Resource.createIntl = function (data) {

        AbcsLib.checkDefined(data.id, 'data.id');
        AbcsLib.checkDataType(data.id, AbcsLib.Type.STRING);
        AbcsLib.checkDefined(data.template, 'data.template');
        AbcsLib.checkDataType(data.template, AbcsLib.Type.STRING);
        AbcsLib.checkDefined(data.entity, 'data.entity');
        AbcsLib.checkDataType(data.entity, AbcsLib.Type.STRING);

        if (AbcsLib.isDefined(data.headers)) {
            AbcsLib.checkDataType(data.headers, AbcsLib.Type.OBJECT);
            var headerNames = Object.keys(data.headers);
            if (headerNames.length === 0) {
                throw new Error('The "headers" object defined at least one header and value expression.');
            }

            headerNames.forEach(function (headerName) {
                var headerValue = data.headers[headerName];
                if (!AbcsLib.isRegExp(headerValue) && !AbcsLib.isString(headerValue)) {
                    throw new Error('The value of a header must be either a regular expression or a string');
                }
            });
        }

        return new Resource(data);
    };

    /**
     * A factory method that creates a resource.
     *
     * @version 17.1.1
     * @AbcsExtension stable
     *
     * @param {Object} data - An Object literal with following properties.
     * @param {String} data.id - Unique ID of the created  {@link bop/js/api/resource/Resource Resource}.
     * @param {String} data.template - The URI template to use for this resource, will be appended to the baseUri parameter if the BOP exports {@link extensions.dt/js/api/CustomParameter CustomParameter} so naned.
     * @param {String} data.entity - Unique ID of the referenced {@link entity/js/api/Entity Entity}.
     * @returns {bop/js/api/resource/Resource} A new resource based on the passed in data
     *
     * @example
     * <caption>
     *  Example of typical implementation a typical resource creating, providing
     *  the required attributes for id, template and entity id.
     * </caption>
     *
     * var parent = Resource.create({
     *      id: 'employee_collection',
     *      template: '/employee',
     *      entity: 'my.custom.bop.Employee'
     *  });
     */
    Resource.create = function (data) {
        AbcsLib.checkParameterCount(arguments, 1);
        Resource._validateData(data);
        return Resource.createIntl(data);
    };

    /**
     * A factory  method that creates a resource relative to the parent resource.
     *
     * @version 17.1.1
     * @AbcsExtension stable
     *
     * @param {bop/js/api/resource/Resource} parent The parent resource to create the resource relative to
     * @param {Object} data - An Object literal with following properties.
     * @param {String} data.id - Unique ID of the created  {@link bop/js/api/resource/Resource Resource}.
     * @param {String} data.template - The URI template to use for this resource that is appended to the template for any parent resource.
     * @param {String} [data.entity] - Override the entity id of the parent resource.
     * @returns {bop/js/api/resource/Resource} A new resource based on the passed in data
     *
     * @example
     * <caption>
     *  Example of typical implementation a typical child resource created
     *  relative to a parent resource.
     * </caption>
     *
     * var parent = Resource.create({
     *      id: 'employee_collection',
     *      template: '/employee',
     *      entity: 'my.custom.bop.Employee'
     *  });
     *  var child = Resource.createChild(parent, {
     *      id: 'employee_instance',
     *      template: '{id}'
     *  });
     */
    Resource.createChild = function (parent, data) {

        AbcsLib.checkParameterCount(arguments, 2);
        AbcsLib.checkDefined(parent, 'parent');
        AbcsLib.checkDataType(parent, Resource);

        if (AbcsLib.isObject(data) && !data.entity) {
            data.entity = parent.getEntityId();
        }
        Resource._validateData(data);

        var parentTemplate = parent.getTemplate();
        // No endsWith, startsWith in Node 4.5?
        if (StringUtils.endsWith(parentTemplate, '/')) {
            if (StringUtils.startsWith(data.template, '/')) {
                data.template = parentTemplate + data.template.substring(1);
            } else {
                data.template = parentTemplate + data.template;
            }
        } else {
            if (StringUtils.startsWith(data.template, '/')) {
                data.template = parentTemplate + data.template;
            } else {
                data.template = parentTemplate + '/' + data.template;
            }
        }

        //
        data.parent_resource = parent.getId();

        return Resource.createIntl(data);
    };

    return Resource;
});