Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor putObjectRetention #1219

Merged
merged 10 commits into from
Nov 6, 2023
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 11 additions & 18 deletions docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -1420,18 +1420,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 @@ -1445,17 +1444,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)
})
57 changes: 56 additions & 1 deletion src/internal/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import xml2js from 'xml2js'

import { CredentialProvider } from '../CredentialProvider.ts'
import * as errors from '../errors.ts'
import { DEFAULT_REGION, LEGAL_HOLD_STATUS } from '../helpers.ts'
import { DEFAULT_REGION, LEGAL_HOLD_STATUS, RETENTION_MODES } from '../helpers.ts'
import { signV4 } from '../signing.ts'
import { Extensions } from './extensions.ts'
import {
Expand Down Expand Up @@ -52,6 +52,7 @@ import type {
RequestHeaders,
ResponseHeader,
ResultCallback,
Retention,
StatObjectOpts,
Tag,
Transport,
Expand Down Expand Up @@ -1166,4 +1167,58 @@ export class TypedClient {
const body = await readAsString(response)
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, any> = {}
trim21 marked this conversation as resolved.
Show resolved Hide resolved

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])
}
}
9 changes: 9 additions & 0 deletions src/internal/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -208,3 +208,12 @@ 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 EmptyObject = Record<string, never>
export type IsoDate = string
28 changes: 8 additions & 20 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,
LegalHoldStatus,
Expand All @@ -40,6 +42,8 @@ import type {
ReplicationRuleFilter,
ReplicationRuleStatus,
ResultCallback,
Retention,
RetentionOptions,
SourceSelectionCriteria,
Tag,
VersionIdentificator,
Expand All @@ -56,9 +60,11 @@ export type {
BucketItemWithMetadata,
BucketStream,
ClientOptions,
EmptyObject,
ExistingObjectReplication,
GetObjectLegalHoldOptions,
IncompleteUploadedBucketItem,
IsoDate,
ItemBucketMetadata,
ItemBucketMetadataList,
LegalHoldStatus,
Expand All @@ -74,6 +80,8 @@ export type {
ReplicationRuleDestination,
ReplicationRuleFilter,
ReplicationRuleStatus,
Retention,
RetentionOptions,
SourceSelectionCriteria,
Tag,
VersionIdentificator,
Expand Down Expand Up @@ -111,13 +119,9 @@ export type LockUnit = RETENTION_VALIDITY_UNITS

export type VersioningConfig = Record<string | number | symbol, unknown>
export type TagList = Record<string, string>
export type EmptyObject = Record<string, never>
export type Lifecycle = LifecycleConfig | null | ''
export type Lock = LockConfig | EmptyObject
export type Encryption = EncryptionConfig | EmptyObject
export type Retention = RetentionOptions | EmptyObject
export type IsoDate = string

export interface PostPolicyResult {
postURL: string
formData: {
Expand Down Expand Up @@ -152,13 +156,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 @@ -375,15 +372,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
59 changes: 1 addition & 58 deletions src/minio.js
Original file line number Diff line number Diff line change
Expand Up @@ -1984,63 +1984,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 @@ -2498,7 +2441,6 @@ Client.prototype.getBucketLifecycle = promisify(Client.prototype.getBucketLifecy
Client.prototype.removeBucketLifecycle = promisify(Client.prototype.removeBucketLifecycle)
Client.prototype.setObjectLockConfig = promisify(Client.prototype.setObjectLockConfig)
Client.prototype.getObjectLockConfig = promisify(Client.prototype.getObjectLockConfig)
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 @@ -2518,3 +2460,4 @@ 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)
Loading