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..3025a5d7 100644 --- a/src/main/kotlin/com/salesforce/revoman/ReVoman.kt +++ b/src/main/kotlin/com/salesforce/revoman/ReVoman.kt @@ -28,11 +28,16 @@ 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.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.squareup.moshi.Moshi import com.squareup.moshi.adapter import io.github.oshai.kotlinlogging.KotlinLogging @@ -65,12 +70,25 @@ object ReVoman { initMoshi( kick.customAdaptersForMarshalling(), kick.customAdaptersFromRequestConfig() + kick.customAdaptersFromResponseConfig(), - kick.typesToIgnoreForMarshalling() - ) + kick.typesToIgnoreForMarshalling(), + ), ) + + val stepExecutionsResult = getStepReportDetails(stepNameToReport) + notifyUsers(stepExecutionsResult) return Rundown(stepNameToReport, pm.environment, kick.haltOnFailureOfTypeExcept()) } + 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) + notifier.notifyUser(slackSummaryReportPayloadBuilder(stepExecutionsResult)) + } + 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..6d89da93 --- /dev/null +++ b/src/main/kotlin/com/salesforce/revoman/notification/NotificationFactory.kt @@ -0,0 +1,12 @@ +package com.salesforce.revoman.notification + +import com.salesforce.revoman.notification.slack.SlackNotifier + +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/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/slack/SlackNotifier.kt b/src/main/kotlin/com/salesforce/revoman/notification/slack/SlackNotifier.kt new file mode 100644 index 00000000..d45c1664 --- /dev/null +++ b/src/main/kotlin/com/salesforce/revoman/notification/slack/SlackNotifier.kt @@ -0,0 +1,26 @@ +package com.salesforce.revoman.notification.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 { + 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}" } + } + + private val logger = KotlinLogging.logger {} +}