-
1.
Building a friendly Kotlin SDK to
connect to JetBrains Space
Maarten Balliauw
@maartenballiauw
-
2.
Agenda
JetBrains Space
JetBrains Space HTTP API
Building a Kotlin SDK for the Space HTTP API
Code generation
Developer experience
-
3.
JetBrains Space
-
4.
JetBrains Space
Integrated Team Environment
https://jetbrains.space
People
Teams
Projects
-
Workspaces
for teams
Collaboration
-
5.
JetBrains Space
demo
-
6.
The Space HTTP API
-
7.
Integrated Team Environment
More information in Space == more value from Space
Internal integrations out of the box
Vacation shown in profile
Blog post targeted to team/location
Calendar integration in blog post
Everything can be a ToDo item
...
What with external integrations? Extensibility?
-
8.
Everything as an HTTP API!
What do you want to build? Which integration do you need?
Import data
Post to chat
Manage users, teams, and locations programatically
Build dashboards
???
“Open up everything”
Large HTTP API surface...
HTTP API Playground to explore!
-
9.
HTTP API Playground
demo
-
10.
$fields
Top-level fields returned by default
Unless $fields query parameter used
Use $fields to be specific, or get nested fields
$fields=id,icon,name
$fields=id,name,boards(id,description)
$fields=id,name,boards(id,info(columns))
$fields=*,boards(id,description)
-
11.
Building a Kotlin SDK
for the Space HTTP API
-
12.
Hello, Kotlin! 👋
Goal: make our users succesful at using Space in their team/organization
Provide a ready-to-use SDK
Minimal configuration needed
Discoverability
TL;DR: Lead integration developers on the shortest path to success
-
13.
How? Handcrafted
No.
Over 150 API endpoints
Over 700 object types
Churn during Space EAP / Space Beta
-
14.
How? OpenAPI?
Use openapi-generator, generate based on OpenAPI / Swagger document
Some incompatible object types needed custom code generation
-
15.
How? Custom code generation
Space exposes HTTP API Model
Enumerations
DTOs (objects passed around)
Resources
+ data types, nullability,
default values, ...
https://your.jetbrains.space/api/http/http-api-model?$fields=dto,enums,urlParams,resources(*,nestedResources!)
-
16.
Enumeration model
class HA_Enum(
val id: TID,
val name: String,
val values: List<String>,
val deprecation: HA_Deprecation?
)
data class HA_Deprecation(
val message: String,
val since: String,
val forRemoval: Boolean
)
-
17.
Enumeration code generator
fun visit(enumType: HA_Enum) = buildString {
enumType.deprecation?.let {
appendLine("@Deprecated(message = "${it.message}") }
val enumClassName = enumType.name.kotlinClassNameJoined()
appendLine("enum class $enumClassName {")
enumType.values.forEach {
appendLine("$it,")
}
appendLine("}")
}
enum class AbsenceListMode {
All,
WithAccessibleReasonUnapproved,
WithAccessibleReasonAll
}
-
18.
DTO model
class HA_Dto(
val id: TID,
val name: String,
val fields: List<HA_DtoField>,
val hierarchyRole: HierarchyRole,
val extends: Ref?,
val implements: List<Ref>,
val inheritors: List<Ref>,
val deprecation: HA_Deprecation?,
val record: Boolean
)
class HA_DtoField(val field: HA_Field, val extension: Boolean)
data class HA_Field(
val name: String,
val type: HA_Type,
val deprecation: HA_Deprecation?,
val optional: Boolean,
val defaultValue: HA_DefaultValue?
)
-
19.
Endpoints model
Endpoint id, name, base path
Resource id, name, path, parameters
Resource id, name, path, parameters
Resource id, name, path, parameters
...
+ parameter types
+ return types
+ default values (const, variable)
-
20.
Custom code generation
demo
-
21.
Strings? KotlinPoet?
Generally, a few ways to generate code:
String-based
Write strings directly
Use a template engine
Easy to recognize what gets written
Model-based
KotlinPoet https://square.github.io/kotlinpoet/
Safety (e.g. can’t forget to write class name)
-
22.
KotlinPoet
Kotlin and Java API for generating .kt source files
Ensures all properties of code are in place (modifier, type name, ...)
Callable references
Formatting of generated code
https://square.github.io/kotlinpoet/
FunSpec.builder("main")
.addStatement("val helloWorld = %L", helloFunction.reference())
.build()
-
23.
Extension functions everywhere
Many places where we need:
Kotlin class name from string
Kotlin classs from Space model type (Enum/DTO/URL parameter/...)
fun String.kotlinClassName(): List<String> = split(".")
fun HA_Dto.getClassName() =
ClassName("space.jetbrains.api.runtime.types", name.kotlinClassName())
fun HA_UrlParameterOption.getClassName() =
ClassName("space.jetbrains.api.runtime.types", optionName.kotlinClassName())
-
24.
Developer experience
-
25.
The ideal “Getting started”
1. Register application in Space
2. Add to Gradle file
dependencies {
implementation "org.jetbrains:space-sdk-jvm:$space_version"
}
3. Create connection
val spaceClient = SpaceHttpClient(HttpClient())
.withServiceAccountTokenSource(
"client-id",
"client-secret",
"https://your.jetbrains.space/")
4. Profit!
-
26.
Large API surface
-
27.
Remember $fields ?
Top-level fields returned by default
Unless $fields query parameter used
Use $fields to be specific, or get nested fields
-
28.
$fields
Top-level fields by default, partial builder to be specific
val memberProfile = spaceClient.teamDirectory.profiles
.getProfile(ProfileIdentifier.Username("Heather.Stewart"))
val memberProfile = spaceClient.teamDirectory.profiles
.getProfile(ProfileIdentifier.Username("Heather.Stewart")) {
defaultPartial() // with all top level fields
managers { // include managers
id() // with their id
username() // and their username
name { // and their name
firstName() // with firstName
lastName() // and firstName
}
}
}
-
29.
API playground
-
30.
Building a chat message...
spaceClient.chats.messages.sendMessage(
recipient = MessageRecipient.Channel(ChatChannel.FromName(chatChannelName)),
content = ChatMessage.Block(
style = MessageStyle.SUCCESS,
outline = MessageOutline(icon = null, text = "Have you tried JetBrains Space?"),
sections = listOf(
MessageSection(
header = "JetBrains Space",
elements = listOf(
MessageText(accessory = MessageIcon(ApiIcon("space"), MessageStyle.SUCCESS),
content = "JetBrains Space is an Integrated Team Environment."),
MessageText(accessory = null, content = "Have you tried JetBrains Space?"),
MessageDivider,
MessageText(accessory = null, content = "Get access at https://www.jetbrains.com/space/")
),
footer = "Check it out at https://www.jetbrains.com/space/"
)
),
messageData = null
),
unfurlLinks = false
)
-
31.
spaceClient.chats.messages.sendMessage(
recipient = MessageRecipient.Channel(ChatChannel.FromName(chatChannelName)),
content = message {
style = MessageStyle.SUCCESS
outline = MessageOutline(icon = null, text = "Have you tried JetBrains Space?")
section {
header = "JetBrains Space"
textWithIcon("JetBrains Space is an Integrated Team Environment.", "space",
MessageStyle.SUCCESS)
text("Have you tried JetBrains Space?")
divider()
text("Get access at https://www.jetbrains.com/space/")
footer = "Check it out at https://www.jetbrains.com/space/"
}
},
unfurlLinks = false
)
Building a chat message...
-
32.
Identifiers and factory methods
-
33.
Developer experience
demo
-
34.
Conclusion
-
35.
Conclusion
JetBrains Space is an Integrated Team Environment
JetBrains Space HTTP API exposes everything
Building a Kotlin SDK for the Space HTTP API
Goal: make our users succesful at using Space
Provide a ready-to-use SDK
Minimal configuration needed
Discoverability
Code generation
Focus on developer experience
-
36.
Thank you!
https://blog.maartenballiauw.be
@maartenballiauw
https://pixabay.com
Clear cookies and auth in demo.jetbrains.space!
Setup everything to work with demo.jetbrains.space
space-api-client, space-api-client-samples, and space-app-tutorials open in IJ
Show home page and mention what is shown (relevat info for me)
Show project, mention repo’s, issues, checklists, ...
Always possible to annotate and ask questions inline in code. Other folks will see this pop up in chat, so they do not necesarily have to switch context if it’s a minor question.
Mention issues, and again, chat integration.
Everything can be a to-do as well, use the lightning bolt icon.
Automation/CI integrated.
Absences integrated is where things get interesting. Who should I contact for certain responsibility? Is that other perso naround to look at my code review?
Blogs. Organization-wide, for specific teams or locations. E.g. To share post-mortems, or team announcements, without overloading others. Again, relevant infomation to you.
Many other things such as a knowledge base, artifact repo’s for NuGet, NPM, Docker, ...
Show HTTP API playground, send a chat message or something like that. Or get profile info.
Mention different endpoints, API’s grouped by functionality
Mention request builder, all that, easy to reproduce
Show getting a profile, selecting properties, and highlight $fields
Show space-api-client
API model classes, show: HA_Enum, HA_Dto, HierarchyRole enumeration
Show Main.kt to see how it is bootstrapped
Show space/jetbrains/api/generator/GenerateTypes.kt:44
Using KotlinPoet (more on that later)
Show the extension methods, like kotlinClassNameJoined() and annotationSpecs.deprecation
KotlinPoet in Kotlin SDK
Show space-api-client-samples
Show connection setup
Show using team directory
Get profiles with their name – explain $fields here.
No partial -> get top level
Partial -> specific, but has code completion
Show exception case where we help you out (unknown property accessor)
Chat: show message builder DSL
Show identifiers with chat