devexpress-reporting
Version:
DevExpress Reporting provides the capability to develop a reporting application to create and customize reports.
210 lines (209 loc) • 9.91 kB
JavaScript
/**
* DevExpress HTML/JS Reporting (designer\helpers\_dataSourceHelper.js)
* Version: 24.2.6
* Build date: Mar 18, 2025
* Copyright (c) 2012 - 2025 Developer Express Inc. ALL RIGHTS RESERVED
* License: https://www.devexpress.com/Support/EULAs/universal.xml
*/
import { extend, findFirstItemMatchesCondition, getUniqueNameForNamedObjectsArray, localizeNoneString, replaceInvalidSymbols } from '@devexpress/analytics-core/analytics-internal';
import { Disposable, ModelSerializer } from '@devexpress/analytics-core/analytics-utils';
import * as ko from 'knockout';
import { DataFederationDataSource, SerializableDataFederationDataSource } from '../dataObjects/dataFederation';
import { createNewObjectItem } from '../dataObjects/objectItemCreation';
import { ObjectItem } from '../dataObjects/objectStorageItem';
export class DataSourceHelper extends Disposable {
dispose() {
super.dispose();
this._objects = null;
this.availableDataSources.splice(0);
this.usedDataSources([]);
this.allDataSources([]);
}
constructor(objects, dataSourceRefs, availableDataSources) {
super();
this.usedDataSources = ko.observableArray();
this.allDataSources = ko.observableArray();
this.usedDataSources.push({ ref: 'none', name: 'none', specifics: 'none', data: null, dataSerializer: null });
this._objects = objects;
for (let i = 0; i < objects().length; i++) {
const currentObject = objects()[i];
const ref = currentObject['_model']['@Ref'];
const dataSourceRef = dataSourceRefs.filter((ds) => { return ds.ref === ref; })[0];
if (dataSourceRef) {
const currentDataSourceInfo = {
ref: ref,
data: currentObject,
name: dataSourceRef.name,
isFederationDataSource: dataSourceRef.isFederationDataSource,
isSqlDataSource: dataSourceRef.isSqlDataSource,
isJsonDataSource: dataSourceRef.isJsonDataSource,
isObjectDataSource: dataSourceRef.isObjectDataSource,
isListType: dataSourceRef.isListType,
isSupportQueries: dataSourceRef.isSupportQueries,
dataSerializer: dataSourceRef.dataSerializer,
hasParams: dataSourceRef.hasParams,
hasErrors: dataSourceRef.hasErrors
};
currentObject['dataSourceInfo'] = currentDataSourceInfo;
this._addUsedDataSource(currentDataSourceInfo);
}
}
const self = this;
this._disposables.push(objects.subscribe((changes) => {
for (let index = 0; index < changes.length; index++) {
if (!changes[index].value['dataSourceInfo'])
return;
if (changes[index].status === 'added') {
self._addUsedDataSource(changes[index].value['dataSourceInfo']);
}
else if (changes[index].status === 'deleted') {
const dataSourceInfo = self.findDataSourceInfo(changes[index].value);
if (dataSourceInfo) {
this.usedDataSources.remove(dataSourceInfo);
this.allDataSources.remove(dataSourceInfo);
}
}
}
}, null, 'arrayChange'));
const serializer = new ModelSerializer();
this.availableDataSources = (availableDataSources || []).map((object) => {
return extend({}, object, { data: createNewObjectItem(object.data, () => { return this; }, serializer) });
});
this.allDataSources.push.apply(this.allDataSources, this.availableDataSources);
}
getDataSourcePath(dataSource) {
const dataSourceInfo = dataSource && this.findDataSourceInfo(dataSource);
if (dataSourceInfo) {
return dataSourceInfo.id || dataSourceInfo.ref;
}
else {
return '';
}
}
_findDataSourceInfo(name, collection) {
return collection().filter((info) => { return info.name === name; })[0];
}
_getDataSourceInfo(name) {
let result = this._findDataSourceInfo(name, this.usedDataSources);
if (!result) {
const resultSource = this._findDataSourceInfo(name, this.allDataSources);
if (resultSource) {
result = this._addDataSource(resultSource, resultSource.data);
}
}
return result;
}
_getDataSourceName(dataSource) {
const dataSourceInfo = this.findDataSourceInfo(dataSource);
return dataSourceInfo && dataSourceInfo.name;
}
_addUsedDataSource(result) {
this.usedDataSources.splice(this.usedDataSources().length - 1, 0, result);
this.allDataSources.push(result);
}
_addDataSource(dataSource, data, uniqueName) {
if (!dataSource.name) {
throw new Error('dataSource name is undefined or null (ref=' + dataSource.ref + ', id=' + dataSource.id + ')');
}
const dataSourceName = uniqueName || this.getUniqueDataSourceName(dataSource.name);
let newData = data;
if (this._objects().indexOf(data) === -1) {
newData = this._cloneObjectItem(data);
newData['dataSourceInfo'] = extend({}, dataSource, { name: dataSourceName, data: newData });
newData['name'] = ko.observable(dataSourceName);
this._objects.push(newData);
}
return this.findDataSourceInfo(newData);
}
_cloneObjectItem(data) {
const serializer = new ModelSerializer();
let serializedObj;
if (data instanceof DataFederationDataSource) {
serializedObj = data.getSerializableModel().serialize();
const newModel = new SerializableDataFederationDataSource(null, serializedObj, data.dsHelperProvider, serializer);
const cloneSerializableSourceMap = [];
data.serializableSourceMap().forEach(source => {
const info = this.allDataSources().filter((item) => { return item.data === source.dataSource(); })[0];
if (info) {
const existedDataSource = this.findDataSourceInfoByName(info.name);
const usedDataSource = existedDataSource || this._addDataSource(info, info.data, info.name);
cloneSerializableSourceMap.push(usedDataSource.data);
}
});
const newSerializableModel = newModel.dataSource.getSerializableModel();
newSerializableModel.dataSources(cloneSerializableSourceMap);
cloneSerializableSourceMap.forEach((item, index) => {
newSerializableModel.dataSource.serializableSourceMap()[index].dataSource(item);
});
newModel.dispose();
return newSerializableModel.dataSource;
}
else {
serializedObj = serializer.serialize(data);
return createNewObjectItem(serializedObj, data.dsHelperProvider, serializer);
}
}
getUniqueDataSourceName(name) {
return getUniqueNameForNamedObjectsArray(this.allDataSources(), replaceInvalidSymbols(name));
}
addDataSource(dataSourceInfo) {
const data = (dataSourceInfo.data instanceof ObjectItem) ? dataSourceInfo.data : createNewObjectItem(dataSourceInfo.data, () => this);
return this._addDataSource(dataSourceInfo, data).data;
}
removeDataSource(dataSourceInfo) {
this._objects.remove(dataSourceInfo.data);
}
restoreDataSource(dataSourceInfo) {
this._objects.push(dataSourceInfo.data);
}
dataSourceValue(value, undoEngine) {
const dataSourceValue = ko.pureComputed({
read: () => {
return this._getDataSourceName(value());
},
write: (val) => {
const _undoEngine = undoEngine && undoEngine();
_undoEngine && _undoEngine.start();
const newDataSource = this._getDataSourceInfo(val);
if (DataSourceHelper._assignValueInTimeout) {
setTimeout(() => {
value(newDataSource && newDataSource.data);
_undoEngine && _undoEngine.end();
}, 1);
}
else {
value(newDataSource && newDataSource.data);
_undoEngine && _undoEngine.end();
}
}
});
this._disposables.push(dataSourceValue);
return dataSourceValue;
}
dataSourceDisplayExpr(dataSource) {
return (!dataSource || !dataSource.data) ? localizeNoneString('none') : dataSource.name;
}
mergedDataSources() {
const dataSources = this.usedDataSources().slice(0, -1);
for (let i = this.availableDataSources.length - 1; i >= 0; i--) {
if (!findFirstItemMatchesCondition(dataSources, (item) => item.name === this.availableDataSources[i].name)) {
dataSources.unshift(this.availableDataSources[i]);
}
}
return dataSources;
}
findDataSourceInfo(dataSource) {
return this.usedDataSources().filter((info) => { return info.data === dataSource; })[0];
}
findDataSourceInfoByID(id) {
return this.usedDataSources().filter((info) => { return info.id === id; })[0];
}
findDataSourceInfoByRef(ref) {
return this.usedDataSources().filter((info) => { return info.ref === ref; })[0];
}
findDataSourceInfoByName(name) {
return this.usedDataSources().filter((item) => { return item.name === name; })[0];
}
}
DataSourceHelper.defaultReportExtensionKey = 'DataSerializationExtension';
DataSourceHelper._assignValueInTimeout = true;