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
8 changes: 4 additions & 4 deletions src/condition/extract-tokens.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ const LOWER = Object.freeze({
});

const SYMBOLS = Object.freeze({
AT: 64,
COLON: 58,
DOLLAR: 36,
DOUBLE_QUOTE: 34,
PARENTHESIS_OPEN: 40,
PARENTHESIS_CLOSE: 41,
Expand All @@ -36,7 +36,7 @@ const GROUP_DELIMITERS = [
];

const FIELD_PATH_SYMBOLS = [
SYMBOLS.AT,
SYMBOLS.DOLLAR,
SYMBOLS.COLON,
SYMBOLS.DOT,
SYMBOLS.SQUARE_BRACKET_OPEN,
Expand All @@ -57,7 +57,7 @@ const isText = (code) => {
return true;
}
if (FIELD_PATH_SYMBOLS.includes(code)) {
// @._[:]
// $._[:]
return true;
}
return false;
Expand Down Expand Up @@ -155,4 +155,4 @@ const extractTokens = (conditionString) => {
.map((token) => token.value);
};

export default extractTokens;
export default extractTokens;
6 changes: 3 additions & 3 deletions src/condition/extract-tokens.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ test.each([
],
],
[
'Relative field path',
'@.level1.child1 = "Yes"',
'Absolute field path',
'$.level1.child1 = "Yes"',
[
'@.level1.child1', '=', '"Yes"',
'$.level1.child1', '=', '"Yes"',
],
],
[
Expand Down
2 changes: 1 addition & 1 deletion src/condition/parse-tokens.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {SyntaxError} from './errors.js';

const FIELD_PATH_PATTERN = /^(?:@\.)?[a-zA-Z0-9_]+(?:\[(?:id:[a-zA-Z0-9_]+|[0-9]+)])?(?:\.[a-zA-Z0-9_]+(?:\[(?:id:[a-zA-Z0-9_]+|[0-9]+)])?)*$/;
const FIELD_PATH_PATTERN = /^(?:\$\.)?[a-zA-Z0-9_]+(?:\[(?:id:[a-zA-Z0-9_]+|[0-9]+)])?(?:\.[a-zA-Z0-9_]+(?:\[(?:id:[a-zA-Z0-9_]+|[0-9]+)])?)*$/;

const xor = (a, b) => Boolean(a) !== Boolean(b);

Expand Down
8 changes: 4 additions & 4 deletions src/condition/parse-tokens.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,16 @@ test('should parse simple conjunction condition without grouping', () => {
});
});

test('should parse condition with relative field path', () => {
test('should parse condition with absolute field path', () => {
const tokens = [
'@.level1.child1', '===', '"value1"',
'$.level1.child1', '===', '"value1"',
];

expect(parseTokens(tokens)).toEqual({
condition: [
{path: '@.level1.child1', operator: 'EQUALS', value: 'value1'},
{path: '$.level1.child1', operator: 'EQUALS', value: 'value1'},
],
fieldPaths: ['@.level1.child1'],
fieldPaths: ['$.level1.child1'],
});
});

Expand Down
2 changes: 2 additions & 0 deletions src/path/constants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export const ABSOLUTE_START = '$.';
export const SEPARATOR = '.';
2 changes: 2 additions & 0 deletions src/path/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './constants.js';
export * from './predicates.js';
9 changes: 9 additions & 0 deletions src/path/predicates.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import isMetadata from '../metadata/is-metadata.js';
import {ABSOLUTE_START} from './constants.js';

export const isAbsolute = (path) => path.slice(0,2) === ABSOLUTE_START;

export {
// Reexport `isMetadata()` for convenience
isMetadata,
};
4 changes: 3 additions & 1 deletion src/record/extract-collection-item.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import {isAbsolute, isMetadata} from '../path/index.js';

/**
* Creates an instance of a relative value extractor for items of a collection field.
* This can be further combined with `extractMember` to extract members of complex collection items.
Expand All @@ -16,7 +18,7 @@ const resolvePath = (parentPath) => (path) => {
return `${parentPath}[${path}].value`;
}

if (path[0] === '[') {
if (isMetadata(path) || isAbsolute(path)) {
return path;
}

Expand Down
8 changes: 8 additions & 0 deletions src/record/extract-collection-item.test.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import extractCollectionMember from './extract-collection-member.js';
import extractValue from './extract-value.js';
import extractCollectionItem from './extract-collection-item.js';

const extractor = extractValue({
id: '1111222233334444',
data: {
'field0': 'Root value 0',
'collection1': [
{id: 'item-1', value: 'Value 1'},
{id: 'item-2', value: 'Value 2'},
Expand All @@ -30,6 +32,12 @@ describe('when single item path', () => {
extractCollectionItem(extractor, 'collection1')('[id]')
).toEqual('1111222233334444');
});

test('should extract absolute path from root', () => {
expect(
extractCollectionItem(extractor, 'collection1')('$.field0')
).toEqual('Root value 0');
});
});

describe('when array of item paths', () => {
Expand Down
4 changes: 3 additions & 1 deletion src/record/extract-collection-member.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import {isAbsolute, isMetadata} from '../path/index.js';

/**
* Creates an instance of a relative value extractor for members of a complex collection field.
*
Expand All @@ -11,7 +13,7 @@ const extractCollectionMember = (extract, collectionPath) => (item) => (path) =>
};

const resolvePath = (parentPath) => (item) => (path) => {
if (path[0] === '[') {
if (isMetadata(path) || isAbsolute(path)) {
return path;
}

Expand Down
6 changes: 6 additions & 0 deletions src/record/extract-collection-member.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import extractValue from './extract-value.js';
const extractor = extractValue({
id: '1111222233334444',
data: {
'field0': 'Root value 0',
'collection1': [
{
id: 'item-1',
Expand Down Expand Up @@ -45,6 +46,11 @@ describe('when single item path', () => {
const extract = extractCollectionMember(extractor, 'collection1')('item-2');
expect(extract('[id]')).toEqual('1111222233334444');
});

test('should extract absolute path from root', () => {
const extract = extractCollectionMember(extractor, 'collection1')('item-2');
expect(extract('$.field0')).toEqual('Root value 0');
});
});

describe('when array of item paths', () => {
Expand Down
6 changes: 4 additions & 2 deletions src/record/extract-member.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import * as Path from '../path/index.js';

/**
* Creates an instance of a relative value extractor for members of a complex field.
*
Expand All @@ -11,10 +13,10 @@ const extractMember = (extract, complexPath) => (path) => {
};

const resolvePath = (parentPath) => (path) => {
if (path[0] === '[') {
if (Path.isMetadata(path) || Path.isAbsolute(path)) {
return path;
}
return parentPath + '.' + path;
return parentPath + Path.SEPARATOR + path;
};

const processPath = (resolve) => (path) => {
Expand Down
7 changes: 7 additions & 0 deletions src/record/extract-member.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import extractMember from './extract-member.js';
const extractor = extractValue({
id: '1111222233334444',
data: {
'field0': 'Root value 0',
'complex1': {
'member1': 'Value 1',
'member2': 'Value 2',
Expand All @@ -24,6 +25,12 @@ describe('when single member path', () => {
extractMember(extractor, 'complex1')('[id]')
).toEqual('1111222233334444');
});

test('should extract absolute path from root', () => {
expect(
extractMember(extractor, 'complex1')('$.field0')
).toEqual('Root value 0');
});
});

describe('when array of member paths', () => {
Expand Down
9 changes: 6 additions & 3 deletions src/record/extract-value.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {isMetadata} from '../metadata/index.js';
import * as Metadata from '../metadata/index.js';
import * as Path from '../path/index.js';
import comboExtractor from '../path/combo-extractor.js';

const COLLECTION_ITEM_PATTERN = /^(?<name>[^[\]]+)(?:\[(?:(?<colIndex>\d+)|id:(?<colId>[^[\]]+))\])?$/;
Expand All @@ -17,12 +17,15 @@ const COLLECTION_ITEM_PATTERN = /^(?<name>[^[\]]+)(?:\[(?:(?<colIndex>\d+)|id:(?
const extractValue = (record) => comboExtractor(valueExtractor(record))

const valueExtractor = (record) => (path) => {
if (isMetadata(path)) {
if (Path.isMetadata(path)) {
return metadataExtractor(record)(path);
}

const caseData = dataExtractor(record);
return caseData ? field(caseData)(path.split('.').map(parsePathElement)) : undefined;
const parsedPath = path.replace(Path.ABSOLUTE_START, '')
.split(Path.SEPARATOR)
.map(parsePathElement);
return caseData ? field(caseData)(parsedPath) : undefined;
};

const metadataExtractor = (record) => (path) => {
Expand Down
13 changes: 13 additions & 0 deletions src/record/extract-value.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,19 @@ test('should extract field from record `case_data`', () => {
expect(fieldValue).toEqual('value');
});

test('should extract field using explicit absolute path', () => {
const record = {
data: {
level1: {
level2: 'value'
}
}
};

const fieldValue = extractValue(record)('$.level1.level2');
expect(fieldValue).toEqual('value');
});

test('should extract field as undefined when path does not exist', () => {
const record = {
data: {
Expand Down
1 change: 0 additions & 1 deletion src/template/template.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ const RecordContext = (extractor, context = new Mustache.Context()) => {
path = path.slice(0, -COERCE_BOOL_SUFFIX.length);
value = extractValue(path)?.toLowerCase() === 'yes';
} else {
console.log(extractValue);
value = extractValue(path);
}

Expand Down