bookr-cli
Version:
A terminal-based CLI tool to book time in Jira using the Tempo plugin by parsing your current Git branch name
163 lines • 6.48 kB
JavaScript
/**
* Tempo API client for worklog-related tasks
* Docs: https://apidocs.tempo.io/
*/
import { loadConfigFromFile } from '../utils/config.js';
export class TempoClient {
constructor(baseUrl, apiToken) {
this.baseUrl = baseUrl;
this.apiToken = apiToken;
}
/**
* Fetch worklogs for a user for a given date range (Tempo API v4)
*/
async getWorklogsForUser(accountId, from, to) {
const url = `${this.baseUrl}/worklogs/user/${encodeURIComponent(accountId)}`;
const params = new URLSearchParams({ from, to, limit: '1000' });
const response = await fetch(`${url}?${params.toString()}`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${this.apiToken}`,
'Accept': 'application/json',
},
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Failed to fetch Tempo worklogs: ${response.status} ${response.statusText} - ${errorText}`);
}
let data = await response.json();
// Map worklogs to add 'started' field
if (Array.isArray(data.results)) {
data.results = data.results.map((worklog) => {
const worklogObj = worklog;
let started = undefined;
if (worklogObj['startDate']) {
// If startTime is present, combine, else use 00:00:00
const time = worklogObj['startTime'] ? worklogObj['startTime'] : '00:00:00';
started = `${worklogObj['startDate']}T${time}`;
}
return { ...worklogObj, started };
});
}
else if (Array.isArray(data)) {
// Defensive: if API returns array directly
data = data.map((worklog) => {
const worklogObj = worklog;
let started = undefined;
if (worklogObj['startDate']) {
const time = worklogObj['startTime'] ? worklogObj['startTime'] : '00:00:00';
started = `${worklogObj['startDate']}T${time}`;
}
return { ...worklogObj, started };
});
}
return data;
}
/**
* Add a worklog for a user
*/
async addWorklog(worklogData) {
const url = `${this.baseUrl}/worklogs`;
const payload = {
issueId: worklogData.issueId,
issueKey: worklogData.issueKey,
timeSpentSeconds: worklogData.timeSpentSeconds,
description: worklogData.comment || '',
startDate: worklogData.startDate, // must be yyyy-MM-dd
authorAccountId: worklogData.authorAccountId,
...(worklogData.startTime && { startTime: worklogData.startTime }),
};
const response = await fetch(url, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiToken}`,
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify(payload),
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Failed to add Tempo worklog: ${response.status} ${response.statusText} - ${errorText}`);
}
return (await response.json());
}
/**
* Delete a worklog by ID
*/
async deleteWorklog(worklogId) {
const url = `${this.baseUrl}/worklogs/${worklogId}`;
const response = await fetch(url, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${this.apiToken}`,
'Accept': 'application/json',
},
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Failed to delete Tempo worklog: ${response.status} ${response.statusText} - ${errorText}`);
}
return true;
}
/**
* Get workload scheme for a user
*/
async getUserWorkload(accountId) {
// Try the user-specific endpoint first
let url = `${this.baseUrl}/workload-schemes/users/${encodeURIComponent(accountId)}`;
let response = await fetch(url, {
method: 'GET',
headers: {
'Authorization': `Bearer ${this.apiToken}`,
'Accept': 'application/json',
},
});
// If user-specific endpoint fails, try the general workload schemes endpoint
if (!response.ok) {
url = `${this.baseUrl}/workload-schemes`;
response = await fetch(url, {
method: 'GET',
headers: {
'Authorization': `Bearer ${this.apiToken}`,
'Accept': 'application/json',
},
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Failed to fetch user workload: ${response.status} ${response.statusText} - ${errorText}`);
}
// If we get the general schemes, try to find the default one
const schemes = await response.json();
const defaultScheme = schemes.results?.find(scheme => scheme.defaultScheme) || schemes.results?.[0];
if (!defaultScheme) {
throw new Error('No workload schemes found');
}
return {
accountId,
workloadScheme: defaultScheme,
};
}
const data = await response.json();
// The user-specific endpoint returns the workload scheme directly, not wrapped
// Check if it's already in the expected format
if (typeof data === 'object' && data !== null && 'accountId' in data && 'workloadScheme' in data) {
return data;
}
// If it's just the workload scheme, wrap it
return {
accountId,
workloadScheme: data,
};
}
}
export function createTempoClient() {
const fileConfig = loadConfigFromFile();
const baseUrl = 'https://api.tempo.io/4';
const apiToken = fileConfig?.tempoApiToken;
if (!baseUrl || !apiToken) {
throw new Error('Missing Tempo configuration. Please set tempoApiToken in your config file.');
}
return new TempoClient(baseUrl, apiToken);
}
//# sourceMappingURL=tempo-client.js.map