Skip to content

Commit

Permalink
Add type guard filter + refactore restore regexp
Browse files Browse the repository at this point in the history
  • Loading branch information
thinkh committed Apr 3, 2020
1 parent a4bf429 commit 304e51f
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 10 deletions.
4 changes: 2 additions & 2 deletions src/lineup/internal/cmds.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {IObjectRef, action, meta, cat, op, ProvenanceGraph, ICmdResult} from 'ph
import {NumberColumn, LocalDataProvider, StackColumn, ScriptColumn, OrdinalColumn, CompositeColumn, Ranking, ISortCriteria, Column, isMapAbleColumn, mappingFunctions} from 'lineupjs';
import {resolveImmediately} from 'phovea_core/src';
import i18n from 'phovea_core/src/i18n';
import {isSerializedFilter, restoreLineUpFilter, serializeLineUpFilter, ILineUpStringFilter} from './cmds/filter';
import {isSerializedFilter, restoreLineUpFilter, serializeLineUpFilter, isLineUpStringFilter} from './cmds/filter';


// used for function calls in the context of tracking or untracking actions in the provenance graph in order to get a consistent defintion of the used strings
Expand Down Expand Up @@ -357,7 +357,7 @@ function recordPropertyChange(source: Column | Ranking, provider: LocalDataProvi
}

if (property === LineUpTrackAndUntrackActions.filter) {
newValue = isSerializedFilter(newValue) ? serializeLineUpFilter(newValue as ILineUpStringFilter) : newValue; // serialize possible RegExp object to be properly stored as provenance graph
newValue = isLineUpStringFilter(newValue) ? serializeLineUpFilter(newValue) : newValue; // serialize possible RegExp object to be properly stored as provenance graph
}

if (source instanceof Column) {
Expand Down
45 changes: 38 additions & 7 deletions src/lineup/internal/cmds/filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ function isIRegExpFilter(filter: any): filter is IRegExpFilter {
* This interface combines the `IStringFilter` from `StringColumn`
* and `ICategoricalFilter` from `ICategoricalColumn`.
*/
export interface ILineUpStringFilter {
interface ILineUpStringFilter {
/**
* Filter value
*/
Expand All @@ -52,6 +52,18 @@ export interface ILineUpStringFilter {
filterMissing: boolean;
}

/**
* This type guard checks if the `filter` parameter matches the `isLineUpStringFilter` type.
*
* @internal
* @param filter Any value that could be a filter
* @returns Returns true if filter should be serialized/restored or false if not.
*/
export function isLineUpStringFilter(filter: any): filter is ILineUpStringFilter {
return filter && filter.hasOwnProperty('filter') && (isLineUpStringFilterValue(filter.filter) || filter.filter instanceof RegExp) && filter.hasOwnProperty('filterMissing');
}


/**
* Similar to the `ILineUpStringFilter`, but the RegExp is replaced with `IRegExpFilter`
*/
Expand Down Expand Up @@ -107,6 +119,25 @@ export function serializeLineUpFilter(filter: ILineUpStringFilter): ISerializabl
};
}

/**
* Coverts a RegExp string to a RegExp instance
*
* @internal
* @param value RegExp string
* @returns The RegExp instance
*/
export function restoreRegExp(value: string): RegExp {
const serializedRegexParser = /^\/(.+)\/(\w+)?$/; // from https://gist.github.com/tenbits/ec7f0155b57b2d61a6cc90ef3d5f8b49
const matches = serializedRegexParser.exec(value);

if(matches === null) {
throw new Error('Unable to parse regular expression from string. The string does not seem to be a valid RegExp.');
}

const [_full, regexString, regexFlags] = matches;
return new RegExp(regexString, regexFlags);
}

/**
* Restores filter values from the provenance graph and returns an `ILineUpStringFilter`
* which can be passed to the LineUp instance (using LineUp > 4.0.0).
Expand All @@ -125,14 +156,14 @@ export function restoreLineUpFilter(filter: LineUpStringFilterValue | IRegExpFil
if (isLineUpStringFilterValue(filter)) {
return {filter, filterMissing};

} else if (isIRegExpFilter(filter)) {
if (filter.isRegExp) {
const serializedRegexParser = /^\/(.+)\/(\w+)?$/; // from https://gist.github.com/tenbits/ec7f0155b57b2d61a6cc90ef3d5f8b49
const matches = serializedRegexParser.exec(filter.value as string);
const [_full, regexString, regexFlags] = matches;
return {filter: new RegExp(regexString, regexFlags), filterMissing};
} else if (isIRegExpFilter(filter) && filter.isRegExp === true) {
if(typeof filter.value === 'string') {
return {filter: restoreRegExp(filter.value), filterMissing};
}

throw new Error('Wrong type of filter value. Unable to restore RegExp instance from the given filter value.');

} else if (isIRegExpFilter(filter) && filter.isRegExp === false) {
return restoreLineUpFilter(filter.value, filterMissing);

} else if (isSerializedFilter(filter)) {
Expand Down
51 changes: 50 additions & 1 deletion tests/lineup/internal/cmds/filter.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/// <reference types="jest" />
import {serializeLineUpFilter, restoreLineUpFilter, ILineUpStringFilter} from '../../../../src/lineup/internal/cmds/filter';
import {serializeLineUpFilter, restoreLineUpFilter, isSerializedFilter, isLineUpStringFilter, restoreRegExp} from '../../../../src/lineup/internal/cmds/filter';

// The following tests worked with LineUp v3.
// With LineUp v4 the filter object changed
Expand All @@ -16,6 +16,55 @@ import {serializeLineUpFilter, restoreLineUpFilter, ILineUpStringFilter} from '.
// });
// });

describe('Type guard isLineUpStringFilter', () => {
it('isLineUpStringFilter with string', () => {
expect(isLineUpStringFilter({filter: 'abc', filterMissing: false})).toBe(true);
});

it('isLineUpStringFilter with string[]', () => {
expect(isLineUpStringFilter({filter: ['abc', 'def'], filterMissing: false})).toBe(true);
});

it('isLineUpStringFilter with null', () => {
expect(isLineUpStringFilter({filter: null, filterMissing: false})).toBe(true);
});

it('isLineUpStringFilter with RegExp', () => {
expect(isLineUpStringFilter({filter: /abc/gm, filterMissing: false})).toBe(true);
});
});

describe('Type guard isSerializedFilter', () => {
it('isSerializedFilter with string', () => {
expect(isSerializedFilter({filter: {value: 'abc', isRegExp: false}, filterMissing: false})).toBe(true);
});

it('isSerializedFilter with string[]', () => {
expect(isSerializedFilter({filter: {value: ['abc', 'def'], isRegExp: false}, filterMissing: false})).toBe(true);
});

it('isSerializedFilter with null', () => {
expect(isSerializedFilter({filter: {value: null, isRegExp: false}, filterMissing: false})).toBe(true);
});

it('isSerializedFilter with RegExp', () => {
expect(isSerializedFilter({filter: {value: '/abc/gm', isRegExp: true}, filterMissing: false})).toBe(true);
});
});

describe('Restore RegExp from string', () => {
it('valid RegExp string', () => {
expect(restoreRegExp('/abc/gm').toString()).toBe('/abc/gm');
expect(restoreRegExp('/def/gm').toString()).not.toBe('/abc/gm');
});

it('invalid RegExp string', () => {
expect(() => {
restoreRegExp('invalid regexp string').toString();
}).toThrow(new Error('Unable to parse regular expression from string. The string does not seem to be a valid RegExp.'));
});
});

describe('Serialize LineUp v4 filter for provenance graph', () => {
it('filter as string', () => {
const lineUpFilter = {filter: 'abc', filterMissing: false};
Expand Down

0 comments on commit 304e51f

Please sign in to comment.