Skip to content

Commit bb5a9c4

Browse files
committed
Add filter for application list
This filter allows to filter by name, url, tags or version and will only display the instances matching the filter. closes #998
1 parent 6ce5a77 commit bb5a9c4

File tree

17 files changed

+374
-255
lines changed

17 files changed

+374
-255
lines changed

spring-boot-admin-server-ui/src/main/frontend/components/font-awesome-icon.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2014-2018 the original author or authors.
2+
* Copyright 2014-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@ import {faGithub} from '@fortawesome/free-brands-svg-icons/faGithub';
1919
import {faGitter} from '@fortawesome/free-brands-svg-icons/faGitter';
2020
import {faStackOverflow} from '@fortawesome/free-brands-svg-icons/faStackOverflow';
2121
import {faTimesCircle as farTimesCircle} from '@fortawesome/free-regular-svg-icons/faTimesCircle';
22+
import {faAngleDoubleRight} from '@fortawesome/free-solid-svg-icons/faAngleDoubleRight';
2223
import {faBan} from '@fortawesome/free-solid-svg-icons/faBan';
2324
import {faBell} from '@fortawesome/free-solid-svg-icons/faBell';
2425
import {faBellSlash} from '@fortawesome/free-solid-svg-icons/faBellSlash';
@@ -27,22 +28,21 @@ import {faCheck} from '@fortawesome/free-solid-svg-icons/faCheck';
2728
import {faDownload} from '@fortawesome/free-solid-svg-icons/faDownload';
2829
import {faExclamation} from '@fortawesome/free-solid-svg-icons/faExclamation';
2930
import {faExclamationTriangle} from '@fortawesome/free-solid-svg-icons/faExclamationTriangle';
31+
import {faFilter} from '@fortawesome/free-solid-svg-icons/faFilter';
3032
import {faHeartbeat} from '@fortawesome/free-solid-svg-icons/faHeartbeat';
3133
import {faHome} from '@fortawesome/free-solid-svg-icons/faHome';
34+
import {faMapMarkerAlt} from '@fortawesome/free-solid-svg-icons/faMapMarkerAlt';
3235
import {faMinusCircle} from '@fortawesome/free-solid-svg-icons/faMinusCircle';
3336
import {faPencilAlt} from '@fortawesome/free-solid-svg-icons/faPencilAlt';
3437
import {faQuestionCircle} from '@fortawesome/free-solid-svg-icons/faQuestionCircle';
38+
import {faSearch} from '@fortawesome/free-solid-svg-icons/faSearch';
3539
import {faSignOutAlt} from '@fortawesome/free-solid-svg-icons/faSignOutAlt';
3640
import {faStepBackward} from '@fortawesome/free-solid-svg-icons/faStepBackward';
3741
import {faStepForward} from '@fortawesome/free-solid-svg-icons/faStepForward';
3842
import {faTimesCircle} from '@fortawesome/free-solid-svg-icons/faTimesCircle';
3943
import {faTrash} from '@fortawesome/free-solid-svg-icons/faTrash';
4044
import {faUserCircle} from '@fortawesome/free-solid-svg-icons/faUserCircle';
4145
import {faWrench} from '@fortawesome/free-solid-svg-icons/faWrench';
42-
import {faAngleDoubleRight} from '@fortawesome/free-solid-svg-icons/faAngleDoubleRight';
43-
import {faSearch} from '@fortawesome/free-solid-svg-icons/faSearch';
44-
import {faMapMarkerAlt} from '@fortawesome/free-solid-svg-icons/faMapMarkerAlt';
45-
import {faFilter} from '@fortawesome/free-solid-svg-icons/faFilter';
4646
import {FontAwesomeIcon} from '@fortawesome/vue-fontawesome';
4747

4848
library.add(

spring-boot-admin-server-ui/src/main/frontend/services/application.js

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2014-2018 the original author or authors.
2+
* Copyright 2014-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -22,8 +22,8 @@ import sortBy from 'lodash/sortBy';
2222
import Instance from './instance';
2323

2424
class Application {
25-
26-
constructor(name) {
25+
constructor({name, instances, ...application}) {
26+
Object.assign(this, application);
2727
this.name = name;
2828
this.axios = axios.create({
2929
baseURL: uri`applications/${this.name}/`
@@ -32,6 +32,14 @@ class Application {
3232
response => response,
3333
redirectOn401()
3434
);
35+
this.instances = sortBy(instances.map(i => new Instance(i), [instance => instance.registration.healthUrl]));
36+
}
37+
38+
filterInstances(predicate) {
39+
return new Application({
40+
...this,
41+
instances: this.instances.filter(predicate)
42+
})
3543
}
3644

3745
findInstance(instanceId) {
@@ -76,18 +84,10 @@ class Application {
7684
}
7785
const json = JSON.parse(data);
7886
if (json instanceof Array) {
79-
const applications = json.map(Application._toApplication);
87+
const applications = json.map(j => new Application(j));
8088
return sortBy(applications, [item => item.name]);
8189
}
82-
return Application._toApplication(json);
83-
}
84-
85-
static _toApplication(application) {
86-
const instances = application.instances.map(instance => Object.assign(new Instance(instance.id), instance));
87-
return Object.assign(new Application(application.name), {
88-
...application,
89-
instances: sortBy(instances, [instance => instance.registration.healthUrl])
90-
});
90+
return new Application(json);
9191
}
9292
}
9393

spring-boot-admin-server-ui/src/main/frontend/services/instance.js

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ const actuatorMimeTypes = [
3030
const isInstanceActuatorRequest = url => url.match(/^instances[/][^/]+[/]actuator([/].*)?$/);
3131

3232
class Instance {
33-
constructor(id) {
33+
constructor({id, ...instance}) {
34+
Object.assign(this, instance);
3435
this.id = id;
3536
this.axios = axios.create({
3637
baseURL: uri`instances/${this.id}/`,
@@ -86,7 +87,7 @@ class Instance {
8687
}
8788

8889
async fetchEnv(name) {
89-
return this.axios.get(uri`actuator/env/${name || '' }`, {
90+
return this.axios.get(uri`actuator/env/${name || ''}`, {
9091
headers: {'Accept': actuatorMimeTypes}
9192
});
9293
}
@@ -171,7 +172,7 @@ class Instance {
171172
}
172173

173174
async clearCache(name, cacheManager) {
174-
return this.axios.delete(uri`actuator/caches/${name}` , {
175+
return this.axios.delete(uri`actuator/caches/${name}`, {
175176
params: {'cacheManager': cacheManager},
176177
headers: {'Accept': actuatorMimeTypes}
177178
});
@@ -322,18 +323,16 @@ class Instance {
322323

323324
static async get(id) {
324325
return axios.get(uri`instances/${id}`, {
325-
transformResponse: Instance._toInstance
326+
transformResponse(data) {
327+
if (!data) {
328+
return data;
329+
}
330+
const instance = JSON.parse(data);
331+
return new Instance(instance);
332+
}
326333
});
327334
}
328335

329-
static _toInstance(data) {
330-
if (!data) {
331-
return data;
332-
}
333-
const instance = JSON.parse(data);
334-
return Object.assign(new Instance(instance.id), instance);
335-
}
336-
337336
static _toLoggers(data) {
338337
if (!data) {
339338
return data;

spring-boot-admin-server-ui/src/main/frontend/utils/collections.js

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2014-2018 the original author or authors.
2+
* Copyright 2014-2019 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -14,9 +14,17 @@
1414
* limitations under the License.
1515
*/
1616

17-
1817
export const compareBy = mapper => (a, b) => {
1918
const valA = mapper(a);
2019
const valB = mapper(b);
2120
return valA > valB ? 1 : valA < valB ? -1 : 0;
2221
};
22+
23+
export const anyValueMatches = (obj, predicate) => {
24+
if (Array.isArray(obj)) {
25+
return obj.some(e => anyValueMatches(e, predicate));
26+
} else if (obj !== null && typeof obj === 'object') {
27+
return anyValueMatches(Object.values(obj), predicate);
28+
}
29+
return predicate(obj);
30+
};
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright 2014-2019 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
18+
import {anyValueMatches} from './collections';
19+
20+
describe('anyValueMatches', () => {
21+
it('should return predicate value', () => {
22+
const predicate = jest.fn(() => true);
23+
expect(anyValueMatches('test', predicate)).toBe(true);
24+
});
25+
26+
it('should call predicate for string', () => {
27+
const predicate = jest.fn();
28+
anyValueMatches('test', predicate);
29+
expect(predicate).toHaveBeenCalledWith('test');
30+
});
31+
32+
it('should call predicate for number', () => {
33+
const predicate = jest.fn();
34+
anyValueMatches(1, predicate);
35+
expect(predicate).toHaveBeenCalledWith(1);
36+
});
37+
38+
it('should call predicate for boolean', () => {
39+
const predicate = jest.fn();
40+
anyValueMatches(true, predicate);
41+
expect(predicate).toHaveBeenCalledWith(true);
42+
});
43+
44+
it('should call predicate for null', () => {
45+
const predicate = jest.fn();
46+
anyValueMatches(null, predicate);
47+
expect(predicate).toHaveBeenCalledWith(null);
48+
});
49+
50+
it('should call predicate for undefined', () => {
51+
const predicate = jest.fn();
52+
anyValueMatches(undefined, predicate);
53+
expect(predicate).toHaveBeenCalledWith(undefined);
54+
});
55+
56+
it('should not call predicate for empty object', () => {
57+
const predicate = jest.fn();
58+
anyValueMatches({}, predicate);
59+
expect(predicate).not.toHaveBeenCalled()
60+
});
61+
62+
it('should not call predicate for empty array', () => {
63+
const predicate = jest.fn();
64+
anyValueMatches([], predicate);
65+
expect(predicate).not.toHaveBeenCalled()
66+
});
67+
68+
it('should not call predicate for elements in array', () => {
69+
const predicate = jest.fn();
70+
anyValueMatches(['test', 1, true, {value: 'nested-obj'}, ['nested-array'], [], {}], predicate);
71+
expect(predicate).toHaveBeenNthCalledWith(1, 'test');
72+
expect(predicate).toHaveBeenNthCalledWith(2, 1);
73+
expect(predicate).toHaveBeenNthCalledWith(3, true);
74+
expect(predicate).toHaveBeenNthCalledWith(4, 'nested-obj');
75+
expect(predicate).toHaveBeenNthCalledWith(5, 'nested-array');
76+
});
77+
});

spring-boot-admin-server-ui/src/main/frontend/views/applications/applications-list-item.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
- Copyright 2014-2018 the original author or authors.
2+
- Copyright 2014-2019 the original author or authors.
33
-
44
- Licensed under the Apache License, Version 2.0 (the "License");
55
- you may not use this file except in compliance with the License.
@@ -98,7 +98,7 @@
9898
},
9999
methods: {
100100
hasActiveNotificationFilter(object) {
101-
return this.notificationFilters.findIndex(f => f.affects(object)) >= 0;
101+
return this.notificationFilters.some(f => f.affects(object));
102102
}
103103
}
104104
}

0 commit comments

Comments
 (0)