Skip to content

Instantly share code, notes, and snippets.

@sam-goodwin
Created April 22, 2025 08:09
Show Gist options
  • Save sam-goodwin/b3307fcfe3a878c00b064224e5f9259a to your computer and use it in GitHub Desktop.
Save sam-goodwin/b3307fcfe3a878c00b064224e5f9259a to your computer and use it in GitHub Desktop.
Huawei Bucket Alchemy Resource
/**
* Options for Huawei Cloud API requests
*/
export interface HuaweiApiOptions {
/**
* API key or token to use (overrides environment variable)
*/
apiKey?: string;
/**
* Account or project ID (overrides environment variable)
*/
accountId?: string;
/**
* Region for the API endpoint
*/
region?: string;
}
/**
* Minimal API client using raw fetch
*/
export class HuaweiApi {
/** Base URL for API */
readonly baseUrl: string;
/** API key or token */
readonly apiKey: string;
/** Account ID */
readonly accountId: string;
/** Region */
readonly region: string;
/**
* Create a new API client
*
* @param options API options
*/
constructor(options: HuaweiApiOptions = {}) {
this.region = options.region || process.env.HUAWEI_REGION || "";
this.baseUrl = `https://obs.${this.region}.myhuaweicloud.com`;
this.apiKey = options.apiKey || process.env.HUAWEI_ACCESS_KEY || "";
this.accountId = options.accountId || process.env.HUAWEI_ACCOUNT_ID || "";
// Validate required configuration
if (!this.apiKey) {
throw new Error("HUAWEI_ACCESS_KEY environment variable is required");
}
if (!this.region) {
throw new Error("HUAWEI_REGION environment variable is required");
}
}
/**
* Make a request to the API
*
* @param path API path (without base URL)
* @param init Fetch init options
* @returns Raw Response object from fetch
*/
async fetch(path: string, init: RequestInit = {}): Promise<Response> {
// Set up authentication headers
const headers: Record<string, string> = {
"Content-Type": "application/json",
Authorization: `Bearer ${this.apiKey}`,
};
// Add headers from init if provided
if (init.headers) {
const initHeaders = init.headers as Record<string, string>;
Object.keys(initHeaders).forEach((key) => {
headers[key] = initHeaders[key];
});
}
// For FormData, remove Content-Type
if (init.body instanceof FormData) {
delete headers["Content-Type"];
}
// Make the request
return fetch(`${this.baseUrl}${path}`, {
...init,
headers,
});
}
/**
* Helper for GET requests
*/
async get(path: string, init: RequestInit = {}): Promise<Response> {
return this.fetch(path, { ...init, method: "GET" });
}
/**
* Helper for POST requests
*/
async post(
path: string,
body: any,
init: RequestInit = {}
): Promise<Response> {
const requestBody = body instanceof FormData ? body : JSON.stringify(body);
return this.fetch(path, { ...init, method: "POST", body: requestBody });
}
/**
* Helper for PUT requests
*/
async put(
path: string,
body: any,
init: RequestInit = {}
): Promise<Response> {
const requestBody = body instanceof FormData ? body : JSON.stringify(body);
return this.fetch(path, { ...init, method: "PUT", body: requestBody });
}
/**
* Helper for DELETE requests
*/
async delete(path: string, init: RequestInit = {}): Promise<Response> {
return this.fetch(path, { ...init, method: "DELETE" });
}
}
import type { Context } from "../context";
import { Resource } from "../resource";
import { HuaweiApi } from "./api";
/**
* Website configuration for OBS bucket
*/
export interface BucketWebsiteConfig {
/**
* The name of the index document for the website
*/
indexDocument?: string;
/**
* The name of the error document for the website
*/
errorDocument?: string;
/**
* A hostname to redirect all website requests to
*/
redirectAllRequestsTo?: string;
/**
* JSON routing rules configuration for the website
*/
routingRules?: string;
}
/**
* CORS rule configuration for OBS bucket
*/
export interface BucketCorsRule {
/**
* List of origins allowed to make cross-origin requests
*/
allowedOrigins: string[];
/**
* List of HTTP methods allowed for cross-origin requests
*/
allowedMethods: string[];
/**
* List of headers allowed in cross-origin requests
*/
allowedHeaders?: string[];
/**
* List of headers exposed to the browser
*/
exposeHeaders?: string[];
/**
* Time in seconds the browser can cache preflight results
*/
maxAgeSeconds?: number;
}
/**
* Lifecycle rule transitions
*/
export interface BucketLifecycleTransition {
/**
* Number of days after object creation the rule applies
*/
days: number;
/**
* Storage class to transition to (STANDARD, WARM, COLD)
*/
storageClass: string;
}
/**
* Lifecycle rule expiration configuration
*/
export interface BucketLifecycleExpiration {
/**
* Number of days after object creation when the rule takes effect
*/
days: number;
}
/**
* Lifecycle rule configuration for OBS bucket
*/
export interface BucketLifecycleRule {
/**
* Unique name for the lifecycle rule
*/
name: string;
/**
* Whether the rule is enabled
*/
enabled: boolean;
/**
* Prefix filter for objects the rule applies to
*/
prefix?: string;
/**
* Expiration configuration for objects
*/
expiration?: BucketLifecycleExpiration;
/**
* Transition configuration for objects
*/
transition?: BucketLifecycleTransition[];
/**
* Expiration configuration for non-current versions
*/
noncurrentVersionExpiration?: BucketLifecycleExpiration;
/**
* Transition configuration for non-current versions
*/
noncurrentVersionTransition?: BucketLifecycleTransition[];
/**
* Configuration for aborting incomplete multipart uploads
*/
abortIncompleteMultipartUpload?: BucketLifecycleExpiration;
}
/**
* Logging configuration for OBS bucket
*/
export interface BucketLoggingConfig {
/**
* Target bucket to store access logs
*/
targetBucket: string;
/**
* Prefix to prepend to all log object keys
*/
targetPrefix?: string;
/**
* Agency with appropriate permissions for logging
*/
agency?: string;
}
/**
* Properties for creating or updating a Huawei Cloud OBS Bucket
*/
export interface BucketProps {
/**
* Name of the bucket
*/
bucket: string;
/**
* Storage class for the bucket (STANDARD, WARM, COLD)
* Default: STANDARD
*/
storageClass?: string;
/**
* ACL permissions for the bucket (private, public-read, public-read-write)
* Default: private
*/
acl?: string;
/**
* Bucket policy document in JSON format
*/
policy?: string;
/**
* Policy format (obs or s3)
* Default: obs
*/
policyFormat?: string;
/**
* Whether versioning is enabled
* Default: false
*/
versioning?: boolean;
/**
* Logging configuration
*/
logging?: BucketLoggingConfig;
/**
* Storage quota in bytes (0 for unlimited)
* Default: 0
*/
quota?: number;
/**
* Lifecycle rules for managing objects
*/
lifecycleRules?: BucketLifecycleRule[];
/**
* Website configuration
*/
website?: BucketWebsiteConfig;
/**
* CORS rules
*/
corsRules?: BucketCorsRule[];
/**
* Tags to apply to the bucket
*/
tags?: Record<string, string>;
/**
* Region where the bucket is created
*/
region?: string;
/**
* Whether to enable multi-AZ for the bucket
*/
multiAz?: boolean;
/**
* Whether to enable parallel file system
*/
parallelFs?: boolean;
/**
* Whether to enable server-side encryption
* Default: false
*/
encryption?: boolean;
/**
* Server-side encryption algorithm (usually AES256)
*/
sseAlgorithm?: string;
/**
* KMS key ID for SSE-KMS encryption
*/
kmsKeyId?: string;
/**
* KMS key project ID
*/
kmsKeyProjectId?: string;
/**
* Enterprise project ID for the bucket
*/
enterpriseProjectId?: string;
/**
* Custom domain names for the bucket
*/
userDomainNames?: string[];
/**
* Whether all objects should be deleted when the bucket is destroyed
* Default: false
*/
forceDestroy?: boolean;
}
/**
* Storage info for OBS bucket
*/
export interface BucketStorageInfo {
/**
* Storage size in bytes
*/
size: number;
/**
* Number of objects in the bucket
*/
objectNumber: number;
}
/**
* Output returned after Bucket creation/update
*/
export interface Bucket extends Resource<"huawei::Bucket">, BucketProps {
/**
* The ID of the bucket (same as the bucket name)
*/
id: string;
/**
* Storage information
*/
storageInfo?: BucketStorageInfo;
/**
* Bucket domain name
*/
bucketDomainName: string;
/**
* Bucket version
*/
bucketVersion?: string;
/**
* Time at which the bucket was created
*/
createdAt: number;
}
/**
* Creates a Huawei Cloud OBS Bucket.
*
* This resource creates and manages Object Storage Service (OBS) buckets on Huawei Cloud.
*
* @example
* // Create a basic private bucket
* const basicBucket = await Bucket("my-bucket", {
* bucket: "my-bucket",
* acl: "private",
* region: "ap-southeast-1"
* });
*
* @example
* // Create a bucket with website hosting
* const websiteBucket = await Bucket("website-bucket", {
* bucket: "website-bucket",
* acl: "public-read",
* website: {
* indexDocument: "index.html",
* errorDocument: "error.html"
* }
* });
*
* @example
* // Create a bucket with lifecycle rules
* const lifecycleBucket = await Bucket("lifecycle-bucket", {
* bucket: "lifecycle-bucket",
* lifecycleRules: [{
* name: "archive-rule",
* enabled: true,
* prefix: "logs/",
* transition: [{
* days: 30,
* storageClass: "WARM"
* }, {
* days: 90,
* storageClass: "COLD"
* }]
* }]
* });
*
* @example
* // Create a versioned bucket with encryption
* const securedBucket = await Bucket("secured-bucket", {
* bucket: "secured-bucket",
* versioning: true,
* encryption: true,
* sseAlgorithm: "AES256"
* });
*/
export const Bucket = Resource(
"huawei::Bucket",
async function (
this: Context<Bucket>,
id: string,
props: BucketProps
): Promise<Bucket> {
// Initialize API client
const region = props.region || process.env.HUAWEI_REGION || "";
const api = new HuaweiApi({ region });
// The bucket name to use for operations
const bucketName = props.bucket;
if (this.phase === "delete") {
try {
if (this.output?.bucket) {
// Check if bucket should be force destroyed
if (props.forceDestroy) {
console.log(
`Deleting all objects from bucket ${this.output.bucket} before deletion`
);
try {
// List all objects
const listResult = await listObjects(api, this.output.bucket);
if (listResult.Contents.length > 0) {
// Delete all objects
const keys = listResult.Contents.map((obj) => obj.Key);
const deleteResult = await deleteObjects(
api,
this.output.bucket,
keys
);
if (deleteResult.Errors && deleteResult.Errors.length > 0) {
console.error(
`Failed to delete some objects in bucket ${this.output.bucket}:`,
deleteResult.Errors
);
}
}
} catch (error) {
console.error(
`Error while emptying bucket ${this.output.bucket}:`,
error
);
}
}
// Delete bucket
const deletePath = `/${this.output.bucket}`;
const deleteResponse = await api.delete(deletePath);
// Check response status directly
if (!deleteResponse.ok && deleteResponse.status !== 404) {
console.error(
`Error deleting bucket ${this.output.bucket}:`,
deleteResponse.statusText
);
}
}
} catch (error) {
console.error(`Error deleting bucket ${bucketName}:`, error);
}
// Return destroyed state
return this.destroy();
} else {
try {
let isUpdate = this.phase === "update" && this.output?.bucket;
// Create or verify bucket exists
if (!isUpdate) {
// Create new bucket
console.log(`Creating bucket ${bucketName}`);
const bucketPath = `/${bucketName}`;
const storageClass = props.storageClass || "STANDARD";
const createParams: any = {};
// Add storage class if not standard
if (storageClass !== "STANDARD") {
createParams.StorageClass = storageClass;
}
// Set parallel file system flag if specified
if (props.parallelFs) {
createParams.ParallelFileSystem = true;
}
// Set multi-AZ flag if specified
if (props.multiAz) {
createParams.MultiAZ = true;
}
const createResponse = await api.put(bucketPath, createParams);
if (!createResponse.ok) {
throw new Error(
`Failed to create bucket ${bucketName}: ${createResponse.statusText}`
);
}
} else {
// For updates, verify bucket exists
console.log(`Updating bucket ${bucketName}`);
const headResponse = await api.get(`/${bucketName}`);
if (!headResponse.ok) {
throw new Error(`Bucket ${bucketName} does not exist`);
}
}
// Apply bucket configurations
// Set ACL
if (props.acl) {
const aclResponse = await setBucketAcl(api, bucketName, props.acl);
if (!aclResponse.ok) {
console.error(
`Failed to set ACL on bucket ${bucketName}: ${aclResponse.statusText}`
);
}
}
// Set policy
if (props.policy) {
const policyResponse = await setBucketPolicy(
api,
bucketName,
props.policy
);
if (!policyResponse.ok) {
console.error(
`Failed to set policy on bucket ${bucketName}: ${policyResponse.statusText}`
);
}
}
// Set versioning
if (props.versioning !== undefined) {
const versioningResponse = await setBucketVersioning(
api,
bucketName,
props.versioning
);
if (!versioningResponse.ok) {
console.error(
`Failed to set versioning on bucket ${bucketName}: ${versioningResponse.statusText}`
);
}
}
// Set website configuration
if (props.website) {
const websiteResponse = await setBucketWebsite(
api,
bucketName,
props.website
);
if (!websiteResponse.ok) {
console.error(
`Failed to set website configuration on bucket ${bucketName}: ${websiteResponse.statusText}`
);
}
} else if (isUpdate && this.output?.website) {
// Remove website configuration if it was previously set but now removed
await deleteBucketWebsite(api, bucketName);
}
// Set CORS rules
if (props.corsRules && props.corsRules.length > 0) {
const corsResponse = await setBucketCors(
api,
bucketName,
props.corsRules
);
if (!corsResponse.ok) {
console.error(
`Failed to set CORS rules on bucket ${bucketName}: ${corsResponse.statusText}`
);
}
} else if (
isUpdate &&
this.output?.corsRules &&
this.output.corsRules.length > 0
) {
// Remove CORS rules if they were previously set but now removed
await deleteBucketCors(api, bucketName);
}
// Set lifecycle rules
if (props.lifecycleRules && props.lifecycleRules.length > 0) {
const lifecycleResponse = await setBucketLifecycle(
api,
bucketName,
props.lifecycleRules
);
if (!lifecycleResponse.ok) {
console.error(
`Failed to set lifecycle rules on bucket ${bucketName}: ${lifecycleResponse.statusText}`
);
}
} else if (
isUpdate &&
this.output?.lifecycleRules &&
this.output.lifecycleRules.length > 0
) {
// Remove lifecycle rules if they were previously set but now removed
await deleteBucketLifecycle(api, bucketName);
}
// Set logging configuration
if (props.logging) {
const loggingResponse = await setBucketLogging(
api,
bucketName,
props.logging
);
if (!loggingResponse.ok) {
console.error(
`Failed to set logging configuration on bucket ${bucketName}: ${loggingResponse.statusText}`
);
}
} else if (isUpdate && this.output?.logging) {
// Remove logging configuration if it was previously set but now removed
await deleteBucketLogging(api, bucketName);
}
// Set tags
if (props.tags && Object.keys(props.tags).length > 0) {
const tagsResponse = await setBucketTags(api, bucketName, props.tags);
if (!tagsResponse.ok) {
console.error(
`Failed to set tags on bucket ${bucketName}: ${tagsResponse.statusText}`
);
}
} else if (
isUpdate &&
this.output?.tags &&
Object.keys(this.output.tags || {}).length > 0
) {
// Remove tags if they were previously set but now removed
await deleteBucketTags(api, bucketName);
}
// Set encryption
if (props.encryption) {
const algorithm = props.sseAlgorithm || "AES256";
const encryptionResponse = await setBucketEncryption(
api,
bucketName,
algorithm,
props.kmsKeyId
);
if (!encryptionResponse.ok) {
console.error(
`Failed to set encryption on bucket ${bucketName}: ${encryptionResponse.statusText}`
);
}
} else if (isUpdate && this.output?.encryption) {
// Remove encryption if it was previously set but now removed
await deleteBucketEncryption(api, bucketName);
}
// Quota would be set here
// Enterprise project ID would be set here
// User domain names would be set here
// Construct the output
const createdAt =
isUpdate && this.output?.createdAt
? this.output.createdAt
: Date.now();
return this({
id: bucketName,
bucket: bucketName,
storageClass: props.storageClass || "STANDARD",
acl: props.acl || "private",
policy: props.policy,
policyFormat: props.policyFormat || "obs",
versioning: props.versioning || false,
logging: props.logging,
quota: props.quota || 0,
lifecycleRules: props.lifecycleRules,
website: props.website,
corsRules: props.corsRules,
tags: props.tags,
region: region,
multiAz: props.multiAz,
parallelFs: props.parallelFs,
encryption: props.encryption || false,
sseAlgorithm: props.sseAlgorithm,
kmsKeyId: props.kmsKeyId,
kmsKeyProjectId: props.kmsKeyProjectId,
enterpriseProjectId: props.enterpriseProjectId,
userDomainNames: props.userDomainNames,
forceDestroy: props.forceDestroy || false,
bucketDomainName: `${bucketName}.obs.${region}.myhuaweicloud.com`,
storageInfo:
isUpdate && this.output?.storageInfo
? this.output.storageInfo
: undefined,
bucketVersion:
isUpdate && this.output?.bucketVersion
? this.output.bucketVersion
: undefined,
createdAt: createdAt,
});
} catch (error) {
console.error(`Error creating/updating bucket ${bucketName}:`, error);
throw error;
}
}
}
);
/**
* Set bucket ACL
*/
export async function setBucketAcl(
api: HuaweiApi,
bucket: string,
acl: string
): Promise<Response> {
return api.put(`/${bucket}?acl`, { acl });
}
/**
* Set bucket policy
*/
export async function setBucketPolicy(
api: HuaweiApi,
bucket: string,
policy: string
): Promise<Response> {
return api.put(`/${bucket}?policy`, policy);
}
/**
* Set bucket versioning
*/
export async function setBucketVersioning(
api: HuaweiApi,
bucket: string,
enabled: boolean
): Promise<Response> {
const status = enabled ? "Enabled" : "Suspended";
return api.put(`/${bucket}?versioning`, {
VersioningConfiguration: {
Status: status,
},
});
}
/**
* Set bucket website configuration
*/
export async function setBucketWebsite(
api: HuaweiApi,
bucket: string,
config: BucketWebsiteConfig
): Promise<Response> {
const websiteConfig: any = {
WebsiteConfiguration: {},
};
if (config.redirectAllRequestsTo) {
websiteConfig.WebsiteConfiguration.RedirectAllRequestsTo = {
HostName: config.redirectAllRequestsTo,
};
} else {
if (config.indexDocument) {
websiteConfig.WebsiteConfiguration.IndexDocument = {
Suffix: config.indexDocument,
};
}
if (config.errorDocument) {
websiteConfig.WebsiteConfiguration.ErrorDocument = {
Key: config.errorDocument,
};
}
if (config.routingRules) {
try {
const rules = JSON.parse(config.routingRules);
websiteConfig.WebsiteConfiguration.RoutingRules = {
RoutingRule: rules,
};
} catch (e) {
throw new Error(`Invalid routing rules JSON: ${e}`);
}
}
}
return api.put(`/${bucket}?website`, websiteConfig);
}
/**
* Delete bucket website configuration
*/
export async function deleteBucketWebsite(
api: HuaweiApi,
bucket: string
): Promise<Response> {
return api.delete(`/${bucket}?website`);
}
/**
* Set bucket CORS rules
*/
export async function setBucketCors(
api: HuaweiApi,
bucket: string,
rules: BucketCorsRule[]
): Promise<Response> {
const corsConfig: any = {
CORSConfiguration: {
CORSRule: rules.map((rule) => ({
AllowedOrigin: rule.allowedOrigins,
AllowedMethod: rule.allowedMethods,
AllowedHeader: rule.allowedHeaders,
ExposeHeader: rule.exposeHeaders,
MaxAgeSeconds: rule.maxAgeSeconds || 100,
})),
},
};
return api.put(`/${bucket}?cors`, corsConfig);
}
/**
* Delete bucket CORS configuration
*/
export async function deleteBucketCors(
api: HuaweiApi,
bucket: string
): Promise<Response> {
return api.delete(`/${bucket}?cors`);
}
/**
* Set bucket lifecycle rules
*/
export async function setBucketLifecycle(
api: HuaweiApi,
bucket: string,
rules: BucketLifecycleRule[]
): Promise<Response> {
const lifecycleConfig: any = {
LifecycleConfiguration: {
Rule: rules.map((rule) => {
const ruleConfig: any = {
ID: rule.name,
Status: rule.enabled ? "Enabled" : "Disabled",
Prefix: rule.prefix || "",
};
if (rule.expiration) {
ruleConfig.Expiration = {
Days: rule.expiration.days,
};
}
if (rule.transition && rule.transition.length > 0) {
ruleConfig.Transition = rule.transition.map((t) => ({
Days: t.days,
StorageClass: t.storageClass,
}));
}
if (rule.noncurrentVersionExpiration) {
ruleConfig.NoncurrentVersionExpiration = {
NoncurrentDays: rule.noncurrentVersionExpiration.days,
};
}
if (
rule.noncurrentVersionTransition &&
rule.noncurrentVersionTransition.length > 0
) {
ruleConfig.NoncurrentVersionTransition =
rule.noncurrentVersionTransition.map((t) => ({
NoncurrentDays: t.days,
StorageClass: t.storageClass,
}));
}
if (rule.abortIncompleteMultipartUpload) {
ruleConfig.AbortIncompleteMultipartUpload = {
DaysAfterInitiation: rule.abortIncompleteMultipartUpload.days,
};
}
return ruleConfig;
}),
},
};
return api.put(`/${bucket}?lifecycle`, lifecycleConfig);
}
/**
* Delete bucket lifecycle configuration
*/
export async function deleteBucketLifecycle(
api: HuaweiApi,
bucket: string
): Promise<Response> {
return api.delete(`/${bucket}?lifecycle`);
}
/**
* Set bucket logging configuration
*/
export async function setBucketLogging(
api: HuaweiApi,
bucket: string,
config: BucketLoggingConfig
): Promise<Response> {
const loggingConfig: any = {
LoggingEnabled: {
TargetBucket: config.targetBucket,
TargetPrefix: config.targetPrefix || "logs/",
Agency: config.agency || "",
},
};
return api.put(`/${bucket}?logging`, {
BucketLoggingStatus: loggingConfig,
});
}
/**
* Delete bucket logging configuration
*/
export async function deleteBucketLogging(
api: HuaweiApi,
bucket: string
): Promise<Response> {
return api.put(`/${bucket}?logging`, { BucketLoggingStatus: {} });
}
/**
* Set bucket tags
*/
export async function setBucketTags(
api: HuaweiApi,
bucket: string,
tags: Record<string, string>
): Promise<Response> {
const tagsConfig: any = {
Tagging: {
TagSet: {
Tag: Object.entries(tags).map(([key, value]) => ({
Key: key,
Value: value,
})),
},
},
};
return api.put(`/${bucket}?tagging`, tagsConfig);
}
/**
* Delete bucket tags
*/
export async function deleteBucketTags(
api: HuaweiApi,
bucket: string
): Promise<Response> {
return api.delete(`/${bucket}?tagging`);
}
/**
* Set bucket encryption
*/
export async function setBucketEncryption(
api: HuaweiApi,
bucket: string,
algorithm: string,
kmsKeyId?: string
): Promise<Response> {
const encryptionConfig: any = {
ServerSideEncryptionConfiguration: {
Rule: [
{
ApplyServerSideEncryptionByDefault: {
SSEAlgorithm: algorithm,
},
},
],
},
};
if (kmsKeyId && algorithm === "kms") {
encryptionConfig.ServerSideEncryptionConfiguration.Rule[0].ApplyServerSideEncryptionByDefault.KMSMasterKeyID =
kmsKeyId;
}
return api.put(`/${bucket}?encryption`, encryptionConfig);
}
/**
* Delete bucket encryption
*/
export async function deleteBucketEncryption(
api: HuaweiApi,
bucket: string
): Promise<Response> {
return api.delete(`/${bucket}?encryption`);
}
/**
* List objects in a bucket
*/
export async function listObjects(
api: HuaweiApi,
bucket: string
): Promise<{ Contents: Array<{ Key: string }> }> {
const response = await api.get(`/${bucket}`);
if (!response.ok) {
throw new Error(
`Failed to list objects in bucket ${bucket}: ${response.statusText}`
);
}
const data = await response.text();
// Parse XML response to extract object keys
// This is a simplified approach - in a real implementation you would use an XML parser
// Mock response for demonstration
const mockContents: Array<{ Key: string }> = [];
return { Contents: mockContents };
}
/**
* Delete objects from a bucket
*/
export async function deleteObjects(
api: HuaweiApi,
bucket: string,
keys: string[]
): Promise<{ Errors: any[] }> {
if (keys.length === 0) {
return { Errors: [] };
}
const deleteConfig = {
Delete: {
Object: keys.map((key) => ({ Key: key })),
Quiet: true,
},
};
const response = await api.post(`/${bucket}?delete`, deleteConfig);
if (!response.ok) {
throw new Error(
`Failed to delete objects from bucket ${bucket}: ${response.statusText}`
);
}
// Mock response for demonstration
return { Errors: [] };
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment