├── .dockerignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── cdk ├── .gitignore ├── .npmignore ├── README.md ├── bin │ └── cdk.ts ├── cdk.json ├── jest.config.js ├── lib │ ├── DDBStack.ts │ └── FargateServiceStack.ts ├── package-lock.json ├── package.json └── tsconfig.json ├── cmd └── main.go ├── docker-compose.yml ├── go.mod ├── go.sum └── pkg ├── api └── postservice_api.go ├── model └── post.go ├── service ├── postservice.go └── postservice_test.go └── utils └── utils.go /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | cdk 3 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | ## Code of Conduct 2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 4 | opensource-codeofconduct@amazon.com with any additional questions or comments. 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing Guidelines 2 | 3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional 4 | documentation, we greatly value feedback and contributions from our community. 5 | 6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary 7 | information to effectively respond to your bug report or contribution. 8 | 9 | 10 | ## Reporting Bugs/Feature Requests 11 | 12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features. 13 | 14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already 15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful: 16 | 17 | * A reproducible test case or series of steps 18 | * The version of our code being used 19 | * Any modifications you've made relevant to the bug 20 | * Anything unusual about your environment or deployment 21 | 22 | 23 | ## Contributing via Pull Requests 24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that: 25 | 26 | 1. You are working against the latest source on the *main* branch. 27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already. 28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted. 29 | 30 | To send us a pull request, please: 31 | 32 | 1. Fork the repository. 33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change. 34 | 3. Ensure local tests pass. 35 | 4. Commit to your fork using clear commit messages. 36 | 5. Send us a pull request, answering any default questions in the pull request interface. 37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation. 38 | 39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and 40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/). 41 | 42 | 43 | ## Finding contributions to work on 44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start. 45 | 46 | 47 | ## Code of Conduct 48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct). 49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact 50 | opensource-codeofconduct@amazon.com with any additional questions or comments. 51 | 52 | 53 | ## Security issue notifications 54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue. 55 | 56 | 57 | ## Licensing 58 | 59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution. 60 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.20 AS build-image 2 | 3 | WORKDIR /app 4 | 5 | COPY go.mod go.sum ./ 6 | RUN GOPROXY=direct go mod download 7 | 8 | COPY /cmd/*.go ./ 9 | COPY /pkg ./pkg 10 | 11 | RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" main.go 12 | 13 | 14 | FROM gcr.io/distroless/base 15 | 16 | WORKDIR /app 17 | 18 | COPY --from=build-image /app/main ./ 19 | 20 | ENTRYPOINT ["/app/main"] 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software is furnished to do so. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 15 | 16 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test run deploy destroy build_docker run_docker 2 | 3 | .EXPORT_ALL_VARIABLES: 4 | AWS_PROFILE = default 5 | AWS_REGION = us-east-1 6 | 7 | run: 8 | cd cmd && go run main.go 9 | 10 | build_docker: 11 | docker build -t ddb-local-fargate . 12 | 13 | run_docker: build_docker 14 | docker run -it -e PARAM1=test1 -p 5050:5050 -e AWS_PROFILE=${AWS_PROFILE} -v ${HOME}/.aws:/root/.aws ddb-local-fargate 15 | 16 | test: 17 | docker-compose up --detach; 18 | cd pkg/service && go test; 19 | docker-compose down; 20 | 21 | deploy: 22 | cd cdk;\ 23 | cdk deploy '*' 24 | 25 | destroy: 26 | cd cdk;\ 27 | cdk destroy \* -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Testing webservice using DynamoDB Local 2 | 3 | This repository contains sample webservice written in Go and deployed with CDK. 4 | 5 | It demonstrates: 6 | * how to build a simple webservice using Go 7 | * how to build the webservice Docker container using multistage build process 8 | * how to deploy the webservice to AWS using CDK as a Fargate service 9 | * how to test the webservice using DynamoDB Local 10 | 11 | ### Prerequisites 12 | The following components need to be installed in order to build and run the code: 13 | * Node.JS 14 | * AWS CDK 15 | * Go 16 | * Docker 17 | 18 | The code was tested using Node.JS 12.18.2, CDK 1.110.0, Go 1.16.5, Docker 20.10.7 19 | 20 | 21 | ### Webservice endpoints 22 | * Add post `POST /post` 23 | 24 | Request format: 25 | ```json 26 | { 27 | "id": "1", 28 | "title": "my post #1", 29 | "content": "here is the post content", 30 | "status": "posted" 31 | } 32 | ```` 33 | * Get post by post number `GET /post/` 34 | 35 | ## Commands 36 | 37 | You need to run the command to install NodeJS modules: `cd cdk; npm install` 38 | 39 | ### Deploy and run the webservice on AWS 40 | 41 | 1. Deploy resources to AWS running `make deploy` 42 | 2. Get DNS name of ALB deployed and call the service using Postman or other HTTP client of your choice. 43 | 3. Remove resources running `make destroy` 44 | 45 | ### Run as a local application 46 | 47 | Run `make run` command to run the service as a local application connected to DynamoDB in AWS (the table should be created before). 48 | 49 | ### Build Docker container 50 | 51 | Run `make build_docker` to build Docker container. 52 | 53 | ### Run in Docker container 54 | 55 | Run `make run_docker` to build Docker container and run locally 56 | 57 | ### Run unit test locally using DynamoDB Local 58 | 59 | Run `make test` to run unit tests of the service using DynamoDB Local database. 60 | 61 | ### Clean resources 62 | 63 | Run `make destroy` to undeploy AWS resources. 64 | 65 | ## Security 66 | 67 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information. 68 | 69 | ## License 70 | 71 | This code is licensed under the MIT-0 License. See the LICENSE file. -------------------------------------------------------------------------------- /cdk/.gitignore: -------------------------------------------------------------------------------- 1 | *.js 2 | !jest.config.js 3 | *.d.ts 4 | node_modules 5 | 6 | # CDK asset staging directory 7 | .cdk.staging 8 | cdk.out 9 | -------------------------------------------------------------------------------- /cdk/.npmignore: -------------------------------------------------------------------------------- 1 | *.ts 2 | !*.d.ts 3 | 4 | # CDK asset staging directory 5 | .cdk.staging 6 | cdk.out 7 | -------------------------------------------------------------------------------- /cdk/README.md: -------------------------------------------------------------------------------- 1 | # Welcome to your CDK TypeScript project! 2 | 3 | This is a blank project for TypeScript development with CDK. 4 | 5 | The `cdk.json` file tells the CDK Toolkit how to execute your app. 6 | 7 | ## Useful commands 8 | 9 | * `npm run build` compile typescript to js 10 | * `npm run watch` watch for changes and compile 11 | * `npm run test` perform the jest unit tests 12 | * `cdk deploy` deploy this stack to your default AWS account/region 13 | * `cdk diff` compare deployed stack with current state 14 | * `cdk synth` emits the synthesized CloudFormation template 15 | -------------------------------------------------------------------------------- /cdk/bin/cdk.ts: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 4 | // SPDX-License-Identifier: MIT-0 5 | 6 | import 'source-map-support/register'; 7 | import * as cdk from '@aws-cdk/core'; 8 | import { DdbLocalTestStack } from '../lib/DDBStack'; 9 | import {FargateServiceStack} from "../lib/FargateServiceStack"; 10 | 11 | const app = new cdk.App(); 12 | new DdbLocalTestStack(app, 'ddblocal-ddb-stack'); 13 | new FargateServiceStack(app, 'ddblocal-fargate-stack'); 14 | 15 | -------------------------------------------------------------------------------- /cdk/cdk.json: -------------------------------------------------------------------------------- 1 | { 2 | "app": "npx ts-node --prefer-ts-exts bin/cdk.ts", 3 | "context": { 4 | "@aws-cdk/aws-apigateway:usagePlanKeyOrderInsensitiveId": true, 5 | "@aws-cdk/core:enableStackNameDuplicates": "true", 6 | "aws-cdk:enableDiffNoFail": "true", 7 | "@aws-cdk/core:stackRelativeExports": "true", 8 | "@aws-cdk/aws-ecr-assets:dockerIgnoreSupport": true, 9 | "@aws-cdk/aws-secretsmanager:parseOwnedSecretName": true, 10 | "@aws-cdk/aws-kms:defaultKeyPolicies": true, 11 | "@aws-cdk/aws-s3:grantWriteWithoutAcl": true, 12 | "@aws-cdk/aws-ecs-patterns:removeDefaultDesiredCount": true, 13 | "@aws-cdk/aws-rds:lowercaseDbIdentifier": true, 14 | "@aws-cdk/aws-efs:defaultEncryptionAtRest": true 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /cdk/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | roots: ['/test'], 3 | testMatch: ['**/*.test.ts'], 4 | transform: { 5 | '^.+\\.tsx?$': 'ts-jest' 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /cdk/lib/DDBStack.ts: -------------------------------------------------------------------------------- 1 | import * as cdk from '@aws-cdk/core'; 2 | import * as dynamodb from '@aws-cdk/aws-dynamodb'; 3 | import * as ssm from '@aws-cdk/aws-ssm' 4 | 5 | export class DdbLocalTestStack extends cdk.Stack { 6 | constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { 7 | super(scope, id, props); 8 | 9 | const postTable = new dynamodb.Table(this, 'PostTable', { 10 | tableName: "blog-post-table", 11 | partitionKey: { name: 'id', type: dynamodb.AttributeType.STRING }, 12 | billingMode: dynamodb.BillingMode.PAY_PER_REQUEST, 13 | //removalPolicy: cdk.RemovalPolicy.RETAIN, 14 | //pointInTimeRecovery: true 15 | }); 16 | 17 | const ddbNameParam = new ssm.StringParameter(this, 'DDBNameParam', { 18 | description: 'DDB Table Name', 19 | parameterName: "/ddblocal/tableName", 20 | stringValue: postTable.tableName, 21 | tier: ssm.ParameterTier.STANDARD, 22 | }); 23 | 24 | const ddbArnParam = new ssm.StringParameter(this, 'DDBArnParam', { 25 | description: 'DDB Table Arn', 26 | parameterName: "/ddblocal/tableArn", 27 | stringValue: postTable.tableArn, 28 | tier: ssm.ParameterTier.STANDARD, 29 | }); 30 | 31 | 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /cdk/lib/FargateServiceStack.ts: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | import * as cdk from '@aws-cdk/core'; 5 | import * as path from 'path'; 6 | import * as ec2 from '@aws-cdk/aws-ec2'; 7 | import * as ecs from '@aws-cdk/aws-ecs'; 8 | import * as iam from '@aws-cdk/aws-iam' 9 | import * as ssm from '@aws-cdk/aws-ssm' 10 | import { LogGroup } from '@aws-cdk/aws-logs' 11 | import { DockerImageAsset } from '@aws-cdk/aws-ecr-assets'; 12 | import { ContainerImage, FargatePlatformVersion } from '@aws-cdk/aws-ecs'; 13 | import * as ecs_patterns from "@aws-cdk/aws-ecs-patterns"; 14 | 15 | 16 | 17 | export class FargateServiceStack extends cdk.Stack { 18 | constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) { 19 | super(scope, id, props); 20 | 21 | const asset = new DockerImageAsset(this, 'ddb-local-image', { 22 | directory: path.join(__dirname, "..", ".."), 23 | }); 24 | 25 | const vpc = new ec2.Vpc(this, "EcsVpc", { 26 | maxAzs: 3 // Default is all AZs in region 27 | }); 28 | 29 | const cluster = new ecs.Cluster(this, "TestCluster", { 30 | vpc: vpc, 31 | clusterName: "ddb-local-cluster", 32 | containerInsights: false 33 | }); 34 | 35 | const logGroup = new LogGroup(this, "FargateLogGroup", { 36 | logGroupName: "/ecs/ddb-local-service" 37 | }) 38 | 39 | const taskDef = new ecs.FargateTaskDefinition(this, "MyTask", { 40 | cpu: 512, 41 | memoryLimitMiB: 1024, 42 | }) 43 | 44 | const container = new ecs.ContainerDefinition(this, "MyContainer", { 45 | image: ContainerImage.fromDockerImageAsset(asset), 46 | taskDefinition: taskDef, 47 | environment: { 48 | PARAM1: "test1" 49 | }, 50 | logging: new ecs.AwsLogDriver({ 51 | logGroup: logGroup, 52 | streamPrefix: `ddb-local-service`, 53 | }) 54 | } 55 | ) 56 | 57 | const service = new ecs_patterns.ApplicationLoadBalancedFargateService(this, "MyFargateService", { 58 | cluster: cluster, // Required 59 | cpu: 512, // Default is 256 60 | desiredCount: 2, // Default is 1 61 | taskImageOptions: { image: ecs.RepositoryImage.fromDockerImageAsset(asset) }, 62 | memoryLimitMiB: 2048, // Default is 512 63 | publicLoadBalancer: true // Default is false 64 | }); 65 | 66 | 67 | const DDB_TABLE_ARN = ssm.StringParameter.valueForStringParameter(this, "/ddblocal/tableArn") 68 | 69 | 70 | service.taskDefinition.addToTaskRolePolicy(new iam.PolicyStatement({ 71 | actions:["dynamodb:PutItem","dynamodb:GetItem"], 72 | resources:[DDB_TABLE_ARN], 73 | effect: iam.Effect.ALLOW 74 | })) 75 | 76 | const serviceDNS = new ssm.StringParameter( this,"serviceDNS",{ 77 | parameterName: "local-ddb-service-dns", 78 | stringValue: service.loadBalancer.loadBalancerDnsName 79 | }) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /cdk/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cdk", 3 | "version": "0.1.0", 4 | "bin": { 5 | "cdk": "bin/cdk.js" 6 | }, 7 | "scripts": { 8 | "build": "tsc", 9 | "watch": "tsc -w", 10 | "test": "jest", 11 | "cdk": "cdk" 12 | }, 13 | "devDependencies": { 14 | "@aws-cdk/assert": "^1.195.0", 15 | "@types/jest": "^26.0.10", 16 | "@types/node": "10.17.27", 17 | "jest": "^26.4.2", 18 | "ts-jest": "^26.2.0", 19 | "aws-cdk": "^1.203.0", 20 | "ts-node": "^9.0.0", 21 | "typescript": "~3.9.7" 22 | }, 23 | "dependencies": { 24 | "@aws-cdk/aws-dynamodb": "^1.195.0", 25 | "@aws-cdk/aws-ecs": "^1.195.0", 26 | "@aws-cdk/aws-ecs-patterns": "^1.195.0", 27 | "@aws-cdk/core": "^1.195.0", 28 | "source-map-support": "^0.5.16" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /cdk/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2018", 4 | "module": "commonjs", 5 | "lib": ["es2018"], 6 | "declaration": true, 7 | "strict": true, 8 | "noImplicitAny": true, 9 | "strictNullChecks": true, 10 | "noImplicitThis": true, 11 | "alwaysStrict": true, 12 | "noUnusedLocals": false, 13 | "noUnusedParameters": false, 14 | "noImplicitReturns": true, 15 | "noFallthroughCasesInSwitch": false, 16 | "inlineSourceMap": true, 17 | "inlineSources": true, 18 | "experimentalDecorators": true, 19 | "strictPropertyInitialization": false, 20 | "typeRoots": ["./node_modules/@types"] 21 | }, 22 | "exclude": ["cdk.out"] 23 | } 24 | -------------------------------------------------------------------------------- /cmd/main.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | package main 5 | 6 | import ( 7 | "context" 8 | "dynamodb-local-test/pkg/api" 9 | "dynamodb-local-test/pkg/model" 10 | "dynamodb-local-test/pkg/service" 11 | "dynamodb-local-test/pkg/utils" 12 | "fmt" 13 | "log" 14 | "os" 15 | 16 | "github.com/aws/aws-sdk-go-v2/aws" 17 | "github.com/aws/aws-sdk-go-v2/config" 18 | "github.com/aws/aws-sdk-go-v2/service/dynamodb" 19 | "github.com/gin-gonic/gin" 20 | ) 21 | 22 | var awsCfg aws.Config 23 | var ctx = context.TODO() 24 | 25 | func main() { 26 | fmt.Println("Starting Server") 27 | 28 | post := model.Post{} 29 | post.Id = "1" 30 | post.Title = "my post" 31 | post.Content = "post content" 32 | post.Status = "posted" 33 | post.CreateTimestamp = utils.GetLocalTimestampNow() 34 | post.LastUpdateTimestamp = utils.GetLocalTimestampNow() 35 | 36 | awsProfile, ok := os.LookupEnv("AWS_PROFILE") 37 | log.Printf("AWS_PROFILE: %s", awsProfile) 38 | var err error 39 | var ps service.PostService 40 | 41 | if ok { 42 | log.Printf("Use AWS profile") 43 | awsCfg, err = config.LoadDefaultConfig(ctx, 44 | config.WithSharedConfigProfile(awsProfile), 45 | ) 46 | if err != nil { 47 | log.Fatalf("Error loading profile %v", err) 48 | } 49 | 50 | } else { 51 | log.Printf("Use default role") 52 | awsCfg, err = config.LoadDefaultConfig(ctx) 53 | } 54 | 55 | ddbSvc := dynamodb.NewFromConfig(awsCfg) 56 | log.Printf("DDB service created") 57 | 58 | ps, _ = service.NewDdbPostService(ddbSvc) 59 | 60 | postServiceHandlers := api.PostServiceApi{PostService: ps} 61 | 62 | router := gin.Default() 63 | 64 | router.GET("/health", func(c *gin.Context) { 65 | c.JSON(200, gin.H{ 66 | "message": "OK", 67 | }) 68 | }) 69 | 70 | router.GET("/post/:id", postServiceHandlers.PostServiceGetApi) 71 | 72 | router.POST("/post", postServiceHandlers.PostServiceAddApi) 73 | 74 | _ = router.Run(":80") 75 | 76 | } 77 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.8' 2 | services: 3 | dynamodb-local: 4 | command: "-jar DynamoDBLocal.jar -inMemory" 5 | image: "amazon/dynamodb-local:latest" 6 | container_name: dynamodb-local 7 | ports: 8 | - "8000:8000" 9 | working_dir: /home/dynamodblocal -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module dynamodb-local-test 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/aws/aws-sdk-go-v2 v1.18.1 // indirect 7 | github.com/aws/aws-sdk-go-v2/config v1.18.27 // indirect 8 | github.com/aws/aws-sdk-go-v2/credentials v1.13.26 // indirect 9 | github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.10.29 // indirect 10 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.4 // indirect 11 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.34 // indirect 12 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.28 // indirect 13 | github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35 // indirect 14 | github.com/aws/aws-sdk-go-v2/service/dynamodb v1.19.11 // indirect 15 | github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.14.14 // indirect 16 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 // indirect 17 | github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.28 // indirect 18 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.28 // indirect 19 | github.com/aws/aws-sdk-go-v2/service/sso v1.12.12 // indirect 20 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.12 // indirect 21 | github.com/aws/aws-sdk-go-v2/service/sts v1.19.2 // indirect 22 | github.com/aws/smithy-go v1.13.5 // indirect 23 | github.com/bytedance/sonic v1.9.1 // indirect 24 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect 25 | github.com/gabriel-vasile/mimetype v1.4.2 // indirect 26 | github.com/gin-contrib/sse v0.1.0 // indirect 27 | github.com/gin-gonic/gin v1.9.1 // indirect 28 | github.com/go-playground/locales v0.14.1 // indirect 29 | github.com/go-playground/universal-translator v0.18.1 // indirect 30 | github.com/go-playground/validator/v10 v10.14.0 // indirect 31 | github.com/goccy/go-json v0.10.2 // indirect 32 | github.com/jmespath/go-jmespath v0.4.0 // indirect 33 | github.com/json-iterator/go v1.1.12 // indirect 34 | github.com/klauspost/cpuid/v2 v2.2.4 // indirect 35 | github.com/leodido/go-urn v1.2.4 // indirect 36 | github.com/mattn/go-isatty v0.0.19 // indirect 37 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 38 | github.com/modern-go/reflect2 v1.0.2 // indirect 39 | github.com/pelletier/go-toml/v2 v2.0.8 // indirect 40 | github.com/pkg/errors v0.9.1 // indirect 41 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 42 | github.com/ugorji/go/codec v1.2.11 // indirect 43 | golang.org/x/arch v0.3.0 // indirect 44 | golang.org/x/crypto v0.9.0 // indirect 45 | golang.org/x/net v0.10.0 // indirect 46 | golang.org/x/sys v0.8.0 // indirect 47 | golang.org/x/text v0.9.0 // indirect 48 | google.golang.org/protobuf v1.30.0 // indirect 49 | gopkg.in/yaml.v3 v3.0.1 // indirect 50 | ) 51 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/aws/aws-sdk-go-v2 v1.18.1 h1:+tefE750oAb7ZQGzla6bLkOwfcQCEtC5y2RqoqCeqKo= 2 | github.com/aws/aws-sdk-go-v2 v1.18.1/go.mod h1:uzbQtefpm44goOPmdKyAlXSNcwlRgF3ePWVW6EtJvvw= 3 | github.com/aws/aws-sdk-go-v2/config v1.18.27 h1:Az9uLwmssTE6OGTpsFqOnaGpLnKDqNYOJzWuC6UAYzA= 4 | github.com/aws/aws-sdk-go-v2/config v1.18.27/go.mod h1:0My+YgmkGxeqjXZb5BYme5pc4drjTnM+x1GJ3zv42Nw= 5 | github.com/aws/aws-sdk-go-v2/credentials v1.13.26 h1:qmU+yhKmOCyujmuPY7tf5MxR/RKyZrOPO3V4DobiTUk= 6 | github.com/aws/aws-sdk-go-v2/credentials v1.13.26/go.mod h1:GoXt2YC8jHUBbA4jr+W3JiemnIbkXOfxSXcisUsZ3os= 7 | github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.10.29 h1:ubvuPhqFtQJEWE6PN2J9+ScHcH3IvwcVWzs5b4lgSWY= 8 | github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue v1.10.29/go.mod h1:Rkt1mQV8uDNkhIY5IfFSKJIGRLsz8fp6EtEDU2BWzJA= 9 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.4 h1:LxK/bitrAr4lnh9LnIS6i7zWbCOdMsfzKFBI6LUCS0I= 10 | github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.13.4/go.mod h1:E1hLXN/BL2e6YizK1zFlYd8vsfi2GTjbjBazinMmeaM= 11 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.34 h1:A5UqQEmPaCFpedKouS4v+dHCTUo2sKqhoKO9U5kxyWo= 12 | github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.34/go.mod h1:wZpTEecJe0Btj3IYnDx/VlUzor9wm3fJHyvLpQF0VwY= 13 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.28 h1:srIVS45eQuewqz6fKKu6ZGXaq6FuFg5NzgQBAM6g8Y4= 14 | github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.28/go.mod h1:7VRpKQQedkfIEXb4k52I7swUnZP0wohVajJMRn3vsUw= 15 | github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35 h1:LWA+3kDM8ly001vJ1X1waCuLJdtTl48gwkPKWy9sosI= 16 | github.com/aws/aws-sdk-go-v2/internal/ini v1.3.35/go.mod h1:0Eg1YjxE0Bhn56lx+SHJwCzhW+2JGtizsrx+lCqrfm0= 17 | github.com/aws/aws-sdk-go-v2/service/dynamodb v1.19.11 h1:tLTGNAsazbfjfjW1k/i43kyCcyTTTTFaD93H7JbSbbs= 18 | github.com/aws/aws-sdk-go-v2/service/dynamodb v1.19.11/go.mod h1:W1oiFegjVosgjIwb2Vv45jiCQT1ee8x85u8EyZRYLes= 19 | github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.14.14 h1:T9FMVvefm8TWwyVYpFVohP2iLM1QnqAB0m/qksVqs+w= 20 | github.com/aws/aws-sdk-go-v2/service/dynamodbstreams v1.14.14/go.mod h1:31kKOlv+a+XLCu0wDK8BeeCOjdcZihEoQcLiPIZoyw4= 21 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11 h1:y2+VQzC6Zh2ojtV2LoC0MNwHWc6qXv/j2vrQtlftkdA= 22 | github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.11/go.mod h1:iV4q2hsqtNECrfmlXyord9u4zyuFEJX9eLgLpSPzWA8= 23 | github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.28 h1:/D994rtMQd1jQ2OY+7tvUlMlrv1L1c7Xtma/FhkbVtY= 24 | github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.7.28/go.mod h1:3bJI2pLY3ilrqO5EclusI1GbjFJh1iXYrhOItf2sjKw= 25 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.28 h1:bkRyG4a929RCnpVSTvLM2j/T4ls015ZhhYApbmYs15s= 26 | github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.28/go.mod h1:jj7znCIg05jXlaGBlFMGP8+7UN3VtCkRBG2spnmRQkU= 27 | github.com/aws/aws-sdk-go-v2/service/sso v1.12.12 h1:nneMBM2p79PGWBQovYO/6Xnc2ryRMw3InnDJq1FHkSY= 28 | github.com/aws/aws-sdk-go-v2/service/sso v1.12.12/go.mod h1:HuCOxYsF21eKrerARYO6HapNeh9GBNq7fius2AcwodY= 29 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.12 h1:2qTR7IFk7/0IN/adSFhYu9Xthr0zVFTgBrmPldILn80= 30 | github.com/aws/aws-sdk-go-v2/service/ssooidc v1.14.12/go.mod h1:E4VrHCPzmVB/KFXtqBGKb3c8zpbNBgKe3fisDNLAW5w= 31 | github.com/aws/aws-sdk-go-v2/service/sts v1.19.2 h1:XFJ2Z6sNUUcAz9poj+245DMkrHE4h2j5I9/xD50RHfE= 32 | github.com/aws/aws-sdk-go-v2/service/sts v1.19.2/go.mod h1:dp0yLPsLBOi++WTxzCjA/oZqi6NPIhoR+uF7GeMU9eg= 33 | github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8= 34 | github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= 35 | github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= 36 | github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= 37 | github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= 38 | github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= 39 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= 40 | github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= 41 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 42 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 43 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 44 | github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= 45 | github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= 46 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 47 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 48 | github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= 49 | github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= 50 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= 51 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 52 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 53 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 54 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 55 | github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= 56 | github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= 57 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 58 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 59 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 60 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 61 | github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= 62 | github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 63 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 64 | github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= 65 | github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= 66 | github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= 67 | github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= 68 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 69 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 70 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 71 | github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= 72 | github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= 73 | github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= 74 | github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= 75 | github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= 76 | github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 77 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 78 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 79 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 80 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 81 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 82 | github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= 83 | github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= 84 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 85 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 86 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 87 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 88 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 89 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 90 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 91 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 92 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 93 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 94 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 95 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 96 | github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 97 | github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 98 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 99 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 100 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 101 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 102 | github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= 103 | github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 104 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 105 | golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k= 106 | golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 107 | golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g= 108 | golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0= 109 | golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M= 110 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= 111 | golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 112 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 113 | golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU= 114 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 115 | golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= 116 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= 117 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 118 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 119 | google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= 120 | google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= 121 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 122 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 123 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 124 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 125 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 126 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 127 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 128 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 129 | -------------------------------------------------------------------------------- /pkg/api/postservice_api.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | package api 5 | 6 | import ( 7 | "dynamodb-local-test/pkg/model" 8 | "dynamodb-local-test/pkg/service" 9 | "dynamodb-local-test/pkg/utils" 10 | "encoding/json" 11 | "io/ioutil" 12 | "net/http" 13 | 14 | "github.com/gin-gonic/gin" 15 | ) 16 | 17 | type PostServiceApi struct { 18 | PostService service.PostService 19 | } 20 | 21 | func (h *PostServiceApi) PostServiceGetApi(c *gin.Context) { 22 | id := c.Param("id") 23 | post, err := h.PostService.Get(id) 24 | if handleError400(c, err) { 25 | return 26 | } 27 | 28 | c.JSON(http.StatusOK, post) 29 | } 30 | 31 | func (h *PostServiceApi) PostServiceAddApi(c *gin.Context) { 32 | 33 | var post model.Post 34 | 35 | jsonData, err := ioutil.ReadAll(c.Request.Body) 36 | if handleError500(c, err) { 37 | return 38 | } 39 | 40 | err = json.Unmarshal(jsonData, &post) 41 | if handleError500(c, err) { 42 | return 43 | } 44 | 45 | if post.CreateTimestamp == "" { 46 | post.CreateTimestamp = utils.GetLocalTimestampNow() 47 | } 48 | 49 | if post.LastUpdateTimestamp == "" { 50 | post.LastUpdateTimestamp = utils.GetLocalTimestampNow() 51 | } 52 | 53 | _, err = h.PostService.Add(post) 54 | if handleError500(c, err) { 55 | return 56 | } 57 | 58 | c.JSON(http.StatusOK, post) 59 | } 60 | 61 | func handleError500(c *gin.Context, err error) bool { 62 | return handleError(c, err, http.StatusInternalServerError) 63 | } 64 | 65 | func handleError400(c *gin.Context, err error) bool { 66 | return handleError(c, err, http.StatusBadRequest) 67 | } 68 | 69 | func handleError(c *gin.Context, err error, status int) bool { 70 | if err != nil { 71 | c.AbortWithStatusJSON(status, gin.H{"error": err.Error()}) 72 | return true 73 | } 74 | return false 75 | } 76 | -------------------------------------------------------------------------------- /pkg/model/post.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | package model 5 | 6 | type Post struct { 7 | Id string `dynamodbav:"id" json:"id"` 8 | Title string `dynamodbav:"title" json:"title"` 9 | Content string `dynamodbav:"content" json:"content"` 10 | Status string `dynamodbav:"status" json:"status"` 11 | CreateTimestamp string `dynamodbav:"createTimestamp" json:"create_timestamp"` 12 | LastUpdateTimestamp string `dynamodbav:"lastUpdateTimestamp" json:"update_timestamp"` 13 | } 14 | -------------------------------------------------------------------------------- /pkg/service/postservice.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | package service 5 | 6 | import ( 7 | "context" 8 | "dynamodb-local-test/pkg/model" 9 | "log" 10 | 11 | "github.com/aws/aws-sdk-go-v2/aws" 12 | "github.com/aws/aws-sdk-go-v2/feature/dynamodb/attributevalue" 13 | "github.com/aws/aws-sdk-go-v2/service/dynamodb" 14 | "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" 15 | "github.com/pkg/errors" 16 | ) 17 | 18 | var ctx = context.TODO() 19 | 20 | type PostService interface { 21 | Add(post model.Post) (string, error) 22 | Get(id string) (*model.Post, error) 23 | } 24 | 25 | type DdbPostService struct { 26 | ddbSvc *dynamodb.Client 27 | tableName string 28 | } 29 | 30 | func (ps DdbPostService) Get(id string) (*model.Post, error) { 31 | 32 | var post model.Post 33 | 34 | input := &dynamodb.GetItemInput{ 35 | TableName: aws.String("blog-post-table"), 36 | Key: map[string]types.AttributeValue{ 37 | "id": &types.AttributeValueMemberS{Value: id}, 38 | }} 39 | 40 | res, err := ps.ddbSvc.GetItem(ctx, input) 41 | if err != nil { 42 | return nil, err 43 | } 44 | 45 | if len(res.Item) == 0 { 46 | return nil, errors.Errorf("Record with ID %v is not found", id) 47 | } 48 | 49 | err = attributevalue.UnmarshalMap(res.Item, &post) 50 | if err != nil { 51 | return nil, err 52 | } 53 | 54 | return &post, nil 55 | } 56 | 57 | func (ps DdbPostService) Add(post model.Post) (string, error) { 58 | 59 | log.Printf("Post to add: %+v\n", post) 60 | 61 | av, err := attributevalue.MarshalMap(post) 62 | if err != nil { 63 | return "", errors.Wrapf(err, "error marshalling post info for DDB") 64 | } 65 | 66 | input := &dynamodb.PutItemInput{ 67 | Item: av, 68 | TableName: aws.String(ps.tableName), 69 | } 70 | 71 | _, err = ps.ddbSvc.PutItem(ctx, input) 72 | 73 | if err != nil { 74 | return "", errors.Wrapf(err, "error adding post DDB: %#v", post) 75 | } 76 | 77 | return "", nil 78 | } 79 | 80 | func NewDdbPostService(ddbSvc *dynamodb.Client) (PostService, error) { 81 | return &DdbPostService{ 82 | ddbSvc: ddbSvc, 83 | tableName: "blog-post-table", 84 | }, nil 85 | } 86 | -------------------------------------------------------------------------------- /pkg/service/postservice_test.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | package service 5 | 6 | import ( 7 | "context" 8 | "dynamodb-local-test/pkg/model" 9 | "dynamodb-local-test/pkg/utils" 10 | "fmt" 11 | 12 | "github.com/aws/aws-sdk-go-v2/aws" 13 | "github.com/aws/aws-sdk-go-v2/config" 14 | "github.com/aws/aws-sdk-go-v2/service/dynamodb" 15 | "github.com/stretchr/testify/assert" 16 | 17 | "log" 18 | "testing" 19 | 20 | "github.com/aws/aws-sdk-go-v2/service/dynamodb/types" 21 | ) 22 | 23 | func newPostService() (PostService, error) { 24 | customResolver := aws.EndpointResolverFunc(func(service, region string) (aws.Endpoint, error) { 25 | if service == dynamodb.ServiceID && region == "us-east-1" { 26 | return aws.Endpoint{ 27 | PartitionID: "aws", 28 | URL: "http://localhost:8000", 29 | SigningRegion: "us-east-1", 30 | }, nil 31 | } 32 | return aws.Endpoint{}, fmt.Errorf("unknown endpoint requested") 33 | }) 34 | 35 | awsCfg, err := config.LoadDefaultConfig(context.TODO(), config.WithEndpointResolver(customResolver)) 36 | if err != nil { 37 | log.Fatalf("Error loading config: %v", err) 38 | } 39 | 40 | ddbSvc := dynamodb.NewFromConfig(awsCfg) 41 | log.Printf("DDB service created") 42 | 43 | err = createTable(ddbSvc, "blog-post-table") 44 | log.Printf("DDB table created") 45 | 46 | ps, _ := NewDdbPostService(ddbSvc) 47 | return ps, nil 48 | 49 | } 50 | 51 | func createTable(ddbSvc *dynamodb.Client, tableName string) error { 52 | _, err := ddbSvc.CreateTable(ctx, &dynamodb.CreateTableInput{ 53 | TableName: aws.String(tableName), 54 | KeySchema: []types.KeySchemaElement{ 55 | { 56 | AttributeName: aws.String("id"), 57 | KeyType: types.KeyTypeHash, 58 | }, 59 | }, 60 | AttributeDefinitions: []types.AttributeDefinition{ 61 | { 62 | AttributeName: aws.String("id"), 63 | AttributeType: types.ScalarAttributeTypeS, 64 | }, 65 | }, 66 | ProvisionedThroughput: &types.ProvisionedThroughput{ 67 | ReadCapacityUnits: aws.Int64(10), 68 | WriteCapacityUnits: aws.Int64(10), 69 | }}, 70 | ) 71 | 72 | return err 73 | } 74 | 75 | func Test_PostService(t *testing.T) { 76 | 77 | post := model.Post{} 78 | post.Id = "1" 79 | post.Title = "my post" 80 | post.Content = "post content" 81 | post.Status = "posted" 82 | post.CreateTimestamp = utils.GetLocalTimestampNow() 83 | post.LastUpdateTimestamp = utils.GetLocalTimestampNow() 84 | 85 | ps, _ := newPostService() 86 | 87 | _, err := ps.Add(post) 88 | if err != nil { 89 | log.Fatalf("Error storing post: %v", err) 90 | } 91 | log.Printf("post added") 92 | 93 | post2, err := ps.Get("1") 94 | 95 | if err != nil { 96 | log.Fatalf("%v", err) 97 | } 98 | log.Printf("Post from DDB: %+v", post2) 99 | 100 | assert.EqualValues(t, post, *post2) 101 | } 102 | -------------------------------------------------------------------------------- /pkg/utils/utils.go: -------------------------------------------------------------------------------- 1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. 2 | // SPDX-License-Identifier: MIT-0 3 | 4 | package utils 5 | 6 | import "time" 7 | 8 | func GetLocalTimestamp(t time.Time) string { 9 | return t.Format("2006-01-02T15:04:05.000Z") 10 | } 11 | 12 | func GetLocalTimestampNow() string { 13 | t := time.Now() 14 | return GetLocalTimestamp(t) 15 | } 16 | --------------------------------------------------------------------------------