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

aws-sdk v3 && aws cdk #3

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
dist/**/**
dist/**/**
node_modules
/.idea
/cdk.out
1 change: 1 addition & 0 deletions .husky/pre-commit
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
npx lint-staged
3 changes: 3 additions & 0 deletions .lintstagedrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"src/**/!(*.spec).ts":
- prettier --write --config ./.prettierrc.yml
- tslint --project ./tsconfig.json -t codeFrame --fix
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
20.11.1
13 changes: 9 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,16 @@ We use three models each with its table
For each model there is a service to execute different operations on the tables.
Write operations are not permitted with the applied IAM Policy. If you want to try these operations out deploy the stack to your own AWS account.

## credentials / security
This demo uses a cognito identity pool for unauthenticated read access to those 3 tables only - which means no write operations will succeed.
## Credentials / Security
This demo uses an AWS Access Key (crated through the CloudFormation stack) to have read access to those 3 tables only -
which means no write operations will succeed.
It's meant for you to play around with it directly in stackblitz or to clone it and test it out on your own stack.

## infrastructure
You can deploy the whole stack trough CloudFormation - see infrastructure.yml for the essential resource definitions and package.json for the scripts involved.
## Infrastructure
You can deploy the whole stack trough `aws-cdk`. Just run `npm run deploy` to deploy the CloudFormation Stack to your
account and get the relevant `Outputs`. Ensure that the `aws-cdk` is able to resolve credentials with enough permissions
to create a stack.
To fill the `DynamoDB` tables with some sample data run `npm run init-data` and then run `npm run start` to see the
output.

# Made with ❤ by [shiftcode.ch](https://www.shiftcode.ch)
3 changes: 3 additions & 0 deletions cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"app": "./dist/app.js"
}
6,846 changes: 4,676 additions & 2,170 deletions package-lock.json

Large diffs are not rendered by default.

60 changes: 27 additions & 33 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@
"dynamoDB",
"demo"
],
"type": "module",
"main": "./dist/index.js",
"engines": {
"node": "8.x"
"node": "^20.11.1"
},
"repository": {
"type": "git",
Expand All @@ -24,41 +25,34 @@
"scripts": {
"lint": "tslint -t codeFrame '{src, test}/**/*.ts' --project ./tsconfig.json --fix",
"build": "tsc",
"start": "ts-node ./src/index.ts",
"deploy": "aws cloudformation deploy --template-file ./infrastructure.yml --stack-name dynamo-easy-demo --capabilities CAPABILITY_NAMED_IAM && npm run generate-outputs",
"generate-outputs": "aws cloudformation --region eu-central-1 describe-stacks --stack-name dynamo-easy-demo --query 'Stacks[0].Outputs' > src/cf-outputs.json"
},
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"linters": {
"{src,test}/**/*.ts": [
"npx prettier --write --config ./.prettierrc.yml",
"npx tslint --fix --project tsconfig.json",
"git add"
]
}
"start": "node --loader ts-node/esm --experimental-json-modules ./src/index.ts",
"init-data": "node --loader ts-node/esm --experimental-json-modules ./src/write-data.ts",
"prepare": "husky",
"deploy": "npm run build && cdk deploy && npm run generate-outputs",
"generate-outputs": "aws cloudformation --region eu-central-1 describe-stacks --stack-name dynamo-easy-demo-LAB --query 'Stacks[0].Outputs' > src/cf-outputs.json",
"iac": "ts-node src/app.ts",
"synth": "npm run build && cdk synth --app ./dist/app.js"
},
"dependencies": {
"@shiftcoders/dynamo-easy": "^5.0.0",
"aws-sdk": "^2.385.0",
"date-fns": "^1.30.1",
"lodash": "^4.17.11",
"reflect-metadata": "^0.1.12",
"tslib": "^1.9.3",
"uuid": "^3.3.2"
"@aws-sdk/client-dynamodb": "^3.556.0",
"@shiftcoders/dynamo-easy": "8.0.0-next.3",
"date-fns": "^3.6.0",
"lodash": "^4.17.21",
"reflect-metadata": "^0.1.13",
"tslib": "^2.6.2",
"uuid": "^9.0.1"
},
"devDependencies": {
"@types/moment-timezone": "^0.5.10",
"@types/node": "^8.10.37",
"husky": "^1.3.1",
"lint-staged": "^8.1.0",
"ts-node": "^7.0.1",
"tslint": "^5.11.0",
"tslint-config-prettier": "^1.17.0",
"typescript": "^3.2.2"
"cdk": "^2.138.0",
"aws-cdk-lib": "^2.138.0",
"constructs": "^10.3.0",
"@types/node": "^20.11.1",
"prettier": "^3.2.5",
"husky": "^9.0.11",
"lint-staged": "^15.2.2",
"ts-node": "^11.0.0-beta.1",
"tslint": "^6.1.3",
"tslint-config-prettier": "^1.18.0",
"typescript": "^5.4.5"
}
}
73 changes: 73 additions & 0 deletions src/app-stack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { CfnOutput, RemovalPolicy, Stack } from 'aws-cdk-lib'
// tslint:disable-next-line:no-submodule-imports
import { AttributeType, BillingMode, ProjectionType, Table } from 'aws-cdk-lib/aws-dynamodb'
// tslint:disable-next-line:no-submodule-imports
import { CfnAccessKey, Effect, PolicyStatement, User } from 'aws-cdk-lib/aws-iam'
import { Construct } from 'constructs'
import { StackConfig } from './app.js'

export class CdkAppStack extends Stack {
constructor(scope: Construct, { stackName }: StackConfig) {
super(scope, stackName)

// The code that defines your stack goes here
const user = new User(this, 'DynamoDBUser', { userName: 'dynamo-user' })

/*
* dynamo tables
*/
const tableEmployee = new Table(this, 'TableEmployee', {
tableName: `${stackName}-employees`,
billingMode: BillingMode.PAY_PER_REQUEST,
partitionKey: { name: 'email', type: AttributeType.STRING },
})

const tableProjects = new Table(this, 'TableProject', {
tableName: `${stackName}-projects`,
billingMode: BillingMode.PAY_PER_REQUEST,
partitionKey: { name: 'clientSlug', type: AttributeType.STRING },
sortKey: { name: 'slug', type: AttributeType.STRING },
})

const tableTimeEntries = new Table(this, 'TimeEntries', {
tableName: `${stackName}-timeEntries`,
billingMode: BillingMode.PAY_PER_REQUEST,
partitionKey: { name: 'monthEmail', type: AttributeType.STRING },
sortKey: { name: 'startDate', type: AttributeType.STRING },
})

const indexName = 'clientProject-unixTsUserId-index'
tableTimeEntries.addGlobalSecondaryIndex({
indexName,
partitionKey: { name: 'clientProject', type: AttributeType.STRING },
sortKey: { name: 'unixTsUserId', type: AttributeType.NUMBER },
projectionType: ProjectionType.ALL,
})

user.addToPrincipalPolicy(
new PolicyStatement({
actions: ['dynamodb:Query', 'dynamodb:Scan', 'dynamodb:GetItem'],
effect: Effect.ALLOW,
resources: [tableEmployee.tableArn, tableProjects.tableArn, tableTimeEntries.tableArn],
})
)

user.addToPrincipalPolicy(
new PolicyStatement({
actions: ['dynamodb:Query'],
effect: Effect.ALLOW,
resources: [`${tableTimeEntries.tableArn}/index/${indexName}`],
})
)

user.applyRemovalPolicy(RemovalPolicy.DESTROY)

const accessKey = new CfnAccessKey(this, 'accessKey', { userName: user.userName })

// tslint:disable:no-unused-expression
new CfnOutput(this, 'CfStackNameServices', { exportName: 'CfStackNameServices', value: stackName })
new CfnOutput(this, 'AwsRegion', { exportName: 'AwsRegion', value: this.region ?? 'unknown' })
new CfnOutput(this, 'accessKeyId', { value: accessKey.ref })
new CfnOutput(this, 'secretAccessKey', { value: accessKey.attrSecretAccessKey })
}
}
11 changes: 11 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { App } from 'aws-cdk-lib'
import { CdkAppStack } from './app-stack.js'

export interface StackConfig {
stackName: string
}

const app = new App()
const config: StackConfig = { stackName: 'dynamo-easy-demo' }
// tslint:disable-next-line:no-unused-expression
new CdkAppStack(app, config)
14 changes: 10 additions & 4 deletions src/cf-outputs.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
[
{
"OutputKey": "CognitoIdentityPoolId",
"OutputValue": "eu-central-1:b473a6fd-5676-4b15-a2ed-7169427cb3a2"
"OutputKey": "accessKeyId",
"OutputValue": "AKIAXYJC77EJ6G5A3UFQ"
},
{
"OutputKey": "secretAccessKey",
"OutputValue": "cBcTnIsnk/hN9jU4f2izh0OzTz7ACTlfHLMvGuLt"
},
{
"OutputKey": "AwsRegion",
"OutputValue": "eu-central-1"
"OutputValue": "eu-central-1",
"ExportName": "AwsRegion"
},
{
"OutputKey": "CfStackNameServices",
"OutputValue": "dynamo-easy-demo"
"OutputValue": "dynamo-easy-demo-LAB",
"ExportName": "CfStackNameServices"
}
]
15 changes: 15 additions & 0 deletions src/create-dynamo-easy-config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { LogLevel, updateDynamoEasyConfig } from '@shiftcoders/dynamo-easy'
import { dynamoEasyDemoTableNameResolver } from './static/table-name-resolver.function.js'
import { fnsDateIsoMapper } from './model/fns-date-iso.mapper.js'
import { createLogReceiver } from './static/my-log-receiver.function.js'

export function createDynamoEasyConfig() {
updateDynamoEasyConfig({
// used to extend the table names with the stack name
tableNameResolver: dynamoEasyDemoTableNameResolver,
// used to define the mapper for all @DateProperty decorated fields
dateMapper: fnsDateIsoMapper,
// used to receive all log statements from dynamo-easy
logReceiver: createLogReceiver(LogLevel.INFO),
})
}
Loading