jira-client.tsā¢6.74 kB
import axios, { AxiosInstance } from "axios";
export interface JiraConfig {
host: string;
email: string;
apiToken: string;
}
export interface JiraIssue {
key: string;
fields: {
summary: string;
description?: any;
status: {
name: string;
};
assignee?: {
displayName: string;
emailAddress: string;
};
reporter?: {
displayName: string;
emailAddress: string;
};
priority?: {
name: string;
};
issuetype: {
name: string;
};
project: {
key: string;
name: string;
};
created: string;
updated: string;
[key: string]: any;
};
}
export interface JiraProject {
id: string;
key: string;
name: string;
projectTypeKey: string;
style?: string;
}
export interface JiraTransition {
id: string;
name: string;
to: {
name: string;
id: string;
};
}
export interface JiraBoard {
id: number;
name: string;
type: string;
location?: {
projectKey: string;
projectName: string;
};
}
export class JiraClient {
private client: AxiosInstance;
private config: JiraConfig;
constructor(config: JiraConfig) {
this.config = config;
// Remove any trailing slashes from host
const host = config.host.replace(/\/+$/, "");
// Check if host already includes protocol
const baseURL =
host.startsWith("http://") || host.startsWith("https://")
? `${host}/rest/api/3`
: `https://${host}/rest/api/3`;
this.client = axios.create({
baseURL,
auth: {
username: config.email,
password: config.apiToken,
},
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
});
}
async searchIssues(
jql: string,
maxResults: number = 50,
startAt: number = 0
): Promise<{ issues: JiraIssue[]; total: number }> {
const response = await this.client.post("/search", {
jql,
maxResults,
startAt,
fields: [
"summary",
"status",
"assignee",
"reporter",
"priority",
"issuetype",
"project",
"created",
"updated",
"description",
],
});
return {
issues: response.data.issues,
total: response.data.total,
};
}
async getIssue(issueKey: string): Promise<JiraIssue> {
const response = await this.client.get(`/issue/${issueKey}`);
return response.data;
}
async createIssue(
projectKey: string,
summary: string,
issueType: string,
description?: string,
additionalFields?: any
): Promise<JiraIssue> {
const issueData: any = {
fields: {
project: { key: projectKey },
summary,
issuetype: { name: issueType },
...additionalFields,
},
};
if (description) {
issueData.fields.description = {
type: "doc",
version: 1,
content: [
{
type: "paragraph",
content: [
{
type: "text",
text: description,
},
],
},
],
};
}
const response = await this.client.post("/issue", issueData);
return await this.getIssue(response.data.key);
}
async updateIssue(issueKey: string, fields: any): Promise<void> {
await this.client.put(`/issue/${issueKey}`, {
fields,
});
}
async addComment(issueKey: string, comment: string): Promise<any> {
const response = await this.client.post(`/issue/${issueKey}/comment`, {
body: {
type: "doc",
version: 1,
content: [
{
type: "paragraph",
content: [
{
type: "text",
text: comment,
},
],
},
],
},
});
return response.data;
}
async getTransitions(issueKey: string): Promise<JiraTransition[]> {
const response = await this.client.get(`/issue/${issueKey}/transitions`);
return response.data.transitions;
}
async transitionIssue(issueKey: string, transitionId: string): Promise<void> {
await this.client.post(`/issue/${issueKey}/transitions`, {
transition: {
id: transitionId,
},
});
}
async assignIssue(issueKey: string, accountId: string | null): Promise<void> {
await this.client.put(`/issue/${issueKey}/assignee`, {
accountId,
});
}
async getProjects(): Promise<JiraProject[]> {
const response = await this.client.get("/project");
return response.data;
}
async getUsers(query: string): Promise<any[]> {
const response = await this.client.get("/user/search", {
params: { query },
});
return response.data;
}
async getIssueComments(issueKey: string): Promise<any[]> {
const response = await this.client.get(`/issue/${issueKey}/comment`);
return response.data.comments;
}
async getBoards(
startAt: number = 0,
maxResults: number = 50
): Promise<{ boards: JiraBoard[]; total: number }> {
// Use the agile API endpoint for boards
const host = this.config.host.replace(/\/+$/, "");
const agileBaseURL =
host.startsWith("http://") || host.startsWith("https://")
? `${host}/rest/agile/1.0`
: `https://${host}/rest/agile/1.0`;
const response = await axios.get(`${agileBaseURL}/board`, {
params: { startAt, maxResults },
auth: {
username: this.config.email,
password: this.config.apiToken,
},
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
});
return {
boards: response.data.values || [],
total: response.data.total || 0,
};
}
async getBoardIssues(
boardId: number,
startAt: number = 0,
maxResults: number = 50,
jql?: string
): Promise<{ issues: JiraIssue[]; total: number }> {
// Use the agile API endpoint for board issues
const host = this.config.host.replace(/\/+$/, "");
const agileBaseURL =
host.startsWith("http://") || host.startsWith("https://")
? `${host}/rest/agile/1.0`
: `https://${host}/rest/agile/1.0`;
const params: any = { startAt, maxResults };
if (jql) {
params.jql = jql;
}
const response = await axios.get(`${agileBaseURL}/board/${boardId}/issue`, {
params,
auth: {
username: this.config.email,
password: this.config.apiToken,
},
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
});
return {
issues: response.data.issues || [],
total: response.data.total || 0,
};
}
}