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

MILAB-1231: add waiter template #563

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
5 changes: 5 additions & 0 deletions .changeset/giant-onions-kneel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@platforma-sdk/workflow-tengo': minor
---

Add 'waiter' template with common 'Ready' event sync logic
2 changes: 1 addition & 1 deletion sdk/workflow-tengo/src/tpl/index.lib.tengo
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ tpl := func() {
},

/**
* In ephemeral template, body will only be executed when corresponding input staet is
* In ephemeral template, body will only be executed when corresponding inputs state is
* reached.
*
* Signature:
Expand Down
59 changes: 59 additions & 0 deletions sdk/workflow-tengo/src/waiter/index.lib.tengo
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
fmt := import("fmt")
ll := import(":ll")
render := import(":render")
validation := import(":validation")
assets := import(":assets")

waiterTpl := assets.importTemplate(":waiter.waiter")

/**
* Create new waiter, that will wait for all resources given to wait() and pass <pass> to <output> afterwards.
*
* @param pass: smart.field | smart.resource - reference to something that should be passed to the output
* once all waits get ready.
*/
builder := func(pass) {
self := undefined

/** type: smart.reference[] */
whatToWait := []

self = ll.toStrict({
/**
* Wait for given reference to become ready.
*
* @param ref: smart.field | smart.resource - reference to resource to wait for ResourceReady
* state before passing <pass> to the output of waiter
*/
wait: func(ref) {
validation.assertJsonSchema(ref, validation.reference, "waiter.builder.wait(ref): ref should be a reference to resource or field")
whatToWait = append(whatToWait, ref)

return self
},

build: func() {
waits := {}
for i, v in whatToWait {
waits[fmt.sprintf("input_%d", i)] = v
}

tpl := render.createEphemeral(waiterTpl, {
"pass": pass,
"trigger": waits
})

return ll.toStrict({
output: func() {
return tpl.output("output")
}
})
}
})

return self
}

export ll.toStrict({
builder: builder
})
12 changes: 12 additions & 0 deletions sdk/workflow-tengo/src/waiter/waiter.tpl.tengo
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
self := import(":tpl")

self.defineOutputs("output")

self.awaitState("AllInputsSet")
self.awaitState("trigger", "ResourceReady")

self.body(func(inputs) {
return {
"output": inputs.pass
}
})
105 changes: 105 additions & 0 deletions tests/workflow-tengo/src/waiter/waiter.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import type {
AnyFieldRef,
ResourceId } from '@milaboratories/pl-middle-layer';
import {
Pl,
field,
resourceType,
toGlobalResourceId,
} from '@milaboratories/pl-middle-layer';
import { tplTest } from '@platforma-sdk/test';
import { Templates } from '../..';

tplTest('test waiter 1', async ({ pl, helper, expect }) => {
let waitResource: ResourceId = 0n as ResourceId; // hack
let passResource: ResourceId = 0n as ResourceId; // hack
const result = await helper.renderTemplate(
true,
Templates['waiter.waiter'],
['output'],
async (tx) => {
passResource = await toGlobalResourceId(
// We need to be sure any resource is transitioned to the output.
// It is not necessary to be ready.
tx.createStruct(resourceType('TestPass', '1')),
);

waitResource = await toGlobalResourceId(
tx.createStruct(resourceType('TestWait', '1')),
);

return {
pass: passResource,
wait: waitResource,
};
},
);
const outputResult = result.computeOutput('output', (a) => a?.id);
await outputResult.refreshState();
expect(await outputResult.getValue()).toBeUndefined();

await pl.withWriteTx('CheckWaiter', async (tx) => {
tx.createField(field(waitResource, 'nestedField'), 'Input');
await tx.commit();
});
await outputResult.refreshState();
expect(await outputResult.getValue()).toBeUndefined();

await pl.withWriteTx('CheckWaiter', async (tx) => {
tx.lockInputs(waitResource);
await tx.commit();
});
await outputResult.refreshState();
expect(await outputResult.getValue()).toBeUndefined();

await pl.withWriteTx('Test', async (tx) => {
tx.setField(
field(waitResource, 'nestedField'),
tx.createValue(Pl.JsonObject, '{}'),
);
await tx.commit();
});
await outputResult.refreshState();
expect(await outputResult.awaitStableValue()).eq(passResource);
});

tplTest('test waiter 2', async ({ pl, helper, expect }) => {
let passField: AnyFieldRef;
const result = await helper.renderTemplate(
true,
Templates['waiter.waiter'],
['output'],
async (tx) => {
const waitResource = await toGlobalResourceId(
tx.createStruct(resourceType('TestWait', '1')),
);
tx.lockInputs(waitResource);

passField = field(waitResource, 'pass');
tx.createField(passField, 'OTW');
tx.lockOutputs(waitResource);

return {
pass: passField,
wait: waitResource,
};
},
);
const outputResult = result.computeOutput('output', (a) => a?.id);
await outputResult.refreshState();
expect(await outputResult.getValue()).toBeUndefined();

let passResource: ResourceId = 0n as ResourceId; // hack

await pl.withWriteTx('CreatePass', async (tx) => {
passResource = await toGlobalResourceId(
tx.createEphemeral(resourceType('TestWait', '1')),
);

tx.setField(passField, passResource);
await tx.commit();
});

await outputResult.refreshState();
expect(await outputResult.awaitStableValue()).eq(passResource);
});
16 changes: 16 additions & 0 deletions tests/workflow-tengo/src/waiter/waiter.tpl.tengo
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// simple1

self := import("@platforma-sdk/workflow-tengo:tpl")
waiter := import("@platforma-sdk/workflow-tengo:waiter")

self.defineOutputs(["output"])

self.body(func(inputs) {

w := waiter.builder(inputs.pass)
w.wait(inputs.wait)

return {
output: w.build().output()
}
})