Skip to content

Commit

Permalink
Merge pull request #15 from bcgov/feature/multi-scenario
Browse files Browse the repository at this point in the history
Feature/multi scenario
  • Loading branch information
brysonjbest authored Jul 4, 2024
2 parents d47f18b + 7e9676f commit a7d6a7d
Show file tree
Hide file tree
Showing 16 changed files with 1,779 additions and 34 deletions.
57 changes: 50 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,13 @@
"@nestjs/config": "^3.2.0",
"@nestjs/core": "^10.0.0",
"@nestjs/mongoose": "^10.0.5",
"@nestjs/platform-express": "^10.3.7",
"@nestjs/platform-express": "^10.3.9",
"axios": "^1.6.8",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"csv-parser": "^3.0.0",
"mongoose": "^8.3.0",
"multer": "^1.4.5-lts.1",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1"
},
Expand All @@ -40,6 +42,7 @@
"@nestjs/testing": "^10.0.0",
"@types/express": "^4.17.17",
"@types/jest": "^29.5.2",
"@types/multer": "^1.4.11",
"@types/node": "^20.3.1",
"@types/supertest": "^6.0.0",
"@typescript-eslint/eslint-plugin": "^6.0.0",
Expand Down
2 changes: 1 addition & 1 deletion src/api/ruleMapping/ruleMapping.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ describe('RuleMappingController', () => {
it('should return the evaluated rule map', async () => {
const nodes = [{ id: '1', type: 'someType', content: { inputs: [], outputs: [] } }];
const edges = [{ id: '2', type: 'someType', targetId: '1', sourceId: '1' }];
const result = { inputs: [], outputs: [], finalOutputs: [] };
const result = { inputs: [], outputs: [], resultOutputs: [] };
jest.spyOn(service, 'ruleSchema').mockReturnValue(result);

const dto: EvaluateRuleMappingDto = { nodes, edges };
Expand Down
22 changes: 11 additions & 11 deletions src/api/ruleMapping/ruleMapping.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ describe('RuleMappingService', () => {
});
});

describe('extractfinalOutputs', () => {
it('should extract final outputs correctly when there is one output node and corresponding edges', () => {
describe('extractResultOutputs', () => {
it('should extract result outputs correctly when there is one output node and corresponding edges', () => {
const nodes: Node[] = [
{
id: '1',
Expand Down Expand Up @@ -129,9 +129,9 @@ describe('RuleMappingService', () => {
],
});

const result = service.extractfinalOutputs(nodes, edges);
const result = service.extractResultOutputs(nodes, edges);
expect(result).toEqual({
finalOutputs: [
resultOutputs: [
{ id: '1', name: 'Output1', type: 'string', property: 'field2' },
{ id: '2', name: 'Output2', type: 'number', property: 'field3' },
],
Expand All @@ -151,7 +151,7 @@ describe('RuleMappingService', () => {

const edges: Edge[] = [{ id: '1', type: 'someType', sourceId: '1', targetId: '2' }];

expect(() => service.extractfinalOutputs(nodes, edges)).toThrow('No outputNode found in the nodes array');
expect(() => service.extractResultOutputs(nodes, edges)).toThrow('No outputNode found in the nodes array');
});

it('should return an empty array if no target edges are found for the output node', () => {
Expand All @@ -178,9 +178,9 @@ describe('RuleMappingService', () => {
outputs: [],
});

const result = service.extractfinalOutputs(nodes, edges);
const result = service.extractResultOutputs(nodes, edges);
expect(result).toEqual({
finalOutputs: [],
resultOutputs: [],
});
});

Expand Down Expand Up @@ -221,9 +221,9 @@ describe('RuleMappingService', () => {
],
});

const result = service.extractfinalOutputs(nodes, edges);
const result = service.extractResultOutputs(nodes, edges);
expect(result).toEqual({
finalOutputs: [
resultOutputs: [
{ id: '1', name: 'Output1', type: 'string', property: 'field2' },
{ id: '2', name: 'Output2', type: 'number', property: 'field3' },
],
Expand Down Expand Up @@ -367,7 +367,7 @@ describe('RuleMappingService', () => {
{ id: '1', name: 'Output1', type: 'string', property: 'field2' },
{ id: '2', name: 'Output2', type: 'number', property: 'field3' },
],
finalOutputs: [],
resultOutputs: [],
});
});
});
Expand Down Expand Up @@ -524,7 +524,7 @@ describe('RuleMappingService', () => {

expect(mockGetFileContent).toHaveBeenCalledWith(filePath);
expect(result).toEqual({
finalOutputs: [],
resultOutputs: [],
inputs: [{ id: '1', name: 'Input1', type: 'string', property: 'field1' }],
outputs: [
{ id: '3', name: 'Output1', type: 'string', property: 'field2' },
Expand Down
35 changes: 23 additions & 12 deletions src/api/ruleMapping/ruleMapping.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,18 @@ export class RuleMappingService {
key: fieldKey === 'inputs' ? expr.key : expr.value,
property: fieldKey === 'inputs' ? expr.value : expr.key,
}));
} else if (node.type === 'functionNode' && node?.content) {
return (node.content.split('\n') || []).reduce((acc: any, line: string) => {
const match = line.match(fieldKey === 'inputs' ? /\s*\*\s*@param\s+/ : /\s*\*\s*@returns\s+/);
if (match) {
const item = line.replace(match[0], '').trim();
acc.push({
key: item,
property: item,
});
}
return acc;
}, []);
} else {
return (node.content?.[fieldKey] || []).map((field: Field) => ({
id: field.id,
Expand All @@ -30,16 +42,15 @@ export class RuleMappingService {
}));
}
});

return { [fieldKey]: fields };
}

// Get the final outputs of a rule from mapping the target output nodes and the edges
extractfinalOutputs(
extractResultOutputs(
nodes: Node[],
edges: Edge[],
): {
finalOutputs: any[];
resultOutputs: any[];
} {
// Find the output node
const outputNode = nodes.find((obj) => obj.type === 'outputNode');
Expand All @@ -53,9 +64,9 @@ export class RuleMappingService {
// Find the edges that connect the output node to other nodes
const targetEdges = edges.filter((edge) => edge.targetId === outputNodeID);
const targetOutputNodes = targetEdges.map((edge) => nodes.find((node) => node.id === edge.sourceId));
const finalOutputs: any[] = this.extractFields(targetOutputNodes, 'outputs').outputs;
const resultOutputs: any[] = this.extractFields(targetOutputNodes, 'outputs').outputs;

return { finalOutputs };
return { resultOutputs };
}

extractInputsAndOutputs(nodes: Node[]): {
Expand Down Expand Up @@ -98,23 +109,23 @@ export class RuleMappingService {
): {
inputs: any[];
outputs: any[];
finalOutputs: any[];
resultOutputs: any[];
} {
const inputs: any[] = this.extractUniqueInputs(nodes).uniqueInputs;
const generalOutputs: any[] = this.extractFields(nodes, 'outputs').outputs;
const finalOutputs: any[] = this.extractfinalOutputs(nodes, edges).finalOutputs;
const resultOutputs: any[] = this.extractResultOutputs(nodes, edges).resultOutputs;

//get unique outputs excluding final outputs
const outputs: any[] = generalOutputs.filter(
(output) =>
!finalOutputs.some(
(finalOutput) =>
finalOutput.id === output.id ||
(finalOutput.key === output.key && finalOutput.property === output.property),
!resultOutputs.some(
(resultOutput) =>
resultOutput.id === output.id ||
(resultOutput.key === output.key && resultOutput.property === output.property),
),
);

return { inputs, outputs, finalOutputs };
return { inputs, outputs, resultOutputs };
}

// generate a schema for the inputs and outputs of a rule given the trace data of a rule run
Expand Down
32 changes: 32 additions & 0 deletions src/api/scenarioData/dto/create-scenario.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { IsNotEmpty, IsString, ValidateNested } from 'class-validator';
import { Type } from 'class-transformer';
import { Variable } from '../scenarioData.schema';

export class VariableClass implements Variable {
name: string;
value: any;
type: string;
}

export class CreateScenarioDto {
@IsNotEmpty()
@IsString()
title: string;

@IsNotEmpty()
@IsString()
ruleID: string;

@IsNotEmpty()
@ValidateNested({ each: true })
@Type(() => VariableClass)
variables: VariableClass[];

@ValidateNested({ each: true })
@Type(() => VariableClass)
expectedResults: VariableClass[];

@IsNotEmpty()
@IsString()
goRulesJSONFilename: string;
}
Loading

0 comments on commit a7d6a7d

Please sign in to comment.