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

Source: viewmodel/js/api/context/ContextualData.js

/*global define*/

define([
    'pages/js/api/NavigationProvider',
    'viewmodel/js/api/DataSemanticsType',
    'viewmodel/js/api/context/BookmarkData',
    'viewmodel/js/api/context/EntityData',
    'viewmodel/js/api/context/NavigationContext',
    'viewmodel/js/api/context/QueryData'
], function(
        NavigationProvider,
        DataSemanticsType,
        BookmarkData,
        EntityData,
        NavigationContext,
        QueryData) {
    'use strict';

    /**
     * Representation of contextual data which can be exchanged between pages
     * during page to page navigation.
     *
     * <p>Contextual data is typically pair of some data and semantic type of that
     * data. Optionally it can contain also data identifing the calling page.</p>
     *
     * <p>You will need to create <code>ContextualData</code> when using the {@link module:api/js/Pages Navigation API}
     * as most of the API methods accept its instance. To create an instance use
     * static factory methods <code>ContextualData</code> provides:</p>
     *
     * <dl>
     * <dt>{@link viewmodel/js/api/context/ContextualData.createBlankPageContext ContextualData.createBlankPageContext}</dt>
     * <dd>to create a blank context. You usually open ABCS landing pages with
     * this context.</dd>
     *
     * <dt>{@link viewmodel/js/api/context/ContextualData.createStartWithBlankRecordContext ContextualData.createStartWithBlankRecordContext}</dt>
     * <dd>to create a context bound to an entity and possibly initialized with
     * a partial and incomplete data. You usually open ABCS create pages with
     * this context.</dd>
     *
     * <dt>{@link viewmodel/js/api/context/ContextualData.createRecordToEditContext ContextualData.createRecordToEditContext}</dt>
     * <dd>to create a context bound to an entity and initialized with record data.
     * You usually open ABCS edit and detail pages with this context.</dd>
     *
     * </dl>
     *
     * @AbcsAPI stable
     * @version 16.3.5
     * @exports viewmodel/js/api/context/ContextualData
     * @constructor
     * @private
     *
     * @param {viewmodel/js/api/Record} data record data
     * @param {viewmodel/js/api/DataSemanticsType} [semanticType=DataSemanticsType.NONE]
     * @param {viewmodel/js/api/context/NavigationContext} [navigationContext] navigation context, if no context
     *                                                     provided there will be used navigation context refering
     *                                                     the current active page
     */
    /*
     * <dt>{@link viewmodel/js/api/context/ContextualData.createRecordWasChangedContext ContextualData.createRecordWasChangedContext}</dt>
     * <dd>to create a context bound to an entity and initialized with updated record data.
     * Use this context when navigating back to the previous page using {@link module:api/js/Pages.returnToPage Pages.returnToPage}.</dd>
     */
    var ContextualData = function(data, semanticType, navigationContext) {
        AbcsLib.checkThis(this);

        this.data = data;
        this.semanticType = semanticType ? semanticType : DataSemanticsType.NONE;
        this.navigationContext = navigationContext || ContextualData._createNavigationContext();
    };

    /**
     * Create contextual data representing a record which can be edited.
     *
     * @AbcsAPI stable
     * @version 16.3.5
     * @param {Object} input
     * @param {String} input.entityId - id of the entity which data are being passed
     * @param {viewmodel/js/api/Record} input.data - record data
     * @param {String} [input.caller=activePageId] - id of the page which page navigation should allow to
     * return to; optional parameter which fallbacks to <code>activePageId</code>
     * as the id of currently open page.
     * @returns {viewmodel/js/api/context/ContextualData}
     *
     * @example
     * <caption>
     * Get data of currently selected customer from a table and annotate the data
     * with their type (that is 'customer' business object), with their semantic
     * (that is values of an existing single record of the given business
     * object are available and can be manipulated), with source where the
     * data come from (that is current page). Send such data to a
     * different page and let it do something useful with them.
     * </caption>
     * var customerToEdit = ContextualData.createRecordToEditContext({
     *      // what business object are these data of:
     *      entityId: 'customer',
     *      // customersListArchetype is instance of ListArchetype which
     *      // provides selected customer row
     *      data: customerListArchetype.getSelectedRecord(),
     *      // current page where this data and request originated;
     *      // by default replaced with the activePageId, if 'caller' not set
     *      caller: 'customerListPage'});
     * // navigate to the page
     * Abcs.Pages().navigateToPage('customerEditPage', customerToEdit);
     */
    ContextualData.createRecordToEditContext = function(input) {
        AbcsLib.checkParameterCount(arguments, 1);
        AbcsLib.checkDefined(input, 'input');
        AbcsLib.checkObjectLiteral(input, ['entityId', 'data', 'caller']);
        return new ContextualData(
                new EntityData(input.entityId, input.data),
                DataSemanticsType.RECORD_TO_EDIT,
                ContextualData._createNavigationContext(input.caller));
    };

    /**
     * Create contextual data representing a record which was just created or
     * updated.
     *
     * @AbcsAPI unstable
     *
     * @param {Object} input
     * @param {String} input.entityId - id of the entity which data are being passed
     * @param {viewmodel/js/api/Record} input.data - record data
     * @returns {viewmodel/js/api/context/ContextualData}
     *
     * @example
     * <caption>
     * After new customer record was persisted in the database send it back to page which
     * initiated creation of new customer; allow that page to use this information
     * to update its UI or whatever is desirable.
     * </caption>
     * var newCustomer = ContextualData.createRecordWasChangedContext({
     *      // what business object are these data of:
     *      entityId: 'customer',
     *      // customerDetailArchetype is instance of DetailArchetype which
     *      // was used to create and persist the customer
     *      data: customerDetailArchetype.getRecord()});
     * // navigate to the page
     * Abcs.Pages().returnToPage('customerListPage', newCustomer);
     */
    ContextualData.createRecordWasChangedContext = function(input) {
        AbcsLib.checkParameterCount(arguments, 1);
        AbcsLib.checkDefined(input, 'input');
        AbcsLib.checkObjectLiteral(input, ['entityId', 'data']);
        return new ContextualData(
                new EntityData(input.entityId, input.data),
                DataSemanticsType.RECORD_WAS_CHANGED);
    };

    /**
     * Create contextual data representing a blank new record which can be
     * populated and created. If record being created is a child record then
     * reference to parent record should be pre-initialized.
     *
     * @AbcsAPI stable
     * @version 16.3.5
     * @param {Object} input
     * @param {String} input.entityId - id of the entity which data are being passed
     * @param {viewmodel/js/api/Record} input.data - record data
     * @param {String} [input.caller=activePageId] - id of the page which page navigation should allow to
     * return to; optional parameter which fallbacks to <code>activePageId</code>
     * as the id of currently open page.
     * @returns {viewmodel/js/api/context/ContextualData}
     *
     * @example
     * <caption>
     * Creating a new child record in a separate page may require reference to parent
     * record be passed around. For example SparePart business object is child
     * of Product. Initiating creation of new SparePart object spanning multiple
     * pages could look like this:
     * </caption>
     * // productDetailArchetype is DetailArchetype representing existing
     * // Product record:
     * var productId = productDetailArchetype.getRecord().getValue('id');
     * // create blank SpareProduct record with all mandatory parent info:
     * var blankSparePart = Record.createSimpleRecord({
     *      // sparePart is child record of product business object and
     *      // product ID is mandatory for new spare part to be created:
     *      product: productId});
     * });
     * var sparePartToCreate = ContextualData.createStartWithBlankRecordContext({
     *      // what business object are these data of:
     *      entityId: 'sparePart',
     *      // blank or preinitialized spare part data:
     *      data: blankSparePart,
     *      // current page where this data and request originated;
     *      // by default replaced with the activePageId, if 'caller' not set
     *      caller: 'productDetailPage'});
     * // navigate to page where spare part is created
     * Abcs.Pages().navigateToPage('sparePartCreatePage', sparePartToCreate);
     */
    ContextualData.createStartWithBlankRecordContext = function(input) {
        AbcsLib.checkParameterCount(arguments, 1);
        AbcsLib.checkDefined(input, 'input');
        AbcsLib.checkObjectLiteral(input, ['entityId', 'data', 'caller']);
        return new ContextualData(
                new EntityData(input.entityId, input.data),
                DataSemanticsType.START_WITH_BLANK_DATA,
                ContextualData._createNavigationContext(input.caller));
    };

    /**
     * Create contextual data representing some query parameters.
     *
     * Condidate for API. In V1 QUERY_PARAMETERS is not used.
     * Right now Query is still used instead of new Condition (in QueryData
     * and elsewhere). Once MartinJ finishes BUFP-2822 the signature of this
     * method can be finalized and exposed as API.
     *
     * @param {Object} input
     * @param {String} input.query - TBD
     * @param {String} [input.caller=activePageId] - id of the page which page navigation should allow to
     *                                               return to; optional parameter which fallbacks to activePageId
     * @returns {viewmodel/js/api/context/ContextualData}
     */
    ContextualData.createQueryParamsContext = function(input) {
        AbcsLib.checkDefined(input, 'input');
        return new ContextualData(
                new QueryData(input.query),
                DataSemanticsType.QUERY_PARAMETERS,
                ContextualData._createNavigationContext(input.caller));
    };

    /**
     * Create empty contextual data.
     *
     * @AbcsAPI stable
     * @version 16.3.5
     * @param {Object} [input]
     * @param {String} [input.caller=activePageId] - id of the page which page navigation should allow to
     *                                               return to; optional parameter which fallbacks to activePageId
     * @returns {viewmodel/js/api/context/ContextualData}
     *
     * @example
     * <caption>
     * Create a blank page context and navigate to an ABCS landing page.
     * </caption>
     * var emptyContext = ContextualData.createBlankPageContext({
     *     // current page where this data and request originated;
     *     // by default replaced with the activePageId, if 'caller' not set
     *     caller: 'customerListPage'
     * });
     * // navigate to the landing page
     * Abcs.Pages().navigateToPage('myLandingPage', emptyContext);
     */
    ContextualData.createBlankPageContext = function(input) {
        AbcsLib.checkParameterCount(arguments, 0, 1);
        if (input) {
            AbcsLib.checkObjectLiteral(input, ['caller']);
        }
        return new ContextualData({},
                DataSemanticsType.NONE,
                ContextualData._createNavigationContext(input && input.caller));
    };

    /**
     * Create contextual data representing a bookmark to use to restore a page.
     *
     * @param {Object} input
     * @param {String} input.bookmark - textual value containing all information required
     *      to restore a page
     * @param {String} [input.caller=activePageId] - id of the page which page navigation should allow to
     *                                               return to; optional parameter which fallbacks to activePageId
     * @returns {viewmodel/js/api/context/ContextualData}
     */
    ContextualData.createBookmarkContext = function(input) {
        AbcsLib.checkDefined(input, 'input');
        return new ContextualData(
                new BookmarkData(input.bookmark),
                DataSemanticsType.BOOKMARK,
                ContextualData._createNavigationContext(input.caller));
    };

    /**
     *
     * @returns {viewmodel/js/api/context/Data}
     */
    ContextualData.prototype.getData = function() {
        return this.data;
    };

    /**
     *
     * @returns {viewmodel/js/api/context/DataSemanticsType}
     */
    ContextualData.prototype.getSemanticType = function() {
        return this.semanticType;
    };

    /**
     *
     * @returns {viewmodel/js/api/context/NavigationContext}
     */
    ContextualData.prototype.getNavigationContext = function() {
        return this.navigationContext;
    };

    /**
     * Creates navaigation context based on caller parameter.
     *
     * @param {String} caller caller's page ID
     * @returns {NavigationContext}
     */
    ContextualData._createNavigationContext = function(caller) {
        // if caller exists use it, otherwise fallback to the activePage
        caller = caller || NavigationProvider.getNavigation().getActivePageId();
        return new NavigationContext(caller);
    };

    /**
     * Serializes the contextual data to JSON object.
     *
     * @returns {JSON} serialized contextual data
     */
    ContextualData.prototype.toJSON = function() {
        var def = {};
        def.data = (!this.data || !this.data.toJSON) ? JSON.stringify(this.data) : this.data.toJSON();
        def.semanticType = this.semanticType;
        def.navigationContext = this.navigationContext.toJSON();
        return def;
    };

    /**
     * Deserializes the contextual data from JSON object.
     *
     * @param {JSON} json serialized contextual data
     * @returns {ContextualData} ContextualData object
     */
    ContextualData.fromJSON = function(json) {
        if (!AbcsLib.isObject(json)) {
            return new ContextualData({});
        }

        var semanticType = json.semanticType;
        var navigationContext = ContextualData._createNavigationContext(json.navigationContext && json.navigationContext.previousPageId);
        var data = {};
        switch (semanticType) {
            case DataSemanticsType.BOOKMARK:
                data = BookmarkData.fromJSON(json.data);
                break;
            case DataSemanticsType.START_WITH_BLANK_DATA:
            case DataSemanticsType.RECORD_TO_EDIT:
            case DataSemanticsType.RECORD_WAS_CHANGED:
                data = EntityData.fromJSON(json.data);
                break;
            case DataSemanticsType.QUERY_PARAMETERS:
                data = QueryData.fromJSON(json.data);
                break;
            case DataSemanticsType.NONE:
            default:
                data = {};
        }

        return new ContextualData(data, semanticType, navigationContext);
    };

    return ContextualData;

});