Skip to content

Commit

Permalink
Merge pull request #22 from dyne/sf/before-plugin
Browse files Browse the repository at this point in the history
Sf/before plugin
  • Loading branch information
denizenging authored Sep 19, 2023
2 parents 7680919 + ea901ac commit 0470734
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 14 deletions.
12 changes: 9 additions & 3 deletions pkg/core/src/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ import type { ZenroomParams, JsonableObject } from '@slangroom/shared';
* place.
*
* The plugin is defined using a single parameter which is a callback,
* named [execute], which takes in the necessary parameters from [BeforeParams].
* named [execute], which takes in the necessary parameters from [BeforeParams]
* and optionally returns a [ZenroomParams].
*/
export class BeforePlugin {
constructor(readonly execute: (params: BeforeParams) => Promise<void> | void) {}
constructor(
readonly execute: (params: BeforeParams
) => Promise<void> | void | Promise<JsonableObject> | JsonableObject
) { }
}

/**
Expand All @@ -19,7 +23,9 @@ export class BeforePlugin {
* named [execute], which takes in the necessary parameters from [AfterParams].
*/
export class AfterPlugin {
constructor(readonly execute: (params: AfterParams) => Promise<void> | void) {}
constructor(readonly execute: (params: AfterParams
) => Promise<void> | void | Promise<JsonableObject> | JsonableObject
) { }
}

/**
Expand Down
19 changes: 11 additions & 8 deletions pkg/core/src/slangroom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,26 +56,29 @@ export class Slangroom {
*/
async execute(contract: string, params?: ZenroomParams): Promise<ZenroomOutput> {
const ignoreds = await getIgnoredStatements(contract, params);
params = params || {data: {}}

// TODO: remove the statements when they match (decide how)
for (const b of this._beforeExecution) {
for (const ignored of ignoreds) {
await b.execute({
for (const ignored of ignoreds) {
for (const b of this._beforeExecution) {
const res = await b.execute({
statement: ignored,
params: params,
});
})
params.data = Object.assign(params.data || {}, res)
}
}

const zout = await zencodeExec(contract, params);

for (const a of this._afterExecution) {
for (const ignored of ignoreds) {
await a.execute({
for (const ignored of ignoreds) {
for (const a of this._afterExecution) {
const res = await a.execute({
statement: ignored,
result: zout.result,
params: params,
});
})
zout.result = Object.assign(zout.result || {}, res)
}
}

Expand Down
76 changes: 76 additions & 0 deletions pkg/core/test/slangroom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,79 @@ Then this statement does not exist
t.true(hasBeforeRan);
t.true(hasAfterRan);
});

test('before-plugins can inject parameters', async (t) => {
const before = new BeforePlugin(() => {
console.log("foobarbra")
return {foo: 'bar' };
});
const slang = new Slangroom(before);
const contract = `Rule unknown ignore
Given I have a 'string' named 'foo'
When I need an ignored statement
Then I print 'foo'
`;
const zout = await slang.execute(contract);
t.is(zout.result['foo'] as string, 'bar');
});

test('after-plugins can return values', async (t) => {
const before = new AfterPlugin(() => {
return { foo: "bar" };
});
const slang = new Slangroom(before);
const contract = `Rule unknown ignore
Given nothing
Then done
Then I need an ignored statement
`;
const zout = await slang.execute(contract);
t.is(zout.result['foo'] as string, 'bar');
});

test('check statements order', async (t) => {
const beforeA = new BeforePlugin((ctx) => {
if(!ctx.params?.data) return
if(ctx.statement == "Given A") {
t.is(ctx.params?.data['state'], "BEGIN")
return {state: "A"}
}
return
});
const beforeB = new BeforePlugin((ctx) => {
if(!ctx.params?.data) return
if(ctx.statement == "Given B") {
t.is(ctx.params?.data['state'], "A")
return {state: "B"}
}
return
});
const afterC = new AfterPlugin((ctx) => {
if(!ctx.params?.data) return
if(ctx.statement == "Then C") {
t.is(ctx.result['state'], "B")
return {state: "C"}
}
return
});
const afterD = new AfterPlugin((ctx) => {
if(!ctx.params?.data) return
if(ctx.statement == "Then D") {
t.is(ctx.result['state'], "C")
return {state: "D"}
}
return
});
const slang = new Slangroom(beforeB, beforeA, afterD, afterC);
const contract = `Rule unknown ignore
Given A
Given B
Given I have a 'string' named 'state'
Then print the 'state'
Then C
Then D
`;
const zout = await slang.execute(contract, { data: { state: "BEGIN" } });
t.is(zout.result['state'] as string, 'D');
});
21 changes: 18 additions & 3 deletions pkg/ignored/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,24 @@ export const getIgnoredStatements = async (
contract: string,
params?: ZenroomParams
): Promise<string[]> => {
// TODO: the zencodeExec() call could potentially be optimized, as
// zencodeExec() parses the output result. Keep in mind: optimization bad.
const { logs } = await zencodeExec(contract, params);
// Since we want to get the list of ignored statements, we don't want to
// throw if Zenroom execution fails (but we do fail if something other than
// that happens). When Zenroom fails, the ZenroomError type's message
// contains the logs.
let logs: string;
try {
// TODO: the zencodeExec() call could potentially be optimized, as
// zencodeExec() parses the output result. Keep in mind: optimization bad.
const zout = await zencodeExec(contract, params);
logs = zout.logs;
} catch (e) {
// Currently, only ZenroomError is available.
// Normally, I'd let this code be, but we're trying to achieve 100%
// coverage, so my "future-proof" code needs to be commented out here.
// if (!(e instanceof ZenroomError))
// throw e;
logs = e.message;
}
const lexed = IgnoredLexer.tokenize(logs);
return lexed.tokens.map((s) => s.image);
};

0 comments on commit 0470734

Please sign in to comment.