Skip to content

Commit

Permalink
Log invalid path syntax due to unmatched brackets
Browse files Browse the repository at this point in the history
  • Loading branch information
cmoesel committed Dec 21, 2024
1 parent 4ef1896 commit bc41246
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 3 deletions.
20 changes: 17 additions & 3 deletions src/utils/PathUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ import { flatten } from 'lodash';
import { InstanceDefinition, PathPart } from '../fhirtypes';
import { splitOnPathPeriods } from '../fhirtypes/common';
import { CaretValueRule, Rule } from '../fshtypes/rules';
import { SourceInfo } from '../fshtypes/FshEntity';
import { logger } from './FSHLogger';

/**
* Parses a FSH Path into a more easily usable form
* @param {string} fshPath - A syntactically valid path in FSH
* @returns {PathPart[]} an array of PathParts that is easier to work with
*/
export function parseFSHPath(fshPath: string): PathPart[] {
export function parseFSHPath(fshPath: string, sourceInfo?: SourceInfo): PathPart[] {
const pathParts: PathPart[] = [];
const seenSlices: string[] = [];
const indexRegex = /^[0-9]+$/;
Expand All @@ -30,6 +31,19 @@ export function parseFSHPath(fshPath: string): PathPart[] {
if (seenSlices.length > 0) {
parsedPart.slices = [...seenSlices];
}
const parsedPartLength =
parsedPart.base.length +
parsedPart.brackets
.map(b => b.length + 2)
.reduce((total: number, current: number) => total + current, 0);
if (pathPart.length !== parsedPartLength) {
const message = `Error processing path due to unmatched brackets: ${fshPath}. `;
if (sourceInfo) {
logger.error(message, sourceInfo);
} else {
logger.error(message);
}
}
}
pathParts.push(parsedPart);
}
Expand Down Expand Up @@ -196,11 +210,11 @@ export function resolveSoftIndexing(rules: Array<Rule | CaretValueRule>, strict
// Parsing and separating rules by base name and bracket indexes
const parsedRules = rules.map(rule => {
const parsedPath: { path: PathPart[]; caretPath?: PathPart[] } = {
path: parseFSHPath(rule.path)
path: parseFSHPath(rule.path, rule.sourceInfo)
};
// If we have a CaretValueRule, we'll need a second round of parsing for the caret path
if (rule instanceof CaretValueRule) {
parsedPath.caretPath = parseFSHPath(rule.caretPath);
parsedPath.caretPath = parseFSHPath(rule.caretPath, rule.sourceInfo);
}
return parsedPath;
});
Expand Down
29 changes: 29 additions & 0 deletions test/utils/PathUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ describe('PathUtils', () => {
const testPath = 'item1.item2.item3';
const pathParts = parseFSHPath(testPath);

expect(loggerSpy.getAllLogs('error')).toBeEmpty();
expect(pathParts).toHaveLength(3);
expect(pathParts[0]).toEqual({ base: 'item1' });
expect(pathParts[1]).toEqual({ base: 'item2' });
Expand All @@ -329,6 +330,7 @@ describe('PathUtils', () => {
const testPath = 'item1[0].item2[0].item3[0]';
const pathParts = parseFSHPath(testPath);

expect(loggerSpy.getAllLogs('error')).toBeEmpty();
expect(pathParts).toHaveLength(3);
expect(pathParts[0]).toEqual({ base: 'item1', brackets: ['0'] });
expect(pathParts[1]).toEqual({ base: 'item2', brackets: ['0'] });
Expand All @@ -339,6 +341,7 @@ describe('PathUtils', () => {
const testPath = 'item1[10].item2[11].item3[12]';
const pathParts = parseFSHPath(testPath);

expect(loggerSpy.getAllLogs('error')).toBeEmpty();
expect(pathParts).toHaveLength(3);
expect(pathParts[0]).toEqual({ base: 'item1', brackets: ['10'] });
expect(pathParts[1]).toEqual({ base: 'item2', brackets: ['11'] });
Expand All @@ -349,6 +352,7 @@ describe('PathUtils', () => {
const testPath = 'item1[10][Slice1].item2[11][Slice2].item3[12][Slice3]';
const pathParts = parseFSHPath(testPath);

expect(loggerSpy.getAllLogs('error')).toBeEmpty();
expect(pathParts).toHaveLength(3);
expect(pathParts[0]).toEqual({
base: 'item1',
Expand All @@ -371,6 +375,7 @@ describe('PathUtils', () => {
const testPath = 'item1[10][foo[x]].item2[11][bar[x]][baz[x]].value[x]';
const pathParts = parseFSHPath(testPath);

expect(loggerSpy.getAllLogs('error')).toBeEmpty();
expect(pathParts).toHaveLength(3);
expect(pathParts[0]).toEqual({
base: 'item1',
Expand All @@ -391,13 +396,37 @@ describe('PathUtils', () => {
const testPath = 'value[x][foo]';
const pathParts = parseFSHPath(testPath);

expect(loggerSpy.getAllLogs('error')).toBeEmpty();
expect(pathParts).toHaveLength(1);
expect(pathParts[0]).toEqual({
base: 'value[x]',
brackets: ['foo'],
slices: ['foo']
});
});

it('should properly detect syntax errors in path elements (drawn from real world example)', () => {
const testPath = 'item1[+}.value[x]';
const sourceInfo = {
file: 'testfile.fsh',
location: { startLine: 5, startColumn: 0, endLine: 5, endColumn: 19 }
};
const pathParts = parseFSHPath(testPath, sourceInfo);

expect(loggerSpy.getLastMessage('error')).toMatch(
'Error processing path due to unmatched brackets: item1[+}.value[x]'
);
expect(loggerSpy.getLastMessage('error')).toMatch(/File: testfile\.fsh.*Line: 5\D*/s);

expect(pathParts).toHaveLength(2);
expect(pathParts[0]).toEqual({
base: 'item1',
brackets: []
});
expect(pathParts[1]).toEqual({
base: 'value[x]'
});
});
});

describe('#collectValuesAtElementIdOrPath', () => {
Expand Down

0 comments on commit bc41246

Please sign in to comment.