@newrelic/aws-sdk
Advanced tools
Comparing version 0.2.0 to 0.3.0
@@ -0,2 +1,16 @@ | ||
### 0.3.0 (2019-07-18): | ||
* Adds support for DocumentClient API calls to be captured as Datastore segments/metrics. | ||
Supported calls are: `get`, `put`, `update`, `delete`, `query` and `scan`. These will be named according to the underlying DynamoDB operation that is executed. For example: `get` will be named `getItem`. DocumentClient calls not listed above will still be captured as Externals. | ||
* Fixed issue that would prevent multiple DynamoDB instances from being instrumented. | ||
* Replaced `database_name` with `collection` in DynamoDB attributes. | ||
* Moved `name` property to the root of DynamoDB segment description object. | ||
Previously, segments were being incorrectly named `"Datastore/operation/DynamoDB/undefined"`, due to the operation name being misplaced. | ||
### 0.2.0 (2019-02-19): | ||
@@ -3,0 +17,0 @@ |
'use strict' | ||
const OPERATIONS = [ | ||
const DDB_OPERATIONS = [ | ||
'putItem', | ||
@@ -14,23 +14,29 @@ 'getItem', | ||
let dynamoProtoWrapped = false | ||
const DOC_CLIENT_OPERATIONS = [ | ||
'get', | ||
'put', | ||
'update', | ||
'delete', | ||
'query', | ||
'scan' | ||
] | ||
function instrument(shim, AWS) { | ||
shim.setDatastore(shim.DYNAMODB) | ||
// DynamoDB's service API methods are dynamically generated | ||
// in the constructor so we have to wrap the return. | ||
shim.wrapReturn(AWS, 'DynamoDB', function wrapDynamo(shim, fn, name, ddb) { | ||
if (dynamoProtoWrapped) { | ||
return | ||
} | ||
dynamoProtoWrapped = true | ||
shim.setDatastore(shim.DYNAMODB) | ||
shim.recordOperation( | ||
ddb, | ||
OPERATIONS, | ||
function wrapMethod(shim, original, name, args) { | ||
DDB_OPERATIONS, | ||
function wrapMethod(shim, original, operationName, args) { | ||
const params = args[0] | ||
return { | ||
name: operationName, | ||
parameters: { | ||
name, | ||
host: this.endpoint.host, | ||
port_path_or_id: this.endpoint.port, | ||
database_name: args[0].TableName || 'Unknown' | ||
collection: params && params.TableName || 'Unknown' | ||
}, | ||
@@ -43,2 +49,27 @@ callback: shim.LAST, | ||
}) | ||
// DocumentClient's API is predefined so we can instrument the prototype. | ||
// DocumentClient does defer to DynamoDB but it also does enough individual | ||
// steps for the request we want to hide that instrumenting specifically and | ||
// setting to opaque is currently required. | ||
const docClientProto = AWS.DynamoDB.DocumentClient.prototype | ||
shim.recordOperation( | ||
docClientProto, | ||
DOC_CLIENT_OPERATIONS, | ||
function wrapOperation(shim, original, operationName, args) { | ||
const params = args[0] | ||
const dynamoOperation = this.serviceClientOperationsMap[operationName] | ||
return { | ||
name: dynamoOperation, | ||
parameters: { | ||
host: this.service.endpoint.host, | ||
port_path_or_id: this.service.endpoint.port, | ||
collection: params && params.TableName || 'Unknown' | ||
}, | ||
callback: shim.LAST, | ||
opaque: true | ||
} | ||
} | ||
) | ||
} | ||
@@ -45,0 +76,0 @@ |
@@ -17,3 +17,3 @@ 'use strict' | ||
function instrument(shim, AWS) { | ||
shim.setLibrary(shim.SNS || 'SNS') // TODO: remove default after next agent release | ||
shim.setLibrary(shim.SNS) | ||
@@ -20,0 +20,0 @@ shim.wrapReturn(AWS, 'SNS', function wrapSns(shim, original, name, sns) { |
{ | ||
"name": "@newrelic/aws-sdk", | ||
"version": "0.2.0", | ||
"version": "0.3.0", | ||
"description": "New Relic instrumentation of the aws-sdk package.", | ||
@@ -26,3 +26,3 @@ "main": "index.js", | ||
"eslint": "^5.12.1", | ||
"newrelic": "^5.3.0", | ||
"newrelic": "^5.6.0", | ||
"sinon": "^7.2.3", | ||
@@ -29,0 +29,0 @@ "tap": "^12.4.0" |
@@ -27,2 +27,3 @@ 'use strict' | ||
} | ||
const ITEM_DEF = { | ||
@@ -44,2 +45,7 @@ Item: { | ||
} | ||
const DELETE_TABLE = { | ||
TableName: FAKE_TABLE_NAME | ||
} | ||
const QUERY = { | ||
@@ -53,13 +59,51 @@ ExpressionAttributeValues: { | ||
const TESTS = [ | ||
{method: 'createTable', params: TABLE_DEF}, | ||
{method: 'putItem', params: ITEM_DEF}, | ||
{method: 'getItem', params: ITEM}, | ||
{method: 'updateItem', params: ITEM}, | ||
{method: 'scan', params: {TableName: TABLE_NAME}}, | ||
{method: 'query', params: QUERY}, | ||
{method: 'deleteItem', params: ITEM}, | ||
{method: 'deleteTable', params: {TableName: FAKE_TABLE_NAME}} | ||
] | ||
const DOC_PUT_ITEM = { | ||
Item: { | ||
AlbumTitle: 'Somewhat Famous', | ||
Artist: UNIQUE_ARTIST, | ||
SongTitle: 'Call Me Today' | ||
}, | ||
TableName: TABLE_NAME | ||
} | ||
const DOC_ITEM = { | ||
Key: { | ||
Artist: UNIQUE_ARTIST, | ||
SongTitle: 'Call Me Today' | ||
}, | ||
TableName: TABLE_NAME | ||
} | ||
const DOC_QUERY = { | ||
ExpressionAttributeValues: { | ||
':v1': UNIQUE_ARTIST | ||
}, | ||
KeyConditionExpression: 'Artist = :v1', | ||
TableName: TABLE_NAME | ||
} | ||
let tests = null | ||
function createTests(ddb, docClient) { | ||
const composedTests = [ | ||
{api: ddb, method: 'createTable', params: TABLE_DEF, operation: 'createTable'}, | ||
{api: ddb, method: 'putItem', params: ITEM_DEF, operation: 'putItem'}, | ||
{api: ddb, method: 'getItem', params: ITEM, operation: 'getItem'}, | ||
{api: ddb, method: 'updateItem', params: ITEM, operation: 'updateItem'}, | ||
{api: ddb, method: 'scan', params: {TableName: TABLE_NAME}, operation: 'scan'}, | ||
{api: ddb, method: 'query', params: QUERY, operation: 'query'}, | ||
{api: ddb, method: 'deleteItem', params: ITEM, operation: 'deleteItem'}, | ||
{api: ddb, method: 'deleteTable', params: DELETE_TABLE, operation: 'deleteTable'}, | ||
{api: docClient, method: 'put', params: DOC_PUT_ITEM, operation: 'putItem'}, | ||
{api: docClient, method: 'get', params: DOC_ITEM, operation: 'getItem'}, | ||
{api: docClient, method: 'update', params: DOC_ITEM, operation: 'updateItem'}, | ||
{api: docClient, method: 'scan', params: {TableName: TABLE_NAME}, operation: 'scan'}, | ||
{api: docClient, method: 'query', params: DOC_QUERY, operation: 'query'}, | ||
{api: docClient, method: 'delete', params: DOC_ITEM, operation: 'deleteItem'} | ||
] | ||
return composedTests | ||
} | ||
tap.test('DynamoDB', (t) => { | ||
@@ -70,3 +114,2 @@ t.autoend() | ||
let AWS = null | ||
let ddb = null | ||
@@ -80,4 +123,9 @@ t.beforeEach((done) => { | ||
}) | ||
AWS = require('aws-sdk') | ||
ddb = new AWS.DynamoDB({region: 'us-east-1'}) | ||
const ddb = new AWS.DynamoDB({region: 'us-east-1'}) | ||
const docClient = new AWS.DynamoDB.DocumentClient({region: 'us-east-1'}) | ||
tests = createTests(ddb, docClient) | ||
done() | ||
@@ -88,2 +136,7 @@ }) | ||
helper && helper.unload() | ||
helper = null | ||
AWS = null | ||
tests = null | ||
done() | ||
@@ -94,5 +147,5 @@ }) | ||
helper.runInTransaction((tx) => { | ||
async.eachSeries(TESTS, (cfg, cb) => { | ||
async.eachSeries(tests, (cfg, cb) => { | ||
t.comment(`Testing ${cfg.method}`) | ||
ddb[cfg.method](cfg.params, (err) => { | ||
cfg.api[cfg.method](cfg.params, (err) => { | ||
if ( | ||
@@ -118,5 +171,16 @@ err && | ||
const segments = common.checkAWSAttributes(t, tx.trace.root, /^Datastore/) | ||
t.equal(segments.length, 8, 'should have 8 aws datastore segments') | ||
t.equal( | ||
segments.length, | ||
tests.length, | ||
`should have ${tests.length} aws datastore segments` | ||
) | ||
segments.forEach((segment, i) => { | ||
const operation = tests[i].operation | ||
t.equal( | ||
segment.name, | ||
`Datastore/operation/DynamoDB/${operation}`, | ||
'should have operation in segment name' | ||
) | ||
const attrs = segment.attributes.get(common.SEGMENT_DESTINATION) | ||
@@ -126,4 +190,5 @@ t.matches(attrs, { | ||
'port_path_or_id': String, | ||
'database_name': String, | ||
'aws.operation': TESTS[i].method, | ||
'product': 'DynamoDB', | ||
'collection': String, | ||
'aws.operation': operation, | ||
'aws.requestId': String, | ||
@@ -130,0 +195,0 @@ 'aws.region': 'us-east-1', |
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
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
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
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
992
40622
25