mirror of
https://github.com/qlik-oss/nebula.js.git
synced 2025-12-19 17:58:43 -05:00
chore: add hypercube generic functions - part02 (#1715)
* chore: add hypercube generic functions - part02
This commit is contained in:
committed by
GitHub
parent
6b84132273
commit
445d6949e7
@@ -1,61 +0,0 @@
|
||||
import HyperCubeHandler from '../hypercube-handler';
|
||||
|
||||
describe('DataPropertyHandler - getDimensions and getMeasure', () => {
|
||||
let handler;
|
||||
let properties;
|
||||
|
||||
beforeEach(() => {
|
||||
properties = {
|
||||
qHyperCubeDef: {
|
||||
qDimensions: [{ qDef: { cId: 'dim1' } }],
|
||||
qLayoutExclude: {
|
||||
qHyperCubeDef: {
|
||||
qDimensions: [{ qDef: { cId: 'altDim1' } }],
|
||||
qMeasures: [{ qDef: { cId: 'altMeasure1' } }],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
handler = new HyperCubeHandler(properties);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('getDimensions()', () => {
|
||||
test('should return null when dimension is undefined', () => {
|
||||
jest.spyOn(handler, 'getDimensions').mockReturnValue([]);
|
||||
const dimension = handler.getDimension(undefined);
|
||||
expect(dimension).toBeFalsy();
|
||||
});
|
||||
|
||||
test('should return dimension when it exists in getDimensions()', () => {
|
||||
jest.spyOn(handler, 'getDimensions').mockReturnValue([{ qDef: { cId: 'dim1' } }]);
|
||||
jest.spyOn(handler, 'getAlternativeDimensions').mockReturnValue([{ qDef: { cId: 'altDim1' } }]);
|
||||
|
||||
const dimension = handler.getDimension('dim1');
|
||||
expect(dimension).toEqual({ qDef: { cId: 'dim1' } });
|
||||
const alternativeDimension = handler.getDimension('altDim1');
|
||||
expect(alternativeDimension).toEqual({ qDef: { cId: 'altDim1' } });
|
||||
});
|
||||
});
|
||||
|
||||
describe('getMeasure()', () => {
|
||||
test('should return null when both measures and alternative measures are empty', () => {
|
||||
jest.spyOn(handler, 'getMeasures').mockReturnValue([]);
|
||||
const measure = handler.getMeasure(undefined);
|
||||
expect(measure).toBeFalsy();
|
||||
});
|
||||
|
||||
test('should return measure when it exists in getMeasures()', () => {
|
||||
jest.spyOn(handler, 'getMeasures').mockReturnValue([{ qDef: { cId: 'measure1' } }]);
|
||||
jest.spyOn(handler, 'getAlternativeMeasures').mockReturnValue([{ qDef: { cId: 'altMeasure1' } }]);
|
||||
|
||||
const measure = handler.getMeasure('measure1');
|
||||
const alternativeMeasure = handler.getMeasure('altMeasure1');
|
||||
expect(measure).toEqual({ qDef: { cId: 'measure1' } });
|
||||
expect(alternativeMeasure).toEqual({ qDef: { cId: 'altMeasure1' } });
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,115 +0,0 @@
|
||||
import HyperCubeHandler from '../hypercube-handler';
|
||||
|
||||
describe('HyperCube Handlers', () => {
|
||||
let handler;
|
||||
let properties;
|
||||
|
||||
beforeEach(() => {
|
||||
properties = {
|
||||
qHyperCubeDef: {
|
||||
qDimensions: [{ qDef: { cId: 'dim1' } }],
|
||||
qInterColumnSortOrder: [0, 1],
|
||||
qLayoutExclude: {
|
||||
qHyperCubeDef: {
|
||||
qDimensions: [{ qDef: { cId: 'altDim1' } }],
|
||||
qMeasures: [{ qDef: { cId: 'altMeasure1' } }],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
handler = new HyperCubeHandler(properties);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('setProperties', () => {
|
||||
test('should return undefined when properties is null or undefined', () => {
|
||||
handler.setProperties(null);
|
||||
expect(handler.hcProperties).toBeUndefined();
|
||||
handler.setProperties({});
|
||||
expect(handler.hcProperties).toBeUndefined();
|
||||
handler.setProperties(undefined);
|
||||
expect(handler.hcProperties).toBeUndefined();
|
||||
});
|
||||
|
||||
test('should set properties when qHyperCubeDef provides defined/undefined values', () => {
|
||||
properties.qHyperCubeDef.qLayoutExclude.qHyperCubeDef.qDimensions = undefined;
|
||||
handler.setProperties(properties);
|
||||
|
||||
expect(handler.hcProperties.qDimensions[0]).toEqual({ qDef: { cId: 'dim1' } });
|
||||
expect(handler.hcProperties.qMeasures).toEqual([]);
|
||||
expect(handler.hcProperties.qInterColumnSortOrder).toEqual([0, 1]);
|
||||
expect(handler.hcProperties.qLayoutExclude).toEqual({
|
||||
qHyperCubeDef: {
|
||||
qDimensions: [],
|
||||
qMeasures: [{ qDef: { cId: 'altMeasure1' } }],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('should set properties when qLayoutExclude.qHyperCubeDef is undefined', () => {
|
||||
properties.qHyperCubeDef.qLayoutExclude.qHyperCubeDef = undefined;
|
||||
handler.setProperties(properties);
|
||||
|
||||
expect(handler.hcProperties.qLayoutExclude).toEqual({
|
||||
qHyperCubeDef: {
|
||||
qDimensions: [],
|
||||
qMeasures: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDimensions and getAlternativeDimensions', () => {
|
||||
test('should return empty arrays when hcProperties is null', () => {
|
||||
handler.hcProperties = null;
|
||||
|
||||
expect(handler.getDimensions()).toEqual([]);
|
||||
expect(handler.getAlternativeDimensions()).toEqual([]);
|
||||
});
|
||||
|
||||
test('should return empty arrays when qDimensions and qLayoutExclude.qHyperCubeDef.qDimensions are empty', () => {
|
||||
handler.hcProperties = {
|
||||
qDimensions: [],
|
||||
qLayoutExclude: {
|
||||
qHyperCubeDef: {
|
||||
qDimensions: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(handler.getDimensions()).toEqual([]);
|
||||
expect(handler.getAlternativeDimensions()).toEqual([]);
|
||||
});
|
||||
|
||||
test('should return qDimensions when qDimensions contains dimensions', () => {
|
||||
handler.hcProperties = {
|
||||
qDimensions: [{ qDef: { cId: 'dim1' } }, { qDef: { cId: 'dim2' } }],
|
||||
qLayoutExclude: {
|
||||
qHyperCubeDef: {
|
||||
qDimensions: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(handler.getDimensions()).toEqual([{ qDef: { cId: 'dim1' } }, { qDef: { cId: 'dim2' } }]);
|
||||
expect(handler.getAlternativeDimensions()).toEqual([]);
|
||||
});
|
||||
|
||||
test('should return qLayoutExclude.qHyperCubeDef.qDimensions when it contains alternative dimensions', () => {
|
||||
handler.hcProperties = {
|
||||
qDimensions: [],
|
||||
qLayoutExclude: {
|
||||
qHyperCubeDef: {
|
||||
qDimensions: [{ qDef: { cId: 'altDim1' } }, { qDef: { cId: 'altDim2' } }],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(handler.getDimensions()).toEqual([]);
|
||||
expect(handler.getAlternativeDimensions()).toEqual([{ qDef: { cId: 'altDim1' } }, { qDef: { cId: 'altDim2' } }]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,180 @@
|
||||
import DataPropertyHandler from '../data-property-handler';
|
||||
import HyperCubeHandler from '../hypercube-handler';
|
||||
|
||||
describe('DataPropertyHandler', () => {
|
||||
let handler;
|
||||
let properties;
|
||||
const sortingProperties = [
|
||||
{
|
||||
qSortByLoadOrder: 1,
|
||||
qSortByNumeric: 1,
|
||||
qSortByAscii: 1,
|
||||
},
|
||||
];
|
||||
|
||||
beforeEach(() => {
|
||||
properties = {
|
||||
qHyperCubeDef: {
|
||||
qDimensions: [{ qDef: { cId: 'dim1' } }],
|
||||
qLayoutExclude: {
|
||||
qHyperCubeDef: {
|
||||
qDimensions: [{ qDef: { cId: 'altDim1' } }],
|
||||
qMeasures: [{ qDef: { cId: 'altMeasure1' } }],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
handler = new HyperCubeHandler(properties);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('getDimensions()', () => {
|
||||
test('should return null when dimension is undefined', () => {
|
||||
jest.spyOn(handler, 'getDimensions').mockReturnValue([]);
|
||||
const dimension = handler.getDimension(undefined);
|
||||
expect(dimension).toBeFalsy();
|
||||
});
|
||||
|
||||
test('should return dimension when it exists in getDimensions()', () => {
|
||||
jest.spyOn(handler, 'getDimensions').mockReturnValue([{ qDef: { cId: 'dim1' } }]);
|
||||
jest.spyOn(handler, 'getAlternativeDimensions').mockReturnValue([{ qDef: { cId: 'altDim1' } }]);
|
||||
|
||||
const dimension = handler.getDimension('dim1');
|
||||
expect(dimension).toEqual({ qDef: { cId: 'dim1' } });
|
||||
const alternativeDimension = handler.getDimension('altDim1');
|
||||
expect(alternativeDimension).toEqual({ qDef: { cId: 'altDim1' } });
|
||||
});
|
||||
});
|
||||
|
||||
describe('getMeasure()', () => {
|
||||
test('should return null when both measures and alternative measures are empty', () => {
|
||||
jest.spyOn(handler, 'getMeasures').mockReturnValue([]);
|
||||
const measure = handler.getMeasure(undefined);
|
||||
expect(measure).toBeFalsy();
|
||||
});
|
||||
|
||||
test('should return measure when it exists in getMeasures()', () => {
|
||||
jest.spyOn(handler, 'getMeasures').mockReturnValue([{ qDef: { cId: 'measure1' } }]);
|
||||
jest.spyOn(handler, 'getAlternativeMeasures').mockReturnValue([{ qDef: { cId: 'altMeasure1' } }]);
|
||||
|
||||
const measure = handler.getMeasure('measure1');
|
||||
const alternativeMeasure = handler.getMeasure('altMeasure1');
|
||||
expect(measure).toEqual({ qDef: { cId: 'measure1' } });
|
||||
expect(alternativeMeasure).toEqual({ qDef: { cId: 'altMeasure1' } });
|
||||
});
|
||||
});
|
||||
|
||||
describe('createFieldDimension', () => {
|
||||
beforeEach(() => {
|
||||
handler = new DataPropertyHandler({
|
||||
dimensionProperties: { someProperty: 'defaultValue' },
|
||||
});
|
||||
});
|
||||
|
||||
test('should create a dimension with default properties when no field is provided', () => {
|
||||
const result = handler.createFieldDimension(null, null, { customDefault: 'value' });
|
||||
|
||||
expect(result.qDef.qFieldDefs).toEqual([null]);
|
||||
expect(result.qDef.qFieldLabels).toEqual(['']);
|
||||
expect(result.qDef.qSortCriterias).toEqual(sortingProperties);
|
||||
expect(result.qDef.autoSort).toBe(true);
|
||||
expect(result.someProperty).toBe('defaultValue');
|
||||
expect(result.customDefault).toBe('value');
|
||||
});
|
||||
|
||||
describe('createLibraryDimension', () => {
|
||||
test('should create a library dimension with default properties', () => {
|
||||
const result = handler.createLibraryDimension('libraryId', { customDefault: 'value' });
|
||||
|
||||
expect(result.qLibraryId).toBe('libraryId');
|
||||
expect(result.qDef.qSortCriterias).toEqual(sortingProperties);
|
||||
expect(result.qDef.autoSort).toBe(true);
|
||||
expect(result.someProperty).toBe('defaultValue');
|
||||
expect(result.customDefault).toBe('value');
|
||||
});
|
||||
|
||||
test('should delete qFieldDefs and qFieldLabels from the dimension', () => {
|
||||
const result = handler.createLibraryDimension('libraryId', {});
|
||||
|
||||
expect(result.qDef.qFieldDefs).toBeUndefined();
|
||||
expect(result.qDef.qFieldLabels).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
test('should create a dimension with provided field and label', () => {
|
||||
const result = handler.createFieldDimension('fieldName', 'fieldLabel', { customDefault: 'value' });
|
||||
|
||||
expect(result.qDef.qFieldDefs).toEqual(['fieldName']);
|
||||
expect(result.qDef.qFieldLabels).toEqual(['fieldLabel']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createExpressionMeasure', () => {
|
||||
beforeEach(() => {
|
||||
handler = new DataPropertyHandler({
|
||||
measureProperties: { someProperty: 'defaultValue' },
|
||||
});
|
||||
});
|
||||
|
||||
test('should create a measure with provided expression and label', () => {
|
||||
const result = handler.createExpressionMeasure('SUM(Sales)', 'Total Sales', { customDefault: 'value' });
|
||||
|
||||
expect(result.qDef.qDef).toBe('SUM(Sales)');
|
||||
expect(result.qDef.qLabel).toBe('Total Sales');
|
||||
expect(result.qDef.autoSort).toBe(true);
|
||||
expect(result.someProperty).toBe('defaultValue');
|
||||
expect(result.customDefault).toBe('value');
|
||||
});
|
||||
|
||||
test('should initialize qDef and qNumFormat if not provided', () => {
|
||||
const result = handler.createExpressionMeasure('SUM(Sales)', 'Total Sales', {});
|
||||
|
||||
expect(result.qDef).toBeDefined();
|
||||
expect(result.qDef.qNumFormat).toBeDefined();
|
||||
});
|
||||
|
||||
test('should handle empty defaults gracefully', () => {
|
||||
const result = handler.createExpressionMeasure('SUM(Sales)', 'Total Sales', null);
|
||||
|
||||
expect(result.qDef.qDef).toBe('SUM(Sales)');
|
||||
expect(result.qDef.qLabel).toBe('Total Sales');
|
||||
expect(result.qDef.autoSort).toBe(true);
|
||||
expect(result.someProperty).toBe('defaultValue');
|
||||
});
|
||||
});
|
||||
|
||||
describe('createLibraryMeasure', () => {
|
||||
beforeEach(() => {
|
||||
handler = new DataPropertyHandler({
|
||||
measureProperties: { someProperty: 'defaultValue' },
|
||||
});
|
||||
});
|
||||
|
||||
test('should create a library measure with provided id and defaults', () => {
|
||||
const result = handler.createLibraryMeasure('libraryId', { customDefault: 'value' });
|
||||
|
||||
expect(result.qLibraryId).toBe('libraryId');
|
||||
expect(result.qDef.qNumFormat).toBeDefined();
|
||||
expect(result.qDef.autoSort).toBe(true);
|
||||
expect(result.someProperty).toBe('defaultValue');
|
||||
expect(result.customDefault).toBe('value');
|
||||
});
|
||||
|
||||
test('should initialize qDef and qNumFormat if not provided', () => {
|
||||
const result = handler.createLibraryMeasure('libraryId', {});
|
||||
|
||||
expect(result.qDef).toBeDefined();
|
||||
expect(result.qDef.qNumFormat).toBeDefined();
|
||||
});
|
||||
|
||||
test('should delete qDef.qDef and qDef.qLabel from the measure', () => {
|
||||
const result = handler.createLibraryMeasure('libraryId', {});
|
||||
|
||||
expect(result.qDef.qDef).toBeUndefined();
|
||||
expect(result.qDef.qLabel).toBeUndefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
291
apis/supernova/src/handler/__tests__/hypercube-handler.test.js
Normal file
291
apis/supernova/src/handler/__tests__/hypercube-handler.test.js
Normal file
@@ -0,0 +1,291 @@
|
||||
import * as hcHelper from '../utils/hypercube-helper/hypercube-utils';
|
||||
import HyperCubeHandler from '../hypercube-handler';
|
||||
|
||||
describe('HyperCube Handlers', () => {
|
||||
let handler;
|
||||
let properties;
|
||||
|
||||
beforeEach(() => {
|
||||
properties = {
|
||||
qHyperCubeDef: {
|
||||
qDimensions: [{ qDef: { cId: 'dim1' } }],
|
||||
qMeasures: [],
|
||||
qInterColumnSortOrder: [0, 1],
|
||||
qLayoutExclude: {
|
||||
qHyperCubeDef: {
|
||||
qDimensions: [{ qDef: { cId: 'altDim1' } }],
|
||||
qMeasures: [{ qDef: { cId: 'altMeas1' } }],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
handler = new HyperCubeHandler(properties);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
handler.hcProperties = undefined;
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('setProperties', () => {
|
||||
test('should return undefined when properties is null or undefined', () => {
|
||||
handler.setProperties(null);
|
||||
expect(handler.hcProperties).toBeUndefined();
|
||||
handler.setProperties({});
|
||||
expect(handler.hcProperties).toBeUndefined();
|
||||
handler.setProperties(undefined);
|
||||
expect(handler.hcProperties).toBeUndefined();
|
||||
});
|
||||
|
||||
test('should set properties when qHyperCubeDef provides defined/undefined values', () => {
|
||||
properties.qHyperCubeDef.qLayoutExclude.qHyperCubeDef.qDimensions = undefined;
|
||||
handler.setProperties(properties);
|
||||
|
||||
expect(handler.hcProperties.qDimensions[0]).toEqual({ qDef: { cId: 'dim1' } });
|
||||
expect(handler.hcProperties.qMeasures).toEqual([]);
|
||||
expect(handler.hcProperties.qInterColumnSortOrder).toEqual([0, 1]);
|
||||
expect(handler.hcProperties.qLayoutExclude).toEqual({
|
||||
qHyperCubeDef: {
|
||||
qDimensions: [],
|
||||
qMeasures: [{ qDef: { cId: 'altMeas1' } }],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test('should set properties when qLayoutExclude.qHyperCubeDef is undefined', () => {
|
||||
properties.qHyperCubeDef.qLayoutExclude.qHyperCubeDef = undefined;
|
||||
handler.setProperties(properties);
|
||||
|
||||
expect(handler.hcProperties.qLayoutExclude).toEqual({
|
||||
qHyperCubeDef: {
|
||||
qDimensions: [],
|
||||
qMeasures: [],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDimensions and getAlternativeDimensions', () => {
|
||||
test('should return empty arrays when hcProperties is null', () => {
|
||||
handler.setProperties(properties);
|
||||
handler.hcProperties = null;
|
||||
|
||||
expect(handler.getDimensions()).toEqual([]);
|
||||
expect(handler.getAlternativeDimensions()).toEqual([]);
|
||||
});
|
||||
|
||||
test('should return empty arrays when qDimensions and alternative dimension are empty', () => {
|
||||
handler.setProperties(properties);
|
||||
handler.hcProperties = {
|
||||
qDimensions: [],
|
||||
qLayoutExclude: {
|
||||
qHyperCubeDef: {
|
||||
qDimensions: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(handler.getDimensions()).toEqual([]);
|
||||
expect(handler.getAlternativeDimensions()).toEqual([]);
|
||||
});
|
||||
|
||||
test('should return alternative dimensions when it has value', () => {
|
||||
handler.setProperties(properties);
|
||||
handler.hcProperties = {
|
||||
qDimensions: [{ qDef: { cId: 'dim1' } }, { qDef: { cId: 'dim2' } }],
|
||||
qLayoutExclude: {
|
||||
qHyperCubeDef: {
|
||||
qDimensions: [{ qDef: { cId: 'altDim1' } }, { qDef: { cId: 'altDim2' } }],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(handler.getDimensions()).toEqual([{ qDef: { cId: 'dim1' } }, { qDef: { cId: 'dim2' } }]);
|
||||
expect(handler.getAlternativeDimensions()).toEqual([{ qDef: { cId: 'altDim1' } }, { qDef: { cId: 'altDim2' } }]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addDimensions', () => {
|
||||
beforeEach(() => {
|
||||
handler.setProperties(properties);
|
||||
jest.spyOn(hcHelper, 'isTotalDimensionsExceeded').mockReturnValue(false);
|
||||
});
|
||||
|
||||
test('should return an empty array when newDimensions is empty', async () => {
|
||||
const result = await handler.addDimensions([]);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
test('should add dimensions to alternative dimensions when alternative is true', async () => {
|
||||
const newDimensions = [{ qDef: { cId: 'altDim2' } }, { qDef: { cId: 'altDim3' } }];
|
||||
const dimensions = await handler.addDimensions(newDimensions, true);
|
||||
|
||||
expect(dimensions).toEqual([
|
||||
{ qDef: { cId: 'altDim2' }, qOtherTotalSpec: {} },
|
||||
{ qDef: { cId: 'altDim3' }, qOtherTotalSpec: {} },
|
||||
]);
|
||||
expect(handler.hcProperties.qLayoutExclude.qHyperCubeDef.qDimensions).toEqual([
|
||||
{ qDef: { cId: 'altDim1' } },
|
||||
{ qDef: { cId: 'altDim2' }, qOtherTotalSpec: {} },
|
||||
{ qDef: { cId: 'altDim3' }, qOtherTotalSpec: {} },
|
||||
]);
|
||||
});
|
||||
|
||||
test('should add dimensions to main dimensions when alternative is false', async () => {
|
||||
const newDimensions = [{ qDef: { cId: 'dim2' } }];
|
||||
handler.maxDimensions = jest.fn().mockReturnValue(2);
|
||||
handler.autoSortDimension = jest.fn();
|
||||
|
||||
const dimensions = await handler.addDimensions(newDimensions, false);
|
||||
expect(handler.autoSortDimension).toHaveBeenCalledTimes(1);
|
||||
expect(handler.autoSortDimension).toHaveBeenCalledWith({ qDef: { cId: 'dim2' }, qOtherTotalSpec: {} });
|
||||
|
||||
expect(dimensions).toEqual([{ qDef: { cId: 'dim2' }, qOtherTotalSpec: {} }]);
|
||||
expect(handler.hcProperties.qDimensions).toEqual([
|
||||
{ qDef: { cId: 'dim1' } },
|
||||
{ qDef: { cId: 'dim2' }, qOtherTotalSpec: {} },
|
||||
]);
|
||||
});
|
||||
|
||||
test('should not add dimensions when isTotalDimensionsExceeded returns true', async () => {
|
||||
jest.spyOn(hcHelper, 'isTotalDimensionsExceeded').mockReturnValue(true);
|
||||
const newDimensions = [{ qDef: { cId: 'dim2' } }];
|
||||
|
||||
const dimensions = await handler.addDimensions(newDimensions);
|
||||
|
||||
expect(dimensions).toEqual([]);
|
||||
});
|
||||
|
||||
test('should add dimensions to alternative dimensions when maxDim is exceeded but less than total maxDim', async () => {
|
||||
handler.maxDimensions = jest.fn().mockReturnValue(1);
|
||||
const newDimensions = [{ qDef: { cId: 'dim2' } }];
|
||||
|
||||
const dimension = await handler.addDimensions(newDimensions, false);
|
||||
|
||||
expect(dimension).toEqual([{ qDef: { cId: 'dim2' }, qOtherTotalSpec: {} }]);
|
||||
expect(handler.hcProperties.qDimensions).toEqual([{ qDef: { cId: 'dim1' } }]);
|
||||
expect(handler.hcProperties.qLayoutExclude.qHyperCubeDef.qDimensions).toEqual([
|
||||
{ qDef: { cId: 'altDim1' } },
|
||||
{ qDef: { cId: 'dim2' }, qOtherTotalSpec: {} },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getMeasures and getAlternativeMeasures', () => {
|
||||
test('should return empty arrays when hcProperties is null', () => {
|
||||
handler.setProperties(properties);
|
||||
handler.hcProperties = null;
|
||||
|
||||
expect(handler.getMeasures()).toEqual([]);
|
||||
expect(handler.getAlternativeMeasures()).toEqual([]);
|
||||
});
|
||||
|
||||
test('should return empty arrays when qMeasures and alternative measures are empty', () => {
|
||||
handler.setProperties(properties);
|
||||
handler.hcProperties = {
|
||||
qMeasures: [],
|
||||
qLayoutExclude: {
|
||||
qHyperCubeDef: {
|
||||
qDimensions: [],
|
||||
qMeasures: [],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(handler.getMeasures()).toEqual([]);
|
||||
expect(handler.getAlternativeMeasures()).toEqual([]);
|
||||
});
|
||||
|
||||
test('should return qMeasures when qMeasures has value', () => {
|
||||
handler.setProperties(properties);
|
||||
handler.hcProperties = {
|
||||
qMeasures: [{ qDef: { cId: 'meas1' } }, { qDef: { cId: 'meas2' } }],
|
||||
qLayoutExclude: {
|
||||
qHyperCubeDef: {
|
||||
qMeasures: [{ qDef: { cId: 'altMeas1' } }, { qDef: { cId: 'altMeas2' } }],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
expect(handler.getMeasures()).toEqual([{ qDef: { cId: 'meas1' } }, { qDef: { cId: 'meas2' } }]);
|
||||
expect(handler.getAlternativeMeasures()).toEqual([{ qDef: { cId: 'altMeas1' } }, { qDef: { cId: 'altMeas2' } }]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addMeasures', () => {
|
||||
beforeEach(() => {
|
||||
properties.qHyperCubeDef.qMeasures = [{ qDef: { cId: 'meas1' } }];
|
||||
handler.setProperties(properties);
|
||||
jest.spyOn(hcHelper, 'isTotalMeasureExceeded').mockReturnValue(false);
|
||||
});
|
||||
|
||||
test('should return an empty array when new measure is empty', () => {
|
||||
const result = handler.addMeasures([]);
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
test('should add measures to alternative measures when alternative is true', () => {
|
||||
const newMeasures = [{ qDef: { cId: 'altMeas2' } }, { qDef: { cId: 'altMeas3' } }];
|
||||
const measures = handler.addMeasures(newMeasures, true);
|
||||
|
||||
expect(measures).toEqual([{ qDef: { cId: 'altMeas2' } }, { qDef: { cId: 'altMeas3' } }]);
|
||||
expect(handler.hcProperties.qMeasures).toEqual([{ qDef: { cId: 'meas1' } }]);
|
||||
expect(handler.hcProperties.qLayoutExclude.qHyperCubeDef.qMeasures).toEqual([
|
||||
{ qDef: { cId: 'altMeas1' } },
|
||||
{ qDef: { cId: 'altMeas2' } },
|
||||
{ qDef: { cId: 'altMeas3' } },
|
||||
]);
|
||||
});
|
||||
|
||||
test('should add measures to main measures when alternative is false', () => {
|
||||
const newMeasures = [{ qDef: { cId: 'meas2' } }];
|
||||
handler.maxMeasures = jest.fn().mockReturnValue(2);
|
||||
handler.autoSortDimension = jest.fn();
|
||||
|
||||
const measures = handler.addMeasures(newMeasures, false);
|
||||
expect(measures).toEqual([
|
||||
{
|
||||
qDef: { cId: 'meas2' },
|
||||
qSortBy: {
|
||||
qSortByLoadOrder: 1,
|
||||
qSortByNumeric: -1,
|
||||
},
|
||||
},
|
||||
]);
|
||||
expect(handler.hcProperties.qMeasures).toEqual([
|
||||
{ qDef: { cId: 'meas1' } },
|
||||
{
|
||||
qDef: { cId: 'meas2' },
|
||||
qSortBy: {
|
||||
qSortByLoadOrder: 1,
|
||||
qSortByNumeric: -1,
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('should not add measures when isTotalMeasureExceeded returns true', () => {
|
||||
jest.spyOn(hcHelper, 'isTotalMeasureExceeded').mockReturnValue(true);
|
||||
const newMeasure = [{ qDef: { cId: 'meas2' } }];
|
||||
|
||||
const measure = handler.addMeasures(newMeasure);
|
||||
expect(measure).toEqual([]);
|
||||
});
|
||||
|
||||
test('should add measure to alternative measures when maxMeasure is exceeded but less than total', () => {
|
||||
handler.maxMeasures = jest.fn().mockReturnValue(1);
|
||||
const newMeasure = [{ qDef: { cId: 'meas2' } }];
|
||||
|
||||
const measure = handler.addMeasures(newMeasure, false);
|
||||
|
||||
expect(measure).toEqual([{ qDef: { cId: 'meas2' } }]);
|
||||
expect(handler.hcProperties.qMeasures).toEqual([{ qDef: { cId: 'meas1' } }]);
|
||||
expect(handler.hcProperties.qLayoutExclude.qHyperCubeDef.qMeasures).toEqual([
|
||||
{ qDef: { cId: 'altMeas1' } },
|
||||
{
|
||||
qDef: { cId: 'meas2' },
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,10 @@
|
||||
import { getFieldById } from './utils/handler-helper';
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import { merge } from 'lodash';
|
||||
// eslint-disable-next-line import/no-relative-packages
|
||||
import isEnabled from '../../../nucleus/src/flags/flags';
|
||||
import { findFieldById, initializeField, useMasterNumberFormat } from './utils/field-helper/field-utils';
|
||||
import { INITIAL_SORT_CRITERIAS } from './utils/constants';
|
||||
import { notSupportedError } from './utils/hypercube-helper/hypercube-utils';
|
||||
|
||||
class DataPropertyHandler {
|
||||
constructor(opts) {
|
||||
@@ -30,7 +36,9 @@ class DataPropertyHandler {
|
||||
throw new Error('Must override this method');
|
||||
}
|
||||
|
||||
// ----------------DIMENSION----------------
|
||||
// ---------------------------------------
|
||||
// ---------------DIMENSION---------------
|
||||
// ---------------------------------------
|
||||
|
||||
static getDimensions() {
|
||||
return [];
|
||||
@@ -40,26 +48,121 @@ class DataPropertyHandler {
|
||||
const dimensions = this.getDimensions();
|
||||
const alternativeDimensions = this.getAlternativeDimensions();
|
||||
|
||||
const dim = getFieldById(dimensions, id);
|
||||
const altDim = getFieldById(alternativeDimensions, id);
|
||||
|
||||
return dim ?? altDim;
|
||||
return findFieldById(dimensions, id) ?? findFieldById(alternativeDimensions, id);
|
||||
}
|
||||
|
||||
static getAlternativeDimensions() {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
static addDimension() {
|
||||
throw notSupportedError;
|
||||
}
|
||||
|
||||
static addDimensions() {
|
||||
throw notSupportedError;
|
||||
}
|
||||
|
||||
static autoSortDimension() {
|
||||
throw notSupportedError;
|
||||
}
|
||||
|
||||
createLibraryDimension(id, defaults) {
|
||||
let dimension = merge({}, this.dimensionProperties || {}, defaults || {});
|
||||
|
||||
dimension = initializeField(dimension);
|
||||
|
||||
dimension.qLibraryId = id;
|
||||
dimension.qDef.autoSort = true;
|
||||
dimension.qDef.qSortCriterias = INITIAL_SORT_CRITERIAS;
|
||||
|
||||
delete dimension.qDef.qFieldDefs;
|
||||
delete dimension.qDef.qFieldLabels;
|
||||
|
||||
return dimension;
|
||||
}
|
||||
|
||||
createFieldDimension(field, label, defaults) {
|
||||
let dimension = merge({}, this.dimensionProperties || {}, defaults || {});
|
||||
|
||||
dimension = initializeField(dimension);
|
||||
|
||||
if (!field) {
|
||||
dimension.qDef.qFieldDefs = [];
|
||||
dimension.qDef.qFieldLabels = [];
|
||||
dimension.qDef.qSortCriterias = [];
|
||||
}
|
||||
|
||||
dimension.qDef.qFieldDefs = [field];
|
||||
dimension.qDef.qFieldLabels = label ? [label] : [''];
|
||||
dimension.qDef.qSortCriterias = INITIAL_SORT_CRITERIAS;
|
||||
|
||||
dimension.qDef.autoSort = true;
|
||||
|
||||
return dimension;
|
||||
}
|
||||
|
||||
addFieldDimension(field, label, defaults) {
|
||||
const dimension = this.createFieldDimension(field, label, defaults);
|
||||
return this.addDimension(dimension);
|
||||
}
|
||||
|
||||
addFieldDimensions(args) {
|
||||
const dimensions = args.map(({ field, label, defaults }) => this.createFieldDimension(field, label, defaults));
|
||||
return this.addDimensions(dimensions);
|
||||
}
|
||||
|
||||
addLibraryDimension(id, defaults) {
|
||||
const dimension = this.createLibraryDimension(id, defaults);
|
||||
return this.addDimension(dimension);
|
||||
}
|
||||
|
||||
addLibraryDimensions(args) {
|
||||
const dimensions = args.map(({ id, defaults }) => this.createLibraryDimension(id, defaults));
|
||||
const result = this.addDimensions(dimensions);
|
||||
return result;
|
||||
}
|
||||
|
||||
async addAltLibraryDimensions(args) {
|
||||
const dimensions = args.map(({ id }) => this.createLibraryDimension(id));
|
||||
return this.addDimensions(dimensions, true);
|
||||
}
|
||||
|
||||
async addAltFieldDimensions(args) {
|
||||
const dimensions = args.map(({ field }) => this.createFieldDimension(field));
|
||||
return this.addDimensions(dimensions, true);
|
||||
}
|
||||
|
||||
addAlternativeFieldDimension(field, label, defaults) {
|
||||
const dimension = this.createFieldDimension(field, label, defaults);
|
||||
return this.addDimension(dimension, true);
|
||||
}
|
||||
|
||||
addAlternativeLibraryDimension(id, defaults) {
|
||||
const dimension = this.createLibraryDimension(id, defaults);
|
||||
return this.addDimension(dimension, true);
|
||||
}
|
||||
|
||||
maxDimensions(decrement = 0) {
|
||||
const measureLength = this.getMeasures().length - decrement;
|
||||
|
||||
if (typeof this.dimensionDefinition.max === 'function') {
|
||||
const dimParams = isEnabled('PS_21371_ANALYSIS_TYPES') ? [measureLength, this.properties] : [measureLength];
|
||||
return this.dimensionDefinition.max?.apply(null, dimParams);
|
||||
}
|
||||
|
||||
return Number.isNaN(+this.dimensionDefinition.max) ? 10000 : this.dimensionDefinition.max;
|
||||
}
|
||||
|
||||
// ---------------------------------------
|
||||
// ----------------MEASURE----------------
|
||||
// ---------------------------------------
|
||||
|
||||
getMeasure(id) {
|
||||
const measures = this.getMeasures();
|
||||
const alternativeMeasures = this.getAlternativeMeasures();
|
||||
|
||||
const meas = getFieldById(measures, id);
|
||||
const altMeas = getFieldById(alternativeMeasures, id);
|
||||
|
||||
return meas ?? altMeas;
|
||||
return findFieldById(measures, id) ?? findFieldById(alternativeMeasures, id);
|
||||
}
|
||||
|
||||
static getMeasures() {
|
||||
@@ -69,6 +172,101 @@ class DataPropertyHandler {
|
||||
static getAlternativeMeasures() {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
static addMeasure() {
|
||||
throw notSupportedError;
|
||||
}
|
||||
|
||||
static addMeasures() {
|
||||
throw notSupportedError;
|
||||
}
|
||||
|
||||
static autoSortMeasure() {
|
||||
throw notSupportedError;
|
||||
}
|
||||
|
||||
createExpressionMeasure(expression, label, defaults) {
|
||||
const measure = merge({}, this.measureProperties || {}, defaults || {});
|
||||
|
||||
measure.qDef = measure.qDef ?? {};
|
||||
measure.qDef.qNumFormat = measure.qDef.qNumFormat ?? {};
|
||||
|
||||
measure.qDef.qDef = expression;
|
||||
measure.qDef.qLabel = label;
|
||||
measure.qDef.autoSort = true;
|
||||
|
||||
return measure;
|
||||
}
|
||||
|
||||
addExpressionMeasure(expression, label, defaults) {
|
||||
const measure = this.createExpressionMeasure(expression, label, defaults);
|
||||
return this.addMeasure(measure);
|
||||
}
|
||||
|
||||
addExpressionMeasures(args) {
|
||||
const measures = args.map(({ expression, label, defaults }) =>
|
||||
this.createExpressionMeasure(expression, label, defaults)
|
||||
);
|
||||
return this.addMeasures(measures);
|
||||
}
|
||||
|
||||
createLibraryMeasure(id, defaults) {
|
||||
const measure = merge({}, this.measureProperties || {}, defaults || {});
|
||||
measure.qDef = measure.qDef ?? {};
|
||||
measure.qDef.qNumFormat = measure.qDef.qNumFormat ?? {};
|
||||
|
||||
if (isEnabled('MASTER_MEASURE_FORMAT')) {
|
||||
useMasterNumberFormat(measure.qDef);
|
||||
}
|
||||
|
||||
measure.qLibraryId = id;
|
||||
measure.qDef.autoSort = true;
|
||||
|
||||
delete measure.qDef.qDef;
|
||||
delete measure.qDef.qLabel;
|
||||
|
||||
return measure;
|
||||
}
|
||||
|
||||
addLibraryMeasure(id, defaults) {
|
||||
const measure = this.createLibraryMeasure(id, defaults);
|
||||
return this.addMeasure(measure);
|
||||
}
|
||||
|
||||
addLibraryMeasures(args) {
|
||||
const measures = args.map(({ id, defaults }) => this.createLibraryMeasure(id, defaults));
|
||||
return this.addMeasures(measures);
|
||||
}
|
||||
|
||||
addAltLibraryMeasures(args) {
|
||||
const measures = args.map(({ id }) => this.createLibraryMeasure(id));
|
||||
return this.addMeasures(measures, true);
|
||||
}
|
||||
|
||||
addAltExpressionMeasures(args) {
|
||||
const measures = args.map(({ expression }) => this.createExpressionMeasure(expression));
|
||||
return this.addMeasures(measures, true);
|
||||
}
|
||||
|
||||
addAlternativeExpressionMeasure(expression, label, defaults) {
|
||||
const measure = this.createExpressionMeasure(expression, label, defaults);
|
||||
return this.addMeasure(measure, true);
|
||||
}
|
||||
|
||||
addAlternativeLibraryMeasure(id, defaults) {
|
||||
const measure = this.createLibraryMeasure(id, defaults);
|
||||
return this.addMeasure(measure, true);
|
||||
}
|
||||
|
||||
maxMeasures(decrement) {
|
||||
const decr = decrement || 0;
|
||||
if (typeof this.measureDefinition.max === 'function') {
|
||||
const dimLength = this.getDimensions().length - decr;
|
||||
const measureParams = isEnabled('PS_21371_ANALYSIS_TYPES') ? [dimLength, this.properties] : [dimLength];
|
||||
return this.measureDefinition.max.apply(null, measureParams);
|
||||
}
|
||||
return Number.isNaN(+this.measureDefinition.max) ? 10000 : this.measureDefinition.max;
|
||||
}
|
||||
}
|
||||
|
||||
export default DataPropertyHandler;
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
// eslint-disable-next-line import/no-relative-packages
|
||||
import utils from '../../../conversion/src/utils';
|
||||
import DataPropertyHandler from './data-property-handler';
|
||||
import { getHyperCube, setFieldProperties } from './utils/handler-helper';
|
||||
import * as hcHelper from './utils/hypercube-helper/hypercube-utils';
|
||||
import getAutoSortLibraryDimension from './utils/field-helper/get-sorted-library-field';
|
||||
import getAutoSortFieldDimension from './utils/field-helper/get-sorted-field';
|
||||
import { initializeField, initializeId } from './utils/field-helper/field-utils';
|
||||
import addMainDimension from './utils/hypercube-helper/add-main-dimension';
|
||||
import addMainMeasure from './utils/hypercube-helper/add-main-measure';
|
||||
|
||||
class HyperCubeHandler extends DataPropertyHandler {
|
||||
constructor(opts) {
|
||||
@@ -11,7 +16,7 @@ class HyperCubeHandler extends DataPropertyHandler {
|
||||
|
||||
setProperties(properties) {
|
||||
if (!properties) {
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
|
||||
super.setProperties(properties);
|
||||
@@ -19,38 +24,17 @@ class HyperCubeHandler extends DataPropertyHandler {
|
||||
this.hcProperties = this.path ? utils.getValue(properties, `${this.path}.qHyperCubeDef`) : properties.qHyperCubeDef;
|
||||
|
||||
if (!this.hcProperties) {
|
||||
return;
|
||||
return {};
|
||||
}
|
||||
|
||||
// Set defaults
|
||||
this.hcProperties.qDimensions = this.hcProperties.qDimensions ?? [];
|
||||
this.hcProperties.qMeasures = this.hcProperties.qMeasures ?? [];
|
||||
this.hcProperties.qInterColumnSortOrder = this.hcProperties.qInterColumnSortOrder ?? [];
|
||||
this.hcProperties.qLayoutExclude = this.hcProperties.qLayoutExclude ?? {
|
||||
qHyperCubeDef: { qDimensions: [], qMeasures: [] },
|
||||
};
|
||||
this.hcProperties.qLayoutExclude.qHyperCubeDef = this.hcProperties.qLayoutExclude.qHyperCubeDef ?? {
|
||||
qDimensions: [],
|
||||
qMeasures: [],
|
||||
};
|
||||
this.hcProperties.qLayoutExclude.qHyperCubeDef.qDimensions =
|
||||
this.hcProperties.qLayoutExclude.qHyperCubeDef.qDimensions ?? [];
|
||||
this.hcProperties.qLayoutExclude.qHyperCubeDef.qMeasures =
|
||||
this.hcProperties.qLayoutExclude.qHyperCubeDef.qMeasures ?? [];
|
||||
|
||||
if (
|
||||
this.hcProperties.isHCEnabled &&
|
||||
this.hcProperties.qDynamicScript.length === 0 &&
|
||||
this.hcProperties.qMode === 'S'
|
||||
) {
|
||||
// this is only for line chart with forecast
|
||||
this.hcProperties.qDynamicScript = [];
|
||||
}
|
||||
hcHelper.setDefaultProperties(this);
|
||||
hcHelper.setPropForLineChartWithForecast(this);
|
||||
|
||||
// Set auto-sort property (compatibility 0.85 -> 0.9),
|
||||
// can probably be removed in 1.0
|
||||
this.hcProperties.qDimensions = setFieldProperties(this.hcProperties.qDimensions);
|
||||
this.hcProperties.qMeasures = setFieldProperties(this.hcProperties.qMeasures);
|
||||
this.hcProperties.qDimensions = hcHelper.setFieldProperties(this.hcProperties.qDimensions);
|
||||
this.hcProperties.qMeasures = hcHelper.setFieldProperties(this.hcProperties.qMeasures);
|
||||
return {};
|
||||
}
|
||||
|
||||
// ----------------------------------
|
||||
@@ -70,10 +54,52 @@ class HyperCubeHandler extends DataPropertyHandler {
|
||||
}
|
||||
|
||||
getDimensionLayouts() {
|
||||
const hc = getHyperCube(this.layout, this.path);
|
||||
const hc = hcHelper.getHyperCube(this.layout, this.path);
|
||||
return hc ? hc.qDimensionInfo : [];
|
||||
}
|
||||
|
||||
addDimension(dimension, alternative, idx) {
|
||||
const dim = initializeField(dimension);
|
||||
|
||||
if (hcHelper.isDimensionAlternative(this, dim, alternative)) {
|
||||
return hcHelper.addAlternativeDimension(this, dim, idx);
|
||||
}
|
||||
|
||||
return addMainDimension(this, dim, idx);
|
||||
}
|
||||
|
||||
async addDimensions(dimensions, alternative = false) {
|
||||
const existingDimensions = this.getDimensions();
|
||||
const addedDimensions = [];
|
||||
let addedActive = 0;
|
||||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for await (const dimension of dimensions) {
|
||||
if (hcHelper.isTotalDimensionsExceeded(this, existingDimensions)) {
|
||||
return addedDimensions;
|
||||
}
|
||||
|
||||
const dim = initializeField(dimension);
|
||||
|
||||
if (hcHelper.isDimensionAlternative(this, alternative)) {
|
||||
const altDim = await hcHelper.addAlternativeDimension(this, dim);
|
||||
addedDimensions.push(altDim);
|
||||
} else if (existingDimensions.length < this.maxDimensions()) {
|
||||
await hcHelper.addActiveDimension(this, dim, existingDimensions, addedDimensions, addedActive);
|
||||
addedActive++;
|
||||
}
|
||||
}
|
||||
|
||||
return addedDimensions;
|
||||
}
|
||||
|
||||
autoSortDimension(dimension) {
|
||||
if (dimension.qLibraryId) {
|
||||
return getAutoSortLibraryDimension(this, dimension);
|
||||
}
|
||||
return getAutoSortFieldDimension(this, dimension);
|
||||
}
|
||||
|
||||
// ----------------------------------
|
||||
// ------------ MEASURES ------------
|
||||
// ----------------------------------
|
||||
@@ -87,13 +113,57 @@ class HyperCubeHandler extends DataPropertyHandler {
|
||||
}
|
||||
|
||||
getMeasureLayouts() {
|
||||
const hc = getHyperCube(this.layout, this.path);
|
||||
const hc = hcHelper.getHyperCube(this.layout, this.path);
|
||||
return hc ? hc.qMeasureInfo : [];
|
||||
}
|
||||
|
||||
getMeasureLayout(cId) {
|
||||
return this.getMeasureLayouts().filter((item) => cId === item.cId)[0];
|
||||
}
|
||||
|
||||
addMeasure(measure, alternative, idx) {
|
||||
const meas = initializeField(measure);
|
||||
|
||||
if (hcHelper.isMeasureAlternative(this, meas, alternative)) {
|
||||
const hcMeasures = this.hcProperties.qLayoutExclude.qHyperCubeDef.qMeasures;
|
||||
return hcHelper.addAlternativeMeasure(meas, hcMeasures, idx);
|
||||
}
|
||||
|
||||
return addMainMeasure(this, meas, idx);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line class-methods-use-this
|
||||
autoSortMeasure(measure) {
|
||||
const meas = { ...measure };
|
||||
meas.qSortBy = {
|
||||
qSortByLoadOrder: 1,
|
||||
qSortByNumeric: -1,
|
||||
};
|
||||
return Promise.resolve(meas);
|
||||
}
|
||||
|
||||
addMeasures(measures, alternative = false) {
|
||||
const existingMeasures = this.getMeasures();
|
||||
const addedMeasures = [];
|
||||
let addedActive = 0;
|
||||
measures.forEach(async (measure) => {
|
||||
if (hcHelper.isTotalMeasureExceeded(this, existingMeasures)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const meas = initializeId(measure);
|
||||
|
||||
if (hcHelper.isMeasureAlternative(this, existingMeasures, alternative)) {
|
||||
hcHelper.addAlternativeMeasure(this, meas);
|
||||
addedMeasures.push(meas);
|
||||
} else if (existingMeasures.length < this.maxMeasures()) {
|
||||
await hcHelper.addActiveMeasure(this, meas, existingMeasures, addedMeasures, addedActive);
|
||||
addedActive++;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
return addedMeasures;
|
||||
}
|
||||
}
|
||||
|
||||
export default HyperCubeHandler;
|
||||
|
||||
14
apis/supernova/src/handler/utils/constants.js
Normal file
14
apis/supernova/src/handler/utils/constants.js
Normal file
@@ -0,0 +1,14 @@
|
||||
export const TOTAL_MAX = {
|
||||
DIMENSIONS: 1000, // Maximum number of active dimensions + disabled dimensions
|
||||
MEASURES: 1000, // Maximum number of active measures + disabled measures
|
||||
};
|
||||
|
||||
export const AUTOCALENDAR_NAME = '.autoCalendar';
|
||||
|
||||
export const INITIAL_SORT_CRITERIAS = [
|
||||
{
|
||||
qSortByLoadOrder: 1,
|
||||
qSortByNumeric: 1,
|
||||
qSortByAscii: 1,
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,70 @@
|
||||
import expandFieldsWithDerivedData from '../expand-field-derived-data';
|
||||
import * as getDataGeoField from '../get-data-geo-field';
|
||||
import * as getDerivedFields from '../get-derived-fields';
|
||||
|
||||
jest.mock('../get-data-geo-field', () => jest.fn());
|
||||
jest.mock('../get-derived-fields', () => jest.fn());
|
||||
|
||||
describe('expandFieldsWithDerivedData', () => {
|
||||
let inputList;
|
||||
let geoField;
|
||||
let derivedFields;
|
||||
|
||||
beforeEach(() => {
|
||||
inputList = [{ name: 'field1' }];
|
||||
geoField = { name: 'geoField' };
|
||||
derivedFields = [{ name: 'derivedFields' }];
|
||||
});
|
||||
|
||||
test('should expand fields with geo and derived fields', () => {
|
||||
getDataGeoField.mockReturnValue(geoField);
|
||||
getDerivedFields.mockReturnValue(derivedFields);
|
||||
|
||||
const result = expandFieldsWithDerivedData(inputList);
|
||||
|
||||
expect(result).toEqual([geoField, { name: 'derivedFields' }]);
|
||||
});
|
||||
|
||||
test('should handle an empty input list', () => {
|
||||
inputList = [];
|
||||
getDataGeoField.mockReturnValue(geoField);
|
||||
getDerivedFields.mockReturnValue(derivedFields);
|
||||
|
||||
const result = expandFieldsWithDerivedData(inputList);
|
||||
|
||||
expect(getDataGeoField.default).not.toHaveBeenCalled();
|
||||
expect(getDerivedFields.default).not.toHaveBeenCalled();
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
test('should handle fields with no derived fields', () => {
|
||||
derivedFields = [];
|
||||
getDataGeoField.mockReturnValue(geoField);
|
||||
getDerivedFields.mockReturnValue(derivedFields);
|
||||
|
||||
const result = expandFieldsWithDerivedData(inputList);
|
||||
|
||||
expect(getDataGeoField).toHaveBeenCalledTimes(1);
|
||||
expect(getDataGeoField).toHaveBeenCalledWith({ name: 'field1' });
|
||||
|
||||
expect(getDerivedFields).toHaveBeenCalledTimes(1);
|
||||
expect(getDerivedFields).toHaveBeenCalledWith({ name: 'field1' });
|
||||
|
||||
expect(result).toEqual([geoField]);
|
||||
});
|
||||
|
||||
test('should handle fields with no geo field', () => {
|
||||
geoField = [];
|
||||
getDataGeoField.mockReturnValue([]);
|
||||
getDerivedFields.mockReturnValue(derivedFields);
|
||||
|
||||
const result = expandFieldsWithDerivedData(inputList);
|
||||
|
||||
expect(getDataGeoField).toHaveBeenCalledTimes(1);
|
||||
expect(getDataGeoField).toHaveBeenCalledWith({ name: 'field1' });
|
||||
|
||||
expect(getDerivedFields).toHaveBeenCalledTimes(1);
|
||||
expect(getDerivedFields).toHaveBeenCalledWith({ name: 'field1' });
|
||||
expect(result).toEqual([[], ...derivedFields]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,39 @@
|
||||
import findFieldInExpandedList from '../find-field-in-expandedList';
|
||||
import * as expandFieldsWithDerivedData from '../expand-field-derived-data';
|
||||
|
||||
describe('findFieldInExpandedList', () => {
|
||||
let fieldList;
|
||||
let expandedList;
|
||||
|
||||
beforeEach(() => {
|
||||
fieldList = [{ qName: 'field1' }, { qName: 'field2' }];
|
||||
expandedList = [{ qName: 'field1' }, { qName: 'field2' }, { qName: 'derivedField' }];
|
||||
});
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
test('should return the field if it exists in the expanded list', () => {
|
||||
jest.spyOn(expandFieldsWithDerivedData, 'default').mockImplementation(() => expandedList);
|
||||
|
||||
const result = findFieldInExpandedList('field1', fieldList);
|
||||
expect(result).toEqual({ qName: 'field1' });
|
||||
});
|
||||
|
||||
test('should return null if the field does not exist in the expanded list', () => {
|
||||
jest.spyOn(expandFieldsWithDerivedData, 'default').mockImplementation(() => expandedList);
|
||||
|
||||
const result = findFieldInExpandedList('nonExistentField', fieldList);
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
test('should return null if the expanded list is empty', () => {
|
||||
fieldList = [];
|
||||
expandedList = null;
|
||||
jest.spyOn(expandFieldsWithDerivedData, 'default').mockImplementation(() => expandedList);
|
||||
|
||||
const result = findFieldInExpandedList('field1', fieldList);
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,24 @@
|
||||
import getDataGeoField from '../get-data-geo-field';
|
||||
import { isDateField, isGeoField } from '../field-utils';
|
||||
|
||||
jest.mock('../field-utils', () => ({
|
||||
isDateField: jest.fn(),
|
||||
isGeoField: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('getDataGeoField', () => {
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
test('should return field with the correct property values', () => {
|
||||
const field = { name: 'dateField' };
|
||||
isDateField.mockReturnValue(true);
|
||||
isGeoField.mockReturnValue(false);
|
||||
|
||||
const result = getDataGeoField(field);
|
||||
|
||||
expect(result.isDateField).toBe(true);
|
||||
expect(result.isGeoField).toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,131 @@
|
||||
import getDerivedFields from '../get-derived-fields';
|
||||
import { trimAutoCalendarName } from '../field-utils';
|
||||
|
||||
jest.mock('../field-utils', () => ({
|
||||
trimAutoCalendarName: jest.fn((name) => `trimmed_${name}`),
|
||||
}));
|
||||
|
||||
describe('getDerivedFields', () => {
|
||||
let field;
|
||||
|
||||
beforeEach(() => {
|
||||
field = {
|
||||
qName: 'field1',
|
||||
qSrcTables: ['table1'],
|
||||
isDateField: true,
|
||||
qDerivedFieldData: {
|
||||
qDerivedFieldLists: [
|
||||
{
|
||||
qDerivedDefinitionName: 'DerivedDef1',
|
||||
qFieldDefs: [
|
||||
{
|
||||
qName: 'derivedField1',
|
||||
qTags: ['tag1'],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
});
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
test('should return an empty array if derived field data is undefined', () => {
|
||||
field = { qName: 'field1' };
|
||||
|
||||
const result = getDerivedFields(field);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
test('should return an empty array if derived field data list is empty', () => {
|
||||
field = {
|
||||
qName: 'field1',
|
||||
qDerivedFieldData: {
|
||||
qDerivedFieldLists: [],
|
||||
},
|
||||
};
|
||||
|
||||
const result = getDerivedFields(field);
|
||||
|
||||
expect(result).toEqual([]);
|
||||
});
|
||||
|
||||
test('should return derived fields with correct properties', () => {
|
||||
const result = getDerivedFields(field);
|
||||
|
||||
expect(trimAutoCalendarName).toHaveBeenCalledWith('derivedField1');
|
||||
expect(result).toEqual([
|
||||
{
|
||||
qName: 'derivedField1',
|
||||
displayName: 'trimmed_derivedField1',
|
||||
qSrcTables: ['table1'],
|
||||
qTags: ['tag1'],
|
||||
isDerived: true,
|
||||
isDerivedFromDate: true,
|
||||
sourceField: 'field1',
|
||||
derivedDefinitionName: 'DerivedDef1',
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test('should set isDerivedFromDate to false if field.isDateField is false', () => {
|
||||
field.isDateField = false;
|
||||
|
||||
const result = getDerivedFields(field);
|
||||
|
||||
expect(result[0].isDerivedFromDate).toBe(false);
|
||||
});
|
||||
|
||||
test('should handle multiple derived fields', () => {
|
||||
field = {
|
||||
qName: 'field1',
|
||||
qSrcTables: ['table1'],
|
||||
isDateField: true,
|
||||
qDerivedFieldData: {
|
||||
qDerivedFieldLists: [
|
||||
{
|
||||
qDerivedDefinitionName: 'DerivedDef1',
|
||||
qFieldDefs: [
|
||||
{
|
||||
qName: 'derivedField1',
|
||||
qTags: ['tag1'],
|
||||
},
|
||||
{
|
||||
qName: 'derivedField2',
|
||||
qTags: ['tag2'],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const result = getDerivedFields(field);
|
||||
|
||||
expect(result).toEqual([
|
||||
{
|
||||
qName: 'derivedField1',
|
||||
displayName: 'trimmed_derivedField1',
|
||||
qSrcTables: ['table1'],
|
||||
qTags: ['tag1'],
|
||||
isDerived: true,
|
||||
isDerivedFromDate: true,
|
||||
sourceField: 'field1',
|
||||
derivedDefinitionName: 'DerivedDef1',
|
||||
},
|
||||
{
|
||||
qName: 'derivedField2',
|
||||
displayName: 'trimmed_derivedField2',
|
||||
qSrcTables: ['table1'],
|
||||
qTags: ['tag2'],
|
||||
isDerived: true,
|
||||
isDerivedFromDate: true,
|
||||
sourceField: 'field1',
|
||||
derivedDefinitionName: 'DerivedDef1',
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,80 @@
|
||||
import getAutoSortFieldDimension from '../get-sorted-field';
|
||||
import findFieldInExpandedList from '../find-field-in-expandedList';
|
||||
import { setAutoSort } from '../field-utils';
|
||||
|
||||
jest.mock('../find-field-in-expandedList', () => jest.fn());
|
||||
jest.mock('../field-utils', () => ({
|
||||
setAutoSort: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('getAutoSortFieldDimension', () => {
|
||||
let self;
|
||||
let dimension;
|
||||
|
||||
beforeEach(() => {
|
||||
self = {
|
||||
app: {
|
||||
getFieldList: jest.fn(),
|
||||
},
|
||||
};
|
||||
dimension = {
|
||||
qDef: {
|
||||
qFieldDefs: ['field1'],
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
test('should call findFieldInExpandedList with correct arguments', async () => {
|
||||
const fieldList = [{ qName: 'field1' }];
|
||||
self.app.getFieldList.mockResolvedValue(fieldList);
|
||||
|
||||
const result = await getAutoSortFieldDimension(self, dimension);
|
||||
|
||||
expect(self.app.getFieldList).toHaveBeenCalled();
|
||||
expect(findFieldInExpandedList).toHaveBeenCalledWith('field1', fieldList);
|
||||
expect(result).toBe(dimension);
|
||||
});
|
||||
|
||||
test('should call setAutoSort if a field is found', async () => {
|
||||
const fieldList = [{ qName: 'field1' }];
|
||||
const field = { qName: 'field1' };
|
||||
self.app.getFieldList.mockResolvedValue(fieldList);
|
||||
findFieldInExpandedList.mockReturnValue(field);
|
||||
|
||||
await getAutoSortFieldDimension(self, dimension);
|
||||
|
||||
expect(setAutoSort).toHaveBeenCalledWith([field], dimension, self);
|
||||
});
|
||||
|
||||
test('should not call setAutoSort if no field is found', async () => {
|
||||
self.app.getFieldList.mockResolvedValue([]);
|
||||
findFieldInExpandedList.mockReturnValue(null);
|
||||
|
||||
await getAutoSortFieldDimension(self, dimension);
|
||||
|
||||
expect(setAutoSort).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should handle empty field list', async () => {
|
||||
self.app.getFieldList.mockResolvedValue([]);
|
||||
|
||||
const result = await getAutoSortFieldDimension(self, dimension);
|
||||
|
||||
expect(result).toBe(dimension);
|
||||
expect(setAutoSort).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should handle missing qFieldDefs', async () => {
|
||||
dimension.qDef.qFieldDefs = undefined;
|
||||
self.app.getFieldList.mockResolvedValue([]);
|
||||
|
||||
const result = await getAutoSortFieldDimension(self, dimension);
|
||||
|
||||
expect(result).toBe(dimension);
|
||||
expect(setAutoSort).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,77 @@
|
||||
import getAutoSortLibraryDimension from '../get-sorted-library-field';
|
||||
import { findLibraryItem, setAutoSort } from '../field-utils';
|
||||
|
||||
jest.mock('../field-utils', () => ({
|
||||
findLibraryItem: jest.fn(),
|
||||
setAutoSort: jest.fn(),
|
||||
}));
|
||||
|
||||
describe('getAutoSortLibraryDimension', () => {
|
||||
let self;
|
||||
let dimension;
|
||||
|
||||
beforeEach(() => {
|
||||
self = {
|
||||
app: {
|
||||
getDimensionList: jest.fn(),
|
||||
},
|
||||
};
|
||||
dimension = {
|
||||
qLibraryId: 'libDim1',
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
test('should call findLibraryItem with correct arguments', async () => {
|
||||
const dimensionList = [{ qInfo: { qId: 'libDim1' }, qData: { info: ['field1'] } }];
|
||||
const libDim = dimensionList[0];
|
||||
self.app.getDimensionList.mockResolvedValue(dimensionList);
|
||||
findLibraryItem.mockReturnValue(libDim);
|
||||
|
||||
const result = await getAutoSortLibraryDimension(self, dimension);
|
||||
|
||||
expect(findLibraryItem).toHaveBeenCalledWith('libDim1', dimensionList);
|
||||
expect(result).toBe(dimension);
|
||||
});
|
||||
|
||||
test('should call setAutoSort if a library dimension is found', async () => {
|
||||
const dimensionList = [{ qInfo: { qId: 'libDim1' }, qData: { info: ['field1'] } }];
|
||||
const libDim = dimensionList[0];
|
||||
self.app.getDimensionList.mockResolvedValue(dimensionList);
|
||||
|
||||
await getAutoSortLibraryDimension(self, dimension);
|
||||
|
||||
expect(setAutoSort).toHaveBeenCalledWith(libDim.qData.info, dimension, self);
|
||||
});
|
||||
|
||||
test('should not call setAutoSort if no library dimension is found', async () => {
|
||||
self.app.getDimensionList.mockResolvedValue([]);
|
||||
findLibraryItem.mockReturnValue(null);
|
||||
|
||||
await getAutoSortLibraryDimension(self, dimension);
|
||||
|
||||
expect(setAutoSort).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should handle empty dimension list', async () => {
|
||||
self.app.getDimensionList.mockResolvedValue([]);
|
||||
|
||||
const result = await getAutoSortLibraryDimension(self, dimension);
|
||||
|
||||
expect(result).toBe(dimension);
|
||||
expect(setAutoSort).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test('should handle missing dimension list', async () => {
|
||||
dimension = undefined;
|
||||
self.app.getDimensionList.mockResolvedValue([]);
|
||||
|
||||
const result = await getAutoSortLibraryDimension(self, dimension);
|
||||
|
||||
expect(result).toBe(dimension);
|
||||
expect(setAutoSort).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,65 @@
|
||||
import { isDateField } from '../field-utils';
|
||||
|
||||
describe('isDateField', () => {
|
||||
test('should return true if field has $date tag', () => {
|
||||
const field01 = {
|
||||
qDerivedFieldData: {},
|
||||
qTags: ['$date', 'otherTag'],
|
||||
};
|
||||
|
||||
const field02 = {
|
||||
qDerivedFieldData: {},
|
||||
qTags: ['otherTag', '$timestamp'],
|
||||
};
|
||||
|
||||
const result01 = isDateField(field01);
|
||||
expect(result01).toBe(true);
|
||||
|
||||
const result02 = isDateField(field02);
|
||||
expect(result02).toBe(true);
|
||||
});
|
||||
|
||||
test('should return false if field does not have $date or $timestamp tag', () => {
|
||||
const field = {
|
||||
qDerivedFieldData: {},
|
||||
qTags: ['otherTag'],
|
||||
};
|
||||
|
||||
const result = isDateField(field);
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
|
||||
test('should return false if qDerivedFieldData is missing', () => {
|
||||
const field = {
|
||||
qTags: ['$date'],
|
||||
};
|
||||
|
||||
const result = isDateField(field);
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
|
||||
test('should return false if qTags is empty', () => {
|
||||
const field = {
|
||||
qDerivedFieldData: {},
|
||||
qTags: [],
|
||||
};
|
||||
|
||||
const result = isDateField(field);
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
|
||||
test('should return false if field is null or undefined', () => {
|
||||
expect(isDateField([])).toBeFalsy();
|
||||
expect(isDateField(undefined)).toBeFalsy();
|
||||
});
|
||||
|
||||
test('should return false if qTags is not an array', () => {
|
||||
const field = {
|
||||
qDerivedFieldData: {},
|
||||
qTags: null,
|
||||
};
|
||||
|
||||
const result = isDateField(field);
|
||||
expect(result).toBeFalsy();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,16 @@
|
||||
import getDataGeoField from './get-data-geo-field';
|
||||
import getDerivedFields from './get-derived-fields';
|
||||
|
||||
const expandFieldsWithDerivedData = (list) => {
|
||||
const fieldList = [];
|
||||
list.forEach((field) => {
|
||||
fieldList.push(getDataGeoField(field));
|
||||
|
||||
const derivedFields = getDerivedFields(field);
|
||||
fieldList.push(...derivedFields);
|
||||
});
|
||||
|
||||
return fieldList;
|
||||
};
|
||||
|
||||
export default expandFieldsWithDerivedData;
|
||||
85
apis/supernova/src/handler/utils/field-helper/field-utils.js
Normal file
85
apis/supernova/src/handler/utils/field-helper/field-utils.js
Normal file
@@ -0,0 +1,85 @@
|
||||
// eslint-disable-next-line import/no-relative-packages
|
||||
import uid from '../../../../../nucleus/src/object/uid';
|
||||
import { AUTOCALENDAR_NAME } from '../constants';
|
||||
|
||||
/**
|
||||
* Get the field name from the expression.
|
||||
* @param {string} expression
|
||||
* @returns the field
|
||||
*/
|
||||
export const getField = (expression) => {
|
||||
let exp = expression;
|
||||
exp = exp.trim();
|
||||
if (exp.charAt(0) === '=') {
|
||||
exp = exp.substring(1);
|
||||
exp = exp.trim();
|
||||
}
|
||||
const lastIndex = exp.length - 1;
|
||||
if (exp.charAt(0) === '[' && exp.charAt(lastIndex) === ']') {
|
||||
exp = exp.substring(1, lastIndex);
|
||||
exp = exp.trim();
|
||||
}
|
||||
return exp;
|
||||
};
|
||||
|
||||
export const findFieldById = (fields, id) => (fields && fields.find((field) => field.qDef?.cId === id)) || null;
|
||||
|
||||
export const findLibraryItem = (id, masterItemList) =>
|
||||
(masterItemList && masterItemList.find((item) => item.qInfo.qId === id)) || null;
|
||||
|
||||
export const findFieldByName = (name, fieldList) =>
|
||||
(fieldList && fieldList.find((field) => field.qName === name)) || null;
|
||||
|
||||
export const initializeId = (field) => ({
|
||||
...field,
|
||||
qDef: {
|
||||
...field.qDef,
|
||||
cId: field.qDef?.cId ?? uid(),
|
||||
},
|
||||
});
|
||||
|
||||
export const initializeField = (field) => ({
|
||||
...initializeId(field),
|
||||
qOtherTotalSpec: field.qOtherTotalSpec ?? {},
|
||||
});
|
||||
|
||||
export const setAutoSort = (fields, dimension, self) => {
|
||||
const dim = dimension;
|
||||
fields.forEach((field, index) => {
|
||||
const tags = field.qTags;
|
||||
const sortCriterias = {
|
||||
qSortByLoadOrder: 1,
|
||||
};
|
||||
|
||||
if (typeof self.dimensionDefinition.autoSort === 'function') {
|
||||
self.dimensionDefinition.autoSort(dim, self.properties, tags, sortCriterias, self);
|
||||
} else {
|
||||
// Default auto sorting
|
||||
sortCriterias.qSortByNumeric = 1;
|
||||
sortCriterias.qSortByAscii = 1;
|
||||
}
|
||||
|
||||
if (!dim.qDef.qSortCriterias) {
|
||||
dim.qDef.qSortCriterias = [sortCriterias];
|
||||
} else {
|
||||
dim.qDef.qSortCriterias[index] = sortCriterias;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useMasterNumberFormat = (formatting) => {
|
||||
const format = formatting;
|
||||
format.quarantine = {
|
||||
qNumFormat: format.qNumFormat || {},
|
||||
isCustomFormatted: format.isCustomFormatted || false,
|
||||
};
|
||||
format.qNumFormat = null;
|
||||
format.isCustomFormatted = undefined;
|
||||
};
|
||||
|
||||
export const isDateField = (field) =>
|
||||
field?.qDerivedFieldData && (field?.qTags?.indexOf('$date') > -1 || field?.qTags?.indexOf('$timestamp') > -1);
|
||||
|
||||
export const isGeoField = (field) => field.qTags.indexOf('$geoname') > -1;
|
||||
|
||||
export const trimAutoCalendarName = (fieldName) => (fieldName ? fieldName.split(AUTOCALENDAR_NAME).join('') : '');
|
||||
@@ -0,0 +1,10 @@
|
||||
import expandFieldsWithDerivedData from './expand-field-derived-data';
|
||||
import { findFieldByName, getField } from './field-utils';
|
||||
|
||||
const findFieldInExpandedList = (name, fieldList) => {
|
||||
const expandedList = expandFieldsWithDerivedData(fieldList.slice(0));
|
||||
const fieldName = getField(name);
|
||||
return (expandedList && findFieldByName(fieldName, expandedList)) || null;
|
||||
};
|
||||
|
||||
export default findFieldInExpandedList;
|
||||
@@ -0,0 +1,10 @@
|
||||
import { isDateField, isGeoField } from './field-utils';
|
||||
|
||||
const getDataGeoField = (field) => {
|
||||
const item = field;
|
||||
item.isDateField = isDateField(item);
|
||||
item.isGeoField = isGeoField(item);
|
||||
return item;
|
||||
};
|
||||
|
||||
export default getDataGeoField;
|
||||
@@ -0,0 +1,28 @@
|
||||
import { trimAutoCalendarName } from './field-utils';
|
||||
|
||||
const getDerivedFields = (field) => {
|
||||
const derivedFields = [];
|
||||
|
||||
if (!field.qDerivedFieldData) {
|
||||
return derivedFields;
|
||||
}
|
||||
|
||||
field.qDerivedFieldData.qDerivedFieldLists.forEach((derived) => {
|
||||
derived.qFieldDefs.forEach((derivedField) => {
|
||||
derivedFields.push({
|
||||
qName: derivedField.qName,
|
||||
displayName: trimAutoCalendarName(derivedField.qName),
|
||||
qSrcTables: field.qSrcTables,
|
||||
qTags: derivedField.qTags,
|
||||
isDerived: true,
|
||||
isDerivedFromDate: field.isDateField,
|
||||
sourceField: field.qName,
|
||||
derivedDefinitionName: derived.qDerivedDefinitionName,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
return derivedFields;
|
||||
};
|
||||
|
||||
export default getDerivedFields;
|
||||
@@ -0,0 +1,14 @@
|
||||
import findFieldInExpandedList from './find-field-in-expandedList';
|
||||
import { setAutoSort } from './field-utils';
|
||||
|
||||
function getAutoSortFieldDimension(self, dimension) {
|
||||
return self.app.getFieldList().then((fieldList) => {
|
||||
const field = dimension?.qDef?.qFieldDefs && findFieldInExpandedList(dimension.qDef.qFieldDefs[0], fieldList);
|
||||
if (field) {
|
||||
setAutoSort([field], dimension, self);
|
||||
}
|
||||
return dimension;
|
||||
});
|
||||
}
|
||||
|
||||
export default getAutoSortFieldDimension;
|
||||
@@ -0,0 +1,13 @@
|
||||
import { findLibraryItem, setAutoSort } from './field-utils';
|
||||
|
||||
function getAutoSortLibraryDimension(self, dimension) {
|
||||
return self.app.getDimensionList().then((dimensionList) => {
|
||||
const libDim = dimension?.qLibraryId && findLibraryItem(dimension.qLibraryId, dimensionList);
|
||||
if (libDim) {
|
||||
setAutoSort(libDim.qData.info, dimension, self);
|
||||
}
|
||||
return dimension;
|
||||
});
|
||||
}
|
||||
|
||||
export default getAutoSortLibraryDimension;
|
||||
@@ -1,32 +0,0 @@
|
||||
// eslint-disable-next-line import/no-relative-packages
|
||||
import getValue from '../../../../conversion/src/utils';
|
||||
|
||||
export const getFieldById = (fields, id) => fields.find((field) => field.qDef?.cId === id) || null;
|
||||
|
||||
export const setFieldProperties = (hcFieldProperties) => {
|
||||
if (!hcFieldProperties) {
|
||||
return [];
|
||||
}
|
||||
const updatedProperties = [...hcFieldProperties];
|
||||
|
||||
return updatedProperties.map((field) => {
|
||||
if (field.qDef?.autoSort && field.autoSort !== undefined) {
|
||||
return {
|
||||
...field,
|
||||
qDef: {
|
||||
...field.qDef,
|
||||
autoSort: field.autoSort,
|
||||
},
|
||||
autoSort: undefined,
|
||||
};
|
||||
}
|
||||
return field;
|
||||
});
|
||||
};
|
||||
|
||||
export const getHyperCube = (layout, path) => {
|
||||
if (!layout) {
|
||||
return undefined;
|
||||
}
|
||||
return path && getValue(layout, path) ? getValue(layout, path).qHyperCube : layout.qHyperCube;
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
import { insertMainDimension } from './hypercube-utils';
|
||||
|
||||
export default function addMainDimension(self, dimension, index) {
|
||||
const dimensions = self.getDimensions();
|
||||
const idx = index ?? dimensions.length;
|
||||
|
||||
if (dimensions.length < self.maxDimensions()) {
|
||||
return insertMainDimension(self, dimension, dimensions, idx);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import { insertMainMeasure } from './hypercube-utils';
|
||||
|
||||
export default function addMainMeasure(self, measure, index) {
|
||||
const measures = self.getMeasures();
|
||||
const idx = index ?? measures.length;
|
||||
|
||||
if (measures.length < self.maxMeasures()) {
|
||||
insertMainMeasure(measure, measures, idx);
|
||||
}
|
||||
|
||||
return measure;
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
// eslint-disable-next-line import/no-relative-packages
|
||||
import getValue from '../../../../../conversion/src/utils';
|
||||
// eslint-disable-next-line import/no-relative-packages
|
||||
import arrayUtil from '../../../../../conversion/src/array-util';
|
||||
import { TOTAL_MAX } from '../constants';
|
||||
|
||||
export const notSupportedError = new Error('Not supported in this object, need to implement in subclass.');
|
||||
|
||||
export const setFieldProperties = (hcFieldProperties) => {
|
||||
if (!hcFieldProperties) {
|
||||
return [];
|
||||
}
|
||||
const updatedProperties = [...hcFieldProperties];
|
||||
|
||||
return updatedProperties.map((field) => {
|
||||
if (field.qDef?.autoSort && field.autoSort !== undefined) {
|
||||
return {
|
||||
...field,
|
||||
qDef: {
|
||||
...field.qDef,
|
||||
autoSort: field.autoSort,
|
||||
},
|
||||
autoSort: undefined,
|
||||
};
|
||||
}
|
||||
return field;
|
||||
});
|
||||
};
|
||||
|
||||
export const getHyperCube = (layout, path) => {
|
||||
if (!layout) {
|
||||
return undefined;
|
||||
}
|
||||
return path && getValue(layout, path) ? getValue(layout, path).qHyperCube : layout.qHyperCube;
|
||||
};
|
||||
|
||||
export function setDefaultProperties(self) {
|
||||
const current = self;
|
||||
current.hcProperties.qDimensions = current.hcProperties.qDimensions ?? [];
|
||||
current.hcProperties.qMeasures = current.hcProperties.qMeasures ?? [];
|
||||
current.hcProperties.qInterColumnSortOrder = current.hcProperties.qInterColumnSortOrder ?? [];
|
||||
current.hcProperties.qLayoutExclude = current.hcProperties.qLayoutExclude ?? {
|
||||
qHyperCubeDef: { qDimensions: [], qMeasures: [] },
|
||||
};
|
||||
current.hcProperties.qLayoutExclude.qHyperCubeDef = current.hcProperties.qLayoutExclude.qHyperCubeDef ?? {
|
||||
qDimensions: [],
|
||||
qMeasures: [],
|
||||
};
|
||||
current.hcProperties.qLayoutExclude.qHyperCubeDef.qDimensions =
|
||||
current.hcProperties.qLayoutExclude.qHyperCubeDef.qDimensions ?? [];
|
||||
current.hcProperties.qLayoutExclude.qHyperCubeDef.qMeasures =
|
||||
current.hcProperties.qLayoutExclude.qHyperCubeDef.qMeasures ?? [];
|
||||
}
|
||||
|
||||
export function setPropForLineChartWithForecast(self) {
|
||||
const current = self;
|
||||
if (
|
||||
current.hcProperties.isHCEnabled &&
|
||||
current.hcProperties.qDynamicScript.length === 0 &&
|
||||
current.hcProperties.qMode === 'S'
|
||||
) {
|
||||
current.hcProperties.qDynamicScript = [];
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------
|
||||
// ----------- DIMENSIONS -----------
|
||||
// ----------------------------------
|
||||
|
||||
export function addAlternativeDimension(self, dimension, index = undefined) {
|
||||
const dimensions = self.hcProperties.qLayoutExclude.qHyperCubeDef.qDimensions;
|
||||
const idx = index ?? dimensions.length;
|
||||
dimensions.splice(idx, 0, dimension);
|
||||
return Promise.resolve(dimension);
|
||||
}
|
||||
|
||||
export function insertMainDimension(self, dimension, dimensions, idx) {
|
||||
dimensions.splice(idx, 0, dimension);
|
||||
|
||||
return self.autoSortDimension(dimension).then(() => {
|
||||
arrayUtil.indexAdded(self.hcProperties.qInterColumnSortOrder, self.getDimensions().length + dimension.length - 1);
|
||||
|
||||
if (typeof self.dimensionDefinition.add === 'function') {
|
||||
return Promise.resolve(self.dimensionDefinition.add.call(null, dimension, self.properties, self));
|
||||
}
|
||||
|
||||
return dimension;
|
||||
});
|
||||
}
|
||||
|
||||
export function addSortedDimension(self, dimension, dimensions, idx) {
|
||||
const dimIdx = idx ?? dimensions.length;
|
||||
dimensions.splice(dimIdx, 0, dimension);
|
||||
|
||||
return self.autoSortDimension(dimension).then(() => {
|
||||
arrayUtil.indexAdded(self.hcProperties.qInterColumnSortOrder, dimIdx ?? dimensions.length - 1);
|
||||
|
||||
return self.dimensionDefinition.add?.call(self, dimension, self.properties, self) || Promise.resolve(dimension);
|
||||
});
|
||||
}
|
||||
|
||||
export function isTotalDimensionsExceeded(self, dimensions) {
|
||||
const altDimensions = self.getAlternativeDimensions();
|
||||
return altDimensions.length + dimensions.length >= TOTAL_MAX.DIMENSIONS;
|
||||
}
|
||||
|
||||
export function isDimensionAlternative(self, alternative) {
|
||||
const dimensions = self.hcProperties.qLayoutExclude.qHyperCubeDef.qDimensions;
|
||||
return alternative || (self.maxDimensions() <= dimensions.length && dimensions.length < TOTAL_MAX.DIMENSIONS);
|
||||
}
|
||||
|
||||
export async function addActiveDimension(self, dimension, existingDimensions, addedDimensions, addedActive) {
|
||||
const initialLength = existingDimensions.length;
|
||||
await self.autoSortDimension(dimension);
|
||||
|
||||
// Update sorting order
|
||||
arrayUtil.indexAdded(self.hcProperties.qInterColumnSortOrder, initialLength + addedActive);
|
||||
|
||||
existingDimensions.push(dimension);
|
||||
addedDimensions.push(dimension);
|
||||
|
||||
if (typeof self.dimensionDefinition.add === 'function') {
|
||||
self.dimensionDefinition.add.call(self, dimension, self.properties, self);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------
|
||||
// ------------ MEASURES ------------
|
||||
// ----------------------------------
|
||||
|
||||
export function addAlternativeMeasure(self, measure, index = undefined) {
|
||||
const measures = self.hcProperties.qLayoutExclude.qHyperCubeDef.qMeasures;
|
||||
const idx = index ?? measures.length;
|
||||
measures.splice(idx, 0, measure);
|
||||
return Promise.resolve(measure);
|
||||
}
|
||||
|
||||
export function insertMainMeasure(self, measure, measures, idx) {
|
||||
measures.splice(idx, 0, measure);
|
||||
|
||||
return self.autoSortMeasure(measure).then(() => {
|
||||
arrayUtil.indexAdded(self.hcProperties.qInterColumnSortOrder, self.getDimensions().length + measure.length - 1);
|
||||
|
||||
if (typeof self.measureDefinition.add === 'function') {
|
||||
return Promise.resolve(self.measureDefinition.add.call(null, measure, self.properties, self));
|
||||
}
|
||||
|
||||
return measure;
|
||||
});
|
||||
}
|
||||
|
||||
export function isTotalMeasureExceeded(self, measures) {
|
||||
// Adding more measures than TOTAL_MAX_MEASURES is not allowed and we expect this.maxMeasures() to always be <= TOTAL_MAX_MEASURES
|
||||
const altMeasures = self.getAlternativeMeasures();
|
||||
return altMeasures.length + measures.length >= TOTAL_MAX.MEASURES;
|
||||
}
|
||||
|
||||
export function isMeasureAlternative(self, measures, alternative) {
|
||||
return alternative || (self.maxMeasures() <= measures.length && measures.length < TOTAL_MAX.MEASURES);
|
||||
}
|
||||
|
||||
export function addActiveMeasure(self, measure, existingMeasures, addedMeasures, addedActive) {
|
||||
const dimensions = self.getDimensions();
|
||||
const meas = { ...measure };
|
||||
meas.qSortBy = {
|
||||
qSortByLoadOrder: 1,
|
||||
qSortByNumeric: -1,
|
||||
};
|
||||
|
||||
arrayUtil.indexAdded(
|
||||
self.hcProperties.qInterColumnSortOrder,
|
||||
dimensions.length + existingMeasures.length + addedActive
|
||||
);
|
||||
existingMeasures.push(meas);
|
||||
addedMeasures.push(meas);
|
||||
|
||||
if (typeof self.measureDefinition.add === 'function') {
|
||||
self.measureDefinition.add.call(null, meas, self.properties, self);
|
||||
}
|
||||
|
||||
return Promise.resolve(addedMeasures);
|
||||
}
|
||||
Reference in New Issue
Block a user