Space is a team tool that integrates chats, meetings, git hosting, automation, and much more. It has an HTTP API that lets you integrate third-party apps and workflows. That API is huge, though!
In this session, we will not spend too much time talking about Space. Instead, we'll focus on its API. We'll look at the process, thought, and technology behind the Kotlin SDK for Space, and how we make that massive API more digestible. You will see how we used code generation, domain-specific language, and how building extensibility for third-party developers means you will have to guide them into the pit of success.
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!
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)
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
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?
)
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())
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
}
}
}
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
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