Compare commits
5 Commits
developmen
...
feature/S3
Author | SHA1 | Date |
---|---|---|
Ilke | be948a9bf4 | |
Dora Korpar | 2abcdb8cc4 | |
Dora Korpar | 986e79ed11 | |
Dora Korpar | 26abc2148b | |
Dora Korpar | a3738fef29 |
|
@ -97,7 +97,6 @@ const constants = {
|
|||
'publicAccessBlock',
|
||||
'requestPayment',
|
||||
'restore',
|
||||
'retention',
|
||||
'torrent',
|
||||
],
|
||||
|
||||
|
|
|
@ -45,6 +45,7 @@ const objectPutACL = require('./objectPutACL');
|
|||
const objectPutTagging = require('./objectPutTagging');
|
||||
const objectPutPart = require('./objectPutPart');
|
||||
const objectPutCopyPart = require('./objectPutCopyPart');
|
||||
const objectPutRetention = require('./objectPutRetention');
|
||||
const prepareRequestContexts
|
||||
= require('./apiUtils/authorization/prepareRequestContexts');
|
||||
const serviceGet = require('./serviceGet');
|
||||
|
@ -210,6 +211,7 @@ const api = {
|
|||
objectPutTagging,
|
||||
objectPutPart,
|
||||
objectPutCopyPart,
|
||||
objectPutRetention,
|
||||
serviceGet,
|
||||
websiteGet,
|
||||
websiteHead,
|
||||
|
|
|
@ -1,12 +1,18 @@
|
|||
const { policies } = require('arsenal');
|
||||
const { config } = require('../../../Config');
|
||||
|
||||
const RequestContext = policies.RequestContext;
|
||||
const requestUtils = policies.requestUtils;
|
||||
const { RequestContext, requestUtils } = policies;
|
||||
let apiMethodAfterVersionCheck;
|
||||
const apiMethodWithVersion = { objectGetACL: true, objectPutACL: true,
|
||||
objectGet: true, objectDelete: true, objectPutTagging: true,
|
||||
objectGetTagging: true, objectDeleteTagging: true };
|
||||
const apiMethodWithVersion = {
|
||||
objectGetACL: true,
|
||||
objectPutACL: true,
|
||||
objectGet: true,
|
||||
objectDelete: true,
|
||||
objectPutTagging: true,
|
||||
objectGetTagging: true,
|
||||
objectDeleteTagging: true,
|
||||
objectPutRetention: true,
|
||||
};
|
||||
|
||||
function isHeaderAcl(headers) {
|
||||
return headers['x-amz-grant-read'] || headers['x-amz-grant-read-acp'] ||
|
||||
|
|
|
@ -11,6 +11,7 @@ const locationConstraintCheck = require('./locationConstraintCheck');
|
|||
const { versioningPreprocessing } = require('./versioning');
|
||||
const removeAWSChunked = require('./removeAWSChunked');
|
||||
const getReplicationInfo = require('./getReplicationInfo');
|
||||
const getObjectLockInfo = require('./getObjectLockInfo');
|
||||
const { config } = require('../../../Config');
|
||||
const validateWebsiteHeader = require('./websiteServing')
|
||||
.validateWebsiteHeader;
|
||||
|
@ -194,6 +195,7 @@ function createAndStoreObject(bucketName, bucketMD, objectKey, objMD, authInfo,
|
|||
logger.newRequestLoggerFromSerializedUids(log.getSerializedUids());
|
||||
return async.waterfall([
|
||||
function storeData(next) {
|
||||
console.log(`\n####\n[lib/api/apiUtils/object/createAndStoreObject.js] waterfall ! L197!`);
|
||||
if (size === 0 && !dontSkipBackend[locationType]) {
|
||||
metadataStoreParams.contentMD5 = constants.emptyFileMd5;
|
||||
return next(null, null, null);
|
||||
|
|
|
@ -36,6 +36,7 @@ function checkHashMatchMD5(stream, hashedStream, dataRetrievalInfo, log, cb) {
|
|||
return cb(errors.BadDigest);
|
||||
});
|
||||
}
|
||||
console.log(`\n^^^^^\n[lib/api/apiUtils/object/storeObject.js] dataRetrievalInfo:${JSON.stringify(dataRetrievalInfo, null,2)}!\n`);
|
||||
return cb(null, dataRetrievalInfo, completedHash);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
const async = require('async');
|
||||
const { errors, s3middleware } = require('arsenal');
|
||||
|
||||
const { decodeVersionId, getVersionIdResHeader }
|
||||
= require('./apiUtils/object/versioning');
|
||||
|
||||
const { metadataValidateBucketAndObj } = require('../metadata/metadataUtils');
|
||||
const { pushMetric } = require('../utapi/utilities');
|
||||
const collectCorsHeaders = require('../utilities/collectCorsHeaders');
|
||||
|
||||
const { convertToXml } = s3middleware.retention;
|
||||
|
||||
/**
|
||||
* Object Get Retention - Return retention info for object
|
||||
* @param {AuthInfo} authInfo - Instance of AuthInfo class with requester's info
|
||||
* @param {object} request - http request object
|
||||
* @param {object} log - Werelogs logger
|
||||
* @param {function} callback - callback to server
|
||||
* @return {undefined}
|
||||
*/
|
||||
function objectGetRetention(authInfo, request, log, callback) {
|
||||
log.debug('processing request', { method: 'objectGetRetention' });
|
||||
|
||||
const { bucketName, objectKey } = request;
|
||||
|
||||
const decodedVidResult = decodeVersionId(request.query);
|
||||
if (decodedVidResult instanceof Error) {
|
||||
log.trace('invalid versionId query', {
|
||||
versionId: request.query.versionId,
|
||||
error: decodedVidResult,
|
||||
});
|
||||
return process.nextTick(() => callback(decodedVidResult));
|
||||
}
|
||||
const reqVersionId = decodedVidResult;
|
||||
|
||||
const metadataValParams = {
|
||||
authInfo,
|
||||
bucketName,
|
||||
objectKey,
|
||||
requestType: 'objectGetRetention',
|
||||
versionId: reqVersionId,
|
||||
};
|
||||
|
||||
return async.waterfall([
|
||||
next => metadataValidateBucketAndObj(metadataValParams, log,
|
||||
(err, bucket, objectMD) => {
|
||||
if (err) {
|
||||
log.trace('request authorization failed',
|
||||
{ method: 'objectGetRetention', error: err });
|
||||
return next(err);
|
||||
}
|
||||
if (!objectMD) {
|
||||
const err = reqVersionId ? errors.NoSuchVersion :
|
||||
errors.NoSuchKey;
|
||||
log.trace('error no object metadata found',
|
||||
{ method: 'objectGetRetention', error: err });
|
||||
return next(err, bucket);
|
||||
}
|
||||
if (objectMD.isDeleteMarker) {
|
||||
if (reqVersionId) {
|
||||
log.trace('requested version is delete marker',
|
||||
{ method: 'objectGetRetention' });
|
||||
return next(errors.MethodNotAllowed);
|
||||
}
|
||||
log.trace('most recent version is delete marker',
|
||||
{ method: 'objectGetRetention' });
|
||||
return next(errors.NoSuchKey);
|
||||
}
|
||||
if (!bucket.isObjectLockEnabled()) {
|
||||
log.trace('object lock not enabled on bucket',
|
||||
{ method: 'objectGetRetention' });
|
||||
return next(errors.InvalidRequest.customizeDescription(
|
||||
'Bucket is missing Object Lock Configuration'));
|
||||
}
|
||||
return next(null, bucket, objectMD);
|
||||
}),
|
||||
(bucket, objectMD, next) => {
|
||||
const { retentionInfo } = objectMD;
|
||||
const xml = convertToXml(retentionInfo);
|
||||
if (xml === '') {
|
||||
return next(errors.NoSuchObjectLockConfiguration);
|
||||
}
|
||||
return next(null, bucket, xml, objectMD);
|
||||
},
|
||||
], (err, bucket, xml, objectMD) => {
|
||||
const additionalResHeaders = collectCorsHeaders(request.headers.origin,
|
||||
request.method, bucket);
|
||||
if (err) {
|
||||
log.trace('error processing request', { error: err,
|
||||
method: 'objectGetRetention' });
|
||||
} else {
|
||||
pushMetric('getObjectRetention', log, {
|
||||
authInfo,
|
||||
bucket: bucketName,
|
||||
});
|
||||
const verCfg = bucket.getVersioningConfiguration();
|
||||
additionalResHeaders['x-amz-version-id'] =
|
||||
getVersionIdResHeader(verCfg, objectMD);
|
||||
}
|
||||
return callback(err, xml, additionalResHeaders);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = objectGetRetention;
|
|
@ -4,6 +4,7 @@ const { errors, versioning } = require('arsenal');
|
|||
const aclUtils = require('../utilities/aclUtils');
|
||||
const { cleanUpBucket } = require('./apiUtils/bucket/bucketCreation');
|
||||
const collectCorsHeaders = require('../utilities/collectCorsHeaders');
|
||||
const getObjectLockInfo = require('./apiUtils/object/getObjectLockInfo');
|
||||
const createAndStoreObject = require('./apiUtils/object/createAndStoreObject');
|
||||
const { checkQueryVersionId } = require('./apiUtils/object/versioning');
|
||||
const { metadataValidateBucketAndObj } = require('../metadata/metadataUtils');
|
||||
|
@ -53,6 +54,7 @@ function objectPut(authInfo, request, streamingV4Params, log, callback) {
|
|||
|
||||
return metadataValidateBucketAndObj(valParams, log,
|
||||
(err, bucket, objMD) => {
|
||||
console.log(`\n------\n[lib/api/objectPut.js] request.headers:${JSON.stringify(request.headers, null, 2)}\n`);
|
||||
const responseHeaders = collectCorsHeaders(request.headers.origin,
|
||||
request.method, bucket);
|
||||
if (err) {
|
||||
|
@ -67,6 +69,11 @@ function objectPut(authInfo, request, streamingV4Params, log, callback) {
|
|||
'from non-owner account');
|
||||
return callback(errors.NoSuchBucket);
|
||||
}
|
||||
// const retentionHeaders = collectRetentionHeaders(headers, bucket);
|
||||
const objectLockEnabledForBucket = bucket.isObjectLockEnabled();
|
||||
// console.log(`\n---\n[lib/api/objectPut.js] retentionHeaders: ${retentionHeaders}`);
|
||||
console.log(`\n+++++++\n[lib/api/objectPut.js] objectLockEnabledForBucket: ${objectLockEnabledForBucket}`);
|
||||
|
||||
return async.waterfall([
|
||||
function handleTransientOrDeleteBuckets(next) {
|
||||
if (bucket.hasTransientFlag() || bucket.hasDeletedFlag()) {
|
||||
|
@ -86,7 +93,23 @@ function objectPut(authInfo, request, streamingV4Params, log, callback) {
|
|||
}
|
||||
return next(null, null);
|
||||
},
|
||||
// function validateObjectLockConditions(next) {
|
||||
// const objectHasRetention = request.headers['x-amz-object-lock-mode']
|
||||
// && request.headers['x-amz-object-lock-retain-until-date'];
|
||||
// const objectHasLegalHold
|
||||
// = request.headers['x-amz-object-lock-legal-hold'];
|
||||
// // If retention headers and/or legal hold header present but
|
||||
// // object lock is not enabled on the bucket
|
||||
// if ((objectHasRetention || objectHasLegalHold)
|
||||
// && !bucket.isObjectLockEnabled()) {
|
||||
// return errors.InvalidRequest.customizeDescription(
|
||||
// 'Bucket is missing ObjectLockConfiguration'
|
||||
// );
|
||||
// }
|
||||
// return next();
|
||||
// },
|
||||
function objectCreateAndStore(cipherBundle, next) {
|
||||
console.log(`\n+++++++\n[lib/api/objectPut.js] bucket.isObjectLockEnabled(): ${bucket.isObjectLockEnabled()}`);
|
||||
return createAndStoreObject(bucketName,
|
||||
bucket, objectKey, objMD, authInfo, canonicalID, cipherBundle,
|
||||
request, false, streamingV4Params, log, next);
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
const async = require('async');
|
||||
const { errors, s3middleware } = require('arsenal');
|
||||
|
||||
const { decodeVersionId, getVersionIdResHeader } =
|
||||
require('./apiUtils/object/versioning');
|
||||
|
||||
const { metadataValidateBucketAndObj } = require('../metadata/metadataUtils');
|
||||
const { pushMetric } = require('../utapi/utilities');
|
||||
const getReplicationInfo = require('./apiUtils/object/getReplicationInfo');
|
||||
const collectCorsHeaders = require('../utilities/collectCorsHeaders');
|
||||
const metadata = require('../metadata/wrapper');
|
||||
const { config } = require('../Config');
|
||||
const multipleBackendGateway = require('../data/multipleBackendGateway');
|
||||
|
||||
const { parseRetentionXml } = s3middleware.retention;
|
||||
const REPLICATION_ACTION = 'PUT_RETENTION';
|
||||
|
||||
/**
|
||||
* Object Put Retention - Adds retention information to object
|
||||
* @param {AuthInfo} authInfo - Instance of AuthInfo class with requester's info
|
||||
* @param {object} request - http request object
|
||||
* @param {object} log - Werelogs logger
|
||||
* @param {function} callback - callback to server
|
||||
* @return {undefined}
|
||||
*/
|
||||
function objectPutRetention(authInfo, request, log, callback) {
|
||||
log.debug('processing request', { method: 'objectPutRetention' });
|
||||
|
||||
const { bucketName, objectKey } = request;
|
||||
|
||||
const decodedVidResult = decodeVersionId(request.query);
|
||||
if (decodedVidResult instanceof Error) {
|
||||
log.trace('invalid versionId query', {
|
||||
versionId: request.query.versionId,
|
||||
error: decodedVidResult,
|
||||
});
|
||||
return process.nextTick(() => callback(decodedVidResult));
|
||||
}
|
||||
const reqVersionId = decodedVidResult;
|
||||
|
||||
const metadataValParams = {
|
||||
authInfo,
|
||||
bucketName,
|
||||
objectKey,
|
||||
requestType: 'objectPutRetention',
|
||||
versionId: reqVersionId,
|
||||
};
|
||||
|
||||
return async.waterfall([
|
||||
next => metadataValidateBucketAndObj(metadataValParams, log,
|
||||
(err, bucket, objectMD) => {
|
||||
if (err) {
|
||||
log.trace('request authorization failed',
|
||||
{ method: 'objectPutRetention', error: err });
|
||||
return next(err);
|
||||
}
|
||||
if (!objectMD) {
|
||||
const err = reqVersionId ? errors.NoSuchVersion :
|
||||
errors.NoSuchKey;
|
||||
log.trace('error no object metadata found',
|
||||
{ method: 'objectPutRetention', error: err });
|
||||
return next(err, bucket);
|
||||
}
|
||||
if (objectMD.isDeleteMarker) {
|
||||
log.trace('version is a delete marker',
|
||||
{ method: 'objectPutRetention' });
|
||||
return next(errors.MethodNotAllowed, bucket);
|
||||
}
|
||||
if (!bucket.isObjectLockEnabled()) {
|
||||
log.trace('object lock not enabled on bucket',
|
||||
{ method: 'objectPutRetention' });
|
||||
return next(errors.InvalidRequest.customizeDescription(
|
||||
'Bucket is missing Object Lock Configuration'
|
||||
), bucket);
|
||||
}
|
||||
return next(null, bucket, objectMD);
|
||||
}),
|
||||
(bucket, objectMD, next) => {
|
||||
log.trace('parsing retention information');
|
||||
parseRetentionXml(request.post, log,
|
||||
(err, retentionInfo) => next(err, bucket, retentionInfo, objectMD));
|
||||
},
|
||||
(bucket, retentionInfo, objectMD, next) => {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
objectMD.retentionInfo = retentionInfo;
|
||||
const params = objectMD.versionId ?
|
||||
{ versionId: objectMD.versionId } : {};
|
||||
const replicationInfo = getReplicationInfo(objectKey, bucket, true,
|
||||
0, REPLICATION_ACTION, objectMD);
|
||||
if (replicationInfo) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
objectMD.replicationInfo = Object.assign({},
|
||||
objectMD.replicationInfo, replicationInfo);
|
||||
}
|
||||
metadata.putObjectMD(bucket.getName(), objectKey, objectMD, params,
|
||||
log, err => next(err, bucket, objectMD));
|
||||
},
|
||||
(bucket, objectMD, next) => {
|
||||
if (config.backends.data === 'multiple') {
|
||||
return multipleBackendGateway.objectRetention('Put', objectKey,
|
||||
bucket, objectMD, log, err => next(err, bucket, objectMD));
|
||||
}
|
||||
return next(null, bucket, objectMD);
|
||||
},
|
||||
], (err, bucket, objectMD) => {
|
||||
const additionalResHeaders = collectCorsHeaders(request.headers.origin,
|
||||
request.method, bucket);
|
||||
if (err) {
|
||||
log.trace('error processing request',
|
||||
{ error: err, method: 'objectPutRetention' });
|
||||
} else {
|
||||
pushMetric('putObjectRetention', log, {
|
||||
authInfo,
|
||||
bucket: bucketName,
|
||||
keys: [objectKey],
|
||||
});
|
||||
const verCfg = bucket.getVersioningConfiguration();
|
||||
additionalResHeaders['x-amz-version-id'] =
|
||||
getVersionIdResHeader(verCfg, objectMD);
|
||||
}
|
||||
return callback(err, additionalResHeaders);
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = objectPutRetention;
|
|
@ -94,6 +94,7 @@ const metadata = {
|
|||
objVal.getValue() : objVal;
|
||||
client.putObject(bucketName, objName, value, params, log,
|
||||
(err, data) => {
|
||||
console.log(`\n[lib/metadata/wrapper.js] L97 putObjectMD: data: ${data} `)
|
||||
if (err) {
|
||||
log.debug('error from metadata', { implName, error: err });
|
||||
return cb(err);
|
||||
|
|
|
@ -95,7 +95,7 @@ const services = {
|
|||
contentType, cacheControl, contentDisposition, contentEncoding,
|
||||
expires, multipart, headers, overrideMetadata, log,
|
||||
lastModifiedDate, versioning, versionId, tagging, taggingCopy,
|
||||
replicationInfo, dataStoreName } = params;
|
||||
replicationInfo, objectLockInfo, dataStoreName } = params;
|
||||
log.trace('storing object in metadata');
|
||||
assert.strictEqual(typeof bucketName, 'string');
|
||||
const md = new ObjectMD();
|
||||
|
@ -128,6 +128,24 @@ const services = {
|
|||
if (headers && headers['x-amz-website-redirect-location']) {
|
||||
md.setRedirectLocation(headers['x-amz-website-redirect-location']);
|
||||
}
|
||||
if (headers && headers['x-amz-object-lock-retain-until-date']
|
||||
&& headers['x-amz-object-lock-mode']) {
|
||||
console.log(`\n++++++\n[lib/services.js] HERE! (retentionInfo)\n ~~~~~~\n`);
|
||||
const retention = {
|
||||
mode: headers['x-amz-object-lock-mode'],
|
||||
retainUntilDate: headers['x-amz-object-lock-retain-until-date'],
|
||||
}
|
||||
md.setRetentionInfo(retention);
|
||||
}
|
||||
console.log(`\n!!!!\n[lib/services.js] L140 headers['x-amz-object-lock-legal-hold']: ${headers['x-amz-object-lock-legal-hold']}`);
|
||||
if (headers && headers['x-amz-object-lock-legal-hold']) {
|
||||
const legalHold = headers['x-amz-object-lock-legal-hold']
|
||||
=== 'ON' ? true : false;
|
||||
console.log(`\n++++++\n[lib/services.js] HERE! legalHold: ${legalHold}\n ~~~~~~\n`);
|
||||
// md.setLegalHold(legalHold);
|
||||
}
|
||||
console.log(`\n!!!!\n[lib/services.js] L147 md.getRetentionInfo: ${JSON.stringify(md.getRetentionInfo())}`);
|
||||
// console.log(`\n!!!!\n[lib/services.js] L141 md: ${JSON.stringify(md, null, 2)}`);
|
||||
if (replicationInfo) {
|
||||
md.setReplicationInfo(replicationInfo);
|
||||
}
|
||||
|
|
|
@ -75,6 +75,19 @@ function collectResponseHeaders(objectMD, corsHeaders, versioningCfg,
|
|||
responseMetaHeaders['x-amz-tagging-count'] =
|
||||
Object.keys(objectMD.tags).length;
|
||||
}
|
||||
if (objectMD.retentionInfo && objectMD.retentionInfo.retainUntilDate
|
||||
&& objectMD.retentionInfo.mode) {
|
||||
responseMetaHeaders['x-amz-object-lock-retain-until-date']
|
||||
= objectMD.retentionInfo.retainUntilDate;
|
||||
responseMetaHeaders['x-amz-object-lock-mode']
|
||||
= objectMD.retentionInfo.mode;
|
||||
console.log(`\n[lib/utilities/collectResponseHeaders.js]\n Y e HU UUUU uuu UU !!! @@@ \n\n`);
|
||||
}
|
||||
if (objectMD.legalHold !== undefined) {
|
||||
console.log(`\n[lib/utilities/collectResponseHeaders.js]\n legal hold !!!\n`);
|
||||
responseMetaHeaders['x-amz-replication-status'] =
|
||||
objectMD.legalHold ? 'ON' : 'OFF';
|
||||
}
|
||||
if (objectMD.replicationInfo && objectMD.replicationInfo.status) {
|
||||
responseMetaHeaders['x-amz-replication-status'] =
|
||||
objectMD.replicationInfo.status;
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
},
|
||||
"homepage": "https://github.com/scality/S3#readme",
|
||||
"dependencies": {
|
||||
"arsenal": "github:scality/Arsenal#0d49eff",
|
||||
"arsenal": "github:scality/Arsenal#bdd8368",
|
||||
"async": "~2.5.0",
|
||||
"aws-sdk": "2.363.0",
|
||||
"azure-storage": "^2.1.0",
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
const assert = require('assert');
|
||||
|
||||
const { bucketPut } = require('../../../lib/api/bucketPut');
|
||||
const objectPut = require('../../../lib/api/objectPut');
|
||||
const objectPutRetention = require('../../../lib/api/objectPutRetention');
|
||||
const objectGetRetention = require('../../../lib/api/objectGetRetention');
|
||||
const { cleanup, DummyRequestLogger, makeAuthInfo } = require('../helpers');
|
||||
const DummyRequest = require('../DummyRequest');
|
||||
|
||||
const log = new DummyRequestLogger();
|
||||
const authInfo = makeAuthInfo('accessKey1');
|
||||
const namespace = 'default';
|
||||
const bucketName = 'bucketname';
|
||||
const objectName = 'objectName';
|
||||
const postBody = Buffer.from('I am a body', 'utf8');
|
||||
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() + 1);
|
||||
|
||||
const bucketPutRequest = {
|
||||
bucketName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: '/',
|
||||
};
|
||||
|
||||
const putObjectRequest = new DummyRequest({
|
||||
bucketName,
|
||||
namespace,
|
||||
objectKey: objectName,
|
||||
headers: {},
|
||||
url: `/${bucketName}/${objectName}`,
|
||||
}, postBody);
|
||||
|
||||
const objectRetentionXml = '<ObjectRetention ' +
|
||||
'xmlns="http://s3.amazonaws.com/doc/2006-03-01/">' +
|
||||
'<Mode>GOVERNANCE</Mode>' +
|
||||
`<RetainUntilDate>${date.toISOString()}</RetainUntilDate>` +
|
||||
'</ObjectRetention>';
|
||||
|
||||
const putObjRetRequest = {
|
||||
bucketName,
|
||||
objectKey: objectName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
post: objectRetentionXml,
|
||||
};
|
||||
|
||||
const getObjRetRequest = {
|
||||
bucketName,
|
||||
objectKey: objectName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
};
|
||||
|
||||
describe('getObjectRetention API', () => {
|
||||
before(() => cleanup());
|
||||
|
||||
describe('without Object Lock enabled on bucket', () => {
|
||||
beforeEach(done => {
|
||||
bucketPut(authInfo, bucketPutRequest, log, err => {
|
||||
assert.ifError(err);
|
||||
objectPut(authInfo, putObjectRequest, undefined, log, done);
|
||||
});
|
||||
});
|
||||
afterEach(() => cleanup());
|
||||
|
||||
it('should return InvalidRequest error', done => {
|
||||
objectGetRetention(authInfo, getObjRetRequest, log, err => {
|
||||
assert.strictEqual(err.InvalidRequest, true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('with Object Lock enabled on bucket', () => {
|
||||
const bucketObjLockRequest = Object.assign({}, bucketPutRequest,
|
||||
{ headers: { 'x-amz-bucket-object-lock-enabled': true } });
|
||||
|
||||
beforeEach(done => {
|
||||
bucketPut(authInfo, bucketObjLockRequest, log, err => {
|
||||
assert.ifError(err);
|
||||
objectPut(authInfo, putObjectRequest, undefined, log, done);
|
||||
});
|
||||
});
|
||||
afterEach(() => cleanup());
|
||||
|
||||
it('should return NoSuchObjectLockConfiguration if no retention set',
|
||||
done => {
|
||||
objectGetRetention(authInfo, getObjRetRequest, log, err => {
|
||||
assert.strictEqual(err.NoSuchObjectLockConfiguration, true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should get an object\'s retention info', done => {
|
||||
objectPutRetention(authInfo, putObjRetRequest, log, err => {
|
||||
assert.ifError(err);
|
||||
objectGetRetention(authInfo, getObjRetRequest, log,
|
||||
(err, xml) => {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(xml, objectRetentionXml);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -162,6 +162,75 @@ describe('objectPut API', () => {
|
|||
});
|
||||
});
|
||||
|
||||
const generatePutObjectReq = (date, mode) => new DummyRequest({
|
||||
bucketName,
|
||||
namespace,
|
||||
objectKey: objectName,
|
||||
headers: {
|
||||
'x-amz-object-lock-retain-until-date': date,
|
||||
'x-amz-object-lock-mode': mode,
|
||||
},
|
||||
url: `/${bucketName}/${objectName}`,
|
||||
calculatedHash: 'vnR+tLdVF79rPPfF+7YvOg==',
|
||||
}, postBody);
|
||||
|
||||
const retentionInfo = (date, mode) => ({
|
||||
retainUntilDate: date,
|
||||
mode: mode,
|
||||
});
|
||||
|
||||
it('should successfully put an object with valid retention date and COMPLIANCE mode', done => {
|
||||
bucketPut(authInfo, testPutBucketRequest, log, () => {
|
||||
const testDate = new Date(2022, 6, 3);
|
||||
const testModeComp = 'COMPLIANCE';
|
||||
const request = generatePutObjectReq(testDate, testModeComp);
|
||||
const expectedRetention = retentionInfo(testDate, testModeComp);
|
||||
objectPut(authInfo, request, undefined, log, (err, resHeaders) => {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(resHeaders.ETag, `"${correctMD5}"`);
|
||||
metadata.getObjectMD(bucketName, objectName, {}, log,
|
||||
(err, objectMD) => {
|
||||
assert(objectMD);
|
||||
assert.ifError(err);
|
||||
assert.deepStrictEqual(objectMD.retentionInfo,
|
||||
expectedRetention);
|
||||
assert.strictEqual(objectMD.retentionInfo.mode,
|
||||
expectedRetention.mode);
|
||||
assert.strictEqual(
|
||||
objectMD.retentionInfo.retainUntilDate,
|
||||
expectedRetention.retainUntilDate);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should successfully put an object with valid retention date and GOVERNANCE mode', done => {
|
||||
bucketPut(authInfo, testPutBucketRequest, log, () => {
|
||||
const testDate = new Date(2022, 6, 3);
|
||||
const testModeComp = 'GOVERNANCE';
|
||||
const request = generatePutObjectReq(testDate, testModeComp);
|
||||
const expectedRetention = retentionInfo(testDate, testModeComp);
|
||||
objectPut(authInfo, request, undefined, log, (err, resHeaders) => {
|
||||
assert.ifError(err);
|
||||
assert.strictEqual(resHeaders.ETag, `"${correctMD5}"`);
|
||||
metadata.getObjectMD(bucketName, objectName, {}, log,
|
||||
(err, objectMD) => {
|
||||
assert(objectMD);
|
||||
assert.ifError(err);
|
||||
assert.deepStrictEqual(objectMD.retentionInfo,
|
||||
expectedRetention);
|
||||
assert.strictEqual(objectMD.retentionInfo.mode,
|
||||
expectedRetention.mode);
|
||||
assert.strictEqual(
|
||||
objectMD.retentionInfo.retainUntilDate,
|
||||
expectedRetention.retainUntilDate);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should successfully put an object with user metadata', done => {
|
||||
const testPutObjectRequest = new DummyRequest({
|
||||
bucketName,
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
const assert = require('assert');
|
||||
|
||||
const { bucketPut } = require('../../../lib/api/bucketPut');
|
||||
const objectPut = require('../../../lib/api/objectPut');
|
||||
const objectPutRetention = require('../../../lib/api/objectPutRetention');
|
||||
const { cleanup, DummyRequestLogger, makeAuthInfo } = require('../helpers');
|
||||
const metadata = require('../../../lib/metadata/wrapper');
|
||||
const DummyRequest = require('../DummyRequest');
|
||||
|
||||
const log = new DummyRequestLogger();
|
||||
const authInfo = makeAuthInfo('accessKey1');
|
||||
const namespace = 'default';
|
||||
const bucketName = 'bucketname';
|
||||
const objectName = 'objectName';
|
||||
const postBody = Buffer.from('I am a body', 'utf8');
|
||||
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() + 1);
|
||||
|
||||
const bucketPutRequest = {
|
||||
bucketName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
url: '/',
|
||||
};
|
||||
|
||||
const putObjectRequest = new DummyRequest({
|
||||
bucketName,
|
||||
namespace,
|
||||
objectKey: objectName,
|
||||
headers: {},
|
||||
url: `/${bucketName}/${objectName}`,
|
||||
}, postBody);
|
||||
|
||||
const objectRetentionXml = '<ObjectRetention ' +
|
||||
'xmlns="http://s3.amazonaws.com/doc/2006-03-01/">' +
|
||||
'<Mode>GOVERNANCE</Mode>' +
|
||||
`<RetainUntilDate>${date.toISOString()}</RetainUntilDate>` +
|
||||
'</ObjectRetention>';
|
||||
|
||||
const putObjRetRequest = {
|
||||
bucketName,
|
||||
objectKey: objectName,
|
||||
headers: { host: `${bucketName}.s3.amazonaws.com` },
|
||||
post: objectRetentionXml,
|
||||
};
|
||||
|
||||
const expectedRetInfo = {
|
||||
retention: {
|
||||
mode: 'GOVERNANCE',
|
||||
retainUntilDate: date.toISOString(),
|
||||
},
|
||||
};
|
||||
|
||||
describe('putObjectRetention API', () => {
|
||||
before(() => cleanup());
|
||||
|
||||
describe('without Object Lock enabled on bucket', () => {
|
||||
beforeEach(done => {
|
||||
bucketPut(authInfo, bucketPutRequest, log, err => {
|
||||
assert.ifError(err);
|
||||
objectPut(authInfo, putObjectRequest, undefined, log, done);
|
||||
});
|
||||
});
|
||||
afterEach(() => cleanup());
|
||||
|
||||
it('should return InvalidRequest error', done => {
|
||||
objectPutRetention(authInfo, putObjRetRequest, log, err => {
|
||||
assert.strictEqual(err.InvalidRequest, true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('with Object Lock enabled on bucket', () => {
|
||||
const bucketObjLockRequest = Object.assign({}, bucketPutRequest,
|
||||
{ headers: { 'x-amz-bucket-object-lock-enabled': true } });
|
||||
|
||||
beforeEach(done => {
|
||||
bucketPut(authInfo, bucketObjLockRequest, log, err => {
|
||||
assert.ifError(err);
|
||||
objectPut(authInfo, putObjectRequest, undefined, log, done);
|
||||
});
|
||||
});
|
||||
afterEach(() => cleanup());
|
||||
|
||||
it('should update an object\'s metadata with retention info', done => {
|
||||
objectPutRetention(authInfo, putObjRetRequest, log, err => {
|
||||
assert.ifError(err);
|
||||
return metadata.getObjectMD(bucketName, objectName, {}, log,
|
||||
(err, objMD) => {
|
||||
assert.ifError(err);
|
||||
assert.deepStrictEqual(objMD.retentionInfo,
|
||||
expectedRetInfo);
|
||||
return done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
80
yarn.lock
80
yarn.lock
|
@ -210,9 +210,9 @@ arraybuffer.slice@0.0.6:
|
|||
resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.6.tgz#f33b2159f0532a3f3107a272c0ccfbd1ad2979ca"
|
||||
integrity sha1-8zshWfBTKj8xB6JywMz70a0peco=
|
||||
|
||||
"arsenal@github:scality/Arsenal#0d49eff":
|
||||
"arsenal@github:scality/Arsenal#bdd8368":
|
||||
version "7.5.0"
|
||||
resolved "https://codeload.github.com/scality/Arsenal/tar.gz/0d49eff7e4da07eb27c516820e258221d543d082"
|
||||
resolved "https://codeload.github.com/scality/Arsenal/tar.gz/bdd8368c39097d9f367cc0901dd643f1b97ea532"
|
||||
dependencies:
|
||||
"@hapi/joi" "^15.1.0"
|
||||
JSONStream "^1.0.0"
|
||||
|
@ -236,7 +236,7 @@ arraybuffer.slice@0.0.6:
|
|||
ioctl "2.0.0"
|
||||
|
||||
arsenal@scality/Arsenal#32c895b:
|
||||
version "7.5.0"
|
||||
version "7.4.3"
|
||||
resolved "https://codeload.github.com/scality/Arsenal/tar.gz/32c895b21a31eb67dacc6e76d7f58b8142bf3ad1"
|
||||
dependencies:
|
||||
"@hapi/joi" "^15.1.0"
|
||||
|
@ -379,9 +379,9 @@ aws-sdk@2.363.0:
|
|||
xml2js "0.4.19"
|
||||
|
||||
aws-sdk@^2.2.23:
|
||||
version "2.678.0"
|
||||
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.678.0.tgz#b16230f4894d40ead50f9e23805c874f4ca62549"
|
||||
integrity sha512-i8t7+1/C6maQzUYUFRQXPAsUPT0YdpNsf/oHZKmmZrsOX+epnn2jmAGIBTZgUakY8jRrZxCJka+QokUIadUVQg==
|
||||
version "2.686.0"
|
||||
resolved "https://registry.yarnpkg.com/aws-sdk/-/aws-sdk-2.686.0.tgz#c58c3874eff4c76c763d137df617e87971321eb7"
|
||||
integrity sha512-QhYhJ5y8tUG5SlmY3CSf9RBaa3EFbta28oarOyiwceHKmY80cMCafRI1YypT6CVDx/q91dbnSNQfWhs0cZPbBQ==
|
||||
dependencies:
|
||||
buffer "4.9.1"
|
||||
events "1.1.1"
|
||||
|
@ -399,9 +399,9 @@ aws-sign2@~0.7.0:
|
|||
integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=
|
||||
|
||||
aws4@^1.8.0:
|
||||
version "1.9.1"
|
||||
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.9.1.tgz#7e33d8f7d449b3f673cd72deb9abdc552dbe528e"
|
||||
integrity sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.10.0.tgz#a17b3a8ea811060e74d47d306122400ad4497ae2"
|
||||
integrity sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==
|
||||
|
||||
axios@^0.18.0:
|
||||
version "0.18.1"
|
||||
|
@ -814,12 +814,12 @@ cron-parser@1.1.0:
|
|||
integrity sha1-B1uExFnBVejEgqtNVq/5na5YNS4=
|
||||
|
||||
cron-parser@^2.11.0:
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/cron-parser/-/cron-parser-2.13.0.tgz#6f930bb6f2931790d2a9eec83b3ec276e27a6725"
|
||||
integrity sha512-UWeIpnRb0eyoWPVk+pD3TDpNx3KCFQeezO224oJIkktBrcW6RoAPOx5zIKprZGfk6vcYSmA8yQXItejSaDBhbQ==
|
||||
version "2.15.0"
|
||||
resolved "https://registry.yarnpkg.com/cron-parser/-/cron-parser-2.15.0.tgz#04803cd51d8efcfcc6f83ac08e60f3f8c40c7ec5"
|
||||
integrity sha512-rMFkrQw8+oG5OuwjiXesup4KeIlEG/IU82YtG4xyAHbO5jhKmYaHPp/ZNhq9+7TjSJ65E3zV3kQPUbmXSff2/g==
|
||||
dependencies:
|
||||
is-nan "^1.2.1"
|
||||
moment-timezone "^0.5.25"
|
||||
is-nan "^1.3.0"
|
||||
moment-timezone "^0.5.31"
|
||||
|
||||
cross-spawn@^6.0.5:
|
||||
version "6.0.5"
|
||||
|
@ -1627,9 +1627,9 @@ hosted-git-info@^2.1.4:
|
|||
integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==
|
||||
|
||||
http-proxy@^1.17.0:
|
||||
version "1.18.0"
|
||||
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.0.tgz#dbe55f63e75a347db7f3d99974f2692a314a6a3a"
|
||||
integrity sha512-84I2iJM/n1d4Hdgc6y2+qY5mDaz2PUVjlg9znE9byl+q0uC3DeByqBGReQu5tpLK0TAqTIXScRUV+dg7+bUPpQ==
|
||||
version "1.18.1"
|
||||
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549"
|
||||
integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==
|
||||
dependencies:
|
||||
eventemitter3 "^4.0.0"
|
||||
follow-redirects "^1.0.0"
|
||||
|
@ -1745,9 +1745,9 @@ ioredis@4.9.5:
|
|||
standard-as-callback "^2.0.1"
|
||||
|
||||
ioredis@^4.9.5:
|
||||
version "4.16.3"
|
||||
resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.16.3.tgz#6a6b85830206fd98353b7ff8536521f17943be53"
|
||||
integrity sha512-Ejvcs2yW19Vq8AipvbtfcX3Ig8XG9EAyFOvGbhI/Q1QoVOK9ZdgY092kdOyOWIYBnPHjfjMJhU9qhsnp0i0K1w==
|
||||
version "4.17.1"
|
||||
resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-4.17.1.tgz#06ef3d3b2cb96b7e6bc90a7b8839a33e743843ad"
|
||||
integrity sha512-kfxkN/YO1dnyaoAGyNdH3my4A1eoGDy4QOfqn6o86fo4dTboxyxYVW0S0v/d3MkwCWlvSWhlwq6IJMY9BlWs6w==
|
||||
dependencies:
|
||||
cluster-key-slot "^1.1.0"
|
||||
debug "^4.1.1"
|
||||
|
@ -1822,7 +1822,7 @@ is-my-json-valid@^2.10.0:
|
|||
jsonpointer "^4.0.0"
|
||||
xtend "^4.0.0"
|
||||
|
||||
is-nan@^1.2.1:
|
||||
is-nan@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/is-nan/-/is-nan-1.3.0.tgz#85d1f5482f7051c2019f5673ccebdb06f3b0db03"
|
||||
integrity sha512-z7bbREymOqt2CCaZVly8aC4ML3Xhfi0ekuOnjO2L8vKdl+CttdVoGZQhd4adMFAsxQ5VeRVwORs4tU8RH+HFtQ==
|
||||
|
@ -2028,9 +2028,9 @@ js-tokens@^3.0.2:
|
|||
integrity sha1-mGbfOVECEw449/mWvOtlRDIJwls=
|
||||
|
||||
js-yaml@3.x, js-yaml@^3.5.1, js-yaml@^3.7.0:
|
||||
version "3.13.1"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847"
|
||||
integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==
|
||||
version "3.14.0"
|
||||
resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.0.tgz#a7a34170f26a21bb162424d8adacb4113a69e482"
|
||||
integrity sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==
|
||||
dependencies:
|
||||
argparse "^1.0.7"
|
||||
esprima "^4.0.0"
|
||||
|
@ -2425,9 +2425,9 @@ mime@^1.3.4:
|
|||
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
|
||||
|
||||
mime@^2.2.0:
|
||||
version "2.4.5"
|
||||
resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.5.tgz#d8de2ecb92982dedbb6541c9b6841d7f218ea009"
|
||||
integrity sha512-3hQhEUF027BuxZjQA3s7rIv/7VCQPa27hN9u9g87sEkWaKwQPuXOkVKtOeiyUrnWqTDiOs8Ed2rwg733mB0R5w==
|
||||
version "2.4.6"
|
||||
resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.6.tgz#e5b407c90db442f2beb5b162373d07b69affa4d1"
|
||||
integrity sha512-RZKhC3EmpBchfTGBVb8fb+RL2cWyw/32lshnsETttkBAyAUXSGHxbEJWWRXc751DrIxG1q04b8QwMbAwkRPpUA==
|
||||
|
||||
minimatch@0.3:
|
||||
version "0.3.0"
|
||||
|
@ -2523,17 +2523,17 @@ mocha@^2.3.3, mocha@^2.3.4:
|
|||
supports-color "1.2.0"
|
||||
to-iso-string "0.0.2"
|
||||
|
||||
moment-timezone@^0.5.25:
|
||||
version "0.5.28"
|
||||
resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.28.tgz#f093d789d091ed7b055d82aa81a82467f72e4338"
|
||||
integrity sha512-TDJkZvAyKIVWg5EtVqRzU97w0Rb0YVbfpqyjgu6GwXCAohVRqwZjf4fOzDE6p1Ch98Sro/8hQQi65WDXW5STPw==
|
||||
moment-timezone@^0.5.31:
|
||||
version "0.5.31"
|
||||
resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.31.tgz#9c40d8c5026f0c7ab46eda3d63e49c155148de05"
|
||||
integrity sha512-+GgHNg8xRhMXfEbv81iDtrVeTcWt0kWmTEY1XQK14dICTXnWJnT0dxdlPspwqF3keKMVPXwayEsk1DI0AA/jdA==
|
||||
dependencies:
|
||||
moment ">= 2.9.0"
|
||||
|
||||
"moment@>= 2.9.0":
|
||||
version "2.25.3"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.25.3.tgz#252ff41319cf41e47761a1a88cab30edfe9808c0"
|
||||
integrity sha512-PuYv0PHxZvzc15Sp8ybUCoQ+xpyPWvjOuK72a5ovzp2LI32rJXOiIfyoFoYvG3s6EwwrdkMyWuRiEHSZRLJNdg==
|
||||
version "2.26.0"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.26.0.tgz#5e1f82c6bafca6e83e808b30c8705eed0dcbd39a"
|
||||
integrity sha512-oIixUO+OamkUkwjhAVE18rAMfRJNsNe/Stid/gwHSOfHrOtw9EhAY2AHvdKZ/k/MggcYELFCJz/Sn2pL8b8JMw==
|
||||
|
||||
ms@0.7.1:
|
||||
version "0.7.1"
|
||||
|
@ -3287,9 +3287,9 @@ source-map@^0.6.1:
|
|||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||
|
||||
spdx-correct@^3.0.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4"
|
||||
integrity sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9"
|
||||
integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==
|
||||
dependencies:
|
||||
spdx-expression-parse "^3.0.0"
|
||||
spdx-license-ids "^3.0.0"
|
||||
|
@ -3640,9 +3640,9 @@ uc.micro@^1.0.0, uc.micro@^1.0.1:
|
|||
integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==
|
||||
|
||||
uglify-js@^3.1.4:
|
||||
version "3.9.3"
|
||||
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.9.3.tgz#4a285d1658b8a2ebaef9e51366b3a0f7acd79ec2"
|
||||
integrity sha512-r5ImcL6QyzQGVimQoov3aL2ZScywrOgBXGndbWrdehKoSvGe/RmiE5Jpw/v+GvxODt6l2tpBXwA7n+qZVlHBMA==
|
||||
version "3.9.4"
|
||||
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.9.4.tgz#867402377e043c1fc7b102253a22b64e5862401b"
|
||||
integrity sha512-8RZBJq5smLOa7KslsNsVcSH+KOXf1uDU8yqLeNuVKwmT0T3FA0ZoXlinQfRad7SDcbZZRZE4ov+2v71EnxNyCA==
|
||||
dependencies:
|
||||
commander "~2.20.3"
|
||||
|
||||
|
|
Loading…
Reference in New Issue