diff --git a/.eslintrc.js b/.eslintrc.js index 794b582d..f98c4065 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -19,7 +19,7 @@ module.exports = { jest: true }, rules: { - indent: ['error', 2], + indent: 'off', 'no-underscore-dangle': [0], 'no-plusplus': [0], 'no-return-await': [0], diff --git a/README.md b/README.md index ce035339..76be9a52 100644 --- a/README.md +++ b/README.md @@ -126,6 +126,11 @@ All operation use es7 async/await to implement. All api is async function. - [.extendBucketWorm(name, wormId, days[, options])](#extendBucketWormname-wormId-days-options) - [.getBucketWorm(name[, options])](#getBucketWormname-options) - [.initiateBucketWorm(name, days[, options])](#initiateBucketWormname-days-options) + - Data Indexing + - [.openMetaQuery(bucketName[, options])](#openMetaQueryBucketName-options) + - [.getMetaQueryStatus(bucketName[, options])](#getMetaQueryStatusBucketName-options) + - [.doMetaQuery(bucketName, queryParam[, options])](#doMetaQueryBucketName-queryParam-options) + - [.closeMetaQuery(bucketName[, options])](#closeMetaQueryBucketName-options) - [Object Operations](#object-operations) - [.list(query[, options])](#listquery-options) @@ -1702,6 +1707,129 @@ Success will return: --- +### .openMetaQuery(bucketName[, options]) + +Enables the metadata management feature for a bucket. + +parameters: + +- bucketName {String} bucket name +- [options] {Object} optional args + +Success will return: + +- status {Number} response status +- res {Object} response info + +--- + +### .getMetaQueryStatus(bucketName[, options]) + +Queries the information about the metadata index library of a bucket. + +parameters: + +- bucketName {String} bucket name +- [options] {Object} optional args + +Success will return: + +- status {Number} response status +- res {Object} response info +- phase {String} the scan type +- state {String} the status of the metadata index library +- createTime {String} the time when the metadata index library was created +- updateTime {String} the time when the metadata index library was updated + +--- + +### .doMetaQuery(bucketName, queryParam[, options]) + +Queries objects that meet specified conditions and lists the information about objects based on specified fields and sorting methods. + +parameters: + +- bucketName {String} the bucket name +- queryParam {Object} query parameters + - [nextToken] {String} The token that is used for the next query when the total number of objects exceeds the value of MaxResults. The object information is returned in alphabetical order starting from the value of NextToken. When this operation is called for the first time, this field does not need to be set. + - [maxResults] {Number} The maximum number of objects to return. Valid values: 0 to 100. + - query {Object} The query condition. + - [field] {String} the fields. For more information about supported fields and supported operators, see Appendix: Supported fields and operators. + - [value] {String} The value of the field. + - operation {String} The operators. Valid values: eq (equal to), gt (greater than), gte (greater than or equal to), lt (less than), lte (less than or equal to), match (fuzzy query), prefix (prefix query), and (AND), or (OR), and not (NOT). + - [subQueries] {Array} The subquery conditions. Options that are included in this element are the same as those of simple query. You must set subquery conditions only when Operation is set to AND, OR, or NOT. + - [sort] {String} The field based on which the results are sorted. For more information about the fields that can be sorted, see Appendix: Supported fields and operators. + - [order] {String} `asc` | `desc` The order in which you want to sort the queried data. Default value: desc. Valid values: asc and desc. + - [aggregations] {Object} The information about aggregate operations. + - field {String} The name of the field. For more information about supported fields and supported operators, see Appendix: Supported fields and operators. + - operation {String} The operator for aggregate operations. Valid values: min, max, average, sum, count, distinct and group. +- [options] {Object} optional parameters + - [timeout] {Number} the operation timeout (ms) + +Success will return: + +- status {Number} response status +- res {Object} response info, including + - status {Number} response status + - headers {Object} response headers + - size {Number} response size + - rt {Number} request total use time (ms) +- nextToken {String} the token that is used for the next query when the total number of objects exceeds the value of MaxResults +- files {Array} the information about objects +- aggregations {Array} the information about aggregate operations + +--- + +example: + +```js +const result = await store.put('test.txt', 'hello world'); +console.log(result); +``` + +--- + +###.get(name[, options]) + +Get the object content. + +parameters: + +- name {String} object name store on OSS + +--- + +example: + +```js +const queryParam = { + maxResults: 2, + query: { operation: 'and', subQueries: [{ field: 'Size', value: '1048575', operation: 'lt' }] }, + sort: 'Size', + order: 'asc' +}; +const result = await store.doMetaQuery(bucket, queryParam); +console.log(result); +``` + +--- + +### .closeMetaQuery(bucketName[, options]) + +Disables the metadata management feature for a bucket. + +parameters: + +- bucketName {String} bucket name +- [options] {Object} optional args + +Success will return: + +- status {Number} response status +- res {Object} response info + +--- + ## Object Operations All operations function return Promise, except `signatureUrl`. diff --git a/karma.conf.js b/karma.conf.js index e69c46e4..481d68ad 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -34,7 +34,7 @@ module.exports = function (config) { concurrency: 1, client: { mocha: { - timeout: 6000 + timeout: 16000 } } }); diff --git a/lib/browser/client.js b/lib/browser/client.js index f8868f2c..f1c29df8 100644 --- a/lib/browser/client.js +++ b/lib/browser/client.js @@ -106,6 +106,9 @@ merge(proto, require('../common/bucket/getBucketWebsite')); merge(proto, require('../common/bucket/putBucketWebsite')); merge(proto, require('../common/bucket/deleteBucketWebsite')); +// bucket data index +merge(proto, require('../common/bucket/dataIndex')); + // lifecycle merge(proto, require('../common/bucket/getBucketLifecycle')); merge(proto, require('../common/bucket/putBucketLifecycle')); diff --git a/lib/common/bucket/dataIndex.d.ts b/lib/common/bucket/dataIndex.d.ts new file mode 100644 index 00000000..363511ad --- /dev/null +++ b/lib/common/bucket/dataIndex.d.ts @@ -0,0 +1,68 @@ +export declare function openMetaQuery( + this: any, + bucketName: string, + options?: {} +): Promise<{ + res: any; + status: any; +}>; +export declare function getMetaQueryStatus( + this: any, + bucketName: string, + options?: {} +): Promise<{ + res: any; + status: any; + phase: any; + state: any; + createTime: any; + updateTime: any; +}>; +interface IQuery { + field?: string; + value?: string; + operation: string; + subQueries?: IQuery[]; +} +declare enum EOperation { + min = 'min', + max = 'max', + average = 'average', + sum = 'sum', + count = 'count', + distinct = 'distinct', + group = 'group' +} +interface IAggregation { + field: string; + operation: EOperation; +} +interface IMetaQuery { + nextToken?: string; + maxResults?: number; + query: IQuery; + sort?: string; + order?: 'asc' | 'desc'; + aggregations?: IAggregation[]; +} +export declare function doMetaQuery( + this: any, + bucketName: string, + queryParam: IMetaQuery, + options?: {} +): Promise<{ + res: any; + status: any; + nextToken: any; + files: any[]; + aggregations: any[]; +}>; +export declare function closeMetaQuery( + this: any, + bucketName: string, + options?: {} +): Promise<{ + res: any; + status: any; +}>; +export {}; diff --git a/lib/common/bucket/dataIndex.js b/lib/common/bucket/dataIndex.js new file mode 100644 index 00000000..23bac7bd --- /dev/null +++ b/lib/common/bucket/dataIndex.js @@ -0,0 +1,183 @@ +'use strict'; +Object.defineProperty(exports, '__esModule', { value: true }); +exports.closeMetaQuery = exports.doMetaQuery = exports.getMetaQueryStatus = exports.openMetaQuery = void 0; +// https://help.aliyun.com/zh/oss/developer-reference/data-indexing +// https://www.alibabacloud.com/help/en/oss/developer-reference/dometaquery +const checkBucketName_1 = require('../utils/checkBucketName'); +const obj2xml_1 = require('../utils/obj2xml'); +const formatObjKey_1 = require('../utils/formatObjKey'); +async function openMetaQuery(bucketName, options = {}) { + checkBucketName_1.checkBucketName(bucketName); + const params = this._bucketRequestParams('POST', bucketName, { metaQuery: '', comp: 'add' }, options); + params.successStatuses = [200]; + const result = await this.request(params); + return { + res: result.res, + status: result.status + }; +} +exports.openMetaQuery = openMetaQuery; +async function getMetaQueryStatus(bucketName, options = {}) { + checkBucketName_1.checkBucketName(bucketName); + const params = this._bucketRequestParams('GET', bucketName, 'metaQuery', options); + params.successStatuses = [200]; + const result = await this.request(params); + const data = await this.parseXML(result.data); + return { + res: result.res, + status: result.status, + phase: data.Phase, + state: data.State, + createTime: data.CreateTime, + updateTime: data.UpdateTime + }; +} +exports.getMetaQueryStatus = getMetaQueryStatus; +var EOperation; +(function (EOperation) { + EOperation['min'] = 'min'; + EOperation['max'] = 'max'; + EOperation['average'] = 'average'; + EOperation['sum'] = 'sum'; + EOperation['count'] = 'count'; + EOperation['distinct'] = 'distinct'; + EOperation['group'] = 'group'; +})(EOperation || (EOperation = {})); +async function doMetaQuery(bucketName, queryParam, options = {}) { + checkBucketName_1.checkBucketName(bucketName); + const params = this._bucketRequestParams('POST', bucketName, { metaQuery: '', comp: 'query' }, options); + params.successStatuses = [200]; + const { aggregations } = queryParam; + let aggregationsParam; + if (aggregations && aggregations.length > 0) { + aggregationsParam = { + Aggregation: aggregations.map(item => ({ Field: item.field, Operation: item.operation })) + }; + } + const paramXMLObj = { + MetaQuery: { + NextToken: queryParam.nextToken, + MaxResults: queryParam.maxResults, + Query: JSON.stringify(formatObjKey_1.formatObjKey(queryParam.query, 'firstUpperCase')), + Sort: queryParam.sort, + Order: queryParam.order, + Aggregations: aggregationsParam + } + }; + params.mime = 'xml'; + params.content = obj2xml_1.obj2xml(paramXMLObj, { headers: true, firstUpperCase: true }); + const result = await this.request(params); + const { NextToken, Files, Aggregations: aggRes } = await this.parseXML(result.data); + let files = []; + if (Files && Files.File) { + const getFileObject = item => { + let ossTagging = []; + const { OSSTagging } = item; + if (OSSTagging && OSSTagging.Tagging) { + const { Tagging } = OSSTagging; + if (Tagging instanceof Array) { + ossTagging = Tagging.map(tagging => ({ + key: tagging.Key, + value: tagging.Value + })); + } else if (Tagging instanceof Object) { + ossTagging = [{ key: Tagging.Key, vlaue: Tagging.Value }]; + } + } + let ossUserMeta = []; + const { OSSUserMeta } = item; + if (OSSUserMeta && OSSUserMeta.UserMeta) { + const { UserMeta } = OSSUserMeta; + if (UserMeta instanceof Array) { + ossUserMeta = UserMeta.map(meta => ({ + key: meta.Key, + value: meta.Value + })); + } else if (UserMeta instanceof Object) { + ossUserMeta = [{ key: UserMeta.Key, vlaue: UserMeta.Value }]; + } + } + let ossTaggingCount = 0; + if (item.OSSTaggingCount && item.OSSTaggingCount.length > 0) ossTaggingCount = parseInt(item.OSSTaggingCount, 10); + return { + fileName: item.Filename, + size: parseInt(item.Size, 10), + fileModifiedTime: item.FileModifiedTime, + ossObjectType: item.OSSObjectType, + ossStorageClass: item.OSSStorageClass, + objectACL: item.ObjectACL, + eTag: item.ETag, + ossTaggingCount, + ossTagging, + ossUserMeta, + ossCRC64: item.OSSCRC64, + serverSideEncryption: item.ServerSideEncryption, + serverSideEncryptionCustomerAlgorithm: item.ServerSideEncryptionCustomerAlgorithm + }; + }; + if (Files.File instanceof Array) { + files = Files.File.map(getFileObject); + } else { + files = [getFileObject(Files.File)]; + } + } + let aggList = []; + if (aggRes) { + const getAggregationObject = item => { + var _a; + let groups = []; + const { Groups } = item; + if (Groups && Groups.Group) { + const { Group } = Groups; + if (Group instanceof Array) { + groups = Group.map(group => { + var _a; + return { + value: group.Value, + count: + ((_a = group.Count) === null || _a === void 0 ? void 0 : _a.length) > 0 ? parseInt(group.Count, 10) : 0 + }; + }); + } else if (Group instanceof Object) { + groups = [ + { + value: Group.Value, + count: + ((_a = Group.Count) === null || _a === void 0 ? void 0 : _a.length) > 0 ? parseInt(Group.Count, 10) : 0 + } + ]; + } + } + return { + field: item.Field, + operation: item.Operation, + value: item.Value ? parseFloat(item.Value) : 0, + groups + }; + }; + if (aggRes.Aggregation instanceof Array) { + aggList = aggRes.Aggregation.map(getAggregationObject); + } else { + aggList = [getAggregationObject(aggRes.Aggregation)]; + } + } + return { + res: result.res, + status: result.status, + nextToken: NextToken, + files, + aggregations: aggList + }; +} +exports.doMetaQuery = doMetaQuery; +async function closeMetaQuery(bucketName, options = {}) { + checkBucketName_1.checkBucketName(bucketName); + const params = this._bucketRequestParams('POST', bucketName, { metaQuery: '', comp: 'delete' }, options); + params.successStatuses = [200]; + const result = await this.request(params); + return { + res: result.res, + status: result.status + }; +} +exports.closeMetaQuery = closeMetaQuery; diff --git a/lib/common/bucket/dataIndex.ts b/lib/common/bucket/dataIndex.ts new file mode 100644 index 00000000..b974bda1 --- /dev/null +++ b/lib/common/bucket/dataIndex.ts @@ -0,0 +1,222 @@ +// https://help.aliyun.com/zh/oss/developer-reference/data-indexing +// https://www.alibabacloud.com/help/en/oss/developer-reference/dometaquery +import { checkBucketName } from '../utils/checkBucketName'; +import { obj2xml } from '../utils/obj2xml'; +import { formatObjKey } from '../utils/formatObjKey'; + +export async function openMetaQuery(this: any, bucketName: string, options = {}) { + checkBucketName(bucketName); + const params = this._bucketRequestParams('POST', bucketName, { metaQuery: '', comp: 'add' }, options); + params.successStatuses = [200]; + const result = await this.request(params); + + return { + res: result.res, + status: result.status + }; +} + +export async function getMetaQueryStatus(this: any, bucketName: string, options = {}) { + checkBucketName(bucketName); + const params = this._bucketRequestParams('GET', bucketName, 'metaQuery', options); + params.successStatuses = [200]; + const result = await this.request(params); + + const data = await this.parseXML(result.data); + return { + res: result.res, + status: result.status, + phase: data.Phase, + state: data.State, + createTime: data.CreateTime, + updateTime: data.UpdateTime + }; +} + +interface IQuery { + // the fields. + field?: string; + // the value of the field. + value?: string; + /* the operators. Valid values: eq (equal to), gt (greater than), gte (greater than or equal to), + lt (less than), lte (less than or equal to), match (fuzzy query), prefix (prefix query), + and (AND), or (OR), and not (NOT). */ + operation: string; + /* the subquery conditions. You must set subquery conditions only when Operation is set to AND, OR, or NOT. */ + subQueries?: IQuery[]; +} + +enum EOperation { + min = 'min', + max = 'max', + average = 'average', + sum = 'sum', + count = 'count', + distinct = 'distinct', + group = 'group' +} + +interface IAggregation { + // The name of the field. + field: string; + // The operator for aggregate operations. Valid values:min,max,average,sum,count,distinct,group + operation: EOperation; +} + +interface IMetaQuery { + /* The token that is used for the next query when the total number of objects exceeds the value of MaxResults. */ + nextToken?: string; + // The maximum number of objects to return. Valid values: 0 to 100. + maxResults?: number; + // The query condition. + query: IQuery; + // The field based on which the results are sorted. + sort?: string; + // The order in which you want to sort the queried data. + order?: 'asc' | 'desc'; + // The information about aggregate operations. + aggregations?: IAggregation[]; +} + +export async function doMetaQuery(this: any, bucketName: string, queryParam: IMetaQuery, options = {}) { + checkBucketName(bucketName); + const params = this._bucketRequestParams('POST', bucketName, { metaQuery: '', comp: 'query' }, options); + params.successStatuses = [200]; + + const { aggregations } = queryParam; + let aggregationsParam; + if (aggregations && aggregations.length > 0) { + aggregationsParam = { + Aggregation: aggregations.map(item => ({ Field: item.field, Operation: item.operation })) + }; + } + + const paramXMLObj = { + MetaQuery: { + NextToken: queryParam.nextToken, + MaxResults: queryParam.maxResults, + Query: JSON.stringify(formatObjKey(queryParam.query, 'firstUpperCase')), + Sort: queryParam.sort, + Order: queryParam.order, + Aggregations: aggregationsParam + } + }; + params.mime = 'xml'; + params.content = obj2xml(paramXMLObj, { headers: true, firstUpperCase: true }); + + const result = await this.request(params); + + const { NextToken, Files, Aggregations: aggRes } = await this.parseXML(result.data); + + let files: any[] = []; + if (Files && Files.File) { + const getFileObject = item => { + let ossTagging: any[] = []; + const { OSSTagging } = item; + if (OSSTagging && OSSTagging.Tagging) { + const { Tagging } = OSSTagging; + if (Tagging instanceof Array) { + ossTagging = Tagging.map(tagging => ({ + key: tagging.Key, + value: tagging.Value + })); + } else if (Tagging instanceof Object) { + ossTagging = [{ key: Tagging.Key, vlaue: Tagging.Value }]; + } + } + + let ossUserMeta: any[] = []; + const { OSSUserMeta } = item; + if (OSSUserMeta && OSSUserMeta.UserMeta) { + const { UserMeta } = OSSUserMeta; + if (UserMeta instanceof Array) { + ossUserMeta = UserMeta.map(meta => ({ + key: meta.Key, + value: meta.Value + })); + } else if (UserMeta instanceof Object) { + ossUserMeta = [{ key: UserMeta.Key, vlaue: UserMeta.Value }]; + } + } + let ossTaggingCount = 0; + if (item.OSSTaggingCount && item.OSSTaggingCount.length > 0) ossTaggingCount = parseInt(item.OSSTaggingCount, 10); + + return { + fileName: item.Filename, + size: parseInt(item.Size, 10), + fileModifiedTime: item.FileModifiedTime, + ossObjectType: item.OSSObjectType, + ossStorageClass: item.OSSStorageClass, + objectACL: item.ObjectACL, + eTag: item.ETag, + ossTaggingCount, + ossTagging, + ossUserMeta, + ossCRC64: item.OSSCRC64, + serverSideEncryption: item.ServerSideEncryption, + serverSideEncryptionCustomerAlgorithm: item.ServerSideEncryptionCustomerAlgorithm + }; + }; + if (Files.File instanceof Array) { + files = Files.File.map(getFileObject); + } else { + files = [getFileObject(Files.File)]; + } + } + + let aggList: any[] = []; + if (aggRes) { + const getAggregationObject = item => { + let groups: any[] = []; + const { Groups } = item; + if (Groups && Groups.Group) { + const { Group } = Groups; + if (Group instanceof Array) { + groups = Group.map(group => ({ + value: group.Value, + count: group.Count?.length > 0 ? parseInt(group.Count, 10) : 0 + })); + } else if (Group instanceof Object) { + groups = [ + { + value: Group.Value, + count: Group.Count?.length > 0 ? parseInt(Group.Count, 10) : 0 + } + ]; + } + } + + return { + field: item.Field, + operation: item.Operation, + value: item.Value ? parseFloat(item.Value) : 0, + groups + }; + }; + if (aggRes.Aggregation instanceof Array) { + aggList = aggRes.Aggregation.map(getAggregationObject); + } else { + aggList = [getAggregationObject(aggRes.Aggregation)]; + } + } + + return { + res: result.res, + status: result.status, + nextToken: NextToken, + files, + aggregations: aggList + }; +} + +export async function closeMetaQuery(this: any, bucketName: string, options = {}) { + checkBucketName(bucketName); + const params = this._bucketRequestParams('POST', bucketName, { metaQuery: '', comp: 'delete' }, options); + params.successStatuses = [200]; + + const result = await this.request(params); + return { + res: result.res, + status: result.status + }; +} diff --git a/lib/common/bucket/index.js b/lib/common/bucket/index.js index 3e394e07..296000f6 100644 --- a/lib/common/bucket/index.js +++ b/lib/common/bucket/index.js @@ -32,3 +32,4 @@ merge(proto, require('./extendBucketWorm')); merge(proto, require('./getBucketWorm')); merge(proto, require('./initiateBucketWorm')); merge(proto, require('./getBucketStat')); +merge(proto, require('./dataIndex')); diff --git a/package-lock.json b/package-lock.json index 74286c68..719194a1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -6990,8 +6990,9 @@ }, "node_modules/eslint-config-prettier": { "version": "8.10.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.0.tgz", + "integrity": "sha512-SM8AMJdeQqRYT9O9zguiruQZaN7+z+E4eAP9oiLNGKMtomwaB1E9dcgUD6ZAn/eQAb52USbvezbiljfZUhbJcg==", "dev": true, - "license": "MIT", "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -7121,8 +7122,9 @@ }, "node_modules/eslint-plugin-prettier": { "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz", + "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==", "dev": true, - "license": "MIT", "dependencies": { "prettier-linter-helpers": "^1.0.0" }, @@ -7564,8 +7566,9 @@ }, "node_modules/fast-diff": { "version": "1.3.0", - "dev": true, - "license": "Apache-2.0" + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true }, "node_modules/fast-glob": { "version": "3.3.2", @@ -15238,8 +15241,9 @@ }, "node_modules/prettier-linter-helpers": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", "dev": true, - "license": "MIT", "dependencies": { "fast-diff": "^1.1.2" }, diff --git a/test/browser/browser.test.js b/test/browser/browser.test.js index 6658198e..4b566bcd 100644 --- a/test/browser/browser.test.js +++ b/test/browser/browser.test.js @@ -38,8 +38,8 @@ const cleanBucket = async store => { const uploads = result.uploads || []; await Promise.all(uploads.map(_ => store.abortMultipartUpload(_.name, _.uploadId))); }; + describe('browser', () => { - /* eslint require-yield: [0] */ before(() => { ossConfig = { region: stsConfig.region, @@ -48,14 +48,8 @@ describe('browser', () => { stsToken: stsConfig.Credentials.SecurityToken, bucket: stsConfig.bucket }; - // this.store = oss({ - // region: stsConfig.region, - // accessKeyId: creds.AccessKeyId, - // accessKeySecret: creds.AccessKeySecret, - // stsToken: creds.SecurityToken, - // bucket: stsConfig.bucket - // }); }); + after(async () => { const store = oss(ossConfig); await cleanBucket(store); @@ -1817,9 +1811,7 @@ describe('browser', () => { // TODO fix callback server // it('should multipart upload file with callback server', async () => { - // const fileContent = Array(1024 * 1024) - // .fill('a') - // .join(''); + // const fileContent = Array(1024 * 1024).fill('a').join(''); // const file = new File([fileContent], 'multipart-callback-server'); // const name = `${prefix}multipart/callback-server`; // const result = await store.multipartUpload(name, file, { @@ -1829,7 +1821,6 @@ describe('browser', () => { // host: 'oss-cn-hangzhou.aliyuncs.com', // body: 'bucket=${bucket}&object=${object}&var1=${x:var1}', // contentType: 'application/x-www-form-urlencoded', - // callbackSNI: true, // customValue: { // var1: 'value1', // var2: 'value2' @@ -2735,4 +2726,112 @@ describe('browser', () => { }); }); }); + + describe('test bucket data indexing', () => { + let store; + const { bucket } = stsConfig; + const sleepTime = 5000; // Opening and closing require delayed effectiveness + before(async () => { + store = oss({ ...ossConfig, refreshSTSTokenInterval: 1000 }); + await store.put('test-doMetaQuery--1', Buffer.from('test-doMetaQuery')); + await store.put('test-doMetaQuery--2', Buffer.from('test-doMetaQuery')); + await store.put('test-doMetaQuery--3', Buffer.from('test-doMetaQuery')); + await store.put('test-doMetaQuery--4', Buffer.from('test-doMetaQuery')); + }); + + it('open meta query of bucket', async () => { + const result = await store.openMetaQuery(bucket); + assert.strictEqual(result.status, 200); + await sleep(sleepTime); + }); + + it('getMetaQueryStatus()', async () => { + const { status, phase, state, createTime, updateTime } = await store.getMetaQueryStatus(bucket); + assert.strictEqual(status, 200); + assert(['FullScanning', 'IncrementalScanning', ''].includes(phase)); + assert(['Ready', 'Stop', 'Running', 'Retrying', 'Failed', 'Deleted'].includes(state)); + assert(!!createTime); + assert(!!updateTime); + }); + + it('doMetaQuery()', async () => { + const maxResults = 2; + const queryParam = { + maxResults, + query: { + operation: 'and', + subQueries: [ + { field: 'Filename', value: 'test-doMetaQuery', operation: 'match' }, + { field: 'Size', value: '1048576', operation: 'lt' } + ] + } + }; + + const { status, files, nextToken } = await store.doMetaQuery(bucket, queryParam); + assert.strictEqual(status, 200); + if (nextToken) { + assert.strictEqual(files.length, maxResults); + + const result = await store.doMetaQuery(bucket, { ...queryParam, nextToken, maxResults: 1 }); + assert.strictEqual(result.status, 200); + assert(result.files.length > 0); + assert(result.files[0].fileName.length > 0); + } + }); + + it('doMetaQuery() one Aggregations', async () => { + const queryParam = { + maxResults: 2, + query: { + operation: 'and', + subQueries: [ + { field: 'Filename', value: '_do', operation: 'match' }, + { field: 'Size', value: '1048576', operation: 'lt' } + ] + }, + aggregations: [{ field: 'Size', operation: 'sum' }] + }; + + const result = await store.doMetaQuery(bucket, queryParam); + assert.strictEqual(result.status, 200); + assert(result.aggregations.length > 0); + assert(result.aggregations[0].field, 'Size'); + }); + + it('doMetaQuery() two Aggregations', async () => { + const queryParam = { + maxResults: 2, + query: { + operation: 'and', + subQueries: [ + { field: 'Filename', value: 'test-', operation: 'match' }, + { field: 'Size', value: '1048576', operation: 'lt' } + ] + }, + aggregations: [ + { field: 'ETag', operation: 'count' }, + { field: 'FileModifiedTime', operation: 'distinct' }, + { field: 'Filename', operation: 'group' }, + { field: 'ObjectACL', operation: 'group' }, + { field: 'OSSCRC64', operation: 'distinct' }, + { field: 'OSSStorageClass', operation: 'group' }, + { field: 'OSSTaggingCount', operation: 'max' }, + { field: 'ServerSideEncryption', operation: 'group' }, + { field: 'Size', operation: 'sum' } + ] + }; + + const result = await store.doMetaQuery(bucket, queryParam); + assert.strictEqual(result.status, 200); + assert(result.aggregations.length > 0); + assert.strictEqual(result.aggregations[0].field, 'ETag'); + assert.strictEqual(result.aggregations[1].field, 'FileModifiedTime'); + }); + + it('closeMetaQuery()', async () => { + const result = await store.closeMetaQuery(bucket); + assert.strictEqual(result.status, 200); + await sleep(sleepTime * 2); + }); + }); }); diff --git a/test/node/bucket.test.js b/test/node/bucket.test.js index 83d1c428..e2b8518a 100644 --- a/test/node/bucket.test.js +++ b/test/node/bucket.test.js @@ -13,19 +13,19 @@ describe('test/bucket.test.js', () => { const { prefix, includesConf } = utils; let store; let bucket; - const bucketRegion = 'oss-ap-southeast-1'; // oss-ap-southeast-1 suport PutBucketLifecycle DeepColdArchive + const bucketRegion = config.region; const { accountId } = config; [ { authorizationV4: false - }, - { - authorizationV4: true } + // { + // authorizationV4: true + // } ].forEach((moreConfigs, idx) => { describe(`test bucket in iterate ${idx}`, () => { before(async () => { - store = oss({ ...config, ...moreConfigs, region: bucketRegion }); + store = oss({ ...config, ...moreConfigs }); bucket = `ali-oss-test-bucket-${prefix.replace(/[/.]/g, '-')}${idx}`; const result = await store.putBucket(bucket, { timeout }); @@ -928,15 +928,20 @@ describe('test/bucket.test.js', () => { } ]); assert.equal(putresult3.res.status, 200); - // Regions that need to support DeepColdArchive - const putresult4 = await store.putBucketLifecycle(bucket, [ + + const region = 'oss-ap-southeast-1'; // oss-ap-southeast-1 suport putBucketLifecycle DeepColdArchive + const client = oss({ ...config, ...moreConfigs, region }); + const bucketName = `ali-oss-test-bucket-deep-${prefix.replace(/[/.]/g, '-')}${idx}`; + const result4 = await client.putBucket(bucketName, { timeout }); + assert.equal(result4.res.status, 200); + const putresult4 = await client.putBucketLifecycle(bucketName, [ { id: 'transition4', prefix: 'logs/', status: 'Enabled', transition: { days: 20, - storageClass: 'DeepColdArchive' + storageClass: 'DeepColdArchive' // Regions that need to support DeepColdArchive }, tag: { key: 'test4', @@ -1393,6 +1398,7 @@ describe('test/bucket.test.js', () => { } }); }); + describe('inventory()', () => { const field = [ 'Size', @@ -1420,7 +1426,7 @@ describe('test/bucket.test.js', () => { frequency: 'Daily', includedObjectVersions: 'All', optionalFields: { - field + field: ['Size', 'LastModifiedDate'] } }; @@ -1583,6 +1589,104 @@ describe('test/bucket.test.js', () => { }); }); }); + + describe('openMetaQuery() openMetaQuery() doMetaQuery() closeMetaQuery()', () => { + const sleepTime = 5000; // Opening and closing require delayed effectiveness + + it('open meta query of bucket', async () => { + const result = await store.openMetaQuery(bucket); // bucket need support meta query + assert.strictEqual(result.status, 200); + await utils.sleep(sleepTime); + }); + + it('getMetaQueryStatus()', async () => { + const { status, phase, state, createTime, updateTime } = await store.getMetaQueryStatus(bucket); + assert.strictEqual(status, 200); + assert(['FullScanning', 'IncrementalScanning', ''].includes(phase)); + assert(['Ready', 'Stop', 'Running', 'Retrying', 'Failed', 'Deleted'].includes(state)); + assert(!!createTime); + assert(!!updateTime); + }); + + it('doMetaQuery()', async () => { + const maxResults = 2; + const queryParam = { + maxResults, + query: { + operation: 'and', + subQueries: [ + { field: 'Filename', value: 'test-doMetaQuery', operation: 'match' }, + { field: 'Size', value: '1048576', operation: 'lt' } + ] + } + }; + const { status, files, nextToken } = await store.doMetaQuery(bucket, queryParam); + assert.strictEqual(status, 200); + if (nextToken) { + assert.strictEqual(files.length, maxResults); + + const result = await store.doMetaQuery(bucket, { ...queryParam, nextToken, maxResults: 1 }); + assert.strictEqual(result.status, 200); + assert(result.files.length > 0); + assert(result.files[0].fileName.length > 0); + } + }); + + it('doMetaQuery() one Aggregations', async () => { + const queryParam = { + maxResults: 2, + query: { + operation: 'and', + subQueries: [ + { field: 'Filename', value: '_do', operation: 'match' }, + { field: 'Size', value: '1048576', operation: 'lt' } + ] + }, + aggregations: [{ field: 'Size', operation: 'sum' }] + }; + + const result = await store.doMetaQuery(bucket, queryParam); + assert.strictEqual(result.status, 200); + assert(result.aggregations.length > 0); + assert.strictEqual(result.aggregations[0].field, 'Size'); + }); + + it('doMetaQuery() two Aggregations', async () => { + const queryParam = { + maxResults: 2, + query: { + operation: 'and', + subQueries: [ + { field: 'Filename', value: 'test-', operation: 'match' }, + { field: 'Size', value: '1048576', operation: 'lt' } + ] + }, + aggregations: [ + { field: 'ETag', operation: 'count' }, + { field: 'FileModifiedTime', operation: 'distinct' }, + { field: 'Filename', operation: 'group' }, + { field: 'ObjectACL', operation: 'group' }, + { field: 'OSSCRC64', operation: 'distinct' }, + { field: 'OSSStorageClass', operation: 'group' }, + { field: 'OSSTaggingCount', operation: 'max' }, + { field: 'ServerSideEncryption', operation: 'group' }, + { field: 'Size', operation: 'sum' } + ] + }; + + const result = await store.doMetaQuery(bucket, queryParam); + assert.strictEqual(result.status, 200); + assert(result.aggregations.length > 0); + assert.strictEqual(result.aggregations[0].field, 'ETag'); + assert.strictEqual(result.aggregations[1].field, 'FileModifiedTime'); + }); + + it('closeMetaQuery()', async () => { + const result = await store.closeMetaQuery(bucket); + assert.strictEqual(result.status, 200); + await utils.sleep(sleepTime * 2); + }); + }); }); }); }); diff --git a/test/node/multipart.test.js b/test/node/multipart.test.js index a4d18adf..5d3f4faf 100644 --- a/test/node/multipart.test.js +++ b/test/node/multipart.test.js @@ -985,6 +985,7 @@ describe('test/multipart.test.js', () => { console.log('info >', error.message); }); await Promise.all([p3, p4]); + await utils.sleep(2000); assert.strictEqual(store.multipartUploadStreams.length, 0); });