diff --git a/src/backlog.ts b/src/backlog.ts index ae5939b..2c723be 100644 --- a/src/backlog.ts +++ b/src/backlog.ts @@ -810,6 +810,39 @@ export default class Backlog extends Request { return this.delete(`issues/${issueIdOrKey}/sharedFiles/${id}`); } + /** + * Get the list of issues related to the specified issue. + * + * GET /api/v2/issues/:issueIdOrKey/relatedIssues + */ + public getRelatedIssues(issueIdOrKey: string | number): Promise { + return this.get(`issues/${issueIdOrKey}/relatedIssues`); + } + + /** + * Add a related issue to the specified issue. + * + * POST /api/v2/issues/:issueIdOrKey/relatedIssues + */ + public addRelatedIssue( + issueIdOrKey: string | number, + params: Option.Issue.AddRelatedIssueParams, + ): Promise { + return this.post(`issues/${issueIdOrKey}/relatedIssues`, params); + } + + /** + * Remove a related issue from the specified issue. + * + * DELETE /api/v2/issues/:issueIdOrKey/relatedIssues/:relatedIssueId + */ + public removeRelatedIssue( + issueIdOrKey: string | number, + relatedIssueId: number, + ): Promise { + return this.delete(`issues/${issueIdOrKey}/relatedIssues/${relatedIssueId}`); + } + /** * https://developer.nulab.com/docs/backlog/api/2/get-wiki-page-list/ */ diff --git a/src/entity.ts b/src/entity.ts index 3d85daf..c5f2789 100644 --- a/src/entity.ts +++ b/src/entity.ts @@ -624,6 +624,10 @@ export namespace Issue { childIssueSummary?: ChildIssueSummary; } + export interface RelatedIssue extends Issue { + type: Types.IssueRelationType; + } + export interface ChildIssueSummary { total: number; closed: number; diff --git a/src/option.ts b/src/option.ts index 1755c9d..02efb79 100644 --- a/src/option.ts +++ b/src/option.ts @@ -462,6 +462,10 @@ export namespace Issue { export interface LinkIssueSharedFilesParams { fileId: number[]; } + + export interface AddRelatedIssueParams { + targetIssueId: number; + } } export namespace PullRequest { diff --git a/src/types.ts b/src/types.ts index 8b36403..3eab08e 100644 --- a/src/types.ts +++ b/src/types.ts @@ -99,6 +99,9 @@ export enum CustomFieldType { Radio = 8, } +/** Relation type of a related issue. Only the undirected `RELATES` link is available. */ +export type IssueRelationType = "RELATES"; + export type WebhookActivityId = number; export type Fetch = typeof globalThis.fetch; diff --git a/test/test.ts b/test/test.ts index 09e5c13..05fa3ce 100644 --- a/test/test.ts +++ b/test/test.ts @@ -306,6 +306,81 @@ describe("Backlog API", () => { expect(data.childIssueSummary).toEqual({ total: 3, closed: 1 }); }); + it("should get related issues.", async () => { + const relatedIssues = [ + { + id: 2, + projectId: 1, + issueKey: "TEST-2", + keyId: 2, + summary: "related", + type: "RELATES", + }, + { + id: 3, + projectId: 1, + issueKey: "TEST-3", + keyId: 3, + summary: "related too", + type: "RELATES", + }, + ]; + mockRequest({ + method: "GET", + path: "/api/v2/issues/TEST-1/relatedIssues", + query: { apiKey }, + status: 200, + data: relatedIssues, + times: 1, + }); + const data = await backlog.getRelatedIssues("TEST-1"); + expect(data).toEqual(relatedIssues); + expect(data[0].type).toBe("RELATES"); + }); + + it("should add a related issue.", async () => { + const relatedIssue = { + id: 2, + projectId: 1, + issueKey: "TEST-2", + keyId: 2, + summary: "related", + type: "RELATES", + }; + mockRequest({ + method: "POST", + path: "/api/v2/issues/TEST-1/relatedIssues", + query: { apiKey }, + body: qs.stringify({ targetIssueId: 2 }), + status: 200, + data: relatedIssue, + times: 1, + }); + const data = await backlog.addRelatedIssue("TEST-1", { targetIssueId: 2 }); + expect(data).toEqual(relatedIssue); + }); + + it("should remove a related issue.", async () => { + const relatedIssue = { + id: 2, + projectId: 1, + issueKey: "TEST-2", + keyId: 2, + summary: "related", + type: "RELATES", + }; + mockRequest({ + method: "DELETE", + path: "/api/v2/issues/TEST-1/relatedIssues/2", + query: { apiKey }, + status: 200, + data: relatedIssue, + times: 1, + }); + const data = await backlog.removeRelatedIssue("TEST-1", 2); + expect(data).toEqual(relatedIssue); + }); + it("should serialize the expand parameter using bracket array format.", () => { const query = (backlog as any).toQueryString({ parentChild: backlogjs.Option.Issue.ParentChildType.GrandchildOnly,