diff --git a/docs/API.md b/docs/API.md index 4dfc5cb4..ad428e49 100644 --- a/docs/API.md +++ b/docs/API.md @@ -1403,18 +1403,17 @@ minioClient.removeIncompleteUpload('mybucket', 'photo.jpg', function (err) { -### putObjectRetention(bucketName, objectName [, retentionOpts] [, callback]) +### async putObjectRetention(bucketName, objectName [, retentionOpts]) Apply retention on an object. **Parameters** -| Param | Type | Description | -| --------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `bucketName` | _string_ | Name of the bucket. | -| `objectName` | _string_ | Name of the object. | -| `retentionOpts` | _object_ | Options for retention like : `{ governanceBypass:true/false ,mode:COMPLIANCE/GOVERNANCE, retainUntilDate: _date_ , versionId:"my-versionId" }` Default is `{}` (Optional) | -| `callback(err)` | _function_ | Callback function is called with non `null` value in case of error. If no callback is passed, a `Promise` is returned. | +| Param | Type | Description | +| --------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `bucketName` | _string_ | Name of the bucket. | +| `objectName` | _string_ | Name of the object. | +| `retentionOpts` | _object_ | Options for retention like : `{ governanceBypass:true/false ,mode:COMPLIANCE/GOVERNANCE, retainUntilDate: _date_ , versionId:"my-versionId" }` Default is `{}` (Optional) | **Example** Apply object retention on an object @@ -1428,17 +1427,11 @@ expirationDate.setDate(expirationDate.getDate() + 1) expirationDate.setUTCHours(0, 0, 0, 0) //Should be start of the day.(midnight) const versionId = 'e67b4b08-144d-4fc4-ba15-43c3f7f9ba74' -const objRetPromise = minioClient.putObjectRetention( - bucketName, - objectName, - { Mode: 'GOVERNANCE', retainUntilDate: retainUntilDate.toISOString(), versionId: versionId }, - function (err) { - if (err) { - return console.log(err) - } - console.log('Success') - }, -) +await minioClient.putObjectRetention(bucketName, objectName, { + Mode: 'GOVERNANCE', + retainUntilDate: retainUntilDate.toISOString(), + versionId: versionId, +}) ``` diff --git a/examples/put-object-retention.js b/examples/put-object-retention.js index 55f6d920..d57b958f 100644 --- a/examples/put-object-retention.js +++ b/examples/put-object-retention.js @@ -17,9 +17,9 @@ // Note: YOUR-ACCESSKEYID, YOUR-SECRETACCESSKEY and my-bucketname are // dummy values, please replace them with original values. -var Minio = require('minio') +import * as Minio from 'minio' -var s3Client = new Minio.Client({ +const s3Client = new Minio.Client({ endPoint: 's3.amazonaws.com', accessKey: 'YOUR-ACCESSKEYID', secretKey: 'YOUR-SECRETACCESSKEY', @@ -33,15 +33,8 @@ expirationDate.setDate(expirationDate.getDate() + 1) expirationDate.setUTCHours(0, 0, 0, 0) //Should be start of the day.(midnight) const versionId = 'my-versionId' -const objRetPromise = s3Client.putObjectRetention(bucketName, objectName, { +await s3Client.putObjectRetention(bucketName, objectName, { mode: 'GOVERNANCE', retainUntilDate: expirationDate.toISOString(), versionId: versionId, }) -objRetPromise - .then(() => { - console.log('Success') - }) - .catch((e) => { - console.log(' Error', e) - }) diff --git a/src/internal/client.ts b/src/internal/client.ts index cf6237d4..c0d6d1e8 100644 --- a/src/internal/client.ts +++ b/src/internal/client.ts @@ -54,6 +54,7 @@ import type { RequestHeaders, ResponseHeader, ResultCallback, + Retention, StatObjectOpts, Tag, Transport, @@ -1169,6 +1170,59 @@ export class TypedClient { return xmlParsers.parseTagging(body) } + async putObjectRetention(bucketName: string, objectName: string, retentionOpts: Retention = {}): Promise { + if (!isValidBucketName(bucketName)) { + throw new errors.InvalidBucketNameError(`Invalid bucket name: ${bucketName}`) + } + if (!isValidObjectName(objectName)) { + throw new errors.InvalidObjectNameError(`Invalid object name: ${objectName}`) + } + if (!isObject(retentionOpts)) { + throw new errors.InvalidArgumentError('retentionOpts should be of type "object"') + } else { + if (retentionOpts.governanceBypass && !isBoolean(retentionOpts.governanceBypass)) { + throw new errors.InvalidArgumentError(`Invalid value for governanceBypass: ${retentionOpts.governanceBypass}`) + } + if ( + retentionOpts.mode && + ![RETENTION_MODES.COMPLIANCE, RETENTION_MODES.GOVERNANCE].includes(retentionOpts.mode) + ) { + throw new errors.InvalidArgumentError(`Invalid object retention mode: ${retentionOpts.mode}`) + } + if (retentionOpts.retainUntilDate && !isString(retentionOpts.retainUntilDate)) { + throw new errors.InvalidArgumentError(`Invalid value for retainUntilDate: ${retentionOpts.retainUntilDate}`) + } + if (retentionOpts.versionId && !isString(retentionOpts.versionId)) { + throw new errors.InvalidArgumentError(`Invalid value for versionId: ${retentionOpts.versionId}`) + } + } + + const method = 'PUT' + let query = 'retention' + + const headers: RequestHeaders = {} + if (retentionOpts.governanceBypass) { + headers['X-Amz-Bypass-Governance-Retention'] = true + } + + const builder = new xml2js.Builder({ rootName: 'Retention', renderOpts: { pretty: false }, headless: true }) + const params: Record = {} + + if (retentionOpts.mode) { + params.Mode = retentionOpts.mode + } + if (retentionOpts.retainUntilDate) { + params.RetainUntilDate = retentionOpts.retainUntilDate + } + if (retentionOpts.versionId) { + query += `&versionId=${retentionOpts.versionId}` + } + + const payload = builder.buildObject(params) + + headers['Content-MD5'] = toMd5(payload) + await this.makeRequestAsyncOmit({ method, bucketName, objectName, query, headers }, payload, [200, 204]) + } getObjectLockConfig(bucketName: string, callback: ResultCallback): void getObjectLockConfig(bucketName: string): void async getObjectLockConfig(bucketName: string): Promise diff --git a/src/internal/type.ts b/src/internal/type.ts index 55b3cfb9..b7e28b29 100644 --- a/src/internal/type.ts +++ b/src/internal/type.ts @@ -209,7 +209,14 @@ export type PutObjectLegalHoldOptions = { versionId?: string status: LEGAL_HOLD_STATUS } - +export interface RetentionOptions { + versionId: string + mode?: RETENTION_MODES + retainUntilDate?: IsoDate + governanceBypass?: boolean +} +export type Retention = RetentionOptions | EmptyObject +export type IsoDate = string export type EmptyObject = Record export type ObjectLockInfo = diff --git a/src/minio.d.ts b/src/minio.d.ts index 6d5c4ab7..8d715c79 100644 --- a/src/minio.d.ts +++ b/src/minio.d.ts @@ -23,9 +23,11 @@ import type { BucketItemStat, BucketItemWithMetadata, BucketStream, + EmptyObject, ExistingObjectReplication, GetObjectLegalHoldOptions, IncompleteUploadedBucketItem, + IsoDate, ItemBucketMetadata, ItemBucketMetadataList, MetadataItem, @@ -40,8 +42,11 @@ import type { ReplicationRuleFilter, ReplicationRuleStatus, ResultCallback, + Retention, + RetentionOptions, SourceSelectionCriteria, Tag, + VersionIdentificator, } from './internal/type.ts' export * from './helpers.ts' @@ -55,9 +60,11 @@ export type { BucketItemWithMetadata, BucketStream, ClientOptions, + EmptyObject, ExistingObjectReplication, GetObjectLegalHoldOptions, IncompleteUploadedBucketItem, + IsoDate, ItemBucketMetadata, ItemBucketMetadataList, MetadataItem, @@ -73,6 +80,8 @@ export type { ReplicationRuleDestination, ReplicationRuleFilter, ReplicationRuleStatus, + Retention, + RetentionOptions, SourceSelectionCriteria, Tag, } @@ -113,13 +122,8 @@ export type LockUnit = RETENTION_VALIDITY_UNITS export type LegalHoldStatus = LEGAL_HOLD_STATUS export type VersioningConfig = Record export type TagList = Record -export type EmptyObject = Record -export type VersionIdentificator = Pick export type Lifecycle = LifecycleConfig | null | '' export type Encryption = EncryptionConfig | EmptyObject -export type Retention = RetentionOptions | EmptyObject -export type IsoDate = string - export interface PostPolicyResult { postURL: string formData: { @@ -154,13 +158,6 @@ export interface EncryptionRule { [key: string]: any } -export interface RetentionOptions { - versionId: string - mode?: RETENTION_MODES - retainUntilDate?: IsoDate - governanceBypass?: boolean -} - export interface LegalHoldOptions { versionId: string status: LEGAL_HOLD_STATUS @@ -370,15 +367,6 @@ export class Client extends TypedClient { removeIncompleteUpload(bucketName: string, objectName: string, callback: NoResultCallback): void removeIncompleteUpload(bucketName: string, objectName: string): Promise - putObjectRetention(bucketName: string, objectName: string, callback: NoResultCallback): void - putObjectRetention( - bucketName: string, - objectName: string, - retentionOptions: Retention, - callback: NoResultCallback, - ): void - putObjectRetention(bucketName: string, objectName: string, retentionOptions?: Retention): Promise - getObjectRetention( bucketName: string, objectName: string, diff --git a/src/minio.js b/src/minio.js index 98d2651c..f9587dbb 100644 --- a/src/minio.js +++ b/src/minio.js @@ -60,7 +60,6 @@ import { uriResourceEscape, } from './internal/helper.ts' import { PostPolicy } from './internal/post-policy.ts' -import { RETENTION_MODES } from './internal/type.ts' import { NotificationConfig, NotificationPoller } from './notification.js' import { ObjectUploader } from './object-uploader.js' import { promisify } from './promisify.js' @@ -1898,63 +1897,6 @@ export class Client extends TypedClient { }) } - putObjectRetention(bucketName, objectName, retentionOpts = {}, cb) { - if (!isValidBucketName(bucketName)) { - throw new errors.InvalidBucketNameError('Invalid bucket name: ' + bucketName) - } - if (!isValidObjectName(objectName)) { - throw new errors.InvalidObjectNameError(`Invalid object name: ${objectName}`) - } - if (!isObject(retentionOpts)) { - throw new errors.InvalidArgumentError('retentionOpts should be of type "object"') - } else { - if (retentionOpts.governanceBypass && !isBoolean(retentionOpts.governanceBypass)) { - throw new errors.InvalidArgumentError('Invalid value for governanceBypass', retentionOpts.governanceBypass) - } - if ( - retentionOpts.mode && - ![RETENTION_MODES.COMPLIANCE, RETENTION_MODES.GOVERNANCE].includes(retentionOpts.mode) - ) { - throw new errors.InvalidArgumentError('Invalid object retention mode ', retentionOpts.mode) - } - if (retentionOpts.retainUntilDate && !isString(retentionOpts.retainUntilDate)) { - throw new errors.InvalidArgumentError('Invalid value for retainUntilDate', retentionOpts.retainUntilDate) - } - if (retentionOpts.versionId && !isString(retentionOpts.versionId)) { - throw new errors.InvalidArgumentError('Invalid value for versionId', retentionOpts.versionId) - } - } - if (!isFunction(cb)) { - throw new TypeError('callback should be of type "function"') - } - - const method = 'PUT' - let query = 'retention' - - const headers = {} - if (retentionOpts.governanceBypass) { - headers['X-Amz-Bypass-Governance-Retention'] = true - } - - const builder = new xml2js.Builder({ rootName: 'Retention', renderOpts: { pretty: false }, headless: true }) - const params = {} - - if (retentionOpts.mode) { - params.Mode = retentionOpts.mode - } - if (retentionOpts.retainUntilDate) { - params.RetainUntilDate = retentionOpts.retainUntilDate - } - if (retentionOpts.versionId) { - query += `&versionId=${retentionOpts.versionId}` - } - - let payload = builder.buildObject(params) - - headers['Content-MD5'] = toMd5(payload) - this.makeRequest({ method, bucketName, objectName, query, headers }, payload, [200, 204], '', false, cb) - } - getObjectRetention(bucketName, objectName, getOpts, cb) { if (!isValidBucketName(bucketName)) { throw new errors.InvalidBucketNameError('Invalid bucket name: ' + bucketName) @@ -2410,7 +2352,6 @@ Client.prototype.removeObjectTagging = promisify(Client.prototype.removeObjectTa Client.prototype.setBucketLifecycle = promisify(Client.prototype.setBucketLifecycle) Client.prototype.getBucketLifecycle = promisify(Client.prototype.getBucketLifecycle) Client.prototype.removeBucketLifecycle = promisify(Client.prototype.removeBucketLifecycle) -Client.prototype.putObjectRetention = promisify(Client.prototype.putObjectRetention) Client.prototype.getObjectRetention = promisify(Client.prototype.getObjectRetention) Client.prototype.setBucketEncryption = promisify(Client.prototype.setBucketEncryption) Client.prototype.getBucketEncryption = promisify(Client.prototype.getBucketEncryption) @@ -2430,5 +2371,6 @@ Client.prototype.getObjectLegalHold = callbackify(Client.prototype.getObjectLega Client.prototype.setObjectLegalHold = callbackify(Client.prototype.setObjectLegalHold) Client.prototype.getBucketTagging = callbackify(Client.prototype.getBucketTagging) Client.prototype.getObjectTagging = callbackify(Client.prototype.getObjectTagging) +Client.prototype.putObjectRetention = callbackify(Client.prototype.putObjectRetention) Client.prototype.setObjectLockConfig = callbackify(Client.prototype.setObjectLockConfig) Client.prototype.getObjectLockConfig = callbackify(Client.prototype.getObjectLockConfig) diff --git a/tests/unit/test.js b/tests/unit/test.js index 14135d9b..5b876ce1 100644 --- a/tests/unit/test.js +++ b/tests/unit/test.js @@ -1187,61 +1187,77 @@ describe('Client', function () { describe('putObjectRetention(bucket, objectName, retentionConfig, callback)', () => { it('should fail on null bucket', (done) => { - try { - client.putObjectRetention(null, '', {}, function () {}) - } catch (e) { - done() - } + client.putObjectRetention(null, '', {}, function (err) { + if (err) { + done() + } else { + done(new Error('expecting error')) + } + }) }) it('should fail on empty bucket', (done) => { - try { - client.putObjectRetention('', '', {}, function () {}) - } catch (e) { - done() - } + client.putObjectRetention('', '', {}, function (err) { + if (err) { + done() + } else { + done(new Error('expecting error')) + } + }) }) it('should fail on null object', (done) => { - try { - client.putObjectRetention('my-bucket', null, {}, function () {}) - } catch (e) { - done() - } + client.putObjectRetention('my-bucket', null, {}, function (err) { + if (err) { + done() + } else { + done(new Error('expecting error')) + } + }) }) it('should fail on empty object', (done) => { - try { - client.putObjectRetention('my-bucket', '', {}, function () {}) - } catch (e) { - done() - } + client.putObjectRetention('my-bucket', '', {}, function (err) { + if (err) { + done() + } else { + done(new Error('expecting error')) + } + }) }) it('should fail on passing invalid mode ', (done) => { - try { - client.putObjectRetention('my-bucket', 'my-object', { mode: 'invalid_mode' }, function () {}) - } catch (e) { - done() - } + client.putObjectRetention('my-bucket', 'my-object', { mode: 'invalid_mode' }, function (err) { + if (err) { + done() + } else { + done(new Error('expecting error')) + } + }) }) it('should fail on passing invalid governanceBypass ', (done) => { - try { - client.putObjectRetention('my-bucket', 'my-object', { governanceBypass: 'nonbool' }, function () {}) - } catch (e) { - done() - } + client.putObjectRetention('my-bucket', 'my-object', { governanceBypass: 'nonbool' }, function (err) { + if (err) { + done() + } else { + done(new Error('expecting error')) + } + }) }) it('should fail on passing invalid (null) retainUntilDate ', (done) => { - try { - client.putObjectRetention('my-bucket', 'my-object', { retainUntilDate: 12345 }, function () {}) - } catch (e) { - done() - } + client.putObjectRetention('my-bucket', 'my-object', { retainUntilDate: 12345 }, function (err) { + if (err) { + done() + } else { + done(new Error('expecting error')) + } + }) }) it('should fail on passing invalid versionId ', (done) => { - try { - client.putObjectRetention('my-bucket', { versionId: 'COMPLIANCE' }, function () {}) - } catch (e) { - done() - } + client.putObjectRetention('my-bucket', { versionId: 'COMPLIANCE' }, function (err) { + if (err) { + done() + } else { + done(new Error('expecting error')) + } + }) }) }) })