UNPKG

imng-kendo-odata

Version:

This library was generated with [Nx](https://nx.dev).

527 lines (509 loc) 21.4 kB
import * as i0 from '@angular/core'; import { inject, Injectable, NgModule } from '@angular/core'; import { isCompositeFilterDescriptor, toODataString } from '@progress/kendo-data-query'; import { HttpClient } from '@angular/common/http'; import { map, filter } from 'rxjs/operators'; import { MILLI_SECS_PER_SEC, isaNumber, distinct, isaDate } from 'imng-nrsrx-client-utils'; import { CommonModule } from '@angular/common'; const emptyODataResult = { data: [], total: 0 }; function createEmptyODataResult() { return { data: Array(), total: 0 }; } function createODataResult(t) { return { data: t, total: t?.length, }; } function isODataResult(source) { return !!source?.data; } function createODataPayload(resultSet) { return { ['@odata.count']: resultSet?.length, value: resultSet, }; } function isExpander(source) { return !!source?.table; } function isComputation(source) { return !!source?.operator; } const mapToExtDataResult = (utcNullableProps = [], dateNullableProps = []) => map((response) => { if (!response) { return { data: [], total: 0 }; } const result = Array.isArray(response) ? { data: response, total: response.length, } : { data: response.value, total: response['@odata.count'], }; result.data = parseDatesInCollection(result.data, utcNullableProps, dateNullableProps); return result; }); const firstRecord = () => map((result) => result?.data?.length > 0 ? result.data[0] : {}); const findById = (id, defaultValue = {}) => map((source) => source?.data?.find((f) => f.id === id) || defaultValue); function parseDatesInCollection(collection, utcNullableProps = [], dateNullableProps = []) { if (collection?.length > 0) { const utcProps = Object.keys(collection[0]).filter((x) => x.endsWith('Utc')); const dateProps = Object.keys(collection[0]).filter((x) => x.endsWith('Date') || x === 'date'); utcNullableProps?.forEach((t) => { if (utcProps.indexOf(t) === -1) { utcProps.push(t); } }); dateNullableProps?.forEach((t) => { if (dateProps.indexOf(t) === -1) { dateProps.push(t); } }); // eslint-disable-next-line @typescript-eslint/no-explicit-any collection.forEach((val) => { //NOSONAR utcProps .filter((p) => val[p]) .forEach((p) => (val[p] = new Date(val[p]))); dateProps .filter((p) => val[p]) .forEach((p) => (val[p] = toLocalDate(val[p]))); }); } return collection; } function toLocalDate(date) { const dt = new Date(date); return new Date(dt.getTime() + Math.abs(dt.getTimezoneOffset() * MILLI_SECS_PER_SEC)); } function getSubGridData(id, mappingFunction) { // tslint:disable-next-line: space-before-function-paren return function (source) { return source.pipe(map((t) => t.data.find((f) => f.id === id)), map((entity) => mappingFunction(entity)), filter((t) => !!t)); }; } function getSubData(id, mappingFunction) { // tslint:disable-next-line: space-before-function-paren return function (source) { return source.pipe(map((t) => t.find((f) => f.id === id)), map((entity) => mappingFunction(entity)), filter((t) => !!t)); }; } function isCompositeChildFilterDescriptor(source) { return !!source?.filters; } function translateChildFilterExpression(odataState, childTableProperty) { const childFieldString = `${childTableProperty.table}.${childTableProperty.field}`; // eslint-disable-next-line @typescript-eslint/no-explicit-any const filterPredicate = (filter) => { return filter.field === childFieldString; }; const childTableFilter = odataState.filter?.filters.find((t) => isCompositeFilterDescriptor(t) && t.filters?.some(filterPredicate)); if (childTableFilter && odataState.filter) { odataState.filter = { ...odataState.filter, filters: [ ...odataState.filter.filters.filter((t) => isCompositeFilterDescriptor(t) && !t.filters?.some(filterPredicate)), ], }; odataState.childFilters = { logic: odataState.childFilters?.logic || 'and', filters: odataState.childFilters?.filters?.filter((childFilterDescriptor) => !isCompositeChildFilterDescriptor(childFilterDescriptor) && childFilterDescriptor.childTableNavigationProperty !== childTableProperty.table && childFilterDescriptor.field !== childTableProperty.field) || [], }; odataState.childFilters.filters?.push({ logic: childTableFilter.logic, filters: childTableFilter?.filters?.map((filter) => ({ ...filter, linqOperation: childTableProperty.linqOperation || 'any', childTableNavigationProperty: childTableProperty.table, field: childTableProperty.field, })), }); } return odataState; } const filterQueryStringParamLen = 8; //"$filter=" const stringFilterOperators = [ `startswith`, `endswith`, `contains`, `doesnotcontain`, `isempty`, `isnotempty`, ]; function processChildFilterDescriptors(state, queryString) { if (!state.childFilters) { return queryString; } else { return transformCompositeChildFilter(state.childFilters, queryString); } } function transformChildExistsFilter(childExistsFilter, logic, queryString) { const filterString = `$filter=${childExistsFilter.childTableNavigationProperty}/${childExistsFilter.linqOperation ?? 'any'}(${childExistsFilter.filter ?? ''})`; if (queryString.match(/\$filter=/)) { queryString = queryString.replace(/\$filter=/, `${filterString} ${logic || 'and'} `); } else { queryString += `&${filterString}`; } return queryString; } function transformCompositeChildFilter(compositeChildFilter, queryString) { let tempFilterString = ''; compositeChildFilter.filters ?.filter((filter) => !isCompositeChildFilterDescriptor(filter)) .forEach((filter, index, array) => { tempFilterString += index === 0 && array.length > 1 ? '(' : ''; tempFilterString += transformChildFilter(filter); if (index === array.length - 1 && array.length > 1) { tempFilterString += ')'; } else if (index !== array.length - 1) { tempFilterString += ` ${compositeChildFilter.logic || 'and'} `; } }); if (tempFilterString.length > 0) { if (queryString.match(/\$filter=/)) { queryString = queryString.replace(/\$filter=/, `$filter=${tempFilterString} and `); } else { queryString += `&$filter=${tempFilterString}`; } } compositeChildFilter.filters ?.filter((filter) => isCompositeChildFilterDescriptor(filter)) .forEach((filter) => { queryString = transformCompositeChildFilter(filter, queryString); }); compositeChildFilter.existsFilters?.forEach((filter) => { queryString = transformChildExistsFilter(filter, compositeChildFilter.logic, queryString); }); return queryString; } function transformChildFilter(childFilter) { let filteringString; if (-1 < stringFilterOperators.findIndex((x) => x === childFilter.operator) && !isaNumber(childFilter.value)) { filteringString = `${childFilter.operator}(o/${childFilter.field}, '${childFilter.value}')`; } else { filteringString = `o/${toODataString({ filter: { logic: 'and', filters: [childFilter] }, }).slice(filterQueryStringParamLen)}`; } return (`${childFilter.childTableNavigationProperty}/${childFilter.linqOperation}` + `(o: ${filteringString})`); } function translateChildSortingExpression(odataState, childTableProperties) { if (!childTableProperties || childTableProperties.length === 0) { return odataState; } const childTableStrings = distinct(childTableProperties.map((x) => x.table)); const childTablePropertyStrings = childTableProperties.map((x) => `${x.table}.${x.field}`); const filterPredicate = (x) => childTablePropertyStrings.indexOf(x.field) > -1; const sortedColumns = odataState.sort ?.filter(filterPredicate) .map((m) => ({ ...m, childProperty: m.field.split('.') })); if (sortedColumns && sortedColumns.length > 0) { odataState.sort = odataState.sort?.filter((x) => !filterPredicate(x)); odataState.expanders = odataState.expanders?.map((m) => (isExpander(m) ? { ...m } : m)); const expanders = odataState.expanders ?.filter((t) => childTableStrings.indexOf(t.table) > -1) .map((t) => { t.sort = (t.sort || []).filter((f) => !sortedColumns.find((s) => t.table === s.childProperty[0] && f.field === s.childProperty[1])); return t; }); sortedColumns.forEach((x) => { const expander = expanders?.find((e) => e.table === x.childProperty[0]); expander?.sort?.push({ field: x.childProperty[1], dir: x.dir }); }); } return odataState; } class ODataService { constructor() { this.http = inject(HttpClient); } fetch(odataEndpoint, state, options = {}) { let tempState = { ...state }; options.boundChildTableProperties?.forEach((prop) => (tempState = translateChildFilterExpression(tempState, prop))); tempState = translateChildSortingExpression(tempState, options.boundChildTableProperties); const countClause = tempState.count === false ? '' : '&$count=true'; const cacheBustClause = options.bustCache === true ? `&timestamp=${new Date().toISOString().replace(/[-:.TZ]/g, '')}` : ''; const queryStr = `${this.getODataString(tempState)}${countClause}${cacheBustClause}`; return this.http .get(`${odataEndpoint}?${queryStr}${this.getAdditionalParams(options)}`) .pipe(mapToExtDataResult(options.utcNullableProps || [], options.dateNullableProps || [])); } getAdditionalParams(options) { if (options.additionalParams) { const keys = Object.keys(options.additionalParams); const values = Object.values(options.additionalParams); const queryStringParams = keys.map((key, index) => `&${encodeURIComponent(key)}=${encodeURIComponent(values[index])}`); return queryStringParams.join(''); } return ''; } fetchByPrimaryKey(odataEndpoint, id, state) { const request = { expanders: state?.expanders, selectors: state?.selectors, filter: { logic: 'and', filters: [{ operator: 'eq', field: 'id', value: id }], }, }; const queryStr = this.getODataString(request); return this.http .get(`${odataEndpoint}?${queryStr}`) .pipe(mapToExtDataResult(), firstRecord()); } getODataString(state) { let queryString = toODataString(state); queryString = this.processExpanders(state, queryString); queryString = this.processSelectors(state, queryString); queryString = processChildFilterDescriptors(state, queryString); queryString = this.processInFilters(state.inFilters, queryString, false); queryString = this.processInFilters(state.notInFilters, queryString, true); queryString = this.applyTransformations(state, queryString); queryString = this.processComputations(state, queryString); queryString = this.processGuids(queryString); queryString = this.processDates(queryString); return queryString; } applyTransformations(state, queryString) { if (state.transformations) { queryString += `&$apply=${state.transformations}`; } return queryString; } processDates(queryString) { const dateRegex = /Date [e-t]{2} \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{3}Z/g; let m; const dateMatches = []; while ((m = dateRegex.exec(queryString)) !== null) { // This is necessary to avoid infinite loops with zero-width matches if (m.index === dateRegex.lastIndex) { dateRegex.lastIndex++; } m.forEach((match) => { dateMatches.push(match); }); } dateMatches.forEach((date) => (queryString = queryString.replace(date, date.replace(/T\d{2}:\d{2}:\d{2}.\d{3}Z/g, '')))); return queryString; } processGuids(queryString) { const guidRegex = /'[\dA-F]{8}-?[\dA-F]{4}-?[\dA-F]{4}-?[\dA-F]{4}-?[\dA-F]{12}'/gi; let m; const guidMatches = []; while ((m = guidRegex.exec(queryString)) !== null) { // This is necessary to avoid infinite loops with zero-width matches if (m.index === guidRegex.lastIndex) { guidRegex.lastIndex++; } m.forEach((match) => { guidMatches.push(match); }); } guidMatches.forEach((guid) => (queryString = queryString.replace(guid, guid.replace(/'/g, '')))); return queryString; } processSelectors(state, queryString) { if (state.selectors && state.selectors.length > 0) { queryString += `&$select=${state.selectors.join()}`; } return queryString; } processExpanders(state, queryString) { if (state.expanders && state.expanders.length > 0) { const expansionStrings = state.expanders.map((element) => this.getExpansionString(element)); queryString += `&$expand=${expansionStrings.join(',')}`; } return queryString; } getExpansionString(element) { let result = ''; if (!element) { return result; } if (typeof element === 'string') { result += element; } else { result += `${element.table}(`; if (element.selectors && element.selectors.length > 0) { result += `$select=${element.selectors.join()};`; } if (element.filter || element.sort) { const state = { sort: element.sort, filter: element.filter, }; result += `${toODataString(state).replace('&', ';')};`; } if (element.expanders) { const expanders = element.expanders.map((expander) => { if (isExpander(expander)) { return this.getExpansionString(expander); } return expander; }); result += `$expand=${expanders.join(',')};`; } if (element.count) { result += `${/[(;]$/.exec(result) ? '' : ';'}$count=true`; } result += ')'; result = result.replace(/\(\)/, '').replace(/;\)/, ')'); } return result; } processInFilters(inFilters, queryString, isNotIn) { if (!inFilters) { return queryString; } inFilters.forEach((inFilter) => { const deDupedVals = Array.from(new Set(inFilter.values.filter((f) => f))); const inVals = deDupedVals .map((m) => { if (isaNumber(m)) { return `${m}`; } else if (isaDate(m)) { return `${m.toISOString()}`; } return `'${m}'`; }) .join(','); const inFilterString = `(${inFilter.field} in (${inVals})${isNotIn ? ' eq false' : ''})`; if (!queryString || queryString.trim().length === 0) { queryString = `$filter=${inFilterString}`; } else if (queryString.match(/\$filter=/)) { queryString = queryString.replace(/\$filter=/, `$filter=${inFilterString} ${inFilter.logic ?? 'and'} `); } else { queryString = `${queryString}&$filter=${inFilterString}`; } }); return queryString; } processComputations(state, queryString) { if (state.compute) { const computeStrings = state.compute .filter((f) => !isComputation(f)) .map((f) => f.toString()); computeStrings.push(...state.compute .filter(isComputation) .map((f) => `${f.fieldA} ${f.operator} ${f.fieldB} as ${f.alias}`)); queryString = `$compute=${computeStrings.join(',')}${queryString}`; } return queryString; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: ODataService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: ODataService, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: ODataService, decorators: [{ type: Injectable, args: [{ providedIn: 'root', }] }] }); class ImngKendoODataModule { static forRoot() { return { ngModule: ImngKendoODataModule, providers: [ODataService], }; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: ImngKendoODataModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); } static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.1.4", ngImport: i0, type: ImngKendoODataModule, imports: [CommonModule] }); } static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: ImngKendoODataModule, providers: [ODataService], imports: [CommonModule] }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.4", ngImport: i0, type: ImngKendoODataModule, decorators: [{ type: NgModule, args: [{ imports: [CommonModule], providers: [ODataService], }] }] }); function isFilterDescriptor(source) { return !!source?.field; } function findMatchingFilters(odataState, filterField) { if (!odataState.filter) { return; } return flattenCompositeFilters(odataState.filter).find((x) => x.field === filterField); } function flattenCompositeFilters(filters) { return filters.filters.map((x) => (isCompositeFilterDescriptor(x) ? flattenCompositeFilters(x) : [x])).flat(); } function removeMatchingFilters(odataState, filterField) { return { ...odataState, filter: !odataState.filter ? { logic: 'and', filters: [] } : { ...odataState.filter, filters: filterFilters(odataState.filter, filterField) }, }; } function filterFilters(filters, filterField) { return [ ...(isCompositeFilterDescriptor(filters) ? filters.filters : filters).filter((m) => { if (isCompositeFilterDescriptor(m)) { m.filters = [...filterFilters(m.filters, filterField)]; return true; } else { return m.field !== filterField; } }), ]; } function updateFilter(compositefilter, newFilter) { compositefilter.filters.forEach(subFilter => { if (isCompositeFilterDescriptor(subFilter)) { updateFilter(subFilter, newFilter); } else if (subFilter.field === newFilter.field) { compositefilter = { ...compositefilter, filters: [...compositefilter.filters.filter(f => isCompositeFilterDescriptor(f) || f.field !== newFilter.field), newFilter] }; } }); return compositefilter; } function applyFilter(odataState, filter) { if (!odataState.filter) { odataState.filter = { logic: 'and', filters: [] }; } const matchedFilter = findMatchingFilters(odataState, filter.field); if (!matchedFilter) { odataState.filter.filters.push({ logic: 'and', filters: [filter] }); } else { updateFilter(odataState.filter, filter); } return odataState; } /** * Generated bundle index. Do not edit. */ export { ImngKendoODataModule, ODataService, applyFilter, createEmptyODataResult, createODataPayload, createODataResult, emptyODataResult, filterFilters, findById, findMatchingFilters, firstRecord, flattenCompositeFilters, getSubData, getSubGridData, isCompositeChildFilterDescriptor, isComputation, isExpander, isFilterDescriptor, isODataResult, mapToExtDataResult, parseDatesInCollection, removeMatchingFilters, toLocalDate, translateChildFilterExpression, translateChildSortingExpression, updateFilter }; //# sourceMappingURL=imng-kendo-odata.mjs.map