Space logo

Space

The intelligent code collaboration platform

Space is pivoting to SpaceCode, focused on Git hosting and code reviews. Learn more →

How-To's

Using Space Automation to Optimize Routine Workflows


Space Automation is more than a CI/CD system. It lets you automate routine tasks that require data from other Space modules or tasks that need to integrate with external systems.

Let’s look at a practical example! In this blog post, you will see how to use external libraries in Space Automation to keep your personal agenda and Space calendar synchronized.

Synchronize a personal iCal calendar with Space calendar

Keeping a good work/life balance is not always easy — especially in these times of remote work, when your work day may be mixed with personal events. Many people, including me, bring some structure by using personal, family, and work calendars.

Have you had a colleague schedule a meeting because your work calendar showed you were available, while you actually planned to do some errands or maybe pick up your kids from school and continue work later? It’s happened to many of us.

Manually keeping those calendars up-to-date when plans change is a real pain. Let’s automate this with Space Automation!

Create a Private Project and Git Repository

To use Space Automation, you will need a Git repository that’s part of a project. Given we’re synchronizing a personal calendar, it’s a good idea to not do this in a team project.

Anyone in a Space organization can create a project, add a Git repository to it, and optionally provide access to others. If you are using the Organization plan, you can also create a private project, which is perfect for our use case.

In Space, create a new project (use the + button in the navigation bar). Give it a name and a key, and make it a Private project.

Create a new Space project

This project is now your personal Space project. It’s only visible to you, and you can create documents, issues, checklists, Git repositories, and more.

We need a Git repository, so let’s create one. You can give it a name and a description, and keep all of the defaults. This will initialize the repository and create a default README.md file in it.

Cerate a private Git repository in JetBrians Space

In this repository, you can now create a new file named .script.kts, which defines the job to synchronize our calendar.

Create an Automation Script

To start working with Automation, you need a .space.kts file in your repository root. In that script, you can define one or more jobs that each run one or more steps.

The full script source code is available at the end of this post, but let’s first build it up so you can see what it does.

Job Outline

For our calendar synchronization task, we’ll create one job with one step. Let’s start with the outline of our automation script, and then explain:

job("Synchronize agenda") {
   startOn {
       gitPush { enabled = true } // run on git push...
       schedule { cron("0 12 * * *") } // ...and on schedule
   }

   failOn {
       timeOut { timeOutInMinutes = 15 }
   }

   container("openjdk:11") {

       env["SPACE_USERNAME"] = Params("sa_username")
       env["ICAL_URL"] = Secrets("sa_ical_url")

       kotlinScript { api ->

           // ...
       }
   }
}

This is not a lot of code, but there are already some things to unpack:

  • This job is named “Synchronize agenda”.
  • The startOn block configures this job to start whenever a change is made to this script (gitPush), and on a schedule (every day at 12 AM).
  • The failOn block configures the failure conditions for this job. By default, jobs will fail when they return a non-zero exit code, or in other words: when an error occurs. For this script, I added a timeout so that when the script runs for more than 15 minutes, it stops running and fails.

The container block is the first (and only) step in this job. It will run all code in the kotlinScript block, in the openjdk:11 container image available on Docker hub.

One last bit here is the use of parameters and secrets. To make it easier to set the iCal URL to synchronize with, we’re storing it as a secret in the project, instead of embedding it in the script. Additionally, by making it a secret, we make sure nobody else will ever be able to see the URL to your iCal calendar, as it sometimes contains a secret token.

If you’re using this script in your own project, make sure to configure these secrets and parameters:

  • A secret named sa_ical_url. Set the value to that of your personal calendar’s iCal URL, and make sure to replace webcal:// at the start with https://.
  • A parameter with your Space username in it.

Note that I chose to make sa_ical_url a secret, so its value can not be retrieved from the Space UI. Very often, these URLs contain a secret token of some sort, so let’s keep them a secret.

Reference External Libraries in Space Automation

The source calendar data will come from an iCal URL, so we will need to be able to parse that data format and do something useful with it.

A Space Automation script really is a Kotlin script, so you can use external libraries that do some of the heavy lifting. To handle iCal, I used the package search website to find a library called “biweekly”.

This library can be included in your .space.kts by adding a @file:DependsOn directive at the top. I’ll also be making an HTTP request using OkHttp3 to download the iCal URL, so let’s reference that in one go:

@file:DependsOn("net.sf.biweekly:biweekly:0.6.4")
@file:DependsOn("com.squareup.okhttp3:okhttp:3.12.1")
import biweekly.component.*
import biweekly.util.*
import biweekly.ICalendar
import biweekly.io.text.ICalReader
import java.util.Date
import okhttp3.OkHttpClient
import okhttp3.Request

job("Synchronize agenda") {
   // ...
}

With that out of the way, let’s move on to implementing the actual synchronization logic in the kotlinScript block.

Synchronize Space Calendar

In the kotlinScript block, we will do the following:

  1. Get current meetings from Space Calendar
  2. Get entries from the iCal URL
  3. Delete meetings from Space if needed
  4. Create new meetings based on the iCal URL

I won’t go into full detail for each of these steps; the full .script.kts can be found at the end of this post. However, there are a couple of noteworthy things to look at.

Access Space APIs

When a job step runs kotlinScript, it has access to all Space APIs, including the Calendar. The following snippet retrieves the user profile that was configured as a parameter earlier, and then retrieves meeting information for this user:

kotlinScript { api ->
    // ...

    println("Get current meetings from Space...")
    val userProfile = api.space().teamDirectory.profiles
       .getProfile(ProfileIdentifier.Username(username))

    val spaceMeetings = api.space().calendars.meetings
       .getAllMeetings(
           profiles = listOf(userProfile.id),
           startingAfter = startingAfter,
           endingBefore = endingBefore,
           includePrivate = true,
           includeArchived = false,
           includeMeetingInstances = true)

    // ...
}

Keep in mind that Space Automation always runs in the context of an Automation user, with limited privileges. In other words, Automation only has access to availability information and public meetings but never to private meeting details.

Functions and Extension Functions

I can’t stress this enough: Space Automation scripts are Kotlin scripts, so you can make use of many of its language features. When writing more complex Automation scripts, you may want to extract common logic into functions, and even use extension functions!

Here’s an example. To download data from your iCal URL, our script uses a retrieveUrl() function, which is extracted from the main portion of the Automation script. This makes the “business logic” more readable:

// ...

job("Synchronize agenda") {

   // ...

   container("openjdk:11") {

       // ...

       kotlinScript { api ->

           // ...

           // 3. Get entries from external calendar
           println("Get entries from external calendar...")
           val icalEvents = mutableListOf<VEvent>()
           val icalResponse = retrieveUrl(icalUrl)

           // ...
       }
   }
}

fun retrieveUrl(icalUrl: String): String {
   val request = Request.Builder()
       .url(icalUrl)
       .build()

   val client = OkHttpClient()
   return client.newCall(request)
       .execute()
       .use { it.body()!!.string() }
}

Note that when you have the Space plugin installed in IntelliJ IDEA, you can also make use of refactoring tools, navigation, code completion, and so on.

Space plugin in IntelliJ IDEA IDE provides tools to refactor

Check out the Space Guide for more examples.

Subscribe to Automation Job Status

After configuring the parameters and secrets, and pushing the complete .space.kts, you will see that the synchronization script is triggered. From now on, the script will also run based on the schedule defined in this script.

When something goes wrong during synchronization, you may want to get a notification. In your private project, find Jobs and then navigate to the job you just created. In the top toolbar, you can then create a subscription:

Subscribe to Spae Automation job status notifications

I only want to get notifications when the job fails, but if you want a daily notification when this job runs, you can configure this here as well.

For both successful and failed jobs, you can always consult log information and see what the calendar synchronization script has been doing:

Inspect automation logs to troubleshoot Space Automation jobs

Complete Automation Script

As promised, here’s the complete .script.kts.

Conclusion

Throughout this blog post, we have seen a number of elements. There’s the iCal to Space Calendar synchronization of course, but there’s another, more important item to remember: Space Automation is much more than CI/CD!

Space Automation gives you a powerful tool to automate tasks that require data from other Space modules through the API. And when you need to integrate with external systems, you can use external libraries to make accessing them easier.

The techniques from this blog post can be used for other tasks, too! Some ideas that come to mind:

  • Send a daily reminder to your team’s chat channel to post their daily standup there.
  • Build a regular report of open issues in a project, store it in an Excel file, and send it to a chat channel.
  • Publish Twitter posts that your marketing team schedules in Space Issues.

I’m looking forward to hearing what you would build – use the comments below!

image description