import moment from 'moment' ; import { upperCaseSnakeCase } from '@sap-cloud-sdk/util' ; import { EntityApi , EntityBase } from '../entity-base' ; import { EdmTypeShared } from '../edm-types' ; import { FilterLambdaExpression , isFilterLambdaExpression , Filterable , FilterList , isFilterList , isBooleanFilterFunction , FilterLink , isFilterLink , isUnaryFilter , UnaryFilter , FilterFunction , FilterFunctionParameterType , Filter , isFilter } from '../filter' ; import { UriConverter } from '../de-serializers' ; import { ComplexTypeField , OneToManyLink } from '../selectable' ; type GetFilterType = < EntityT extends EntityBase > ( filter : Filterable < EntityT , any > , entityApi : EntityApi < EntityT , any > ) => Partial < { filter : string ; } > ; internal export interface GetFilter { getFilter : GetFilterType ; } param uriConverter returns internal export function createGetFilter ( uriConverter ) { typeparam param filter param entityApi returns function getFilter < EntityT extends EntityBase > ( filter , entityApi ) { if ( typeof filter !== 'undefined' ) { const filterExpression = getODataFilterExpression ( filter , [ ] , entityApi ) ; if ( filterExpression ) { return { filter : encodeURIComponent ( filterExpression ) } ; } } return { } ; } function getODataFilterExpression < FilterEntityT extends EntityBase > ( filter , parentFieldNames = [ ] , targetEntityApi , lambdaExpressionLevel = 0 ) { if ( isFilterList ( filter ) ) { return getODataFilterExpressionForFilterList ( filter , parentFieldNames , targetEntityApi , lambdaExpressionLevel ) ; } if ( isFilterLink ( filter ) ) { return getODataFilterExpressionForFilterLink ( filter , parentFieldNames , lambdaExpressionLevel ) ; } if ( isFilter ( filter ) ) { return getODataFilterExpressionForFilter ( filter , parentFieldNames , targetEntityApi ) ; } if ( isBooleanFilterFunction ( filter ) ) { return filterFunctionToString ( filter , parentFieldNames ) ; } if ( isUnaryFilter ( filter ) ) { return getODataFilterExpressionForUnaryFilter ( filter , parentFieldNames , targetEntityApi ) ; } if ( isFilterLambdaExpression ( filter ) ) { return getODataFilterExpressionForFilterLambdaExpression ( filter , parentFieldNames , targetEntityApi , lambdaExpressionLevel ) ; } if ( filter instanceof OneToManyLink ) { return getODataFilterExpressionForFilterLink ( filter . _filters , parentFieldNames , lambdaExpressionLevel ) ; } throw new Error ( `Could not construct query parameters from filter. Filter is not valid: ${ JSON . stringify ( filter ) }` ) ; } function retrieveField ( filterField , targetEntityApi , filterEdmType ? ) { const [ fieldName ] = filterField . split ( '/' ) ; const field = targetEntityApi . schema [ upperCaseSnakeCase ( fieldName ) ] ; if ( field instanceof ComplexTypeField ) { return Object . values ( field ) . filter ( pField => pField ?. fieldPath ) . find ( pField => pField . fieldPath ( ) === filterField ) ; } return field || { edmType : filterEdmType } ; } function filterFunctionToString < EntityT extends EntityBase , FieldT > ( filterFunction , parentFieldNames = [ ] ) { const params = filterFunction . parameters . map ( param => filterFunctionParameterToString ( param , parentFieldNames ) ) . join ( ',' ) ; return `${ filterFunction . functionName }(${ params })` ; } function filterFunctionParameterToString < EntityT extends EntityBase > ( param , parentFieldNames ) { if ( typeof param === 'number' ) { return param . toString ( ) ; } if ( typeof param === 'string' ) { return uriConverter ( param , 'Edm.String' ) ; } if ( param instanceof FilterFunction ) { return filterFunctionToString ( param , parentFieldNames ) ; } if ( moment . isMoment ( param ) ) { return uriConverter ( param , 'Edm.DateTimeOffset' ) ; } if ( Array . isArray ( param ) ) { return `[${ param . map ( p => filterFunctionParameterToString ( p , parentFieldNames ) ) . join ( ',' ) }]` ; } return [ ... parentFieldNames , param . _fieldName ] . join ( '/' ) ; } function convertFilterValue ( value , edmType ) { return Array . isArray ( value ) ? `[${ value . map ( v => uriConverter ( v , edmType ) ) . join ( ',' ) }]` : uriConverter ( value , edmType ) ; } function getODataFilterExpressionForUnaryFilter < FilterEntityT extends EntityBase > ( filter , parentFieldNames , targetEntityApi ) { return `${ filter . operator } (${ getODataFilterExpression ( filter . singleOperand , parentFieldNames , targetEntityApi ) })` ; } function getODataFilterExpressionForFilterLambdaExpression < FilterEntityT extends EntityBase > ( filter , parentFieldNames , targetEntityApi , lambdaExpressionLevel ) { const alias = `a${ lambdaExpressionLevel }` ; const filterExp = getODataFilterExpression ( filter . filters , [ alias ] , targetEntityApi , lambdaExpressionLevel + 1 ) ; return `${ parentFieldNames . join ( '/' ) }/${ filter . lambdaOperator }(${ alias }:${ filterExp })` ; } function getODataFilterExpressionForFilterList < FilterEntityT extends EntityBase > ( filter , parentFieldNames , targetEntityApi , lambdaExpressionLevel ) { let andExp = filter . andFilters . map ( subFilter => getODataFilterExpression ( subFilter , parentFieldNames , targetEntityApi , lambdaExpressionLevel ) ) . filter ( f => ! ! f ) . join ( ' and ' ) ; andExp = andExp ? `(${ andExp })` : andExp ; let orExp = filter . orFilters . map ( subFilter => getODataFilterExpression ( subFilter , parentFieldNames , targetEntityApi , lambdaExpressionLevel ) ) . filter ( f => ! ! f ) . join ( ' or ' ) ; orExp = orExp ? `(${ orExp })` : orExp ; const exp = [ ] ; if ( andExp ) { exp . push ( andExp ) ; } if ( orExp ) { exp . push ( orExp ) ; } return exp . join ( ' and ' ) ; } function getODataFilterExpressionForFilterLink < FilterEntityT extends EntityBase > ( filter , parentFieldNames , lambdaExpressionLevel ) { let linkExp = filter . filters . map ( subFilter => getODataFilterExpression ( subFilter , [ ... parentFieldNames , filter . link . _fieldName ] , filter . link . _linkedEntityApi , lambdaExpressionLevel ) ) . filter ( f => ! ! f ) . join ( ' and ' ) ; linkExp = linkExp ? `(${ linkExp })` : linkExp ; return linkExp ; } function getODataFilterExpressionForFilter < FilterEntityT extends EntityBase > ( filter , parentFieldNames , targetEntityApi ) { if ( typeof filter . field === 'string' ) { const field = retrieveField ( filter . field , targetEntityApi , filter . edmType ) ; return [ [ ... parentFieldNames , filter . field ] . join ( '/' ) , filter . operator , convertFilterValue ( filter . value , field . edmType ) ] . join ( ' ' ) ; } return [ filterFunctionToString ( filter . field , parentFieldNames ) , filter . operator , convertFilterValue ( filter . value , filter . edmType ! ) ] . join ( ' ' ) ; } return { getFilter } ; }