Spring boot - FirebaseMessaging Service injection is not working in another service - spring-boot

I am trying to send firebase push notifications from a spring boot application.
Here I configured the FirebaseMessaging like:
#Configuration
class FirebaseConfig {
var logger: Logger = LoggerFactory.getLogger(FirebaseConfig::class.java)
#Value("\${app.firebase-config-file}")
lateinit var firebaseConfigPath: String
#Bean("firebaseMessaging")
#Throws(IOException::class)
fun firebaseMessaging(): FirebaseMessaging {
val firebaseOptions = FirebaseOptions
.builder()
.setCredentials(GoogleCredentials.fromStream(ClassPathResource(firebaseConfigPath).inputStream))
.build()
val app = FirebaseApp.initializeApp(firebaseOptions, "QInspect")
logger.info("Firebase application has been initialized")
return FirebaseMessaging.getInstance(app)
}
}
Here is the service I am using to send the push notifications:
#Service
class FirebaseNotificationService constructor(
#Autowired val firebaseMessaging: FirebaseMessaging,
#Autowired val mobileDeviceLogService: MobileDeviceLogService
) {
private var logger: Logger = LoggerFactory.getLogger(FirebaseNotificationService::class.java)
private val gson = Gson()
fun sendNotification(token: String, title: String, body: String): Status {
try {
val notification = Notification
.builder()
.setTitle(title)
.setBody(body)
.build()
val message = Message
.builder()
.setToken(token)
.setNotification(notification)
.build()
firebaseMessaging.send(message)
logger.info("Firebase Notification sent)
return Status(Constants.ResponseCode.SUCCESS, "Success")
} catch (e: Exception) {
logger.error("Firebase Notification Failed: ${e.printStackTrace()}")
}
return Status(Constants.ResponseCode.EXCEPTION, "Failed")
}
}
I can able to send the push notification using postman using the endpoint:
#RequestMapping("/notification")
#RestController
class NotificationResource constructor(#Autowired val firebaseService: FirebaseNotificationService) {
#RequestMapping(value = ["/message"], method = [RequestMethod.POST])
#ApiOperation(value = "Send firebase push notification with token", authorizations = [Authorization(value = "basic"), Authorization(value = "oauth2", scopes = [])])
#ResponseBody
#Throws(FirebaseMessagingException::class)
fun sendNotificationMsg(#RequestParam(value = "title", required = true) title: String, #RequestParam(value = "body", required = true) body: String): Status {
val user = SessionUtil.getUser()
return firebaseService.sendNotification(user.token, title, body)
}
Now the problem is when I am trying to inject the FirebaseNotificationService in another service to send the push notifications, But it's not working without any errors.
#Service
#Transactional
class MessageSubscriber {
#Autowired
var firebaseService: FirebaseNotificationService? = null
fun sendPush() {
firebaseService?.sendNotification("token", "Some Title", "Some body")
}
}
What I am missing here?
Update:
I got the issue, it's my mistake it is going inside the FirebaseNotificationService but failing to get the user token which I didn't mention. It was returning here.
val notificationToken =
tokenService.getTopByUserId(user.uuid!!)
?: return Status(Constants.ResponseCode.FAILED, "User or token not found")

Related

How to get a data from mongoDB with the _id in Kotlin

I'm new in programming and I'm had having some problems to create an API for Kotlin getting data from a existing DB from mongoDB to my API.
I'm making Tests and I want to return a data with it Id, but I can't.
I'm using the SpringBoot, JPA, and mongoDB Data. The host is my machine, the database name is ArthurLeywin, and the collection name is Capitulos.
I'm receiving the error status=404, error=Not Found, path=/ArthueLeywin/Capitulos/6306e6417d5b2e259951f292}
The code of the Test is
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ExtendWith(SpringExtension::class)
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
class TbateMobileReederApplicationTests #Autowired constructor(
private val capituloRepository: CapituloRepository,
private val restTemplate: TestRestTemplate
) {
private val defaultCapituloId : ObjectId = ObjectId("6306e6417d5b2e259951f292")
//ObjectId.get()
#LocalServerPort
protected var port: Int = 27017
private fun getRootUrl(): String? = "http://localhost:$port/ArthueLeywin/Capitulos"
#Test
fun `should return single cap by id`() {
val response = restTemplate.getForObject(
getRootUrl() + "/$defaultCapituloId",
Object::class.java
)
val titleResponse = response.toString()
println(titleResponse)
}
}
My controller, if it's needed is
#RestController
#RequestMapping("/Capitulos")
class CapituloController (
private val capituloRepository : CapituloRepository
){
#GetMapping
fun getAllCapitulos() : ResponseEntity<List<Capitulo>>{
val capitulos = capituloRepository.findAll()
return ResponseEntity.ok(capitulos)
}
#GetMapping("/{id}")
fun getOneCapitulo(#PathVariable("id") id: String): ResponseEntity<Capitulo> {
val capitulo = capituloRepository.findOneById(ObjectId(id))
return ResponseEntity.ok(capitulo)
}
#PostMapping
fun createCapitulo (#RequestBody request: CapituloRequest) : ResponseEntity<Capitulo>{
val capitulo = capituloRepository.save(Capitulo(
nomeCapitulo = request.nomeCapitulo,
urlString = request.urlString,
novelTexto = request.novelTexto
))
return ResponseEntity(capitulo, HttpStatus.CREATED)
}
}
As I said I'm new in this, so please help me with explanations <3

AWS Spring can't set object mapper

im trying to set jackson serializer at spring MessageTemplate to send messages alredy formated using jacksonMapper and SQS/SNS, the problem is that it keeps sending messages unformated, even with notation #JsonProperty or #JsonFormat
here is my configs:
#Configuration
class AwsMessagingConfigurationLocal(
#Value("\${cloud.aws.region.static}") val region: String,
#Value("\${cloud.aws.credentials.accessKey}") val accessKey: String,
#Value("\${cloud.aws.credentials.secretKey}") val secretKey: String,
#Value("\${cloud.aws.endpoint}") val endpoint: String
) {
#Bean
fun notificationMessageTemplate(): NotificationMessagingTemplate {
return NotificationMessagingTemplate(buildAmazonSNSAsync())
}
#Bean
fun queueMessagingTemplate(): QueueMessagingTemplate {
return QueueMessagingTemplate(buildAmazonSQSAsync())
}
#Bean
fun simpleMessageListenerContainerFactory(): SimpleMessageListenerContainerFactory {
val messageListenerContainerFactory = SimpleMessageListenerContainerFactory()
messageListenerContainerFactory.setAmazonSqs(buildAmazonSQSAsync())
return messageListenerContainerFactory
}
#Bean
fun queueMessageHandlerFactory(objectMapper: ObjectMapper): QueueMessageHandlerFactory {
val messageConverter = MappingJackson2MessageConverter()
messageConverter.objectMapper = objectMapper.registerModule(JavaTimeModule())
val queueMessageHandlerFactory = QueueMessageHandlerFactory()
queueMessageHandlerFactory.messageConverters = listOf(messageConverter)
queueMessageHandlerFactory.setArgumentResolvers(listOf(NotificationMessageArgumentResolver(messageConverter)))
return queueMessageHandlerFactory
}
private fun buildAmazonSNSAsync(): AmazonSNSAsync {
return AmazonSNSAsyncClientBuilder.standard()
.withEndpointConfiguration(AwsClientBuilder.EndpointConfiguration(endpoint, region))
.withCredentials(AWSStaticCredentialsProvider(BasicAWSCredentials(accessKey, secretKey)))
.build()
}
private fun buildAmazonSQSAsync(): AmazonSQSAsync {
return AmazonSQSAsyncClientBuilder.standard()
.withEndpointConfiguration(AwsClientBuilder.EndpointConfiguration(endpoint, region))
.withCredentials(AWSStaticCredentialsProvider(BasicAWSCredentials(accessKey, secretKey)))
.build()
}
}
but when i send an object with LocalDateTime, even with #JsonFormat it keeps sending wrong format:
data class Blob(
#JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'")
val timestamp: LocalDateTime
)
i made this simple dispatch function to send messages through SNS(topic) and connected a queue to receive the responses.
class Dispatcher(
private val private val notificationTemplate: NotificationMessagingTemplate
)
fun dispatch(blob: Blob) {
val header = mapOf("randon" to "message")
notificationTemplate.convertAndSend("topic", blob, header)
}
wanted result :
\"timestamp\"=\"2021-11-26T19:18:46.905505\"
current result :
"timestamp\":{\"nano\":913070000,\"year\":2021,\"monthValue\":11,\"dayOfMonth\":26,\"hour\":19,\"minute\":18,\"second\":46,\"month\":\"NOVEMBER\",\"dayOfWeek\":\"FRIDAY\",\"dayOfYear\":330,\"chronology\":{\"calendarType\":\"iso8601\",\"id\":\"ISO\"}}
i don't have any clue about what is happening.
PS. i tested SQS too, and the result is the same.
after searching inside awspring, i found that the SimpleMessageConverter is set by default, after some research, i found the right way to set the custom mapper at sendAndConvert() function. here is the configurtions:
#Bean
fun notificationMessageTemplate(objectMapper: ObjectMapper): NotificationMessagingTemplate {
// here we set the custom message converter
val messageConverter = MappingJackson2MessageConverter()
messageConverter.objectMapper = objectMapper
// as we want to send it as a Json, we need to set the serialization to String
messageConverter.serializedPayloadClass = String::class.java
val notificationMessageTemplate = NotificationMessagingTemplate(buildAmazonSNSAsync())
// here is where we set the proper message converter
notificationMessageTemplate.messageConverter = messageConverter
return notificationMessageTemplate
}
#Bean
fun queueMessagingTemplate(objectMapper: ObjectMapper): QueueMessagingTemplate {
val messageConverter = MappingJackson2MessageConverter()
messageConverter.objectMapper = objectMapper
messageConverter.serializedPayloadClass = String::class.java
val queueMessageConverter = QueueMessagingTemplate(buildAmazonSQSAsync())
queueMessageConverter.messageConverter = messageConverter
return queueMessageConverter
}
after setting this mappers, every notation will be working fine.
here is the result:
\"timestamp\":\"2021-11-29T17:46:52Z\"

Spring boot validating #RequestParam not working in kotlin

I'm trying to validate text parameter to be not blank in spring boot kotlin project, but its not working correctly and returning just response code 200 instead of 422.
org.springframework.boot:spring-boot-starter-validation" is also present in classpath.
Controller.kt
#Validated
#RestController
#RequestMapping(ApiEndPoints.Search.SEARCH_ROOT_PATH)
class SearchController(
private val searchService: SearchService
) {
#GetMapping(ApiEndPoints.Search.BY_USERS)
fun searchUsers(
#Valid #NotBlank(message = "Text must not be blank") #RequestParam text: String,
#RequestParam(defaultValue = Constants.DEFAULT_PAGE_0) page: Int,
#RequestParam(defaultValue = Constants.DEFAULT_SIZE_15) size: Int
): ResponseEntity<ApiResponse.Success<Map<String, Any>>> {
val users = searchService.searchUsers(text, page, size)
return ResponseEntity.ok(ApiResponse.Success(true, users.createResponseMapFromSlice(Constants.USERS_LABEL)))
}
}
ControllerAdvice.kt
#Order(Ordered.HIGHEST_PRECEDENCE)
#RestControllerAdvice
class GlobalExceptionHandler : ResponseEntityExceptionHandler() {
override fun handleMethodArgumentNotValid(
ex: MethodArgumentNotValidException,
headers: HttpHeaders,
status: HttpStatus,
request: WebRequest
): ResponseEntity<Any> {
val errors = hashMapOf<String, String>()
ex.bindingResult.allErrors.forEach {
val fieldName = (it as FieldError).field
val errorMessage = it.defaultMessage
errors[fieldName] = errorMessage.toString()
}
return ResponseEntity(
ApiResponse.ValidationError(
errors,
ErrorCodes.VALIDATION_FAILED
), HttpStatus.UNPROCESSABLE_ENTITY
)
}
}

Using ConnectableFlux for hot stream REST endpoint

I'm trying to create a REST endpoint to subscribe to a hot stream using Reactor.
My test provider for the stream looks like this:
#Service
class TestProvider {
fun getStream(resourceId: String): ConnectableFlux<QData> {
return Flux.create<QData> {
for (i in 1..10) {
it.next(QData(LocalDateTime.now().toString(), "next"))
Thread.sleep(500L)
}
it.complete()
}.publish()
}
}
My service for the REST endpoint looks like this:
#Service
class DataService #Autowired constructor(
private val prv: TestProvider
) {
private val streams = mutableMapOf<String, ConnectableFlux<QData>>()
fun subscribe(resourceId: String): Flux<QData> {
val stream = getStream(resourceId)
return Flux.push { flux ->
stream.subscribe{
flux.next(it)
}
flux.complete()
}
}
private fun getStream(resourceId: String): ConnectableFlux<QData> {
if(streams.containsKey(resourceId).not()) {
streams.put(resourceId, createStream(resourceId))
}
return streams.get(resourceId)!!
}
private fun createStream(resourceId: String): ConnectableFlux<QData> {
val stream = prv.getStream(resourceId)
stream.connect()
return stream
}
}
The controller looks like this:
#RestController
class DataController #Autowired constructor(
private val dataService: DataService
): DataApi {
override fun subscribe(resourceId: String): Flux<QData> {
return dataService.subscribe(resourceId)
}
}
The API interface looks like this:
interface DataApi {
#ApiResponses(value = [
ApiResponse(responseCode = "202", description = "Client is subscribed", content = [
Content(mediaType = "application/json", array = ArraySchema(schema = Schema(implementation = QData::class)))
])
])
#GetMapping(path = ["/subscription/{resourceId}"], produces = [MediaType.APPLICATION_JSON_VALUE])
fun subscribe(
#Parameter(description = "The resource id for which quality data is subscribed for", required = true, example = "example",allowEmptyValue = false)
#PathVariable("resourceId", required = true) #NotEmpty resourceId: String
): Flux<QData>
}
Unfortunately, my curl delivers an empty array.
Does anyone has an idea what the issue is? Thanks in advance!
I had to run connect() async in DataService:
CompletableFuture.runAsync {
stream.connect()
}

Validating Spring #PathVariable in Kotlin Controller

I'm attempting to validate a String #PathVariable inside a Kotlin Spring controller.
#RestController
#Validated
#RequestMapping("/kotlin/")
open class KStringController {
#GetMapping(path = ["string/{username}"],
produces = [APPLICATION_JSON_VALUE])
#ResponseBody
fun validateStringPathVariable(
#Pattern(regexp = "[A-Za-z]+", message = "Username Pattern Validation Message")
#Size(min = 2, max = 15, message = "Username Size Validation Message")
#PathVariable("username") username: String?
): ResponseEntity<String>? {
logger.info { String.format("validateStringPathVariable: Got Username [%s]", username) }
System.out.printf("validateStringPathVariable: Username is [%s]%n", username)
return ResponseEntity.ok("Username is valid")
}
}
I have unit tests for it:
#ExtendWith(SpringExtension::class)
#WebMvcTest(KStringController::class)
#AutoConfigureMockMvc
class KStringGetControllerTest {
#field:Autowired
private lateinit var mvc: MockMvc
#Test
#Throws(Exception::class)
fun validStringPathVariable() {
val request: MockHttpServletRequestBuilder = givenARequestFor("/kotlin/string/mike")
val actions: ResultActions = whenTheRequestIsMade(request)
thenExpect(actions,
MockMvcResultMatchers.status().isOk,
MockMvcResultMatchers.content().bytes("Username is valid".toByteArray()))
}
#Test
#Throws(Exception::class)
fun shoutsWhenStringPathVariableIsTooShort() {
val request = givenARequestFor("/kotlin/string/a")
val actions = whenTheRequestIsMade(request)
val response = """{
"validationErrors": [
{
"fieldName": "validateStringPathVariable.username",
"message": "Username Size Validation Message"
}
]
}"""
val content = MockMvcResultMatchers.content()
thenExpect(actions,
MockMvcResultMatchers.status().isBadRequest,
content.contentType(MediaType.APPLICATION_JSON),
content.json(response))
}
private fun givenARequestFor(url: String): MockHttpServletRequestBuilder {
return MockMvcRequestBuilders.get(url)
.characterEncoding("UTF-8")
}
#Throws(Exception::class)
private fun whenTheRequestIsMade(request: MockHttpServletRequestBuilder): ResultActions {
return mvc.perform(request)
}
#Throws(Exception::class)
private fun thenExpect(resultActions: ResultActions, vararg matchers: ResultMatcher) {
resultActions.andExpect(ResultMatcher.matchAll(*matchers))
}
Currently the second test is failing; it's returning a 200 response code when I'm expecting a 400. Much much more code at https://github.com/Mavelous/spring-validation.
With help from others, I've managed to find a solution.
Quick answer: Change the function to open fun validateStringPathVariable, and the validations will start working.
Alternative answer: Use the org.jetbrains.kotlin.plugin.spring plugin in your build.gradle file.
I also created a blog post with a longer description: http://mavelo.us/2021/04/07/spring-validation-in-kotlin.html

Resources