Receive Payload from Space
With some types of applications, Space acts as a client – it sends POST requests to the application endpoint with JSON payload inside. For example, when a user types in the application chat channel, Space sends payload containing the user input; if the application is subscribed to webhook notifications, Space sends payload with event details, and so on.
The payload consists of the name of a payload class, class-specific payload data, and data that is common for all payload types (like Space URL, user ID, and so on).
For example, this is a sample payload from Space when a user sends help
command to a chatbot:
{
// Payload class
"className": "MessagePayload",
// Class-specific payload data
"message": {
"messageId": "CsT000CsT",
"channelId": "3FhQeS2URbeY",
"messageData": null,
"body": {
"className": "ChatMessage.Text",
"text": "help "
},
"attachments": null,
"externalId": null,
"createdTime": "2021-05-21T17:01:33.767Z"
},
// Common payload data
"accessToken": "",
"verificationToken": "abc1234",
"userId": "1mEGCd1FvoAh",
"serverUrl": "https://mycompany.jetbrains.space",
"clientId": "2ulA3W2Vltg6",
"orgId": "8fd4d79a-d164-4a71-839a-ff8f8bcd6beb"
}
To help you process different kinds of payload, Space SDK provides a number of classes. All these classes implement the ApplicationPayload
interface.
Class | Description | Relevant for |
---|---|---|
JetBrains Marketplace sends this payload during application verification. | Multi-org applications distributed through JetBrains Marketplace. | |
A Space instance sends this payload when the application is uninstalled from this instance. | Multi-org applications distributed via links or through JetBrains Marketplace. | |
Initializes the application that was installed from JetBrains Marketplace. | Multi-org applications distributed via links or through JetBrains Marketplace. | |
Requests a list of available commands. Space sends this payload type when a user types slash | Chatbots, slash commands. | |
Contains info about the custom menu item selected by a user. | Applications that extend Space context menus with custom items. | |
Contains a message sent by a user to the application from a chat channel. | Chatbots, slash commands. | |
Contains info about the action executed by a user in the application chat channel. Typically, this is a result of user interaction with a UI control in a chat message, for example, clicking a button. | Chatbots, slash commands. | |
Contains info about the event occurred in Space. The application must be subscribed to this event with a corresponding webhook. | Applications that use webhooks to receive notifications about Space events. |
To help you handle the payload, Space SDK provides the Space
helper object. This is how you can use it to process the payload depending on its type. For example, in a Ktor application:
@file:OptIn(ExperimentalSpaceSdkApi::class)
package com.example
import io.ktor.application.*
import io.ktor.http.*
import io.ktor.request.*
import io.ktor.response.*
import io.ktor.routing.*
import space.jetbrains.api.ExperimentalSpaceSdkApi
import space.jetbrains.api.runtime.Space
import space.jetbrains.api.runtime.helpers.RequestAdapter
import space.jetbrains.api.runtime.helpers.SpaceHttpResponse
import space.jetbrains.api.runtime.helpers.processPayload
import space.jetbrains.api.runtime.ktorClientForSpace
import space.jetbrains.api.runtime.types.InitPayload
import space.jetbrains.api.runtime.types.MenuActionPayload
import space.jetbrains.api.runtime.types.RefreshTokenPayload
import space.jetbrains.api.runtime.SpaceAppInstance
import space.jetbrains.api.runtime.helpers.SpaceAppInstanceStorage
// we use in-memory storage for storing Space instances
// in real life, use a persistent storage, e.g., a database
val spaceInstances = HashMap<String, SpaceAppInstance>()
object AppInstanceStorage : SpaceAppInstanceStorage {
override suspend fun loadAppInstance(clientId: String): SpaceAppInstance? {
return spaceInstances[clientId]
}
override suspend fun saveAppInstance(appInstance: SpaceAppInstance) {
spaceInstances[appInstance.clientId] = appInstance
}
}
val ktorClient = ktorClientForSpace()
class KtorRequestAdapter(private val call: ApplicationCall) : RequestAdapter {
override suspend fun receiveText() = call.receiveText()
override fun getHeader(headerName: String) = call.request.headers[headerName]
override suspend fun respond(httpStatusCode: Int, body: String) {
call.respond(HttpStatusCode.fromValue(httpStatusCode), body)
}
}
fun Routing.routes() {
post("api/myapp") {
Space.processPayload(KtorRequestAdapter(call), ktorClient, AppInstanceStorage) { payload ->
// analyze payload type
when (payload) {
is InitPayload -> {
// initialize the app...
SpaceHttpResponse.RespondWithOk
}
is MenuActionPayload -> {
// react to menu item click
// val result = ...
SpaceHttpResponse.RespondWithResult(result)
}
is RefreshTokenPayload -> {
// save refresh token...
SpaceHttpResponse.RespondWithOk
}
else -> {
SpaceHttpResponse.RespondWithOk
}
}
}
}
}
In a more generic approach, you can use only the readPayload(body: String): ApplicationPayload
SDK helper function to handle payload:
fun main() {
embeddedServer(Netty, port = 8080) {
routing {
post("/api/from-space") {
val body = call.receiveText()
val payload = readPayload(body)
// analyze payload type
when (payload) {
is MessagePayload -> {
println("This is MessagePayload")
call.respond(HttpStatusCode.OK, "")
}
else -> call.respond(HttpStatusCode.BadRequest,
"Unsupported payload type")
}
}
}
}.start(wait = true)
}
Regardless of the type, the payload that comes from Space always contains some common information: user ID, server URL, and other. To help you get this data, Space SDK provides a number of extension methods for the ApplicationPayload
interface.
val payload = readPayload(body)
// ID of a user who initiated the request
// Not available for the payload types that
// do not imply user interaction
val userId = payload.userId
// Client ID issued to the app
// during the app registration
val clientId = payload.clientId
// URL of your Space instance
val url = payload.serverUrl
// ID of your organization in Space
val orgId = payload.orgId
// Verification token
// used to verify Space in the app
// null if you choose other verification way
val token = payload.verificationToken
Thanks for your feedback!