space-slack-sync/src/main/kotlin/org/jetbrains/spaceSlackSync/space/SpacePayloadProcessing.kt (102 lines of code) (raw):

package org.jetbrains.spaceSlackSync.space import io.ktor.http.* import io.ktor.server.application.* import io.ktor.server.request.* import io.ktor.server.response.* import kotlinx.coroutines.launch import kotlinx.coroutines.slf4j.MDCContext import kotlinx.coroutines.withContext import org.jetbrains.spaceSlackSync.MDCParams import org.jetbrains.spaceSlackSync.homepage.spaceHttpClient import org.jetbrains.spaceSlackSync.newTraceId import org.jetbrains.spaceSlackSync.platform.Server import org.jetbrains.spaceSlackSync.withSpaceLogContext import org.slf4j.Logger import org.slf4j.LoggerFactory import space.jetbrains.api.runtime.Space import space.jetbrains.api.runtime.SpaceClient import space.jetbrains.api.runtime.helpers.* import space.jetbrains.api.runtime.resources.applications import space.jetbrains.api.runtime.types.* import java.lang.invoke.MethodHandles suspend fun processSpaceCall(call: ApplicationCall) { withContext(MDCContext(mapOf(MDCParams.SPACE_CALL_TRACE_ID to newTraceId()))) { log.debug("Incoming API call from Space") try { doProcessSpaceCall(call) } catch (e: Exception) { log.error("Exception while handling a call from Space", e) call.respond(HttpStatusCode.InternalServerError) } } } private suspend fun doProcessSpaceCall(call: ApplicationCall) { val ktorRequestAdapter = object : RequestAdapter { override suspend fun receiveText() = call.receiveText() override fun getHeader(headerName: String) = call.request.header(headerName) override suspend fun respond(httpStatusCode: Int, body: String) = call.respond(HttpStatusCode.fromValue(httpStatusCode), body) } Space.processPayload( ktorRequestAdapter, spaceHttpClient, spaceAppInstanceStorage, onAuthFailed = { message, _ -> log.warn("Space request authentication failed - $message") SpaceHttpResponse.RespondWithCode(HttpStatusCode.BadRequest) }, payloadProcessor = { payload -> withSpaceLogContext(spaceAppClientId = appInstance.clientId, spaceUserId = payload.userId) { processSpacePayload(payload, call) } } ) } private suspend fun ProcessingScope.processSpacePayload( payload: ApplicationPayload, call: ApplicationCall, ): SpaceHttpResponse { return when (payload) { is InitPayload -> handleAppInstallation(clientWithClientCredentials()) is WebhookRequestPayload -> { when (val event = payload.payload) { is ChatMessageCreatedEvent, is ChatMessageDeletedEvent, is ChatMessageUpdatedEvent -> { call.application.launch(Server + MDCContext()) { processChatMessageFromSpace(event) } } } SpaceHttpResponse.RespondWithOk } else -> { log.info("Processed payload type ${payload::class.simpleName}") SpaceHttpResponse.RespondWithOk } } } private suspend fun handleAppInstallation(spaceClient: SpaceClient): SpaceHttpResponse { try { doHandleAppInstallation(spaceClient) log.info("Processed InitPayload from Space") } catch (e: Exception) { log.error("Exception during processing InitPayload", e) return SpaceHttpResponse.RespondWithCode(HttpStatusCode.InternalServerError) } return SpaceHttpResponse.RespondWithOk } private suspend fun doHandleAppInstallation(spaceClient: SpaceClient) { spaceClient.applications.setUiExtensions( GlobalPermissionContextIdentifier, listOf(ApplicationHomepageUiExtensionIn("/space-iframe")) ) spaceClient.applications.authorizations.authorizedRights.requestRights( application = ApplicationIdentifier.Me, contextIdentifier = GlobalPermissionContextIdentifier, rightCodes = listOf( PermissionIdentifier.ViewMemberProfiles ) ) } private val log: Logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass())