Skip to content

Commit

Permalink
Merge pull request #26 from dvsa/chore/cb2-11281
Browse files Browse the repository at this point in the history
chore(cb2-11281): upgrade aws sdk v2 to v3
  • Loading branch information
owen-corrigan-bjss authored May 8, 2024
2 parents dabb71f + 2aa8470 commit 61c8ed3
Show file tree
Hide file tree
Showing 8 changed files with 3,376 additions and 680 deletions.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"editor.defaultFormatter": "esbenp.prettier-vscode",
"eslint.packageManager": "npm",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
"source.fixAll.eslint": "explicit"
},
"[javascript]": {
"editor.formatOnSave": true
Expand Down
3,877 changes: 3,296 additions & 581 deletions package-lock.json

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,11 @@
"pre-push": "npm run coverage && npm run build && npm run test-i"
},
"dependencies": {
"@aws-sdk/client-dynamodb": "3.567.0",
"@aws-sdk/client-secrets-manager": "3.567.0",
"@aws-sdk/lib-dynamodb": "3.567.0",
"@azure/msal-node": "1.16.0",
"aws-lambda": "1.0.7",
"aws-sdk": "2.1330.0",
"axios": "1.3.4",
"dateformat": "5.0.3",
"knex": "2.4.2",
Expand All @@ -57,6 +59,7 @@
"@types/jest": "^29.4.0",
"@types/node": "^18.14.6",
"@types/supertest": "^2.0.12",
"aws-sdk-client-mock": "4.0.0",
"commitlint-plugin-function-rules": "^1.7.1",
"concurrently": "^7.6.0",
"cross-env": "^7.0.3",
Expand Down
35 changes: 18 additions & 17 deletions src/dynamo/getDynamoRecords.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,25 @@
import * as AWS from 'aws-sdk';
import config from '../config';
import IDynamoRecord, { ResourceType } from './IDynamoRecord';
import { DynamoDBDocumentClient, QueryCommandInput, QueryCommand } from "@aws-sdk/lib-dynamodb"
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";

const dynamo = new AWS.DynamoDB.DocumentClient();
const dynamo = DynamoDBDocumentClient.from(new DynamoDBClient());

export const getDynamoMembers: () => Promise<IDynamoRecord[]> = async () => {
const result = await dynamo
.query({
TableName: config.aws.dynamoTable,
KeyConditionExpression: 'resourceType = :type',
FilterExpression: 'attribute_not_exists(#ttl_key) or #ttl_key = :null',
ExpressionAttributeValues: {
':type': ResourceType.User,
':null': null,
},
ExpressionAttributeNames: {
'#ttl_key': 'ttl',
},
} as AWS.DynamoDB.DocumentClient.QueryInput)
.promise();

return result.Items as IDynamoRecord[];
.send(new QueryCommand(
{
TableName: config.aws.dynamoTable,
KeyConditionExpression: 'resourceType = :type',
FilterExpression: 'attribute_not_exists(#ttl_key) or #ttl_key = :null',
ExpressionAttributeValues: {
':type': ResourceType.User,
':null': null,
},
ExpressionAttributeNames: {
'#ttl_key': 'ttl',
},
} as QueryCommandInput
));
return result.Items as unknown as IDynamoRecord[];
};
14 changes: 7 additions & 7 deletions src/handler.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import * as AWS from 'aws-sdk';
import 'source-map-support/register';
import IMemberDetails from './aad/IMemberDetails';
import { getMemberDetails } from './aad/getMemberDetails';
import config from './config';
import IDynamoRecord, { ResourceType } from './dynamo/IDynamoRecord';
import { getDynamoMembers } from './dynamo/getDynamoRecords';
import logger from './observability/logger';
import { DynamoDBDocumentClient, PutCommand, PutCommandInput } from "@aws-sdk/lib-dynamodb"
import { DynamoDB } from "@aws-sdk/client-dynamodb";

const { NODE_ENV, SERVICE, AWS_PROVIDER_REGION, AWS_PROVIDER_STAGE } = process.env;

logger.info(
`\nRunning Service:\n '${SERVICE}'\n mode: ${NODE_ENV}\n stage: '${AWS_PROVIDER_STAGE}'\n region: '${AWS_PROVIDER_REGION}'\n\n`,
);

const client = new AWS.DynamoDB.DocumentClient();
const client = DynamoDBDocumentClient.from(new DynamoDB());

const handler = async (): Promise<void> => {
logger.info('Function triggered, getting member details...');
Expand All @@ -24,7 +24,7 @@ const handler = async (): Promise<void> => {

logger.info(`Found ${dynamoList.length} existing dynamo records, generating and executing...`);
const stmts = await Promise.allSettled(
generateStatements(azureList, dynamoList).map((stmt) => client.put(stmt).promise()),
generateStatements(azureList, dynamoList).map((stmt) => client.send(new PutCommand(stmt))),
);

stmts.filter((r) => r.status === 'rejected').map((r) => logger.error((<PromiseRejectedResult>r).reason));
Expand All @@ -35,10 +35,10 @@ const handler = async (): Promise<void> => {
function generateStatements(
azureMembers: IMemberDetails[],
dynamoRecords: IDynamoRecord[],
): AWS.DynamoDB.DocumentClient.PutItemInput[] {
): PutCommandInput[] {
const memberMap = azureMembers.map(
(am) =>
<AWS.DynamoDB.DocumentClient.PutItemInput>{
<PutCommandInput>{
TableName: config.aws.dynamoTable,
Item: <IDynamoRecord>{
resourceType: ResourceType.User,
Expand All @@ -60,7 +60,7 @@ function generateStatements(
.filter((dr) => !azureMembers.some((am) => am.id === dr.resourceKey))
.map(
(dr) =>
<AWS.DynamoDB.DocumentClient.PutItemInput>{
<PutCommandInput>{
TableName: config.aws.dynamoTable,
Item: <IDynamoRecord>{
resourceType: dr.resourceType,
Expand Down
4 changes: 2 additions & 2 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { SecretsManager } from 'aws-sdk';
import { SecretsManager } from '@aws-sdk/client-secrets-manager';
import logger from '../observability/logger';

const getSecret = async (secretName: string): Promise<string> => {
logger.debug('getSecret starting.');
const secretsManager = new SecretsManager();
const secretValue = await secretsManager.getSecretValue({ SecretId: secretName }).promise();
const secretValue = await secretsManager.getSecretValue({ SecretId: secretName });
logger.debug('getSecret finishing.');
return secretValue.SecretString;
};
Expand Down
60 changes: 23 additions & 37 deletions tests/unit/getDynamoRecords.test.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,42 @@
import { DynamoDBDocumentClient, QueryCommandOutput, QueryCommand } from '@aws-sdk/lib-dynamodb';
import { mockClient } from 'aws-sdk-client-mock';
import config from '../../src/config';
import { getDynamoMembers } from '../../src/dynamo/getDynamoRecords';
import { ResourceType } from '../../src/dynamo/IDynamoRecord';
import { getDynamoMembers } from '../../src/dynamo/getDynamoRecords';

// has to be 'var' as jest "hoists" execution behind the scenes and let/const cause errors
/* tslint:disable */
var mockDynamoQuery: jest.Mock;
/* tslint:enable */

jest.mock('aws-sdk', () => {
mockDynamoQuery = jest.fn().mockImplementation(() => ({
promise: jest.fn().mockResolvedValue({
Items: [
{
resourceType: ResourceType.User,
resourceKey: '6adbf131-c6c2-4bc6-b1e9-b62f812bed29',
name: 'test user',
email: '[email protected]',
},
{
resourceType: ResourceType.User,
resourceKey: '7d9e8e38-78d5-46ad-9fd0-6adad882161b',
name: 'test user 2',
email: '[email protected]',
},
],
} as AWS.DynamoDB.DocumentClient.QueryOutput),
}));
class FakeDynamoDb {
query = mockDynamoQuery;
}

const AWS = {
DynamoDB: {
DocumentClient: FakeDynamoDb,
const mockItems = {
Items: [
{
resourceType: ResourceType.User,
resourceKey: '6adbf131-c6c2-4bc6-b1e9-b62f812bed29',
name: 'test user',
email: '[email protected]',
},
{
resourceType: ResourceType.User,
resourceKey: '7d9e8e38-78d5-46ad-9fd0-6adad882161b',
name: 'test user 2',
email: '[email protected]',
},
};
],
}

return AWS;
});
const client = mockClient(DynamoDBDocumentClient);

describe('getDynamoMembers', () => {
beforeEach(() => {
config.aws.dynamoTable = 'testTable';
client.reset()
});

afterEach(() => {
jest.clearAllMocks();
});

it('should call dynamo query', async () => {
client.on(QueryCommand).resolves(mockItems as unknown as QueryCommandOutput)
await getDynamoMembers();
expect(mockDynamoQuery).toBeCalledWith({
expect(client.call(0).firstArg.input).toEqual({
TableName: 'testTable',
KeyConditionExpression: 'resourceType = :type',
FilterExpression: 'attribute_not_exists(#ttl_key) or #ttl_key = :null',
Expand All @@ -61,6 +47,6 @@ describe('getDynamoMembers', () => {
ExpressionAttributeNames: {
'#ttl_key': 'ttl',
},
} as AWS.DynamoDB.DocumentClient.QueryInput);
})
});
});
59 changes: 25 additions & 34 deletions tests/unit/handler.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { DynamoDBDocumentClient, PutCommand } from "@aws-sdk/lib-dynamodb";
import { mockClient } from 'aws-sdk-client-mock';
import IMemberDetails, { MemberType } from '../../src/aad/IMemberDetails';
import IDynamoRecord, { ResourceType } from '../../src/dynamo/IDynamoRecord';
import { handler } from '../../src/handler';
Expand All @@ -6,7 +8,6 @@ import { handler } from '../../src/handler';
/* tslint:disable */
var mockMemberDetails: jest.Mock;
var mockDynamoRecords: jest.Mock;
var mockDynamoPut: jest.Mock;
var emptyMemberDetails = true;
var emptyDynamoDetails = true;
/* tslint:enable */
Expand All @@ -16,14 +17,14 @@ jest.mock('../../src/aad/getMemberDetails', () => {
emptyMemberDetails
? new Array<IMemberDetails>()
: [
{
'@odata.type': MemberType.User,
id: '5afcf0b5-fb7f-4b83-98cc-851a8b27025c',
displayName: 'Test User',
mail: '[email protected]',
userPrincipalName: '[email protected]',
} as IMemberDetails,
],
{
'@odata.type': MemberType.User,
id: '5afcf0b5-fb7f-4b83-98cc-851a8b27025c',
displayName: 'Test User',
mail: '[email protected]',
userPrincipalName: '[email protected]',
} as IMemberDetails,
],
);
return { getMemberDetails: mockMemberDetails };
});
Expand All @@ -33,35 +34,23 @@ jest.mock('../../src/dynamo/getDynamoRecords', () => {
emptyDynamoDetails
? new Array<IDynamoRecord>()
: [
{
resourceType: ResourceType.User,
resourceKey: '932a98cb-8946-4796-8291-c7bcf4badb50',
email: '[email protected]',
name: 'Deleted User',
} as IDynamoRecord,
],
{
resourceType: ResourceType.User,
resourceKey: '932a98cb-8946-4796-8291-c7bcf4badb50',
email: '[email protected]',
name: 'Deleted User',
} as IDynamoRecord,
],
);
return { getDynamoMembers: mockDynamoRecords };
});

jest.mock('aws-sdk', () => {
mockDynamoPut = jest.fn().mockImplementation(() => ({
promise: jest.fn().mockResolvedValue({} as AWS.DynamoDB.DocumentClient.PutItemOutput),
}));
class FakeDynamoDb {
put = mockDynamoPut;
}

const AWS = {
DynamoDB: {
DocumentClient: FakeDynamoDb,
},
};

return AWS;
});
const client = mockClient(DynamoDBDocumentClient)

describe('Handler', () => {
beforeEach(() => {
client.reset();
})
afterEach(() => {
jest.clearAllMocks();
emptyMemberDetails = true;
Expand All @@ -79,8 +68,9 @@ describe('Handler', () => {

it('should run a dynamo put for an active record', async () => {
emptyMemberDetails = false;
client.on(PutCommand).resolves({});
await handler();
expect(mockDynamoPut).toBeCalledWith({
expect(client.call(0).firstArg.input).toEqual({
TableName: '',
Item: {
resourceType: ResourceType.User,
Expand All @@ -93,8 +83,9 @@ describe('Handler', () => {

it('should run a dynamo put with a ttl for an inactive record', async () => {
emptyDynamoDetails = false;
client.on(PutCommand).resolves({});
await handler();
expect(mockDynamoPut).toHaveBeenCalledWith(
expect(client.call(1).firstArg.input).toEqual(
expect.objectContaining({
TableName: '',
Item: {
Expand Down

0 comments on commit 61c8ed3

Please sign in to comment.