Status service: improve overall status summary (#114228) (#114453)

* improve getSummaryStatus

* fix unit tests

Co-authored-by: Pierre Gayvallet <pierre.gayvallet@gmail.com>
This commit is contained in:
Kibana Machine 2021-10-11 11:10:26 -04:00 committed by GitHub
parent e190acd1cf
commit c4ddd03f95
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 128 additions and 125 deletions

View file

@ -81,93 +81,86 @@ describe('getSummaryStatus', () => {
});
describe('summary', () => {
describe('when a single service is at highest level', () => {
it('returns all information about that single service', () => {
expect(
getSummaryStatus(
Object.entries({
s1: degraded,
s2: {
level: ServiceStatusLevels.unavailable,
summary: 'Lorem ipsum',
meta: {
custom: { data: 'here' },
},
it('returns correct summary when a single service is affected', () => {
expect(
getSummaryStatus(
Object.entries({
s1: degraded,
s2: {
level: ServiceStatusLevels.unavailable,
summary: 'Lorem ipsum',
meta: {
custom: { data: 'here' },
},
})
)
).toEqual({
level: ServiceStatusLevels.unavailable,
summary: '[s2]: Lorem ipsum',
detail: 'See the status page for more information',
meta: {
affectedServices: ['s2'],
},
});
});
it('allows the single service to override the detail and documentationUrl fields', () => {
expect(
getSummaryStatus(
Object.entries({
s1: degraded,
s2: {
level: ServiceStatusLevels.unavailable,
summary: 'Lorem ipsum',
detail: 'Vivamus pulvinar sem ac luctus ultrices.',
documentationUrl: 'http://helpmenow.com/problem1',
meta: {
custom: { data: 'here' },
},
},
})
)
).toEqual({
level: ServiceStatusLevels.unavailable,
summary: '[s2]: Lorem ipsum',
detail: 'Vivamus pulvinar sem ac luctus ultrices.',
documentationUrl: 'http://helpmenow.com/problem1',
meta: {
affectedServices: ['s2'],
},
});
},
})
)
).toEqual({
level: ServiceStatusLevels.unavailable,
summary: '1 service is unavailable: s2',
detail: 'See the status page for more information',
meta: {
affectedServices: ['s2'],
},
});
});
describe('when multiple services is at highest level', () => {
it('returns aggregated information about the affected services', () => {
expect(
getSummaryStatus(
Object.entries({
s1: degraded,
s2: {
level: ServiceStatusLevels.unavailable,
summary: 'Lorem ipsum',
detail: 'Vivamus pulvinar sem ac luctus ultrices.',
documentationUrl: 'http://helpmenow.com/problem1',
meta: {
custom: { data: 'here' },
},
it('returns correct summary when multiple services are affected', () => {
expect(
getSummaryStatus(
Object.entries({
s1: degraded,
s2: {
level: ServiceStatusLevels.unavailable,
summary: 'Lorem ipsum',
detail: 'Vivamus pulvinar sem ac luctus ultrices.',
documentationUrl: 'http://helpmenow.com/problem1',
meta: {
custom: { data: 'here' },
},
s3: {
level: ServiceStatusLevels.unavailable,
summary: 'Proin mattis',
detail: 'Nunc quis nulla at mi lobortis pretium.',
documentationUrl: 'http://helpmenow.com/problem2',
meta: {
other: { data: 'over there' },
},
},
s3: {
level: ServiceStatusLevels.unavailable,
summary: 'Proin mattis',
detail: 'Nunc quis nulla at mi lobortis pretium.',
documentationUrl: 'http://helpmenow.com/problem2',
meta: {
other: { data: 'over there' },
},
})
)
).toEqual({
level: ServiceStatusLevels.unavailable,
summary: '[2] services are unavailable',
detail: 'See the status page for more information',
meta: {
affectedServices: ['s2', 's3'],
},
});
},
})
)
).toEqual({
level: ServiceStatusLevels.unavailable,
summary: '2 services are unavailable: s2, s3',
detail: 'See the status page for more information',
meta: {
affectedServices: ['s2', 's3'],
},
});
});
it('returns correct summary more than `maxServices` services are affected', () => {
expect(
getSummaryStatus(
Object.entries({
s1: degraded,
s2: available,
s3: degraded,
s4: degraded,
s5: degraded,
s6: available,
s7: degraded,
}),
{ maxServices: 3 }
)
).toEqual({
level: ServiceStatusLevels.degraded,
summary: '5 services are degraded: s1, s3, s4 and 2 other(s)',
detail: 'See the status page for more information',
meta: {
affectedServices: ['s1', 's3', 's4', 's5', 's7'],
},
});
});
});

View file

@ -10,11 +10,13 @@ import { ServiceStatus, ServiceStatusLevels, ServiceStatusLevel } from './types'
/**
* Returns a single {@link ServiceStatus} that summarizes the most severe status level from a group of statuses.
* @param statuses
*/
export const getSummaryStatus = (
statuses: Array<[string, ServiceStatus]>,
{ allAvailableSummary = `All services are available` }: { allAvailableSummary?: string } = {}
{
allAvailableSummary = `All services are available`,
maxServices = 3,
}: { allAvailableSummary?: string; maxServices?: number } = {}
): ServiceStatus => {
const { highestLevel, highestStatuses } = highestLevelSummary(statuses);
@ -23,30 +25,38 @@ export const getSummaryStatus = (
level: ServiceStatusLevels.available,
summary: allAvailableSummary,
};
} else if (highestStatuses.length === 1) {
const [serviceName, status] = highestStatuses[0]! as [string, ServiceStatus];
return {
...status,
summary: `[${serviceName}]: ${status.summary!}`,
// TODO: include URL to status page
detail: status.detail ?? `See the status page for more information`,
meta: {
affectedServices: [serviceName],
},
};
} else {
const affectedServices = highestStatuses.map(([serviceName]) => serviceName);
return {
level: highestLevel,
summary: `[${highestStatuses.length}] services are ${highestLevel.toString()}`,
summary: getSummaryContent(affectedServices, highestLevel, maxServices),
// TODO: include URL to status page
detail: `See the status page for more information`,
meta: {
affectedServices: highestStatuses.map(([serviceName]) => serviceName),
affectedServices,
},
};
}
};
const getSummaryContent = (
affectedServices: string[],
statusLevel: ServiceStatusLevel,
maxServices: number
): string => {
const serviceCount = affectedServices.length;
if (serviceCount === 1) {
return `1 service is ${statusLevel.toString()}: ${affectedServices[0]}`;
} else if (serviceCount > maxServices) {
const exceedingCount = serviceCount - maxServices;
return `${serviceCount} services are ${statusLevel.toString()}: ${affectedServices
.slice(0, maxServices)
.join(', ')} and ${exceedingCount} other(s)`;
} else {
return `${serviceCount} services are ${statusLevel.toString()}: ${affectedServices.join(', ')}`;
}
};
type StatusPair = [string, ServiceStatus];
const highestLevelSummary = (

View file

@ -73,7 +73,7 @@ describe('PluginStatusService', () => {
});
expect(await serviceDegraded.getDerivedStatus$('a').pipe(first()).toPromise()).toEqual({
level: ServiceStatusLevels.degraded,
summary: '[savedObjects]: savedObjects degraded',
summary: '1 service is degraded: savedObjects',
detail: 'See the status page for more information',
meta: expect.any(Object),
});
@ -84,7 +84,7 @@ describe('PluginStatusService', () => {
});
expect(await serviceCritical.getDerivedStatus$('a').pipe(first()).toPromise()).toEqual({
level: ServiceStatusLevels.critical,
summary: '[elasticsearch]: elasticsearch critical',
summary: '1 service is critical: elasticsearch',
detail: 'See the status page for more information',
meta: expect.any(Object),
});
@ -95,7 +95,7 @@ describe('PluginStatusService', () => {
service.set('a', of({ level: ServiceStatusLevels.degraded, summary: 'a is degraded' }));
expect(await service.getDerivedStatus$('b').pipe(first()).toPromise()).toEqual({
level: ServiceStatusLevels.degraded,
summary: '[2] services are degraded',
summary: '2 services are degraded: savedObjects, a',
detail: 'See the status page for more information',
meta: expect.any(Object),
});
@ -106,7 +106,7 @@ describe('PluginStatusService', () => {
service.set('a', of({ level: ServiceStatusLevels.unavailable, summary: 'a is not working' }));
expect(await service.getDerivedStatus$('b').pipe(first()).toPromise()).toEqual({
level: ServiceStatusLevels.unavailable,
summary: '[a]: a is not working',
summary: '1 service is unavailable: a',
detail: 'See the status page for more information',
meta: expect.any(Object),
});
@ -120,7 +120,7 @@ describe('PluginStatusService', () => {
service.set('a', of({ level: ServiceStatusLevels.unavailable, summary: 'a is not working' }));
expect(await service.getDerivedStatus$('b').pipe(first()).toPromise()).toEqual({
level: ServiceStatusLevels.critical,
summary: '[elasticsearch]: elasticsearch critical',
summary: '1 service is critical: elasticsearch',
detail: 'See the status page for more information',
meta: expect.any(Object),
});
@ -132,7 +132,7 @@ describe('PluginStatusService', () => {
service.set('b', of({ level: ServiceStatusLevels.unavailable, summary: 'b is not working' }));
expect(await service.getDerivedStatus$('c').pipe(first()).toPromise()).toEqual({
level: ServiceStatusLevels.unavailable,
summary: '[b]: b is not working',
summary: '1 service is unavailable: b',
detail: 'See the status page for more information',
meta: expect.any(Object),
});
@ -166,19 +166,19 @@ describe('PluginStatusService', () => {
expect(await serviceDegraded.getAll$().pipe(first()).toPromise()).toEqual({
a: {
level: ServiceStatusLevels.degraded,
summary: '[savedObjects]: savedObjects degraded',
summary: '1 service is degraded: savedObjects',
detail: 'See the status page for more information',
meta: expect.any(Object),
},
b: {
level: ServiceStatusLevels.degraded,
summary: '[savedObjects]: savedObjects degraded',
summary: '1 service is degraded: savedObjects',
detail: 'See the status page for more information',
meta: expect.any(Object),
},
c: {
level: ServiceStatusLevels.degraded,
summary: '[savedObjects]: savedObjects degraded',
summary: '1 service is degraded: savedObjects',
detail: 'See the status page for more information',
meta: expect.any(Object),
},
@ -191,19 +191,19 @@ describe('PluginStatusService', () => {
expect(await serviceCritical.getAll$().pipe(first()).toPromise()).toEqual({
a: {
level: ServiceStatusLevels.critical,
summary: '[elasticsearch]: elasticsearch critical',
summary: '1 service is critical: elasticsearch',
detail: 'See the status page for more information',
meta: expect.any(Object),
},
b: {
level: ServiceStatusLevels.critical,
summary: '[elasticsearch]: elasticsearch critical',
summary: '1 service is critical: elasticsearch',
detail: 'See the status page for more information',
meta: expect.any(Object),
},
c: {
level: ServiceStatusLevels.critical,
summary: '[elasticsearch]: elasticsearch critical',
summary: '1 service is critical: elasticsearch',
detail: 'See the status page for more information',
meta: expect.any(Object),
},
@ -218,13 +218,13 @@ describe('PluginStatusService', () => {
a: { level: ServiceStatusLevels.available, summary: 'a status' }, // a is available depsite savedObjects being degraded
b: {
level: ServiceStatusLevels.degraded,
summary: '[savedObjects]: savedObjects degraded',
summary: '1 service is degraded: savedObjects',
detail: 'See the status page for more information',
meta: expect.any(Object),
},
c: {
level: ServiceStatusLevels.degraded,
summary: '[2] services are degraded',
summary: '2 services are degraded: savedObjects, b',
detail: 'See the status page for more information',
meta: expect.any(Object),
},
@ -298,7 +298,7 @@ describe('PluginStatusService', () => {
a: { level: ServiceStatusLevels.unavailable, summary: 'Status check timed out after 30s' },
b: {
level: ServiceStatusLevels.unavailable,
summary: '[a]: Status check timed out after 30s',
summary: '1 service is unavailable: a',
detail: 'See the status page for more information',
meta: {
affectedServices: ['a'],
@ -341,7 +341,7 @@ describe('PluginStatusService', () => {
a: { level: ServiceStatusLevels.available, summary: 'a status' }, // a is available depsite savedObjects being degraded
b: {
level: ServiceStatusLevels.degraded,
summary: '[savedObjects]: savedObjects degraded',
summary: '1 service is degraded: savedObjects',
detail: 'See the status page for more information',
meta: expect.any(Object),
},

View file

@ -186,7 +186,7 @@ describe('StatusService', () => {
);
expect(await setup.overall$.pipe(first()).toPromise()).toMatchObject({
level: ServiceStatusLevels.degraded,
summary: '[2] services are degraded',
summary: '2 services are degraded: elasticsearch, savedObjects',
});
});
@ -206,15 +206,15 @@ describe('StatusService', () => {
const subResult3 = await setup.overall$.pipe(first()).toPromise();
expect(subResult1).toMatchObject({
level: ServiceStatusLevels.degraded,
summary: '[2] services are degraded',
summary: '2 services are degraded: elasticsearch, savedObjects',
});
expect(subResult2).toMatchObject({
level: ServiceStatusLevels.degraded,
summary: '[2] services are degraded',
summary: '2 services are degraded: elasticsearch, savedObjects',
});
expect(subResult3).toMatchObject({
level: ServiceStatusLevels.degraded,
summary: '[2] services are degraded',
summary: '2 services are degraded: elasticsearch, savedObjects',
});
});
@ -263,7 +263,7 @@ describe('StatusService', () => {
"savedObjects",
],
},
"summary": "[savedObjects]: This is degraded!",
"summary": "1 service is degraded: savedObjects",
},
Object {
"level": available,
@ -313,7 +313,7 @@ describe('StatusService', () => {
"savedObjects",
],
},
"summary": "[savedObjects]: This is degraded!",
"summary": "1 service is degraded: savedObjects",
},
Object {
"level": available,
@ -338,7 +338,7 @@ describe('StatusService', () => {
);
expect(await setup.coreOverall$.pipe(first()).toPromise()).toMatchObject({
level: ServiceStatusLevels.degraded,
summary: '[2] services are degraded',
summary: '2 services are degraded: elasticsearch, savedObjects',
});
});
@ -355,7 +355,7 @@ describe('StatusService', () => {
);
expect(await setup.coreOverall$.pipe(first()).toPromise()).toMatchObject({
level: ServiceStatusLevels.critical,
summary: '[savedObjects]: This is critical!',
summary: '1 service is critical: savedObjects',
});
});
@ -377,15 +377,15 @@ describe('StatusService', () => {
expect(subResult1).toMatchObject({
level: ServiceStatusLevels.degraded,
summary: '[2] services are degraded',
summary: '2 services are degraded: elasticsearch, savedObjects',
});
expect(subResult2).toMatchObject({
level: ServiceStatusLevels.degraded,
summary: '[2] services are degraded',
summary: '2 services are degraded: elasticsearch, savedObjects',
});
expect(subResult3).toMatchObject({
level: ServiceStatusLevels.degraded,
summary: '[2] services are degraded',
summary: '2 services are degraded: elasticsearch, savedObjects',
});
});
@ -434,7 +434,7 @@ describe('StatusService', () => {
"savedObjects",
],
},
"summary": "[savedObjects]: This is degraded!",
"summary": "1 service is degraded: savedObjects",
},
Object {
"level": available,
@ -484,7 +484,7 @@ describe('StatusService', () => {
"savedObjects",
],
},
"summary": "[savedObjects]: This is degraded!",
"summary": "1 service is degraded: savedObjects",
},
Object {
"level": available,