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: 2 additions & 0 deletions docs/api/alerts/find.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ Retrieve a paginated set of alerts based on condition.
NOTE: As alerts change in {kib}, the results on each page of the response also
change. Use the find API for traditional paginated results, but avoid using it to export large amounts of data.

NOTE: Alert `params` are stored as {ref}/flattened.html[flattened] and analyzed as `keyword`.

[[alerts-api-find-request-codes]]
==== Response code

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ const mockMappings = {
},
},
},
params: {
type: 'flattened',
},
},
},
hiddenType: {
Expand Down Expand Up @@ -168,6 +171,12 @@ describe('Filter Utils', () => {
).toEqual(esKuery.fromKueryExpression('alert.actions:{ actionTypeId: ".server-log" }'));
});

test('Assemble filter for flattened fields', () => {
expect(
validateConvertFilterToKueryNode(['alert'], 'alert.attributes.params.foo:bar', mockMappings)
).toEqual(esKuery.fromKueryExpression('alert.params.foo:bar'));
});

test('Lets make sure that we are throwing an exception if we get an error', () => {
expect(() => {
validateConvertFilterToKueryNode(
Expand Down
36 changes: 23 additions & 13 deletions src/core/server/saved_objects/service/lib/filter_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -207,23 +207,33 @@ export const hasFilterKeyError = (

export const fieldDefined = (indexMappings: IndexMapping, key: string): boolean => {
const mappingKey = 'properties.' + key.split('.').join('.properties.');
const potentialKey = get(indexMappings, mappingKey);
if (get(indexMappings, mappingKey) != null) {
return true;
}

// If the `mappingKey` does not match a valid path, before returning null,
// If the `mappingKey` does not match a valid path, before returning false,
// we want to check and see if the intended path was for a multi-field
// such as `x.attributes.field.text` where `field` is mapped to both text
// and keyword
if (potentialKey == null) {
const propertiesAttribute = 'properties';
const indexOfLastProperties = mappingKey.lastIndexOf(propertiesAttribute);
const fieldMapping = mappingKey.substr(0, indexOfLastProperties);
const fieldType = mappingKey.substr(
mappingKey.lastIndexOf(propertiesAttribute) + `${propertiesAttribute}.`.length
);
const mapping = `${fieldMapping}fields.${fieldType}`;

return get(indexMappings, mapping) != null;
} else {
const propertiesAttribute = 'properties';
const indexOfLastProperties = mappingKey.lastIndexOf(propertiesAttribute);
const fieldMapping = mappingKey.substr(0, indexOfLastProperties);
const fieldType = mappingKey.substr(
mappingKey.lastIndexOf(propertiesAttribute) + `${propertiesAttribute}.`.length
);
const mapping = `${fieldMapping}fields.${fieldType}`;
if (get(indexMappings, mapping) != null) {
return true;
}

// If the path is for a flattned type field, we'll assume the mappings are defined.
const keys = key.split('.');
for (let i = 0; i < keys.length; i++) {
const path = `properties.${keys.slice(0, i + 1).join('.properties.')}`;
if (get(indexMappings, path)?.type === 'flattened') {
return true;
}
}

return false;
};
3 changes: 1 addition & 2 deletions x-pack/plugins/alerts/server/saved_objects/mappings.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@
}
},
"params": {
"enabled": false,
"type": "object"
"type": "flattened"
},
"scheduledTaskId": {
"type": "keyword"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ export default function createFindTests({ getService }: FtrProviderContext) {

afterEach(() => objectRemover.removeAll());

async function createAlert(overwrites = {}) {
const { body: createdAlert } = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`)
.set('kbn-xsrf', 'foo')
.send(getTestAlertData(overwrites))
.expect(200);
objectRemover.add(Spaces.space1.id, createdAlert.id, 'alert', 'alerts');
return createdAlert;
}

it('should handle find alert request appropriately', async () => {
const { body: createdAlert } = await supertest
.post(`${getUrlPrefix(Spaces.space1.id)}/api/alerts/alert`)
Expand Down Expand Up @@ -85,5 +95,23 @@ export default function createFindTests({ getService }: FtrProviderContext) {
data: [],
});
});

it('should filter on string parameters', async () => {
await Promise.all([
createAlert({ params: { strValue: 'my a' } }),
createAlert({ params: { strValue: 'my b' } }),
createAlert({ params: { strValue: 'my c' } }),
]);

const response = await supertest.get(
`${getUrlPrefix(
Spaces.space1.id
)}/api/alerts/_find?filter=alert.attributes.params.strValue:"my b"`
);

expect(response.status).to.eql(200);
expect(response.body.total).to.equal(1);
expect(response.body.data[0].params.strValue).to.eql('my b');
});
});
}