@sap-cloud-sdk/odata-common
Version:
SAP Cloud SDK for JavaScript common functions of OData client generator and OpenAPI clint generator.
234 lines • 9.36 kB
JavaScript
"use strict";
/* eslint-disable max-classes-per-file */
Object.defineProperty(exports, "__esModule", { value: true });
exports.EntityBase = void 0;
exports.isSelectedProperty = isSelectedProperty;
exports.isExistentProperty = isExistentProperty;
exports.isExpandedProperty = isExpandedProperty;
exports.entityBuilder = entityBuilder;
const util_1 = require("@sap-cloud-sdk/util");
const entity_builder_1 = require("./entity-builder");
const properties_util_1 = require("./properties-util");
/**
* Super class for all representations of OData entity types.
*/
class EntityBase {
/**
* @internal
*/
constructor(_entityApi) {
this._entityApi = _entityApi;
(0, properties_util_1.nonEnumerable)(this, '_customFields');
(0, properties_util_1.nonEnumerable)(this, '_entityApi');
(0, properties_util_1.nonEnumerable)(this, 'remoteState');
(0, properties_util_1.nonEnumerable)(this, '_versionIdentifier');
this._customFields = {};
}
/**
* ETag version identifier accessor.
* @returns The ETag version identifier of the retrieved entity, returns `undefined` if not retrieved.
*/
get versionIdentifier() {
return this._versionIdentifier;
}
/**
* Returns a map that contains all entity custom fields.
* @returns A map of all defined custom fields in the entity.
*/
getCustomFields() {
return this._customFields;
}
/**
* Custom field value getter.
* @param fieldName - The name of the custom field.
* @returns The value of the corresponding custom field.
*/
getCustomField(fieldName) {
return this._customFields[fieldName];
}
/**
* Sets a new custom field in the entity or updates it.
* Throws an error, if the provided custom field name is already defined by an original field in entity.
* @param fieldName - The name of the custom field to update.
* @param value - The value of the field.
* @returns The entity itself, to facilitate method chaining.
*/
setCustomField(fieldName, value) {
if (this.isConflictingCustomField(fieldName)) {
throw new Error(`The field name "${fieldName}" is already defined in the entity and cannot be set as custom field.`);
}
this._customFields[fieldName] = value;
return this;
}
/**
* Validates whether a custom field exists in the entity.
* @param fieldName - The name of the custom field to update.
* @returns A boolean value, that indicates whether a custom field is defined in entity.
*/
hasCustomField(fieldName) {
return this._customFields[fieldName] !== undefined;
}
/**
* Sets custom fields on an entity.
* @param customFields - Custom fields to set on the entity.
* @returns The entity itself, to facilitate method chaining.
*/
setCustomFields(customFields) {
Object.entries(customFields).forEach(([key, value]) => {
this.setCustomField(key, value);
});
return this;
}
/**
* Set the ETag version identifier of the retrieved entity.
* @param etag - The returned ETag version of the entity.
* @returns The entity itself, to facilitate method chaining.
*/
setVersionIdentifier(etag) {
if (etag && typeof etag === 'string') {
this._versionIdentifier = etag;
}
return this;
}
/**
* Initializes or sets the remoteState of the entity.
* This function is called on all read, create and update requests.
* @param state - State to be set as remote state.
* @returns The entity itself, to facilitate method chaining.
*/
setOrInitializeRemoteState(state) {
state = state || this.asObject();
this.remoteState = Object.entries(state).reduce((stateObject, [fieldName, value]) => {
const propertyName = this[(0, util_1.camelCase)(fieldName)]
? (0, util_1.camelCase)(fieldName)
: fieldName;
return { ...stateObject, [propertyName]: value };
}, {});
return this;
}
/**
* Returns all updated custom field properties compared to the last known remote state.
* @returns An object containing all updated custom properties, with their new values.
*/
getUpdatedCustomFields() {
if (!this.remoteState) {
return this._customFields;
}
return Object.entries(this.getCustomFields())
.filter(([fieldName, value]) => this.remoteState[fieldName] !== value)
.reduce((updatedCustomFields, [fieldName, value]) => ({
...updatedCustomFields,
[fieldName]: value
}), {});
}
/**
* Returns all changed properties compared to the last known remote state.
* The returned properties do not include custom fields.
* Use {@link getUpdatedCustomFields}, if you need custom fields.
* @returns EntityBase with all properties that changed.
*/
getUpdatedProperties() {
const current = this.asObject();
return this.getUpdatedPropertyNames().reduce((patch, key) => ({ ...patch, [key]: current[key] }), {});
}
/**
* Returns all changed property names compared to the last known remote state.
* The returned properties names do not include custom fields.
* Use {@link getUpdatedCustomFields}, if you need custom fields.
* @returns EntityBase with all properties that changed.
*/
getUpdatedPropertyNames() {
const currentState = this.asObject();
const names = Object.keys(currentState).filter(key => this.propertyIsEnumerable(key) && !this.hasCustomField(key));
return !this.remoteState
? names
: names.filter(key => !(0, util_1.equal)(this.remoteState[key], currentState[key]));
}
/**
* Overwrites the default toJSON method so that all instance variables as well as all custom fields of the entity are returned.
* @returns An object containing all instance variables + custom fields.
*/
toJSON() {
return { ...this, ...this._customFields };
}
isVisitedEntity(entity, visitedEntities = []) {
return Array.isArray(entity)
? entity.some(multiLinkChild => visitedEntities.includes(multiLinkChild))
: visitedEntities.includes(entity);
}
getCurrentStateForKey(key, visitedEntities = []) {
if ((0, properties_util_1.isNavigationProperty)(key, this._entityApi.schema)) {
if ((0, util_1.isNullish)(this[key])) {
return this[key];
}
return Array.isArray(this[key])
? this[key].map(linkedEntity => linkedEntity.asObject(visitedEntities))
: this[key].asObject(visitedEntities);
}
return Array.isArray(this[key]) ? [...this[key]] : this[key];
}
/**
* Validates whether a field name does not conflict with an original field name and thus can be defined as custom fields.
* @param customFieldName - Field name to check.
* @returns Boolean value that describes whether a field name can be defined as custom field.
*/
isConflictingCustomField(customFieldName) {
return Object.values(this._entityApi.schema)
.map((f) => f._fieldName)
.includes(customFieldName);
}
/**
* Creates an object containing all defined properties, navigation properties and custom fields in the entity.
* @param visitedEntities - List of entities to check in case of circular dependencies.
* @returns EntityBase as an object with all defined entity fields.
*/
asObject(visitedEntities = []) {
visitedEntities.push(this);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
return Object.keys(this)
.filter(key => this.propertyIsEnumerable(key) &&
(!(0, properties_util_1.isNavigationProperty)(key, this._entityApi.schema) ||
!this.isVisitedEntity(this[key], visitedEntities)))
.reduce((accumulatedMap, key) => ({
...accumulatedMap,
[key]: this.getCurrentStateForKey(key, visitedEntities)
}), this.getCustomFields());
}
}
exports.EntityBase = EntityBase;
/**
* @internal
*/
function isSelectedProperty(json, field) {
return json.hasOwnProperty(field._fieldName);
}
/**
* @internal
*/
function isExistentProperty(json, link) {
return isSelectedProperty(json, link) && json[link._fieldName] !== null;
}
/**
* @internal
*/
function isExpandedProperty(json, link) {
return (isExistentProperty(json, link) &&
!json[link._fieldName].hasOwnProperty('__deferred'));
}
/**
* Create an entity builder for an entity API.
* @param entityApi - The entity API to build entities for.
* @returns An entity builder instance for the given entity API.
*/
function entityBuilder(entityApi) {
const builder = new entity_builder_1.EntityBuilder(entityApi);
Object.values(entityApi.schema).forEach((field) => {
const fieldName = `${(0, util_1.camelCase)(field._fieldName)}`;
builder[fieldName] = function (value) {
this._entity[fieldName] = value;
return this;
};
});
return builder;
}
//# sourceMappingURL=entity-base.js.map