feat: init
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled

This commit is contained in:
chuan
2025-11-11 01:56:44 +08:00
commit bba4bb40c8
4638 changed files with 447437 additions and 0 deletions

View File

@@ -0,0 +1,10 @@
export * from "./issue_archive.service";
export * from "./issue.service";
export * from "./issue_reaction.service";
export * from "./issue_label.service";
export * from "./issue_attachment.service";
export * from "./issue_activity.service";
export * from "./issue_comment.service";
export * from "./issue_relation.service";
export * from "./work_item_version.service";
export * from "./workspace_draft.service";

View File

@@ -0,0 +1,497 @@
// plane imports
import { API_BASE_URL } from "@plane/constants";
import { EIssueServiceType } from "@plane/types";
import type {
TIssueParams,
IIssueDisplayProperties,
TBulkOperationsPayload,
TIssue,
TIssueActivity,
TIssueLink,
TIssueServiceType,
TIssuesResponse,
TIssueSubIssues,
} from "@plane/types";
import { getIssuesShouldFallbackToServer } from "@plane/utils";
// services
import { APIService } from "@/services/api.service";
export class IssueService extends APIService {
private serviceType: TIssueServiceType;
constructor(serviceType: TIssueServiceType = EIssueServiceType.ISSUES) {
super(API_BASE_URL);
this.serviceType = serviceType;
}
async createIssue(workspaceSlug: string, projectId: string, data: Partial<TIssue>): Promise<TIssue> {
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/`, data)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async getIssuesFromServer(
workspaceSlug: string,
projectId: string,
queries?: any,
config = {}
): Promise<TIssuesResponse> {
const path =
(queries.expand as string)?.includes("issue_relation") && !queries.group_by
? `/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}-detail/`
: `/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/`;
return this.get(
path,
{
params: queries,
},
config
)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async getIssuesForSync(
workspaceSlug: string,
projectId: string,
queries?: any,
config = {}
): Promise<TIssuesResponse> {
return this.get(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/v2/${this.serviceType}/`,
{ params: queries },
config
)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async getIssues(
workspaceSlug: string,
projectId: string,
queries?: Partial<Record<TIssueParams, string | boolean>>,
config = {}
): Promise<TIssuesResponse> {
if (getIssuesShouldFallbackToServer(queries) || this.serviceType !== EIssueServiceType.ISSUES) {
return await this.getIssuesFromServer(workspaceSlug, projectId, queries, config);
}
const { persistence } = await import("@/local-db/storage.sqlite");
const response = await persistence.getIssues(workspaceSlug, projectId, queries, config);
return response as TIssuesResponse;
}
async getDeletedIssues(workspaceSlug: string, projectId: string, queries?: any): Promise<TIssuesResponse> {
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/deleted-issues/`, {
params: queries,
})
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async getIssuesWithParams(
workspaceSlug: string,
projectId: string,
queries?: any
): Promise<TIssue[] | { [key: string]: TIssue[] }> {
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/`, {
params: queries,
})
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async retrieve(workspaceSlug: string, projectId: string, issueId: string, queries?: any): Promise<TIssue> {
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/`, {
params: queries,
})
.then(async (response) => {
// skip issue update when the service type is epic
if (response.data && this.serviceType === EIssueServiceType.ISSUES) {
const { updateIssue } = await import("@/local-db/utils/load-issues");
updateIssue({ ...response.data, is_local_update: 1 });
}
// add is_epic flag when the service type is epic
if (response.data && this.serviceType === EIssueServiceType.EPICS) {
response.data.is_epic = true;
}
return response?.data;
})
.catch((error) => {
throw error?.response?.data;
});
}
async retrieveIssues(workspaceSlug: string, projectId: string, issueIds: string[]): Promise<TIssue[]> {
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/list/`, {
params: { issues: issueIds.join(",") },
})
.then(async (response) => {
if (response?.data && Array.isArray(response?.data) && this.serviceType === EIssueServiceType.ISSUES) {
const { addIssuesBulk } = await import("@/local-db/utils/load-issues");
addIssuesBulk(response.data);
}
return response?.data;
})
.catch((error) => {
throw error?.response?.data;
});
}
async getIssueActivities(workspaceSlug: string, projectId: string, issueId: string): Promise<TIssueActivity[]> {
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/history/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async addIssueToCycle(
workspaceSlug: string,
projectId: string,
cycleId: string,
data: {
issues: string[];
}
) {
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/${cycleId}/cycle-issues/`, data)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async removeIssueFromCycle(workspaceSlug: string, projectId: string, cycleId: string, bridgeId: string) {
return this.delete(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/cycles/${cycleId}/cycle-issues/${bridgeId}/`
)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async createIssueRelation(
workspaceSlug: string,
projectId: string,
issueId: string,
data: {
related_list: Array<{
relation_type: "duplicate" | "relates_to" | "blocked_by";
related_issue: string;
}>;
relation?: "blocking" | null;
}
) {
return this.post(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/issue-relation/`,
data
)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async deleteIssueRelation(workspaceSlug: string, projectId: string, issueId: string, relationId: string) {
return this.delete(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/issue-relation/${relationId}/`
)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async getIssueDisplayProperties(workspaceSlug: string, projectId: string): Promise<any> {
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issue-display-properties/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async updateIssueDisplayProperties(
workspaceSlug: string,
projectId: string,
data: IIssueDisplayProperties
): Promise<any> {
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issue-display-properties/`, {
properties: data,
})
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async patchIssue(workspaceSlug: string, projectId: string, issueId: string, data: Partial<TIssue>): Promise<any> {
return this.patch(`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/`, data)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async deleteIssue(workspaceSlug: string, projectId: string, issuesId: string): Promise<any> {
if (this.serviceType === EIssueServiceType.ISSUES) {
const { deleteIssueFromLocal } = await import("@/local-db/utils/load-issues");
deleteIssueFromLocal(issuesId);
}
return this.delete(`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issuesId}/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async updateIssueDates(
workspaceSlug: string,
projectId: string,
updates: { id: string; start_date?: string; target_date?: string }[]
): Promise<void> {
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issue-dates/`, { updates })
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async subIssues(
workspaceSlug: string,
projectId: string,
issueId: string,
queries?: Partial<Record<TIssueParams, string | boolean>>
): Promise<TIssueSubIssues> {
return this.get(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/${this.serviceType === EIssueServiceType.EPICS ? "issues" : "sub-issues"}/`,
{ params: queries }
)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async addSubIssues(
workspaceSlug: string,
projectId: string,
issueId: string,
data: { sub_issue_ids: string[] }
): Promise<TIssueSubIssues> {
return this.post(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/${this.serviceType === EIssueServiceType.EPICS ? "issues" : "sub-issues"}/`,
data
)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async fetchIssueLinks(workspaceSlug: string, projectId: string, issueId: string): Promise<TIssueLink[]> {
return this.get(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/${this.serviceType === EIssueServiceType.EPICS ? "links" : "issue-links"}/`
)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async createIssueLink(
workspaceSlug: string,
projectId: string,
issueId: string,
data: Partial<TIssueLink>
): Promise<TIssueLink> {
return this.post(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/${this.serviceType === EIssueServiceType.EPICS ? "links" : "issue-links"}/`,
data
)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async updateIssueLink(
workspaceSlug: string,
projectId: string,
issueId: string,
linkId: string,
data: Partial<TIssueLink>
): Promise<TIssueLink> {
return this.patch(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/${this.serviceType === EIssueServiceType.EPICS ? "links" : "issue-links"}/${linkId}/`,
data
)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async deleteIssueLink(workspaceSlug: string, projectId: string, issueId: string, linkId: string): Promise<any> {
return this.delete(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/${this.serviceType === EIssueServiceType.EPICS ? "links" : "issue-links"}/${linkId}/`
)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async bulkOperations(workspaceSlug: string, projectId: string, data: TBulkOperationsPayload): Promise<any> {
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/bulk-operation-issues/`, data)
.then(async (response) => {
if (this.serviceType === EIssueServiceType.ISSUES) {
const { persistence } = await import("@/local-db/storage.sqlite");
persistence.syncIssues(projectId);
}
return response?.data;
})
.catch((error) => {
throw error?.response?.data;
});
}
async bulkDeleteIssues(
workspaceSlug: string,
projectId: string,
data: {
issue_ids: string[];
}
): Promise<any> {
return this.delete(`/api/workspaces/${workspaceSlug}/projects/${projectId}/bulk-delete-issues/`, data)
.then(async (response) => {
if (this.serviceType === EIssueServiceType.ISSUES) {
const { persistence } = await import("@/local-db/storage.sqlite");
persistence.syncIssues(projectId);
}
return response?.data;
})
.catch((error) => {
throw error?.response?.data;
});
}
async bulkArchiveIssues(
workspaceSlug: string,
projectId: string,
data: {
issue_ids: string[];
}
): Promise<{
archived_at: string;
}> {
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/bulk-archive-issues/`, data)
.then(async (response) => {
if (this.serviceType === EIssueServiceType.ISSUES) {
const { persistence } = await import("@/local-db/storage.sqlite");
persistence.syncIssues(projectId);
}
return response?.data;
})
.catch((error) => {
throw error?.response?.data;
});
}
// issue subscriptions
async getIssueNotificationSubscriptionStatus(
workspaceSlug: string,
projectId: string,
issueId: string
): Promise<{
subscribed: boolean;
}> {
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/subscribe/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async unsubscribeFromIssueNotifications(workspaceSlug: string, projectId: string, issueId: string): Promise<any> {
return this.delete(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/subscribe/`
)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async subscribeToIssueNotifications(workspaceSlug: string, projectId: string, issueId: string): Promise<any> {
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/subscribe/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async bulkSubscribeIssues(
workspaceSlug: string,
projectId: string,
data: {
issue_ids: string[];
}
): Promise<any> {
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/bulk-subscribe-issues/`, data)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async getIssueMetaFromURL(
workspaceSlug: string,
projectId: string,
issueId: string
): Promise<{
project_identifier: string;
sequence_id: string;
}> {
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/meta/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async retrieveWithIdentifier(
workspaceSlug: string,
project_identifier: string,
issue_sequence: string,
queries?: any
): Promise<TIssue> {
return this.get(`/api/workspaces/${workspaceSlug}/work-items/${project_identifier}-${issue_sequence}/`, {
params: queries,
})
.then(async (response) => {
// skip issue update when the service type is epic
if (response.data && this.serviceType === EIssueServiceType.ISSUES) {
const { updateIssue } = await import("@/local-db/utils/load-issues");
updateIssue({ ...response.data, is_local_update: 1 });
}
// add is_epic flag when the service type is epic
if (response.data && this.serviceType === EIssueServiceType.EPICS) {
response.data.is_epic = true;
}
return response?.data;
})
.catch((error) => {
throw error?.response?.data;
});
}
}

View File

@@ -0,0 +1,37 @@
import { API_BASE_URL } from "@plane/constants";
import type { TIssueActivity, TIssueServiceType } from "@plane/types";
import { EIssueServiceType } from "@plane/types";
import { APIService } from "@/services/api.service";
// types
// helper
export class IssueActivityService extends APIService {
private serviceType: TIssueServiceType;
constructor(serviceType: TIssueServiceType = EIssueServiceType.ISSUES) {
super(API_BASE_URL);
this.serviceType = serviceType;
}
async getIssueActivities(
workspaceSlug: string,
projectId: string,
issueId: string,
params:
| {
created_at__gt: string;
}
| object = {}
): Promise<TIssueActivity[]> {
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/history/`, {
params: {
activity_type: `${this.serviceType === EIssueServiceType.EPICS ? "epic-property" : "issue-property"}`,
...params,
},
})
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
}

View File

@@ -0,0 +1,66 @@
import { API_BASE_URL } from "@plane/constants";
import type { TIssue, TIssueServiceType } from "@plane/types";
import { EIssueServiceType } from "@plane/types";
import { APIService } from "@/services/api.service";
// types
// constants
export class IssueArchiveService extends APIService {
private serviceType: TIssueServiceType;
constructor(serviceType: TIssueServiceType = EIssueServiceType.ISSUES) {
super(API_BASE_URL);
this.serviceType = serviceType;
}
async getArchivedIssues(workspaceSlug: string, projectId: string, queries?: any, config = {}): Promise<any> {
return this.get(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/archived-issues/`,
{
params: { ...queries },
},
config
)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async archiveIssue(
workspaceSlug: string,
projectId: string,
issueId: string
): Promise<{
archived_at: string;
}> {
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/archive/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async restoreIssue(workspaceSlug: string, projectId: string, issueId: string): Promise<any> {
return this.delete(`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/archive/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async retrieveArchivedIssue(
workspaceSlug: string,
projectId: string,
issueId: string,
queries?: any
): Promise<TIssue> {
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/archive/`, {
params: queries,
})
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
}

View File

@@ -0,0 +1,89 @@
import type { AxiosRequestConfig } from "axios";
import { API_BASE_URL } from "@plane/constants";
// plane types
import { getFileMetaDataForUpload, generateFileUploadPayload } from "@plane/services";
import type { TIssueAttachment, TIssueAttachmentUploadResponse, TIssueServiceType } from "@plane/types";
import { EIssueServiceType } from "@plane/types";
// services
import { APIService } from "@/services/api.service";
import { FileUploadService } from "@/services/file-upload.service";
export class IssueAttachmentService extends APIService {
private fileUploadService: FileUploadService;
private serviceType: TIssueServiceType;
constructor(serviceType: TIssueServiceType = EIssueServiceType.ISSUES) {
super(API_BASE_URL);
// upload service
this.fileUploadService = new FileUploadService();
this.serviceType = serviceType;
}
private async updateIssueAttachmentUploadStatus(
workspaceSlug: string,
projectId: string,
issueId: string,
attachmentId: string
): Promise<void> {
return this.patch(
`/api/assets/v2/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/attachments/${attachmentId}/`
)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async uploadIssueAttachment(
workspaceSlug: string,
projectId: string,
issueId: string,
file: File,
uploadProgressHandler?: AxiosRequestConfig["onUploadProgress"]
): Promise<TIssueAttachment> {
const fileMetaData = await getFileMetaDataForUpload(file);
return this.post(
`/api/assets/v2/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/attachments/`,
fileMetaData
)
.then(async (response) => {
const signedURLResponse: TIssueAttachmentUploadResponse = response?.data;
const fileUploadPayload = generateFileUploadPayload(signedURLResponse, file);
await this.fileUploadService.uploadFile(
signedURLResponse.upload_data.url,
fileUploadPayload,
uploadProgressHandler
);
await this.updateIssueAttachmentUploadStatus(workspaceSlug, projectId, issueId, signedURLResponse.asset_id);
return signedURLResponse.attachment;
})
.catch((error) => {
throw error?.response?.data;
});
}
async getIssueAttachments(workspaceSlug: string, projectId: string, issueId: string): Promise<TIssueAttachment[]> {
return this.get(
`/api/assets/v2/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/attachments/`
)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async deleteIssueAttachment(
workspaceSlug: string,
projectId: string,
issueId: string,
assetId: string
): Promise<TIssueAttachment> {
return this.delete(
`/api/assets/v2/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/attachments/${assetId}/`
)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
}

View File

@@ -0,0 +1,89 @@
// plane types
import { API_BASE_URL } from "@plane/constants";
import type { TIssueComment, TIssueServiceType } from "@plane/types";
import { EIssueServiceType } from "@plane/types";
// services
import { APIService } from "@/services/api.service";
import { FileUploadService } from "@/services/file-upload.service";
export class IssueCommentService extends APIService {
private fileUploadService: FileUploadService;
private serviceType: TIssueServiceType;
constructor(serviceType: TIssueServiceType = EIssueServiceType.ISSUES) {
super(API_BASE_URL);
// upload service
this.fileUploadService = new FileUploadService();
this.serviceType = serviceType;
}
async getIssueComments(
workspaceSlug: string,
projectId: string,
issueId: string,
params:
| {
created_at__gt: string;
}
| object = {}
): Promise<TIssueComment[]> {
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/history/`, {
params: {
activity_type: `${this.serviceType === EIssueServiceType.EPICS ? "epic-comment" : "issue-comment"}`,
...params,
},
})
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async createIssueComment(
workspaceSlug: string,
projectId: string,
issueId: string,
data: Partial<TIssueComment>
): Promise<TIssueComment> {
return this.post(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/comments/`,
data
)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async patchIssueComment(
workspaceSlug: string,
projectId: string,
issueId: string,
commentId: string,
data: Partial<TIssueComment>
): Promise<TIssueComment> {
return this.patch(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/comments/${commentId}/`,
data
)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async deleteIssueComment(
workspaceSlug: string,
projectId: string,
issueId: string,
commentId: string
): Promise<void> {
return this.delete(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/comments/${commentId}/`
)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
}

View File

@@ -0,0 +1,51 @@
import { API_BASE_URL } from "@plane/constants";
import type { IIssueLabel } from "@plane/types";
// services
import { APIService } from "@/services/api.service";
// types
export class IssueLabelService extends APIService {
constructor() {
super(API_BASE_URL);
}
async getWorkspaceIssueLabels(workspaceSlug: string): Promise<IIssueLabel[]> {
return this.get(`/api/workspaces/${workspaceSlug}/labels/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async getProjectLabels(workspaceSlug: string, projectId: string): Promise<IIssueLabel[]> {
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issue-labels/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async createIssueLabel(workspaceSlug: string, projectId: string, data: any): Promise<IIssueLabel> {
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issue-labels/`, data)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async patchIssueLabel(workspaceSlug: string, projectId: string, labelId: string, data: any): Promise<any> {
return this.patch(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issue-labels/${labelId}/`, data)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async deleteIssueLabel(workspaceSlug: string, projectId: string, labelId: string): Promise<any> {
return this.delete(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issue-labels/${labelId}/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
}

View File

@@ -0,0 +1,89 @@
import { API_BASE_URL } from "@plane/constants";
import { EIssueServiceType } from "@plane/types";
import type { TIssueCommentReaction, TIssueReaction, TIssueServiceType } from "@plane/types";
// services
import { APIService } from "@/services/api.service";
// types
export class IssueReactionService extends APIService {
private serviceType: TIssueServiceType;
constructor(serviceType: TIssueServiceType = EIssueServiceType.ISSUES) {
super(API_BASE_URL);
this.serviceType = serviceType;
}
async createIssueReaction(
workspaceSlug: string,
projectId: string,
issueId: string,
data: Partial<TIssueReaction>
): Promise<any> {
return this.post(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/reactions/`,
data
)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async listIssueReactions(workspaceSlug: string, projectId: string, issueId: string): Promise<TIssueReaction[]> {
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/reactions/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async deleteIssueReaction(workspaceSlug: string, projectId: string, issueId: string, reaction: string): Promise<any> {
return this.delete(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${issueId}/reactions/${reaction}/`
)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async createIssueCommentReaction(
workspaceSlug: string,
projectId: string,
commentId: string,
data: Partial<TIssueCommentReaction>
): Promise<any> {
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/comments/${commentId}/reactions/`, data)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async listIssueCommentReactions(
workspaceSlug: string,
projectId: string,
commentId: string
): Promise<TIssueCommentReaction[]> {
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/comments/${commentId}/reactions/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async deleteIssueCommentReaction(
workspaceSlug: string,
projectId: string,
commentId: string,
reaction: string
): Promise<any> {
return this.delete(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/comments/${commentId}/reactions/${reaction}/`
)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
}

View File

@@ -0,0 +1,47 @@
import { API_BASE_URL } from "@plane/constants";
import type { TIssueRelation, TIssue } from "@plane/types";
// helpers
// Plane-web
import type { TIssueRelationTypes } from "@/plane-web/types";
// services
import { APIService } from "@/services/api.service";
export class IssueRelationService extends APIService {
constructor() {
super(API_BASE_URL);
}
async listIssueRelations(workspaceSlug: string, projectId: string, issueId: string): Promise<TIssueRelation> {
return this.get(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/issue-relation/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async createIssueRelations(
workspaceSlug: string,
projectId: string,
issueId: string,
data: { relation_type: TIssueRelationTypes; issues: string[] }
): Promise<TIssue[]> {
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/issue-relation/`, data)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async deleteIssueRelation(
workspaceSlug: string,
projectId: string,
issueId: string,
data: { relation_type: TIssueRelationTypes; related_issue: string }
): Promise<any> {
return this.post(`/api/workspaces/${workspaceSlug}/projects/${projectId}/issues/${issueId}/remove-relation/`, data)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
}

View File

@@ -0,0 +1,45 @@
// plane imports
import { API_BASE_URL } from "@plane/constants";
import { EIssueServiceType } from "@plane/types";
import type { TDescriptionVersionsListResponse, TDescriptionVersionDetails, TIssueServiceType } from "@plane/types";
// helpers
// services
import { APIService } from "@/services/api.service";
export class WorkItemVersionService extends APIService {
private serviceType: TIssueServiceType;
constructor(serviceType: TIssueServiceType = EIssueServiceType.WORK_ITEMS) {
super(API_BASE_URL);
this.serviceType = serviceType;
}
async listDescriptionVersions(
workspaceSlug: string,
projectId: string,
workItemId: string
): Promise<TDescriptionVersionsListResponse> {
return this.get(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${workItemId}/description-versions/`
)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async retrieveDescriptionVersion(
workspaceSlug: string,
projectId: string,
workItemId: string,
versionId: string
): Promise<TDescriptionVersionDetails> {
return this.get(
`/api/workspaces/${workspaceSlug}/projects/${projectId}/${this.serviceType}/${workItemId}/description-versions/${versionId}/`
)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
}

View File

@@ -0,0 +1,73 @@
import { API_BASE_URL } from "@plane/constants";
import type { TIssue, TWorkspaceDraftIssue, TWorkspaceDraftPaginationInfo } from "@plane/types";
// helpers
// services
import { APIService } from "@/services/api.service";
export class WorkspaceDraftService extends APIService {
constructor() {
super(API_BASE_URL);
}
async getIssues(
workspaceSlug: string,
query: object = {}
): Promise<TWorkspaceDraftPaginationInfo<TWorkspaceDraftIssue> | undefined> {
return this.get(`/api/workspaces/${workspaceSlug}/draft-issues/`, { params: { ...query } })
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
async getIssueById(workspaceSlug: string, issueId: string): Promise<TWorkspaceDraftIssue | undefined> {
return this.get(`/api/workspaces/${workspaceSlug}/draft-issues/${issueId}/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async createIssue(
workspaceSlug: string,
payload: Partial<TWorkspaceDraftIssue | TIssue>
): Promise<TWorkspaceDraftIssue | undefined> {
return this.post(`/api/workspaces/${workspaceSlug}/draft-issues/`, payload)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async updateIssue(
workspaceSlug: string,
issueId: string,
payload: Partial<TWorkspaceDraftIssue | TIssue>
): Promise<TWorkspaceDraftIssue | undefined> {
return this.patch(`/api/workspaces/${workspaceSlug}/draft-issues/${issueId}/`, payload)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async deleteIssue(workspaceSlug: string, issueId: string): Promise<void> {
return this.delete(`/api/workspaces/${workspaceSlug}/draft-issues/${issueId}/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
async moveIssue(workspaceSlug: string, issueId: string, payload: Partial<TWorkspaceDraftIssue>): Promise<TIssue> {
return this.post(`/api/workspaces/${workspaceSlug}/draft-to-issue/${issueId}/`, payload)
.then((response) => response?.data)
.catch((error) => {
throw error?.response;
});
}
}
const workspaceDraftService = new WorkspaceDraftService();
export default workspaceDraftService;