diff --git a/README.md b/README.md index 0d56cb79..6c82c219 100644 --- a/README.md +++ b/README.md @@ -182,8 +182,8 @@ The full API Reference is available here. - [set-bucket-lifecycle.js](https://github.com/minio/minio-js/blob/master/examples/set-bucket-lifecycle.js) - [get-bucket-lifecycle.js](https://github.com/minio/minio-js/blob/master/examples/get-bucket-lifecycle.js) - [remove-bucket-lifecycle.js](https://github.com/minio/minio-js/blob/master/examples/remove-bucket-lifecycle.js) -- [get-object-lock-config.js](https://github.com/minio/minio-js/blob/master/examples/get-object-lock-config.js) -- [set-object-lock-config.js](https://github.com/minio/minio-js/blob/master/examples/set-object-lock-config.js) +- [get-object-lock-config.mjs](https://github.com/minio/minio-js/blob/master/examples/get-object-lock-config.mjs) +- [set-object-lock-config.mjs](https://github.com/minio/minio-js/blob/master/examples/set-object-lock-config.mjs) - [set-bucket-replication.mjs](https://github.com/minio/minio-js/blob/master/examples/set-bucket-replication.mjs) - [get-bucket-replication.mjs](https://github.com/minio/minio-js/blob/master/examples/get-bucket-replication.mjs) - [remove-bucket-replication.mjs](https://github.com/minio/minio-js/blob/master/examples/remove-bucket-replication.mjs) diff --git a/docs/API.md b/docs/API.md index 3a2b1654..faa64c60 100644 --- a/docs/API.md +++ b/docs/API.md @@ -765,33 +765,22 @@ Set Object lock config on a Bucket **Parameters** -| Param | Type | Description | -| --------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `bucketName` | _string_ | Name of the bucket. | -| `lockConfig` | _object_ | Lock Configuration can be either `{}` to reset or object with all of the following key/value pairs: `{mode: ["COMPLIANCE"/'GOVERNANCE'], unit: ["Days"/"Years"], validity: }` | -| `callback(err)` | _function_ | Callback is called with `err` in case of error. | +| Param | Type | Description | +| ------------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `bucketName` | _string_ | Name of the bucket. | +| `lockConfig` | _object_ | Lock Configuration can be either `{}` to reset or object with all of the following key/value pairs: `{mode: ["COMPLIANCE"/'GOVERNANCE'], unit: ["Days"/"Years"], validity: }` | **Example 1** ```js -s3Client.setObjectLockConfig('my-bucketname', { mode: 'COMPLIANCE', unit: 'Days', validity: 10 }, function (err) { - if (err) { - return console.log(err) - } - console.log('Success') -}) +await minioClient.setObjectLockConfig('my-bucketname', { mode: 'COMPLIANCE', unit: 'Days', validity: 10 }) ``` **Example 2** To reset/remove object lock config on a bucket. ```js -s3Client.setObjectLockConfig('my-bucketname', {}, function (err) { - if (err) { - return console.log(err) - } - console.log('Success') -}) +await s3Client.setObjectLockConfig('my-bucketname', {}) ``` @@ -802,21 +791,15 @@ Get Lock config on a Bucket **Parameters** -| Param | Type | Description | -| --------------------------- | ---------- | ----------------------------------------------------------------------------------------- | -| `bucketName` | _string_ | Name of the bucket. | -| `callback(err, lockConfig)` | _function_ | Callback is called with `err` in case of error. else it is called with lock configuration | +| Param | Type | Description | +| ------------ | -------- | ------------------- | +| `bucketName` | _string_ | Name of the bucket. | **Example ** Get object lock configuration on a Bucket ```js -s3Client.getObjectLockConfig('my-bucketname', function (err, lockConfig) { - if (err) { - return console.log(err) - } - console.log(lockConfig) -}) +await minioClient.getObjectLockConfig('my-bucketname') ``` diff --git a/examples/get-object-lock-config.js b/examples/get-object-lock-config.mjs similarity index 78% rename from examples/get-object-lock-config.js rename to examples/get-object-lock-config.mjs index 42e044b9..35055427 100644 --- a/examples/get-object-lock-config.js +++ b/examples/get-object-lock-config.mjs @@ -17,17 +17,17 @@ // 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', }) -s3Client.getObjectLockConfig('my-bucketname', function (err, lockConfig) { - if (err) { - return console.log(err) - } - console.log(lockConfig) -}) +try { + const lockConfig = await s3Client.getObjectLockConfig('my-bucketname') + console.log('Success', lockConfig) +} catch (err) { + console.log('Error ', err.message) +} diff --git a/examples/set-object-lock-config.js b/examples/set-object-lock-config.mjs similarity index 72% rename from examples/set-object-lock-config.js rename to examples/set-object-lock-config.mjs index 3b5faae3..55519d24 100644 --- a/examples/set-object-lock-config.js +++ b/examples/set-object-lock-config.mjs @@ -17,26 +17,27 @@ // 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', }) //Set Object lock on a bucket -s3Client.setObjectLockConfig('my-bucketname', { mode: 'COMPLIANCE', unit: 'Days', validity: 10 }, function (err) { - if (err) { - return console.log(err) - } +try { + await s3Client.setObjectLockConfig('my-bucketname', { mode: 'GOVERNANCE', unit: 'Days', validity: 20 }) console.log('Success') -}) +} catch (err) { + console.log('Error::', err.message) +} //To reset/remove object lock config. -s3Client.setObjectLockConfig('my-bucketname', {}, function (err) { - if (err) { - return console.log(err) - } +//Set Object lock on a bucket +try { + await s3Client.setObjectLockConfig('my-bucketname', {}) console.log('Success') -}) +} catch (err) { + console.log('Error::', err.message) +} diff --git a/src/internal/client.ts b/src/internal/client.ts index 4f0bc53f..9e06cbd9 100644 --- a/src/internal/client.ts +++ b/src/internal/client.ts @@ -9,7 +9,7 @@ import xml2js from 'xml2js' import { CredentialProvider } from '../CredentialProvider.ts' import * as errors from '../errors.ts' -import { DEFAULT_REGION } from '../helpers.ts' +import { DEFAULT_REGION, RETENTION_MODES, RETENTION_VALIDITY_UNITS } from '../helpers.ts' import { signV4 } from '../signing.ts' import { Extensions } from './extensions.ts' import { @@ -44,6 +44,8 @@ import type { BucketItemFromList, BucketItemStat, IRequest, + ObjectLockConfigParam, + ObjectLockInfo, ReplicationConfig, ReplicationConfigOpts, RequestHeaders, @@ -1031,4 +1033,90 @@ export class TypedClient { const xmlResult = await readAsString(httpRes) return xmlParsers.parseReplicationConfig(xmlResult) } + + getObjectLockConfig(bucketName: string, callback: ResultCallback): void + // @ts-ignore + async getObjectLockConfig(bucketName: string): Promise + async getObjectLockConfig(bucketName: string) { + if (!isValidBucketName(bucketName)) { + throw new errors.InvalidBucketNameError('Invalid bucket name: ' + bucketName) + } + const method = 'GET' + const query = 'object-lock' + + const httpRes = await this.makeRequestAsync({ method, bucketName, query }) + const xmlResult = await readAsString(httpRes) + return xmlParsers.parseObjectLockConfig(xmlResult) + } + + setObjectLockConfig( + bucketName: string, + lockConfigOpts: Omit, + callback: NoResultCallback, + ): void + // @ts-ignore + async setObjectLockConfig( + bucketName: string, + lockConfigOpts: Omit, + ): Promise + async setObjectLockConfig(bucketName: string, lockConfigOpts: Omit) { + const retentionModes = [RETENTION_MODES.COMPLIANCE, RETENTION_MODES.GOVERNANCE] + const validUnits = [RETENTION_VALIDITY_UNITS.DAYS, RETENTION_VALIDITY_UNITS.YEARS] + + if (!isValidBucketName(bucketName)) { + throw new errors.InvalidBucketNameError('Invalid bucket name: ' + bucketName) + } + + if (lockConfigOpts.mode && !retentionModes.includes(lockConfigOpts.mode)) { + throw new TypeError(`lockConfigOpts.mode should be one of ${retentionModes}`) + } + if (lockConfigOpts.unit && !validUnits.includes(lockConfigOpts.unit)) { + throw new TypeError(`lockConfigOpts.unit should be one of ${validUnits}`) + } + if (lockConfigOpts.validity && !isNumber(lockConfigOpts.validity)) { + throw new TypeError(`lockConfigOpts.validity should be a number`) + } + + const method = 'PUT' + const query = 'object-lock' + + const config: ObjectLockConfigParam = { + ObjectLockEnabled: 'Enabled', + } + const configKeys = Object.keys(lockConfigOpts) + + const isAllKeysSet = ['unit', 'mode', 'validity'].every((lck) => configKeys.includes(lck)) + // Check if keys are present and all keys are present. + if (configKeys.length > 0) { + if (!isAllKeysSet) { + throw new TypeError( + `lockConfigOpts.mode,lockConfigOpts.unit,lockConfigOpts.validity all the properties should be specified.`, + ) + } else { + config.Rule = { + DefaultRetention: {}, + } + if (lockConfigOpts.mode) { + config.Rule.DefaultRetention.Mode = lockConfigOpts.mode + } + if (lockConfigOpts.unit === RETENTION_VALIDITY_UNITS.DAYS) { + config.Rule.DefaultRetention.Days = lockConfigOpts.validity + } else if (lockConfigOpts.unit === RETENTION_VALIDITY_UNITS.YEARS) { + config.Rule.DefaultRetention.Years = lockConfigOpts.validity + } + } + } + + const builder = new xml2js.Builder({ + rootName: 'ObjectLockConfiguration', + renderOpts: { pretty: false }, + headless: true, + }) + const payload = builder.buildObject(config) + + const headers: RequestHeaders = {} + headers['Content-MD5'] = toMd5(payload) + + await this.makeRequestAsyncOmit({ method, bucketName, query, headers }, payload) + } } diff --git a/src/internal/type.ts b/src/internal/type.ts index ec9499d1..80f15943 100644 --- a/src/internal/type.ts +++ b/src/internal/type.ts @@ -20,6 +20,7 @@ export type Encryption = KMSMasterKeyID?: string } +export type EnabledOrDisabledStatus = 'Enabled' | 'Disabled' export enum ENCRYPTION_TYPES { /** * SSEC represents server-side-encryption with customer provided keys @@ -129,7 +130,7 @@ export type StatObjectOpts = { /* Replication Config types */ export type ReplicationRuleStatus = { - Status: 'Enabled' | 'Disabled' + Status: EnabledOrDisabledStatus } export type Tag = { @@ -187,3 +188,29 @@ export type ReplicationConfig = { /* Replication Config types */ export type ResultCallback = (error: Error | null, result: T) => void + +export type EmptyObject = Record + +export type ObjectLockInfo = + | { + objectLockEnabled: EnabledOrDisabledStatus + mode: RETENTION_MODES + unit: RETENTION_VALIDITY_UNITS + validity: number + } + | EmptyObject + +export type ObjectLockConfigParam = { + ObjectLockEnabled?: 'Enabled' | undefined + Rule?: + | { + DefaultRetention: + | { + Mode: RETENTION_MODES + Days: number + Years: number + } + | EmptyObject + } + | EmptyObject +} diff --git a/src/internal/xml-parser.ts b/src/internal/xml-parser.ts index bc5ec60e..a498b210 100644 --- a/src/internal/xml-parser.ts +++ b/src/internal/xml-parser.ts @@ -5,7 +5,8 @@ import { XMLParser } from 'fast-xml-parser' import * as errors from '../errors.ts' import { parseXml, sanitizeETag, sanitizeObjectKey, toArray } from './helper.ts' import { readAsString } from './response.ts' -import type { BucketItemFromList, BucketItemWithMetadata, ReplicationConfig } from './type.ts' +import type { BucketItemFromList, BucketItemWithMetadata, ObjectLockInfo, ReplicationConfig } from './type.ts' +import { RETENTION_VALIDITY_UNITS } from './type.ts' // parse XML response for bucket region export function parseBucketRegion(xml: string): string { @@ -241,3 +242,34 @@ export function parseReplicationConfig(xml: string): ReplicationConfig { }, } } + +export function parseObjectLockConfig(xml: string): ObjectLockInfo { + const xmlObj = parseXml(xml) + let lockConfigResult = {} as ObjectLockInfo + if (xmlObj.ObjectLockConfiguration) { + lockConfigResult = { + objectLockEnabled: xmlObj.ObjectLockConfiguration.ObjectLockEnabled, + } as ObjectLockInfo + let retentionResp + if ( + xmlObj.ObjectLockConfiguration && + xmlObj.ObjectLockConfiguration.Rule && + xmlObj.ObjectLockConfiguration.Rule.DefaultRetention + ) { + retentionResp = xmlObj.ObjectLockConfiguration.Rule.DefaultRetention || {} + lockConfigResult.mode = retentionResp.Mode + } + if (retentionResp) { + const isUnitYears = retentionResp.Years + if (isUnitYears) { + lockConfigResult.validity = isUnitYears + lockConfigResult.unit = RETENTION_VALIDITY_UNITS.YEARS + } else { + lockConfigResult.validity = retentionResp.Days + lockConfigResult.unit = RETENTION_VALIDITY_UNITS.DAYS + } + } + } + + return lockConfigResult +} diff --git a/src/minio.d.ts b/src/minio.d.ts index 6727ec10..c5f69058 100644 --- a/src/minio.d.ts +++ b/src/minio.d.ts @@ -23,11 +23,13 @@ import type { BucketItemStat, BucketItemWithMetadata, BucketStream, + EmptyObject, ExistingObjectReplication, IncompleteUploadedBucketItem, ItemBucketMetadata, ItemBucketMetadataList, MetadataItem, + ObjectLockInfo, ReplicaModifications, ReplicationConfig, ReplicationConfigOpts, @@ -52,12 +54,14 @@ export type { BucketItemWithMetadata, BucketStream, ClientOptions, + EmptyObject, ExistingObjectReplication, IncompleteUploadedBucketItem, ItemBucketMetadata, ItemBucketMetadataList, MetadataItem, NoResultCallback, + ObjectLockInfo, RemoveOptions, ReplicaModifications, ReplicationConfig, @@ -104,13 +108,10 @@ export type LockUnit = RETENTION_VALIDITY_UNITS /** * @deprecated keep for backward compatible */ -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 Lock = LockConfig | EmptyObject export type Encryption = EncryptionConfig | EmptyObject export type Retention = RetentionOptions | EmptyObject export type IsoDate = string @@ -268,13 +269,6 @@ export class Client extends TypedClient { removeBucketLifecycle(bucketName: string, callback: NoResultCallback): void removeBucketLifecycle(bucketName: string): Promise - setObjectLockConfig(bucketName: string, callback: NoResultCallback): void - setObjectLockConfig(bucketName: string, lockConfig: Lock, callback: NoResultCallback): void - setObjectLockConfig(bucketName: string, lockConfig?: Lock): Promise - - getObjectLockConfig(bucketName: string, callback: ResultCallback): void - getObjectLockConfig(bucketName: string): Promise - getBucketEncryption(bucketName: string, callback: ResultCallback): void getBucketEncryption(bucketName: string): Promise diff --git a/src/minio.js b/src/minio.js index 36d695c8..c82ef363 100644 --- a/src/minio.js +++ b/src/minio.js @@ -60,7 +60,7 @@ import { uriResourceEscape, } from './internal/helper.ts' import { PostPolicy } from './internal/post-policy.ts' -import { LEGAL_HOLD_STATUS, RETENTION_MODES, RETENTION_VALIDITY_UNITS } from './internal/type.ts' +import { LEGAL_HOLD_STATUS, RETENTION_MODES } from './internal/type.ts' import { NotificationConfig, NotificationPoller } from './notification.js' import { ObjectUploader } from './object-uploader.js' import { promisify } from './promisify.js' @@ -1973,92 +1973,6 @@ export class Client extends TypedClient { }) } - setObjectLockConfig(bucketName, lockConfigOpts = {}, cb) { - const retentionModes = [RETENTION_MODES.COMPLIANCE, RETENTION_MODES.GOVERNANCE] - const validUnits = [RETENTION_VALIDITY_UNITS.DAYS, RETENTION_VALIDITY_UNITS.YEARS] - - if (!isValidBucketName(bucketName)) { - throw new errors.InvalidBucketNameError('Invalid bucket name: ' + bucketName) - } - - if (lockConfigOpts.mode && !retentionModes.includes(lockConfigOpts.mode)) { - throw new TypeError(`lockConfigOpts.mode should be one of ${retentionModes}`) - } - if (lockConfigOpts.unit && !validUnits.includes(lockConfigOpts.unit)) { - throw new TypeError(`lockConfigOpts.unit should be one of ${validUnits}`) - } - if (lockConfigOpts.validity && !isNumber(lockConfigOpts.validity)) { - throw new TypeError(`lockConfigOpts.validity should be a number`) - } - - const method = 'PUT' - const query = 'object-lock' - - let config = { - ObjectLockEnabled: 'Enabled', - } - const configKeys = Object.keys(lockConfigOpts) - // Check if keys are present and all keys are present. - if (configKeys.length > 0) { - if (_.difference(configKeys, ['unit', 'mode', 'validity']).length !== 0) { - throw new TypeError( - `lockConfigOpts.mode,lockConfigOpts.unit,lockConfigOpts.validity all the properties should be specified.`, - ) - } else { - config.Rule = { - DefaultRetention: {}, - } - if (lockConfigOpts.mode) { - config.Rule.DefaultRetention.Mode = lockConfigOpts.mode - } - if (lockConfigOpts.unit === RETENTION_VALIDITY_UNITS.DAYS) { - config.Rule.DefaultRetention.Days = lockConfigOpts.validity - } else if (lockConfigOpts.unit === RETENTION_VALIDITY_UNITS.YEARS) { - config.Rule.DefaultRetention.Years = lockConfigOpts.validity - } - } - } - - const builder = new xml2js.Builder({ - rootName: 'ObjectLockConfiguration', - renderOpts: { pretty: false }, - headless: true, - }) - const payload = builder.buildObject(config) - - const headers = {} - headers['Content-MD5'] = toMd5(payload) - - this.makeRequest({ method, bucketName, query, headers }, payload, [200], '', false, cb) - } - - getObjectLockConfig(bucketName, cb) { - if (!isValidBucketName(bucketName)) { - throw new errors.InvalidBucketNameError('Invalid bucket name: ' + bucketName) - } - if (!isFunction(cb)) { - throw new errors.InvalidArgumentError('callback should be of type "function"') - } - const method = 'GET' - const query = 'object-lock' - - this.makeRequest({ method, bucketName, query }, '', [200], '', true, (e, response) => { - if (e) { - return cb(e) - } - - let objectLockConfig = Buffer.from('') - pipesetup(response, transformers.objectLockTransformer()) - .on('data', (data) => { - objectLockConfig = data - }) - .on('error', cb) - .on('end', () => { - cb(null, objectLockConfig) - }) - }) - } - putObjectRetention(bucketName, objectName, retentionOpts = {}, cb) { if (!isValidBucketName(bucketName)) { throw new errors.InvalidBucketNameError('Invalid bucket name: ' + bucketName) @@ -2676,8 +2590,6 @@ Client.prototype.getObjectTagging = promisify(Client.prototype.getObjectTagging) Client.prototype.setBucketLifecycle = promisify(Client.prototype.setBucketLifecycle) Client.prototype.getBucketLifecycle = promisify(Client.prototype.getBucketLifecycle) 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) @@ -2696,3 +2608,5 @@ Client.prototype.listBuckets = callbackify(Client.prototype.listBuckets) Client.prototype.removeBucketReplication = callbackify(Client.prototype.removeBucketReplication) Client.prototype.setBucketReplication = callbackify(Client.prototype.setBucketReplication) Client.prototype.getBucketReplication = callbackify(Client.prototype.getBucketReplication) +Client.prototype.setObjectLockConfig = callbackify(Client.prototype.setObjectLockConfig) +Client.prototype.getObjectLockConfig = callbackify(Client.prototype.getObjectLockConfig) diff --git a/src/transformers.js b/src/transformers.js index d835bf1b..4a657816 100644 --- a/src/transformers.js +++ b/src/transformers.js @@ -149,10 +149,6 @@ export function lifecycleTransformer() { return getConcater(xmlParsers.parseLifecycleConfig) } -export function objectLockTransformer() { - return getConcater(xmlParsers.parseObjectLockConfig) -} - export function objectRetentionTransformer() { return getConcater(xmlParsers.parseObjectRetentionConfig) } diff --git a/src/xml-parsers.js b/src/xml-parsers.js index 19109d5a..628ee238 100644 --- a/src/xml-parsers.js +++ b/src/xml-parsers.js @@ -19,7 +19,6 @@ import crc32 from 'buffer-crc32' import * as errors from './errors.ts' import { SelectResults } from './helpers.ts' import { isObject, parseXml, readableStream, sanitizeETag, sanitizeObjectKey, toArray } from './internal/helper.ts' -import { RETENTION_VALIDITY_UNITS } from './internal/type.ts' // parse XML response for copy object export function parseCopyObject(xml) { @@ -387,36 +386,6 @@ export function parseLifecycleConfig(xml) { return xmlObj.LifecycleConfiguration } -export function parseObjectLockConfig(xml) { - const xmlObj = parseXml(xml) - let lockConfigResult = {} - if (xmlObj.ObjectLockConfiguration) { - lockConfigResult = { - objectLockEnabled: xmlObj.ObjectLockConfiguration.ObjectLockEnabled, - } - let retentionResp - if ( - xmlObj.ObjectLockConfiguration && - xmlObj.ObjectLockConfiguration.Rule && - xmlObj.ObjectLockConfiguration.Rule.DefaultRetention - ) { - retentionResp = xmlObj.ObjectLockConfiguration.Rule.DefaultRetention || {} - lockConfigResult.mode = retentionResp.Mode - } - if (retentionResp) { - const isUnitYears = retentionResp.Years - if (isUnitYears) { - lockConfigResult.validity = isUnitYears - lockConfigResult.unit = RETENTION_VALIDITY_UNITS.YEARS - } else { - lockConfigResult.validity = retentionResp.Days - lockConfigResult.unit = RETENTION_VALIDITY_UNITS.DAYS - } - } - return lockConfigResult - } -} - export function parseObjectRetentionConfig(xml) { const xmlObj = parseXml(xml) const retentionConfig = xmlObj.Retention