Skip to content

Commit 2a6ebda

Browse files
ycombinatormattapperson
authored andcommitted
[Beats Management] APIs: List beats (#19086)
* WIP checkin * Add API integration test * Converting to Jest test * WIP checkin * Fixing API for default case + adding test for it * Fixing copy pasta typos * Fixing variable name * Using a single index * Implementing GET /api/beats/agents API * Updating mapping
1 parent 4676f3b commit 2a6ebda

File tree

6 files changed

+178
-0
lines changed

6 files changed

+178
-0
lines changed

x-pack/plugins/beats/server/routes/api/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66

77
import { registerCreateEnrollmentTokensRoute } from './register_create_enrollment_tokens_route';
88
import { registerEnrollBeatRoute } from './register_enroll_beat_route';
9+
import { registerListBeatsRoute } from './register_list_beats_route';
910

1011
export function registerApiRoutes(server) {
1112
registerCreateEnrollmentTokensRoute(server);
1213
registerEnrollBeatRoute(server);
14+
registerListBeatsRoute(server);
1315
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import {
8+
get,
9+
omit
10+
} from "lodash";
11+
import { INDEX_NAMES } from "../../../common/constants";
12+
import { callWithRequestFactory } from '../../lib/client';
13+
import { wrapEsError } from "../../lib/error_wrappers";
14+
15+
async function getBeats(callWithRequest) {
16+
const params = {
17+
index: INDEX_NAMES.BEATS,
18+
type: '_doc',
19+
q: 'type:beat'
20+
};
21+
22+
const response = await callWithRequest('search', params);
23+
return get(response, 'hits.hits', []);
24+
}
25+
26+
// TODO: add license check pre-hook
27+
export function registerListBeatsRoute(server) {
28+
server.route({
29+
method: 'GET',
30+
path: '/api/beats/agents',
31+
handler: async (request, reply) => {
32+
const callWithRequest = callWithRequestFactory(server, request);
33+
let beats;
34+
35+
try {
36+
beats = await getBeats(callWithRequest);
37+
} catch (err) {
38+
return reply(wrapEsError(err));
39+
}
40+
41+
const response = {
42+
beats: beats.map(beat => omit(beat._source.beat, ['access_token']))
43+
};
44+
reply(response);
45+
}
46+
});
47+
}

x-pack/test/api_integration/apis/beats/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@ export default function ({ getService, loadTestFile }) {
1919

2020
loadTestFile(require.resolve('./create_enrollment_tokens'));
2121
loadTestFile(require.resolve('./enroll_beat'));
22+
loadTestFile(require.resolve('./list_beats'));
2223
});
2324
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
7+
import expect from 'expect.js';
8+
9+
export default function ({ getService }) {
10+
const supertest = getService('supertest');
11+
const esArchiver = getService('esArchiver');
12+
13+
describe('list_beats', () => {
14+
const archive = 'beats/list';
15+
16+
beforeEach('load beats archive', () => esArchiver.load(archive));
17+
afterEach('unload beats archive', () => esArchiver.unload(archive));
18+
19+
it('should return all beats', async () => {
20+
const { body: apiResponse } = await supertest
21+
.get(
22+
'/api/beats/agents'
23+
)
24+
.expect(200);
25+
26+
const beatsFromApi = apiResponse.beats;
27+
28+
expect(beatsFromApi.length).to.be(3);
29+
expect(beatsFromApi.filter(beat => beat.hasOwnProperty('verified_on')).length).to.be(1);
30+
expect(beatsFromApi.find(beat => beat.hasOwnProperty('verified_on')).id).to.be('foo');
31+
});
32+
33+
it('should not return access tokens', async () => {
34+
const { body: apiResponse } = await supertest
35+
.get(
36+
'/api/beats/agents'
37+
)
38+
.expect(200);
39+
40+
const beatsFromApi = apiResponse.beats;
41+
42+
expect(beatsFromApi.length).to.be(3);
43+
expect(beatsFromApi.filter(beat => beat.hasOwnProperty('access_token')).length).to.be(0);
44+
});
45+
});
46+
}
343 Bytes
Binary file not shown.
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
{
2+
"type": "index",
3+
"value": {
4+
"index": ".management-beats",
5+
"settings": {
6+
"index": {
7+
"codec": "best_compression",
8+
"number_of_shards": "1",
9+
"auto_expand_replicas": "0-1",
10+
"number_of_replicas": "0"
11+
}
12+
},
13+
"mappings": {
14+
"_doc": {
15+
"dynamic": "strict",
16+
"properties": {
17+
"type": {
18+
"type": "keyword"
19+
},
20+
"enrollment_token": {
21+
"properties": {
22+
"token": {
23+
"type": "keyword"
24+
},
25+
"expires_on": {
26+
"type": "date"
27+
}
28+
}
29+
},
30+
"configuration_block": {
31+
"properties": {
32+
"tag": {
33+
"type": "keyword"
34+
},
35+
"type": {
36+
"type": "keyword"
37+
},
38+
"block_yml": {
39+
"type": "text"
40+
}
41+
}
42+
},
43+
"beat": {
44+
"properties": {
45+
"id": {
46+
"type": "keyword"
47+
},
48+
"access_token": {
49+
"type": "keyword"
50+
},
51+
"verified_on": {
52+
"type": "date"
53+
},
54+
"type": {
55+
"type": "keyword"
56+
},
57+
"host_ip": {
58+
"type": "ip"
59+
},
60+
"host_name": {
61+
"type": "keyword"
62+
},
63+
"ephemeral_id": {
64+
"type": "keyword"
65+
},
66+
"local_configuration_yml": {
67+
"type": "text"
68+
},
69+
"central_configuration_yml": {
70+
"type": "text"
71+
},
72+
"metadata": {
73+
"dynamic": "true",
74+
"type": "object"
75+
}
76+
}
77+
}
78+
}
79+
}
80+
}
81+
}
82+
}

0 commit comments

Comments
 (0)