Skip to content

Commit 757f7e6

Browse files
committed
♻️ Migrate bucketGet and objectGetLegalHold to async/await
Issue: CLDSRV-823
1 parent 99b9108 commit 757f7e6

2 files changed

Lines changed: 134 additions & 110 deletions

File tree

lib/api/bucketGet.js

Lines changed: 62 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
const { promisify } = require('util');
12
const querystring = require('querystring');
23
const { errors, errorInstances, versioning, s3middleware } = require('arsenal');
34
const constants = require('../../constants');
@@ -236,8 +237,7 @@ function processMasterVersions(bucketName, listParams, list) {
236237
return xml.join('');
237238
}
238239

239-
function handleResult(listParams, requestMaxKeys, encoding, authInfo,
240-
bucketName, list, corsHeaders, log, callback) {
240+
function handleResult(listParams, requestMaxKeys, encoding, authInfo, bucketName, list, log) {
241241
// eslint-disable-next-line no-param-reassign
242242
listParams.maxKeys = requestMaxKeys;
243243
// eslint-disable-next-line no-param-reassign
@@ -250,7 +250,7 @@ function handleResult(listParams, requestMaxKeys, encoding, authInfo,
250250
}
251251
pushMetric('listBucket', log, { authInfo, bucket: bucketName });
252252
monitoring.promMetrics('GET', bucketName, '200', 'listBucket');
253-
return callback(null, res, corsHeaders);
253+
return res;
254254
}
255255

256256
/**
@@ -259,11 +259,9 @@ function handleResult(listParams, requestMaxKeys, encoding, authInfo,
259259
* requester's info
260260
* @param {object} request - http request object
261261
* @param {function} log - Werelogs request logger
262-
* @param {function} callback - callback to respond to http request
263-
* with either error code or xml response body
264-
* @return {undefined}
262+
* @return {Promise<object>} - object containing xml and additionalResHeaders
265263
*/
266-
function bucketGet(authInfo, request, log, callback) {
264+
async function bucketGet(authInfo, request, log) {
267265
const params = request.query;
268266
const bucketName = request.bucketName;
269267
const v2 = params['list-type'];
@@ -277,9 +275,9 @@ function bucketGet(authInfo, request, log, callback) {
277275
}
278276

279277
if (v2 !== undefined && Number.parseInt(v2, 10) !== 2) {
280-
return callback(errorInstances.InvalidArgument.customizeDescription('Invalid ' +
281-
'List Type specified in Request'));
278+
throw errorInstances.InvalidArgument.customizeDescription('Invalid List Type specified in Request');
282279
}
280+
283281
if (v2) {
284282
log.addDefaultFields({ action: 'ListObjectsV2', });
285283
if (request.serverAccessLog) {
@@ -297,18 +295,14 @@ function bucketGet(authInfo, request, log, callback) {
297295
const encoding = params['encoding-type'];
298296
if (encoding !== undefined && encoding !== 'url') {
299297
monitoring.promMetrics('GET', bucketName, 400, 'listBucket');
300-
return callback(errorInstances.InvalidArgument.customizeDescription('Invalid ' +
301-
'Encoding Method specified in Request'));
298+
throw errorInstances.InvalidArgument.customizeDescription('Invalid Encoding Method specified in Request');
302299
}
303300

304301
const requestMaxKeys = params['max-keys'] ? Number.parseInt(params['max-keys'], 10) : 1000;
305302
if (Number.isNaN(requestMaxKeys) || requestMaxKeys < 0) {
306303
monitoring.promMetrics('GET', bucketName, 400, 'listBucket');
307-
return callback(errors.InvalidArgument);
304+
throw errors.InvalidArgument;
308305
}
309-
// AWS only returns 1000 keys even if max keys are greater.
310-
// Max keys stated in response xml can be greater than actual
311-
// keys returned.
312306
const actualMaxKeys = Math.min(constants.listingHardLimit, requestMaxKeys);
313307

314308
const metadataValParams = {
@@ -336,51 +330,67 @@ function bucketGet(authInfo, request, log, callback) {
336330
listParams.marker = params.marker;
337331
}
338332

339-
standardMetadataValidateBucket(metadataValParams, request.actionImplicitDenies, log, (err, bucket) => {
340-
const corsHeaders = collectCorsHeaders(request.headers.origin, request.method, bucket);
333+
let error;
334+
let bucket;
341335

342-
if (err) {
343-
log.debug('error processing request', { error: err });
344-
monitoring.promMetrics('GET', bucketName, err.code, 'listBucket');
345-
return callback(err, null, corsHeaders);
346-
}
336+
try {
337+
const standardMetadataValidateBucketPromised = promisify(standardMetadataValidateBucket);
338+
bucket = await standardMetadataValidateBucketPromised(metadataValParams, request.actionImplicitDenies, log);
339+
} catch (err) {
340+
error = err;
341+
}
347342

348-
if (params.versions !== undefined) {
349-
listParams.listingType = 'DelimiterVersions';
350-
delete listParams.marker;
351-
listParams.keyMarker = params['key-marker'];
352-
listParams.versionIdMarker = params['version-id-marker'] ?
353-
versionIdUtils.decode(params['version-id-marker']) :
354-
undefined;
355-
}
343+
const corsHeaders = collectCorsHeaders(request.headers.origin, request.method, bucket);
356344

357-
if (!requestMaxKeys) {
358-
const emptyList = {
359-
CommonPrefixes: [],
360-
Contents: [],
361-
Versions: [],
362-
IsTruncated: false,
363-
};
364-
return handleResult(listParams, requestMaxKeys, encoding, authInfo,
365-
bucketName, emptyList, corsHeaders, log, callback);
366-
}
345+
if (error) {
346+
log.debug('error processing request', { error });
347+
monitoring.promMetrics('GET', bucketName, error.code, 'listBucket');
348+
error.additionalResHeaders = corsHeaders;
349+
throw error;
350+
}
351+
if (params.versions !== undefined) {
352+
listParams.listingType = 'DelimiterVersions';
353+
delete listParams.marker;
354+
listParams.keyMarker = params['key-marker'];
355+
listParams.versionIdMarker = params['version-id-marker'] ?
356+
versionIdUtils.decode(params['version-id-marker']) :
357+
undefined;
358+
}
359+
if (!requestMaxKeys) {
360+
const emptyList = {
361+
CommonPrefixes: [],
362+
Contents: [],
363+
Versions: [],
364+
IsTruncated: false,
365+
};
366+
const res = handleResult(listParams, requestMaxKeys, encoding, authInfo, bucketName, emptyList, log);
367+
return [res, corsHeaders];
368+
}
367369

368-
return services.getObjectListing(bucketName, listParams, log, (err, list) => {
369-
if (err) {
370-
log.debug('error processing request', { error: err });
371-
monitoring.promMetrics('GET', bucketName, err.code, 'listBucket');
372-
return callback(err, null, corsHeaders);
373-
}
370+
let list;
371+
try {
372+
list = await promisify(services.getObjectListing)(bucketName, listParams, log);
373+
} catch (err) {
374+
log.debug('error processing request', { error: err });
375+
monitoring.promMetrics('GET', bucketName, err.code, 'listBucket');
374376

375-
return handleResult(listParams, requestMaxKeys, encoding, authInfo,
376-
bucketName, list, corsHeaders, log, callback);
377-
});
378-
});
379-
return undefined;
377+
err.additionalResHeaders = corsHeaders;
378+
throw err;
379+
}
380+
381+
const res = handleResult(listParams, requestMaxKeys, encoding, authInfo, bucketName, list, log);
382+
return [res, corsHeaders];
380383
}
381384

382385
module.exports = {
383386
processVersions,
384387
processMasterVersions,
385-
bucketGet,
388+
bucketGet: (...args) => {
389+
const callback = args.at(-1);
390+
const argsWithoutCallback = args.slice(0, -1);
391+
392+
bucketGet(...argsWithoutCallback)
393+
.then(result => callback(null, ...result))
394+
.catch(err => callback(err, null, err.additionalResHeaders));
395+
},
386396
};

lib/api/objectGetLegalHold.js

Lines changed: 72 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
const async = require('async');
21
const { errors, errorInstances, s3middleware } = require('arsenal');
32

43
const { decodeVersionId, getVersionIdResHeader }
@@ -10,23 +9,30 @@ const collectCorsHeaders = require('../utilities/collectCorsHeaders');
109

1110
const { convertToXml } = s3middleware.objectLegalHold;
1211

12+
const standardMetadataValidateBucketAndObjPromised = (metadataValParams, actionImplicitDenies, log) =>
13+
new Promise((resolve, reject) => {
14+
standardMetadataValidateBucketAndObj(metadataValParams, actionImplicitDenies, log, (err, bucket, objectMD) => {
15+
if (err) {return reject(err);}
16+
return resolve({ bucket, objectMD });
17+
});
18+
});
19+
1320
/**
1421
* Returns legal hold status of object
1522
* @param {AuthInfo} authInfo - Instance of AuthInfo class with requester's info
1623
* @param {object} request - http request object
1724
* @param {object} log - Werelogs logger
18-
* @param {function} callback - callback to server
19-
* @return {undefined}
25+
* @return {Promise<object>} - object containing xml and additionalResHeaders
2026
*/
21-
function objectGetLegalHold(authInfo, request, log, callback) {
27+
async function objectGetLegalHold(authInfo, request, log) {
2228
log.debug('processing request', { method: 'objectGetLegalHold' });
2329

2430
const { bucketName, objectKey, query } = request;
2531

2632
const decodedVidResult = decodeVersionId(query);
2733
if (decodedVidResult instanceof Error) {
2834
log.trace('invalid versionId query', { versionId: query.versionId, error: decodedVidResult });
29-
return process.nextTick(() => callback(decodedVidResult));
35+
throw decodedVidResult;
3036
}
3137
const versionId = decodedVidResult;
3238

@@ -40,60 +46,68 @@ function objectGetLegalHold(authInfo, request, log, callback) {
4046
request,
4147
};
4248

43-
return async.waterfall([
44-
next => standardMetadataValidateBucketAndObj(metadataValParams, request.actionImplicitDenies, log,
45-
(err, bucket, objectMD) => {
46-
if (err) {
47-
log.trace('request authorization failed', { method: 'objectGetLegalHold', error: err });
48-
return next(err);
49-
}
50-
if (!objectMD) {
51-
const err = versionId ? errors.NoSuchVersion : errors.NoSuchKey;
52-
log.trace('error no object metadata found', { method: 'objectGetLegalHold', error: err });
53-
return next(err, bucket);
54-
}
55-
if (objectMD.isDeleteMarker) {
56-
if (versionId) {
57-
log.trace('requested version is delete marker', { method: 'objectGetLegalHold' });
58-
// FIXME we should return a `x-amz-delete-marker: true` header, see S3C-7592
59-
return next(errors.MethodNotAllowed);
60-
}
61-
log.trace('most recent version is delete marker', { method: 'objectGetLegalHold' });
62-
// FIXME we should return a `x-amz-delete-marker: true` header, see S3C-7592
63-
return next(errors.NoSuchKey);
64-
}
65-
if (!bucket.isObjectLockEnabled()) {
66-
log.trace('object lock not enabled on bucket', { method: 'objectGetRetention' });
67-
return next(errorInstances.InvalidRequest.customizeDescription(
68-
'Bucket is missing Object Lock Configuration'));
69-
}
70-
return next(null, bucket, objectMD);
71-
}),
72-
(bucket, objectMD, next) => {
73-
const { legalHold } = objectMD;
74-
const xml = convertToXml(legalHold);
75-
if (xml === '') {
76-
return next(errors.NoSuchObjectLockConfiguration);
77-
}
78-
return next(null, bucket, xml, objectMD);
79-
},
80-
], (err, bucket, xml, objectMD) => {
81-
const additionalResHeaders = collectCorsHeaders(request.headers.origin, request.method, bucket);
82-
if (err) {
83-
log.trace('error processing request', { error: err, method: 'objectGetLegalHold' });
84-
} else {
85-
pushMetric('getObjectLegalHold', log, {
86-
authInfo,
87-
bucket: bucketName,
88-
keys: [objectKey],
89-
versionId: objectMD ? objectMD.versionId : undefined,
90-
location: objectMD ? objectMD.dataStoreName : undefined,
91-
});
92-
const verCfg = bucket.getVersioningConfiguration();
93-
additionalResHeaders['x-amz-version-id'] = getVersionIdResHeader(verCfg, objectMD);
49+
let bucket, objectMD;
50+
51+
try {
52+
({ bucket, objectMD } = await standardMetadataValidateBucketAndObjPromised(
53+
metadataValParams,
54+
request.actionImplicitDenies,
55+
log,
56+
));
57+
} catch (err) {
58+
log.trace('request authorization failed', { method: 'objectGetLegalHold', error: err });
59+
throw err;
60+
}
61+
62+
if (!objectMD) {
63+
const err = versionId ? errors.NoSuchVersion : errors.NoSuchKey;
64+
log.trace('error no object metadata found', { method: 'objectGetLegalHold', error: err });
65+
throw err;
66+
}
67+
68+
if (objectMD.isDeleteMarker) {
69+
if (versionId) {
70+
log.trace('requested version is delete marker', { method: 'objectGetLegalHold' });
71+
// FIXME we should return a `x-amz-delete-marker: true` header, see S3C-7592
72+
throw errors.MethodNotAllowed;
9473
}
95-
return callback(err, xml, additionalResHeaders);
74+
75+
log.trace('most recent version is delete marker', { method: 'objectGetLegalHold' });
76+
// FIXME we should return a `x-amz-delete-marker: true` header, see S3C-7592
77+
throw errors.NoSuchKey;
78+
}
79+
80+
if (!bucket.isObjectLockEnabled()) {
81+
log.trace('object lock not enabled on bucket', { method: 'objectGetRetention' });
82+
throw errorInstances.InvalidRequest.customizeDescription('Bucket is missing Object Lock Configuration');
83+
}
84+
85+
const { legalHold } = objectMD;
86+
const xml = convertToXml(legalHold);
87+
if (xml === '') {
88+
throw errors.NoSuchObjectLockConfiguration;
89+
}
90+
91+
const additionalResHeaders = collectCorsHeaders(request.headers.origin, request.method, bucket);
92+
93+
pushMetric('getObjectLegalHold', log, {
94+
authInfo,
95+
bucket: bucketName,
96+
keys: [objectKey],
97+
versionId: objectMD ? objectMD.versionId : undefined,
98+
location: objectMD ? objectMD.dataStoreName : undefined,
9699
});
100+
const verCfg = bucket.getVersioningConfiguration();
101+
additionalResHeaders['x-amz-version-id'] = getVersionIdResHeader(verCfg, objectMD);
102+
103+
return [xml, additionalResHeaders];
97104
}
98105

99-
module.exports = objectGetLegalHold;
106+
module.exports = (...args) => {
107+
const callback = args.at(-1);
108+
const argsWithoutCallback = args.slice(0, -1);
109+
110+
objectGetLegalHold(...argsWithoutCallback)
111+
.then(result => callback(null, ...result))
112+
.catch(err => callback(err, null, err.additionalResHeaders));
113+
};

0 commit comments

Comments
 (0)