diff --git a/x-pack/legacy/plugins/actions/server/mappings.json b/x-pack/legacy/plugins/actions/server/mappings.json index a9c4d80b00af10..ef6a0c9919920f 100644 --- a/x-pack/legacy/plugins/actions/server/mappings.json +++ b/x-pack/legacy/plugins/actions/server/mappings.json @@ -2,7 +2,12 @@ "action": { "properties": { "name": { - "type": "text" + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + } }, "actionTypeId": { "type": "keyword" diff --git a/x-pack/legacy/plugins/alerting/server/mappings.json b/x-pack/legacy/plugins/alerting/server/mappings.json index 31733f44e7ce6e..a7e85febf2446b 100644 --- a/x-pack/legacy/plugins/alerting/server/mappings.json +++ b/x-pack/legacy/plugins/alerting/server/mappings.json @@ -5,7 +5,12 @@ "type": "boolean" }, "name": { - "type": "text" + "type": "text", + "fields": { + "keyword": { + "type": "keyword" + } + } }, "tags": { "type": "keyword" diff --git a/x-pack/plugins/actions/server/routes/find.test.ts b/x-pack/plugins/actions/server/routes/find.test.ts index 862e26132fdc35..b51130b2640aa4 100644 --- a/x-pack/plugins/actions/server/routes/find.test.ts +++ b/x-pack/plugins/actions/server/routes/find.test.ts @@ -81,6 +81,7 @@ describe('findActionRoute', () => { "perPage": 1, "search": undefined, "sortField": undefined, + "sortOrder": undefined, }, }, ] diff --git a/x-pack/plugins/actions/server/routes/find.ts b/x-pack/plugins/actions/server/routes/find.ts index 71d4274980fcc3..820dd32d710aed 100644 --- a/x-pack/plugins/actions/server/routes/find.ts +++ b/x-pack/plugins/actions/server/routes/find.ts @@ -26,6 +26,7 @@ const querySchema = schema.object({ }), search_fields: schema.maybe(schema.oneOf([schema.arrayOf(schema.string()), schema.string()])), sort_field: schema.maybe(schema.string()), + sort_order: schema.maybe(schema.oneOf([schema.literal('asc'), schema.literal('desc')])), has_reference: schema.maybe( // use nullable as maybe is currently broken // in config-schema @@ -70,6 +71,7 @@ export const findActionRoute = (router: IRouter, licenseState: LicenseState) => sortField: query.sort_field, fields: query.fields, filter: query.filter, + sortOrder: query.sort_order, }; if (query.search_fields) { diff --git a/x-pack/plugins/alerting/server/routes/find.test.ts b/x-pack/plugins/alerting/server/routes/find.test.ts index ba0114c99a9bdd..391d6df3f99317 100644 --- a/x-pack/plugins/alerting/server/routes/find.test.ts +++ b/x-pack/plugins/alerting/server/routes/find.test.ts @@ -82,6 +82,7 @@ describe('findAlertRoute', () => { "perPage": 1, "search": undefined, "sortField": undefined, + "sortOrder": undefined, }, }, ] diff --git a/x-pack/plugins/alerting/server/routes/find.ts b/x-pack/plugins/alerting/server/routes/find.ts index efc5c3ea97183d..1f8f161cf30285 100644 --- a/x-pack/plugins/alerting/server/routes/find.ts +++ b/x-pack/plugins/alerting/server/routes/find.ts @@ -26,6 +26,7 @@ const querySchema = schema.object({ }), search_fields: schema.maybe(schema.oneOf([schema.arrayOf(schema.string()), schema.string()])), sort_field: schema.maybe(schema.string()), + sort_order: schema.maybe(schema.oneOf([schema.literal('asc'), schema.literal('desc')])), has_reference: schema.maybe( // use nullable as maybe is currently broken // in config-schema @@ -70,6 +71,7 @@ export const findAlertRoute = (router: IRouter, licenseState: LicenseState) => { sortField: query.sort_field, fields: query.fields, filter: query.filter, + sortOrder: query.sort_order, }; if (query.search_fields) { diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api.test.ts index f568e0a71d0cf4..62e7b1cf022bba 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api.test.ts @@ -57,6 +57,8 @@ describe('loadAllActions', () => { Object { "query": Object { "per_page": 10000, + "sort_field": "name.keyword", + "sort_order": "asc", }, }, ] diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api.ts index 5b2b59603d281d..26ad97f05849d4 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/action_connector_api.ts @@ -30,6 +30,8 @@ export async function loadAllActions({ return await http.get(`${BASE_ACTION_API_PATH}/_find`, { query: { per_page: MAX_ACTIONS_RETURNED, + sort_field: 'name.keyword', + sort_order: 'asc', }, }); } diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts index 0b06982828446c..0555823d0245e6 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.test.ts @@ -170,6 +170,8 @@ describe('loadAlerts', () => { "per_page": 10, "search": undefined, "search_fields": undefined, + "sort_field": "name.keyword", + "sort_order": "asc", }, }, ] @@ -188,20 +190,22 @@ describe('loadAlerts', () => { const result = await loadAlerts({ http, searchText: 'apples', page: { index: 0, size: 10 } }); expect(result).toEqual(resolvedValue); expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - "/api/alert/_find", - Object { - "query": Object { - "default_search_operator": "AND", - "filter": undefined, - "page": 1, - "per_page": 10, - "search": "apples", - "search_fields": "[\\"name\\",\\"tags\\"]", - }, - }, - ] - `); + Array [ + "/api/alert/_find", + Object { + "query": Object { + "default_search_operator": "AND", + "filter": undefined, + "page": 1, + "per_page": 10, + "search": "apples", + "search_fields": "[\\"name\\",\\"tags\\"]", + "sort_field": "name.keyword", + "sort_order": "asc", + }, + }, + ] + `); }); test('should call find API with actionTypesFilter', async () => { @@ -220,20 +224,22 @@ describe('loadAlerts', () => { }); expect(result).toEqual(resolvedValue); expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - "/api/alert/_find", - Object { - "query": Object { - "default_search_operator": "AND", - "filter": undefined, - "page": 1, - "per_page": 10, - "search": "foo", - "search_fields": "[\\"name\\",\\"tags\\"]", - }, - }, - ] - `); + Array [ + "/api/alert/_find", + Object { + "query": Object { + "default_search_operator": "AND", + "filter": undefined, + "page": 1, + "per_page": 10, + "search": "foo", + "search_fields": "[\\"name\\",\\"tags\\"]", + "sort_field": "name.keyword", + "sort_order": "asc", + }, + }, + ] + `); }); test('should call find API with typesFilter', async () => { @@ -252,20 +258,22 @@ describe('loadAlerts', () => { }); expect(result).toEqual(resolvedValue); expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - "/api/alert/_find", - Object { - "query": Object { - "default_search_operator": "AND", - "filter": "alert.attributes.alertTypeId:(foo or bar)", - "page": 1, - "per_page": 10, - "search": undefined, - "search_fields": undefined, - }, - }, - ] - `); + Array [ + "/api/alert/_find", + Object { + "query": Object { + "default_search_operator": "AND", + "filter": "alert.attributes.alertTypeId:(foo or bar)", + "page": 1, + "per_page": 10, + "search": undefined, + "search_fields": undefined, + "sort_field": "name.keyword", + "sort_order": "asc", + }, + }, + ] + `); }); test('should call find API with actionTypesFilter and typesFilter', async () => { @@ -285,20 +293,22 @@ describe('loadAlerts', () => { }); expect(result).toEqual(resolvedValue); expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - "/api/alert/_find", - Object { - "query": Object { - "default_search_operator": "AND", - "filter": "alert.attributes.alertTypeId:(foo or bar)", - "page": 1, - "per_page": 10, - "search": "baz", - "search_fields": "[\\"name\\",\\"tags\\"]", - }, - }, - ] - `); + Array [ + "/api/alert/_find", + Object { + "query": Object { + "default_search_operator": "AND", + "filter": "alert.attributes.alertTypeId:(foo or bar)", + "page": 1, + "per_page": 10, + "search": "baz", + "search_fields": "[\\"name\\",\\"tags\\"]", + "sort_field": "name.keyword", + "sort_order": "asc", + }, + }, + ] + `); }); test('should call find API with searchText and tagsFilter and typesFilter', async () => { @@ -318,20 +328,22 @@ describe('loadAlerts', () => { }); expect(result).toEqual(resolvedValue); expect(http.get.mock.calls[0]).toMatchInlineSnapshot(` - Array [ - "/api/alert/_find", - Object { - "query": Object { - "default_search_operator": "AND", - "filter": "alert.attributes.alertTypeId:(foo or bar)", - "page": 1, - "per_page": 10, - "search": "apples, foo, baz", - "search_fields": "[\\"name\\",\\"tags\\"]", - }, - }, - ] - `); + Array [ + "/api/alert/_find", + Object { + "query": Object { + "default_search_operator": "AND", + "filter": "alert.attributes.alertTypeId:(foo or bar)", + "page": 1, + "per_page": 10, + "search": "apples, foo, baz", + "search_fields": "[\\"name\\",\\"tags\\"]", + "sort_field": "name.keyword", + "sort_order": "asc", + }, + }, + ] + `); }); }); diff --git a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts index ff6b4ba17c6d98..1b18460ba11cb8 100644 --- a/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts +++ b/x-pack/plugins/triggers_actions_ui/public/application/lib/alert_api.ts @@ -87,6 +87,8 @@ export async function loadAlerts({ search: searchText, filter: filters.length ? filters.join(' and ') : undefined, default_search_operator: 'AND', + sort_field: 'name.keyword', + sort_order: 'asc', }, }); } diff --git a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts index 791712fa244893..4354b19da24aca 100644 --- a/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts +++ b/x-pack/test/functional_with_es_ssl/apps/triggers_actions_ui/alerts.ts @@ -18,20 +18,21 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { const supertest = getService('supertest'); const find = getService('find'); - async function createAlert(alertTypeId?: string, name?: string, params?: any) { + async function createAlert(overwrites: Record = {}) { const { body: createdAlert } = await supertest .post(`/api/alert`) .set('kbn-xsrf', 'foo') .send({ enabled: true, - name: name ?? generateUniqueKey(), + name: generateUniqueKey(), tags: ['foo', 'bar'], - alertTypeId: alertTypeId ?? 'test.noop', + alertTypeId: 'test.noop', consumer: 'test', schedule: { interval: '1m' }, throttle: '1m', actions: [], - params: params ?? {}, + params: {}, + ...overwrites, }) .expect(200); return createdAlert; @@ -98,6 +99,22 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { ]); }); + it('should display alerts in alphabetical order', async () => { + const uniqueKey = generateUniqueKey(); + await createAlert({ name: 'b', tags: [uniqueKey] }); + await createAlert({ name: 'c', tags: [uniqueKey] }); + await createAlert({ name: 'a', tags: [uniqueKey] }); + + await pageObjects.common.navigateToApp('triggersActions'); + await pageObjects.triggersActionsUI.searchAlerts(uniqueKey); + + const searchResults = await pageObjects.triggersActionsUI.getAlertsList(); + expect(searchResults).to.have.length(3); + expect(searchResults[0].name).to.eql('a'); + expect(searchResults[1].name).to.eql('b'); + expect(searchResults[2].name).to.eql('c'); + }); + it('should search for alert', async () => { const createdAlert = await createAlert(); await pageObjects.common.navigateToApp('triggersActions'); @@ -115,16 +132,20 @@ export default ({ getPageObjects, getService }: FtrProviderContext) => { }); it('should edit an alert', async () => { - const createdAlert = await createAlert('.index-threshold', 'new alert', { - aggType: 'count', - termSize: 5, - thresholdComparator: '>', - timeWindowSize: 5, - timeWindowUnit: 'm', - groupBy: 'all', - threshold: [1000, 5000], - index: ['.kibana_1'], - timeField: 'alert', + const createdAlert = await createAlert({ + alertTypeId: '.index-threshold', + name: 'new alert', + params: { + aggType: 'count', + termSize: 5, + thresholdComparator: '>', + timeWindowSize: 5, + timeWindowUnit: 'm', + groupBy: 'all', + threshold: [1000, 5000], + index: ['.kibana_1'], + timeField: 'alert', + }, }); await pageObjects.common.navigateToApp('triggersActions'); await pageObjects.triggersActionsUI.searchAlerts(createdAlert.name);