Skip to content
Merged
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
130 changes: 64 additions & 66 deletions src/TemplateArchiveProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export type InitResponse = {
*/
export class TemplateArchiveProcessor {
template: Template;
private compiledLogicCache?: Record<string, TwoSlashReturn>;

/**
* Creates a template archive processor
Expand Down Expand Up @@ -82,22 +83,23 @@ export class TemplateArchiveProcessor {

}


/**
* Trigger the logic of a template
* @param {object} request - the request to send to the template logic
* @param {object} state - the current state of the template
* @param {[string]} currentTime - the current time, defaults to now
* @param {[number]} utcOffset - the UTC offset, defaults to zero
* @returns {Promise} the response and any events
* Compile the logic of a template
* @param {boolean} [enableCompiledLogicCache] - whether to cache the compiled logic for future use
* @returns {Promise<Record<string, TwoSlashReturn>>} the compiled code for each typescript file
*/
async trigger(data: any, request: any, state?: any, currentTime?: string, utcOffset?: number): Promise<TriggerResponse> {
async compileLogic(enableCompiledLogicCache: boolean = false): Promise<Record<string, TwoSlashReturn>> {
if (enableCompiledLogicCache && this.compiledLogicCache) {
return this.compiledLogicCache;
}

const logicManager = this.template.getLogicManager();
if(logicManager.getLanguage() === 'typescript') {
const compiledCode:Record<string, TwoSlashReturn> = {};
const tsFiles:Array<Script> = logicManager.getScriptManager().getScriptsForTarget('typescript');
for(let n=0; n < tsFiles.length; n++) {
const tsFile = tsFiles[n];
// console.log(`Compiling ${tsFile.getIdentifier()}`);

const compiler = new TypeScriptToJavaScriptCompiler(this.template.getModelManager(),
this.template.getTemplateModel().getFullyQualifiedName());
Expand All @@ -106,83 +108,79 @@ export class TemplateArchiveProcessor {

// add the runtime type definitions to all ts files??
const code = `${Buffer.from(SMART_LEGAL_CONTRACT_BASE64, 'base64').toString()}
${tsFile.getContents()}`
${tsFile.getContents()}`;

const result = compiler.compile(code);
compiledCode[tsFile.getIdentifier()] = result;
}
// console.log(compiledCode['logic/logic.ts'].code);
const resolvedTime = currentTime ?? new Date().toISOString();
const resolvedOffset = utcOffset ?? 0;
const evaluator = new JavaScriptEvaluator();
const evalResponse = await evaluator.evalDangerously( {
templateLogic: true,
verbose: false,
functionName: 'trigger',
code: compiledCode['logic/logic.ts'].code, // TODO DCS - how to find the code to run?
argumentNames: ['data', 'request', 'state'],
arguments: [data, request, state, resolvedTime, resolvedOffset]
});
if(evalResponse.result) {
return evalResponse.result;
}
else {
throw new Error('Trigger failed with message: ' + evalResponse.message);
if (enableCompiledLogicCache) {
this.compiledLogicCache = compiledCode;
}
return compiledCode;
}
else {
throw new Error('Only TypeScript is supported at this time');
}
}

/**
* Init the logic of a template
* Trigger the logic of a template.
* @param {object} data - the data for the template
* @param {object} request - the request to send to the template logic
* @param {object} state - the current state of the template
* @param {[string]} currentTime - the current time, defaults to now
* @param {[number]} utcOffset - the UTC offset, defaults to zero
* @returns {Promise<InitResponse>} the new state
* @param {boolean} [enableCompiledLogicCache] - whether to use the compiled logic cache
* @returns {Promise<TriggerResponse>} the response and any events
*/
async init(data: any, currentTime?: string, utcOffset?: number): Promise<InitResponse> {
const logicManager = this.template.getLogicManager();
if(logicManager.getLanguage() === 'typescript') {
const compiledCode:Record<string, TwoSlashReturn> = {};
const tsFiles:Array<Script> = logicManager.getScriptManager().getScriptsForTarget('typescript');
for(let n=0; n < tsFiles.length; n++) {
const tsFile = tsFiles[n];
// console.log(`Compiling ${tsFile.getIdentifier()}`);

const compiler = new TypeScriptToJavaScriptCompiler(this.template.getModelManager(),
this.template.getTemplateModel().getFullyQualifiedName());

await compiler.initialize();
async trigger(data: any, request: any, state?: any, currentTime?: string, utcOffset?: number, enableCompiledLogicCache?: boolean): Promise<TriggerResponse> {
const compiledCode = await this.compileLogic(enableCompiledLogicCache);
const resolvedTime = currentTime ?? new Date().toISOString();
const resolvedOffset = utcOffset ?? 0;
const evaluator = new JavaScriptEvaluator();
const evalResponse = await evaluator.evalDangerously( {
templateLogic: true,
verbose: false,
functionName: 'trigger',
code: compiledCode['logic/logic.ts'].code, // TODO DCS - how to find the code to run?
argumentNames: ['data', 'request', 'state'],
arguments: [data, request, state, resolvedTime, resolvedOffset]
});
Comment thread
23harshitkumar marked this conversation as resolved.
if(evalResponse.result) {
return evalResponse.result as TriggerResponse;
}
else {
throw new Error('Trigger failed with message: ' + evalResponse.message);
}
}

// add the runtime type definitions to all ts files??
const code = `${Buffer.from(SMART_LEGAL_CONTRACT_BASE64, 'base64').toString()}
${tsFile.getContents()}`

const result = compiler.compile(code);
compiledCode[tsFile.getIdentifier()] = result;
}
// console.log(compiledCode['logic/logic.ts'].code);
const resolvedTime = currentTime ?? new Date().toISOString();
const resolvedOffset = utcOffset ?? 0;
const evaluator = new JavaScriptEvaluator();
const evalResponse = await evaluator.evalDangerously( {
templateLogic: true,
verbose: false,
functionName: 'init',
code: compiledCode['logic/logic.ts'].code, // TODO DCS - how to find the code to run?
argumentNames: ['data'],
arguments: [data, resolvedTime, resolvedOffset]
});
if(evalResponse.result) {
return evalResponse.result;
}
else {
throw new Error('Init failed with message: ' + evalResponse.message);
}
/**
* Init the logic of a template.
* @param {object} data - the data for the template
* @param {[string]} currentTime - the current time, defaults to now
* @param {[number]} utcOffset - the UTC offset, defaults to zero
* @param {boolean} [enableCompiledLogicCache] - whether to use the compiled logic cache
* @returns {Promise<InitResponse>} the new state
*/
async init(data: any, currentTime?: string, utcOffset?: number, enableCompiledLogicCache?: boolean): Promise<InitResponse> {
const compiledCode = await this.compileLogic(enableCompiledLogicCache);
const resolvedTime = currentTime ?? new Date().toISOString();
const resolvedOffset = utcOffset ?? 0;
const evaluator = new JavaScriptEvaluator();
const evalResponse = await evaluator.evalDangerously( {
templateLogic: true,
verbose: false,
functionName: 'init',
code: compiledCode['logic/logic.ts'].code, // TODO DCS - how to find the code to run?
argumentNames: ['data'],
arguments: [data, resolvedTime, resolvedOffset]
});
Comment thread
23harshitkumar marked this conversation as resolved.
if(evalResponse.result) {
return evalResponse.result as InitResponse;
}
else {
throw new Error('Only TypeScript is supported at this time');
throw new Error('Init failed with message: ' + evalResponse.message);
}
}
}
28 changes: 18 additions & 10 deletions test/TemplateArchiveProcessor.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {Template} from '@accordproject/cicero-core';
import { TemplateArchiveProcessor } from '../src/TemplateArchiveProcessor';
import { TemplateArchiveProcessor, InitResponse, TriggerResponse } from '../src/TemplateArchiveProcessor';

describe('template archive processor', () => {
test('should draft a template', async () => {
Expand Down Expand Up @@ -51,12 +51,19 @@ describe('template archive processor', () => {
"clauseId": "c88e5ed7-c3e0-4249-a99c-ce9278684ac8",
"$identifier": "c88e5ed7-c3e0-4249-a99c-ce9278684ac8"
};
const response = await templateArchiveProcessor.init(data);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const payload:any = response.state;
const response: InitResponse = await templateArchiveProcessor.init(data);
const payload = response.state as { count?: number };
expect(payload.count).toBe(0);
});

test('should compile logic', async () => {
const template = await Template.fromDirectory('test/archives/latedeliveryandpenalty-typescript', {offline: true});
const templateArchiveProcessor = new TemplateArchiveProcessor(template);
const compiledCode = await templateArchiveProcessor.compileLogic();
expect(compiledCode['logic/logic.ts']).toBeDefined();
expect(compiledCode['logic/logic.ts'].code).toContain('LateDeliveryAndPenalty');
});

test('should trigger a template', async () => {
const template = await Template.fromDirectory('test/archives/latedeliveryandpenalty-typescript', {offline: true});
const templateArchiveProcessor = new TemplateArchiveProcessor(template);
Expand Down Expand Up @@ -87,17 +94,18 @@ describe('template archive processor', () => {
const stateResponse = await templateArchiveProcessor.init(data);

// then we trigger the template
const response = await templateArchiveProcessor.trigger(data, request, stateResponse.state);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const payload:any = response;
const response: TriggerResponse = await templateArchiveProcessor.trigger(data, request, stateResponse.state);

// we should have a result
expect(payload.result.penalty).toBe(2625);
const resultPayload = response.result as { penalty?: number };
expect(resultPayload.penalty).toBe(2625);

// the state should have been updated
expect(payload.state.count).toBe(1);
const statePayload = response.state as { count?: number };
expect(statePayload.count).toBe(1);

// the events should have been emitted
expect(payload.events[0].penaltyCalculated).toBe(true);
const eventPayload = response.events[0] as { penaltyCalculated?: boolean };
expect(eventPayload.penaltyCalculated).toBe(true);
});
});
Loading