diff --git a/bridge/client/app/_components/ktb-evaluation-details/ktb-evaluation-details.component.ts b/bridge/client/app/_components/ktb-evaluation-details/ktb-evaluation-details.component.ts index b75de316f0..5e7840721d 100644 --- a/bridge/client/app/_components/ktb-evaluation-details/ktb-evaluation-details.component.ts +++ b/bridge/client/app/_components/ktb-evaluation-details/ktb-evaluation-details.component.ts @@ -21,6 +21,7 @@ export class KtbEvaluationDetailsComponent { [ResultTypes.PASSED]: 'recovered', [ResultTypes.WARNING]: 'warning', [ResultTypes.FAILED]: 'error', + [ResultTypes.INFO]: 'info', }; public selectedEvaluation?: Trace; private _evaluationData: IEvaluationSelectionData = { shouldSelect: true }; diff --git a/bridge/client/app/_components/ktb-sli-breakdown/ktb-sli-breakdown.component.html b/bridge/client/app/_components/ktb-sli-breakdown/ktb-sli-breakdown.component.html index 83ebf6f60a..267e440e15 100644 --- a/bridge/client/app/_components/ktb-sli-breakdown/ktb-sli-breakdown.component.html +++ b/bridge/client/app/_components/ktb-sli-breakdown/ktb-sli-breakdown.component.html @@ -153,9 +153,10 @@ [class.warning]="result.result === ResultTypes.WARNING" class="underline-info" > - {{ result.score | truncateNumber: 2 }}/{{ - (maximumAvailableWeight !== 0 ? result.weight / maximumAvailableWeight : 0) * 100 | truncateNumber: 2 - }} + {{ result.score | truncateNumber: 2 }}/{{ getRelativeSliWeight(result) | truncateNumber: 2 }} + - @@ -176,6 +177,13 @@ > The score was halved because the result is warning

+

+ No score since the SLI is of type info. +

diff --git a/bridge/client/app/_components/ktb-sli-breakdown/ktb-sli-breakdown.component.ts b/bridge/client/app/_components/ktb-sli-breakdown/ktb-sli-breakdown.component.ts index 84f18ac7fe..84bfec0cf6 100644 --- a/bridge/client/app/_components/ktb-sli-breakdown/ktb-sli-breakdown.component.ts +++ b/bridge/client/app/_components/ktb-sli-breakdown/ktb-sli-breakdown.component.ts @@ -125,7 +125,9 @@ export class KtbSliBreakdownComponent { return; } // max reachable weight is actually the max reachable score. max weight = 100% score - this.maximumAvailableWeight = sliResults.reduce((acc, result) => acc + result.weight, 0); + this.maximumAvailableWeight = sliResults + .filter((sli) => sli.result !== ResultTypes.INFO) + .reduce((acc, result) => acc + result.weight, 0); this.tableEntries.data = sliResults; this.updateSort(); } @@ -232,4 +234,8 @@ export class KtbSliBreakdownComponent { result.expanded = !result.expanded; } } + + public getRelativeSliWeight(result: SliResult): number { + return this.maximumAvailableWeight !== 0 ? (result.weight / this.maximumAvailableWeight) * 100 : 0; + } } diff --git a/bridge/cypress/fixtures/get.sockshop.service.carts.info-evaluations.mock.json b/bridge/cypress/fixtures/get.sockshop.service.carts.info-evaluations.mock.json new file mode 100644 index 0000000000..a3a3440785 --- /dev/null +++ b/bridge/cypress/fixtures/get.sockshop.service.carts.info-evaluations.mock.json @@ -0,0 +1,98 @@ +{ + "events": [ + { + "data": { + "evaluation": { + "comparedEvents": ["f5c5026d-f2cf-45a9-be75-da4056ccda49"], + "indicatorResults": [ + { + "displayName": "Response time P95", + "keySli": false, + "passTargets": [ + { + "criteria": "<=+15%", + "targetValue": 34.94786254083712, + "violated": false + }, + { + "criteria": "<200", + "targetValue": 200, + "violated": false + } + ], + "score": 1, + "status": "pass", + "value": { + "comparedValue": 30.38944568768445, + "metric": "response_time_p95", + "success": true, + "value": 33.90626490067223 + }, + "warningTargets": [ + { + "criteria": "<=400", + "targetValue": 400, + "violated": false + } + ] + }, + { + "displayName": "Error Rate", + "keySli": false, + "passTargets": null, + "score": 0, + "status": "info", + "value": { + "comparedValue": 0, + "metric": "error_rate", + "success": true, + "value": 0 + }, + "warningTargets": null + }, + { + "displayName": "Throughput", + "keySli": false, + "passTargets": null, + "score": 0, + "status": "info", + "value": { + "comparedValue": 0, + "metric": "throughput", + "success": true, + "value": 1814 + }, + "warningTargets": null + } + ], + "result": "pass", + "score": 100, + "sloFileContent": "Ci0tLQpzcGVjX3ZlcnNpb246ICIxLjAiCmNvbXBhcmlzb246CiAgYWdncmVnYXRlX2Z1bmN0aW9uOiAiYXZnIgogIGNvbXBhcmVfd2l0aDogInNpbmdsZV9yZXN1bHQiCiAgaW5jbHVkZV9yZXN1bHRfd2l0aF9zY29yZTogInBhc3MiCiAgbnVtYmVyX29mX2NvbXBhcmlzb25fcmVzdWx0czogMQpmaWx0ZXI6Cm9iamVjdGl2ZXM6CiAgLSBzbGk6ICJyZXNwb25zZV90aW1lX3A5NSIKICAgIGRpc3BsYXlOYW1lOiAiUmVzcG9uc2UgdGltZSBQOTUiCiAgICBrZXlfc2xpOiBmYWxzZQogICAgcGFzczogICAgICAgICAgICAgIyBwYXNzIGlmIChyZWxhdGl2ZSBjaGFuZ2UgPD0gMTAlIEFORCBhYnNvbHV0ZSB2YWx1ZSBpcyA8IDYwMG1zKQogICAgICAtIGNyaXRlcmlhOgogICAgICAgICAgLSAiPD0rMTUlIiAgIyByZWxhdGl2ZSB2YWx1ZXMgcmVxdWlyZSBhIHByZWZpeGVkIHNpZ24gKHBsdXMgb3IgbWludXMpCiAgICAgICAgICAtICI8MjAwIiAgICAjIGFic29sdXRlIHZhbHVlcyBvbmx5IHJlcXVpcmUgYSBsb2dpY2FsIG9wZXJhdG9yCiAgICB3YXJuaW5nOiAgICAgICAgICAjIGlmIHRoZSByZXNwb25zZSB0aW1lIGlzIGJlbG93IDgwMG1zLCB0aGUgcmVzdWx0IHNob3VsZCBiZSBhIHdhcm5pbmcKICAgICAgLSBjcml0ZXJpYToKICAgICAgICAgIC0gIjw9NDAwIgogICAgd2VpZ2h0OiAxCiAgLSBzbGk6ICJlcnJvcl9yYXRlIgogICAgZGlzcGxheU5hbWU6ICJFcnJvciBSYXRlIgogIC0gc2xpOiAidGhyb3VnaHB1dCIKICAgIGRpc3BsYXlOYW1lOiAiVGhyb3VnaHB1dCIKdG90YWxfc2NvcmU6CiAgcGFzczogIjkwJSIKICB3YXJuaW5nOiAiNzUlIgo=", + "timeEnd": "2022-10-27T07:00:00.000Z", + "timeStart": "2022-10-27T06:00:00.000Z" + }, + "labels": { + "DtCreds": "dynatrace-zrp63" + }, + "project": "tempberry", + "result": "pass", + "service": "tempberry-backend", + "stage": "production", + "status": "succeeded", + "temporaryData": { + "distributor": { + "subscriptionID": "" + } + } + }, + "id": "1500b971-bfc3-4e20-8dc1-a624e0faf963", + "source": "lighthouse-service", + "specversion": "1.0", + "time": "2022-02-09T09:30:07.865Z", + "type": "sh.keptn.event.evaluation.finished", + "shkeptncontext": "da740469-9920-4e0c-b304-0fd4b18d17c2", + "shkeptnspecversion": "0.2.3", + "triggeredid": "09f53f15-c89d-4a4a-82cc-138985696172" + } + ] +} diff --git a/bridge/cypress/integration/sli-breakdown.spec.ts b/bridge/cypress/integration/sli-breakdown.spec.ts index 2f53367bd0..f55cded71c 100644 --- a/bridge/cypress/integration/sli-breakdown.spec.ts +++ b/bridge/cypress/integration/sli-breakdown.spec.ts @@ -117,12 +117,10 @@ describe('sli-breakdown', () => { .verifySliBreakdownSorting(1, 'descending', 'request_throughput', 'http_response_time_seconds_main_page_sum'); // sort score asc - servicesPage.clickSliBreakdownHeader('Score').verifySliBreakdownSorting(7, 'ascending', ' 0/33.33 ', ' 0/33.33 '); + servicesPage.clickSliBreakdownHeader('Score').verifySliBreakdownSorting(7, 'ascending', '0/33.33', '0/33.33'); // sort score desc - servicesPage - .clickSliBreakdownHeader('Score') - .verifySliBreakdownSorting(7, 'descending', ' 33.99/33.33 ', ' 0/33.33 '); + servicesPage.clickSliBreakdownHeader('Score').verifySliBreakdownSorting(7, 'descending', '33.99/33.33', '0/33.33'); }); describe('score overlay', () => { @@ -174,3 +172,19 @@ describe('sli-breakdown with fallback api call', () => { servicesPage.assertSliBreakdownLoading(true); }); }); + +describe('sli-breakdown with info sli', () => { + const servicesPage = new ServicesPage(); + const heatmapPage = new HeatmapComponentPage(); + + beforeEach(() => { + servicesPage.interceptAll().interceptWithInfoSli(); + }); + + it("Info SLIs shouldn't contribute to weight", () => { + servicesPage.visitServicePage('sockshop').selectService('carts', 'v0.1.2'); + servicesPage.assertSliScoreColumn('Response time P95', 100, 100); + servicesPage.assertSliScoreColumn('Error Rate', undefined, undefined, true); + servicesPage.assertSliScoreColumn('Throughput', undefined, undefined, true); + }); +}); diff --git a/bridge/cypress/support/intercept.ts b/bridge/cypress/support/intercept.ts index 579e6f29a6..50b4bc5145 100644 --- a/bridge/cypress/support/intercept.ts +++ b/bridge/cypress/support/intercept.ts @@ -404,7 +404,7 @@ export function interceptD3(): void { export function interceptHeatmapComponent(): void { interceptD3(); - cy.intercept('/api/v1/metadata', { fixture: 'metadata.mock' }); + cy.intercept('/api/v1/metadata', { fixture: 'metadata.mock' }).as('metadata'); cy.intercept('/api/hasUnreadUniformRegistrationLogs', { body: false }); cy.intercept('/api/controlPlane/v1/project?disableUpstreamSync=true&pageSize=50', { fixture: 'projects.mock' }); cy.intercept('GET', '/api/project/sockshop/serviceStates', { diff --git a/bridge/cypress/support/pageobjects/ServicesPage.ts b/bridge/cypress/support/pageobjects/ServicesPage.ts index 3097498196..01effae352 100644 --- a/bridge/cypress/support/pageobjects/ServicesPage.ts +++ b/bridge/cypress/support/pageobjects/ServicesPage.ts @@ -2,6 +2,7 @@ import { SliResult } from '../../../client/app/_interfaces/sli-result'; import { + interceptHeatmapComponent, interceptProjectBoard, interceptServicesPage, interceptServicesPageWithLoadingSequences, @@ -62,6 +63,15 @@ class ServicesPage { return this; } + public interceptWithInfoSli(): this { + interceptHeatmapComponent(); + cy.intercept('GET', 'api/mongodb-datastore/event/type/sh.keptn.event.evaluation.finished?*', { + statusCode: 200, + fixture: 'get.sockshop.service.carts.info-evaluations.mock.json', + }).as('heatmapEvaluations'); + return this; + } + public visitServicePage(projectName: string): this { cy.visit(`/project/${projectName}/service`).wait('@metadata'); return this; @@ -232,8 +242,8 @@ class ServicesPage { return this; } - public assertSliScoreColumn(sliName: string, score: number, availableScore: number): this { - return this.assertSliColumnText(sliName, 'score', ` ${score}/${availableScore} `); + public assertSliScoreColumn(sliName: string, score?: number, availableScore?: number, isInfo?: boolean): this { + return this.assertSliColumnText(sliName, 'score', isInfo ? '-' : `${score}/${availableScore}`); } private showSliScoreOverlay(sliName: string): this { diff --git a/bridge/shared/models/result-types.ts b/bridge/shared/models/result-types.ts index 8460942576..daf234f4fb 100644 --- a/bridge/shared/models/result-types.ts +++ b/bridge/shared/models/result-types.ts @@ -2,4 +2,5 @@ export enum ResultTypes { PASSED = 'pass', WARNING = 'warning', FAILED = 'fail', + INFO = 'info', }