From 1a9c186b78d0bbcdb5394ced31ecbb2233b415e0 Mon Sep 17 00:00:00 2001 From: pshubham Date: Wed, 21 Feb 2024 16:05:42 +0530 Subject: [PATCH 1/3] Initial commit for slack integration --- build.gradle.kts | 4 ++++ .../kotlin/com/salesforce/revoman/ReVoman.kt | 14 +++++++++++++ .../revoman/notification/Notification.kt | 5 +++++ .../notification/NotificationFactory.kt | 10 +++++++++ .../revoman/notification/NotifierTypes.kt | 5 +++++ .../revoman/notification/SlackNotifier.kt | 21 +++++++++++++++++++ 6 files changed, 59 insertions(+) create mode 100644 src/main/kotlin/com/salesforce/revoman/notification/Notification.kt create mode 100644 src/main/kotlin/com/salesforce/revoman/notification/NotificationFactory.kt create mode 100644 src/main/kotlin/com/salesforce/revoman/notification/NotifierTypes.kt create mode 100644 src/main/kotlin/com/salesforce/revoman/notification/SlackNotifier.kt diff --git a/build.gradle.kts b/build.gradle.kts index de207f50..1f6f2cb7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -28,6 +28,10 @@ dependencies { implementation(libs.underscore) implementation(libs.okio.jvm) implementation(libs.spring.beans) + // Slack notification packages + implementation("com.slack.api:slack-api-client:1.38.1") + implementation("com.slack.api:slack-api-model-kotlin-extension:1.38.1") + implementation("com.slack.api:slack-api-client-kotlin-extension:1.38.1") kapt(libs.immutables.value) compileOnly(libs.immutables.builder) compileOnly(libs.immutables.value.annotations) diff --git a/src/main/kotlin/com/salesforce/revoman/ReVoman.kt b/src/main/kotlin/com/salesforce/revoman/ReVoman.kt index 000398c5..b3e91d2d 100644 --- a/src/main/kotlin/com/salesforce/revoman/ReVoman.kt +++ b/src/main/kotlin/com/salesforce/revoman/ReVoman.kt @@ -28,11 +28,15 @@ import com.salesforce.revoman.internal.postman.RegexReplacer import com.salesforce.revoman.internal.postman.initPmEnvironment import com.salesforce.revoman.internal.postman.pm import com.salesforce.revoman.internal.postman.template.Template +import com.salesforce.revoman.notification.NotificationFactory +import com.salesforce.revoman.notification.NotifierTypes import com.salesforce.revoman.output.Rundown import com.salesforce.revoman.output.report.Step import com.salesforce.revoman.output.report.StepReport import com.salesforce.revoman.output.report.StepReport.Companion.toVavr import com.salesforce.revoman.output.report.TxnInfo +import com.slack.api.webhook.Payload +import com.slack.api.webhook.Payload.PayloadBuilder import com.squareup.moshi.Moshi import com.squareup.moshi.adapter import io.github.oshai.kotlinlogging.KotlinLogging @@ -68,9 +72,19 @@ object ReVoman { kick.typesToIgnoreForMarshalling() ) ) + + notifyUsers(kick, stepNameToReport) return Rundown(stepNameToReport, pm.environment, kick.haltOnFailureOfTypeExcept()) } + private fun notifyUsers(kick: Kick, stepNameToReport: List) { + val notifier = NotificationFactory().createNotifier(NotifierTypes.SLACK) + val message = Payload.builder() + .text("Hello from app!!") + .build() + notifier.notifyUser(message) + } + private fun executeStepsSerially( pmStepsFlattened: List, kick: Kick, diff --git a/src/main/kotlin/com/salesforce/revoman/notification/Notification.kt b/src/main/kotlin/com/salesforce/revoman/notification/Notification.kt new file mode 100644 index 00000000..cca8ebdd --- /dev/null +++ b/src/main/kotlin/com/salesforce/revoman/notification/Notification.kt @@ -0,0 +1,5 @@ +package com.salesforce.revoman.notification + +interface Notification { + fun notifyUser(message: T) +} diff --git a/src/main/kotlin/com/salesforce/revoman/notification/NotificationFactory.kt b/src/main/kotlin/com/salesforce/revoman/notification/NotificationFactory.kt new file mode 100644 index 00000000..75dce095 --- /dev/null +++ b/src/main/kotlin/com/salesforce/revoman/notification/NotificationFactory.kt @@ -0,0 +1,10 @@ +package com.salesforce.revoman.notification + +class NotificationFactory { + fun createNotifier(type: NotifierTypes): Notification { + return when (type) { + NotifierTypes.SLACK -> SlackNotifier() + else -> throw IllegalArgumentException("Notification type $type not supported") + } + } +} diff --git a/src/main/kotlin/com/salesforce/revoman/notification/NotifierTypes.kt b/src/main/kotlin/com/salesforce/revoman/notification/NotifierTypes.kt new file mode 100644 index 00000000..90a951c9 --- /dev/null +++ b/src/main/kotlin/com/salesforce/revoman/notification/NotifierTypes.kt @@ -0,0 +1,5 @@ +package com.salesforce.revoman.notification + +enum class NotifierTypes { + SLACK +} diff --git a/src/main/kotlin/com/salesforce/revoman/notification/SlackNotifier.kt b/src/main/kotlin/com/salesforce/revoman/notification/SlackNotifier.kt new file mode 100644 index 00000000..2c834486 --- /dev/null +++ b/src/main/kotlin/com/salesforce/revoman/notification/SlackNotifier.kt @@ -0,0 +1,21 @@ +package com.salesforce.revoman.notification + +import io.github.oshai.kotlinlogging.KotlinLogging +import com.slack.api.Slack; +import com.slack.api.webhook.Payload + +class SlackNotifier : Notification { + companion object { + const val SLACK_WEBHOOK_URL = "https://hooks.slack.com/services/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" + } + + override fun notifyUser(message: T) { + logger.info { "Sending message: ${message.toString()}" } + val slack = Slack.getInstance() + + val response = slack.send(SLACK_WEBHOOK_URL, message as Payload); + logger.info { "Got response: ${response.body}" } + } +} + +private val logger = KotlinLogging.logger {} From da5c4241997f793ccdaeb49dd8fcf3336301b2bc Mon Sep 17 00:00:00 2001 From: pshubham Date: Wed, 21 Feb 2024 17:02:59 +0530 Subject: [PATCH 2/3] Retrieve Slack webhook URL from env variables --- .../com/salesforce/revoman/notification/SlackNotifier.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/com/salesforce/revoman/notification/SlackNotifier.kt b/src/main/kotlin/com/salesforce/revoman/notification/SlackNotifier.kt index 2c834486..1b381e08 100644 --- a/src/main/kotlin/com/salesforce/revoman/notification/SlackNotifier.kt +++ b/src/main/kotlin/com/salesforce/revoman/notification/SlackNotifier.kt @@ -6,13 +6,17 @@ import com.slack.api.webhook.Payload class SlackNotifier : Notification { companion object { - const val SLACK_WEBHOOK_URL = "https://hooks.slack.com/services/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" + val SLACK_WEBHOOK_URL: String = System.getenv("SLACK_WEBHOOK_URL"); } override fun notifyUser(message: T) { logger.info { "Sending message: ${message.toString()}" } val slack = Slack.getInstance() - + if (SLACK_WEBHOOK_URL.isBlank()) { + logger.error { "Failed to send message to Slack webhook $SLACK_WEBHOOK_URL" } + return + } + val response = slack.send(SLACK_WEBHOOK_URL, message as Payload); logger.info { "Got response: ${response.body}" } } From 2df800b48cb346bd8ee755a25ba47a4a528103d1 Mon Sep 17 00:00:00 2001 From: pshubham Date: Fri, 23 Feb 2024 14:19:27 +0530 Subject: [PATCH 3/3] Formatted slack payload with step execution summary report --- .../kotlin/com/salesforce/revoman/ReVoman.kt | 22 ++++++---- .../notification/NotificationFactory.kt | 2 + .../revoman/notification/PayloadBuilder.kt | 43 +++++++++++++++++++ .../reports/StepExecutionsResult.kt | 3 ++ .../notification/{ => slack}/SlackNotifier.kt | 11 ++--- 5 files changed, 67 insertions(+), 14 deletions(-) create mode 100644 src/main/kotlin/com/salesforce/revoman/notification/PayloadBuilder.kt create mode 100644 src/main/kotlin/com/salesforce/revoman/notification/reports/StepExecutionsResult.kt rename src/main/kotlin/com/salesforce/revoman/notification/{ => slack}/SlackNotifier.kt (78%) diff --git a/src/main/kotlin/com/salesforce/revoman/ReVoman.kt b/src/main/kotlin/com/salesforce/revoman/ReVoman.kt index b3e91d2d..3025a5d7 100644 --- a/src/main/kotlin/com/salesforce/revoman/ReVoman.kt +++ b/src/main/kotlin/com/salesforce/revoman/ReVoman.kt @@ -30,13 +30,14 @@ import com.salesforce.revoman.internal.postman.pm import com.salesforce.revoman.internal.postman.template.Template import com.salesforce.revoman.notification.NotificationFactory import com.salesforce.revoman.notification.NotifierTypes +import com.salesforce.revoman.notification.reports.StepExecutionsResult +import com.salesforce.revoman.notification.PayloadBuilder.slackSummaryReportPayloadBuilder import com.salesforce.revoman.output.Rundown import com.salesforce.revoman.output.report.Step import com.salesforce.revoman.output.report.StepReport import com.salesforce.revoman.output.report.StepReport.Companion.toVavr import com.salesforce.revoman.output.report.TxnInfo import com.slack.api.webhook.Payload -import com.slack.api.webhook.Payload.PayloadBuilder import com.squareup.moshi.Moshi import com.squareup.moshi.adapter import io.github.oshai.kotlinlogging.KotlinLogging @@ -69,20 +70,23 @@ object ReVoman { initMoshi( kick.customAdaptersForMarshalling(), kick.customAdaptersFromRequestConfig() + kick.customAdaptersFromResponseConfig(), - kick.typesToIgnoreForMarshalling() - ) + kick.typesToIgnoreForMarshalling(), + ), ) - notifyUsers(kick, stepNameToReport) + val stepExecutionsResult = getStepReportDetails(stepNameToReport) + notifyUsers(stepExecutionsResult) return Rundown(stepNameToReport, pm.environment, kick.haltOnFailureOfTypeExcept()) } - private fun notifyUsers(kick: Kick, stepNameToReport: List) { + private fun getStepReportDetails(stepNameToReport: List): StepExecutionsResult { + val summaryResult = StepExecutionsResult(stepNameToReport.size, stepNameToReport.count { it.isSuccessful }, stepNameToReport.count { !it.isSuccessful }, "https://www.google.com") + return summaryResult + } + + private fun notifyUsers(stepExecutionsResult: StepExecutionsResult) { val notifier = NotificationFactory().createNotifier(NotifierTypes.SLACK) - val message = Payload.builder() - .text("Hello from app!!") - .build() - notifier.notifyUser(message) + notifier.notifyUser(slackSummaryReportPayloadBuilder(stepExecutionsResult)) } private fun executeStepsSerially( diff --git a/src/main/kotlin/com/salesforce/revoman/notification/NotificationFactory.kt b/src/main/kotlin/com/salesforce/revoman/notification/NotificationFactory.kt index 75dce095..6d89da93 100644 --- a/src/main/kotlin/com/salesforce/revoman/notification/NotificationFactory.kt +++ b/src/main/kotlin/com/salesforce/revoman/notification/NotificationFactory.kt @@ -1,5 +1,7 @@ package com.salesforce.revoman.notification +import com.salesforce.revoman.notification.slack.SlackNotifier + class NotificationFactory { fun createNotifier(type: NotifierTypes): Notification { return when (type) { diff --git a/src/main/kotlin/com/salesforce/revoman/notification/PayloadBuilder.kt b/src/main/kotlin/com/salesforce/revoman/notification/PayloadBuilder.kt new file mode 100644 index 00000000..9fc555f6 --- /dev/null +++ b/src/main/kotlin/com/salesforce/revoman/notification/PayloadBuilder.kt @@ -0,0 +1,43 @@ +package com.salesforce.revoman.notification + +import com.salesforce.revoman.notification.reports.StepExecutionsResult +import com.slack.api.model.block.Blocks +import com.slack.api.model.block.HeaderBlock +import com.slack.api.model.block.SectionBlock +import com.slack.api.model.block.composition.MarkdownTextObject +import com.slack.api.model.block.composition.PlainTextObject +import com.slack.api.webhook.Payload + +object PayloadBuilder { + fun slackSummaryReportPayloadBuilder(stepExecutionsResult: StepExecutionsResult): Payload { + val blocks = listOf( + HeaderBlock.builder() + .text( + PlainTextObject.builder() + .text(":clipboard: Steps execution report") + .emoji(true) + .build() + ) + .build(), + Blocks.divider(), + SectionBlock.builder() + .text( + MarkdownTextObject.builder() + .text("``` ---------------------- ---------- ---------- \n| Total Steps Executed | Success | Failure |\n ====================== ========== ========== \n| ${stepExecutionsResult.totalSteps} | ${stepExecutionsResult.successStepsCount} | ${stepExecutionsResult.failedStepsCount} |\n ---------------------- ---------- ---------- ```") + .build() + ) + .build(), + SectionBlock.builder() + .text( + MarkdownTextObject.builder() + .text("For detailed report please click <${stepExecutionsResult.detailedReportUrl}|*here*>.") + .build() + ) + .build() + ) + + return Payload.builder() + .blocks(blocks) + .build() + } +} diff --git a/src/main/kotlin/com/salesforce/revoman/notification/reports/StepExecutionsResult.kt b/src/main/kotlin/com/salesforce/revoman/notification/reports/StepExecutionsResult.kt new file mode 100644 index 00000000..6f1032b2 --- /dev/null +++ b/src/main/kotlin/com/salesforce/revoman/notification/reports/StepExecutionsResult.kt @@ -0,0 +1,3 @@ +package com.salesforce.revoman.notification.reports + +data class StepExecutionsResult(val totalSteps: Int, val successStepsCount: Int, val failedStepsCount: Int, val detailedReportUrl: String) diff --git a/src/main/kotlin/com/salesforce/revoman/notification/SlackNotifier.kt b/src/main/kotlin/com/salesforce/revoman/notification/slack/SlackNotifier.kt similarity index 78% rename from src/main/kotlin/com/salesforce/revoman/notification/SlackNotifier.kt rename to src/main/kotlin/com/salesforce/revoman/notification/slack/SlackNotifier.kt index 1b381e08..d45c1664 100644 --- a/src/main/kotlin/com/salesforce/revoman/notification/SlackNotifier.kt +++ b/src/main/kotlin/com/salesforce/revoman/notification/slack/SlackNotifier.kt @@ -1,8 +1,9 @@ -package com.salesforce.revoman.notification +package com.salesforce.revoman.notification.slack -import io.github.oshai.kotlinlogging.KotlinLogging -import com.slack.api.Slack; +import com.salesforce.revoman.notification.Notification +import com.slack.api.Slack import com.slack.api.webhook.Payload +import io.github.oshai.kotlinlogging.KotlinLogging class SlackNotifier : Notification { companion object { @@ -20,6 +21,6 @@ class SlackNotifier : Notification { val response = slack.send(SLACK_WEBHOOK_URL, message as Payload); logger.info { "Got response: ${response.body}" } } -} -private val logger = KotlinLogging.logger {} + private val logger = KotlinLogging.logger {} +}