@cumulus/common
Advanced tools
Comparing version 1.12.1 to 1.13.0
30
aws.js
@@ -19,3 +19,8 @@ 'use strict'; | ||
const concurrency = require('./concurrency'); | ||
const { deprecate, setErrorStack, noop } = require('./util'); | ||
const { | ||
deprecate, | ||
isNil, | ||
setErrorStack, | ||
noop | ||
} = require('./util'); | ||
const { UnparsableFileLocationError } = require('./errors'); | ||
@@ -41,4 +46,3 @@ | ||
return await fn(...args); | ||
} | ||
catch (err) { | ||
} catch (err) { | ||
setErrorStack(err, tracerError.stack); | ||
@@ -191,8 +195,6 @@ throw err; | ||
callback(null, matchingArn); | ||
} | ||
else if (data.NextToken) { | ||
} else if (data.NextToken) { | ||
const nextOpts = Object.assign({}, opts, { NextToken: data.NextToken }); | ||
exports.findResourceArn(obj, fn, prefix, baseName, nextOpts, callback); | ||
} | ||
else { | ||
} else { | ||
callback(`Could not find resource ${baseName} in ${fn}`); | ||
@@ -233,3 +235,3 @@ } | ||
exports.s3ObjectExists = (params) => | ||
exports.s3().headObject(params).promise() | ||
exports.headObject(params.Bucket, params.Key) | ||
.then(() => true) | ||
@@ -382,4 +384,3 @@ .catch((e) => { | ||
return r; | ||
} | ||
catch (e) { | ||
} catch (e) { | ||
// if file is not return false | ||
@@ -618,4 +619,3 @@ if (e.stack.match(/(NotFound)/) || e.stack.match(/(NoSuchBucket)/)) { | ||
this.params.ContinuationToken = response.NextContinuationToken; | ||
} | ||
else this.items.push(null); | ||
} else this.items.push(null); | ||
} | ||
@@ -869,3 +869,4 @@ } | ||
* @param {integer} [options.numOfMessages=1] - number of messages to read from the queue | ||
* @param {integer} [options.timeout=30] - seconds it takes for a message to timeout | ||
* @param {integer} [options.visibilityTimeout=30] - number of seconds a message is invisible | ||
* after read | ||
* @param {integer} [options.waitTimeSeconds=0] - number of seconds to poll SQS queue (long polling) | ||
@@ -878,3 +879,4 @@ * @returns {Promise.<Array>} an array of messages | ||
AttributeNames: ['All'], | ||
VisibilityTimeout: options.timeout || 30, | ||
// 0 is a valid value for VisibilityTimeout | ||
VisibilityTimeout: isNil(options.visibilityTimeout) ? 30 : options.visibilityTimeout, | ||
WaitTimeSeconds: options.waitTimeSeconds || 0, | ||
@@ -881,0 +883,0 @@ MaxNumberOfMessages: options.numOfMessages || 1 |
@@ -23,4 +23,3 @@ 'use strict'; | ||
return JSON.parse(bucketsString.Body); | ||
} | ||
catch (error) { | ||
} catch (error) { | ||
error.message = `Unable to read bucketsConfiguration from ${bucket}/${Key}: ${error.message}`; | ||
@@ -27,0 +26,0 @@ throw error; |
@@ -135,4 +135,3 @@ 'use strict'; | ||
this.queue = []; | ||
} | ||
catch (err) { | ||
} catch (err) { | ||
log.error(err, err.stack); | ||
@@ -139,0 +138,0 @@ if (this.retries < MAX_RETRIES) { |
@@ -32,4 +32,3 @@ 'use strict'; | ||
return stackDetails.Stacks[0].StackStatus; | ||
} | ||
catch (err) { | ||
} catch (err) { | ||
if (isThrottlingException(err)) throw new Error('Trigger retry'); | ||
@@ -36,0 +35,0 @@ throw new pRetry.AbortError(err); |
@@ -53,4 +53,3 @@ 'use strict'; | ||
}).promise(); | ||
} | ||
catch (err) { | ||
} catch (err) { | ||
if (err.code === 'NoSuchKey') { | ||
@@ -57,0 +56,0 @@ throw new Error(`A collection config for data type "${dataType}__${dataVersion}" was not found.`); |
@@ -9,3 +9,2 @@ 'use strict'; | ||
const log = require('./log'); | ||
const ResourcesLockedError = require('./errors').ResourcesLockedError; | ||
@@ -77,4 +76,3 @@ /** | ||
reject(new Error(`HTTP Error ${response.statusCode}`)); | ||
} | ||
else { | ||
} else { | ||
resolve(response); | ||
@@ -85,75 +83,2 @@ } | ||
class Semaphore { | ||
constructor(docClient, tableName) { | ||
this.docClient = docClient; | ||
this.tableName = tableName; | ||
} | ||
up(key) { | ||
return this.add(key, 1); | ||
} | ||
down(key) { | ||
return this.add(key, -1); | ||
} | ||
async checkout(key, count, max, fn) { | ||
let result = null; | ||
log.info(`Incrementing ${key} by ${count}`); | ||
try { | ||
await this.add(key, count, max); | ||
} | ||
catch (e) { | ||
if (e.message === 'The conditional request failed') { | ||
throw new ResourcesLockedError(`Could not increment ${key} by ${count}`); | ||
} | ||
log.error(e.message, e.stack); | ||
throw e; | ||
} | ||
try { | ||
result = await fn(); | ||
} | ||
finally { | ||
log.info(`Decrementing ${key} by ${count}`); | ||
await this.add(key, -count); | ||
} | ||
return result; | ||
} | ||
async add(key, count, max = 0) { | ||
try { | ||
const params = { | ||
TableName: this.tableName, | ||
Item: { | ||
key: key, | ||
semvalue: 0 | ||
}, | ||
ConditionExpression: '#key <> :key', | ||
ExpressionAttributeNames: { '#key': 'key' }, | ||
ExpressionAttributeValues: { ':key': key } | ||
}; | ||
await this.docClient.put(params).promise(); | ||
} | ||
catch (e) { | ||
if (e.code !== 'ConditionalCheckFailedException') { | ||
throw e; | ||
} | ||
} | ||
const updateParams = { | ||
TableName: this.tableName, | ||
Key: { key: key }, | ||
UpdateExpression: 'set semvalue = semvalue + :val', | ||
ExpressionAttributeValues: { ':val': count }, | ||
ReturnValues: 'UPDATED_NEW' | ||
}; | ||
if (count > 0 && max > 0) { | ||
updateParams.ExpressionAttributeValues[':max'] = max - count; | ||
updateParams.ConditionExpression = 'semvalue <= :max'; | ||
} | ||
return this.docClient.update(updateParams).promise(); | ||
} | ||
} | ||
class Mutex { | ||
@@ -173,4 +98,3 @@ constructor(docClient, tableName) { | ||
result = await fn(); | ||
} | ||
finally { | ||
} finally { | ||
log.info(`Releasing lock ${key}`); | ||
@@ -215,3 +139,2 @@ await this.unlock(key); | ||
Mutex: Mutex, | ||
Semaphore: Semaphore, | ||
limit: limit, | ||
@@ -218,0 +141,0 @@ mapTolerant: mapTolerant, |
@@ -15,4 +15,3 @@ const isFunction = require('lodash.isfunction'); | ||
Error.captureStackTrace(this, this.constructor); | ||
} | ||
else { | ||
} else { | ||
this.stack = (new Error(message)).stack; | ||
@@ -38,2 +37,10 @@ } | ||
/** | ||
* Returns true if the error is a DynamoDB conditional check exception. | ||
* | ||
* @param {Error} error | ||
* @returns {boolean} | ||
*/ | ||
const isConditionalCheckException = (error) => error.code === 'ConditionalCheckFailedException'; | ||
module.exports = { | ||
@@ -43,2 +50,3 @@ | ||
isConditionalCheckException, | ||
isWorkflowError, | ||
@@ -95,2 +103,4 @@ | ||
UnexpectedFileSize: createErrorType('UnexpectedFileSize'), | ||
// Error thrown when system encounters a conflicting request. | ||
@@ -103,3 +113,6 @@ InvalidArgument: createErrorType('InvalidArgument'), | ||
// Error class for file locations that are unparsable | ||
UnparsableFileLocationError: createErrorType('UnparsableFileLocationError') | ||
UnparsableFileLocationError: createErrorType('UnparsableFileLocationError'), | ||
// if a record cannot be found | ||
RecordDoesNotExist: createErrorType('RecordDoesNotExist') | ||
}; |
@@ -45,4 +45,3 @@ 'use strict'; | ||
this.patternStr = patternVal; | ||
} | ||
else { | ||
} else { | ||
this.type = patternVal.type || 'string'; | ||
@@ -54,4 +53,3 @@ this.patternStr = patternVal.value; | ||
this.fields = match.map((f) => f.substr(1, f.length - 2)); | ||
} | ||
else { | ||
} else { | ||
this.fields = []; | ||
@@ -115,4 +113,3 @@ } | ||
isoDateTime = datetime.toISOString(); | ||
} | ||
else if (date.match(/^\d{4}-\d{2}-\d{2}T/)) { | ||
} else if (date.match(/^\d{4}-\d{2}-\d{2}T/)) { | ||
isoDateTime = date; | ||
@@ -119,0 +116,0 @@ } |
@@ -19,1 +19,3 @@ 'use strict'; | ||
exports.errors = require('./errors'); | ||
exports.Semaphore = require('./Semaphore'); | ||
exports.DynamoDb = require('./DynamoDb'); |
@@ -73,4 +73,3 @@ /** | ||
return key; | ||
} | ||
catch (err) { | ||
} catch (err) { | ||
log.error(`Failed to retrieve S3KeyPair key from bucket ${b} on stack ${s}`); | ||
@@ -77,0 +76,0 @@ throw err; |
@@ -30,4 +30,3 @@ 'use strict'; | ||
return r.Plaintext.toString(); | ||
} | ||
catch (e) { | ||
} catch (e) { | ||
if (e.toString().includes('InvalidCiphertextException')) { | ||
@@ -34,0 +33,0 @@ throw new KMSDecryptionFailed( |
@@ -26,7 +26,5 @@ const fs = require('fs'); | ||
rootPath = '../../../..'; | ||
} | ||
else if (isJupyter || isAva || isDebug) { | ||
} else if (isJupyter || isAva || isDebug) { | ||
rootPath = '../..'; | ||
} | ||
else { | ||
} else { | ||
rootPath = '../../..'; | ||
@@ -33,0 +31,0 @@ } |
@@ -108,4 +108,3 @@ 'use strict'; | ||
return workflowConfig[taskName]; | ||
} | ||
catch (e) { | ||
} catch (e) { | ||
log.info('Exception in loadConfigTemplate'); | ||
@@ -128,4 +127,3 @@ throw e; | ||
return JSON.parse(data.Body.toString()); | ||
} | ||
catch (e) { | ||
} catch (e) { | ||
if (e.code !== 'NoSuchKey') { | ||
@@ -146,4 +144,3 @@ throw e; | ||
return Object.assign({}, message, { payload: JSON.parse(payloadJson.Body) }); | ||
} | ||
catch (e) { | ||
} catch (e) { | ||
log.info('Exception in loadMessageData'); | ||
@@ -175,4 +172,3 @@ throw e; | ||
returnValue = Promise.resolve(null); | ||
} | ||
else { | ||
} else { | ||
log.debug('Using S3 payload'); | ||
@@ -263,4 +259,3 @@ const scopedKey = [handler.name, this.key, uuid()].join('/'); | ||
resolve(this.getMessageScopedJsonImmediate()); | ||
} | ||
else { | ||
} else { | ||
this.callbacks.push(() => { | ||
@@ -267,0 +262,0 @@ resolve(this.getMessageScopedJsonImmediate()); |
{ | ||
"name": "@cumulus/common", | ||
"version": "1.12.1", | ||
"version": "1.13.0", | ||
"description": "Common utilities used across tasks", | ||
@@ -44,5 +44,4 @@ "keywords": [ | ||
"dependencies": { | ||
"@cumulus/checksum": "^1.12.1", | ||
"@cumulus/logger": "^1.12.1", | ||
"@cumulus/test-data": "^1.12.1", | ||
"@cumulus/checksum": "1.13.0", | ||
"@cumulus/logger": "1.13.0", | ||
"ajv": "^5.2.2", | ||
@@ -83,9 +82,10 @@ "async": "^2.0.0", | ||
"devDependencies": { | ||
"@cumulus/test-data": "1.13.0", | ||
"ava": "^0.25.0", | ||
"jsdoc-to-markdown": "^4.0.1", | ||
"nock": "^10.0.0", | ||
"nyc": "^13.3.0", | ||
"nyc": "^14.0.0", | ||
"sinon": "^7.1.1" | ||
}, | ||
"gitHead": "384a111d85cc5df2fc1e53270e55459f3e699ce9" | ||
"gitHead": "9b6c2519043e9d0474c9cb5322140a8e766f1d6b" | ||
} |
@@ -10,3 +10,64 @@ /* eslint no-console: "off" */ | ||
const fs = require('fs-extra'); | ||
const { isNil } = require('./util'); | ||
/** | ||
* Create a function which will allow methods of an AWS service interface object | ||
* to be wrapped. | ||
* | ||
* When invoked, this returned function will take two arguments: | ||
* - methodName - the name of the service interface object method to wrap | ||
* - dataHandler - a handler function which will be used to process the result | ||
* of invoking `methodName` | ||
* | ||
* @param {Object} client - AWS Service interface object | ||
* @returns {Function} function taking a client method name and a dataHandler | ||
* function to be called upon completion of the client method with return | ||
* value of the client method and the original parameters passed into the | ||
* client method | ||
* | ||
* @example | ||
* const s3 = new AWS.S3(); | ||
* | ||
* // Initialize wrapper for AWS S3 service interface object | ||
* const s3Wrapper = awsServiceInterfaceMethodWrapper(s3); | ||
* | ||
* // Add a "RequestParams" property to the result, which shows what params were | ||
* // used in the `listObjects` request. This is, obviously, a very contrived | ||
* // example. | ||
* s3Wrapper( | ||
* 'listObjects', | ||
* (data, params) => ({ ...data, RequestParams: params }) | ||
* ); | ||
* | ||
* const result = await s3().listObjects({ Bucket: 'my-bucket' }).promise(); | ||
* | ||
* assert(result.RequestParams.Bucket === 'my-bucket'); | ||
*/ | ||
const awsServiceInterfaceMethodWrapper = (client) => { | ||
const originalFunctions = {}; | ||
return (methodName, dataHandler) => { | ||
originalFunctions[methodName] = client[methodName]; | ||
// eslint-disable-next-line no-param-reassign | ||
client[methodName] = (params = {}, callback) => { | ||
if (callback) { | ||
return originalFunctions[methodName].call( | ||
client, | ||
params, | ||
(err, data) => { | ||
if (err) callback(err); | ||
callback(null, dataHandler(data, params)); | ||
} | ||
); | ||
} | ||
return { | ||
promise: () => originalFunctions[methodName].call(client, params).promise() | ||
.then((data) => dataHandler(data, params)) | ||
}; | ||
}; | ||
}; | ||
}; | ||
exports.inTestMode = () => process.env.NODE_ENV === 'test'; | ||
@@ -137,2 +198,68 @@ | ||
function testAwsClient(Service, options) { | ||
if (Service.serviceIdentifier === 'lambda') { | ||
// This is all a workaround for a Localstack bug where the Lambda event source mapping state | ||
// is not respected and is always 'Enabled'. To work around this, we keep the state of each | ||
// event source mapping internally and override the event source mapping functions to set | ||
// and use the internal states. This can be removed when the Localstack issue is fixed. | ||
const lambdaClient = localStackAwsClient(Service, options); | ||
const eventSourceMappingStates = {}; | ||
const deleteState = (UUID) => { | ||
delete eventSourceMappingStates[UUID]; | ||
}; | ||
const getState = (UUID) => eventSourceMappingStates[UUID]; | ||
const setState = (state, UUID) => { | ||
eventSourceMappingStates[UUID] = state; | ||
}; | ||
const lambdaWrapper = awsServiceInterfaceMethodWrapper(lambdaClient); | ||
lambdaWrapper( | ||
'createEventSourceMapping', | ||
(data, params) => { | ||
setState((isNil(params.Enabled) || params.Enabled) ? 'Enabled' : 'Disabled', data.UUID); | ||
return { ...data, State: getState(data.UUID) }; | ||
} | ||
); | ||
lambdaWrapper( | ||
'deleteEventSourceMapping', | ||
(data, params) => { | ||
deleteState(params.UUID); | ||
return { ...data, State: '' }; | ||
} | ||
); | ||
lambdaWrapper( | ||
'getEventSourceMapping', | ||
(data) => ({ ...data, State: getState(data.UUID) }) | ||
); | ||
lambdaWrapper( | ||
'listEventSourceMappings', | ||
(data) => ({ | ||
...data, | ||
EventSourceMappings: data.EventSourceMappings | ||
.filter((esm) => Object.keys(eventSourceMappingStates).includes(esm.UUID)) | ||
.map((esm) => ({ ...esm, State: getState(esm.UUID) })) | ||
}) | ||
); | ||
lambdaWrapper( | ||
'updateEventSourceMapping', | ||
(data, params) => { | ||
if (!isNil(params.Enabled)) { | ||
const enabled = isNil(params.Enabled) || params.Enabled; | ||
setState(enabled ? 'Enabled' : 'Disabled', data.UUID); | ||
} | ||
return { ...data, State: getState(data.UUID) }; | ||
} | ||
); | ||
return lambdaClient; | ||
} | ||
if (localstackSupportedService(Service)) { | ||
@@ -139,0 +266,0 @@ return localStackAwsClient(Service, options); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
161625
35
3588
6
+ Added@cumulus/checksum@1.13.0(transitive)
+ Added@cumulus/logger@1.13.0(transitive)
- Removed@cumulus/test-data@^1.12.1
- Removed@cumulus/checksum@1.24.0(transitive)
- Removed@cumulus/logger@1.24.0(transitive)
- Removed@cumulus/test-data@1.24.0(transitive)
Updated@cumulus/checksum@1.13.0
Updated@cumulus/logger@1.13.0