import { camelCase , equal , isNullish } from '@sap-cloud-sdk/util' ; import { EntityBuilder } from './entity-builder' ; import { isNavigationProperty , nonEnumerable } from './properties-util' ; import type { CustomField , Field , Link } from './selectable' ; import type { RequestBuilder } from './request-builder' ; import { DefaultDeSerializers , DeSerializers } from './de-serializers' ; internal export type ODataVersionOf < T extends EntityBase > = T [ '_oDataVersion' ] ; internal export interface Constructable < EntityT extends EntityBase > { _entityName : string ; _defaultServicePath : string ; _keys : string [ ] ; new ( ... args ) : EntityT ; } internal typeparam typeparam typeparam export interface EntityApi < EntityT extends EntityBase , DeSerializersT extends DeSerializers = DefaultDeSerializers > { deSerializers : DeSerializersT ; requestBuilder ( ) : RequestBuilder < EntityT , DeSerializersT > ; entityBuilder ( ) : EntityBuilderType < EntityT , DeSerializersT > ; entityConstructor : Constructable < EntityT > ; schema : Record < string , any > ; customField < NullableT extends boolean > ( fieldName ) : CustomField < EntityT , DeSerializersT , NullableT > ; } internal export type EntityBuilderType < EntityT extends EntityBase , DeSerializersT extends DeSerializers > = { [ property in keyof Required < Omit < EntityT , keyof EntityBase > > ] : ( value : EntityT [ property ] ) => EntityBuilderType < EntityT , DeSerializersT > ; } & EntityBuilder < EntityT , DeSerializersT > ; internal export abstract class EntityBase { static _serviceName : string ; static _entityName : string ; static _defaultServicePath : string ; /**
     * The remote state of the entity.
     * Remote state refers to the last known state of the entity on the remote system from which it has been retrieved or to which it has been posted.
     * It is stored as map, where the keys are stored in the format of the original OData properties.
     */ protected remoteState : { [ keys ] : any ; } ; /**
     * The current ETag version of the entity in the remote system.
     * The ETag identified the version of the in the remote system. It will be automatically set in the "if-match" header of update requests and can be set as a custom header for delete requests.
     * When no ETag is provided by the remote system the value of this variable defaults to "*".
     */ protected _versionIdentifier : string ; /**
     * A mapper representing custom fields in an entity.
     * Custom fields are represented by their field names and the corresponding values.
     * A custom field can be added or updated using [[setCustomField]] method.
     */ protected _customFields : Record < string , any > ; abstract readonly _oDataVersion : any ; constructor ( private schema ) { nonEnumerable ( this , 'schema' ) ; nonEnumerable ( this , '_oDataVersion' ) ; nonEnumerable ( this , '_customFields' ) ; this . _customFields = { } ; } returns get versionIdentifier ( ) : string { return this . _versionIdentifier ; } returns getCustomFields ( ) { return this . _customFields ; } param fieldName returns getCustomField ( fieldName ) { return this . _customFields [ fieldName ] ; } param fieldName param value returns 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 ; } param fieldName returns hasCustomField ( fieldName ) { return this . _customFields [ fieldName ] !== undefined ; } param customFields returns setCustomFields ( customFields ) { Object . entries ( customFields ) . forEach ( ( [ key , value ] ) => { this . setCustomField ( key , value ) ; } ) ; return this ; } param etag returns setVersionIdentifier ( etag ) { if ( etag && typeof etag === 'string' ) { nonEnumerable ( this , '_versionIdentifier' ) ; this . _versionIdentifier = etag ; } return this ; } param state returns setOrInitializeRemoteState ( state ? ) { if ( ! this . remoteState ) { nonEnumerable ( this , 'remoteState' ) ; } state = state || this . asObject ( ) ; this . remoteState = Object . entries ( state ) . reduce ( ( stateObject , [ fieldName , value ] ) => { const propertyName = this [ camelCase ( fieldName ) ] ? camelCase ( fieldName ) : fieldName ; return { ... stateObject , [ propertyName ] : value } ; } , { } ) ; return this ; } returns 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 getUpdatedProperties ( ) { const current = this . asObject ( ) ; return this . getUpdatedPropertyNames ( ) . reduce ( ( patch , key ) => ( { ... patch , [ key ] : current [ key ] } ) , { } ) ; } returns 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 => ! equal ( this . remoteState [ key ] , currentState [ key ] ) ) ; } returns toJSON ( ) { return { ... this , ... this . _customFields } ; } protected isVisitedEntity < EntityT extends EntityBase > ( entity , visitedEntities = [ ] ) { return Array . isArray ( entity ) ? entity . some ( multiLinkChild => visitedEntities . includes ( multiLinkChild ) ) : visitedEntities . includes ( entity ) ; } protected getCurrentStateForKey ( key , visitedEntities = [ ] ) { if ( isNavigationProperty ( key , this . schema ) ) { if ( 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 ] ; } param customFieldName returns protected isConflictingCustomField ( customFieldName ) { return Object . values ( this . schema ) . map ( ( f ) => f . _fieldName ) . includes ( customFieldName ) ; } param visitedEntities returns protected asObject ( visitedEntities = [ ] ) { visitedEntities . push ( this ) ; return Object . keys ( this ) . filter ( key => this . propertyIsEnumerable ( key ) && ( ! isNavigationProperty ( key , this . schema ) || ! this . isVisitedEntity ( this [ key ] , visitedEntities ) ) ) . reduce ( ( accumulatedMap , key ) => ( { ... accumulatedMap , [ key ] : this . getCurrentStateForKey ( key , visitedEntities ) } ) , this . getCustomFields ( ) ) ; } } internal export interface EntityIdentifiable < T extends EntityBase , DeSerializersT extends DeSerializers > { readonly _entity : T ; readonly _deSerializers : DeSerializersT ; } internal export function isSelectedProperty < EntityT extends EntityBase , DeSerializersT extends DeSerializers > ( json , field ) { return json . hasOwnProperty ( field . _fieldName ) ; } internal export function isExistentProperty < EntityT extends EntityBase , DeSerializersT extends DeSerializers , LinkedEntityT extends EntityBase > ( json , link ) { return isSelectedProperty ( json , link ) && json [ link . _fieldName ] !== null ; } internal export function isExpandedProperty < EntityT extends EntityBase , DeSerializersT extends DeSerializers , LinkedEntityT extends EntityBase > ( json , link ) { return ( isExistentProperty ( json , link ) && ! json [ link . _fieldName ] . hasOwnProperty ( '__deferred' ) ) ; } internal export function entityBuilder < EntityT extends EntityBase , DeSerializersT extends DeSerializers > ( entityApi ) { const builder = new EntityBuilder < EntityT , DeSerializersT > ( entityApi ) ; Object . values ( entityApi . schema ) . forEach ( field => { const fieldName = `${ camelCase ( field . _fieldName ) }` ; builder [ fieldName ] = function ( value ) { this . entity [ fieldName ] = value ; return this ; } ; } ) ; return builder as EntityBuilderType < EntityT , DeSerializersT > ; }