diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9e1f82f..b22c143 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -41,6 +41,16 @@ jobs: echo "${{ secrets.MONGO }}" | base64 --decode > ./mongo.yml working-directory: ./dmforu-infrastructure/storage/mongo/src/main/resources + - name: Create SQS resources directory + run: | + mkdir -p ./dmforu-infrastructure/sqs/src/main/resources + + - name: make sqs.yml + run: | + touch ./sqs.yml + echo "${{ secrets.SQS }}" | base64 --decode > ./sqs.yml + working-directory: ./dmforu-infrastructure/sqs/src/main/resources + - name: Create Firebase key resources directory run: | mkdir -p ./dmforu-infrastructure/fcm/src/main/resources/key diff --git a/.gitignore b/.gitignore index bc993a8..bd151f8 100644 --- a/.gitignore +++ b/.gitignore @@ -42,4 +42,5 @@ out/ ### Configuration Settings ### **/mongo.yml **/mysql.yml +**/sqs.yml **/fire-base-key.json \ No newline at end of file diff --git a/dmforu-admin/build.gradle.kts b/dmforu-admin/build.gradle.kts index c0dff20..c5578e8 100644 --- a/dmforu-admin/build.gradle.kts +++ b/dmforu-admin/build.gradle.kts @@ -13,7 +13,7 @@ dependencies { implementation("org.springframework.boot:spring-boot-starter-web") - runtimeOnly(project(":dmforu-infrastructure:fcm")) + runtimeOnly(project(":dmforu-infrastructure:sqs")) runtimeOnly(project(":dmforu-infrastructure:storage:mysql")) runtimeOnly(project(":dmforu-infrastructure:storage:mongo")) diff --git a/dmforu-admin/src/main/kotlin/com/dmforu/admin/AdminApplication.kt b/dmforu-admin/src/main/kotlin/com/dmforu/admin/AdminApplication.kt index ca6d4be..10ecf15 100644 --- a/dmforu-admin/src/main/kotlin/com/dmforu/admin/AdminApplication.kt +++ b/dmforu-admin/src/main/kotlin/com/dmforu/admin/AdminApplication.kt @@ -8,7 +8,7 @@ import java.util.* @SpringBootApplication( scanBasePackages = [ "com.dmforu.admin", - "com.dmforu.fcm", + "com.dmforu.sqs", "com.dmforu.storage.db.mongo", "com.dmforu.storage.db.mysql" ] diff --git a/dmforu-admin/src/main/resources/application.yml b/dmforu-admin/src/main/resources/application.yml index ec5181b..8c832cb 100644 --- a/dmforu-admin/src/main/resources/application.yml +++ b/dmforu-admin/src/main/resources/application.yml @@ -7,6 +7,7 @@ spring: - mysql.yml - mongo.yml - monitoring.yml + - sqs.yml web.resources.add-mappings: false spring.lifecycle.timeout-per-shutdown-phase: 30s diff --git a/dmforu-infrastructure/sqs/build.gradle.kts b/dmforu-infrastructure/sqs/build.gradle.kts new file mode 100644 index 0000000..79719ae --- /dev/null +++ b/dmforu-infrastructure/sqs/build.gradle.kts @@ -0,0 +1,9 @@ +dependencies { + compileOnly(project(":dmforu-domain")) + + implementation(enforcedPlatform("software.amazon.awssdk:bom:2.21.20")) + implementation("software.amazon.awssdk:sqs") + + testImplementation(project(":dmforu-domain")) + testImplementation("org.mockito.kotlin:mockito-kotlin:4.1.0") +} \ No newline at end of file diff --git a/dmforu-infrastructure/sqs/src/main/kotlin/com/dmforu/sqs/AWSConfig.kt b/dmforu-infrastructure/sqs/src/main/kotlin/com/dmforu/sqs/AWSConfig.kt new file mode 100644 index 0000000..407e6ef --- /dev/null +++ b/dmforu-infrastructure/sqs/src/main/kotlin/com/dmforu/sqs/AWSConfig.kt @@ -0,0 +1,39 @@ +package com.dmforu.sqs + +import org.springframework.beans.factory.annotation.Value +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import software.amazon.awssdk.auth.credentials.AwsBasicCredentials +import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider +import software.amazon.awssdk.regions.Region +import software.amazon.awssdk.services.sqs.SqsAsyncClient + +@Configuration +class AWSConfig( + @Value("\${cloud.aws.credentials.access-key}") + val accessKey: String, + + @Value("\${cloud.aws.credentials.secret-key}") + val secretKey: String, + + @Value("\${cloud.aws.sqs.queue.url}") + val queueUrl: String, + + @Value("\${cloud.aws.sqs.queue.message-delay-seconds}") + val messageDelaySecs: Int, +) { + + @Bean + fun sqsAsyncClient(): SqsAsyncClient { + return SqsAsyncClient.builder() + .region(Region.AP_NORTHEAST_2) + .credentialsProvider(createAwsCredentialsProvider()) + .build() + } + + private fun createAwsCredentialsProvider(): StaticCredentialsProvider { + val basicAwsCredentials = AwsBasicCredentials.create(accessKey, secretKey) + + return StaticCredentialsProvider.create(basicAwsCredentials) + } +} \ No newline at end of file diff --git a/dmforu-infrastructure/sqs/src/main/kotlin/com/dmforu/sqs/SqsMessageMapper.kt b/dmforu-infrastructure/sqs/src/main/kotlin/com/dmforu/sqs/SqsMessageMapper.kt new file mode 100644 index 0000000..d89a05f --- /dev/null +++ b/dmforu-infrastructure/sqs/src/main/kotlin/com/dmforu/sqs/SqsMessageMapper.kt @@ -0,0 +1,32 @@ +package com.dmforu.sqs + +import software.amazon.awssdk.services.sqs.model.MessageAttributeValue +import software.amazon.awssdk.services.sqs.model.SendMessageRequest + +object SqsMessageMapper { + + fun createRequest(content: String, attributes: Map, queueUrl: String, delaySeconds: Int): SendMessageRequest { + return SendMessageRequest.builder() + .queueUrl(queueUrl) + .delaySeconds(delaySeconds) + .messageAttributes(attributes) + .messageBody(content) + .build() + } + + fun createAttributes( + tokens: List, + title: String, + type: String, + url: String, + ): Map { + val attributes = mutableMapOf() + + attributes["tokens"] = MessageAttributeValue.builder().stringValue(tokens.toString()).dataType("String").build() + attributes["title"] = MessageAttributeValue.builder().stringValue(title).dataType("String").build() + attributes["type"] = MessageAttributeValue.builder().stringValue(type).dataType("String").build() + attributes["url"] = MessageAttributeValue.builder().stringValue(url).dataType("String").build() + + return attributes + } +} \ No newline at end of file diff --git a/dmforu-infrastructure/sqs/src/main/kotlin/com/dmforu/sqs/SqsMessageSendException.kt b/dmforu-infrastructure/sqs/src/main/kotlin/com/dmforu/sqs/SqsMessageSendException.kt new file mode 100644 index 0000000..86d994e --- /dev/null +++ b/dmforu-infrastructure/sqs/src/main/kotlin/com/dmforu/sqs/SqsMessageSendException.kt @@ -0,0 +1,3 @@ +package com.dmforu.sqs + +class SqsMessageSendException (message: String, cause: Throwable? = null) : RuntimeException(message, cause) \ No newline at end of file diff --git a/dmforu-infrastructure/sqs/src/main/kotlin/com/dmforu/sqs/SqsMessageSender.kt b/dmforu-infrastructure/sqs/src/main/kotlin/com/dmforu/sqs/SqsMessageSender.kt new file mode 100644 index 0000000..18d0236 --- /dev/null +++ b/dmforu-infrastructure/sqs/src/main/kotlin/com/dmforu/sqs/SqsMessageSender.kt @@ -0,0 +1,47 @@ +package com.dmforu.sqs + +import com.dmforu.domain.message.MessageSender +import com.dmforu.domain.message.NoticeMessage +import com.dmforu.sqs.SqsMessageMapper.createAttributes +import com.dmforu.sqs.SqsMessageMapper.createRequest +import org.springframework.stereotype.Component +import software.amazon.awssdk.services.sqs.SqsAsyncClient +import software.amazon.awssdk.services.sqs.model.SendMessageResponse +import software.amazon.awssdk.services.sqs.model.SqsException + +@Component +class SqsMessageSender( + private val sqsAsyncClient: SqsAsyncClient, + private val awsConfig: AWSConfig, +) : MessageSender { + + override fun sendNoticeMessage(message: NoticeMessage, tokens: List) { + val attributes = createAttributes( + tokens = tokens, + title = message.title, + type = message.type, + url = message.url + ) + + val request = createRequest( + content = message.body, + attributes = attributes, + queueUrl = awsConfig.queueUrl, + delaySeconds = awsConfig.messageDelaySecs + ) + + try { + val future = sqsAsyncClient.sendMessage(request) + + future.whenComplete { sendMessageResponse: SendMessageResponse, throwable: Throwable? -> + if (throwable != null) { + throw SqsMessageSendException("[SQS Async Client] 메세지 전송에 실패하였습니다.", throwable) + } + } + } catch (sqsException: SqsException) { + throw SqsMessageSendException("[SQS] 메세지 전송에 실패하였습니다.", sqsException) + } + } + + +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 5c804bc..08752ba 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -6,6 +6,7 @@ include( "dmforu-domain", "dmforu-crawling", + "dmforu-infrastructure:sqs", "dmforu-infrastructure:fcm", "dmforu-infrastructure:storage:mysql", "dmforu-infrastructure:storage:mongo",