Skip to content

Commit 4f6b182

Browse files
committed
♻️ Migrate bucketGet and objectGetLegalHold to async/await
Issue: CLDSRV-823
1 parent 7559941 commit 4f6b182

File tree

2 files changed

+140
-111
lines changed

2 files changed

+140
-111
lines changed

lib/api/bucketGet.js

Lines changed: 68 additions & 53 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,18 +259,17 @@ 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'];
268+
270269
if (v2 !== undefined && Number.parseInt(v2, 10) !== 2) {
271-
return callback(errorInstances.InvalidArgument.customizeDescription('Invalid ' +
272-
'List Type specified in Request'));
270+
throw errorInstances.InvalidArgument.customizeDescription('Invalid List Type specified in Request');
273271
}
272+
274273
if (v2) {
275274
log.addDefaultFields({ action: 'ListObjectsV2', });
276275
if (request.serverAccessLog) {
@@ -288,18 +287,14 @@ function bucketGet(authInfo, request, log, callback) {
288287
const encoding = params['encoding-type'];
289288
if (encoding !== undefined && encoding !== 'url') {
290289
monitoring.promMetrics('GET', bucketName, 400, 'listBucket');
291-
return callback(errorInstances.InvalidArgument.customizeDescription('Invalid ' +
292-
'Encoding Method specified in Request'));
290+
throw errorInstances.InvalidArgument.customizeDescription('Invalid Encoding Method specified in Request');
293291
}
294292

295293
const requestMaxKeys = params['max-keys'] ? Number.parseInt(params['max-keys'], 10) : 1000;
296294
if (Number.isNaN(requestMaxKeys) || requestMaxKeys < 0) {
297295
monitoring.promMetrics('GET', bucketName, 400, 'listBucket');
298-
return callback(errors.InvalidArgument);
296+
throw errors.InvalidArgument;
299297
}
300-
// AWS only returns 1000 keys even if max keys are greater.
301-
// Max keys stated in response xml can be greater than actual
302-
// keys returned.
303298
const actualMaxKeys = Math.min(constants.listingHardLimit, requestMaxKeys);
304299

305300
const metadataValParams = {
@@ -327,47 +322,67 @@ function bucketGet(authInfo, request, log, callback) {
327322
listParams.marker = params.marker;
328323
}
329324

330-
standardMetadataValidateBucket(metadataValParams, request.actionImplicitDenies, log, (err, bucket) => {
331-
const corsHeaders = collectCorsHeaders(request.headers.origin, request.method, bucket);
332-
if (err) {
333-
log.debug('error processing request', { error: err });
334-
monitoring.promMetrics('GET', bucketName, err.code, 'listBucket');
335-
return callback(err, null, corsHeaders);
336-
}
337-
if (params.versions !== undefined) {
338-
listParams.listingType = 'DelimiterVersions';
339-
delete listParams.marker;
340-
listParams.keyMarker = params['key-marker'];
341-
listParams.versionIdMarker = params['version-id-marker'] ?
342-
versionIdUtils.decode(params['version-id-marker']) :
343-
undefined;
344-
}
345-
if (!requestMaxKeys) {
346-
const emptyList = {
347-
CommonPrefixes: [],
348-
Contents: [],
349-
Versions: [],
350-
IsTruncated: false,
351-
};
352-
return handleResult(listParams, requestMaxKeys, encoding, authInfo,
353-
bucketName, emptyList, corsHeaders, log, callback);
354-
}
355-
return services.getObjectListing(bucketName, listParams, log,
356-
(err, list) => {
357-
if (err) {
358-
log.debug('error processing request', { error: err });
359-
monitoring.promMetrics('GET', bucketName, err.code, 'listBucket');
360-
return callback(err, null, corsHeaders);
361-
}
362-
return handleResult(listParams, requestMaxKeys, encoding, authInfo,
363-
bucketName, list, corsHeaders, log, callback);
364-
});
365-
});
366-
return undefined;
325+
let error;
326+
let bucket;
327+
328+
try {
329+
const standardMetadataValidateBucketPromised = promisify(standardMetadataValidateBucket);
330+
bucket = await standardMetadataValidateBucketPromised(metadataValParams, request.actionImplicitDenies, log);
331+
} catch (err) {
332+
error = err;
333+
}
334+
335+
const corsHeaders = collectCorsHeaders(request.headers.origin, request.method, bucket);
336+
337+
if (error) {
338+
log.debug('error processing request', { error });
339+
monitoring.promMetrics('GET', bucketName, error.code, 'listBucket');
340+
error.additionalResHeaders = corsHeaders;
341+
throw error;
342+
}
343+
if (params.versions !== undefined) {
344+
listParams.listingType = 'DelimiterVersions';
345+
delete listParams.marker;
346+
listParams.keyMarker = params['key-marker'];
347+
listParams.versionIdMarker = params['version-id-marker'] ?
348+
versionIdUtils.decode(params['version-id-marker']) :
349+
undefined;
350+
}
351+
if (!requestMaxKeys) {
352+
const emptyList = {
353+
CommonPrefixes: [],
354+
Contents: [],
355+
Versions: [],
356+
IsTruncated: false,
357+
};
358+
const res = handleResult(listParams, requestMaxKeys, encoding, authInfo, bucketName, emptyList, log);
359+
return [res, corsHeaders];
360+
}
361+
362+
let list;
363+
try {
364+
list = await promisify(services.getObjectListing)(bucketName, listParams, log);
365+
} catch (err) {
366+
log.debug('error processing request', { error: err });
367+
monitoring.promMetrics('GET', bucketName, err.code, 'listBucket');
368+
369+
err.additionalResHeaders = corsHeaders;
370+
throw err;
371+
}
372+
373+
const res = handleResult(listParams, requestMaxKeys, encoding, authInfo, bucketName, list, log);
374+
return [res, corsHeaders];
367375
}
368376

369377
module.exports = {
370378
processVersions,
371379
processMasterVersions,
372-
bucketGet,
380+
bucketGet: (...args) => {
381+
const callback = args.at(-1);
382+
const argsWithoutCallback = args.slice(0, -1);
383+
384+
bucketGet(...argsWithoutCallback)
385+
.then(result => callback(null, ...result))
386+
.catch(err => callback(err, null, err.additionalResHeaders));
387+
},
373388
};

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)