Dodatek Tempo jest chyba najpopularniejszym pluginem do rejestracji i zarządzania czasem pracy zespołów w Jirze. Oprócz logowania przepracowanych godzin oferuje szereg narzędzi wspierających użytkownika i menadżerów: indywidualne harmonogramy, obsługa świąt i innych dni wolnych, tworzenie i akceptowanie okresów rozliczeniowych, wyliczanie należności za przepracowane godziny, dostęp do poszczególnych funkcjonalności na podstawie ról użytkowników i wiele, wiele innych.
Jakkolwiek plugin od strony funkcjonalnej jest obecnie bezkonkurencyjny, to sposób jego integracji z Jirą implikuje pewne problemy, które dają o sobie znać w trakcie pisania skryptów komunikujących się z wtyczką. Jednym słowem Tempo nie zważa na elegancję, tylko brutalnie wchodzi z butami w ekosystem Jiry przynosząc swoje własne worklogi, bazę użytkowników, role itp. W rezultacie w systemie są worklogi, użytkownicy i uprawnienia sterowane zarówno przez Tempo, jak i przez Jirę.
Plugin oferuje dwie metody pobierania i modyfikacji danych poza interfejsem GUI, które mogą być wykorzystywane przy automatycznych operacjach na worklogach za pomocą skryptów.
Tempo REST API
Użytkownicy Jiry w wersji Server / Data Center mają do dyspozycji interfejs REST API o dość szerokich możliwościach. W skład API wchodzi obecnie siedem interfejsów, dzięki którym można wysyłać zapytania ze skryptów Groovy (ScriptRunner, JMWE) oraz z zewnętrznych aplikacji po odpowiedniej autoryzacji.

Wykonanie zapytania sprowadza się do przygotowania ciała w formacie JSON, zgodnego z dokumentacją API, a następnie wysłanie go metodą GET, POST, PUT lub DEL. W zależności od rodzaju operacji można przetworzyć zwrócone dane lub odebrać informację o statusie wykonania żądania.
W poniższym przykładzie zostanie wykonana operacja wstawienia worklogu Tempo dowolnemu użytkownikowi. W tym celu należy wyszukać w dokumentacji endpoint Creates worklogs i zaimplementować go w skrypcie Groovy’ego (ewentualnie w samodzielnej aplikacji).

import java.text.SimpleDateFormat;
import com.atlassian.jira.component.ComponentAccessor;
// 1. Inicjalizacja daty początkowej i końcowej dla worklogu.
def worklogStartDate = new SimpleDateFormat("yyyy-MM-dd").parse("2023-01-04");
def worklogEndDate = new SimpleDateFormat("yyyy-MM-dd").parse("2023-01-04");
// 2. Pobranie obiektu użytkownika, dla którego będzie wstawiany worklog.
def worker = ComponentAccessor.getUserManager().getUserByName("jkalinowski");
// 3. Przygotowanie ciała zapytania na podstawie dokumentacji REST Enpointu
// originTaskId - klucz zgłoszenia, do którego ma być dodany worklog.
// timeSpentSeconds, billableSeconds - jeden dzień to 28800 sekund.
def body = """
{
"attributes": { },
"billableSeconds": 28800,
"comment": "Praca w ${worklogStartDate.format("yyyy-MM-dd")}",
"endDate": "${worklogEndDate.format("yyyy-MM-dd")}",
"originTaskId": "TASK-123",
"remainingEstimate": 0,
"started": "${worklogStartDate.format("yyyy-MM-dd")}",
"timeSpentSeconds": 28800,
"worker": "${worker.getKey()}"
}
""";
// 4. Wysłanie zapytania.
def urlHttp = new URL('https://jira/rest/tempo-timesheets/4/worklogs').openConnection() as HttpURLConnection;
urlHttp.setRequestMethod("POST");
urlHttp.doOutput = true;
urlHttp.setRequestProperty("Content-Type", "application/json;charset=UTF-8");
// Sposób wygenerowania hashcode: ("<username>:<password>").getBytes().encodeBase64().toString();
urlHttp.addRequestProperty("Authorization", "Basic <hashcode>");
urlHttp.getOutputStream().write(body.getBytes("UTF-8"));
urlHttp.connect();
// Zwrócenie kodu odpowiedzi.
return urlHttp.getResponseCode();
Tempo Java API
Drugim sposobem komunikacji z Tempo jest skorzystanie z bibliotek Java Tempo, do których niestety nie ma żadnej dokumentacji oprócz jakichś szczątkowych wskazówek w Atlassian Community i starej strony ScriptRunnera. W poniższym przykładzie została przedstawiona analogiczna operacja wstawienia worklogu za pomocą Java API i ScriptRunnera.
import java.text.SimpleDateFormat;
import com.atlassian.jira.component.ComponentAccessor;
import com.onresolve.scriptrunner.runner.customisers.WithPlugin;
import com.tempoplugin.worklog.v4.rest.TimesheetWorklogBean;
import com.onresolve.scriptrunner.runner.customisers.PluginModule;
import com.tempoplugin.worklog.v4.rest.InputWorklogsFactory;
import com.tempoplugin.worklog.v4.services.WorklogService;
// 1. Podmapowanie bibliotek Java z zainstalowanego pluginu Tempo.
@WithPlugin("is.origo.jira.tempo-plugin")
// 2. Wstrzyknięcie komponentu InputWorklogsFactory z pluginu Tempo.
@PluginModule
InputWorklogsFactory inputWorklogsFactory;
// 3. Wstrzyknięcie komponentu WorklogService z pluginu Tempo.
@PluginModule
WorklogService worklogService;
// 4. Inicjalizacja daty początkowej i końcowej dla worklogu.
def worklogStartDate = new SimpleDateFormat("yyyy-MM-dd").parse("2023-01-04");
def worklogEndDate = new SimpleDateFormat("yyyy-MM-dd").parse("2023-01-04");
// 5. Pobranie obiektu użytkownika, dla którego będzie wstawiany worklog.
def worker = ComponentAccessor.getUserManager().getUserByName("jkalinowski");
// 6. Wstawienie worklogu.
// Inicjalizacja obiektu worklogu.
def timesheetWorklogBean = new TimesheetWorklogBean.Builder()
.issueIdOrKey("TASK-123")
.comment("Praca w ${worklogStartDate.format("yyyy-MM-dd")}")
.startDate(worklogStartDate.format("yyyy-MM-dd"))
.workerKey(worker.getKey())
.timeSpentSeconds(28800)
.remainingEstimate(0)
.build();
def inputWorklogs = inputWorklogsFactory.buildForCreate(timesheetWorklogBean);
// Wstawienie worklogu do zgłoszenia.
worklogService.createTempoWorklogs(inputWorklogs);
Podsumowanie
Testując obie metody modyfikacji na dużych zbiorach danych można zauważyć, że operacje za pomocą Java API wykonują się znacząco szybciej. Co może mieć znaczenie przy niektórych rozwiązaniach. Na uwagę zasługuje również kwestia ewentualnych błędów, powstałych w trakcie próby wstawienia worklogu. Zawsze może zdażyć się sytuacja, że będziemy próbować ustawić worklog osobie, która nie ma z jakichś względów uprawnień do logowania czasu. Oba rozwiązania oczywiście zwrócą tę informację, ale w odmienny sposób.
REST API – zwrócenie informacji w kodzie odpowiedzi i ciele odpowiedzi. Przy tak napisanym skrypcie, jak w pierwszym przykładzie, aplikacja nie zatrzyma się, a jedynym skutkiem będzie nie wstawienie worklogu danej osobie.
Java API – zostanie wyrzucony wyjątek, który zatrzyma aplikację. Do poprawnego działanie niezbędne jest ujęcie wywołania metody createTempoWorklogs w blok try… catch i obsłużenie błędu, aby nie spowodować zatrzymania skryptu.