operations/codeup/files.ts (206 lines of code) (raw):
import { z } from "zod";
import {yunxiaoRequest, buildUrl, pathEscape} from "../../common/utils.js";
import {
FileContentSchema,
CreateFileResponseSchema,
DeleteFileResponseSchema,
FileInfoSchema,
GetFileBlobsSchema,
GetFileBlobsOptions,
CreateFileSchema,
CreateFileOptions,
UpdateFileSchema,
UpdateFileOptions,
DeleteFileSchema,
DeleteFileOptions,
ListFilesSchema,
ListFilesOptions
} from "../../common/types.js";
// Common helper function to handle repositoryId and filePath encoding
function handlePathEncoding(repositoryId: string, filePath: string): { encodedRepoId: string; encodedFilePath: string } {
let encodedRepoId = repositoryId;
let encodedFilePath = filePath;
// 自动处理repositoryId中未编码的斜杠
if (repositoryId.includes("/")) {
// 发现未编码的斜杠,自动进行URL编码
const parts = repositoryId.split("/", 2);
if (parts.length === 2) {
const encodedRepoName = encodeURIComponent(parts[1]);
// 移除编码中的+号(空格被编码为+,但我们需要%20)
const formattedEncodedName = encodedRepoName.replace(/\+/g, "%20");
encodedRepoId = `${parts[0]}%2F${formattedEncodedName}`;
}
}
// 确保filePath已被URL编码
if (filePath.includes("/")) {
const startsWithSlash = filePath.startsWith("/");
if (startsWithSlash) {
encodedFilePath = encodedFilePath.substring(1);
}
encodedFilePath = encodeURIComponent(filePath);
}
return { encodedRepoId, encodedFilePath };
}
/**
* 查询文件内容
* @param organizationId
* @param repositoryId
* @param filePath
* @param ref
*/
export async function getFileBlobsFunc(
organizationId: string,
repositoryId: string,
filePath: string,
ref: string
): Promise<z.infer<typeof FileContentSchema>> {
// const { encodedRepoId, encodedFilePath } = handlePathEncoding(repositoryId, filePath);
let encodedRepoId = repositoryId;
let encodedFilePath = filePath;
// 自动处理repositoryId中未编码的斜杠
if (repositoryId.includes("/")) {
// 发现未编码的斜杠,自动进行URL编码
const parts = repositoryId.split("/", 2);
if (parts.length === 2) {
const encodedRepoName = encodeURIComponent(parts[1]);
// 移除编码中的+号(空格被编码为+,但我们需要%20)
const formattedEncodedName = encodedRepoName.replace(/\+/g, "%20");
encodedRepoId = `${parts[0]}%2F${formattedEncodedName}`;
}
}
// 确保filePath已被URL编码
if (filePath.includes("/")) {
encodedFilePath = encodeURIComponent(filePath);
}
const baseUrl = `/oapi/v1/codeup/organizations/${organizationId}/repositories/${encodedRepoId}/files/${encodedFilePath}`;
// 构建查询参数
const queryParams: Record<string, string | number | undefined> = {
ref: ref
};
// 使用buildUrl函数构建包含查询参数的URL
const url = buildUrl(baseUrl, queryParams);
const response = await yunxiaoRequest(url, {
method: "GET",
});
return FileContentSchema.parse(response);
}
/**
* 创建文件
* @param organizationId
* @param repositoryId
* @param filePath
* @param content
* @param commitMessage
* @param branch
* @param encoding
*/
export async function createFileFunc(
organizationId: string,
repositoryId: string,
filePath: string,
content: string,
commitMessage: string,
branch: string,
encoding?: string
): Promise<z.infer<typeof CreateFileResponseSchema>> {
let encodedRepoId = repositoryId;
let encodedFilePath = filePath;
if (repositoryId.includes("/")) {
// 发现未编码的斜杠,自动进行URL编码
const parts = repositoryId.split("/", 2);
if (parts.length === 2) {
const encodedRepoName = encodeURIComponent(parts[1]);
// 移除编码中的+号(空格被编码为+,但我们需要%20)
const formattedEncodedName = encodedRepoName.replace(/\+/g, "%20");
encodedRepoId = `${parts[0]}%2F${formattedEncodedName}`;
}
}
// 确保filePath已被URL编码
if (filePath.includes("/")) {
encodedFilePath = pathEscape(filePath);
}
const url = `/oapi/v1/codeup/organizations/${organizationId}/repositories/${encodedRepoId}/files`;
const body = {
branch: branch,
filePath: encodedFilePath,
content: content,
commitMessage: commitMessage,
encoding: encoding || "text" // 默认使用text编码
};
const response = await yunxiaoRequest(url, {
method: "POST",
body: body
});
return CreateFileResponseSchema.parse(response);
}
/**
* 更新文件内容
* @param organizationId
* @param repositoryId
* @param filePath
* @param content
* @param commitMessage
* @param branch
* @param encoding
*/
export async function updateFileFunc(
organizationId: string,
repositoryId: string,
filePath: string,
content: string,
commitMessage: string,
branch: string,
encoding?: string
): Promise<z.infer<typeof CreateFileResponseSchema>> {
//const { encodedRepoId, encodedFilePath } = handlePathEncoding(repositoryId, filePath);
let encodedRepoId = repositoryId;
let encodedFilePath = filePath;
// 自动处理repositoryId中未编码的斜杠
if (repositoryId.includes("/")) {
// 发现未编码的斜杠,自动进行URL编码
const parts = repositoryId.split("/", 2);
if (parts.length === 2) {
const encodedRepoName = encodeURIComponent(parts[1]);
// 移除编码中的+号(空格被编码为+,但我们需要%20)
const formattedEncodedName = encodedRepoName.replace(/\+/g, "%20");
encodedRepoId = `${parts[0]}%2F${formattedEncodedName}`;
}
}
// 确保filePath已被URL编码
if (filePath.includes("/")) {
const pathToEncode = filePath.startsWith("/") ? filePath.substring(1) : filePath;
encodedFilePath = encodeURIComponent(pathToEncode);
}
const url = `/oapi/v1/codeup/organizations/${organizationId}/repositories/${encodedRepoId}/files/${encodedFilePath}`;
const body = {
branch: branch,
commitMessage: commitMessage,
content: content,
encoding: encoding || "text" // 默认使用text编码
};
const response = await yunxiaoRequest(url, {
method: "PUT",
body: body
});
return CreateFileResponseSchema.parse(response);
}
/**
* 删除文件
* @param organizationId
* @param repositoryId
* @param filePath
* @param commitMessage
* @param branch
*/
export async function deleteFileFunc(
organizationId: string,
repositoryId: string,
filePath: string,
commitMessage: string,
branch: string
): Promise<z.infer<typeof DeleteFileResponseSchema>> {
let encodedRepoId = repositoryId;
let encodedFilePath = filePath;
if (repositoryId.includes("/")) {
// 发现未编码的斜杠,自动进行URL编码
const parts = repositoryId.split("/", 2);
if (parts.length === 2) {
const encodedRepoName = encodeURIComponent(parts[1]);
// 移除编码中的+号(空格被编码为+,但我们需要%20)
const formattedEncodedName = encodedRepoName.replace(/\+/g, "%20");
encodedRepoId = `${parts[0]}%2F${formattedEncodedName}`;
}
}
// 确保filePath已被URL编码
if (filePath.includes("/")) {
encodedFilePath = encodeURIComponent(filePath);
}
const baseUrl = `/oapi/v1/codeup/organizations/${organizationId}/repositories/${encodedRepoId}/files/${encodedFilePath}`;
// 构建查询参数
const queryParams: Record<string, string | number | undefined> = {
branch: branch,
commitMessage: commitMessage
};
// 使用buildUrl函数构建包含查询参数的URL
const url = buildUrl(baseUrl, queryParams);
const response = await yunxiaoRequest(url, {
method: "DELETE",
});
return DeleteFileResponseSchema.parse(response);
}
/**
* 查询文件树
* @param organizationId
* @param repositoryId
* @param path
* @param ref
* @param type
*/
export async function listFilesFunc(
organizationId: string,
repositoryId: string,
path?: string,
ref?: string,
type?: string // Possible values: DIRECT, RECURSIVE, FLATTEN
): Promise<z.infer<typeof FileInfoSchema>[]> {
// 自动处理repositoryId中未编码的斜杠
let encodedRepoId = repositoryId;
if (repositoryId.includes("/")) {
// 发现未编码的斜杠,自动进行URL编码
const parts = repositoryId.split("/", 2);
if (parts.length === 2) {
const encodedRepoName = encodeURIComponent(parts[1]);
// 移除编码中的+号(空格被编码为+,但我们需要%20)
const formattedEncodedName = encodedRepoName.replace(/\+/g, "%20");
encodedRepoId = `${parts[0]}%2F${formattedEncodedName}`;
}
}
const baseUrl = `/oapi/v1/codeup/organizations/${organizationId}/repositories/${encodedRepoId}/files/tree`;
// 构建查询参数
const queryParams: Record<string, string | number | undefined> = {};
if (path) {
queryParams.path = path;
}
if (ref) {
queryParams.ref = ref;
}
if (type) {
queryParams.type = type;
}
// 使用buildUrl函数构建包含查询参数的URL
const url = buildUrl(baseUrl, queryParams);
const response = await yunxiaoRequest(url, {
method: "GET",
});
// 确保响应是数组
if (!Array.isArray(response)) {
return [];
}
// 解析每个文件信息对象
return response.map(fileInfo => FileInfoSchema.parse(fileInfo));
}