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

Source: viewmodel/js/api/Record.js

define([
], function (
        ) {

    'use strict';

    /**
     * Represents and wraps single data record.
     *
     * You will often use this object instead of a plain JS map object when using
     * the ABCS {@link api/js/Pages Navigation API} or communicating with ABCS
     * {@link viewmodel/js/api/Archetype Archetypes}.
     *
     * In contrast to a plain JS object this provides a set of helper methods
     * when accessing wrapped data and mainly allows you to access (and update)
     * values of archetype's observables and record model from your code (see the
     * example below).
     *
     * @AbcsAPI stable
     * @version 16.3.5
     *
     * @exports viewmodel/js/api/Record
     * @constructor
     * @private
     *
     * @example <caption>Custom code accessing a form archetype, getting one of
     * record model values and updating the value</caption>
     *
     * // get the current live archetype's instance of a Record
     * var customerRecord = customerEditArchetype.getRecord();
     *
     * // get number of orders the customer has made so far
     * var numberOfOrders = customerRecord.getValue('numberOfOrders');
     *
     * // check if number of orders is more than five in which case promote the
     * // customer to a Premium state
     * if (numberOfOrders > 5) {
     *     customerRecord.setValue('state', 'Premium');
     * }
     *
     * @example <caption>Custom code fetching customer data and immediately
     * opening the customer's detail page</caption>
     *
     * require([
     *     'operation/js/api/Conditions',
     *     'operation/js/api/Operator',
     *     'viewmodel/js/api/ContextualData',
     *     'viewmodel/js/api/Record'
     * ], function (Conditions, Operator, ContextualData, Record) {
     *     // construct read condition for customer with customerId=100
     *     var customer = Abcs.Entities().findById('my.custom.bop.Customer');
     *     var customerId = customer.getProperty('customerId');
     *     var condition = Conditions.SIMPLE(customerId, Operator.EQUALS, 100);
     *     // get read operation for the constructed condition
     *     var operation = Abcs.Operations().read({
     *         entity: customer,
     *         condition: condition
     *     });
     *
     *     // fetch customer data
     *     operation.perform().then(function(operationResult) {
     *
     *         // when data are fetched prepare the record to pass to a detail page
     *         var customerRecord = Record.createFromJSON(operationResult.getData());
     *
     *         // open a detail page with id 'customerDetail' and pass the record
     *         // wrapped in the contextual data so the page can show its details
     *         // in the UI
     *         var customerToEdit = ContextualData.createRecordToEditContext({
     *             entityId: 'customer',
     *             // pass the record instance here
     *             data: customerRecord
     *         });
     *         return Abcs.Pages().navigateToPage('customerDetail', customerToEdit);
     *     });
     * });
     *
     * @see {@link viewmodel/js/api/Archetype Archetype API}
     * @see {@link viewmodel/js/api/DetailArchetype Detail/Form Archetype API} which represents
     *      record of one businness object
     * @see {@link module:api/js/Pages Navigation API} which uses the Record object for sharing data
     *      between pages
     */
    var Record = function(data, originalOperationResult) {
        this.data = data || {};
        this.__originalResult = originalOperationResult;
        this._lastSyncTime = 0;
        if (!originalOperationResult && this.data.__originalResult) {
            this.__originalResult = this.data.__originalResult;
            delete this.data.__originalResult;
        }
    };

    Record._ERROR_CREATE_NO_OBJECT_LITERAL = function (contructor) {
        return 'Record must be based on a valid JSON object. You seem to be passing an object of type ' + contructor.name;
    };

    /**
     * Creates simple record backed by plain JSON object literal.
     *
     * @AbcsAPI stable
     * @version 16.3.5
     *
     * @param {Object} data JSON with initial values
     * @returns {viewmodel/js/api/Record} created record instance
     *
     * @example <caption>Clone the Record object using Record's createFromJSON() and getDefinition() methods</caption>
     * // creates data for this sample
     * var data = {
     *     prop1: 'value1',
     *     prop2: 'value2
     * }
     * // creates the Record objects
     * var record = Record.createFromJSON(data);
     * // clones the Record
     * var clone = Record.createFromJSON(record.getDefinition());
     */
    Record.createFromJSON = function (data) {
        AbcsLib.checkParameterCount(arguments, 1);
        AbcsLib.checkDefined(data, 'data');
        AbcsLib.checkDataType(data, AbcsLib.Type.OBJECT);
        if (typeof data.constructor === 'function' && data.constructor !== Object && !data.hasOwnProperty('constructor')) {
            throw new Error(Record._ERROR_CREATE_NO_OBJECT_LITERAL(data.constructor));
        }
        return new Record(AbcsLib.clone(data));
    };

    /**
     * Creates simple record backed by plain JSON object literal.
     *
     * <p>This duplicates the method {@link viewmodel/js/api/Record.createFromJSON Record.createFromJSON}
     * due to compatibility reasons. This method is spread over custom code in existing applications.</p>
     *
     * @param {Object} data JSON with initial values
     * @returns {viewmodel/js/api/Record} created record instance
     */
    Record.createSimpleRecord = function (data) {
        return Record.createFromJSON(data);
    };

    /**
     * Create simple record backed by plain JSON object literal, with the result
     * from the operations layer that retrieved it.
     *
     * @param {Object} data JSON with initial values
     * @param {String} operationResult the operationResult that was was the data
     * response for this record when retrieved from the server.
     * @returns {Record}
     */
    Record.createSimpleRecordWithResult = function(data, operationResult) {
        return new Record(data, operationResult);
    };

    Record.createSimpleRecordForRVM = function(rvm) {
        return new Record.RVMRecord(rvm);
    };

    Record.prototype.getOriginalResult = function() {
        return this.__originalResult;
    };

    Record.prototype.setOriginalResult = function(operationResult) {
        return this.__originalResult = operationResult;
    };

    /**
     * Returns array of record's property names.
     *
     * @AbcsAPI stable
     * @version 16.3.5
     *
     * @returns {String[]} list of all property names
     */
    Record.prototype.getProperties = function() {
        AbcsLib.checkParameterCount(arguments, 0);
        return Object.keys(this.data);
    };

    /**
     * Get value of a single property.
     *
     * @AbcsAPI stable
     * @version 16.3.5
     * @param {String} property property ID
     *
     * @returns {*} property value, may be <code>undefined</code> if the property doesn't exist
     */
    Record.prototype.getValue = function(property) {
        AbcsLib.checkParameterCount(arguments, 1);
        AbcsLib.checkDefined(property, 'property');
        return this.data[property];
    };

    /**
     * Set value of a single property.
     *
     * In case the record is based on {@link viewmodel/js/api/DetailArchetype detail
     * archetype's} data this writes through the archetype's model and allows you
     * to communicate with other components bound to the same property.
     *
     * @AbcsAPI stable
     * @version 16.3.5
     *
     * @param {String} property property ID
     * @param {Object} value value to set
     */
    Record.prototype.setValue = function(property, value) {
        AbcsLib.checkParameterCount(arguments, 2);
        AbcsLib.checkDefined(property, 'property');
        AbcsLib.checkDataType(property, AbcsLib.Type.STRING);
        this.data[property] = value;
    };

    /**
     * Erase value of single proeprty.
     *
     * @AbcsAPI unstable
     * @param {String} property property ID
     * @returns {Record} this
     */
    Record.prototype.removeValue = function(property) {
        delete this.data[property];
        return this;
    };

    /**
     * Gets the map where key is PropertyID and value is the corresponding record value.
     *
     * @returns {Object[]}
     */
    Record.prototype.getData = function() {
        return this.data;
    };

    /**
     * Return record data as plain JSON object.
     *
     * This is useful when you want to pass the record data to a remote REST endpoint
     * via the {@link module:api/js/Operations Operations API}.
     *
     * @AbcsAPI stable
     * @version 16.3.5
     *
     * @returns {Object}
     *
     * @example <caption>Clone the Record object using Record's createFromJSON() and getDefinition() methods</caption>
     * // creates data for this sample
     * var data = {
     *     prop1: 'value1',
     *     prop2: 'value2
     * }
     * // creates the Record objects
     * var record = Record.createFromJSON(data);
     * // clones the Record
     * var clone = Record.createFromJSON(record.getDefinition());
     */
    Record.prototype.getDefinition = function() {
        return this.toJSON();
    };

    Record.prototype.toJSON = function() {
        var clone = AbcsLib.clone(this.data);
        var originalResult = this.getOriginalResult();
        if (clone && !clone.__originalResult && originalResult) {
            clone.__originalResult = originalResult;
        }
        return clone;
    };

    /**
     * Get timestamp of the last sync with backend
     * @returns {Number} timestamp - in milliseconds - of the last sync.
     */
    Record.prototype.getLastSyncTime = function () {
        return this._lastSyncTime;
    };

    /**
     * Set timestamp of the last sync with backend
     * @param {Number} timestamp timestamp - in milliseconds - of the last sync.
     */
    Record.prototype.setLastSyncTime = function (timestamp) {
        this._lastSyncTime = timestamp;
    };

    /**
     *
     * @param {RecordViewModel} rvm
     */
    Record.RVMRecord = function(rvm) {
        this.recordViewModel = rvm;
    };
    AbcsLib.extend(Record.RVMRecord, Record);

    Record.RVMRecord.prototype.getProperties = function() {
        AbcsLib.checkParameterCount(arguments, 0);
        return Object.keys(this.recordViewModel.getData());
    };

    Record.RVMRecord.prototype.getOriginalResult = function() {
        return this.recordViewModel.getOriginalResult();
    };

    Record.RVMRecord.prototype.setOriginalResult = function(operationResult) {
        return this.recordViewModel.setOriginalResult(operationResult);
    };

    Record.RVMRecord.prototype.getValue = function(property) {
        AbcsLib.checkParameterCount(arguments, 1);
        AbcsLib.checkDefined(property, 'property');
        return this.recordViewModel.getValue(property);
    };

    Record.RVMRecord.prototype.getData = function() {
        return this.recordViewModel.getData();
    };

    Record.RVMRecord.prototype.setValue = function(property, value) {
        AbcsLib.checkParameterCount(arguments, 2);
        AbcsLib.checkDefined(property, 'property');
        AbcsLib.checkDataType(property, AbcsLib.Type.STRING);
        this.recordViewModel.setValue(property, value);
        return this;
    };

    Record.RVMRecord.prototype.removeValue = function(/*property*/) {
        // XXX: I do not think this is currently supported
        return this;
    };

    Record.RVMRecord.prototype.getDefinition = function() {
        return this.toJSON();
    };

    Record.RVMRecord.prototype.toJSON = function() {
        return this.recordViewModel.getData();
    };

    Record.RVMRecord.prototype.getLastSyncTime = function () {
        return this.recordViewModel.getLastSyncTime();
    };

    Record.RVMRecord.prototype.setLastSyncTime = function (timestamp) {
        this.recordViewModel.setLastSyncTime(timestamp);
    };

    return Record;
});