diff --git a/.github/workflows/code_review.yml b/.github/workflows/code_review.yml index 3ea4730..8887f2b 100644 --- a/.github/workflows/code_review.yml +++ b/.github/workflows/code_review.yml @@ -18,7 +18,7 @@ on: workflow_dispatch: env: - PROJECT_KEY: # TODO + PROJECT_KEY: pagopa_pagopa-qi-alerts-management-function permissions: id-token: write diff --git a/api-spec/openapi.yaml b/api-spec/openapi.yaml index a971dce..be8cf85 100644 --- a/api-spec/openapi.yaml +++ b/api-spec/openapi.yaml @@ -9,7 +9,7 @@ tags: - name: QI description: Api's for alert ingestion externalDocs: - url: TODO + url: https://pagopa.atlassian.net/wiki/spaces/PPAOP/pages/758874245/DR+Gestione+Alert description: Technical specifications paths: @@ -83,51 +83,103 @@ components: example: 200 QiAlertIngestionRequest: type: object - description: Alert ingestion request + description: Alert ingestion request. This maps grafana webhook specifications https://grafana.com/docs/grafana/latest/alerting/alerting-rules/manage-contact-points/webhook-notifier properties: + receiver: + type: string + description: Name of the webhook + status: + type: string + description: Current status of the alert, firing or resolved + orgId: + type: number + description: ID of the organization related to the payload alerts: type: array + description: Alerts that are triggering items: $ref: '#/components/schemas/AlertInfo' - minItems: 1 - required: - - alerts - AlertInfo: + groupLabels: + $ref: '#/components/schemas/KeyValueMapStringValue' + commonLabels: + $ref: '#/components/schemas/KeyValueMapStringValue' + commonAnnotations: + $ref: '#/components/schemas/KeyValueMapStringValue' + externalURL: + type: string + format: uri + description: External URL to the Grafana instance sending this webhook + version: + type: string + description: Version of the payload + groupKey: + type: string + description: Key that is used for grouping + truncatedAlerts: + type: number + description: Number of alerts that were truncated + title: + type: string + description: Will be deprecated soon + state: + type: string + description: Will be deprecated soon + message: + type: string + description: Will be deprecated soon + AlertInfo: type: object description: Alert information - properties: - code: - type: string - description: Unique code that identifies the alert typology - enum: - - TDP - - TGP - - TSRT - - TVP - - TPNP - - TNSPO - - TPSPO - - NFER - - NBI - owner: - type: string - description: Identifier of subject that caused the alert trigger - threshold: - type: integer - description: Alert metric threshold - value: - type: integer - description: Detected metric threshold that trigger alert - triggerDate: + properties: + status: + type: string + description: Current status of the alert, firing or resolved + labels: + $ref: '#/components/schemas/KeyValueMapStringValue' + annotations: + $ref: '#/components/schemas/KeyValueMapStringValue' + startsAt: type: string format: date-time - description: Timestamp when alert is triggered - required: - - code - - owner - - threshold - - value - - triggerDate + description: Start time of the alert + endsAt: + type: string + format: date-time + description: End time of the alert, default value when not resolved is 0001-01-01T00:00:00Z + values: + $ref: '#/components/schemas/KeyValueMapNumericValue' + generatorURL: + type: string + format: uri + description: URL of the alert rule in the Grafana UI + fingerprint: + type: string + description: The labels fingerprint, alarms with the same labels will have the same fingerprint + silenceURL: + type: string + format: uri + description: URL to silence the alert rule in the Grafana UI + dashboardURL: + type: string + format: uri + description: Will be deprecated soon + panelURL: + type: string + format: uri + description: Will be deprecated soon + imageURL: + type: string + format: uri + description: Will be deprecated soon + KeyValueMapStringValue: + type: object + additionalProperties: + type: string + KeyValueMapNumericValue: + type: object + additionalProperties: + type: number + format: double requestBodies: QiAlertIngestionRequest: required: true diff --git a/pom.xml b/pom.xml index d4d08a9..34a9944 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 it.pagopa - pagopa-qi-alerte-management-function + pagopa-qi-alerts-management-function 0.0.1 jar diff --git a/src/test/java/it/pagopa/qi/alertmanagement/AlertParsingTests.java b/src/test/java/it/pagopa/qi/alertmanagement/AlertParsingTests.java new file mode 100644 index 0000000..ead5cff --- /dev/null +++ b/src/test/java/it/pagopa/qi/alertmanagement/AlertParsingTests.java @@ -0,0 +1,142 @@ +package it.pagopa.qi.alertmanagement; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import it.pagopa.generated.qi.alerts.v1.dto.AlertInfoDto; +import it.pagopa.generated.qi.alerts.v1.dto.QiAlertIngestionRequestDto; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.net.URI; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class AlertParsingTests { + + private final ObjectMapper objectMapper = new ObjectMapper().registerModule(new JavaTimeModule()); + + /** + * Webhook alert example taken from grafana documentation at: ... + */ + private final String webhokAlertExample = "{\n" + + " \"receiver\": \"My Super Webhook\",\n" + + " \"status\": \"firing\",\n" + + " \"orgId\": 1,\n" + + " \"alerts\": [\n" + + " {\n" + + " \"status\": \"firing\",\n" + + " \"labels\": {\n" + + " \"alertname\": \"High memory usage\",\n" + + " \"team\": \"blue\",\n" + + " \"zone\": \"us-1\"\n" + + " },\n" + + " \"annotations\": {\n" + + " \"description\": \"The system has high memory usage\",\n" + + " \"runbook_url\": \"https://myrunbook.com/runbook/1234\",\n" + + " \"summary\": \"This alert was triggered for zone us-1\"\n" + + " },\n" + + " \"startsAt\": \"2021-10-12T09:51:03.157076+02:00\",\n" + + " \"endsAt\": \"0001-01-01T00:00:00Z\",\n" + + " \"generatorURL\": \"https://play.grafana.org/alerting/1afz29v7z/edit\",\n" + + " \"fingerprint\": \"c6eadffa33fcdf37\",\n" + + " \"silenceURL\": \"https://play.grafana.org/alerting/silence/new?alertmanager=grafana&matchers=alertname%3DT2%2Cteam%3Dblue%2Czone%3Dus-1\",\n" + + " \"dashboardURL\": \"\",\n" + + " \"panelURL\": \"\",\n" + + " \"values\": {\n" + + " \"B\": 44.23943737541908,\n" + + " \"C\": 1\n" + + " }\n" + + " },\n" + + " {\n" + + " \"status\": \"firing\",\n" + + " \"labels\": {\n" + + " \"alertname\": \"High CPU usage\",\n" + + " \"team\": \"blue\",\n" + + " \"zone\": \"eu-1\"\n" + + " },\n" + + " \"annotations\": {\n" + + " \"description\": \"The system has high CPU usage\",\n" + + " \"runbook_url\": \"https://myrunbook.com/runbook/1234\",\n" + + " \"summary\": \"This alert was triggered for zone eu-1\"\n" + + " },\n" + + " \"startsAt\": \"2021-10-12T09:56:03.157076+02:00\",\n" + + " \"endsAt\": \"0001-01-01T00:00:00Z\",\n" + + " \"generatorURL\": \"https://play.grafana.org/alerting/d1rdpdv7k/edit\",\n" + + " \"fingerprint\": \"bc97ff14869b13e3\",\n" + + " \"silenceURL\": \"https://play.grafana.org/alerting/silence/new?alertmanager=grafana&matchers=alertname%3DT1%2Cteam%3Dblue%2Czone%3Deu-1\",\n" + + " \"dashboardURL\": \"\",\n" + + " \"panelURL\": \"\",\n" + + " \"values\": {\n" + + " \"B\": 44.23943737541908,\n" + + " \"C\": 1\n" + + " }\n" + + " }\n" + + " ],\n" + + " \"groupLabels\": {},\n" + + " \"commonLabels\": {\n" + + " \"team\": \"blue\"\n" + + " },\n" + + " \"commonAnnotations\": {},\n" + + " \"externalURL\": \"https://play.grafana.org/\",\n" + + " \"version\": \"1\",\n" + + " \"groupKey\": \"{}:{}\",\n" + + " \"truncatedAlerts\": 0,\n" + + " \"title\": \"[FIRING:2] (blue)\",\n" + + " \"state\": \"alerting\",\n" + + " \"message\": \"**Firing**\\n\\nLabels:\\n - alertname = T2\\n - team = blue\\n - zone = us-1\\nAnnotations:\\n - description = This is the alert rule checking the second system\\n - runbook_url = https://myrunbook.com\\n - summary = This is my summary\\nSource: https://play.grafana.org/alerting/1afz29v7z/edit\\nSilence: https://play.grafana.org/alerting/silence/new?alertmanager=grafana&matchers=alertname%3DT2%2Cteam%3Dblue%2Czone%3Dus-1\\n\\nLabels:\\n - alertname = T1\\n - team = blue\\n - zone = eu-1\\nAnnotations:\\nSource: https://play.grafana.org/alerting/d1rdpdv7k/edit\\nSilence: https://play.grafana.org/alerting/silence/new?alertmanager=grafana&matchers=alertname%3DT1%2Cteam%3Dblue%2Czone%3Deu-1\\n\"\n" + + "}\n"; + + @Test + void shouldParseAlertSuccessfully() throws JsonProcessingException { + //test + QiAlertIngestionRequestDto parsedRequest = objectMapper.readValue(webhokAlertExample, QiAlertIngestionRequestDto.class); + QiAlertIngestionRequestDto expectedRequest = new QiAlertIngestionRequestDto() + .receiver("My Super Webhook") + .status("firing") + .orgId(BigDecimal.ONE) + .alerts(List.of( + new AlertInfoDto() + .status("firing") + .labels(Map.of("alertname", "High memory usage", "team", "blue", "zone", "us-1")) + .annotations(Map.of("description", "The system has high memory usage", "runbook_url", "https://myrunbook.com/runbook/1234", "summary", "This alert was triggered for zone us-1")) + .startsAt(OffsetDateTime.parse("2021-10-12T07:51:03.157076Z")) + .endsAt(OffsetDateTime.parse("0001-01-01T00:00Z")) + .values(Map.of("B", 44.23943737541908, "C", 1.0)) + .generatorURL(URI.create("https://play.grafana.org/alerting/1afz29v7z/edit")) + .fingerprint("c6eadffa33fcdf37") + .silenceURL(URI.create("https://play.grafana.org/alerting/silence/new?alertmanager=grafana&matchers=alertname%3DT2%2Cteam%3Dblue%2Czone%3Dus-1")) + .dashboardURL(URI.create("")) + .panelURL(URI.create("")) + .imageURL(null), + new AlertInfoDto() + .status("firing") + .labels(Map.of("alertname", "High CPU usage", "team", "blue", "zone", "eu-1")) + .annotations(Map.of("description", "The system has high CPU usage", "runbook_url", "https://myrunbook.com/runbook/1234", "summary", "This alert was triggered for zone eu-1")) + .startsAt(OffsetDateTime.parse("2021-10-12T07:56:03.157076Z")) + .endsAt(OffsetDateTime.parse("0001-01-01T00:00Z")) + .values(Map.of("B", 44.23943737541908, "C", 1.0)) + .generatorURL(URI.create("https://play.grafana.org/alerting/d1rdpdv7k/edit")) + .fingerprint("bc97ff14869b13e3") + .silenceURL(URI.create("https://play.grafana.org/alerting/silence/new?alertmanager=grafana&matchers=alertname%3DT1%2Cteam%3Dblue%2Czone%3Deu-1")) + .dashboardURL(URI.create("")) + .panelURL(URI.create("")) + .imageURL(null) + )) + .groupLabels(Map.of()) + .commonLabels(Map.of("team", "blue")) + .commonAnnotations(Map.of()) + .externalURL(URI.create("https://play.grafana.org/")) + .version("1") + .groupKey("{}:{}") + .truncatedAlerts(BigDecimal.ZERO) + .title("[FIRING:2] (blue)") + .state("alerting") + .message("**Firing**\n\nLabels:\n - alertname = T2\n - team = blue\n - zone = us-1\nAnnotations:\n - description = This is the alert rule checking the second system\n - runbook_url = https://myrunbook.com\n - summary = This is my summary\nSource: https://play.grafana.org/alerting/1afz29v7z/edit\nSilence: https://play.grafana.org/alerting/silence/new?alertmanager=grafana&matchers=alertname%3DT2%2Cteam%3Dblue%2Czone%3Dus-1\n\nLabels:\n - alertname = T1\n - team = blue\n - zone = eu-1\nAnnotations:\nSource: https://play.grafana.org/alerting/d1rdpdv7k/edit\nSilence: https://play.grafana.org/alerting/silence/new?alertmanager=grafana&matchers=alertname%3DT1%2Cteam%3Dblue%2Czone%3Deu-1\n"); + assertEquals(expectedRequest, parsedRequest); + + } +}