export interface AtlassianConfig {
domain: string;
email: string;
apiToken: string;
}
export class AtlassianClient {
private config: AtlassianConfig;
private baseUrl: string;
private authHeader: string;
constructor(config: AtlassianConfig) {
this.config = config;
this.baseUrl = `https://${config.domain}`;
this.authHeader = 'Basic ' + Buffer.from(`${config.email}:${config.apiToken}`).toString('base64');
}
private async request(path: string, options: RequestInit = {}): Promise<unknown> {
const url = `${this.baseUrl}${path}`;
const response = await fetch(url, {
...options,
headers: {
'Authorization': this.authHeader,
'Content-Type': 'application/json',
'Accept': 'application/json',
...options.headers,
},
});
if (!response.ok) {
const error = await response.text();
throw new Error(`Atlassian API error (${response.status}): ${error}`);
}
const text = await response.text();
return text ? JSON.parse(text) : null;
}
// Jira Methods
async searchIssues(jql: string, maxResults: number = 50): Promise<unknown> {
return this.request(`/rest/api/3/search?jql=${encodeURIComponent(jql)}&maxResults=${maxResults}`);
}
async getIssue(issueKey: string): Promise<unknown> {
return this.request(`/rest/api/3/issue/${issueKey}`);
}
async createIssue(data: Record<string, unknown>): Promise<unknown> {
return this.request('/rest/api/3/issue', {
method: 'POST',
body: JSON.stringify(data),
});
}
async updateIssue(issueKey: string, data: Record<string, unknown>): Promise<unknown> {
return this.request(`/rest/api/3/issue/${issueKey}`, {
method: 'PUT',
body: JSON.stringify(data),
});
}
async deleteIssue(issueKey: string): Promise<void> {
await this.request(`/rest/api/3/issue/${issueKey}`, {
method: 'DELETE',
});
}
async transitionIssue(issueKey: string, transitionId: string): Promise<unknown> {
return this.request(`/rest/api/3/issue/${issueKey}/transitions`, {
method: 'POST',
body: JSON.stringify({ transition: { id: transitionId } }),
});
}
async getTransitions(issueKey: string): Promise<unknown> {
return this.request(`/rest/api/3/issue/${issueKey}/transitions`);
}
async addComment(issueKey: string, body: string): Promise<unknown> {
return this.request(`/rest/api/3/issue/${issueKey}/comment`, {
method: 'POST',
body: JSON.stringify({
body: {
type: 'doc',
version: 1,
content: [{ type: 'paragraph', content: [{ type: 'text', text: body }] }]
}
}),
});
}
async listProjects(): Promise<unknown> {
return this.request('/rest/api/3/project');
}
async listBoards(): Promise<unknown> {
return this.request('/rest/agile/1.0/board');
}
async listSprints(boardId: number): Promise<unknown> {
return this.request(`/rest/agile/1.0/board/${boardId}/sprint`);
}
// Confluence Methods
async listSpaces(): Promise<unknown> {
return this.request('/wiki/api/v2/spaces');
}
async getPage(pageId: string): Promise<unknown> {
return this.request(`/wiki/api/v2/pages/${pageId}?body-format=storage`);
}
async searchContent(cql: string): Promise<unknown> {
return this.request(`/wiki/rest/api/content/search?cql=${encodeURIComponent(cql)}`);
}
async createPage(spaceId: string, title: string, body: string, parentId?: string): Promise<unknown> {
const data: Record<string, unknown> = {
spaceId,
status: 'current',
title,
body: {
representation: 'storage',
value: body,
},
};
if (parentId) {
data.parentId = parentId;
}
return this.request('/wiki/api/v2/pages', {
method: 'POST',
body: JSON.stringify(data),
});
}
async updatePage(pageId: string, title: string, body: string, version: number): Promise<unknown> {
return this.request(`/wiki/api/v2/pages/${pageId}`, {
method: 'PUT',
body: JSON.stringify({
id: pageId,
status: 'current',
title,
body: {
representation: 'storage',
value: body,
},
version: {
number: version + 1,
},
}),
});
}
}
export function createAtlassianClient(): AtlassianClient {
const config: AtlassianConfig = {
domain: process.env.ATLASSIAN_DOMAIN || '',
email: process.env.ATLASSIAN_EMAIL || '',
apiToken: process.env.ATLASSIAN_API_TOKEN || '',
};
if (!config.domain || !config.email || !config.apiToken) {
throw new Error('ATLASSIAN_DOMAIN, ATLASSIAN_EMAIL, and ATLASSIAN_API_TOKEN environment variables are required');
}
return new AtlassianClient(config);
}