diff --git a/lib/api/bucketGet.js b/lib/api/bucketGet.js index 767437b992..8cfd95aa76 100644 --- a/lib/api/bucketGet.js +++ b/lib/api/bucketGet.js @@ -138,6 +138,7 @@ function processVersions(bucketName, listParams, list) { const objectKey = escapeXmlFn(item.key); const isLatest = lastKey !== objectKey; lastKey = objectKey; + xml.push( v.IsDeleteMarker ? '' : '', `${objectKey}`, @@ -153,14 +154,17 @@ function processVersions(bucketName, listParams, list) { `${v.Owner.ID}`, `${v.Owner.DisplayName}`, '', + ...processOptionalAttributes(v, listParams.optionalAttributes), `${v.StorageClass}`, v.IsDeleteMarker ? '' : '' ); }); + list.CommonPrefixes.forEach(item => { const val = escapeXmlFn(item); xml.push(`${val}`); }); + xml.push(''); return xml.join(''); } @@ -224,6 +228,7 @@ function processMasterVersions(bucketName, listParams, list) { if (v.isDeleteMarker) { return null; } + const objectKey = escapeXmlFn(item.key); xml.push( '', @@ -232,6 +237,7 @@ function processMasterVersions(bucketName, listParams, list) { `"${v.ETag}"`, `${v.Size}` ); + if (!listParams.v2 || listParams.fetchOwner) { xml.push( '', @@ -240,6 +246,9 @@ function processMasterVersions(bucketName, listParams, list) { '' ); } + + xml.push(...processOptionalAttributes(v, listParams.optionalAttributes)); + return xml.push( `${v.StorageClass}`, '' @@ -253,20 +262,48 @@ function processMasterVersions(bucketName, listParams, list) { return xml.join(''); } +function processOptionalAttributes(item, optionalAttributes) { + const xml = []; + + for (const key of Object.keys(item)) { + if (key.startsWith('x-amz-meta-')) { + if (optionalAttributes.includes('x-amz-meta-*') || optionalAttributes.includes(key)) { + xml.push(`<${key}>${item[key]}`); + } + } + } + + if (optionalAttributes.includes('RestoreStatus')) { + xml.push(''); + xml.push(`${!!item.restoreStatus?.inProgress}`); + + if (item.restoreStatus?.expiryDate) { + xml.push(`${item.restoreStatus?.expiryDate}`); + } + + xml.push(''); + } + + return xml; +} + function handleResult(listParams, requestMaxKeys, encoding, authInfo, bucketName, list, corsHeaders, log, callback) { // eslint-disable-next-line no-param-reassign listParams.maxKeys = requestMaxKeys; // eslint-disable-next-line no-param-reassign listParams.encoding = encoding; + let res; if (listParams.listingType === 'DelimiterVersions') { res = processVersions(bucketName, listParams, list); } else { res = processMasterVersions(bucketName, listParams, list); } + pushMetric('listBucket', log, { authInfo, bucket: bucketName }); monitoring.promMetrics('GET', bucketName, '200', 'listBucket'); + return callback(null, res, corsHeaders); } @@ -344,6 +381,7 @@ function bucketGet(authInfo, request, log, callback) { listingType: 'DelimiterMaster', maxKeys: actualMaxKeys, prefix: params.prefix, + optionalAttributes, }; if (params.delimiter) { diff --git a/lib/api/metadataSearch.js b/lib/api/metadataSearch.js index 959068eee0..900b0cceef 100644 --- a/lib/api/metadataSearch.js +++ b/lib/api/metadataSearch.js @@ -19,6 +19,8 @@ function handleResult(listParams, requestMaxKeys, encoding, authInfo, listParams.maxKeys = requestMaxKeys; // eslint-disable-next-line no-param-reassign listParams.encoding = encoding; + // eslint-disable-next-line no-param-reassign + listParams.optionalAttributes = []; let res; if (listParams.listingType === 'DelimiterVersions') { res = processVersions(bucketName, listParams, list); diff --git a/lib/routes/veeam/list.js b/lib/routes/veeam/list.js index 1fcb2e0158..5fd8585bfb 100644 --- a/lib/routes/veeam/list.js +++ b/lib/routes/veeam/list.js @@ -25,6 +25,7 @@ function buildXMLResponse(request, arrayOfFiles, versioned = false) { prefix: validPath, maxKeys: parsedQs['max-keys'] || 1000, delimiter: '/', + optionalAttributes: [], }; const list = { IsTruncated: false, diff --git a/package.json b/package.json index 245b813e9e..ce23614187 100644 --- a/package.json +++ b/package.json @@ -21,7 +21,7 @@ "dependencies": { "@azure/storage-blob": "^12.28.0", "@hapi/joi": "^17.1.1", - "arsenal": "git+https://github.com/scality/Arsenal#8.2.43", + "arsenal": "git+https://github.com/scality/arsenal#feature/ARSN-544/list-objects-v2-optional-attributes", "async": "2.6.4", "aws-sdk": "^2.1692.0", "bucketclient": "scality/bucketclient#8.2.7", diff --git a/tests/functional/aws-node-sdk/test/object/mpuVersion.js b/tests/functional/aws-node-sdk/test/object/mpuVersion.js index 0a89eb7e32..e801a3910c 100644 --- a/tests/functional/aws-node-sdk/test/object/mpuVersion.js +++ b/tests/functional/aws-node-sdk/test/object/mpuVersion.js @@ -101,7 +101,7 @@ function checkObjMdAndUpdate(objMDBefore, objMDAfter, props) { }); } -function clearUploadIdFromVersions(versions) { +function clearUploadIdFromVersionsAndRestoreStatus(versions) { if (!versions || versions.length === 0) { return versions; } @@ -109,6 +109,7 @@ function clearUploadIdFromVersions(versions) { for (const version of versions) { if (version.value) { version.value.uploadId = undefined; + version.value.restoreStatus = undefined; } } @@ -263,7 +264,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), next => putMPUVersion(s3, bucketName, objectName, '', next), @@ -272,7 +273,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -304,7 +305,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), next => putMPUVersion(s3, bucketName, objectName, '', next), @@ -313,7 +314,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -353,7 +354,7 @@ describe('MPU with x-scal-s3-version-id header', () => { }), next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -366,7 +367,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -405,7 +406,7 @@ describe('MPU with x-scal-s3-version-id header', () => { }), next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -418,7 +419,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -458,7 +459,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), next => putMPUVersion(s3, bucketName, objectName, 'null', next), @@ -467,7 +468,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -511,7 +512,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); next(err); }), next => putMPUVersion(s3, bucketName, objectName, vId, next), @@ -520,7 +521,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -567,7 +568,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); next(err); }), next => putMPUVersion(s3, bucketName, objectName, '', next), @@ -576,7 +577,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -621,7 +622,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), next => putMPUVersion(s3, bucketName, objectName, vId, next), @@ -630,7 +631,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -670,7 +671,7 @@ describe('MPU with x-scal-s3-version-id header', () => { }), next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -683,7 +684,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -729,7 +730,7 @@ describe('MPU with x-scal-s3-version-id header', () => { }), next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -743,7 +744,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -777,7 +778,7 @@ describe('MPU with x-scal-s3-version-id header', () => { next => s3.putObject(params, next), next => fakeMetadataArchive(bucketName, objectName, undefined, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = clearUploadIdFromVersions(res.Versions); + versionsBefore = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, undefined, (err, objMD) => { @@ -791,7 +792,7 @@ describe('MPU with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = clearUploadIdFromVersions(res.Versions); + versionsAfter = clearUploadIdFromVersionsAndRestoreStatus(res.Versions); return next(err); }), ], err => { diff --git a/tests/functional/aws-node-sdk/test/object/putVersion.js b/tests/functional/aws-node-sdk/test/object/putVersion.js index f90d712536..74dc164ff9 100644 --- a/tests/functional/aws-node-sdk/test/object/putVersion.js +++ b/tests/functional/aws-node-sdk/test/object/putVersion.js @@ -34,6 +34,21 @@ function putObjectVersion(s3, params, vid, next) { return request.send(next); } +function clearRestoreStatus(versions) { + if (!versions || versions.length === 0) { + return versions; + } + + for (const version of versions) { + if (version.value) { + version.value.restoreStatus = undefined; + } + } + + return versions; +} + + function checkVersionsAndUpdate(versionsBefore, versionsAfter, indexes) { indexes.forEach(i => { assert.notStrictEqual(versionsAfter[i].value.Size, versionsBefore[i].value.Size); @@ -203,7 +218,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); return next(err); }), next => putObjectVersion(s3, params, '', next), @@ -212,7 +227,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -250,7 +265,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { }), next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -263,7 +278,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -301,7 +316,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { }), next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -314,7 +329,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -353,7 +368,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); next(err); }), next => putObjectVersion(s3, params, 'null', next), @@ -362,7 +377,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -405,7 +420,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); return next(err); }), next => putObjectVersion(s3, params, vId, next), @@ -414,7 +429,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -460,7 +475,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); return next(err); }), next => putObjectVersion(s3, params, '', next), @@ -469,7 +484,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -509,7 +524,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => s3.putObject(params, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -522,7 +537,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -561,7 +576,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { }), next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -574,7 +589,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -619,7 +634,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { }), next => fakeMetadataArchive(bucketName, objectName, vId, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, vId, (err, objMD) => { @@ -633,7 +648,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { @@ -666,7 +681,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { next => s3.putObject(params, next), next => fakeMetadataArchive(bucketName, objectName, undefined, archive, next), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsBefore = res.Versions; + versionsBefore = clearRestoreStatus(res.Versions); return next(err); }), next => getMetadata(bucketName, objectName, undefined, (err, objMD) => { @@ -680,7 +695,7 @@ describe('PUT object with x-scal-s3-version-id header', () => { return next(err); }), next => metadata.listObject(bucketName, mdListingParams, log, (err, res) => { - versionsAfter = res.Versions; + versionsAfter = clearRestoreStatus(res.Versions); return next(err); }), ], err => { diff --git a/tests/unit/api/bucketGet.js b/tests/unit/api/bucketGet.js index 971e58748b..ed8a1eb5f1 100644 --- a/tests/unit/api/bucketGet.js +++ b/tests/unit/api/bucketGet.js @@ -514,5 +514,99 @@ describe('bucketGet API V2', () => { done(); }); }); + + it('should return user metadata if requested and present', done => { + const objectNameMeta = 'objectWithMeta'; + const putRequest = new DummyRequest({ + bucketName, + headers: { 'x-amz-meta-color': 'red' }, + url: `/${bucketName}/${objectNameMeta}`, + namespace, + objectKey: objectNameMeta, + }, postBody); + + const testGetRequest = Object.assign({ + query: {}, + url: baseUrl, + }, baseGetRequest); + testGetRequest.headers['x-amz-optional-object-attributes'] = 'x-amz-meta-color'; + + async.waterfall([ + next => bucketPut(authInfo, testPutBucketRequest, log, next), + (_, next) => objectPut(authInfo, putRequest, undefined, log, next), + (_, next) => bucketGet(authInfo, testGetRequest, log, next), + (result, _, next) => parseString(result, next), + ], + (err, result) => { + assert.strictEqual(err, null); + const content = result.ListBucketResult.Contents[0]; + assert.strictEqual(content.Key[0], objectNameMeta); + assert.strictEqual(content['x-amz-meta-color'][0], 'red'); + done(); + }); + }); + + it('should return all user metadata if wildcard requested', done => { + const objectNameMeta = 'objectWithMetaWildcard'; + const putRequest = new DummyRequest({ + bucketName, + headers: { 'x-amz-meta-color': 'red', 'x-amz-meta-size': 'large' }, + url: `/${bucketName}/${objectNameMeta}`, + namespace, + objectKey: objectNameMeta, + }, postBody); + + const testGetRequest = Object.assign({ + query: {}, + url: baseUrl, + }, baseGetRequest); + testGetRequest.headers['x-amz-optional-object-attributes'] = 'x-amz-meta-*'; + + async.waterfall([ + next => bucketPut(authInfo, testPutBucketRequest, log, next), + (_, next) => objectPut(authInfo, putRequest, undefined, log, next), + (_, next) => bucketGet(authInfo, testGetRequest, log, next), + (result, _, next) => parseString(result, next), + ], + (err, result) => { + assert.strictEqual(err, null); + const content = result.ListBucketResult.Contents[0]; + assert.strictEqual(content.Key[0], objectNameMeta); + assert.strictEqual(content['x-amz-meta-color'][0], 'red'); + assert.strictEqual(content['x-amz-meta-size'][0], 'large'); + done(); + }); + }); + + it('should return user metadata in version listing if requested', done => { + const objectNameMeta = 'objectWithMetaVersion'; + const putRequest = new DummyRequest({ + bucketName, + headers: { 'x-amz-meta-ver': '1' }, + url: `/${bucketName}/${objectNameMeta}`, + namespace, + objectKey: objectNameMeta, + }, postBody); + + const testGetRequest = Object.assign({ + query: { versions: '' }, + url: `${baseUrl}?versions`, + }, baseGetRequest); + testGetRequest.headers['x-amz-optional-object-attributes'] = 'x-amz-meta-ver'; + + async.waterfall([ + next => bucketPut(authInfo, testPutBucketRequest, log, next), + (_, next) => objectPut(authInfo, putRequest, undefined, log, next), + (_, next) => bucketGet(authInfo, testGetRequest, log, next), + (result, _, next) => parseString(result, next), + ], + (err, result) => { + assert.strictEqual(err, null); + const version = result.ListVersionsResult.Version[0]; + assert.strictEqual(version.Key[0], objectNameMeta); + assert.strictEqual(version['x-amz-meta-ver'][0], '1'); + done(); + }); + }); }); }); diff --git a/yarn.lock b/yarn.lock index 88c3dfed6a..4ab9aa8e08 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1495,9 +1495,9 @@ arraybuffer.prototype.slice@^1.0.4: optionalDependencies: ioctl "^2.0.2" -"arsenal@git+https://github.com/scality/Arsenal#8.2.43": - version "8.2.43" - resolved "git+https://github.com/scality/Arsenal#c2375224bb0f0a0391c0509b4545859b77f87c92" +"arsenal@git+https://github.com/scality/arsenal#feature/ARSN-544/list-objects-v2-optional-attributes": + version "8.2.44" + resolved "git+https://github.com/scality/arsenal#7b5b61bd6616219941fe3ff31ef38d25971e4c5b" dependencies: "@azure/identity" "^4.13.0" "@azure/storage-blob" "^12.28.0"