Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce Slack message sending #68

Merged
merged 12 commits into from
Aug 30, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Introduce Slack message sending
KjellBerlin committed Aug 30, 2024
commit b5fa38ccfbefb865c5ce8653311f5adc90659b30
4 changes: 4 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -49,6 +49,10 @@ dependencies {
// Mollie
implementation("be.woutschoovaerts:mollie:4.3.0")

// Slack
implementation("com.slack.api:slack-api-model-kotlin-extension:1.42.0")
implementation("com.slack.api:slack-api-client-kotlin-extension:1.42.0")

testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("io.mockk:mockk:1.13.11")
}
8 changes: 8 additions & 0 deletions src/main/kotlin/com/carbonara/core/address/Address.kt
Original file line number Diff line number Diff line change
@@ -12,5 +12,13 @@ data class Address(
fun addressPropertiesComplete(): Boolean {
return street != null && streetNumber != null && postCode != null && city != null && country != null
}

override fun toString(): String {
return "$street $streetNumber, $postCode $city"
}

fun createGoogleMapsLink(): String {
return "https://www.google.com/maps/place/?q=place_id:$googlePlaceId"
}
}

12 changes: 10 additions & 2 deletions src/main/kotlin/com/carbonara/core/order/OrderService.kt
Original file line number Diff line number Diff line change
@@ -6,6 +6,7 @@ import com.carbonara.core.payment.MolliePaymentService
import com.carbonara.core.payment.PaymentException
import com.carbonara.core.product.ProductDao
import com.carbonara.core.product.ProductService
import com.carbonara.core.slack.SlackMessageService
import kotlinx.coroutines.reactor.awaitSingleOrNull
import mu.KotlinLogging
import org.bson.types.ObjectId
@@ -16,7 +17,8 @@ import java.time.OffsetDateTime
class OrderService(
private val orderRepository: OrderRepository,
private val productService: ProductService,
private val molliePaymentService: MolliePaymentService
private val molliePaymentService: MolliePaymentService,
private val slackMessageService: SlackMessageService
) {

suspend fun createOrder(
@@ -96,7 +98,13 @@ class OrderService(
if (order.paymentDetails.internalPaymentStatus != InternalPaymentStatus.PAID) {
updateOrderToPaid(order, paymentStatus)

// TODO: trigger delivery
slackMessageService.sendNewOrderMessage(
customerName = order.userName,
orderId = order.orderId.toString(),
address = order.deliveryAddress.toString(),
googleMapsLink = order.deliveryAddress.createGoogleMapsLink(),
productNames = order.products.map { it.productName }
)
}
}

3 changes: 3 additions & 0 deletions src/main/kotlin/com/carbonara/core/slack/SlackException.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.carbonara.core.slack

class SlackException(message: String) : RuntimeException(message)
71 changes: 71 additions & 0 deletions src/main/kotlin/com/carbonara/core/slack/SlackMessageService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.carbonara.core.slack

import com.slack.api.Slack
import com.slack.api.methods.kotlin_extension.request.chat.blocks
import com.slack.api.model.block.composition.BlockCompositions.plainText
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Service

@Service
class SlackMessageService {

@Value("\${slack.token}")
lateinit var slackToken: String

@Value("\${slack.channel}")
lateinit var slackChannel: String

fun sendNewOrderMessage(
customerName: String,
orderId: String,
address: String,
googleMapsLink: String,
productNames: List<String>
) {
val slack = Slack.getInstance()
val token = System.getenv(slackToken)
val response = slack.methods(token).chatPostMessage { req -> req
.channel(slackChannel)
.blocks {
header {
plainText("New order")
}
section {
markdownText("*Customer Name:*\n$customerName")
markdownText("*OrderId:*\n$orderId")
}
section {
markdownText("*Address:*\n$address\n$googleMapsLink")
markdownText("*Products:*\n${productNames}")
}
actions {
button {
text("ACCEPT", emoji = true)
style("primary")
value("processing_order")
}
button {
text("DELIVERY_IN_PROGRESS", emoji = true)
style("primary")
value("delivery_in_progress")
}
button {
text("DELIVERED", emoji = true)
style("primary")
value("delivered")
}
button {
text("CANCELLED", emoji = true)
style("danger")
value("cancelled")
}
}
}
}

// TODO: test this
if (!response.isOk) {
throw SlackException("Failed to send slack message for orderId: $orderId")
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.carbonara.core.delivery
package com.carbonara.core.slack

import com.carbonara.core.order.OrderService
import mu.KotlinLogging
@@ -12,6 +12,8 @@ class SlackDeliveryWebhookController(

// Potential dos attack endpoint, introduce rate limiting

// TODO: Handle webhook

@PostMapping("/slack-delivery-status", "application/x-www-form-urlencoded")
suspend fun handleSlackWebhook(requestBody: SlackWebhookRequestBody) {
log.info("--Start Slack--")
3 changes: 3 additions & 0 deletions src/main/resources/application-staging.properties
Original file line number Diff line number Diff line change
@@ -16,3 +16,6 @@ mollie.redirectUrl=carbonara://order-status
mollie.paymentWebhookUrl=https://carbonara-core-mkvkriomda-ew.a.run.app/mollie-payment-status

google.apiKey=${sm://projects/897213585789/secrets/google-apiKey}

slack.token=${sm://projects/897213585789/secrets/slack-token}
slack.channel=C07GLAASC5P
27 changes: 22 additions & 5 deletions src/test/kotlin/com/carbonara/core/address/AddressTest.kt
Original file line number Diff line number Diff line change
@@ -2,19 +2,36 @@ package com.carbonara.core.address

import com.carbonara.core.serviceAvailability.ServiceAvailabilityServiceTest.Companion.GOOGLE_PLACE_ID
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Nested
import org.junit.jupiter.api.Test

class AddressTest {

@Nested
inner class AddressPropertiesCompleteTests {

@Test
fun `Happy case - address complete`() {
assertEquals(true, ADDRESS.addressPropertiesComplete())
}

@Test
fun `Happy case - address incomplete`() {
val address = ADDRESS.copy(streetNumber = null)
assertEquals(false, address.addressPropertiesComplete())
}
}

@Test
fun `Happy case - address complete`() {
assertEquals(true, ADDRESS.addressPropertiesComplete())
fun `Happy case - address to string`() {
val result = ADDRESS.toString()
assertEquals("Baker Street 221b, 12345 London", result)
}

@Test
fun `Happy case - address incomplete`() {
val address = ADDRESS.copy(streetNumber = null)
assertEquals(false, address.addressPropertiesComplete())
fun `Happy case - googleMapsLink`() {
val result = ADDRESS.createGoogleMapsLink()
assertEquals("https://www.google.com/maps/place/?q=place_id:$GOOGLE_PLACE_ID", result)
}

companion object {
6 changes: 5 additions & 1 deletion src/test/kotlin/com/carbonara/core/order/OrderServiceTest.kt
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ import com.carbonara.core.payment.MolliePaymentService
import com.carbonara.core.payment.PaymentException
import com.carbonara.core.product.ProductDao
import com.carbonara.core.product.ProductService
import com.carbonara.core.slack.SlackMessageService
import io.mockk.coEvery
import io.mockk.coVerify
import io.mockk.every
@@ -31,16 +32,19 @@ class OrderServiceTest {
private lateinit var orderRepository: OrderRepository
private lateinit var productService: ProductService
private lateinit var molliePaymentService: MolliePaymentService
private lateinit var slackMessageService: SlackMessageService

@BeforeEach
fun init() {
orderRepository = mockk()
productService = mockk()
molliePaymentService = mockk()
orderService = OrderService(orderRepository, productService, molliePaymentService)
slackMessageService = mockk()
orderService = OrderService(orderRepository, productService, molliePaymentService, slackMessageService)

mockkStatic(OffsetDateTime::class)
every { OffsetDateTime.now() } returns TIME
every { slackMessageService.sendNewOrderMessage(any(), any(), any(), any(), any()) } returns Unit
}

@Nested