feat: conditional filtering & aggregation support & data ordering support (#2107)
* feat: wip * feat: add filter on findOne * fix: tests & small bug * feat: add test and support aggregation * feat: add order by support * fix: fix comments * fix: tests
This commit is contained in:
@@ -66,4 +66,25 @@ describe('convertArguments', () => {
|
||||
const expected = { column_1randomFirstNameKey: 'John', lastName: 'Doe' };
|
||||
expect(convertArguments(args, fields)).toEqual(expected);
|
||||
});
|
||||
|
||||
test('should handle deeper nested object arguments', () => {
|
||||
const args = {
|
||||
user: {
|
||||
details: {
|
||||
firstName: 'John',
|
||||
website: { link: 'https://www.example.com', text: 'example' },
|
||||
},
|
||||
},
|
||||
};
|
||||
const expected = {
|
||||
user: {
|
||||
details: {
|
||||
column_1randomFirstNameKey: 'John',
|
||||
column_randomLinkKey: 'https://www.example.com',
|
||||
column_randomTex7Key: 'example',
|
||||
},
|
||||
},
|
||||
};
|
||||
expect(convertArguments(args, fields)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import { generateArgsInput } from 'src/tenant/entity-resolver/utils/generate-args-input.util';
|
||||
|
||||
const normalizeWhitespace = (str) => str.replace(/\s+/g, '');
|
||||
|
||||
describe('generateArgsInput', () => {
|
||||
it('should handle string inputs', () => {
|
||||
const args = { someKey: 'someValue' };
|
||||
|
||||
expect(normalizeWhitespace(generateArgsInput(args))).toBe(
|
||||
normalizeWhitespace('someKey: "someValue"'),
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle number inputs', () => {
|
||||
const args = { someKey: 123 };
|
||||
|
||||
expect(normalizeWhitespace(generateArgsInput(args))).toBe(
|
||||
normalizeWhitespace('someKey: 123'),
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle boolean inputs', () => {
|
||||
const args = { someKey: true };
|
||||
|
||||
expect(normalizeWhitespace(generateArgsInput(args))).toBe(
|
||||
normalizeWhitespace('someKey: true'),
|
||||
);
|
||||
});
|
||||
|
||||
it('should skip undefined values', () => {
|
||||
const args = { definedKey: 'value', undefinedKey: undefined };
|
||||
|
||||
expect(normalizeWhitespace(generateArgsInput(args))).toBe(
|
||||
normalizeWhitespace('definedKey: "value"'),
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle object inputs', () => {
|
||||
const args = { someKey: { nestedKey: 'nestedValue' } };
|
||||
|
||||
expect(normalizeWhitespace(generateArgsInput(args))).toBe(
|
||||
normalizeWhitespace('someKey: {nestedKey: "nestedValue"}'),
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle null inputs', () => {
|
||||
const args = { someKey: null };
|
||||
|
||||
expect(normalizeWhitespace(generateArgsInput(args))).toBe(
|
||||
normalizeWhitespace('someKey: null'),
|
||||
);
|
||||
});
|
||||
|
||||
it('should remove trailing commas', () => {
|
||||
const args = { firstKey: 'firstValue', secondKey: 'secondValue' };
|
||||
|
||||
expect(normalizeWhitespace(generateArgsInput(args))).toBe(
|
||||
normalizeWhitespace('firstKey: "firstValue", secondKey: "secondValue"'),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,55 +0,0 @@
|
||||
import {
|
||||
FieldMetadata,
|
||||
FieldMetadataTargetColumnMap,
|
||||
} from 'src/metadata/field-metadata/field-metadata.entity';
|
||||
import { getFieldAliases } from 'src/tenant/entity-resolver/utils/get-fields-aliases.util';
|
||||
|
||||
describe('getFieldAliases', () => {
|
||||
let fields: FieldMetadata[];
|
||||
|
||||
beforeEach(() => {
|
||||
// Setup sample field metadata
|
||||
fields = [
|
||||
{
|
||||
name: 'singleValueField',
|
||||
targetColumnMap: {
|
||||
value: 'column_singleValue',
|
||||
} as FieldMetadataTargetColumnMap,
|
||||
},
|
||||
{
|
||||
name: 'multipleValuesField',
|
||||
targetColumnMap: {
|
||||
link: 'column_value1',
|
||||
text: 'column_value2',
|
||||
} as FieldMetadataTargetColumnMap,
|
||||
},
|
||||
] as FieldMetadata[];
|
||||
});
|
||||
|
||||
test('should return correct aliases for fields with a single value in targetColumnMap', () => {
|
||||
const aliases = getFieldAliases(fields);
|
||||
expect(aliases).toHaveProperty('singleValueField', 'column_singleValue');
|
||||
});
|
||||
|
||||
test('should return correct aliases for fields with multiple values in targetColumnMap', () => {
|
||||
const aliases = getFieldAliases(fields);
|
||||
expect(aliases).toHaveProperty('column_value1', 'column_value1');
|
||||
});
|
||||
|
||||
test('should handle empty fields array', () => {
|
||||
const aliases = getFieldAliases([]);
|
||||
expect(aliases).toEqual({});
|
||||
});
|
||||
|
||||
test('should not create aliases for fields without targetColumnMap values', () => {
|
||||
const fieldsWithEmptyMap = [
|
||||
...fields,
|
||||
{
|
||||
name: 'emptyField',
|
||||
targetColumnMap: {} as FieldMetadataTargetColumnMap,
|
||||
},
|
||||
] as FieldMetadata[];
|
||||
const aliases = getFieldAliases(fieldsWithEmptyMap);
|
||||
expect(aliases).not.toHaveProperty('emptyField');
|
||||
});
|
||||
});
|
||||
@@ -1,5 +1,3 @@
|
||||
import isEmpty from 'lodash.isempty';
|
||||
|
||||
import { FieldMetadata } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||
|
||||
export const convertArguments = (args: any, fields: FieldMetadata[]): any => {
|
||||
@@ -7,31 +5,46 @@ export const convertArguments = (args: any, fields: FieldMetadata[]): any => {
|
||||
fields.map((metadata) => [metadata.name, metadata]),
|
||||
);
|
||||
|
||||
if (Array.isArray(args)) {
|
||||
return args.map((arg) => convertArguments(arg, fields));
|
||||
}
|
||||
const processObject = (obj: any): any => {
|
||||
if (typeof obj !== 'object' || obj === null) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
const newArgs = {};
|
||||
if (Array.isArray(obj)) {
|
||||
return obj.map((item) => processObject(item));
|
||||
}
|
||||
|
||||
for (const [key, value] of Object.entries(args)) {
|
||||
if (fieldsMap.has(key)) {
|
||||
const fieldMetadata = fieldsMap.get(key)!;
|
||||
const newObj = {};
|
||||
|
||||
if (typeof value === 'object' && value !== null && !isEmpty(value)) {
|
||||
for (const [key, value] of Object.entries(obj)) {
|
||||
const fieldMetadata = fieldsMap.get(key);
|
||||
|
||||
if (
|
||||
fieldMetadata &&
|
||||
typeof value === 'object' &&
|
||||
value !== null &&
|
||||
Object.values(fieldMetadata.targetColumnMap).length > 1
|
||||
) {
|
||||
for (const [subKey, subValue] of Object.entries(value)) {
|
||||
if (fieldMetadata.targetColumnMap[subKey]) {
|
||||
newArgs[fieldMetadata.targetColumnMap[subKey]] = subValue;
|
||||
const mappedKey = fieldMetadata.targetColumnMap[subKey];
|
||||
|
||||
if (mappedKey) {
|
||||
newObj[mappedKey] = subValue;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (fieldMetadata.targetColumnMap.value) {
|
||||
newArgs[fieldMetadata.targetColumnMap.value] = value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
newArgs[key] = value;
|
||||
}
|
||||
}
|
||||
} else if (fieldMetadata) {
|
||||
const mappedKey = fieldMetadata.targetColumnMap.value;
|
||||
|
||||
return newArgs;
|
||||
if (mappedKey) {
|
||||
newObj[mappedKey] = value;
|
||||
}
|
||||
} else {
|
||||
newObj[key] = processObject(value);
|
||||
}
|
||||
}
|
||||
|
||||
return newObj;
|
||||
};
|
||||
|
||||
return processObject(args);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { stringifyWithoutKeyQuote } from './stringify-without-key-quote.util';
|
||||
|
||||
export const generateArgsInput = (args: any) => {
|
||||
let argsString = '';
|
||||
|
||||
for (const key in args) {
|
||||
// Check if the value is not undefined
|
||||
if (args[key] === undefined) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (typeof args[key] === 'string') {
|
||||
// If it's a string, add quotes
|
||||
argsString += `${key}: "${args[key]}", `;
|
||||
} else if (typeof args[key] === 'object' && args[key] !== null) {
|
||||
// If it's an object (and not null), stringify it
|
||||
argsString += `${key}: ${stringifyWithoutKeyQuote(args[key])}, `;
|
||||
} else {
|
||||
// For other types (number, boolean), add as is
|
||||
argsString += `${key}: ${args[key]}, `;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove trailing comma and space, if present
|
||||
if (argsString.endsWith(', ')) {
|
||||
argsString = argsString.slice(0, -2);
|
||||
}
|
||||
|
||||
return argsString;
|
||||
};
|
||||
@@ -1,21 +0,0 @@
|
||||
import { FieldMetadata } from 'src/metadata/field-metadata/field-metadata.entity';
|
||||
|
||||
export const getFieldAliases = (fields: FieldMetadata[]) => {
|
||||
const fieldAliases = fields.reduce((acc, column) => {
|
||||
const values = Object.values(column.targetColumnMap);
|
||||
|
||||
if (values.length === 1) {
|
||||
return {
|
||||
...acc,
|
||||
[column.name]: values[0],
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
...acc,
|
||||
[values[0]]: values[0],
|
||||
};
|
||||
}
|
||||
}, {});
|
||||
|
||||
return fieldAliases;
|
||||
};
|
||||
Reference in New Issue
Block a user