1
0
mirror of https://github.com/Unleash/unleash.git synced 2024-12-22 19:07:54 +01:00

feat: playground custom properties are nested (#4686)

This commit is contained in:
Mateusz Kwasniewski 2023-09-14 12:28:28 +02:00 committed by GitHub
parent 31fcf603ac
commit 878780f068
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 179 additions and 12 deletions

View File

@ -27,7 +27,10 @@ import { formatUnknownError } from 'utils/formatUnknownError';
import useToast from 'hooks/useToast';
import { PlaygroundEditor } from './PlaygroundEditor/PlaygroundEditor';
import { parseDateValue, parseValidDate } from 'component/common/util';
import { isStringOrStringArray } from '../../playground.utils';
import {
isStringOrStringArray,
normalizeCustomContextProperties,
} from '../../playground.utils';
interface IPlaygroundCodeFieldsetProps {
context: string | undefined;
setContext: Dispatch<SetStateAction<string | undefined>>;
@ -58,7 +61,10 @@ export const PlaygroundCodeFieldset: VFC<IPlaygroundCodeFieldsetProps> = ({
try {
const contextValue = JSON.parse(input);
setFieldExist(contextValue[contextField] !== undefined);
setFieldExist(
contextValue[contextField] !== undefined ||
contextValue?.properties[contextField] !== undefined
);
} catch (error: unknown) {
return setError(formatUnknownError(error));
}
@ -75,12 +81,13 @@ export const PlaygroundCodeFieldset: VFC<IPlaygroundCodeFieldsetProps> = ({
const onAddField = () => {
try {
const currentValue = JSON.parse(context || '{}');
setContext(
JSON.stringify(
{
normalizeCustomContextProperties({
...currentValue,
[contextField]: contextValue,
},
}),
null,
2
)

View File

@ -0,0 +1,78 @@
import {
normalizeCustomContextProperties,
NormalizedContextProperties,
} from './playground.utils';
test('should keep standard properties in their place', () => {
const input: NormalizedContextProperties = {
appName: 'testApp',
environment: 'testEnv',
userId: 'testUser',
sessionId: 'testSession',
remoteAddress: '127.0.0.1',
currentTime: 'now',
};
const output = normalizeCustomContextProperties(input);
expect(output).toEqual(input);
});
test('should move non-standard properties to nested properties field', () => {
const input = {
appName: 'testApp',
customProp: 'customValue',
anotherCustom: 'anotherValue',
};
const output = normalizeCustomContextProperties(input);
expect(output).toEqual({
appName: 'testApp',
properties: {
customProp: 'customValue',
anotherCustom: 'anotherValue',
},
});
});
test('should not have properties field if there are no non-standard properties', () => {
const input = {
appName: 'testApp',
};
const output = normalizeCustomContextProperties(input);
expect(output).toEqual(input);
expect(output.properties).toBeUndefined();
});
test('should combine existing properties field with non-standard properties', () => {
const input = {
appName: 'testApp',
properties: {
existingProp: 'existingValue',
},
customProp: 'customValue',
};
const output = normalizeCustomContextProperties(input);
expect(output).toEqual({
appName: 'testApp',
properties: {
existingProp: 'existingValue',
customProp: 'customValue',
},
});
});
test('should add multiple standard properties without breaking custom properties', () => {
const input = {
appName: 'testApp',
properties: {
existingProp: 'existingValue',
},
currentTime: 'value',
};
const output = normalizeCustomContextProperties(input);
expect(output).toEqual({
appName: 'testApp',
currentTime: 'value',
properties: {
existingProp: 'existingValue',
},
});
});

View File

@ -73,3 +73,55 @@ export const isStringOrStringArray = (
return false;
};
type InputContextProperties = {
appName?: string;
environment?: string;
userId?: string;
sessionId?: string;
remoteAddress?: string;
currentTime?: string;
properties?: { [key: string]: any };
[key: string]: any;
};
export type NormalizedContextProperties = Omit<
InputContextProperties,
'properties'
> & {
properties?: { [key: string]: any };
};
export const normalizeCustomContextProperties = (
input: InputContextProperties
): NormalizedContextProperties => {
const standardProps = new Set([
'appName',
'environment',
'userId',
'sessionId',
'remoteAddress',
'currentTime',
'properties',
]);
const output: InputContextProperties = { ...input };
let hasCustomProperties = false;
for (const key in input) {
if (!standardProps.has(key)) {
if (!output.properties) {
output.properties = {};
}
output.properties[key] = input[key];
delete output[key];
hasCustomProperties = true;
}
}
if (!hasCustomProperties && !input.properties) {
delete output.properties;
}
return output;
};

View File

@ -37,3 +37,28 @@ test('should generate all combinations correctly when only one combination', ()
expect(actualCombinations).toEqual(expectedCombinations);
});
test('should generate combinations with nested properties', () => {
const obj = {
sessionId: '1,2',
nonString: 2,
properties: {
nonString: 1,
channels: 'internet',
appName: 'a,b,c',
},
};
const expectedCombinations = [
{ sessionId: '1', appName: 'a', channels: 'internet', nonString: 1 },
{ sessionId: '1', appName: 'b', channels: 'internet', nonString: 1 },
{ sessionId: '1', appName: 'c', channels: 'internet', nonString: 1 },
{ sessionId: '2', appName: 'a', channels: 'internet', nonString: 1 },
{ sessionId: '2', appName: 'b', channels: 'internet', nonString: 1 },
{ sessionId: '2', appName: 'c', channels: 'internet', nonString: 1 },
];
const actualCombinations = generateObjectCombinations(obj);
expect(actualCombinations).toEqual(expectedCombinations);
});

View File

@ -1,14 +1,19 @@
type Dict<T> = { [K in keyof T]: string[] };
type Dict<T> = { [K in keyof T]: (string | number)[] };
export const splitByComma = <T extends Record<string, unknown>>(
obj: T,
): Dict<T> =>
Object.fromEntries(
Object.entries(obj).map(([key, value]) => [
key,
typeof value === 'string' ? value.split(',') : [value],
]),
) as Dict<T>;
): Dict<T> => {
return Object.entries(obj).reduce((acc, [key, value]) => {
if (key === 'properties' && typeof value === 'object') {
const nested = splitByComma(value as any);
return { ...acc, ...nested };
} else if (typeof value === 'string') {
return { ...acc, [key]: value.split(',') };
} else {
return { ...acc, [key]: [value] };
}
}, {} as Dict<T>);
};
export const generateCombinations = <T extends Record<string, unknown>>(
obj: Dict<T>,