Skip to content

Commit

Permalink
Data change callback op (#49)
Browse files Browse the repository at this point in the history
* WIP: dataChangeCallback removed

* typo in setDataChangeCallback

* fix datachange callback for 'delete' op

* v0.1.5

---------

Co-authored-by: Sergey Sergeev <[email protected]>
Co-authored-by: Geoffrey Hendrey <[email protected]>
  • Loading branch information
3 people authored Feb 28, 2024
1 parent 43893fa commit 6aa5fd5
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 22 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "stated-js",
"version": "0.1.4",
"version": "0.1.5",
"license": "Apache-2.0",
"description": "JSONata embedded in JSON",
"main": "./dist/src/TemplateProcessor.js",
Expand Down
27 changes: 17 additions & 10 deletions src/TemplateProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -964,15 +964,15 @@ export default class TemplateProcessor {
}
}

private async executePlan(plan:JsonPointerString[], data:any = TemplateProcessor.NOOP, op:"set"|"setDeferred"|"delete"="set"):Promise<void> {
private async executePlan(changedJsonPointers:JsonPointerString[], data:any = TemplateProcessor.NOOP, op:"set"|"setDeferred"|"delete"="set"):Promise<void> {
const resp = [];
let dependencies = plan;
let dependencies = changedJsonPointers;
if (data !== TemplateProcessor.NOOP) { //this plan begins with setting data
await this.applyMutationToFirstJsonPointerOfPlan(plan, data, op);
dependencies = plan.slice(1); //we have processes=d the entry point which can receive a mutation (data), so now just need to process remaining dependencies
await this.applyMutationToFirstJsonPointerOfPlan(changedJsonPointers, data, op);
dependencies = changedJsonPointers.slice(1); //we have processes=d the entry point which can receive a mutation (data), so now just need to process remaining dependencies
}
await this.executeDependentExpressions(dependencies);
await this.executeDataChangeCallbacks(plan);
await this.executeDataChangeCallbacks(changedJsonPointers, op);
}

private async executeDependentExpressions(dependencies: JsonPointerString[]) {
Expand All @@ -989,18 +989,20 @@ export default class TemplateProcessor {
}
}

private async executeDataChangeCallbacks(plan: JsonPointerString[]) {
private async executeDataChangeCallbacks(changedJsonPointers: JsonPointerString[], op:"set"|"setDeferred"|"delete"="set") {
let anyUpdates = false;
const thoseThatUpdated = plan.filter(jptr => {
const thoseThatUpdated = changedJsonPointers.filter(jptr => {
const meta = jp.get(this.templateMeta, jptr);
anyUpdates ||= meta.didUpdate__;
return meta.didUpdate__
});
if (anyUpdates) {
// current callback APIs are not interested in deferred updates, so we reduce op to boolean "removed"
const removed = op==="delete";
//admittedly this structure of this common callback is disgusting. Essentially if you are using the
//common callback you don't want to get passed any data that changed because you are saying in essence
//"I don't care what changed".
await this.callDataChangeCallbacks(this.output, plan);
await this.callDataChangeCallbacks(this.output, changedJsonPointers, removed);
}
}

Expand Down Expand Up @@ -1117,7 +1119,12 @@ export default class TemplateProcessor {
return didSet; //true means that the data was new/fresh/changed and that subsequent updates must be propagated
}

private async setUntrackedLocation(output, jsonPtr, data) {
private async setUntrackedLocation(output, jsonPtr, data, op:"set" |"setDeferred"| "delete"="set") {
if(op==="delete"){
if(!jp.has(this.output, jsonPtr)){
return; // we are being asked to remove something that isn't here
}
}
jp.set(output, jsonPtr, data); //this is just the weird case of setting something into the template that has no effect on any expressions
jp.set(this.templateMeta, jsonPtr, {
"materialized__": true,
Expand Down Expand Up @@ -1193,6 +1200,7 @@ export default class TemplateProcessor {
if(op === 'delete'){
if(jp.has(output, jsonPtr)) {
jp.remove(output, jsonPtr);
this.callDataChangeCallbacks(data, jsonPtr, true);
return true;
}
return false;
Expand All @@ -1204,7 +1212,6 @@ export default class TemplateProcessor {
if (!isEqual(existingData, data)) {
jp.set(output, jsonPtr, data);
this.callDataChangeCallbacks(data, jsonPtr, false);
//this.commonCallback && this.commonCallback(data, jsonPtr); //called if callback set on "/"
return true;
} else {
this.logger.verbose(`data to be set at ${jsonPtr} did not change, ignored. `);
Expand Down
26 changes: 15 additions & 11 deletions src/test/TemplateProcessor.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import DependencyFinder from "../../dist/src/DependencyFinder.js";
import jsonata from "jsonata";
import { default as jp } from "../../dist/src/JsonPointer.js";
import StatedREPL from "../../dist/src/StatedREPL.js";
import {expect} from "@jest/globals";


test("test 1", async () => {
Expand Down Expand Up @@ -2089,14 +2090,17 @@ test("data change on array append (/foo/-)", async () => {
expect(cbCount1).toBe(1);
});












test("dataChangeCallback on delete op", async () => {
const tp = new TemplateProcessor({"foo": "bar"});
let done;
let latch = new Promise(resolve => done = resolve);
tp.setDataChangeCallback('/foo', (data, jsonPtr, removed)=>{
if(removed){
done();
}
});
await tp.initialize();
tp.setData("/foo", undefined, "delete");
await latch;
expect(tp.output.foo).toBeUndefined();
})

0 comments on commit 6aa5fd5

Please sign in to comment.