define([
'data/js/api/OperationOutput',
'entity/js/api/Property'
], function (
OperationOutputImpl,
Property
) {
'use strict';
/**
* Builds the operation output.
*
* <p>
* The operation output description drives some Application Builder UI features. For example if table is configured to use {@link operation/js/api/Operation Operation}
* with only two output {@link entity/js/api/Property Properties} selected, these will be available for Business User to select in Table Creation Wizard - Data section.
* </p>
*
* <p>
* Most typical use case is when {@link operation/js/api/Operation Operation} of type {@link operation/js/api/Operation.Type.READ_ONE Operation.Type.READ_ONE} returns all
* available {@link entity/js/api/Entity Entity} properties (resp. detailed information about {@link entity/js/api/Entity Entity}), but related {@link operation/js/api/Operation Operation}
* of type {@link operation/js/api/Operation.Type.READ_MANY Operation.Type.READ_MANY} returns only certain sub-set of them (e.g. the most important ones which user want to see in
* some Collection component). In such case, you need to create two {@link bop/js/api/operation/OperationOutput OperationOutput} instances and configure output
* for each {@link operation/js/api/Operation Operation} separately.
* </p>
*
* <p>
* What's being build here needs to be passed into {@link bop/js/api/operation/OperationBuilder#returns OperationBuilder.returns(..)} method creating the final {@link operation/js/api/Operation Operation}
* instance which is the base building block for creating custom BOP.
* </p>
*
* @AbcsExtension stable
* @version 17.1.1
* @exports bop/js/api/operation/OperationOutput
* @constructor
*
* @param {Object} params - Object literal with all possible parameters.
* @param {entity/js/api/Entity} params.entity - {@link entity/js/api/Entity Entity} whose {@link entity/js/api/Property Properties} the build {@link operation/js/api/Operation operation} returns on it's output.
*
* @see {@link entity/js/api/Entity Entity}
* @see {@link entity/js/api/Property Property}
* @see {@link operation/js/api/Operation Operation}
* @see {@link bop/js/api/operation/OperationBuilder OperationBuilder}
* @see {@link bop/js/api/operation/OperationBuilder#returns OperationBuilder.returns(..)}
*/
var OperationOutput = function(params) {
AbcsLib.checkThis(this);
AbcsLib.checkDefined(params, 'params', 'You need to pass object literal with "entity" attribute set');
AbcsLib.checkParameterCount(arguments, 1);
AbcsLib.checkObjectLiteral(params, ['entity'], ['entity']);
this._entity = params.entity;
this._properties = [];
this._includeAll = false;
};
/**
* Extends all {@link entity/js/api/Property Properties} from either single {@link bop/js/api/operation/OperationOutput OperationOutput}
* instance or array of {@link bop/js/api/operation/OperationOutput OperationOutput}s.
*
* @AbcsExtension stable
* @version 17.1.1
*
* @param {bop/js/api/operation/OperationOutput | bop/js/api/operation/OperationOutput[]} outputs - {@link bop/js/api/operation/OperationOutput OperationOutput}(s)
* from which you want to extend all it's (resp. their's) {@link entity/js/api/Property Properties}.
* @returns {bop/js/api/operation/OperationOutput} a reference to this object to allow method chaining
*
* @example
* <caption>
* Creates an instance of {@link bop/js/api/operation/OperationOutput OperationOutput} which is inheriting all defined {@link entity/js/api/Property Properties} from
* another {@link bop/js/api/operation/OperationOutput OperationOutput} plus including some others explicitly.
* </caption>
*
* // Assuming Employee has properties: "Firstname", "Lastname", "Age", "Salary" and some others
* var employee = Abcs.Entities().findById('my.custom.bop.Employee');
* var firstname = employee.getProperty('firstname');
* var lastname = employee.getProperty('lastname');
* var age = employee.getProperty('age');
* var salary = employee.getProperty('salary');
*
* // If some operation returns only basic Name information about Employee, this OperationOutput can be used
* var nameOutput = new OperationOutput({
* entity: employee
* }).property(firstname).
* property(lastname);
*
* // If on the other hand some other operation returns more information about Employee, full-OperationOutput can be used
* var fullOutput = new OperationOutput({
* entity: employee
* }).propertiesFrom(nameOutput). // Inheriting all properties defined inside nameOutput ("Firstname", "Lastname")
* property(age). // Explicitly including "Age" property
* property(salary); // Explicitly including "Salary" property
*
* @example
* <caption>
* Creates an instance of {@link bop/js/api/operation/OperationOutput OperationOutput} which is inheriting all defined {@link entity/js/api/Property Properties} from
* two different {@link bop/js/api/operation/OperationOutput OperationOutput}s.
* </caption>
*
* // Assuming Employee has properties: "Firstname", "Lastname", "Age", "Salary" and some others
* var employee = Abcs.Entities().findById('my.custom.bop.Employee');
* var firstname = employee.getProperty('firstname');
* var lastname = employee.getProperty('lastname');
* var age = employee.getProperty('age');
* var salary = employee.getProperty('salary');
*
* var firstOutput = new OperationOutput({
* entity: employee
* }).property(firstname).
* property(lastname);
*
* var secondOutput = new OperationOutput({
* entity: employee
* }).property(age).
* property(salary);
*
* // If on the other hand some other operation returns more information about Employee, full-OperationOutput can be used
* var fullOutput = new OperationOutput({
* entity: employee
* }).propertiesFrom(firstOutput). // Inheriting all properties defined inside firstOutput ("Firstname", "Lastname")
* propertiesFrom(secondOutput); // Inheriting all properties defined inside secondOutput ("Age", "Salary")
*/
OperationOutput.prototype.propertiesFrom = function(outputs) {
AbcsLib.checkDefined(outputs, 'outputs', 'Either an OperationOutput instance or array of OperationOutput\'s needs to be passed.');
AbcsLib.checkDataType(outputs, [OperationOutput, AbcsLib.Type.ARRAY]);
AbcsLib.checkParameterCount(arguments, 1);
if (outputs) {
if (AbcsLib.isArray(outputs)) {
for (var i = 0; i < outputs.length; i++) {
this._addAllFromOutput(outputs[i]);
}
} else {
this._addAllFromOutput(outputs);
}
}
return this;
};
/**
* Explicitly including additional {@link entity/js/api/Property Properties} into the resulted {@link bop/js/api/operation/OperationOutput OperationOutput}.
*
* @AbcsExtension stable
* @version 17.1.1
*
* @param {entity/js/api/Property} property - {@link entity/js/api/Property Property} you want to include into the {@link bop/js/api/operation/OperationOutput OperationOutput}.
* @returns {bop/js/api/operation/OperationOutput} a reference to this object to allow method chaining
*
* @example
* <caption>
* Creates an instance of {@link bop/js/api/operation/OperationOutput OperationOutput} with two explicit properties included.
* </caption>
*
* // Assuming Employee has properties: "Firstname", "Lastname", "Age", "Salary" and some others
* var employee = Abcs.Entities().findById('my.custom.bop.Employee');
* var firstname = employee.getProperty('firstname');
* var lastname = employee.getProperty('lastname');
*
* var output = new OperationOutput({
* entity: employee
* }).property(firstname).
* property(lastname);
*/
OperationOutput.prototype.property = function(property) {
AbcsLib.checkDefined(property, 'property');
AbcsLib.checkDataType(property, Property);
AbcsLib.checkParameterCount(arguments, 1);
var propertyID = property.getId();
var exists = this.getEntity().getProperty(propertyID);
if (!exists) {
throw new Error('No Property with ID = ' + propertyID + ' exists in the configured Entity. Please be aware that your OperationOutput can\n\
be configured to only work with Properties that exists inside defined Entity.');
}
this._addProperty(property);
return this;
};
/**
* Automatically includes all {@link entity/js/api/Property Properties} from the configured {@link entity/js/api/Entity Entity} and adds them into the resulted {@link bop/js/api/operation/OperationOutput OperationOutput}.
*
* @returns {bop/js/api/operation/OperationOutput} a reference to this object to allow method chaining
*
* @example
* <caption>
* Creates an instance of {@link bop/js/api/operation/OperationOutput OperationOutput} with two explicit properties included.
* </caption>
*
* // Assuming Employee has properties: "Firstname", "Lastname" and "Age"
* var employee = Abcs.Entities().findById('my.custom.bop.Employee');
* var output = new OperationOutput({
* entity: employee
* }).parametersAll(); // This will automatically includes all three Properties
*/
OperationOutput.prototype.propertiesAll = function() {
AbcsLib.checkParameterCount(arguments, 0);
this._includeAll = true;
return this;
};
/**
* Adds all {@link entity/js/api/Property Properties} from the given {@link bop/js/api/operation/OperationOutput OperationOutput}.
*
* @param {OperationOutput} output
*/
OperationOutput.prototype._addAllFromOutput = function(output) {
AbcsLib.checkDataType(output, OperationOutput);
var entityID = this._entity.getId();
var extendEntityID = output.getEntity().getId();
if (entityID !== extendEntityID) {
throw new Error('This OperationOutput instance is working on top of Entity (ID = "' + entityID + '") but the OperationOutput instance\n\
you are trying to extend is working with different one (Entity ID = "' + extendEntityID + '")');
}
var properties = output.getProperties();
for (var i = 0; i < properties.length; i++) {
this._addProperty(properties[i]);
}
};
OperationOutput.prototype._addProperty = function(property) {
var self = this;
for (var i = 0; i < self._properties.length; i++) {
var existingProperty = self._properties[i];
if (existingProperty.getId() === property.getId()) {
return;
}
}
self._properties.push(property);
};
/**
* Gets {@link entity/js/api/Entity Entity} which the {@link bop/js/api/operation/OperationOutput OperationOutput} is working with.
*
* @returns {Entity}
*/
OperationOutput.prototype.getEntity = function() {
return this._entity;
};
/**
* Gets all {@link entity/js/api/Property Properties} of this {@link bop/js/api/operation/OperationOutput OperationOutput}.
*
* @returns {Property[]}
*/
OperationOutput.prototype.getProperties = function() {
// If parametersAll(..) had been called, include all available Entity properties automatically
if (this._includeAll) {
return this.getEntity().getProperties();
} else {
return this._properties;
}
};
/**
* Builds an {@link data/js/api/OperationOutput OperationOutput} instance based on the current configuration.
*
* @returns {data/js/api/OperationOutput}
*/
OperationOutput.prototype.build = function() {
return new OperationOutputImpl(this.getEntity(),
// If we are including all properties lazily load this
// as custom fields can be added later
this._includeAll ? undefined : this.getProperties());
};
return OperationOutput;
});