Skip to content
This repository has been archived by the owner on Feb 2, 2024. It is now read-only.

feat(aws-sdk): upgrade aws-sdk to v3 #1

Merged
merged 3 commits into from
Aug 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .npmrc
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
@fjandin:registry=https://registry.npmjs.org
@pleo-io:registry=https://npm.pkg.github.com
loglevel = warn
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
# Changelog

## [2.0.0]

### Added
- Upgraded from aws-sdk v2 to client specific v3 libraries (@aws-sdk/client-dynamodb & @aws-sdk/client-secrets-manager)

## [1.7.0]

### Added
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Install

`yarn add @fjandin/config-man`
`yarn add @pleo-io/config-man`

## Initialize

Expand All @@ -15,7 +15,7 @@ Add `[ProjectRoot]/config-man.json` file
```

```ts
import * as configMan from '@fjandin/config-man'
import * as configMan from '@pleo-io/config-man'

configMan.init({
cwd: __dirname,
Expand Down
21 changes: 21 additions & 0 deletions opslevel.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
version: 1
service:
name: Config Man NodeJS Configuration Manager
lifecycle: generally_available
tier:
product:
owner: localisation
language: TypeScript
description: NodeJS Configuration Manager
aliases:
- "@pleo-io/config-man"
tags:
- key: type
value: library
repositories:
- name: pleo-io/config-man
path: "/"
provider: github
tools:
dependencies:
15 changes: 8 additions & 7 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
{
"name": "@fjandin/config-man",
"version": "1.7.0",
"name": "@pleo-io/config-man",
"version": "2.0.0",
"description": "Config manager",
"main": "index.js",
"author": "René Bischoff <[email protected]> (https://github.com/fjandin)",
"homepage": "https://github.com/fjandin/config-man",
"homepage": "https://github.com/pleo-io/config-man",
"readme": "README.md",
"repository": {
"type": "git",
"url": "https://github.com/fjandin/config-man.git"
"url": "https://github.com/pleo-io/config-man.git"
},
"bugs": {
"url": "https://github.com/fjandin/config-man/issues",
"url": "https://github.com/pleo-io/config-man/issues",
"email": "[email protected]"
},
"license": "MIT",
"private": false,
"scripts": {
"peer": "yarn add --peer --frozen-lockfile aws-sdk@^2.895.0",
"peer": "yarn add --peer --frozen-lockfile @aws-sdk/client-secrets-manager@^3.379.0 @aws-sdk/client-dynamodb@^3.379.0",
"lint": "eslint src --ext .ts,.js",
"lint:fix": "eslint src --ext .ts,.js --fix",
"typecheck": "tsc --noEmit",
Expand Down Expand Up @@ -46,6 +46,7 @@
"typescript": "^4.2.4"
},
"peerDependencies": {
"aws-sdk": "^2.895.0"
"@aws-sdk/client-dynamodb": "^3.379.0",
"@aws-sdk/client-secrets-manager": "^3.379.0"
}
}
30 changes: 16 additions & 14 deletions src/lib/config/dynamodb.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,6 @@ const configManJson = {
],
}

const awsSdk = {
config: {
update: () => null,
},
DynamoDB: {
DocumentClient: documentClient,
},
}

// eslint-disable-next-line @typescript-eslint/no-empty-function
function documentClient() {}
documentClient.prototype.scan = (_params: any, callback: any) => {
Expand All @@ -29,13 +20,24 @@ documentClient.prototype.scan = (_params: any, callback: any) => {
})
}

jest.mock('aws-sdk', () => awsSdk)
const mockScanCommand = jest.fn()
// eslint-disable-next-line @typescript-eslint/naming-convention
const mockDynamoDBClientSend = jest.fn().mockResolvedValue({
Items: [{test3: 2}, {'db.unknown': 'dynamo'}],
})

jest.mock('@aws-sdk/client-dynamodb', () => ({
ScanCommand: mockScanCommand,
DynamoDBClient: class MockedDynamoDBClient {
public send = mockDynamoDBClientSend
},
}))

import typeDynamodb from './dynamodb'

describe('Config type dynamo db', () => {
afterAll(() => {
jest.unmock('aws-sdk')
jest.unmock('@aws-sdk/client-dynamodb')
})

it('should get config', async () => {
Expand All @@ -49,13 +51,13 @@ describe('Config type dynamo db', () => {
})

it('should not handle sync (not supported)', async () => {
const result = () =>
typeDynamodb({
const funcToThrow = async () =>
await typeDynamodb({
sync: true,
schema: configManJson.schema,
tableName: 'Test',
region: 'eu-west-1',
})
expect(result).toThrow('This type does not support sync')
await expect(funcToThrow()).rejects.toThrow('This type does not support sync')
})
})
26 changes: 6 additions & 20 deletions src/lib/config/dynamodb.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import * as AWS from 'aws-sdk'
import {DynamoDBClient, ScanCommand} from '@aws-sdk/client-dynamodb'

import {OptionsConfigItemOptions} from './../../index'

export default function getConfigAwsDynamo(
export default async function getConfigAwsDynamo(
options: OptionsConfigItemOptions,
): Promise<{[key: string]: any}> {
if (options.sync) {
Expand All @@ -11,23 +11,9 @@ export default function getConfigAwsDynamo(
const scanParams = {TableName: options.tableName, ConsistentRead: true}
const awsConfig = {region: options.region}

AWS.config.update(awsConfig)
const dynamodb = new AWS.DynamoDB.DocumentClient()
const resultPromise: Promise<AWS.DynamoDB.DocumentClient.ScanOutput> = new Promise(
(resolve, reject) => {
dynamodb.scan(scanParams, (err, data) => {
if (err) {
return reject(err)
}
return resolve(data)
})
},
)
const dynamodb = new DynamoDBClient(awsConfig)
const result = await dynamodb.send(new ScanCommand(scanParams))

return resultPromise.then((result) => {
const config = result.Items
? result.Items.reduce((a, b) => ({...a, [b.key]: b.value}), {})
: {}
return config
})
const config = result.Items ? result.Items.reduce((a, b) => ({...a, ...b}), {}) : {}
return config
}
69 changes: 28 additions & 41 deletions src/lib/config/secret-manager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,15 @@ const configManJson = {
],
}

const mockGetSecretValue = jest.fn()
const mockConfigUpdate = jest.fn()
const mockAws = {
config: {
update: mockConfigUpdate,
},
SecretsManager: class MockedSecretsManager {
public getSecretValue = mockGetSecretValue
},
}
const mockSecretsManagerClientSend = jest.fn()
const mockGetSecretValueCommand = jest.fn()

jest.mock('aws-sdk', () => mockAws)
jest.mock('@aws-sdk/client-secrets-manager', () => ({
GetSecretValueCommand: mockGetSecretValueCommand,
SecretsManagerClient: class MockedSecretsManagerClient {
public send = mockSecretsManagerClientSend
},
}))

import typeSecretManager from './secret-manager'

Expand All @@ -30,15 +27,13 @@ describe('Config type secret manager', () => {
})

it('should get config (boolean and number)', async () => {
mockGetSecretValue.mockImplementation((_params: any, callback: any) =>
callback(null, {
SecretString: JSON.stringify({
test4: 'true',
test3: '1234',
unknown: 'false',
}),
mockSecretsManagerClientSend.mockResolvedValue({
SecretString: JSON.stringify({
test4: 'true',
test3: '1234',
unknown: 'false',
}),
)
})
const result = await typeSecretManager({
sync: false,
schema: configManJson.schema,
Expand All @@ -49,14 +44,12 @@ describe('Config type secret manager', () => {
})

it('should get config (text)', async () => {
mockGetSecretValue.mockImplementation((_params: any, callback: any) =>
callback(null, {
SecretString: JSON.stringify({
'test1.test1a': 'foo',
test3: 1,
}),
mockSecretsManagerClientSend.mockResolvedValue({
SecretString: JSON.stringify({
'test1.test1a': 'foo',
test3: 1,
}),
)
})
const result = await typeSecretManager({
sync: false,
schema: configManJson.schema,
Expand All @@ -70,11 +63,9 @@ describe('Config type secret manager', () => {
const config = JSON.stringify({'test1.test1a': 'foo', test3: 1})
const configBuffer = Buffer.from(config).toString('base64')

mockGetSecretValue.mockImplementation((_params: any, callback: any) =>
callback(null, {
SecretBinary: configBuffer,
}),
)
mockSecretsManagerClientSend.mockResolvedValue({
SecretBinary: configBuffer,
})
const result = await typeSecretManager({
sync: false,
schema: configManJson.schema,
Expand All @@ -85,11 +76,9 @@ describe('Config type secret manager', () => {
})

it('should reject if config is empty', async () => {
mockGetSecretValue.mockImplementation((_params: any, callback: any) =>
callback(null, {
SecretString: undefined,
}),
)
mockSecretsManagerClientSend.mockResolvedValue({
SecretString: undefined,
})
await expect(
typeSecretManager({
sync: false,
Expand All @@ -101,11 +90,9 @@ describe('Config type secret manager', () => {
})

it('should reject if config is malformed', async () => {
mockGetSecretValue.mockImplementation((_params: any, callback: any) =>
callback(null, {
SecretString: '{{}',
}),
)
mockSecretsManagerClientSend.mockResolvedValue({
SecretString: '{{}',
})
await expect(
typeSecretManager({
sync: false,
Expand Down
62 changes: 27 additions & 35 deletions src/lib/config/secret-manager.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import * as AWS from 'aws-sdk'
import {SecretsManagerClient, GetSecretValueCommand} from '@aws-sdk/client-secrets-manager'

import {parseValue} from './../helpers'

import {OptionsConfigItemOptions} from './../../index'

export default function getConfigAwsDynamo(
export default async function getConfigAwsDynamo(
options: OptionsConfigItemOptions,
): Promise<{[key: string]: any}> {
if (options.sync) {
Expand All @@ -15,39 +15,31 @@ export default function getConfigAwsDynamo(
const params = {SecretId: options.secretName}
const awsConfig = {region: options.region}

AWS.config.update(awsConfig)
const client = new AWS.SecretsManager(awsConfig)
const client = new SecretsManagerClient(awsConfig)

return new Promise((resolve, reject) => {
client.getSecretValue(params, (err, data) => {
if (err) {
return reject(err)
}
let secret: string | undefined
if ('SecretString' in data) {
secret = data.SecretString
} else {
const buffer = Buffer.from(data.SecretBinary as string, 'base64')
secret = buffer.toString('ascii')
}
if (!secret) {
return reject(new Error('No secrets returned'))
}
try {
const secretObj = JSON.parse(secret)
const result = schema.reduce((a: {[key: string]: any}, schemaItem) => {
if (secretObj[schemaItem.key]) {
return {
...a,
[schemaItem.key]: parseValue(schemaItem, secretObj[schemaItem.key]),
}
}
return a
}, secretObj)
return resolve(result)
} catch (e) {
return reject(e)
const secretValue = await client.send(new GetSecretValueCommand(params))

let secret: string | undefined
if ('SecretString' in secretValue) {
secret = secretValue.SecretString
} else {
const buffer = secretValue.SecretBinary
? Buffer.from(secretValue.SecretBinary?.toString(), 'base64')
: undefined
secret = buffer?.toString('ascii')
}
if (!secret) {
throw new Error('No secrets returned')
}
const secretObj = JSON.parse(secret)
const result = schema.reduce((a: {[key: string]: any}, schemaItem) => {
if (secretObj[schemaItem.key]) {
return {
...a,
[schemaItem.key]: parseValue(schemaItem, secretObj[schemaItem.key]),
}
})
})
}
return a
}, secretObj)
return result
}