Skip to content

Commit

Permalink
Merge pull request #37 from bcgov/migration/filepath-name
Browse files Browse the repository at this point in the history
Update all uses of 'goRulesJSONFilename' to 'filepath' and adds 'name'
  • Loading branch information
timwekkenbc authored Oct 7, 2024
2 parents b06d511 + 13897ff commit ae4ee11
Show file tree
Hide file tree
Showing 13 changed files with 154 additions and 94 deletions.
11 changes: 9 additions & 2 deletions src/api/ruleData/ruleData.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,14 @@ export class RuleData {
@Prop({ required: true, description: 'The GoRules ID' })
_id: string;

@Prop({ unique: true, description: 'A unique name currently derived from the filepath' })
name: string;

@Prop({ description: 'The title of the rule' })
title: string;

@Prop({ required: true, description: 'The filename of the JSON file containing the rule' })
goRulesJSONFilename: string;
@Prop({ required: true, description: 'The filepath of the JSON file containing the rule' })
filepath: string;

@Prop({ type: Types.ObjectId, description: 'Draft of updated rule content', ref: 'RuleDraft' })
ruleDraft?: RuleDraftDocument | Types.ObjectId;
Expand All @@ -21,6 +24,10 @@ export class RuleData {

@Prop({ description: 'If the rule has been published' })
isPublished?: boolean;

// TODO: REMOVE AFTER MIGRATIONS ALL COMPLETE
@Prop({ description: 'This is being deprecated' })
goRulesJSONFilename?: string;
}

export type RuleDataDocument = RuleData & Document;
Expand Down
14 changes: 9 additions & 5 deletions src/api/ruleData/ruleData.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ import axios from 'axios';

export const mockRuleData = {
_id: 'testId',
name: 'title',
title: 'Title',
goRulesJSONFilename: 'filename.json',
filepath: 'title.json',
};
const mockRuleDraft = { content: { nodes: [], edges: [] } };

Expand Down Expand Up @@ -98,20 +99,23 @@ describe('RuleDataService', () => {
it('should correctly remove inReview statuses when branches no longer exist', async () => {
const ruleWithBranchToRemove: RuleData = {
_id: 'testId1',
name: 'title1',
title: 'Title 1',
goRulesJSONFilename: 'filename1.json',
filepath: 'title1.json',
reviewBranch: 'myoldbranch',
};
const ruleWithBranchToKeep: RuleData = {
_id: 'testId2',
name: 'title2',
title: 'Title 2',
goRulesJSONFilename: 'filename2.json',
filepath: 'title2.json',
reviewBranch: 'branch2',
};
const ruleWithoutBranch: RuleData = {
_id: 'testId3',
name: 'title3',
title: 'Title 3',
goRulesJSONFilename: 'filename3.json',
filepath: 'path/title3.json',
};
const mockedbranches = { data: [{ name: 'branch1' }, { name: ruleWithBranchToKeep.reviewBranch }] };
jest.spyOn(axios, 'get').mockResolvedValue(mockedbranches);
Expand All @@ -137,7 +141,7 @@ describe('RuleDataService', () => {
});

it('should handle adding duplicate files gracefully', async () => {
const unsyncedFiles = ['file1.txt', mockRuleData.goRulesJSONFilename];
const unsyncedFiles = ['file1.txt', mockRuleData.filepath];
jest.spyOn(documentsService, 'getAllJSONFiles').mockResolvedValue(unsyncedFiles);
jest.spyOn(service, 'createRuleData').mockImplementation((rData: RuleData) => Promise.resolve(rData));
jest
Expand Down
13 changes: 9 additions & 4 deletions src/api/ruleData/ruleData.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { ObjectId } from 'mongodb';
import { Model } from 'mongoose';
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import axios from 'axios';
import { DocumentsService } from '../documents/documents.service';
import { RuleData, RuleDataDocument } from './ruleData.schema';
import { RuleDraft, RuleDraftDocument } from './ruleDraft.schema';
import axios from 'axios';
import { deriveNameFromFilepath } from '../../utils/helpers';

@Injectable()
export class RuleDataService {
Expand Down Expand Up @@ -70,6 +71,7 @@ export class RuleDataService {
const newRuleID = new ObjectId();
ruleData._id = newRuleID.toHexString();
}
ruleData.name = deriveNameFromFilepath(ruleData.filepath);
ruleData = await this._addOrUpdateDraft(ruleData);
const newRuleData = new this.ruleDataModel(ruleData);
const response = await newRuleData.save();
Expand All @@ -86,6 +88,9 @@ export class RuleDataService {
if (!existingRuleData) {
throw new Error('Rule data not found');
}
if (updatedData.filepath) {
updatedData.name = deriveNameFromFilepath(updatedData.filepath);
}
updatedData = await this._addOrUpdateDraft(updatedData);
Object.assign(existingRuleData, updatedData);
return await existingRuleData.save();
Expand Down Expand Up @@ -130,10 +135,10 @@ export class RuleDataService {
async addUnsyncedFiles(existingRules: RuleData[]) {
// Find rules not yet defined in db (but with an exisitng JSON file) and add them
const jsonRuleDocuments = await this.documentsService.getAllJSONFiles();
jsonRuleDocuments.forEach((goRulesJSONFilename: string) => {
const existingRule = existingRules.find((rule) => rule.goRulesJSONFilename === goRulesJSONFilename);
jsonRuleDocuments.forEach((filepath: string) => {
const existingRule = existingRules.find((rule) => rule.filepath === filepath);
if (!existingRule) {
this.createRuleData({ goRulesJSONFilename, isPublished: true });
this.createRuleData({ filepath, isPublished: true });
} else if (!existingRule.isPublished) {
// Update status to isPublished if it isn't yet
this.updateRuleData(existingRule._id, { isPublished: true });
Expand Down
4 changes: 2 additions & 2 deletions src/api/ruleMapping/ruleMapping.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ export class RuleMappingController {
// Map a rule file to its unique inputs, and all outputs
@Post('/')
async getRuleSchema(
@Body('goRulesJSONFilename') goRulesJSONFilename: string,
@Body('filepath') filepath: string,
@Body('ruleContent') ruleContent: EvaluateRuleMappingDto,
@Res() res: Response,
) {
const rulemap = await this.ruleMappingService.inputOutputSchema(ruleContent);

try {
res.setHeader('Content-Type', 'application/json');
res.setHeader('Content-Disposition', `attachment; filename=${goRulesJSONFilename}`);
res.setHeader('Content-Disposition', `attachment; filename=${filepath}`);
res.send(rulemap);
} catch (error) {
if (error instanceof InvalidRuleContent) {
Expand Down
2 changes: 1 addition & 1 deletion src/api/scenarioData/dto/create-scenario.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,5 @@ export class CreateScenarioDto {

@IsNotEmpty()
@IsString()
goRulesJSONFilename: string;
filepath: string;
}
26 changes: 13 additions & 13 deletions src/api/scenarioData/scenarioData.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ describe('ScenarioDataController', () => {
title: 'title',
ruleID: 'ruleID',
variables: [],
goRulesJSONFilename: 'filename',
filepath: 'filename',
expectedResults: [],
};

Expand All @@ -127,7 +127,7 @@ describe('ScenarioDataController', () => {
title: result.title,
ruleID: result.ruleID,
variables: variables,
goRulesJSONFilename: result.goRulesJSONFilename,
filepath: result.filepath,
expectedResults: expectedResults,
};

Expand All @@ -142,7 +142,7 @@ describe('ScenarioDataController', () => {
title: 'title',
ruleID: 'ruleID',
variables: [],
goRulesJSONFilename: 'filename',
filepath: 'filename',
expectedResults: [],
};

Expand All @@ -156,7 +156,7 @@ describe('ScenarioDataController', () => {
title: 'title',
ruleID: 'ruleID',
variables: [],
goRulesJSONFilename: 'filename',
filepath: 'filename',
expectedResults: [],
};

Expand All @@ -166,7 +166,7 @@ describe('ScenarioDataController', () => {
title: result.title,
ruleID: result.ruleID,
variables: [],
goRulesJSONFilename: result.goRulesJSONFilename,
filepath: result.filepath,
expectedResults: [],
};

Expand All @@ -181,7 +181,7 @@ describe('ScenarioDataController', () => {
title: 'title',
ruleID: 'ruleID',
variables: [],
goRulesJSONFilename: 'filename',
filepath: 'filename',
expectedResults: [],
};

Expand All @@ -205,7 +205,7 @@ describe('ScenarioDataController', () => {

describe('getCSVForRuleRun', () => {
it('should return CSV content with correct headers', async () => {
const goRulesJSONFilename = 'test.json';
const filepath = 'test.json';
const ruleContent = { nodes: [], edges: [] };
const csvContent = `Scenario,Input: familyComposition,Input: numberOfChildren,Output: isEligible,Output: baseAmount
Scenario 1,single,,true,
Expand All @@ -219,20 +219,20 @@ Scenario 2,couple,3,,200`;
setHeader: jest.fn(),
};

await controller.getCSVForRuleRun(goRulesJSONFilename, ruleContent, mockResponse as any);
await controller.getCSVForRuleRun(filepath, ruleContent, mockResponse as any);

expect(mockResponse.status).toHaveBeenCalledWith(HttpStatus.OK);
expect(mockResponse.setHeader).toHaveBeenCalledWith('Content-Type', 'text/csv');
expect(mockResponse.setHeader).toHaveBeenCalledWith(
'Content-Disposition',
`attachment; filename=${goRulesJSONFilename.replace(/\.json$/, '.csv')}`,
`attachment; filename=${filepath.replace(/\.json$/, '.csv')}`,
);
expect(mockResponse.send).toHaveBeenCalledWith(csvContent);
});

it('should throw an error if service fails', async () => {
const errorMessage = 'Error generating CSV for rule run';
const goRulesJSONFilename = 'test.json';
const filepath = 'test.json';
const ruleContent = { nodes: [], edges: [] };
jest.spyOn(service, 'getCSVForRuleRun').mockRejectedValue(new Error(errorMessage));

Expand All @@ -242,11 +242,11 @@ Scenario 2,couple,3,,200`;
};

await expect(async () => {
await controller.getCSVForRuleRun(goRulesJSONFilename, ruleContent, mockResponse as any);
await controller.getCSVForRuleRun(filepath, ruleContent, mockResponse as any);
}).rejects.toThrow(Error);

try {
await controller.getCSVForRuleRun(goRulesJSONFilename, ruleContent, mockResponse as any);
await controller.getCSVForRuleRun(filepath, ruleContent, mockResponse as any);
} catch (error) {
expect(error.message).toBe('Error generating CSV for rule run');
}
Expand Down Expand Up @@ -292,7 +292,7 @@ Scenario 2,couple,3,,200`;
title: 'Scenario 1',
ruleID: '',
variables: [{ name: 'Age', value: 25, type: 'number' }],
goRulesJSONFilename: 'test.json',
filepath: 'test.json',
},
];

Expand Down
24 changes: 12 additions & 12 deletions src/api/scenarioData/scenarioData.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,9 @@ export class ScenarioDataController {
}

@Post('/by-filename')
async getScenariosByFilename(@Body('goRulesJSONFilename') goRulesJSONFilename: string): Promise<ScenarioData[]> {
async getScenariosByFilename(@Body('filepath') filepath: string): Promise<ScenarioData[]> {
try {
return await this.scenarioDataService.getScenariosByFilename(goRulesJSONFilename);
return await this.scenarioDataService.getScenariosByFilename(filepath);
} catch (error) {
if (error instanceof FileNotFoundError) {
throw new HttpException('Rule not found', HttpStatus.NOT_FOUND);
Expand All @@ -75,7 +75,7 @@ export class ScenarioDataController {
title: createScenarioDto.title,
ruleID: createScenarioDto.ruleID,
variables: createScenarioDto.variables,
goRulesJSONFilename: createScenarioDto.goRulesJSONFilename,
filepath: createScenarioDto.filepath,
expectedResults: createScenarioDto.expectedResults,
};
return await this.scenarioDataService.createScenarioData(scenarioData);
Expand All @@ -94,7 +94,7 @@ export class ScenarioDataController {
title: updateScenarioDto.title,
ruleID: updateScenarioDto.ruleID,
variables: updateScenarioDto.variables,
goRulesJSONFilename: updateScenarioDto.goRulesJSONFilename,
filepath: updateScenarioDto.filepath,
expectedResults: updateScenarioDto.expectedResults,
};
return await this.scenarioDataService.updateScenarioData(scenarioId, scenarioData);
Expand All @@ -114,14 +114,14 @@ export class ScenarioDataController {

@Post('/evaluation')
async getCSVForRuleRun(
@Body('goRulesJSONFilename') goRulesJSONFilename: string,
@Body('filepath') filepath: string,
@Body('ruleContent') ruleContent: RuleContent,
@Res() res: Response,
) {
try {
const fileContent = await this.scenarioDataService.getCSVForRuleRun(goRulesJSONFilename, ruleContent);
const fileContent = await this.scenarioDataService.getCSVForRuleRun(filepath, ruleContent);
res.setHeader('Content-Type', 'text/csv');
res.setHeader('Content-Disposition', `attachment; filename=${goRulesJSONFilename.replace(/\.json$/, '.csv')}`);
res.setHeader('Content-Disposition', `attachment; filename=${filepath.replace(/\.json$/, '.csv')}`);
res.status(HttpStatus.OK).send(fileContent);
} catch (error) {
throw new HttpException('Error generating CSV for rule run', HttpStatus.INTERNAL_SERVER_ERROR);
Expand All @@ -130,11 +130,11 @@ export class ScenarioDataController {

@Post('/run-decisions')
async runDecisionsForScenarios(
@Body('goRulesJSONFilename') goRulesJSONFilename: string,
@Body('filepath') filepath: string,
@Body('ruleContent') ruleContent: RuleContent,
): Promise<{ [scenarioId: string]: any }> {
try {
return await this.scenarioDataService.runDecisionsForScenarios(goRulesJSONFilename, ruleContent);
return await this.scenarioDataService.runDecisionsForScenarios(filepath, ruleContent);
} catch (error) {
throw new HttpException('Error running scenario decisions', HttpStatus.INTERNAL_SERVER_ERROR);
}
Expand All @@ -145,16 +145,16 @@ export class ScenarioDataController {
async uploadCSVAndProcess(
@UploadedFile() file: Express.Multer.File | undefined,
@Res() res: Response,
@Body('goRulesJSONFilename') goRulesJSONFilename: string,
@Body('filepath') filepath: string,
@Body('ruleContent') ruleContent: RuleContent,
) {
if (!file) {
throw new HttpException('No file uploaded', HttpStatus.BAD_REQUEST);
}

try {
const scenarios = await this.scenarioDataService.processProvidedScenarios(goRulesJSONFilename, file);
const csvContent = await this.scenarioDataService.getCSVForRuleRun(goRulesJSONFilename, ruleContent, scenarios);
const scenarios = await this.scenarioDataService.processProvidedScenarios(filepath, file);
const csvContent = await this.scenarioDataService.getCSVForRuleRun(filepath, ruleContent, scenarios);
res.setHeader('Content-Type', 'text/csv');
res.setHeader('Content-Disposition', `attachment; filename=processed_data.csv`);
res.status(HttpStatus.OK).send(csvContent);
Expand Down
2 changes: 1 addition & 1 deletion src/api/scenarioData/scenarioData.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export class ScenarioData {
expectedResults: Variable[];

@Prop({ required: true, description: 'The filename of the JSON file containing the rule' })
goRulesJSONFilename: string;
filepath: string;
}

export const ScenarioDataSchema = SchemaFactory.createForClass(ScenarioData);
Loading

0 comments on commit ae4ee11

Please sign in to comment.