define([
'module',
'bop/js/EmptyCondition',
'bop/js/api/operation/Pagination',
'core/js/api/Listenable',
'core/js/api/utils/ArrayUtils',
'operation/js/api/OperationData',
'operation/js/api/OperationResult'
], function(
module,
EmptyCondition,
Pagination,
Listenable,
ArrayUtils,
OperationData,
OperationResult
) {
'use strict';
/**
* An API object representing a single data operation.
*
* <p>
* This object is useful for both roles, ABCS Business User and ABCS Developer.
* <ul>
* <li>
* ABCS Developer needs to produce Operation instances to make his custom custom Business Object Provider (BOP) useful.
* </li>
* <li>
* ABCS Business User consumes these Operation instances and performs the operation using {@link operation/js/api/Operation#perform Operation.perform(..)} method.
* </li>
* </p>
*
* @AbcsAPI stable
* @version 17.1.1
* @exports operation/js/api/Operation
*
* @constructor
* @private
*
* @param {operation/js/api/Operation.Type} type - Type of the operation
* @param {OperationInput} input - Description of what kind of input the operation accepts
* @param {OperationOutput} output - Description of the output this operation returns
* @param {function} functionToRun - Function performing the real REST call
* @returns {Operation}
*
* @see {@link module:api/js/Operations Operations} to understand how to get certain registered operation and perform it.
* @see {@link module:api/js/Operations.read Operations.read(..)}
* @see {@link module:api/js/Operations.create Operations.create(..)}
* @see {@link module:api/js/Operations.update Operations.update(..)}
* @see {@link module:api/js/Operations.delete Operations.delete(..)}
*/
var Operation = function(type, input, output, functionToRun) {
AbcsLib.checkThis(this);
Listenable.apply(this, arguments);
this._type = type;
this._functionToRun = functionToRun;
this._input = input;
this._output = output;
this._operationProvider = undefined;
};
AbcsLib.extend(Operation, Listenable);
Operation._LOGGER = Logger.get(module.id);
Operation._MISSING_FUNCTION_TO_RUN = 'FunctionToRun is undefined. This operation can\'t be performed.';
Operation.DATA_HAS_CHANGED = 'dataChanged';
Operation.DATA_FULLY_LOADED = 'dataFullyLoaded';
Operation.DATA_LOADING_FAILED = 'dataLoadingFailed';
Operation.prototype.getId = function() {};
Operation.prototype.getName = function() {};
Operation.prototype.getDescription = function() {};
Operation.prototype.getType = function() {
return this._type;
};
Operation.prototype.getInput = function() {
return this._input;
};
Operation.prototype.getOutput = function() {
return this._output;
};
/**
* Sets the owning {@link OperationProvider} for this Operation instance.
*
* <p>
* BE AWARE:
* This is an infrastructure method which is supposed to be called only by OperationProvider itself. The reason why
* it's here is to avoid each Operation instance accepting OP instance within it's constructor. Since every method
* that should possibly live inside Abcs DataModel should be registered using OP, this should be ensured. But if you
* think about calling this method from somewhere else, it's very suspicious and you should consult such change first.
* </p>
*
* @param {OperationProvider} provider
*/
Operation.prototype.setOperationProvider = function(provider) {
this._operationProvider = provider;
};
/**
* Gets an instance of OperationProvider associated with this Operation.
*
* <p>
* Allows client to access e.g. Authentication mechanism which is provided directly by OperationProvider itself.
* </p>
*
* @returns {OperationProvider}
*/
Operation.prototype.getOperationProvider = function() {
return this._operationProvider;
};
Operation.prototype.setInputData = function(operationData) {
this._operationData = operationData;
};
/**
* Gets explicitly set {@link operation/js/api/OperationData OperationData} set to this <code>Operation</code>.
*
* @returns {operation/js/api/OperationData}
*/
Operation.prototype.getInputData = function() {
return this._operationData;
};
/**
* Performs this operation.
*
* <p>
* Each {@link operation/js/api/Operation#perform Operation.perform(..)} method call returns an instance of {@link operation/js/api/OperationResult OperationResult}.
* It either:
* <ul>
* <li>
* Provides returned data in case {@link operation/js/api/Operation Operation} perform correctly. <br/>
* It also can contain any other metadata which Business Object Provider decides to return. For example {@link operation/js/api/PaginationCursor PaginationCursor} can be available if the perfomed
* {@link operation/js/api/Operation Operation} has type {@link operation/js/api/Operation.Type.READ_MANY Operation.Type.READ_MANY} set and provider implemented capability to paginate through
* the resulted records.
* </li>
* <li>
* Provides error code, message and possibly any other additional error information in case {@link operation/js/api/Operation Operation} performed incorrectly.
* </li>
* </ul>
*
* To check the result of {@link operation/js/api/Operation#perform Operation.perform(..)} call, you can use:
* <ul>
* <li>
* {@link operation/js/api/OperationResult#isSuccess OperationResult.isSuccess()} to check if the call was performed correctly and resulted in a {@link operation/js/api/OperationResult.Success OperationResult.Success}.
* </li>
* <li>
* {@link operation/js/api/OperationResult#isFailure OperationResult.isFailure()} to check if the call was performed incorrectly and resulted in an {@link operation/js/api/OperationResult.Failure OperationResult.Failure}.
* </li>
* </ul>
* </p>
*
* @AbcsAPI stable
* @version 15.4.5
*
* @see {@link operation/js/api/OperationResult OperationResult}
* @see {@link operation/js/api/OperationResult.Failure OperationResult.Failure}
* @see {@link operation/js/api/OperationResult.Success OperationResult.Success}
* @see {@link operation/js/api/OperationResult#isFailure OperationResult.isFailure()}
* @see {@link operation/js/api/OperationResult#isSuccess OperationResult.isSuccess()}
*
* @return {Promise<operation/js/api/OperationResult>} - Promise of the {@link operation/js/api/OperationResult OperationResult} for this operation.
*/
Operation.prototype.perform = function(operationData) {
var self = this;
var inputData = self._operationData ? self._operationData : (operationData ? operationData : new OperationData());
var query = inputData.getQuery();
if (query) {
return query.replaceLookups().then(function() {
return self._perform(inputData);
}, function() {
// If replaceLookups() is rejected, it means that there is a string value which doesn't correspond
// to any of the existing Lookup values and thus we can be sure that the operation will not return
// any record
return Promise.resolve(OperationResult.success([]));
});
} else {
return self._perform(inputData);
}
};
Operation.prototype._perform = function(inputData) {
var self = this;
if (self._functionToRun) {
// This is a user function so might thrown an exception rather
// than correctly return a promise.
try {
return self._functionToRun(inputData).then(function(result) {
self.fireEvent(Operation.DATA_FULLY_LOADED);
self._operationData = undefined;
return result;
}).catch(self._onErrorHandler.bind(self));
} catch (error) {
return self._onErrorHandler(error);
}
} else {
return Promise.reject(OperationResult.failure(Operation._MISSING_FUNCTION_TO_RUN));
}
};
/**
* Handle and log the given error object.
*
* @param {Error | String | OperationResult.Failure} error
* @returns {Promise} A Promise that returns an OperationResult object
* representation of the input
*/
Operation.prototype._onErrorHandler = function(error) {
var self = this;
self.fireEvent(Operation.DATA_LOADING_FAILED);
self._operationData = undefined;
Operation._LOGGER.error('Performing operation with ID = \'{0}\' failed.', self.getId());
if (OperationResult.isFailureResult(error)) {
var code = error.getCode();
if (code) {
Operation._LOGGER.error('Error code: ' + code);
}
var message = error.getMessage();
if (message) {
Operation._LOGGER.error('Error message: ' + message);
}
var additionalInfo = error.getAdditionalInfo();
if (additionalInfo) {
Operation._LOGGER.error('Additional info: ' + JSON.stringify(additionalInfo));
}
return Promise.reject(error);
} else if (error && error.stack) {
Operation._LOGGER.error('Stack trace: ' + error.stack);
// Intentionally expose failure to the user to aid diagnostics
return Promise.reject(OperationResult.failure(
AbcsLib.i18n('operation.js.api.errorInPerform', error.stack)));
} else {
return Promise.reject(OperationResult.failure(
error ? AbcsLib.i18n('operation.js.api.errorInPerform', error)
: AbcsLib.i18n('operation.js.api.unidentifiableErrorInPerform')));
}
};
/**
* Gets all fields that are available for simple filtering.
*
* <p>
* These are all fields which are optional in addition to mandatory fields and supports Operator.CONTAINS required by the UI logic.
* </p>
*
* @returns {entity/js/api/Property[]}
*/
Operation.prototype.getFilterableFields = function() {
var result = [];
if (this.getType() === Operation.Type.READ_MANY) {
var operationInput = this.getInput();
if (operationInput) {
var queryDesc = operationInput.getQuery();
if (queryDesc) {
var queryParams = queryDesc.getQueryParameters();
if (queryParams && queryParams.length > 0) {
queryParams.forEach(function(queryParam) {
if (queryParam.isSimpleFilterable()) {
result.push(queryParam.getProperty());
}
});
}
}
}
}
return result;
};
/**
* Checks whether this {@link Operation} is working on the given entity or not.
*
* <p>
* That means, if the operation is either returning data of the entity or accepting
* an entity as a parameter, then this method return true.
* </p>
*
* @param {Entity} entity
* @returns {Boolean}
*/
Operation.prototype.isWorkingOn = function(entity) {
return this.hasOutputData(entity) || this.hasInputData(entity);
};
Operation.prototype.hasInputData = function(entity) {
if (entity === undefined) {
return this._input.getData().length > 0;
}
var entityID = entity.getId();
return this._input && entityID.length > 0 && entityID === this._input.getData();
};
Operation.prototype.hasQuery = function() {
return (this._input && this._input.getQuery());
};
/**
* Checks whether this operation supports the given {@link operation/js/api/OperationData OperationData} or not.
*
* @param {operation/js/api/OperationData} operationData
* @returns {Boolean} true if this operation supports the given {@link operation/js/api/OperationData OperationData}, false otherwise.
*/
Operation.prototype.supports = function(operationData) {
switch (this._type) {
case Operation.Type.READ_MANY:
return this.supportsQuery(operationData.getQuery());
case Operation.Type.CREATE:
case Operation.Type.UPDATE:
case Operation.Type.DELETE:
var missingValues = this.getMissingValues(operationData.getData());
if (missingValues.length > 0) {
return false;
}
return true;
// Read-one operation can possibly have some required parameters as well. This is not exposed to Dave at the moment but when it's gonna be,
// should these be an instance of Condition as for READ_MANY or is it just record object as in case of CREATE/UPDATE/DELETE ??
case Operation.Type.READ_ONE:
default:
return true;
}
};
Operation.prototype.getMissingValues = function(record) {
// This is sort of messy as Query object actually holds an instances of QueryDescription and QueryParameterDescription and these are holding information
// about whether certain property is required or not. It would make sense to split implementation of {@link data/js/api/OperationInput OperationInput}
// at some point and create two child implemenations of OperationInput. That way we could let only "ReadOperationInput.js" to hold Query object. And
// similarly something like "NonReadOperation.js" could hold some array of mandatory values which would be much more simplier than QueryParameterDescription
// itself which holds much more information that are in fact required only for READ-type of Operations.
var missingValues = [];
var input = this.getInput();
var queryDesc = input ? input.getQuery() : undefined;
if (queryDesc) {
var requiredParams = queryDesc.getRequiredQueryParameters();
for (var i = 0; i < requiredParams.length; i++) {
var queryParamDesc = requiredParams[i];
var propertyID = queryParamDesc.getPropertyID();
// If at least one required parameter is missing value in the given "record", this is unsupported operation
if (record[propertyID] === undefined) {
missingValues.push(propertyID);
}
}
}
return missingValues;
};
/**
* Checks whether this operation works over the given {@link Query}.
*
* <p>
* If the given Condition contains
* </p>
*
* @param {Query} query
* @returns {Boolean}
*/
Operation.prototype.supportsQuery = function(query) {
var input = this.getInput();
var currentQueryDesc = input ? input.getQuery() : undefined;
var condition = query && query.getCondition();
if (condition && !(condition instanceof EmptyCondition)) {
var conditions = query.getQueryParameters();
var conditionCounts = {}; // Map of <PropertyID, Number> where number is the count of all SimpleCondition's working over the PropertyID
// Some Condition were passed but this Operation don't support any input
if (!currentQueryDesc) {
return false;
}
for (var i = 0; i < conditions.length; i++) {
var simpleCondition = conditions[i];
var fieldPath = simpleCondition.getFieldPath();
var fieldPathId = fieldPath.getId();
var operator = simpleCondition.getOperator();
// Increase counter of condition numbers
if (conditionCounts[fieldPathId]) {
conditionCounts[fieldPathId]++;
} else {
conditionCounts[fieldPathId] = 1;
}
var queryParameterExists = currentQueryDesc.getQueryParameter(fieldPath);
// If there is no description parameter for this Condition, this Operation can't support such input
if (!queryParameterExists) {
// BUFP-12626:
// Remove this hack and code it properly if the direction change happened again and we get back to this code
if (fieldPathId.indexOf('.') > 0) {
// short term workaround to allow custom code to take advantage of "Framework Version 2" and search by properties of children objects
// where child property ID consist of child BO and property separated by dot, for example: "types.PartnerTypeCode" see. "Framework Version 2" at
// <http://docs.oracle.com/middleware/12212/adf/develop/GUID-8F85F6FA-1A13-4111-BBDB-1195445CB630.htm#ADFFD-GUID-9F6BC67D-453E-4E97-B397-DC662A4D4325>
} else {
return false;
}
} else {
// Or even if the parameter description exists but the given Operator is unsupported
var supportedOperators = queryParameterExists.getSupportedOperators();
if (!ArrayUtils.contains(supportedOperators, operator)) {
return false;
}
}
}
if (!currentQueryDesc.isQueryByIDs() && !currentQueryDesc.isQueryByAnything()) {
// Map of <PropertyID, Number> where number is the count of all QueryParameters defined in this Operation for the corresponding PropertyID
var queryParameterCounts = {};
var queryParameterProperties = currentQueryDesc.getProperties();
for (var k = 0; k < queryParameterProperties.length; k++) {
var queryParamProperty = queryParameterProperties[k];
var queryParamID = queryParamProperty.getId();
// Increase counter of query parameter numbers
if (queryParameterCounts[queryParamID]) {
queryParameterCounts[queryParamID]++;
} else {
queryParameterCounts[queryParamID] = 1;
}
}
// Let's check whether more SimpleConditions working over the same property were passed but the Operation can handle only one
for (var key in conditionCounts) {
if (conditionCounts.hasOwnProperty(key)) {
var conditionNumber = conditionCounts[key];
var queryParameterCount = queryParameterCounts[key];
// If there is at least one occurence where number of Conditions is higher than
// what's defined in the description this operation can't handle the given input
if (conditionNumber > queryParameterCount) {
return false;
}
}
}
}
return true;
} else {
if (currentQueryDesc && currentQueryDesc.hasRequiredQueryParameter()) {
// If current Query has some required parameter, it's obviously missing
return false;
} else {
// But otherwise all are optional, so we definitely support Condition.EMPTY
return true;
}
}
};
Operation.prototype.hasOutputData = function(entity) {
if (entity === undefined) {
var output = this._output;
var outputEntityID = output.getData();
return outputEntityID.length > 0 && !output.isBlank();
}
var entityID = entity.getId();
return this._output && entityID.length > 0 && entityID === this._output.getData();
};
/**
* Gets the {@link bop/js/api/operation/Pagination Pagination} metadata information set for this <code>Operation</code>.
*
* @returns {bop/js/api/operation/Pagination}
*/
Operation.prototype.getPagination = function() {
return this._input ? this._input.getPagination() : Pagination.NONE;
};
/**
* Represents behavior classification of single {@link operation/js/api/Operation Operation} instance.
*
* <p>
* Type of the {@link operation/js/api/Operation Operation} is the most important information that Abcs needs to understand
* in order to see the logical purpose of the operation itself. The information is used to make the action accessible on
* appropriate places within Abcs UI.
* </p>
*
* <p>
* <ul>
* <li>
* If your operation is expected to be available for retrieving collection data, it needs to have {@link operation/js/api/Operation.Type.READ_MANY Operation.Type.READ_MANY}
* set. That makes it available for example when client drops the Table into an empty page.
* </li>
* <li>
* If you would also like to have Create page available for such table, you need to provide operation with {@link operation/js/api/Operation.Type.CREATE Operation.Type.CREATE}
* type which gives Abcs an understanding of what to perform when "Create" button is being clicked.
* </li>
* <li>
* If you would also like to have Delete action available for each Collection record, you need to provide operation with
* {@link operation/js/api/Operation.Type.DELETE Operation.Type.DELETE} type which gives Abcs an understanding of what to perform when "Delete" item is being clicked.
* </li>
* <li>
* If you would like to have also Edit/Detail pages available for the selected Collection record, you will need to implement
* operation with {@link operation/js/api/Operation.Type.READ_ONE Operation.Type.READ_ONE} type and with special type set to {@link operation/js/api/Operation.SpecialType.QUERY_BY_ID QUERY_BY_ID}
* which gives Abcs a possibility to read whole record. It also allows the infrastructure to restore bookmarked Edit/Detail page.<br/>
*
* To make Edit page working correctly, you also need to provide operation with {@link operation/js/api/Operation.Type.UPDATE Operation.Type.UPDATE} type which gives Abcs an
* understanding of what to perform when either Edit or Detail button is being clicked.
* </li>
* <li>
* If you would also like to provide an Action-like operation which performs certain business rule (e.g. Fire an employee, Raise Salary etc.),
* {@link operation/js/api/Operation.Type.PERFORM Operation.Type.PERFORM} should be used. Although, please be aware that these are unsupported by ABCS UI at the moment and because of that, no
* functionality is guaranteed by ABCS itself.
* </li>
* <ul>
* </p>
*
* @AbcsExtension stable
* @version 17.1.1
* @enum {String}
*/
Operation.Type = {
/**
* Operation that reads a single record.
*/
READ_ONE: 'RETRIEVE_ONE',
/**
* Operation that reads multiple records.
*/
READ_MANY: 'RETRIEVE_MANY',
/**
* Operation that creates a single record.
*/
CREATE: 'CREATE_ONE',
/**
* Operation that updates a single record.
*/
UPDATE: 'UPDATE_ONE',
/**
* Operation that deletes a single record.
*/
DELETE: 'DELETE_ONE',
/**
* Operation that performs non-CRUD action.
*/
PERFORM: 'PERFORM'
};
/**
* Represents a special kind of an {@link operation/js/api/Operation Operation}.
*
* <p>
* Special type is relevant only in cases of {@link operation/js/api/Operation Operation} instance with standard type set to
* {@link operation/js/api/Operation.Type.READ_MANY Operation.Type.READ_MANY} or {@link operation/js/api/Operation.Type.READ_ONE Operation.Type.READ_ONE}.
* For all other {@link operation/js/api/Operation.Type Operation.Type}s, the value is ignored and resulted behavior is not guaranteed by ABCS.
* </p>
*
* @AbcsExtension stable
* @version 17.1.1
* @enum {String}
*
* @see {@link entity/js/api/Entity Entity}
*/
Operation.SpecialType = {
/**
* Marks an {@link operation/js/api/Operation Operation} capable to retrieve single record by ID.
*
* <p>
* Having an {@link operation/js/api/Operation Operation} instance marked with this special type has strong impact
* on the resulted capabilities of the {@link entity/js/api/Entity Entity} which supports such operation:
* <ul>
* <li>
* If an {@link entity/js/api/Entity Entity} provides {@link operation/js/api/Operation Operation} of type {@link operation/js/api/Operation.SpecialType.QUERY_BY_ID QUERY_BY_ID},
* it allows ABCS UI to make Edit/Detail pages available for that {@link entity/js/api/Entity Entity}. This is because ABCS needs to be able to fetch only particular record when
* you click on Edit/Detail action in the table itself.
* </li>
* <li>
* Also Edit/Detail pages of that {@link entity/js/api/Entity Entity} will become automatically bookmarkable. This is caused by the fact that later ABCS needs to be able to
* retrieve opened record from bookmarked URL. Since the URL contains ID of the record, ABCS reads the record automatically by using the {@link operation/js/api/Operation Operation}
* marked with special type {@link operation/js/api/Operation.SpecialType.QUERY_BY_ID QUERY_BY_ID}.
* </li>
* </ul>
* </p>
*
* <p>
* Please be aware, that only one {@link operation/js/api/Operation Operation} of type {@link operation/js/api/Operation.SpecialType.QUERY_BY_ID QUERY_BY_ID}
* is expected for each {@link entity/js/api/Entity Entity}. If your BOP provides more {@link operation/js/api/Operation Operation}s of this type, the
* behavior is not guaranteed by ABCS.
* </p>
*
* @AbcsExtension stable
* @version 17.1.1
* @type {String}
*/
QUERY_BY_ID: 'queryByID',
/**
* Marks an {@link operation/js/api/Operation Operation} capable to retrieve many records using the given list of IDs.
*
* <p>
* Includes a capability for ABCS to fetch many records given their IDs. Such {@link operation/js/api/Operation Operation} is used when
* {@link entity/js/api/Entity Entity} which provides this {@link operation/js/api/Operation Operation} is being referenced from other
* {@link entity/js/api/Entity Entity}. If there is a table of referencing {@link entity/js/api/Entity Entity} and the table has visible
* column which correspond to the referenced {@link entity/js/api/Entity Entity}, operation of type {@link operation/js/api/Operation.SpecialType.QUERY_BY_IDS QUERY_BY_IDS}
* will be used to fetch all referenced values needed for the table view. And it will be done effectively in one call.
* </p>
*
* <p>
* To illustrate on an example:
* <ul>
* <li>
* ABCS application consists of two Entities: Employee and Department
* </li>
* <li>
* Department contains few {@link entity/js/api/Property Properties}: Id (KEY), Name (TEXT)<br/>
* Name could be string like 'Sales', 'Department' etc.
* </li>
* <li>
* Employee contains few {@link entity/js/api/Property Properties}: Id (KEY), Firstname (TEXT), Lastname (TEXT), Age (NUMBER)
* </li>
* <li>
* Each Employee also has it's Department assigned: refToEmployee (REFERENCE)
* </li>
* </ul>
*
* Now assume that ABCS client want to have an Employee table on the Home page and the table is configured to show Firstname, Lastname and
* Department-Name columns. If client opens Home page, we need to fetch all Employees and for each record fetch assigned Department to be
* able show Department name instead of just ID. To make this effective, ABCS looks for {@link operation/js/api/Operation Operation} of type
* {@link operation/js/api/Operation.SpecialType.QUERY_BY_IDS QUERY_BY_IDS} and use that one if available. If it's not provided by the referred
* {@link entity/js/api/Entity Entity} (Department in this case), ABCS attempts to do the same using {@link operation/js/api/Operation Operation}
* of type {@link operation/js/api/Operation.SpecialType.QUERY_BY_ID QUERY_BY_ID} which is obviously much less effective as one call needs to be
* done per each visible row.
* </p>
*
* <p>
* Please be aware, that only one {@link operation/js/api/Operation Operation} of type {@link operation/js/api/Operation.SpecialType.QUERY_BY_IDS QUERY_BY_IDS}
* is expected for each {@link entity/js/api/Entity Entity}. If your BOP provides more {@link operation/js/api/Operation Operation}s of this type, the behavior
* is not guaranteed by ABCS.
* </p>
*
* @AbcsExtension stable
* @version 17.1.1
* @type {String}
*/
QUERY_BY_IDS: 'queryByIDs',
/**
* Marks an {@link operation/js/api/Operation Operation} capable of handling any kind of input {@link operation/js/api/Condition Condition}.
*
* <p>
* Having an {@link operation/js/api/Operation Operation} of this special type available gives ABCS an opportunity to query any kind of data.
* This is used for ABCS UIs like "Advanced Search" or "Default Query" which, thanks to having this {@link operation/js/api/Operation Operation}
* available, can give ABCS user much more flexibility on how to visually build the query.
* </p>
*
* <p>
* Please be aware that by registering the {@link operation/js/api/Operation Operation} of this type, you're obligating that the operation function
* will handle all possible combinations of AND/OR {@link module:bop/js/api/operation/RelationOperator RelationOperator}s between unlimited number of parameters.
* If that's not true, ABCS UI might allow final client to setup query which is actually unsupported by your BOP.
* </p>
*
* <p>
* To illustrate on an example:
*
* <ul>
* <li>
* Assume ABCS application contains Employee {@link entity/js/api/Entity Entity}.
* </li>
* <li>
* Employee contains few {@link entity/js/api/Property Properties}: Id (KEY), Firstname (TEXT), Lastname (TEXT), Age (NUMBER)
* </li>
* <li>
* Employee also provides an {@link operation/js/api/Operation Operation} of type {@link operation/js/api/Operation.SpecialType.QUERY_BY_ANYTHING QUERY_BY_ANYTHING}.
* </li>
* </ul>
*
* Now assume that ABCS client wants to have an Employee table on the Home page and opens "Default Query" editor. Since {@link operation/js/api/Operation Operation} of type
* {@link operation/js/api/Operation.SpecialType.QUERY_BY_ANYTHING QUERY_BY_ANYTHING} is available, ABCS allows client to Add/Remove any number of query rows. It also allows
* client to set any {@link module:operation/js/api/Operator Operator}s which is by default supported by ABCS. Because the client is capable to make very various configurations
* and because he always expects from ABCS that his application is going to work, the underlying function making the real call has to be capable to manage all these possible
* configurations. Otherwise client app will became broken.
* </p>
*
* <p>
* Please be aware, that only one {@link operation/js/api/Operation Operation} of type {@link operation/js/api/Operation.SpecialType.QUERY_BY_ANYTHING QUERY_BY_ANYTHING}
* is expected for each {@link entity/js/api/Entity Entity}. If your BOP provides more {@link operation/js/api/Operation Operation}s of this type, the behavior is not
* guaranteed by ABCS.
* </p>
*
* @AbcsExtension stable
* @version 17.1.1
* @type {String}
*/
QUERY_BY_ANYTHING: 'queryByAnything'
};
return Operation;
});