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
2 changes: 1 addition & 1 deletion src/gmp/collection/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ export function parseInfoCounts(response: InfoWithCounts) {
}

export function parseFilter(element: FilterElement): FilterType {
return Filter.fromElement(element.filters);
return Filter.fromResponseElement(element.filters);
}

export function parseCounts<TElement = Element>(
Expand Down
2 changes: 1 addition & 1 deletion src/gmp/commands/__tests__/entities.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ describe('EntitiesCommand tests', () => {
});

test('should prefer filter_id over filter parameter', async () => {
const filter = Filter.fromElement({
const filter = Filter.fromResponseElement({
_id: 'bar',
keywords: {
keyword: {relation: '=', value: 'bar', column: 'foo'},
Expand Down
2 changes: 1 addition & 1 deletion src/gmp/commands/__tests__/entity.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ describe('EntityCommand tests', () => {
});

test('should get entity and prefer filter_id over filter parameter', async () => {
const filter = Filter.fromElement({
const filter = Filter.fromResponseElement({
_id: 'bar',
keywords: {
keyword: {relation: '=', value: 'bar', column: 'foo'},
Expand Down
28 changes: 21 additions & 7 deletions src/gmp/models/__tests__/filter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ describe('Filter parse from string tests', () => {
});
});

describe('Filter parse from keywords', () => {
describe('Filter fromResponseElement', () => {
test('should parse approx relation without column', () => {
const elem = {
keywords: {
Expand All @@ -276,7 +276,7 @@ describe('Filter parse from keywords', () => {
],
},
};
const filter = Filter.fromElement(elem);
const filter = Filter.fromResponseElement(elem);
expect(filter.toFilterString()).toEqual('~abc');
});

Expand Down Expand Up @@ -307,7 +307,7 @@ describe('Filter parse from keywords', () => {
],
},
};
let filter = Filter.fromElement(elem);
let filter = Filter.fromResponseElement(elem);
expect(filter.toFilterString()).toEqual('~abc and not ~def');

elem = {
Expand Down Expand Up @@ -351,7 +351,7 @@ describe('Filter parse from keywords', () => {
],
},
};
filter = Filter.fromElement(elem);
filter = Filter.fromResponseElement(elem);
expect(filter.toFilterString()).toEqual(
'~abc and not ~def rows=10 first=1 sort=name',
);
Expand Down Expand Up @@ -395,7 +395,7 @@ describe('Filter parse from keywords', () => {
},
};

const filter = Filter.fromElement(elem);
const filter = Filter.fromResponseElement(elem);
const filterString =
'severity>3.9 and severity<7 first=1 rows=10 sort=name';
expect(filter.toFilterString()).toEqual(filterString);
Expand All @@ -416,9 +416,20 @@ describe('Filter parse from keywords', () => {
],
},
};
const filter = Filter.fromElement(elem);
const filter = Filter.fromResponseElement(elem);
expect(filter.toFilterString()).toEqual('_foo=abc');
});

test('should parse id', () => {
const filter1 = Filter.fromResponseElement();
expect(filter1.id).toBeUndefined();

const filter2 = Filter.fromResponseElement({_id: '123'});
expect(filter2.id).toBe('123');

const filter3 = Filter.fromResponseElement({_id: UNKNOWN_FILTER_ID});
expect(filter3.id).toBeUndefined();
});
});

describe('Filter set', () => {
Expand Down Expand Up @@ -1613,6 +1624,7 @@ describe('should lower the case of capitalized keywords', () => {
'severity>3.9 and qod_min=70 rows=14',
);
});

test('should do the same for filters from arrays', () => {
const element = {
keywords: {
Expand Down Expand Up @@ -1655,15 +1667,17 @@ describe('should lower the case of capitalized keywords', () => {
],
},
};
const filter = Filter.fromElement(element);
const filter = Filter.fromResponseElement(element);
expect(filter.toFilterString()).toEqual(
'~abc and not ~def rows=10 first=1 sort=name',
);
});

test('a more wacky scenario', () => {
const filter1 = Filter.fromString('~abc SorT=name');
expect(filter1.toFilterString()).toEqual('~abc sort=name');
});

test('just a value', () => {
const filter2 = Filter.fromString('~AbC');
expect(filter2.toFilterString()).toEqual('~AbC');
Expand Down
92 changes: 80 additions & 12 deletions src/gmp/models/filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,81 @@ import Model, {type ModelElement, type ModelProperties} from 'gmp/models/model';
import {parseInt} from 'gmp/parser';
import {map} from 'gmp/utils/array';
import {isDefined, isString, isArray, hasValue} from 'gmp/utils/identity';
import {isEmpty} from 'gmp/utils/string';

export interface FilterKeyword {
column?: string;
relation?: string;
value?: string;
}

/**
* XML Structure of a filter model element as returned by `<get_filters>`
* queries.
*
* Example XML Structure:
* ```xml
* <get_filters_response status="200" status_text="OK">
* <filter id="0c239c16-d597-48a1-9f51-347627a23dac">
* ...
* <term>apply_overrides=0 min_qod=70 sort=name first=1 rows=1</term>
* </filter>
* </get_filters_response>
* ```
*/
export interface FilterModelElement extends ModelElement {
alerts?: {
alert: ModelElement[];
};
filter_type?: string;
keywords?: {
keyword?: FilterKeyword | FilterKeyword[];
};
term?: string;
}

/**
* XML Structure of a filter response element
* as returned by all `<get_xyz>` queries, for example `<get_tasks>`.
*
* Example XML Structure:
* ```xml
* <get_tasks_response>
* <task>
* ...
* </task>
* <filters id="">
* <term>apply_overrides=0 min_qod=70 sort=name first=1 rows=1</term>
* <keywords>
* <keyword>
* <column>apply_overrides</column>
* <relation>=</relation>
* <value>0</value>
* </keyword>
* <keyword>
* <column>min_qod</column>
* <relation>=</relation>
* <value>70</value>
* </keyword>
* <keyword>
* <column>sort</column>
* <relation>=</relation>
* <value>name</value>
* </keyword>
* <keyword>
* <column>first</column>
* <relation>=</relation>
* <value>1</value>
* </keyword>
* <keyword>
* <column>rows</column>
* <relation>=</relation>
* <value>1</value>
* </keyword>
* </keywords>
* </filters>
* </get_tasks_response>
* ```
*/
export interface FilterResponseElement {
_id?: string;
keywords?: {
keyword?: FilterKeyword | FilterKeyword[];
};
Expand Down Expand Up @@ -201,15 +257,7 @@ class Filter extends EntityModel implements FilterType {
if (ret.id === UNKNOWN_FILTER_ID) {
ret.id = undefined;
}
if (isDefined(element.keywords)) {
ret.terms = map(
element.keywords.keyword,
({relation, value, column: key}: FilterKeyword) =>
new FilterTerm(convert(key, value, relation)),
);
// @ts-expect-error
delete ret.keywords;
} else if (isDefined(element.term)) {
if (isDefined(element.term)) {
ret.terms = parseFilterTermsFromString(element.term);

// ret.term should not be part of the public api
Expand All @@ -230,6 +278,26 @@ class Filter extends EntityModel implements FilterType {
return new Filter(ret);
}

static fromResponseElement(element: FilterResponseElement = {}): FilterType {
const id =
!isEmpty(element._id) && element._id !== UNKNOWN_FILTER_ID
? element._id
: undefined;

let terms: FilterTerm[] = [];
if (isDefined(element.keywords)) {
terms = map(
element.keywords.keyword,
({relation, value, column: key}: FilterKeyword) =>
new FilterTerm(convert(key, value, relation)),
);
} else if (isDefined(element.term)) {
terms = parseFilterTermsFromString(element.term);
}

return new Filter({id, terms});
}

/**
* @private
*
Expand Down
Loading