Skip to content

Commit

Permalink
refactor putObjectRetention (#1219)
Browse files Browse the repository at this point in the history
  • Loading branch information
trim21 authored Nov 6, 2023
1 parent 79425dd commit 16dcbbf
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 149 deletions.
29 changes: 11 additions & 18 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -1403,18 +1403,17 @@ minioClient.removeIncompleteUpload('mybucket', 'photo.jpg', function (err) {

<a name="putObjectRetention"></a>

### 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
Expand All @@ -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,
})
```

<a name="getObjectRetention"></a>
Expand Down
13 changes: 3 additions & 10 deletions examples/put-object-retention.js
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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)
})
54 changes: 54 additions & 0 deletions src/internal/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import type {
RequestHeaders,
ResponseHeader,
ResultCallback,
Retention,
StatObjectOpts,
Tag,
Transport,
Expand Down Expand Up @@ -1169,6 +1170,59 @@ export class TypedClient {
return xmlParsers.parseTagging(body)
}

async putObjectRetention(bucketName: string, objectName: string, retentionOpts: Retention = {}): Promise<void> {
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<string, string> = {}

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<ObjectLockInfo>): void
getObjectLockConfig(bucketName: string): void
async getObjectLockConfig(bucketName: string): Promise<ObjectLockInfo>
Expand Down
9 changes: 8 additions & 1 deletion src/internal/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, never>

export type ObjectLockInfo =
Expand Down
30 changes: 9 additions & 21 deletions src/minio.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ import type {
BucketItemStat,
BucketItemWithMetadata,
BucketStream,
EmptyObject,
ExistingObjectReplication,
GetObjectLegalHoldOptions,
IncompleteUploadedBucketItem,
IsoDate,
ItemBucketMetadata,
ItemBucketMetadataList,
MetadataItem,
Expand All @@ -40,8 +42,11 @@ import type {
ReplicationRuleFilter,
ReplicationRuleStatus,
ResultCallback,
Retention,
RetentionOptions,
SourceSelectionCriteria,
Tag,
VersionIdentificator,
} from './internal/type.ts'

export * from './helpers.ts'
Expand All @@ -55,9 +60,11 @@ export type {
BucketItemWithMetadata,
BucketStream,
ClientOptions,
EmptyObject,
ExistingObjectReplication,
GetObjectLegalHoldOptions,
IncompleteUploadedBucketItem,
IsoDate,
ItemBucketMetadata,
ItemBucketMetadataList,
MetadataItem,
Expand All @@ -73,6 +80,8 @@ export type {
ReplicationRuleDestination,
ReplicationRuleFilter,
ReplicationRuleStatus,
Retention,
RetentionOptions,
SourceSelectionCriteria,
Tag,
}
Expand Down Expand Up @@ -113,13 +122,8 @@ export type LockUnit = RETENTION_VALIDITY_UNITS
export type LegalHoldStatus = LEGAL_HOLD_STATUS
export type VersioningConfig = Record<string | number | symbol, unknown>
export type TagList = Record<string, string>
export type EmptyObject = Record<string, never>
export type VersionIdentificator = Pick<RetentionOptions, 'versionId'>
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: {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -370,15 +367,6 @@ export class Client extends TypedClient {
removeIncompleteUpload(bucketName: string, objectName: string, callback: NoResultCallback): void
removeIncompleteUpload(bucketName: string, objectName: string): Promise<void>

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<void>

getObjectRetention(
bucketName: string,
objectName: string,
Expand Down
60 changes: 1 addition & 59 deletions src/minio.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Loading

0 comments on commit 16dcbbf

Please sign in to comment.