Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adds a new export from the package for aws-sdk v3 #758

Merged
merged 14 commits into from
Apr 24, 2024
5 changes: 5 additions & 0 deletions .github/workflows/bundler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,20 @@ jobs:
npm install --prefix test/bundlers/parcel-test
npm install --prefix test/bundlers/rollup-test
npm install --prefix test/bundlers/webpack-test
npm install --prefix test/bundlers/esbuild-test

- name: Build
run: |
npm run build --prefix test/bundlers/parcel-test
npm run build --prefix test/bundlers/rollup-test
npm run build --prefix test/bundlers/webpack-test
npm run build:aws --prefix test/bundlers/esbuild-test
npm run build:awsv3 --prefix test/bundlers/esbuild-test

- name: Run bundle
run: |
npm start --prefix test/bundlers/parcel-test
npm start --prefix test/bundlers/rollup-test
npm start --prefix test/bundlers/webpack-test
npm run start:aws --prefix test/bundlers/esbuild-test
npm run start:awsv3 --prefix test/bundlers/esbuild-test
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,4 @@ test/benchmarks/macro/fixtures/*

test/bundlers/**/bundle.js
test/bundlers/parcel-test/.parcel-cache
test/bundlers/esbuild-test/bundle-*.js
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)

## [Unreleased]
### Added
- Added `@opensearch-project/opensearch/aws-v3` import ([758](https://github.com/opensearch-project/opensearch-js/pull/758)).
### Dependencies
- Bumps `@types/node` from 20.12.5 to 20.12.7
### Changed
Expand Down
4 changes: 3 additions & 1 deletion USER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,9 @@ const client = new Client({
```javascript
const { defaultProvider } = require('@aws-sdk/credential-provider-node'); // V3 SDK.
const { Client } = require('@opensearch-project/opensearch');
const { AwsSigv4Signer } = require('@opensearch-project/opensearch/aws');
const { AwsSigv4Signer } = require('@opensearch-project/opensearch/aws-v3'); // use aws-v3 import path if you are using aws-sdk v3
dblock marked this conversation as resolved.
Show resolved Hide resolved
// Unlike the import path in the v2 example above that lazy loads both aws-sdk v3 credential providers & entire aws-sdk v2 if available
// This will only lazy load the aws-sdk v3 credential providers

const client = new Client({
...AwsSigv4Signer({
Expand Down
32 changes: 32 additions & 0 deletions lib/aws/AwsSigv4Signer-sdk-v3.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*
*/

'use strict';
const AwsSigv4SignerError = require('./errors');
const { giveAwsV4Signer, giveAwsCredentialProviderLoader } = require('./shared');

const getAwsSDKCredentialsProvider = async () => {
try {
const awsV3 = await import('@aws-sdk/credential-provider-node');
if (typeof awsV3.defaultProvider === 'function') {
return awsV3.defaultProvider();

Check warning on line 19 in lib/aws/AwsSigv4Signer-sdk-v3.js

View check run for this annotation

Codecov / codecov/patch

lib/aws/AwsSigv4Signer-sdk-v3.js#L18-L19

Added lines #L18 - L19 were not covered by tests
}
} catch (err) {
throw new AwsSigv4SignerError(
"Missing '@aws-sdk/credential-provider-node' module. Install it as a dependency."
);
}
};

const AwsSigv4Signer = giveAwsV4Signer(
giveAwsCredentialProviderLoader(getAwsSDKCredentialsProvider)
);

module.exports = AwsSigv4Signer;
154 changes: 4 additions & 150 deletions lib/aws/AwsSigv4Signer.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,8 @@
*/

'use strict';
const Connection = require('../Connection');
const Transport = require('../Transport');
const aws4 = require('aws4');
const AwsSigv4SignerError = require('./errors');
const crypto = require('crypto');
const { toMs } = Transport.internals;
const { giveAwsV4Signer, giveAwsCredentialProviderLoader } = require('./shared');

const getAwsSDKCredentialsProvider = async () => {
// First try V3
Expand Down Expand Up @@ -49,150 +45,8 @@ const getAwsSDKCredentialsProvider = async () => {
);
};

const awsDefaultCredentialsProvider = () =>
new Promise((resolve, reject) => {
getAwsSDKCredentialsProvider()
.then((provider) => {
provider().then(resolve).catch(reject);
})
.catch((err) => {
reject(err);
});
});
const AwsSigv4Signer = giveAwsV4Signer(
giveAwsCredentialProviderLoader(getAwsSDKCredentialsProvider)
);

function AwsSigv4Signer(opts = {}) {
const credentialsState = {
credentials: null,
};
if (!opts.region) {
throw new AwsSigv4SignerError('Region cannot be empty');
}
if (!opts.service) {
opts.service = 'es';
}
if (typeof opts.getCredentials !== 'function') {
opts.getCredentials = awsDefaultCredentialsProvider;
}

function buildSignedRequestObject(request = {}) {
request.service = opts.service;
request.region = opts.region;
request.headers = request.headers || {};
request.headers['host'] = request.hostname;

if (request['auth']) {
const awssigv4Cred = request['auth'];
credentialsState.credentials = {
accessKeyId: awssigv4Cred.credentials.accessKeyId,
secretAccessKey: awssigv4Cred.credentials.secretAccessKey,
sessionToken: awssigv4Cred.credentials.sessionToken,
};
request.region = awssigv4Cred.region;
request.service = awssigv4Cred.service;
delete request['auth'];
}
const signed = aws4.sign(request, credentialsState.credentials);
signed.headers['x-amz-content-sha256'] = crypto
.createHash('sha256')
.update(request.body || '', 'utf8')
.digest('hex');
return signed;
}

class AwsSigv4SignerConnection extends Connection {
buildRequestObject(params) {
const request = super.buildRequestObject(params);
return buildSignedRequestObject(request);
}
}

class AwsSigv4SignerTransport extends Transport {
request(params, options = {}, callback = undefined) {
// options is optional so if options is a function, it's the callback.
if (typeof options === 'function') {
callback = options;
options = {};
}

const currentCredentials = credentialsState.credentials;
/**
* For AWS SDK V3
* Make sure token will expire no earlier than `expiryBufferMs` milliseconds in the future.
*/
const expiryBufferMs = toMs(options.requestTimeout || this.requestTimeout);

let expired = false;
if (!currentCredentials) {
// Credentials haven't been acquired yet.
expired = true;
}
// AWS SDK V2, needsRefresh should be available.
else if (typeof currentCredentials.needsRefresh === 'function') {
expired = currentCredentials.needsRefresh();
}
// AWS SDK V2, alternative to needsRefresh.
else if (currentCredentials.expired === true) {
expired = true;
}
// AWS SDK V2, alternative to needsRefresh and expired.
else if (currentCredentials.expireTime && currentCredentials.expireTime < new Date()) {
expired = true;
}
// AWS SDK V3, Credentials.expiration is a Date object
else if (
currentCredentials.expiration &&
currentCredentials.expiration.getTime() - Date.now() < expiryBufferMs
) {
expired = true;
}

if (!expired) {
if (typeof callback === 'undefined') {
return super.request(params, options);
}
super.request(params, options, callback);
return;
}

// In AWS SDK V2 Credentials.refreshPromise should be available.
if (currentCredentials && typeof currentCredentials.refreshPromise === 'function') {
if (typeof callback === 'undefined') {
return currentCredentials.refreshPromise().then(() => {
return super.request(params, options);
});
} else {
currentCredentials
.refreshPromise()
.then(() => {
super.request(params, options, callback);
})
.catch(callback);
return;
}
}

// For AWS SDK V3 or when the client has not acquired credentials yet.
if (typeof callback === 'undefined') {
return opts.getCredentials().then((credentials) => {
credentialsState.credentials = credentials;
return super.request(params, options);
});
} else {
opts
.getCredentials()
.then((credentials) => {
credentialsState.credentials = credentials;
super.request(params, options, callback);
})
.catch(callback);
}
}
}

return {
Transport: AwsSigv4SignerTransport,
Connection: AwsSigv4SignerConnection,
buildSignedRequestObject,
};
}
module.exports = AwsSigv4Signer;
19 changes: 19 additions & 0 deletions lib/aws/index-v3.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*
*/

'use strict';

const AwsSigv4Signer = require('./AwsSigv4Signer-sdk-v3');
const AwsSigv4SignerError = require('./errors');

module.exports = {
AwsSigv4Signer,
AwsSigv4SignerError,
};
Loading
Loading