From 56c106e441ff919873fcd42350ff2c7ad2d127ba Mon Sep 17 00:00:00 2001 From: boggard Date: Wed, 7 Jun 2023 16:43:06 +0300 Subject: [PATCH] feat: telegram chat authorization via jira --- .../mikesafonov/jira/telegram/dao/Entities.kt | 4 +-- .../jira/telegram/service/ChatService.kt | 17 ++++++++++--- .../jira/telegram/service/TagService.kt | 2 +- .../telegram/service/jira/JiraApiService.kt | 8 ++++++ .../service/jira/JiraRestClientFactory.kt | 9 +++++++ .../telegram/service/jira/MySelfRestClient.kt | 25 +++++++++++++++++++ .../telegram/service/telegram/TelegramDto.kt | 2 +- .../service/telegram/TelegramUpdateManager.kt | 6 ++++- .../JiraAuthApproveTelegramCommandHandler.kt | 12 ++++++++- .../JiraAuthTelegramCommandHandler.kt | 2 +- .../MyIssuesTelegramCommandHandler.kt | 2 +- .../migration/h2/V1_0_6__nullableJiraId.sql | 1 + .../mysql/V1_0_6__nullableJiraId.sql | 1 + .../postgresql/V1_0_6__nullableJiraId.sql | 1 + 14 files changed, 81 insertions(+), 11 deletions(-) create mode 100644 src/main/kotlin/com/github/mikesafonov/jira/telegram/service/jira/MySelfRestClient.kt create mode 100644 src/main/resources/db/migration/h2/V1_0_6__nullableJiraId.sql create mode 100644 src/main/resources/db/migration/mysql/V1_0_6__nullableJiraId.sql create mode 100644 src/main/resources/db/migration/postgresql/V1_0_6__nullableJiraId.sql diff --git a/src/main/kotlin/com/github/mikesafonov/jira/telegram/dao/Entities.kt b/src/main/kotlin/com/github/mikesafonov/jira/telegram/dao/Entities.kt index 89bdfdb..3724cb2 100644 --- a/src/main/kotlin/com/github/mikesafonov/jira/telegram/dao/Entities.kt +++ b/src/main/kotlin/com/github/mikesafonov/jira/telegram/dao/Entities.kt @@ -13,8 +13,8 @@ data class Chat( @GeneratedValue(strategy = GenerationType.IDENTITY) val id: Int?, - @Column(name = "jira_id", nullable = false) - val jiraId: String, + @Column(name = "jira_id") + var jiraId: String?, @Column(name = "telegram_id", nullable = false) val telegramId: Long, diff --git a/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/ChatService.kt b/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/ChatService.kt index 3218d9a..63bb9f8 100644 --- a/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/ChatService.kt +++ b/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/ChatService.kt @@ -18,8 +18,12 @@ class ChatService(private val chatRepository: ChatRepository, private val chatTa val validatedJiraLogin = validateJiraLogin(jiraLogin) val validatedTelegramId = validateTelegramId(telegramId) - val chat = Chat(null, validatedJiraLogin, validatedTelegramId, State.INIT) - chatRepository.save(chat) + addChatInState(validatedTelegramId, validatedJiraLogin, State.INIT) + } + + fun addChatInState(telegramId: Long, jiraId: String?, state: State) { + val chat = Chat(null, jiraId, telegramId, state) + save(chat) } fun getAll() : List { @@ -27,7 +31,7 @@ class ChatService(private val chatRepository: ChatRepository, private val chatTa } fun getAllLogins() : List { - return chatRepository.findAll().map { it.jiraId } + return chatRepository.findAll().map { it.jiraId }.filterNotNull() } fun findByJiraId(jiraId: String): Chat? { @@ -47,6 +51,13 @@ class ChatService(private val chatRepository: ChatRepository, private val chatTa } } + fun updateJiraId(telegramId: Long, jiraId: String) { + findByTelegramId(telegramId)?.let { + it.jiraId = jiraId + save(it) + } + } + fun save(chat: Chat): Chat { return chatRepository.save(chat) } diff --git a/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/TagService.kt b/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/TagService.kt index 15092c2..922f714 100644 --- a/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/TagService.kt +++ b/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/TagService.kt @@ -18,6 +18,6 @@ class TagService(private val repository: TagRepository) { @Transactional fun getJiraLoginsByTagKey(key: String): List { val tag = getTagByKey(key) ?: return emptyList() - return tag.chats.map { it.jiraId } + return tag.chats.mapNotNull { it.jiraId } } } diff --git a/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/jira/JiraApiService.kt b/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/jira/JiraApiService.kt index 201391e..42da7ed 100644 --- a/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/jira/JiraApiService.kt +++ b/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/jira/JiraApiService.kt @@ -1,6 +1,8 @@ package com.github.mikesafonov.jira.telegram.service.jira import com.atlassian.jira.rest.client.api.domain.Issue +import com.atlassian.jira.rest.client.api.domain.User +import com.atlassian.jira.rest.client.internal.async.AsynchronousHttpClientFactory import com.github.mikesafonov.jira.telegram.config.conditional.ConditionalOnJiraOAuth import org.springframework.stereotype.Service @@ -41,4 +43,10 @@ class JiraApiService(private val jiraRestClientFactory: JiraRestClientFactory) { .getIssue(issueKey) .claim() } + + fun getMySelf(telegramId: Long): User? { + return jiraRestClientFactory.createMySelfRestClient(telegramId) + .getMySelf() + .claim() + } } \ No newline at end of file diff --git a/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/jira/JiraRestClientFactory.kt b/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/jira/JiraRestClientFactory.kt index 42f636e..676b04d 100644 --- a/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/jira/JiraRestClientFactory.kt +++ b/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/jira/JiraRestClientFactory.kt @@ -1,6 +1,7 @@ package com.github.mikesafonov.jira.telegram.service.jira import com.atlassian.jira.rest.client.api.JiraRestClient +import com.atlassian.jira.rest.client.internal.async.AsynchronousHttpClientFactory import com.atlassian.jira.rest.client.internal.async.AsynchronousJiraRestClientFactory import com.github.mikesafonov.jira.telegram.config.JiraOAuthProperties import com.github.mikesafonov.jira.telegram.config.conditional.ConditionalOnJiraOAuth @@ -26,4 +27,12 @@ class JiraRestClientFactory( JiraOAuthAuthenticationHandler(oAuthParameters) ) } + + fun createMySelfRestClient(telegramId: Long): MySelfRestClient { + val oAuthParameters = authService.getOAuthParameters(telegramId) + val serverUri = URI(jiraProperties.baseUrl) + val httpClient = AsynchronousHttpClientFactory().createClient(serverUri, + JiraOAuthAuthenticationHandler(oAuthParameters)) + return MySelfRestClient(httpClient, serverUri) + } } \ No newline at end of file diff --git a/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/jira/MySelfRestClient.kt b/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/jira/MySelfRestClient.kt new file mode 100644 index 0000000..56cfc3b --- /dev/null +++ b/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/jira/MySelfRestClient.kt @@ -0,0 +1,25 @@ +package com.github.mikesafonov.jira.telegram.service.jira + +import com.atlassian.httpclient.api.HttpClient +import com.atlassian.jira.rest.client.api.domain.User +import com.atlassian.jira.rest.client.internal.async.AbstractAsynchronousRestClient +import com.atlassian.jira.rest.client.internal.json.UserJsonParser +import io.atlassian.util.concurrent.Promise +import java.net.URI +import javax.ws.rs.core.UriBuilder + + +class MySelfRestClient(httpClient: HttpClient, serverUri: URI) : AbstractAsynchronousRestClient(httpClient) { + + private val baseUri: URI + private val userJsonParser = UserJsonParser() + + init { + baseUri = UriBuilder.fromUri(serverUri).path("/rest/api/latest").build() + } + + fun getMySelf(): Promise { + val userUri = UriBuilder.fromUri(baseUri).path("myself").build() + return getAndParse(userUri, userJsonParser) + } +} diff --git a/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/telegram/TelegramDto.kt b/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/telegram/TelegramDto.kt index 1248204..216e88c 100644 --- a/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/telegram/TelegramDto.kt +++ b/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/telegram/TelegramDto.kt @@ -46,6 +46,6 @@ data class TelegramCommand( } fun isAnonymous() : Boolean{ - return chat == null + return chat?.jiraId == null && chat?.state != State.WAIT_APPROVE } } \ No newline at end of file diff --git a/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/telegram/TelegramUpdateManager.kt b/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/telegram/TelegramUpdateManager.kt index b659b06..d7979a6 100644 --- a/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/telegram/TelegramUpdateManager.kt +++ b/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/telegram/TelegramUpdateManager.kt @@ -5,6 +5,7 @@ import com.github.mikesafonov.jira.telegram.service.AuthorizationService import com.github.mikesafonov.jira.telegram.service.ChatService import mu.KotlinLogging import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional import org.telegram.telegrambots.meta.api.objects.Message import org.telegram.telegrambots.meta.api.objects.Update @@ -22,6 +23,7 @@ class TelegramUpdateManager( /** * process [update] */ + @Transactional fun onUpdate(update: Update) { if (update.hasMessage() && update.message.hasText()) { val telegramCommand = toCommand(update.message) @@ -45,7 +47,9 @@ class TelegramUpdateManager( command: TelegramCommand, nextState: State ) { - if (command.chat != null && command.chat.state != nextState) { + if (command.chat == null) { + chatService.addChatInState(command.chatId, null, nextState) + } else if (command.chat.state != nextState) { command.chat.state = nextState chatService.save(command.chat) } diff --git a/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/telegram/handlers/JiraAuthApproveTelegramCommandHandler.kt b/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/telegram/handlers/JiraAuthApproveTelegramCommandHandler.kt index b427987..57f7033 100644 --- a/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/telegram/handlers/JiraAuthApproveTelegramCommandHandler.kt +++ b/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/telegram/handlers/JiraAuthApproveTelegramCommandHandler.kt @@ -2,6 +2,8 @@ package com.github.mikesafonov.jira.telegram.service.telegram.handlers import com.github.mikesafonov.jira.telegram.config.conditional.ConditionalOnJiraOAuth import com.github.mikesafonov.jira.telegram.dao.State +import com.github.mikesafonov.jira.telegram.service.ChatService +import com.github.mikesafonov.jira.telegram.service.jira.JiraApiService import com.github.mikesafonov.jira.telegram.service.jira.JiraAuthService import com.github.mikesafonov.jira.telegram.service.telegram.TelegramClient import com.github.mikesafonov.jira.telegram.service.telegram.TelegramCommand @@ -18,6 +20,8 @@ private val logger = KotlinLogging.logger {} @ConditionalOnJiraOAuth class JiraAuthApproveTelegramCommandHandler( private val jiraAuthService: JiraAuthService, + private val jiraApiService: JiraApiService, + private val chatService: ChatService, telegramClient: TelegramClient ) : BaseCommandHandler(telegramClient) { @@ -33,7 +37,13 @@ class JiraAuthApproveTelegramCommandHandler( } else { try { jiraAuthService.createAccessToken(id, command.text!!) - replaceUserMessage(id, messageId, "Authorization success!") + val username = jiraApiService.getMySelf(id)?.name + if (username == null) { + replaceUserMessage(id, messageId, "Can't get username from jira!") + } else { + chatService.updateJiraId(id, username) + replaceUserMessage(id, messageId, "Authorization success!") + } } catch (e: HttpResponseException) { logger.error(e.message, e) val message = "${e.statusCode} ${e.content}" diff --git a/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/telegram/handlers/JiraAuthTelegramCommandHandler.kt b/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/telegram/handlers/JiraAuthTelegramCommandHandler.kt index d013a2a..fff6dd1 100644 --- a/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/telegram/handlers/JiraAuthTelegramCommandHandler.kt +++ b/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/telegram/handlers/JiraAuthTelegramCommandHandler.kt @@ -20,7 +20,7 @@ class JiraAuthTelegramCommandHandler( telegramClient: TelegramClient ) : BaseCommandHandler(telegramClient) { override fun isHandle(command: TelegramCommand): Boolean { - return command.isInState(State.INIT) && command.isMatchText("/auth") + return command.isAnonymous() } override fun handle(command: TelegramCommand): State { diff --git a/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/telegram/handlers/MyIssuesTelegramCommandHandler.kt b/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/telegram/handlers/MyIssuesTelegramCommandHandler.kt index 28b9fdd..381696c 100644 --- a/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/telegram/handlers/MyIssuesTelegramCommandHandler.kt +++ b/src/main/kotlin/com/github/mikesafonov/jira/telegram/service/telegram/handlers/MyIssuesTelegramCommandHandler.kt @@ -24,7 +24,7 @@ class MyIssuesTelegramCommandHandler( override fun handle(command: TelegramCommand): State { val jiraId = command.chat!!.jiraId val telegramId = command.chat.telegramId - if(command.authorization == null){ + if(command.authorization == null || jiraId == null){ telegramClient.sendTextMessage(telegramId, "You must be logged in to use this command. Use the /auth to log in to JIRA") } else { val myIssues = jiraApiService.getMyIssues(telegramId, jiraId) diff --git a/src/main/resources/db/migration/h2/V1_0_6__nullableJiraId.sql b/src/main/resources/db/migration/h2/V1_0_6__nullableJiraId.sql new file mode 100644 index 0000000..70c5a99 --- /dev/null +++ b/src/main/resources/db/migration/h2/V1_0_6__nullableJiraId.sql @@ -0,0 +1 @@ +ALTER TABLE chats ALTER COLUMN jira_id DROP NOT NULL; diff --git a/src/main/resources/db/migration/mysql/V1_0_6__nullableJiraId.sql b/src/main/resources/db/migration/mysql/V1_0_6__nullableJiraId.sql new file mode 100644 index 0000000..f17fd4d --- /dev/null +++ b/src/main/resources/db/migration/mysql/V1_0_6__nullableJiraId.sql @@ -0,0 +1 @@ +ALTER TABLE chats MODIFY COLUMN jira_id varchar(100); diff --git a/src/main/resources/db/migration/postgresql/V1_0_6__nullableJiraId.sql b/src/main/resources/db/migration/postgresql/V1_0_6__nullableJiraId.sql new file mode 100644 index 0000000..70c5a99 --- /dev/null +++ b/src/main/resources/db/migration/postgresql/V1_0_6__nullableJiraId.sql @@ -0,0 +1 @@ +ALTER TABLE chats ALTER COLUMN jira_id DROP NOT NULL;