Skip to content

Commit

Permalink
impl Object.deleteMulti()
Browse files Browse the repository at this point in the history
  • Loading branch information
fengmk2 committed Sep 18, 2023
1 parent 21ca4dd commit 3f81b7a
Show file tree
Hide file tree
Showing 10 changed files with 174 additions and 178 deletions.
57 changes: 0 additions & 57 deletions lib/common/object/deleteMulti.js

This file was deleted.

1 change: 0 additions & 1 deletion lib/common/utils/obj2xml.d.ts

This file was deleted.

41 changes: 0 additions & 41 deletions lib/common/utils/obj2xml.js

This file was deleted.

13 changes: 7 additions & 6 deletions src/OSSBaseClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ export abstract class OSSBaseClient {

let data = result.data as T;
if (params.xmlResponse) {
data = await this.#parseXML<T>(result.data);
data = await this.#xml2json<T>(result.data);
}
return {
data,
Expand Down Expand Up @@ -255,11 +255,12 @@ export abstract class OSSBaseClient {
return `${sdk} ${platform}`;
}

async #parseXML<T = any>(content: string | Buffer) {
if (Buffer.isBuffer(content)) {
content = content.toString();
async #xml2json<T = any>(xml: string | Buffer) {
if (Buffer.isBuffer(xml)) {
xml = xml.toString();
}
return await parseStringPromise(content, {
debug('xml2json %o', xml);
return await parseStringPromise(xml, {
explicitRoot: false,
explicitArray: false,
}) as T;
Expand All @@ -285,7 +286,7 @@ export abstract class OSSBaseClient {

let info;
try {
info = await this.#parseXML(xml);
info = await this.#xml2json(xml);
} catch (e: any) {
err = new OSSClientError('PreconditionFailed', `${e.message} (raw xml=${JSON.stringify(xml)})`, requestId, hostId);
return err;
Expand Down
61 changes: 59 additions & 2 deletions src/OSSObject.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Readable } from 'node:stream';

Check failure on line 1 in src/OSSObject.ts

View workflow job for this annotation

GitHub Actions / build (16, ubuntu-latest)

'"oss-interface"' has no exported member named 'DeleteObjectOptions'. Did you mean 'GetObjectOptions'?

Check failure on line 1 in src/OSSObject.ts

View workflow job for this annotation

GitHub Actions / build (16, ubuntu-latest)

'"oss-interface"' has no exported member named 'DeleteObjectResult'. Did you mean 'GetObjectResult'?

Check failure on line 1 in src/OSSObject.ts

View workflow job for this annotation

GitHub Actions / build (16, ubuntu-latest)

Property 'timeout' does not exist on type 'DeleteMultipleObjectOptions'.

Check failure on line 1 in src/OSSObject.ts

View workflow job for this annotation

GitHub Actions / build (16, ubuntu-latest)

Property 'versionId' does not exist on type 'DeleteMultipleObjectOptions'.

Check failure on line 1 in src/OSSObject.ts

View workflow job for this annotation

GitHub Actions / build (16, ubuntu-latest)

Property 'versionId' does not exist on type 'DeleteMultipleObjectOptions'.

Check failure on line 1 in src/OSSObject.ts

View workflow job for this annotation

GitHub Actions / build (16, ubuntu-latest)

Argument of type 'object' is not assignable to parameter of type 'IncomingHttpHeaders'.

Check failure on line 1 in src/OSSObject.ts

View workflow job for this annotation

GitHub Actions / build (16, ubuntu-latest)

Element implicitly has an 'any' type because expression of type '"x-oss-callback"' can't be used to index type '{}'.

Check failure on line 1 in src/OSSObject.ts

View workflow job for this annotation

GitHub Actions / build (16, ubuntu-latest)

Argument of type 'PutObjectOptions' is not assignable to parameter of type 'Pick<OSSRequestParams, "timeout" | "subResource" | "headers">'.

Check failure on line 1 in src/OSSObject.ts

View workflow job for this annotation

GitHub Actions / build (18, ubuntu-latest)

'"oss-interface"' has no exported member named 'DeleteObjectOptions'. Did you mean 'GetObjectOptions'?

Check failure on line 1 in src/OSSObject.ts

View workflow job for this annotation

GitHub Actions / build (18, ubuntu-latest)

'"oss-interface"' has no exported member named 'DeleteObjectResult'. Did you mean 'GetObjectResult'?

Check failure on line 1 in src/OSSObject.ts

View workflow job for this annotation

GitHub Actions / build (18, ubuntu-latest)

Property 'timeout' does not exist on type 'DeleteMultipleObjectOptions'.

Check failure on line 1 in src/OSSObject.ts

View workflow job for this annotation

GitHub Actions / build (18, ubuntu-latest)

Property 'versionId' does not exist on type 'DeleteMultipleObjectOptions'.

Check failure on line 1 in src/OSSObject.ts

View workflow job for this annotation

GitHub Actions / build (18, ubuntu-latest)

Property 'versionId' does not exist on type 'DeleteMultipleObjectOptions'.

Check failure on line 1 in src/OSSObject.ts

View workflow job for this annotation

GitHub Actions / build (18, ubuntu-latest)

Argument of type 'object' is not assignable to parameter of type 'IncomingHttpHeaders'.

Check failure on line 1 in src/OSSObject.ts

View workflow job for this annotation

GitHub Actions / build (18, ubuntu-latest)

Element implicitly has an 'any' type because expression of type '"x-oss-callback"' can't be used to index type '{}'.

Check failure on line 1 in src/OSSObject.ts

View workflow job for this annotation

GitHub Actions / build (18, ubuntu-latest)

Argument of type 'PutObjectOptions' is not assignable to parameter of type 'Pick<OSSRequestParams, "timeout" | "subResource" | "headers">'.
import { createReadStream } from 'node:fs';
import { strict as assert } from 'node:assert';
import fs from 'node:fs/promises';
import { IncomingHttpHeaders } from 'node:http';
import mime from 'mime';
import { isReadable } from 'is-type-of';

import type {
// IObjectSimple,
// GetObjectOptions,
Expand Down Expand Up @@ -32,11 +34,16 @@ import {
OSSBaseClient,
} from './OSSBaseClient.js';
import {
DeleteMultipleObject,
DeleteMultipleObjectOptions,
DeleteMultipleObjectResponse,
DeleteMultipleObjectXML,
OSSRequestParams,
RequestMethod,
} from './type/index.js';
import { checkBucketName } from './util/index.js';
import { encodeCallback } from './util/encodeCallback.js';
import { json2xml } from './util/json2xml.js';

export interface OSSObjectClientInitOptions extends OSSBaseClientInitOptions {
bucket: string;
Expand Down Expand Up @@ -325,6 +332,57 @@ export class OSSObject extends OSSBaseClient {
};
}

/**
* DeleteMultipleObjects
* @see https://help.aliyun.com/zh/oss/developer-reference/deletemultipleobjects
*/
async deleteMulti(namesOrObjects: string[] | DeleteMultipleObject[], options?: DeleteMultipleObjectOptions): Promise<DeleteMultipleObjectResponse> {
const objects: DeleteMultipleObjectXML[] = [];
assert(namesOrObjects.length > 0, 'namesOrObjects is empty');
for (const nameOrObject of namesOrObjects) {
if (typeof nameOrObject === 'string') {
objects.push({ Key: this.#objectName(nameOrObject) });
} else {
assert(nameOrObject.key, 'key is empty');
objects.push({ Key: this.#objectName(nameOrObject.key), VersionId: nameOrObject.versionId });
}
}

const xml = json2xml({
Delete: {
Quiet: !!options?.quiet,
Object: objects,
},
}, { headers: true });

const requestOptions = {
timeout: options?.timeout,
// ?delete
subResource: { delete: '' } as Record<string, string>,
};
if (options?.versionId) {
requestOptions.subResource.versionId = options.versionId;
}

const params = this.#objectRequestParams('POST', '', requestOptions);
params.mime = 'xml';
params.content = Buffer.from(xml, 'utf-8');
params.xmlResponse = true;
params.successStatuses = [ 200 ];
const { data, res } = await this.request(params);
// quiet will return null
let deleted = data?.Deleted || [];
if (deleted) {
if (!Array.isArray(deleted)) {
deleted = [ deleted ];
}
}
return {
res,
deleted,
} satisfies DeleteMultipleObjectResponse;
}

/** protected methods */

protected getRequestEndpoint(): string {
Expand All @@ -334,7 +392,6 @@ export class OSSObject extends OSSBaseClient {
/** private methods */

async #sendPutRequest(name: string, options: PutObjectOptions, contentOrStream: Buffer | Readable) {
const method = 'PUT';
options.headers = options.headers ?? {};
name = this.#objectName(name);
this.#convertMetaToHeaders(options.meta, options.headers);
Expand All @@ -343,7 +400,7 @@ export class OSSObject extends OSSBaseClient {
const callbackHeaders = encodeCallback(options.callback);
Object.assign(options.headers, callbackHeaders);
}
const params = this.#objectRequestParams(method, name, options);
const params = this.#objectRequestParams('PUT', name, options);
params.mime = options.mime;
if (Buffer.isBuffer(contentOrStream)) {
params.content = contentOrStream;
Expand Down
27 changes: 27 additions & 0 deletions src/type/Object.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import type { DeleteObjectOptions, NormalSuccessResponse } from 'oss-interface';

export interface DeleteMultipleObject {
key: string;
versionId?: string;
}

export interface DeleteMultipleObjectXML {
Key: string;
VersionId?: string;
}

export interface DeleteMultipleObjectOptions extends DeleteObjectOptions {
quiet?: boolean;
}

export interface DeleteMultipleResponseObjectXML {
Key: string;
VersionId?: string;
DeleteMarker?: boolean;
DeleteMarkerVersionId?: string;
}

export interface DeleteMultipleObjectResponse {
res: NormalSuccessResponse;
deleted: DeleteMultipleResponseObjectXML[];
}
1 change: 1 addition & 0 deletions src/type/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './Object.js';
export * from './Request.js';
26 changes: 26 additions & 0 deletions src/util/json2xml.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import utility from 'utility';

export function json2xml(json: Record<string, any>, options?: { headers: boolean }) {
let xml = '';
if (options?.headers) {
xml = '<?xml version="1.0" encoding="UTF-8"?>\n';
}
for (const key in json) {
const value = json[key];
if (value === null || value === undefined) continue;
if (Array.isArray(value)) {
for (const item of value) {
xml += `<${key}>`;
xml += json2xml(item);
xml += `</${key}>`;
}
} else if (typeof value === 'object') {
xml += `<${key}>`;
xml += json2xml(value);
xml += `</${key}>`;
} else {
xml += `<${key}>${utility.escape(value.toString())}</${key}>`;
}
}
return xml;
}
54 changes: 54 additions & 0 deletions test/OSSObject.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,8 @@ describe('test/OSSObject.test.ts', () => {
});

// delete the new file
const result = await ossObject.delete(name);
assert.equal(result.res.status, 204);
});

// it('should with options.ctx', async () => {
Expand Down Expand Up @@ -427,4 +429,56 @@ describe('test/OSSObject.test.ts', () => {
assert.equal(info.res.status, 204);
});
});

describe('deleteMulti()', () => {
const names: string[] = [];
beforeEach(async () => {
let name = `${prefix}oss-client/oss/deleteMulti0.js`;
names.push(name);
await ossObject.put(name, __filename);

name = `${prefix}oss-client/oss/deleteMulti1.js`;
names.push(name);
await ossObject.put(name, __filename);

name = `${prefix}oss-client/oss/deleteMulti2.js`;
names.push(name);
await ossObject.put(name, __filename);
});

it('should delete 3 exists objs', async () => {
const result = await ossObject.deleteMulti(names);
assert.deepEqual(
result.deleted.map(v => v.Key),
names,
);
assert.equal(result.res.status, 200);
});

it('should delete 2 exists and 2 not exists objs', async () => {
const result = await ossObject.deleteMulti(names.slice(0, 2).concat([ 'not-exist1', 'not-exist2' ]));
assert.deepEqual(
result.deleted.map(v => v.Key),
names.slice(0, 2).concat([ 'not-exist1', 'not-exist2' ]),
);
assert.equal(result.res.status, 200);
});

it('should delete 1 exists objs', async () => {
const result = await ossObject.deleteMulti(names.slice(0, 1));
assert.deepEqual(
result.deleted.map(v => v.Key),
names.slice(0, 1),
);
assert.equal(result.res.status, 200);
});

it('should delete in quiet mode', async () => {
const result = await ossObject.deleteMulti(names, {
quiet: true,
});
assert.equal(result.deleted.length, 0);
assert.equal(result.res.status, 200);
});
});
});
Loading

0 comments on commit 3f81b7a

Please sign in to comment.