Skip to content

Commit aad0c3d

Browse files
committed
🎨 migrate buckgetGet to async/await
Issue: CLDSRV-823
1 parent c022d57 commit aad0c3d

File tree

1 file changed

+121
-126
lines changed

1 file changed

+121
-126
lines changed

lib/api/bucketGet.js

Lines changed: 121 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,9 @@ const escapeForXml = s3middleware.escapeForXml;
88
const { pushMetric } = require('../utapi/utilities');
99
const versionIdUtils = versioning.VersionID;
1010
const monitoring = require('../utilities/monitoringHandler');
11-
const { generateToken, decryptToken }
12-
= require('../api/apiUtils/object/continueToken');
11+
const { generateToken, decryptToken } = require('../api/apiUtils/object/continueToken');
1312

14-
// do not url encode the continuation tokens
15-
const skipUrlEncoding = new Set([
16-
'ContinuationToken',
17-
'NextContinuationToken',
18-
]);
13+
const xmlParamsToSkipUrlEncoding = new Set(['ContinuationToken', 'NextContinuationToken',]);
1914

2015
/* Sample XML response for GET bucket objects V2:
2116
<ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
@@ -122,17 +117,16 @@ function processVersions(bucketName, listParams, list) {
122117
{ tag: 'IsTruncated', value: isTruncated },
123118
];
124119

125-
const escapeXmlFn = listParams.encoding === 'url' ?
126-
querystring.escape : escapeForXml;
120+
const escapeXmlFn = listParams.encoding === 'url' ? querystring.escape : escapeForXml;
127121
xmlParams.forEach(p => {
128122
if (p.value) {
129123
const val = p.tag !== 'NextVersionIdMarker' || p.value === 'null' ?
130-
p.value : versionIdUtils.encode(p.value);
124+
p.value :
125+
versionIdUtils.encode(p.value);
131126
xml.push(`<${p.tag}>${escapeXmlFn(val)}</${p.tag}>`);
132127
}
133128
});
134-
let lastKey = listParams.keyMarker ?
135-
escapeXmlFn(listParams.keyMarker) : undefined;
129+
let lastKey = listParams.keyMarker ? escapeXmlFn(listParams.keyMarker) : undefined;
136130
list.Versions.forEach(item => {
137131
const v = item.value;
138132
const objectKey = escapeXmlFn(item.key);
@@ -143,7 +137,8 @@ function processVersions(bucketName, listParams, list) {
143137
`<Key>${objectKey}</Key>`,
144138
'<VersionId>',
145139
(v.IsNull || v.VersionId === undefined) ?
146-
'null' : versionIdUtils.encode(v.VersionId),
140+
'null'
141+
: versionIdUtils.encode(v.VersionId),
147142
'</VersionId>',
148143
`<IsLatest>${isLatest}</IsLatest>`,
149144
`<LastModified>${v.LastModified}</LastModified>`,
@@ -182,31 +177,19 @@ function processMasterVersions(bucketName, listParams, list) {
182177
];
183178

184179
if (listParams.v2) {
185-
xmlParams.push(
186-
{ tag: 'StartAfter', value: listParams.startAfter || '' });
187-
xmlParams.push(
188-
{ tag: 'FetchOwner', value: `${listParams.fetchOwner}` });
189-
xmlParams.push({
190-
tag: 'ContinuationToken',
191-
value: generateToken(listParams.continuationToken) || '',
192-
});
193-
xmlParams.push({
194-
tag: 'NextContinuationToken',
195-
value: generateToken(list.NextContinuationToken),
196-
});
197-
xmlParams.push({
198-
tag: 'KeyCount',
199-
value: list.Contents ? list.Contents.length : 0,
200-
});
180+
xmlParams.push({ tag: 'StartAfter', value: listParams.startAfter || '' });
181+
xmlParams.push({ tag: 'FetchOwner', value: `${listParams.fetchOwner}` });
182+
xmlParams.push({ tag: 'ContinuationToken', value: generateToken(listParams.continuationToken) || '', });
183+
xmlParams.push({ tag: 'NextContinuationToken', value: generateToken(list.NextContinuationToken), });
184+
xmlParams.push({ tag: 'KeyCount', value: list.Contents ? list.Contents.length : 0, });
201185
} else {
202186
xmlParams.push({ tag: 'Marker', value: listParams.marker || '' });
203187
xmlParams.push({ tag: 'NextMarker', value: list.NextMarker });
204188
}
205189

206-
const escapeXmlFn = listParams.encoding === 'url' ?
207-
querystring.escape : escapeForXml;
190+
const escapeXmlFn = listParams.encoding === 'url' ? querystring.escape : escapeForXml;
208191
xmlParams.forEach(p => {
209-
if (p.value && skipUrlEncoding.has(p.tag)) {
192+
if (p.value && xmlParamsToSkipUrlEncoding.has(p.tag)) {
210193
xml.push(`<${p.tag}>${p.value}</${p.tag}>`);
211194
} else if (p.value || p.tag === 'KeyCount' || p.tag === 'MaxKeys') {
212195
xml.push(`<${p.tag}>${escapeXmlFn(p.value)}</${p.tag}>`);
@@ -246,15 +229,14 @@ function processMasterVersions(bucketName, listParams, list) {
246229
);
247230
});
248231
list.CommonPrefixes.forEach(item => {
249-
const val = escapeXmlFn(item);
250-
xml.push(`<CommonPrefixes><Prefix>${val}</Prefix></CommonPrefixes>`);
232+
xml.push(`<CommonPrefixes><Prefix>${escapeXmlFn(item)}</Prefix></CommonPrefixes>`);
251233
});
252234
xml.push('</ListBucketResult>');
235+
253236
return xml.join('');
254237
}
255238

256-
function handleResult(listParams, requestMaxKeys, encoding, authInfo,
257-
bucketName, list, corsHeaders, log, callback) {
239+
function handleResult(listParams, requestMaxKeys, encoding, authInfo, bucketName, list, log) {
258240
// eslint-disable-next-line no-param-reassign
259241
listParams.maxKeys = requestMaxKeys;
260242
// eslint-disable-next-line no-param-reassign
@@ -267,9 +249,24 @@ function handleResult(listParams, requestMaxKeys, encoding, authInfo,
267249
}
268250
pushMetric('listBucket', log, { authInfo, bucket: bucketName });
269251
monitoring.promMetrics('GET', bucketName, '200', 'listBucket');
270-
return callback(null, res, corsHeaders);
252+
return res;
271253
}
272254

255+
const validateBucket = (params, denials, log) => new Promise(resolve => {
256+
standardMetadataValidateBucket(params, denials, log, (error, bucket) => {
257+
resolve({ error, bucket });
258+
});
259+
});
260+
261+
const getObjectListing = (bucketName, listParams, log) => new Promise((resolve, reject) => {
262+
services.getObjectListing(bucketName, listParams, log, (err, list) => {
263+
if (err) {
264+
return reject(err);
265+
}
266+
return resolve(list);
267+
});
268+
});
269+
273270
/**
274271
* bucketGet - Return list of objects in bucket, supports v1 & v2
275272
* @param {AuthInfo} authInfo - Instance of AuthInfo class with
@@ -280,92 +277,86 @@ function handleResult(listParams, requestMaxKeys, encoding, authInfo,
280277
* with either error code or xml response body
281278
* @return {undefined}
282279
*/
283-
function bucketGet(authInfo, request, log, callback) {
284-
const params = request.query;
285-
const bucketName = request.bucketName;
286-
const v2 = params['list-type'];
287-
if (v2 !== undefined && Number.parseInt(v2, 10) !== 2) {
288-
return callback(errorInstances.InvalidArgument.customizeDescription('Invalid ' +
289-
'List Type specified in Request'));
290-
}
291-
if (v2) {
292-
log.addDefaultFields({
293-
action: 'ListObjectsV2',
294-
});
295-
if (request.serverAccessLog) {
296-
// eslint-disable-next-line no-param-reassign
297-
request.serverAccessLog.analyticsAction = 'ListObjectsV2';
280+
async function bucketGet(authInfo, request, log, callback) {
281+
try {
282+
const params = request.query;
283+
const bucketName = request.bucketName;
284+
const v2 = params['list-type'];
285+
286+
if (v2 !== undefined && Number.parseInt(v2, 10) !== 2) {
287+
return callback(errorInstances.InvalidArgument.customizeDescription(
288+
'Invalid List Type specified in Request'
289+
));
298290
}
299-
} else if (params.versions !== undefined) {
300-
log.addDefaultFields({
301-
action: 'ListObjectVersions',
302-
});
303-
if (request.serverAccessLog) {
304-
// eslint-disable-next-line no-param-reassign
305-
request.serverAccessLog.analyticsAction = 'ListObjectVersions';
291+
292+
if (v2) {
293+
log.addDefaultFields({ action: 'ListObjectsV2', });
294+
if (request.serverAccessLog) {
295+
// eslint-disable-next-line no-param-reassign
296+
request.serverAccessLog.analyticsAction = 'ListObjectsV2';
297+
}
298+
} else if (params.versions !== undefined) {
299+
log.addDefaultFields({ action: 'ListObjectVersions', });
300+
if (request.serverAccessLog) {
301+
// eslint-disable-next-line no-param-reassign
302+
request.serverAccessLog.analyticsAction = 'ListObjectVersions';
303+
}
306304
}
307-
}
308-
log.debug('processing request', { method: 'bucketGet' });
309-
const encoding = params['encoding-type'];
310-
if (encoding !== undefined && encoding !== 'url') {
311-
monitoring.promMetrics(
312-
'GET', bucketName, 400, 'listBucket');
313-
return callback(errorInstances.InvalidArgument.customizeDescription('Invalid ' +
314-
'Encoding Method specified in Request'));
315-
}
316-
const requestMaxKeys = params['max-keys'] ?
317-
Number.parseInt(params['max-keys'], 10) : 1000;
318-
if (Number.isNaN(requestMaxKeys) || requestMaxKeys < 0) {
319-
monitoring.promMetrics(
320-
'GET', bucketName, 400, 'listBucket');
321-
return callback(errors.InvalidArgument);
322-
}
323-
// AWS only returns 1000 keys even if max keys are greater.
324-
// Max keys stated in response xml can be greater than actual
325-
// keys returned.
326-
const actualMaxKeys = Math.min(constants.listingHardLimit, requestMaxKeys);
305+
log.debug('processing request', { method: 'bucketGet' });
306+
const encoding = params['encoding-type'];
307+
if (encoding !== undefined && encoding !== 'url') {
308+
monitoring.promMetrics('GET', bucketName, 400, 'listBucket');
309+
return callback(errorInstances.InvalidArgument.customizeDescription(
310+
'Invalid Encoding Method specified in Request'
311+
));
312+
}
313+
const requestMaxKeys = params['max-keys'] ? Number.parseInt(params['max-keys'], 10) : 1000;
314+
if (Number.isNaN(requestMaxKeys) || requestMaxKeys < 0) {
315+
monitoring.promMetrics('GET', bucketName, 400, 'listBucket');
316+
return callback(errors.InvalidArgument);
317+
}
318+
const actualMaxKeys = Math.min(constants.listingHardLimit, requestMaxKeys);
327319

328-
const metadataValParams = {
329-
authInfo,
330-
bucketName,
331-
requestType: request.apiMethods || 'bucketGet',
332-
request,
333-
};
334-
const listParams = {
335-
listingType: 'DelimiterMaster',
336-
maxKeys: actualMaxKeys,
337-
prefix: params.prefix,
338-
};
320+
const metadataValParams = {
321+
authInfo,
322+
bucketName,
323+
requestType: request.apiMethods || 'bucketGet',
324+
request,
325+
};
326+
const listParams = {
327+
listingType: 'DelimiterMaster',
328+
maxKeys: actualMaxKeys,
329+
prefix: params.prefix,
330+
};
339331

340-
if (params.delimiter) {
341-
listParams.delimiter = params.delimiter;
342-
}
332+
if (params.delimiter) {
333+
listParams.delimiter = params.delimiter;
334+
}
343335

344-
if (v2) {
345-
listParams.v2 = true;
346-
listParams.startAfter = params['start-after'];
347-
listParams.continuationToken =
348-
decryptToken(params['continuation-token']);
349-
listParams.fetchOwner = params['fetch-owner'] === 'true';
350-
} else {
351-
listParams.marker = params.marker;
352-
}
336+
if (v2) {
337+
listParams.v2 = true;
338+
listParams.startAfter = params['start-after'];
339+
listParams.continuationToken = decryptToken(params['continuation-token']);
340+
listParams.fetchOwner = params['fetch-owner'] === 'true';
341+
} else {
342+
listParams.marker = params.marker;
343+
}
353344

354-
standardMetadataValidateBucket(metadataValParams, request.actionImplicitDenies, log, (err, bucket) => {
355-
const corsHeaders = collectCorsHeaders(request.headers.origin,
356-
request.method, bucket);
357-
if (err) {
358-
log.debug('error processing request', { error: err });
359-
monitoring.promMetrics(
360-
'GET', bucketName, err.code, 'listBucket');
361-
return callback(err, null, corsHeaders);
345+
const { error, bucket } = await validateBucket(metadataValParams, request.actionImplicitDenies, log);
346+
const corsHeaders = collectCorsHeaders(request.headers.origin, request.method, bucket);
347+
348+
if (error) {
349+
log.debug('error processing request', { error });
350+
monitoring.promMetrics('GET', bucketName, error.code, 'listBucket');
351+
return callback(error, null, corsHeaders);
362352
}
363353
if (params.versions !== undefined) {
364354
listParams.listingType = 'DelimiterVersions';
365355
delete listParams.marker;
366356
listParams.keyMarker = params['key-marker'];
367357
listParams.versionIdMarker = params['version-id-marker'] ?
368-
versionIdUtils.decode(params['version-id-marker']) : undefined;
358+
versionIdUtils.decode(params['version-id-marker']) :
359+
undefined;
369360
}
370361
if (!requestMaxKeys) {
371362
const emptyList = {
@@ -374,26 +365,30 @@ function bucketGet(authInfo, request, log, callback) {
374365
Versions: [],
375366
IsTruncated: false,
376367
};
377-
return handleResult(listParams, requestMaxKeys, encoding, authInfo,
378-
bucketName, emptyList, corsHeaders, log, callback);
368+
const res = handleResult(listParams, requestMaxKeys, encoding, authInfo, bucketName, emptyList, log);
369+
return callback(null, res, corsHeaders);
379370
}
380-
return services.getObjectListing(bucketName, listParams, log,
381-
(err, list) => {
382-
if (err) {
383-
log.debug('error processing request', { error: err });
384-
monitoring.promMetrics(
385-
'GET', bucketName, err.code, 'listBucket');
386-
return callback(err, null, corsHeaders);
387-
}
388-
return handleResult(listParams, requestMaxKeys, encoding, authInfo,
389-
bucketName, list, corsHeaders, log, callback);
390-
});
391-
});
392-
return undefined;
371+
372+
let list;
373+
try {
374+
list = await getObjectListing(bucketName, listParams, log);
375+
} catch (err) {
376+
log.debug('error processing request', { error: err });
377+
monitoring.promMetrics('GET', bucketName, err.code, 'listBucket');
378+
return callback(err, null, corsHeaders);
379+
}
380+
381+
const res = handleResult(listParams, requestMaxKeys, encoding, authInfo, bucketName, list, log);
382+
return callback(null, res, corsHeaders);
383+
} catch (err) {
384+
log.error('unhandled error in bucketGet', { error: err });
385+
return callback(errors.InternalError);
386+
}
393387
}
394388

395389
module.exports = {
396390
processVersions,
397391
processMasterVersions,
398392
bucketGet,
399393
};
394+

0 commit comments

Comments
 (0)