Skip to content

Commit

Permalink
feat: add actionParams support (#47)
Browse files Browse the repository at this point in the history
* feat: add actionParams support

* chore: clean code

* fix: actionParams logic

* fix: improve typings

* chore: fix typings

* fix: small fixes after review

* fix: refactored helpers

* chore: add helpers tests

* refact: move actionParams clear logic from update & fix actionParams queue logic

* chore: add actionParams tests for state-and-params function

* fix: fix after review

* fix: remove actionParams from state field

* chore: remove unused code

* fix: fix after review

* fix: fix after review

* chore: add comments

* chore: change sample text in tests

* chore: fix after review
  • Loading branch information
Marginy605 authored Apr 6, 2023
1 parent dbeb2e1 commit 0c78ae2
Show file tree
Hide file tree
Showing 7 changed files with 846 additions and 288 deletions.
2 changes: 2 additions & 0 deletions src/shared/constants/common.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
export const CURRENT_VERSION = 2;

export const META_KEY = '__meta__';

export const ACTION_PARAM_PREFIX = '_ap_';
113 changes: 111 additions & 2 deletions src/shared/modules/__tests__/helpers.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import {ConfigItem, StringParams, ItemsStateAndParams, ConfigItemData} from '../../types';
import {META_KEY} from '../../constants';
import {formQueueData} from '../helpers';
import {ACTION_PARAM_PREFIX, META_KEY} from '../../constants';
import {
formQueueData,
hasActionParams,
pickExceptActionParamsFromParams,
transformParamsToActionParams,
} from '../helpers';

const DEFAULT_CONTROL_ID = 'controlId';
const DEFAULT_WIDGET_ID = 'widgetId';
Expand Down Expand Up @@ -28,6 +33,66 @@ type MockedWidgetItemArgs = {
tabs?: ConfigItemData['tabs'];
};

const stateAndParamsWithParamsOnly = {
params: {
paramName: 'param1',
},
};

const stateAndParamsWithStateOnly = {
state: {
tabId: 'tabId1',
},
};

const stateAndParamsWithActionParamsInParamsOnly = {
params: {
_ap_paramName: 'param1',
},
};

const stateAndParamsWithStateAndActionParamsInParams = {
state: {
tabId: 'tabId1',
},
params: {
_ap_paramName: 'param1',
},
};

const stateAndParamsWithParamsAndActionParamsInParams = {
params: {
paramName2: 'param2',
_ap_paramName: 'param1',
},
};

const paramsToTransform = {
in: {
param1: 'val 1',
param2: 'val 2',
},
out: {
[`${ACTION_PARAM_PREFIX}param1`]: 'val 1',
[`${ACTION_PARAM_PREFIX}param2`]: 'val 2',
},
};

const exeptActionParam1 = {
in: stateAndParamsWithParamsOnly.params,
out: stateAndParamsWithParamsOnly.params,
};
const exeptActionParam2 = {
in: stateAndParamsWithActionParamsInParamsOnly.params,
out: {},
};
const exeptActionParam3 = {
in: stateAndParamsWithParamsAndActionParamsInParams.params,
out: {
paramName2: 'param2',
},
};

const getMockedWidgetItem = ({id = DEFAULT_WIDGET_ID, tabs}: MockedWidgetItemArgs): ConfigItem => ({
id,
data: {tabs},
Expand Down Expand Up @@ -255,4 +320,48 @@ describe('modules.helpers', () => {
);
});
});

describe('actionParams helpers', () => {
it('check hasActionParams', () => {
const empty = hasActionParams({});
expect(empty).toBeFalsy();

const withParamsOnly = hasActionParams(stateAndParamsWithParamsOnly);
expect(withParamsOnly).toBeFalsy();

const withStateOnly = hasActionParams(stateAndParamsWithStateOnly);
expect(withStateOnly).toBeFalsy();

const withAPinParams = hasActionParams(stateAndParamsWithActionParamsInParamsOnly);
expect(withAPinParams).toBeTruthy();

const withAPinParamsNState = hasActionParams(
stateAndParamsWithStateAndActionParamsInParams,
);
expect(withAPinParamsNState).toBeTruthy();

const withParamsAndAPinParams = hasActionParams(
stateAndParamsWithParamsAndActionParamsInParams,
);
expect(withParamsAndAPinParams).toBeTruthy();
});

it('check transformParamsToActionParams', () => {
expect(paramsToTransform.out).toEqual(
transformParamsToActionParams(paramsToTransform.in),
);
});

it('check pickExceptActionParamsFromParams', () => {
expect(exeptActionParam1.out).toEqual(
pickExceptActionParamsFromParams(exeptActionParam1.in),
);
expect(exeptActionParam2.out).toEqual(
pickExceptActionParamsFromParams(exeptActionParam2.in),
);
expect(exeptActionParam3.out).toEqual(
pickExceptActionParamsFromParams(exeptActionParam3.in),
);
});
});
});
121 changes: 116 additions & 5 deletions src/shared/modules/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import get from 'lodash/get';
import invert from 'lodash/invert';
import isEmpty from 'lodash/isEmpty';
import pick from 'lodash/pick';
import {META_KEY, CURRENT_VERSION} from '../constants';
import {META_KEY, CURRENT_VERSION, ACTION_PARAM_PREFIX} from '../constants';
import {
PluginBase,
ConfigItem,
Expand All @@ -16,6 +16,7 @@ import {
ConfigItemWithTabs,
Config,
QueueItem,
ItemStateAndParams,
} from '../types';

function getNormalizedPlugins(plugins: PluginBase[]) {
Expand Down Expand Up @@ -77,7 +78,7 @@ export type FormedQueueData = {
params: StringParams;
};

// Массив параметров, которые исходят от виджетов, согласно очереди
// Array of parameters from widgets according to queue
export function formQueueData({
items,
itemsStateAndParams,
Expand Down Expand Up @@ -106,10 +107,33 @@ export function formQueueData({
}

const itemQueueParams: StringParams = get(itemsStateAndParams, [item.id, 'params'], {});
const filteredParamsByDefaults = pick(itemQueueParams, Object.keys(itemDefaultParams));

/**
* filtering actionParams without prefixes by defaults params
* ex.:
* itemDefaultParams contains 'Country' in defaults
* and we receive '_ap_Country' and '_ap_City' in itemQueueParams
* we need to ignore '_ap_City' param because we don't have actionParam without prefix ('City') in defaults
*/
const actionParams = pickActionParamsFromParams(itemQueueParams, false) || {};
const filteredActionParamsByDefaults = pick(
actionParams,
Object.keys(itemDefaultParams),
);

/**
* merging filtered params and filtered actionParams with prefixes
*/
const params = {
...filteredParamsByDefaults,
...transformParamsToActionParams(filteredActionParamsByDefaults),
};

return {
id: item.id,
namespace: item.namespace,
params: pick(itemQueueParams, Object.keys(itemDefaultParams)),
params,
};
})
.filter(nonNullable);
Expand Down Expand Up @@ -181,14 +205,20 @@ export function mergeParamsWithAliases({
aliases,
namespace,
params,
actionParams,
}: {
aliases: ConfigAliases;
namespace: string;
params: StringParams;
actionParams?: StringParams;
}): StringParams {
const aliasesByNamespace = get(aliases, [namespace], []) as string[][];
return Object.keys(params).reduce((matchedParams: StringParams, paramKey) => {
const paramValue = params[paramKey];
const items = {
...(params || {}),
...(actionParams || {}),
};
return Object.keys(items).reduce((matchedParams: StringParams, paramKey) => {
const paramValue = items[paramKey];
const collectAliasesParamsKeys = aliasesByNamespace.reduce(
(collect, group) => {
return group.includes(paramKey) ? collect.concat(group) : collect;
Expand Down Expand Up @@ -250,3 +280,84 @@ export function deleteFromQueue(data: ChangeQueueArg): StateAndParamsMetaData {
queue: meta.queue.slice(0, -1),
};
}

/**
* public function for getting only actionParams from object (all fields with keys that contains prefix)
* @param params - object for pick fields
* @param returnWithPrefix - format of returning actionParams fields (with actionParams prefix or without them)
*
* ex1: pickActionParamsFromParams({City: 'NY', _ap_Year: '2023'}, true) returns {_ap_Year: '2023'}
* ex2: pickActionParamsFromParams({City: 'NY', _ap_Year: '2023'}) returns {Year: '2023'}
*/
export function pickActionParamsFromParams(
params: ItemStateAndParams['params'],
returnWithPrefix?: boolean,
) {
if (!params || isEmpty(params)) {
return {};
}

const actionParams: StringParams = {};
for (const [key, val] of Object.entries(params)) {
// starts with actionParams prefix (from'_ap_')
if (key.startsWith(ACTION_PARAM_PREFIX)) {
const paramName = returnWithPrefix ? key : key.slice(ACTION_PARAM_PREFIX.length);
actionParams[paramName] = val;
}
}
return actionParams;
}

/**
* public function for getting params from object without actionParams
* @param params
*/
export function pickExceptActionParamsFromParams(params: ItemStateAndParams['params']) {
if (!params || isEmpty(params)) {
return {};
}

const onlyParams: StringParams = {};
for (const [key, val] of Object.entries(params)) {
if (!key.startsWith(ACTION_PARAM_PREFIX)) {
onlyParams[key] = val;
}
}
return onlyParams;
}

/**
* public function for transforming object to actionParams format
* @param params
*/
export function transformParamsToActionParams(params: ItemStateAndParams['params']) {
if (!params || isEmpty(params)) {
return {};
}

const actionParams: StringParams = {};
for (const [key, val] of Object.entries(params)) {
actionParams[`${ACTION_PARAM_PREFIX}${key}`] = val;
}
return actionParams;
}

/**
* check if object contains actionParams
* @param conf
*/
export function hasActionParam(conf?: StringParams): boolean {
return Object.keys(conf || {}).some((key) => key.startsWith(ACTION_PARAM_PREFIX));
}

/**
* check if ItemStateAndParams object has actionParams in params or state field
* @param stateAndParams
*/
export function hasActionParams(stateAndParams: ItemStateAndParams) {
if (!stateAndParams || isEmpty(stateAndParams)) {
return false;
}

return hasActionParam(stateAndParams.params);
}
Loading

0 comments on commit 0c78ae2

Please sign in to comment.