Compare commits

...

5 Commits

Author SHA1 Message Date
Artyom ad61be705b Create FcmClient class to make integration implementation closed 2022-08-12 15:13:48 +03:00
Artyom 37fb9443ea Update PushMessageProviderServiceFactory implementation 2022-08-12 12:29:30 +03:00
Artyom 5f81afcb08 Update readme 2022-08-11 17:49:47 +03:00
Artyom 1fffef06d9 Add push-message-provider-fcm module 2022-08-11 17:46:56 +03:00
Artyom 47759afac8 Add push-message-provider module 2022-08-11 17:46:54 +03:00
36 changed files with 750 additions and 0 deletions

View File

@ -213,3 +213,29 @@ server.info:
``` ```
3) Implement ServerInfoService (optional. If you want to add other headers) 3) Implement ServerInfoService (optional. If you want to add other headers)
4) Add dir with impl ServerInfoService in ComponentScan annotation 4) Add dir with impl ServerInfoService in ComponentScan annotation
## push-message-provider
Интерфейсы и компоненты для модулей по обеспечению интеграции с сервисами отправки пуш-уведомлений.
## push-message-provider-fcm
Модуль по обеспечению интеграции с Firebase Cloud Messaging.
1) Подключение компонентов Spring осуществляется при помощи аннотации `@EnablePushMessageProviderFcm`.
2) Необходимо добавление конфигурации для модуля. Пример файла конфигурации в формате yaml:
``` yaml
push-message-provider:
platformProviders:
ANDROID_GOOGLE:
- FCM
IOS:
- FCM
fcm:
appName: ${appName}
auth:
resourcePath: credentials/firebase-admin.json
client:
readTimeout: 10s
connectionTimeout: 1s
```
3) По обозначенному пути `push-message-provider-fcm.auth.resourcePath` добавляется json файл с настройками и доступами из консоли Firebase.

View File

@ -72,6 +72,8 @@ subprojects {
dependency("com.auth0:java-jwt:3.10.3") dependency("com.auth0:java-jwt:3.10.3")
dependency("software.amazon.awssdk:s3:2.10.11") dependency("software.amazon.awssdk:s3:2.10.11")
dependency("com.google.firebase:firebase-admin:9.0.0")
} }
} }

View File

@ -0,0 +1,19 @@
plugins {
id("kotlin")
id("kotlin-spring")
id("maven-publish")
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.springframework.boot:spring-boot")
implementation(project(":logger-spring"))
implementation(project(":push-message-provider"))
implementation("com.google.firebase:firebase-admin")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.testcontainers:junit-jupiter")
}

View File

@ -0,0 +1,8 @@
@file:Suppress("unused")
package ru.touchin.push.message.provider.fcm
import org.springframework.context.annotation.Import
import ru.touchin.push.message.provider.fcm.configurations.PushMessageProviderFcmConfiguration
@Import(value = [PushMessageProviderFcmConfiguration::class])
annotation class EnablePushMessageProviderFcm

View File

@ -0,0 +1,38 @@
package ru.touchin.push.message.provider.fcm.clients
import com.google.firebase.messaging.FirebaseMessaging
import com.google.firebase.messaging.FirebaseMessagingException
import org.springframework.stereotype.Component
import ru.touchin.push.message.provider.dto.request.PushTokenMessage
import ru.touchin.push.message.provider.dto.result.SendPushResult
import ru.touchin.push.message.provider.dto.result.SendPushTokenMessageResult
import ru.touchin.push.message.provider.exceptions.InvalidPushTokenException
import ru.touchin.push.message.provider.exceptions.PushMessageProviderException
import ru.touchin.push.message.provider.fcm.converters.FirebaseMessagingExceptionConverter
import ru.touchin.push.message.provider.fcm.converters.PushTokenMessageConverter
/**
* Service that provides integration with FCM.
* @see <a href="https://firebase.google.com/docs/cloud-messaging">FCM documentation</a>
*/
@Component
class FcmClient(
private val firebaseMessaging: FirebaseMessaging,
private val pushTokenMessageConverter: PushTokenMessageConverter,
private val firebaseMessagingExceptionConverter: FirebaseMessagingExceptionConverter
) {
@Throws(PushMessageProviderException::class, InvalidPushTokenException::class)
fun sendPushTokenMessage(request: PushTokenMessage): SendPushResult {
val message = pushTokenMessageConverter(request)
return try {
val messageId = firebaseMessaging.send(message)
SendPushTokenMessageResult(messageId)
} catch (e: FirebaseMessagingException) {
throw firebaseMessagingExceptionConverter(e)
}
}
}

View File

@ -0,0 +1,37 @@
package ru.touchin.push.message.provider.fcm.configurations
import com.google.auth.oauth2.GoogleCredentials
import com.google.firebase.FirebaseApp
import com.google.firebase.FirebaseOptions
import com.google.firebase.messaging.FirebaseMessaging
import org.springframework.boot.context.properties.ConfigurationPropertiesScan
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Import
import org.springframework.core.io.ClassPathResource
import ru.touchin.push.message.provider.configurations.PushMessageProviderConfiguration
import ru.touchin.push.message.provider.fcm.properties.PushMessageProviderFcmProperties
@ComponentScan("ru.touchin.push.message.provider.fcm")
@ConfigurationPropertiesScan(basePackages = ["ru.touchin.push.message.provider.fcm"])
@Import(value = [PushMessageProviderConfiguration::class])
class PushMessageProviderFcmConfiguration {
@Bean
fun firebaseMessaging(
properties: PushMessageProviderFcmProperties
): FirebaseMessaging {
val credentials = GoogleCredentials.fromStream(ClassPathResource(properties.auth.resourcePath).inputStream)
val options: FirebaseOptions = FirebaseOptions.builder()
.setCredentials(credentials)
.setConnectTimeout(properties.client.connectionTimeout.toMillis().toInt())
.setReadTimeout(properties.client.readTimeout.toMillis().toInt())
.build()
val firebaseApp: FirebaseApp = FirebaseApp.initializeApp(options, properties.appName)
return FirebaseMessaging.getInstance(firebaseApp)
}
}

View File

@ -0,0 +1,24 @@
package ru.touchin.push.message.provider.fcm.converters
import com.google.firebase.messaging.FirebaseMessagingException
import com.google.firebase.messaging.MessagingErrorCode
import org.springframework.stereotype.Component
import ru.touchin.common.exceptions.CommonException
import ru.touchin.push.message.provider.exceptions.InvalidPushTokenException
import ru.touchin.push.message.provider.exceptions.PushMessageProviderException
@Component
class FirebaseMessagingExceptionConverter {
operator fun invoke(exception: FirebaseMessagingException): CommonException {
return when (exception.messagingErrorCode) {
MessagingErrorCode.INVALID_ARGUMENT,
MessagingErrorCode.UNREGISTERED -> InvalidPushTokenException()
else -> PushMessageProviderException(
description = exception.message.orEmpty(),
cause = exception
)
}
}
}

View File

@ -0,0 +1,18 @@
package ru.touchin.push.message.provider.fcm.converters
import com.google.firebase.messaging.Notification as FcmNotification
import org.springframework.stereotype.Component
import ru.touchin.push.message.provider.dto.Notification
@Component
class NotificationConverter {
operator fun invoke(notification: Notification): FcmNotification {
return FcmNotification.builder()
.setTitle(notification.title)
.setBody(notification.description)
.setImage(notification.imageUrl)
.build()
}
}

View File

@ -0,0 +1,29 @@
package ru.touchin.push.message.provider.fcm.converters
import com.google.firebase.messaging.Message
import org.springframework.stereotype.Component
import ru.touchin.push.message.provider.dto.Notification
import ru.touchin.push.message.provider.dto.request.PushTokenMessage
@Component
class PushTokenMessageConverter(
private val notificationConverter: NotificationConverter
) {
operator fun invoke(request: PushTokenMessage): Message {
return Message.builder()
.setToken(request.token)
.setIfExists(request.notification)
.putAllData(request.data)
.build()
}
private fun Message.Builder.setIfExists(notification: Notification?): Message.Builder {
return if (notification != null) {
setNotification(notificationConverter(notification))
} else {
this
}
}
}

View File

@ -0,0 +1,31 @@
package ru.touchin.push.message.provider.fcm.properties
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.ConstructorBinding
import ru.touchin.push.message.provider.enums.PlatformType
import ru.touchin.push.message.provider.enums.PushMessageProviderType
import ru.touchin.push.message.provider.properties.PushMessageProviderProperties
import java.time.Duration
@ConstructorBinding
@ConfigurationProperties(prefix = "push-message-provider.fcm")
class PushMessageProviderFcmProperties(
val appName: String,
val auth: Auth.Credentials,
val client: Client
) {
sealed interface Auth {
data class Credentials(
val resourcePath: String
) : Auth
}
data class Client(
val readTimeout: Duration,
val connectionTimeout: Duration
)
}

View File

@ -0,0 +1,27 @@
package ru.touchin.push.message.provider.fcm.services
import org.springframework.stereotype.Service
import ru.touchin.push.message.provider.dto.request.PushTokenMessage
import ru.touchin.push.message.provider.dto.request.SendPushRequest
import ru.touchin.push.message.provider.dto.result.SendPushResult
import ru.touchin.push.message.provider.enums.PushMessageProviderType
import ru.touchin.push.message.provider.exceptions.InvalidPushTokenException
import ru.touchin.push.message.provider.exceptions.PushMessageProviderException
import ru.touchin.push.message.provider.fcm.clients.FcmClient
import ru.touchin.push.message.provider.services.PushMessageProviderService
@Service
class PushMessageProviderFcmService(
private val fcmClient: FcmClient
) : PushMessageProviderService {
override val type: PushMessageProviderType = PushMessageProviderType.FCM
@Throws(PushMessageProviderException::class, InvalidPushTokenException::class)
override fun send(request: SendPushRequest): SendPushResult {
return when (request) {
is PushTokenMessage -> fcmClient.sendPushTokenMessage(request)
}
}
}

View File

@ -0,0 +1,29 @@
package ru.touchin.push.message.provider.fcm
import com.fasterxml.jackson.annotation.JsonAutoDetect
import com.fasterxml.jackson.annotation.PropertyAccessor
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializationFeature
import org.springframework.boot.SpringBootConfiguration
import org.springframework.boot.context.properties.ConfigurationPropertiesScan
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Import
import org.springframework.test.context.ContextConfiguration
import ru.touchin.push.message.provider.fcm.configurations.PushMessageProviderFcmConfiguration
@TestConfiguration
@SpringBootConfiguration
@EnablePushMessageProviderFcm
class PushMessageProviderFcmTestApplication {
@Bean
fun objectMapper(): ObjectMapper {
return ObjectMapper().apply {
configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false)
setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE)
setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY)
}
}
}

View File

@ -0,0 +1,48 @@
package ru.touchin.push.message.provider.fcm.converters
import com.fasterxml.jackson.databind.ObjectMapper
import org.junit.Assert
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import ru.touchin.push.message.provider.dto.Notification
import com.google.firebase.messaging.Notification as FcmNotification
import org.junit.jupiter.api.DisplayName
@SpringBootTest
class NotificationConverterTest {
@Autowired
lateinit var notificationConverter: NotificationConverter
@Autowired
lateinit var objectMapper: ObjectMapper
@Test
@DisplayName("Конвертация уведомления происходит корректно")
fun invoke_basic() {
val notification = Notification(
title = "title",
description = "description",
imageUrl = "imageUrl"
)
val realResult = notificationConverter(notification)
val realResultJson = objectMapper.writeValueAsString(realResult)
val expectedResult = FcmNotification.builder()
.setTitle(notification.title)
.setBody(notification.description)
.setImage(notification.imageUrl)
.build()
val expectedResultJson = objectMapper.writeValueAsString(expectedResult)
Assert.assertEquals(
"Конвертация некорректна",
realResultJson,
expectedResultJson
)
}
}

View File

@ -0,0 +1,83 @@
package ru.touchin.push.message.provider.fcm.converters
import com.fasterxml.jackson.databind.ObjectMapper
import com.google.firebase.messaging.Message
import org.junit.Assert
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import ru.touchin.push.message.provider.dto.Notification
import ru.touchin.push.message.provider.dto.request.PushTokenMessage
@SpringBootTest
class PushTokenMessageConverterTest {
@Autowired
lateinit var pushTokenMessageConverter: PushTokenMessageConverter
@Autowired
lateinit var notificationConverter: NotificationConverter
@Autowired
lateinit var objectMapper: ObjectMapper
@Test
@DisplayName("Конвертация сообщения с уведомлением происходит корректно")
fun invoke_withNotification() {
val notification = Notification(
title = "title",
description = "description",
imageUrl = "imageUrl"
)
val pushTokenMessage = PushTokenMessage(
token = "token",
notification = notification,
data = mapOf("testKey" to "testvalue")
)
val realResult = pushTokenMessageConverter(pushTokenMessage)
val realResultJson = objectMapper.writeValueAsString(realResult)
val expectedResult = Message.builder()
.setToken(pushTokenMessage.token)
.setNotification(notificationConverter(notification))
.putAllData(pushTokenMessage.data)
.build()
val expectedResultJson = objectMapper.writeValueAsString(expectedResult)
Assert.assertEquals(
"Конвертация некорректна",
realResultJson,
expectedResultJson
)
}
@Test
@DisplayName("Конвертация сообщения без уведомления происходит корректно")
fun invoke_withoutNotification() {
val pushTokenMessage = PushTokenMessage(
token = "token",
notification = null,
data = mapOf("testKey" to "testvalue")
)
val realResult = pushTokenMessageConverter(pushTokenMessage)
val realResultJson = objectMapper.writeValueAsString(realResult)
val expectedResult = Message.builder()
.setToken(pushTokenMessage.token)
.putAllData(pushTokenMessage.data)
.build()
val expectedResultJson = objectMapper.writeValueAsString(expectedResult)
Assert.assertEquals(
"Конвертация некорректна",
realResultJson,
expectedResultJson
)
}
}

View File

@ -0,0 +1,50 @@
package ru.touchin.push.message.provider.fcm.services
import org.junit.Assert
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.mockito.Mockito
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.mock.mockito.MockBean
import ru.touchin.push.message.provider.dto.request.PushTokenMessage
import ru.touchin.push.message.provider.dto.result.SendPushTokenMessageResult
import ru.touchin.push.message.provider.fcm.clients.FcmClient
import ru.touchin.push.message.provider.services.PushMessageProviderService
@SpringBootTest
class PushMessageProviderFcmServiceTest {
@MockBean
lateinit var fcmClient: FcmClient
@Autowired
lateinit var pushMessageProviderService: PushMessageProviderService
@Test
@DisplayName("Обработка запроса на отправку единичного сообщения происходит корректно")
fun send_basic() {
val request = PushTokenMessage(
token = "testToken",
notification = null,
data = emptyMap()
)
val expectedResult = SendPushTokenMessageResult("testMessageId")
Mockito.`when`(
fcmClient.sendPushTokenMessage(request)
).thenReturn(
expectedResult
)
val realResult = pushMessageProviderService.send(request)
Assert.assertEquals(
"Обработка запроса на отправку единичного сообщения происходит некорректно",
expectedResult,
realResult
)
}
}

View File

@ -0,0 +1,13 @@
push-message-provider:
platformProviders:
ANDROID_GOOGLE:
- FCM
IOS:
- FCM
fcm:
appName: testAppName
auth:
resourcePath: credentials/firebase-admin.json
client:
readTimeout: 10s
connectionTimeout: 1s

View File

@ -0,0 +1,12 @@
{
"type": "service_account",
"project_id": "testProjectId",
"private_key_id": "privateKeyId",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALfBshaLMW2yddmAZJRNXTZzcSbwvY93Dnjj6naWgoBJoB3mOM5bcoyWwBw12A4rwecorz74OUOc6zdqX3j8hwsSyzgAUStKM5PkOvPNRKsI4eXAWU0fmb8h1jyXwftl7EzeBjEMBTpyXkgDk3wLfHN6ciCZrnQndOvS+mMl3b0hAgMBAAECgYEAmIQZByMSrITR0ewCDyFDO52HjhWEkF310hsBkNoNiOMTFZ3vCj/WjJ/W5dM+90wUTYN0KOSnytmkVUNh6K5Yekn+yRg/mBRTwwn88hU6umB8tUqoNz7AyUltAOGyQMWqAAcVgxV+mAp/Y018j69poEHgrW4qKol65/NRZyV7/J0CQQD4rCDjmxGEuA1yMzL2i8NyNl/5vvLVfLcEnVqpHbc1+KfUHZuY7iv38xpzfmErqhCxAXfQ52edq5rXmMIVSbFrAkEAvSvfSSK9XQDJl3NEyfR3BGbsoqKIYOuJAnv4OQPSODZfTNWhc11S8y914qaSWB+Iid9HoLvAIgPH5mrzPzjSowJBAJcw4FZCI+aTmOlEI8ous8gvMy8/X5lZWFUf7s0/2fKgmjmnPsE+ndEFJ6HsxturbLaR8+05pJAClARdRjN3OL0CQGoF+8gmw1ErztCmVyiFbms2MGxagesoN4r/5jg2Tw0YVENg/HMHHCWWNREJ4L2pNsJnNOL+N4oY6mHXEWwesdcCQCUYTfLYxi+Wg/5BSC7fgl/gu0mlx07AzMoMQLDOXdisV5rpxrOoT3BOLBqyccv37AZ3e2gqb8JYyNzO6C0zswQ=\n-----END PRIVATE KEY-----\n",
"client_email": "clientEmail",
"client_id": "clientId",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://accounts.google.com/o/oauth2/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "clientX509CertUrl"
}

View File

@ -0,0 +1,16 @@
plugins {
id("kotlin")
id("kotlin-spring")
id("maven-publish")
}
dependencies {
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
implementation("org.springframework.boot:spring-boot")
implementation(project(":common"))
testImplementation("org.testcontainers:junit-jupiter")
testImplementation("org.springframework.boot:spring-boot-starter-test")
}

View File

@ -0,0 +1,8 @@
package ru.touchin.push.message.provider.configurations
import org.springframework.boot.context.properties.ConfigurationPropertiesScan
import org.springframework.context.annotation.ComponentScan
@ComponentScan("ru.touchin.push.message.provider")
@ConfigurationPropertiesScan(basePackages = ["ru.touchin.push.message.provider"])
class PushMessageProviderConfiguration

View File

@ -0,0 +1,7 @@
package ru.touchin.push.message.provider.dto
class Notification(
val title: String,
val description: String,
val imageUrl: String
)

View File

@ -0,0 +1,9 @@
package ru.touchin.push.message.provider.dto.request
import ru.touchin.push.message.provider.dto.Notification
class PushTokenMessage(
val token: String,
override val notification: Notification?,
override val data: Map<String, String>
) : SendPushRequest

View File

@ -0,0 +1,10 @@
package ru.touchin.push.message.provider.dto.request
import ru.touchin.push.message.provider.dto.Notification
sealed interface SendPushRequest {
val notification: Notification?
val data: Map<String, String>
}

View File

@ -0,0 +1,3 @@
package ru.touchin.push.message.provider.dto.result
sealed interface SendPushResult

View File

@ -0,0 +1,5 @@
package ru.touchin.push.message.provider.dto.result
class SendPushTokenMessageResult(
val messageId: String
) : SendPushResult

View File

@ -0,0 +1,8 @@
package ru.touchin.push.message.provider.enums
enum class PlatformType {
ANDROID_GOOGLE,
IOS
}

View File

@ -0,0 +1,7 @@
package ru.touchin.push.message.provider.enums
enum class PushMessageProviderType {
FCM
}

View File

@ -0,0 +1,5 @@
package ru.touchin.push.message.provider.exceptions
import ru.touchin.common.exceptions.CommonException
class InvalidPushTokenException : CommonException("Invalid push token")

View File

@ -0,0 +1,8 @@
package ru.touchin.push.message.provider.exceptions
import ru.touchin.common.exceptions.CommonException
open class PushMessageProviderException(
description: String,
cause: Throwable?
) : CommonException(description, cause)

View File

@ -0,0 +1,10 @@
package ru.touchin.push.message.provider.factories
import ru.touchin.push.message.provider.enums.PlatformType
import ru.touchin.push.message.provider.services.PushMessageProviderService
interface PushMessageProviderServiceFactory {
fun get(platformType: PlatformType): PushMessageProviderService
}

View File

@ -0,0 +1,25 @@
package ru.touchin.push.message.provider.factories
import org.springframework.stereotype.Component
import ru.touchin.common.exceptions.CommonException
import ru.touchin.push.message.provider.enums.PlatformType
import ru.touchin.push.message.provider.properties.PushMessageProviderProperties
import ru.touchin.push.message.provider.services.PushMessageProviderService
import kotlin.jvm.Throws
@Component
class PushMessageProviderServiceFactoryImpl(
private val pushMessageProviderProperties: PushMessageProviderProperties,
private val pushMessageProviderServices: List<PushMessageProviderService>
) : PushMessageProviderServiceFactory {
@Throws(CommonException::class)
override fun get(platformType: PlatformType): PushMessageProviderService {
val supportedProviderTypes = pushMessageProviderProperties.platformProviders[platformType]?.firstOrNull()
?: throw CommonException("Configuration has no setup for platform '$platformType'")
return pushMessageProviderServices.find { it.type == supportedProviderTypes }
?: throw CommonException("Configuration has no push message provider support for platform '$platformType'")
}
}

View File

@ -0,0 +1,12 @@
package ru.touchin.push.message.provider.properties
import org.springframework.boot.context.properties.ConfigurationProperties
import org.springframework.boot.context.properties.ConstructorBinding
import ru.touchin.push.message.provider.enums.PlatformType
import ru.touchin.push.message.provider.enums.PushMessageProviderType
@ConstructorBinding
@ConfigurationProperties(prefix = "push-message-provider")
class PushMessageProviderProperties(
val platformProviders: Map<PlatformType, List<PushMessageProviderType>>
)

View File

@ -0,0 +1,13 @@
package ru.touchin.push.message.provider.services
import ru.touchin.push.message.provider.dto.request.SendPushRequest
import ru.touchin.push.message.provider.dto.result.SendPushResult
import ru.touchin.push.message.provider.enums.PushMessageProviderType
interface PushMessageProviderService {
val type: PushMessageProviderType
fun send(request: SendPushRequest): SendPushResult
}

View File

@ -0,0 +1,14 @@
package ru.touchin.push.message.provider
import org.springframework.boot.SpringBootConfiguration
import org.springframework.boot.context.properties.ConfigurationPropertiesScan
import org.springframework.boot.test.context.TestConfiguration
import org.springframework.context.annotation.Import
import org.springframework.test.context.ContextConfiguration
import ru.touchin.push.message.provider.configurations.PushMessageProviderConfiguration
@TestConfiguration
@SpringBootConfiguration
@ContextConfiguration(classes = [PushMessageProviderConfiguration::class])
@Import(PushMessageProviderConfiguration::class)
class PushMessageProviderTestApplication

View File

@ -0,0 +1,88 @@
package ru.touchin.push.message.provider.factories
import org.junit.Assert
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import ru.touchin.common.exceptions.CommonException
import ru.touchin.push.message.provider.dto.request.SendPushRequest
import ru.touchin.push.message.provider.dto.result.SendPushResult
import ru.touchin.push.message.provider.enums.PlatformType
import ru.touchin.push.message.provider.enums.PushMessageProviderType
import ru.touchin.push.message.provider.properties.PushMessageProviderProperties
import ru.touchin.push.message.provider.services.PushMessageProviderService
class PushMessageProviderServiceFactoryImplTest {
private val pushMessageProviderServiceFcm = object : PushMessageProviderService {
override val type: PushMessageProviderType = PushMessageProviderType.FCM
override fun send(request: SendPushRequest): SendPushResult = throw NotImplementedError()
}
@Test
@DisplayName("При отсутствии поддерживаемых платформ выбрасывается исключение")
fun get_platformNotFound() {
val pushMessageProviderServiceFactory = PushMessageProviderServiceFactoryImpl(
pushMessageProviderProperties = PushMessageProviderProperties(
platformProviders = emptyMap()
),
pushMessageProviderServices = listOf(pushMessageProviderServiceFcm)
)
Assert.assertThrows(
"Исключение не выбрасывается или принадлежит иному типу",
CommonException::class.java
) {
pushMessageProviderServiceFactory.get(PlatformType.IOS)
}
}
@Test
@DisplayName("При отсутствии назначенного провайдера у поддерживаемой платформы выбрасывается исключение")
fun get_providerServiceForPlatformNotFound() {
val pushMessageProviderServiceFactory = PushMessageProviderServiceFactoryImpl(
pushMessageProviderProperties = PushMessageProviderProperties(
platformProviders = mapOf(
PlatformType.IOS to emptyList()
)
),
pushMessageProviderServices = listOf(pushMessageProviderServiceFcm)
)
Assert.assertThrows(
"Исключение не выбрасывается или принадлежит иному типу",
CommonException::class.java
) {
pushMessageProviderServiceFactory.get(PlatformType.IOS)
}
}
@Test
@DisplayName("Настроенной платформе назначается первый сервис из доступных")
fun get_firstSupportedProviderService() {
val pushMessageProviderServiceFactory = PushMessageProviderServiceFactoryImpl(
pushMessageProviderProperties = PushMessageProviderProperties(
platformProviders = mapOf(PlatformType.IOS to listOf(PushMessageProviderType.FCM))
),
pushMessageProviderServices = listOf(
pushMessageProviderServiceFcm,
object : PushMessageProviderService {
override val type: PushMessageProviderType = PushMessageProviderType.FCM
override fun send(request: SendPushRequest): SendPushResult = throw NotImplementedError()
}
)
)
Assert.assertEquals(
"Платформе назначен не первый сервис из доступных",
pushMessageProviderServiceFcm,
pushMessageProviderServiceFactory.get(PlatformType.IOS)
)
}
}

View File

@ -0,0 +1,6 @@
push-message-provider:
platformProviders:
ANDROID_GOOGLE:
- FCM
IOS:
- FCM

View File

@ -42,6 +42,8 @@ include("exception-handler-spring-security-web")
include("exception-handler-logger-spring-web") include("exception-handler-logger-spring-web")
include("validation-spring") include("validation-spring")
include("version-spring-web") include("version-spring-web")
include("push-message-provider")
include("push-message-provider-fcm")
include("response-wrapper-spring-web") include("response-wrapper-spring-web")
include("settings-spring-jpa") include("settings-spring-jpa")
include("security-authorization-server-core") include("security-authorization-server-core")