[WEB-3066] refactor: replace Space Services with Services Package (#6353)

* [WEB-3066] refactor: replace Space Services with Services Package

* chore: minor improvements

* fix: type

---------

Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
This commit is contained in:
Prateek Shourya 2025-01-11 15:16:11 +05:30 committed by GitHub
parent 39ca600091
commit ade4d290f5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
66 changed files with 1002 additions and 631 deletions

View file

@ -0,0 +1,52 @@
import axios from "axios";
// api service
import { APIService } from "../api.service";
/**
* Service class for handling file upload operations
* Handles file uploads
* @extends {APIService}
*/
export class FileUploadService extends APIService {
private cancelSource: any;
constructor() {
super("");
}
/**
* Uploads a file to the specified signed URL
* @param {string} url - The URL to upload the file to
* @param {FormData} data - The form data to upload
* @returns {Promise<void>} Promise resolving to void
* @throws {Error} If the request fails
*/
async uploadFile(
url: string,
data: FormData,
): Promise<void> {
this.cancelSource = axios.CancelToken.source();
return this.post(url, data, {
headers: {
"Content-Type": "multipart/form-data",
},
cancelToken: this.cancelSource.token,
withCredentials: false,
})
.then((response) => response?.data)
.catch((error) => {
if (axios.isCancel(error)) {
console.log(error.message);
} else {
throw error?.response?.data;
}
});
}
/**
* Cancels the upload
*/
cancelUpload() {
this.cancelSource.cancel("Upload canceled");
}
}

View file

@ -0,0 +1,67 @@
// plane imports
import { API_BASE_URL } from "@plane/constants";
// api service
import { APIService } from "../api.service";
// helpers
import { getAssetIdFromUrl } from "./helper";
/**
* Service class for managing file operations within plane applications.
* Extends APIService to handle HTTP requests to the file-related endpoints.
* @extends {APIService}
*/
export class FileService extends APIService {
/**
* Creates an instance of FileService
* @param {string} BASE_URL - The base URL for API requests
*/
constructor(BASE_URL?: string) {
super(BASE_URL || API_BASE_URL);
}
/**
* Deletes a new asset
* @param {string} assetPath - The asset path
* @returns {Promise<void>} Promise resolving to void
* @throws {Error} If the request fails
*/
async deleteNewAsset(assetPath: string): Promise<void> {
return this.delete(assetPath)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
/**
* Deletes an old editor asset
* @param {string} workspaceId - The workspace identifier
* @param {string} src - The asset source
* @returns {Promise<any>} Promise resolving to void
* @throws {Error} If the request fails
*/
async deleteOldEditorAsset(workspaceId: string, src: string): Promise<any> {
const assetKey = getAssetIdFromUrl(src);
return this.delete(`/api/workspaces/file-assets/${workspaceId}/${assetKey}/`)
.then((response) => response?.status)
.catch((error) => {
throw error?.response?.data;
});
}
/**
* Restores an old editor asset
* @param {string} workspaceId - The workspace identifier
* @param {string} src - The asset source
* @returns {Promise<void>} Promise resolving to void
* @throws {Error} If the request fails
*/
async restoreOldEditorAsset(workspaceId: string, src: string): Promise<void> {
const assetKey = getAssetIdFromUrl(src);
return this.post(`/api/workspaces/file-assets/${workspaceId}/${assetKey}/restore/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
}

View file

@ -0,0 +1,36 @@
import { TFileMetaDataLite, TFileSignedURLResponse } from "@plane/types";
/**
* @description from the provided signed URL response, generate a payload to be used to upload the file
* @param {TFileSignedURLResponse} signedURLResponse
* @param {File} file
* @returns {FormData} file upload request payload
*/
export const generateFileUploadPayload = (signedURLResponse: TFileSignedURLResponse, file: File): FormData => {
const formData = new FormData();
Object.entries(signedURLResponse.upload_data.fields).forEach(([key, value]) => formData.append(key, value));
formData.append("file", file);
return formData;
};
/**
* @description returns the necessary file meta data to upload a file
* @param {File} file
* @returns {TFileMetaDataLite} payload with file info
*/
export const getFileMetaDataForUpload = (file: File): TFileMetaDataLite => ({
name: file.name,
size: file.size,
type: file.type,
});
/**
* @description this function returns the assetId from the asset source
* @param {string} src
* @returns {string} assetId
*/
export const getAssetIdFromUrl = (src: string): string => {
const sourcePaths = src.split("/");
const assetUrl = sourcePaths[sourcePaths.length - 1];
return assetUrl;
};

View file

@ -0,0 +1,3 @@
export * from "./file-upload.service";
export * from "./sites-file.service";
export * from "./file.service";

View file

@ -0,0 +1,117 @@
// plane imports
import { API_BASE_URL } from "@plane/constants";
// local services
import { TFileEntityInfo, TFileSignedURLResponse } from "@plane/types";
import { FileUploadService } from "./file-upload.service";
// helpers
import { FileService } from "./file.service";
import { generateFileUploadPayload, getAssetIdFromUrl, getFileMetaDataForUpload } from "./helper";
/**
* Service class for managing file operations within plane sites application.
* Extends FileService to manage file-related operations.
* @extends {FileService}
* @remarks This service is only available for plane sites
*/
export class SitesFileService extends FileService {
private cancelSource: any;
fileUploadService: FileUploadService;
/**
* Creates an instance of SitesFileService
* @param {string} BASE_URL - The base URL for API requests
*/
constructor(BASE_URL?: string) {
super(BASE_URL || API_BASE_URL);
this.cancelUpload = this.cancelUpload.bind(this);
// services
this.fileUploadService = new FileUploadService();
}
/**
* Updates the upload status of an asset
* @param {string} anchor - The anchor identifier
* @param {string} assetId - The asset identifier
* @returns {Promise<void>} Promise resolving to void
* @throws {Error} If the request fails
*/
private async updateAssetUploadStatus(anchor: string, assetId: string): Promise<void> {
return this.patch(`/api/public/assets/v2/anchor/${anchor}/${assetId}/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
/**
* Updates the upload status of multiple assets
* @param {string} anchor - The anchor identifier
* @param {string} entityId - The entity identifier
* @param {Object} data - The data payload
* @returns {Promise<void>} Promise resolving to void
* @throws {Error} If the request fails
*/
async updateBulkAssetsUploadStatus(
anchor: string,
entityId: string,
data: {
asset_ids: string[];
}
): Promise<void> {
return this.post(`/api/public/assets/v2/anchor/${anchor}/${entityId}/bulk/`, data)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
/**
* Uploads a file to the specified anchor
* @param {string} anchor - The anchor identifier
* @param {TFileEntityInfo} data - The data payload
* @param {File} file - The file to upload
* @returns {Promise<TFileSignedURLResponse>} Promise resolving to the signed URL response
* @throws {Error} If the request fails
*/
async uploadAsset(anchor: string, data: TFileEntityInfo, file: File): Promise<TFileSignedURLResponse> {
const fileMetaData = getFileMetaDataForUpload(file);
return this.post(`/api/public/assets/v2/anchor/${anchor}/`, {
...data,
...fileMetaData,
})
.then(async (response) => {
const signedURLResponse: TFileSignedURLResponse = response?.data;
const fileUploadPayload = generateFileUploadPayload(signedURLResponse, file);
await this.fileUploadService.uploadFile(signedURLResponse.upload_data.url, fileUploadPayload);
await this.updateAssetUploadStatus(anchor, signedURLResponse.asset_id);
return signedURLResponse;
})
.catch((error) => {
throw error?.response?.data;
});
}
/**
* Restores a new asset
* @param {string} workspaceSlug - The workspace slug
* @param {string} src - The asset source
* @returns {Promise<void>} Promise resolving to void
* @throws {Error} If the request fails
*/
async restoreNewAsset(workspaceSlug: string, src: string): Promise<void> {
// remove the last slash and get the asset id
const assetId = getAssetIdFromUrl(src);
return this.post(`/api/public/assets/v2/workspaces/${workspaceSlug}/restore/${assetId}/`)
.then((response) => response?.data)
.catch((error) => {
throw error?.response?.data;
});
}
/**
* Cancels the upload
*/
cancelUpload() {
this.cancelSource.cancelUpload();
}
}