Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: set_item action: optionally target an index or id #2215

Merged
merged 43 commits into from
Apr 30, 2024
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
882cc4e
wip: set_item_at_index action
jfmcquade Mar 1, 2024
33ac196
feat: add rest_data action
jfmcquade Mar 1, 2024
a406ad2
chore: clarified comment
jfmcquade Mar 1, 2024
93d7102
Merge branch 'master' into feat/set-item-at-index
jfmcquade Mar 5, 2024
11831a9
refactor: incorporate set_item_at_index logic into set_item instead
jfmcquade Mar 5, 2024
63f4d7f
Merge branch 'master' into feat/set-item-at-index
jfmcquade Mar 6, 2024
4107611
chore: remove workaround for fixed cdr issue; properly handle _index …
jfmcquade Mar 6, 2024
60d5ef0
chore: handle _index === 0
jfmcquade Mar 6, 2024
89885d5
Merge branch 'feat/data-items-eval-context' into feat/set-item-at-index
jfmcquade Mar 6, 2024
40e86f9
fix: @item references in expressions within data-items loops
jfmcquade Mar 6, 2024
e1ea106
fix: complete merge of feat/data-items-eval-context
jfmcquade Mar 6, 2024
8b3b134
chore: remove comments
jfmcquade Mar 6, 2024
afad693
Merge branch 'feat/data-items-eval-context' into feat/set-item-at-index
jfmcquade Mar 6, 2024
c3a014d
Merge branch 'feat/data-items-eval-context' into feat/set-item-at-index
jfmcquade Mar 6, 2024
06cc641
chore: code tidy
jfmcquade Mar 6, 2024
3ca52ca
chore: code tidy
jfmcquade Mar 6, 2024
c75bb43
Merge branch 'master' into feat/set-item-at-index
jfmcquade Apr 3, 2024
989a715
chore: post-merge fix
jfmcquade Apr 3, 2024
5108803
chore: tidy comments
jfmcquade Apr 4, 2024
f9f17ca
Merge branch 'master' into feat/set-item-at-index
jfmcquade Apr 5, 2024
5c21fd8
chore: code tidy
jfmcquade Apr 5, 2024
fda82d6
fix: handle itemIndex passed from data items component
jfmcquade Apr 5, 2024
f96209d
chore: remove debug logs
jfmcquade Apr 5, 2024
40c98cf
Merge branch 'master' into feat/set-item-at-index
esmeetewinkel Apr 12, 2024
ef3047a
chore: review code tidying
chrismclarke Apr 12, 2024
b8074b9
Merge branch 'master' into feat/set-item-at-index
esmeetewinkel Apr 19, 2024
e262c20
chore: tidy console logs
jfmcquade Apr 22, 2024
350bd47
chore: reset theme file
jfmcquade Apr 22, 2024
9f9f483
chore: tidy getTargetItemRow logic
jfmcquade Apr 22, 2024
1b1e782
wip: create spec file for template variables service
jfmcquade Apr 22, 2024
2e04f56
Merge branch 'refactor/extract-dynamic-fields' into feat/set-item-at-…
jfmcquade Apr 22, 2024
b315003
wip: template variable spec test setup
jfmcquade Apr 23, 2024
72f89bb
test: add spec tests for template-variables service
jfmcquade Apr 24, 2024
9ea2aeb
refactor: set_item logic
jfmcquade Apr 24, 2024
c327d3a
test: add spec tests for dynamic data setItem logic
jfmcquade Apr 24, 2024
8a95b0d
refactor: set_item logic
jfmcquade Apr 24, 2024
2a2f1e5
test: add spec tests for dynamic data setItem logic
jfmcquade Apr 24, 2024
daddda1
merge
jfmcquade Apr 24, 2024
a0120e3
Merge branch 'master' into feat/set-item-at-index
jfmcquade Apr 24, 2024
378f256
chore: mock template-field and error-handler service bootstrap
chrismclarke Apr 29, 2024
149c868
refactor: template-variable specs
chrismclarke Apr 30, 2024
04168d3
chore: remove focused test
chrismclarke Apr 30, 2024
74a3efb
Merge branch 'master' into feat/set-item-at-index
chrismclarke Apr 30, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions packages/data-models/flowTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,7 @@ export namespace FlowTypes {
_evalContext?: { itemContext: TemplateRowItemEvalContext }; // force specific context variables when calculating eval statements (such as loop items)
__EMPTY?: any; // empty cells (can be removed after pr 679 merged)
}

export type IDynamicField = { [key: string]: IDynamicField | TemplateRowDynamicEvaluator[] };

export interface TemplateRowItemEvalContext {
Expand All @@ -351,6 +352,7 @@ export namespace FlowTypes {
// item data
[key: string]: any;
}
export const itemMetadataFieldNames = ["_id", "_index", "_first", "_last"];

type IDynamicPrefix = IAppConfig["DYNAMIC_PREFIXES"][number];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export class TmplDataItemsComponent extends TemplateBaseComponent implements OnD
if (r.action_list) {
r.action_list = r.action_list.map((a) => {
if (a.action_id === "set_item") {
a.args = [this.dataListName, r._evalContext.itemContext._id];
a.args = [this.dataListName, itemDataIDs, itemIndex];
}
if (a.action_id === "set_items") {
// TODO - add a check for @item refs and replace parameter list with correct values
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,18 @@ export class TemplateVariablesService extends AsyncServiceBase {
}

/**
* Inore evaluation of meta, comment, and specifiedfields.
* Ignore evaluation of meta, comment, and specifiedfields.
* Could provide single list of approved fields, but as dynamic fields also can be found in parameter lists
* would likely prove too restrictive
**/
private shouldEvaluateField(fieldName: keyof FlowTypes.TemplateRow, omitFields: string[] = []) {
private shouldEvaluateField(
fieldName: keyof FlowTypes.TemplateRow | (typeof FlowTypes.itemMetadataFieldNames)[number],
omitFields: string[] = []
) {
if (omitFields.includes(fieldName)) return false;
// Evaluate fields that are names of item metadata fields, e.g. "_index", "_id",
// E.g. for use in actions such as `click | set_item | _index: @item._index + 1, completed:false`
if (FlowTypes.itemMetadataFieldNames.includes(fieldName)) return true;
if (fieldName.startsWith("_")) return false;
return true;
}
Expand Down Expand Up @@ -183,6 +189,11 @@ export class TemplateVariablesService extends AsyncServiceBase {
return evaluator.fullExpression.replace(/`/gi, "");
}

// Do not evaluate if the appropriate context is not available
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This took me ages to figure out! There may be a nicer solution, but this is why something like this is needed:

Even though we have the check lower down (line 393), without this additional check then the string gets evaluated as JS, without the dynamic evaluator value being replaced. This meant that my string @item._index + 1 was being evaluated as JS before itemContext was available, evaluating to @item._index1 because it was treated as the addition of a string and a number, "@item._index" + 1. Then, when the expression was re-evaluated after the data-items component had added the itemContext, @item._index1 was evaluated with the value of @item_index replaced. For the item at index 0, this actually meant the expression evaluated as 01, i.e. 1, and so for that one case the evaluation actually gave the expected value, which appeared as a false positive in my testing...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really useful to catch, it would be good to include such things in spec test if possible

if (type === "item" && !context.itemContext) {
return evaluator.fullExpression;
}

// process the main lookup, e.g. @local.some_val, @campaign.some_val
// NOTE - if parse fail an empty string will be returned
let { parsedValue, parseSuccess } = await this.processDynamicEvaluator(evaluator, context);
Expand Down
36 changes: 33 additions & 3 deletions src/app/shared/services/dynamic-data/dynamic-data.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { FlowTypes } from "data-models";
import { environment } from "src/environments/environment";
import { AppDataService } from "../data/app-data.service";
import { AsyncServiceBase } from "../asyncService.base";
import { arrayToHashmap, deepMergeObjects } from "../../utils";
import { arrayToHashmap, deepMergeObjects, evaluateJSExpression } from "../../utils";
import { PersistedMemoryAdapter } from "./adapters/persistedMemory";
import { ReactiveMemoryAdapater, REACTIVE_SCHEMA_BASE } from "./adapters/reactiveMemory";
import { TemplateActionRegistry } from "../../components/template/services/instance/template-action.registry";
Expand Down Expand Up @@ -66,9 +66,39 @@ export class DynamicDataService extends AsyncServiceBase {
}
private registerTemplateActionHandlers() {
this.templateActionRegistry.register({
/**
* Write properties on the current item (default), or on an explicitly targeted item,
* e.g.
* click | set_item | completed:true;
* click | set_item | _id: @item.id, completed:true;
* click | set_item | _index: @item._index + 1, completed:true;
*/
set_item: async ({ args, params }) => {
const [flow_name, row_id] = args;
await this.update("data_list", flow_name, row_id, params);
const [flow_name, itemDataIDs, itemIndex] = args;
const { _index, _id, ...writeableProps } = params;

// Target current row if another target is not explicitly provided
let targetRowId = itemDataIDs[itemIndex];
if (_index !== undefined) {
targetRowId = itemDataIDs[_index];
}
if (_id) {
targetRowId = _id;
}

if (itemDataIDs.includes(targetRowId)) {
console.log(
`[SET ITEM] - Setting properties on ${targetRowId}: ${JSON.stringify(writeableProps)}`
);
await this.update("data_list", flow_name, targetRowId, writeableProps);
} else {
if (_id) {
console.warn(`[SET ITEM] - No item with ID ${_id}`);
}
if (_index !== undefined) {
console.warn(`[SET ITEM] - No item at index ${_index}`);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit(blocking): I feel like this is a large amount of item-specific logic being handled in the dynamic-data service. Would it be possible to move the item selection logic to the items component instead, so that the action is always triggered with the correct item index instead?
There's also quite some debug logs that I think probably should make it into production (would suggest remove console log and merge warn statements

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeh, I can see why having the logic here isn't great, but there's an issue with putting it in the data-items component itself:
We're setting the action args metadata (itemDataIDs, current itemIndex) from the data-items component which has full access to these values at the point of rendering the rows. This provides the context that we need to evaluate the target item ID based upon the action's params (_index or _id). However at the point of rendering the item rows in the data-items component, those action param values as they are read from the template row's action list by the data-items component are still un-evaluated representations of dynamic values, e.g. the string @item._index + 1. This only gets evaluated fully when the action is actually triggered.

In order to extract the item-specific logic from the dynamic data service, perhaps it would be sensible to add a new items (or data-items?) service, and have that register the set_item and set_items actions itself and call the update method of the dynamic data service with a specific item ID. If you think that's a good idea then I'll go ahead and refactor the logic (and corresponding tests) into a new service.

For now, I've just tidied the logic and extracted it to a (testable) function. I muddled the commits somewhat, but looking at the differences since your last review should be instructive.

}
},
set_items: async ({ args, params }) => {
const [flow_name, row_ids] = args;
Expand Down
2 changes: 1 addition & 1 deletion src/theme/themes/pfr.scss
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
// radio-button-font-color: var(--ion-color-primary),
ion-item-background: var(--ion-color-gray-light),
// task-progress-bar-color: var(--ion-color-primary),
// checkbox-background-color: white,,,
// checkbox-background-color: white,,,,
chrismclarke marked this conversation as resolved.
Show resolved Hide resolved
);
@include utils.generateTheme($color-primary, $color-secondary, $page-background, $g: $green);
@each $name, $value in $variable-overrides {
Expand Down
Loading