Skip to content

Commit

Permalink
Merge pull request #31 from trade-tariff/FPO-194-setup-localstack
Browse files Browse the repository at this point in the history
FPO-194: Adds localstack for local development
  • Loading branch information
willfish authored Apr 26, 2024
2 parents ad34b12 + e5a9501 commit 264767d
Show file tree
Hide file tree
Showing 6 changed files with 180 additions and 28 deletions.
85 changes: 85 additions & 0 deletions .bin/seed.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#!/bin/sh

set -o xtrace
set -o errexit
set -o nounset

export AWS_REGION=eu-west-2
export EDGE_ENDPOINT=http://localhost:4566
export DYNAMODB_TABLE_NAME=CustomerApiKeys

create_table() {
tableName=$1
hashKey=$2
rangeKey=$3

aws --endpoint-url="$EDGE_ENDPOINT" \
--region "$AWS_REGION" \
dynamodb create-table \
--table-name "$tableName" \
--attribute-definitions \
AttributeName="$hashKey",AttributeType=S \
AttributeName="$rangeKey",AttributeType=S \
--key-schema \
AttributeName="$hashKey",KeyType=HASH \
AttributeName="$rangeKey",KeyType=RANGE \
--provisioned-throughput ReadCapacityUnits=5,WriteCapacityUnits=5
}

create_usage_plan() {
aws --endpoint-url="$EDGE_ENDPOINT" \
--region "$AWS_REGION" \
apigateway create-usage-plan \
--name local-development \
--query 'id' \
--output text
}

create_api_key() {
aws --endpoint-url="$EDGE_ENDPOINT" \
--region "$AWS_REGION" \
apigateway create-api-key \
--name \'"$id"\' \
--description \'"$description"\' \
--value \'"$secret"\' \
--enabled \
--query 'id' \
--output text
}

create_customer_api_key() {
id=$1
usagePlanId=$2
secret=$3
enabled=$4
fpoId="local-development"
createdAt=$(date -u +"%Y-%m-%dT%H:%M:%S.%3NZ")
description="Autogenerated on $createdAt"

# create the api key
apiGatewayId=$(create_api_key "$id" "$description" "$secret")

# create the dynnamodb item
aws dynamodb put-item \
--endpoint-url "$EDGE_ENDPOINT" \
--region "$AWS_REGION" \
--table-name $DYNAMODB_TABLE_NAME --item "{
\"CustomerApiKeyId\": {\"S\": \"$id\"},
\"Secret\": {\"S\": \"$secret\"},
\"Enabled\": {\"BOOL\": $enabled},
\"Description\": {\"S\": \"$description\"},
\"FpoId\": {\"S\": \"$fpoId\"},
\"CreatedAt\": {\"S\": \"$createdAt\"},
\"UpdatedAt\": {\"S\": \"$createdAt\"},
\"ApiGatewayId\": {\"S\": \"$apiGatewayId\"},
\"UsagePlanId\": {\"S\": \"$usagePlanId\"}
}"
}

usagePlanId=$(create_usage_plan)

create_table "$DYNAMODB_TABLE_NAME" "CustomerApiKeyId" "FpoId"
create_customer_api_key "HUBU461PU63PXH7GZFSW" "$usagePlanId" "z3bCvLUzre+VFohy:zucSqkBr39kcDUnheZk/IiGJPhLBgYY89blaxffSDH8aZOHUY6oMtpILAAC6D6W/OZENOX4tSQHcFINY" true
create_customer_api_key "HUBSFF5Z90SVHK1D5DY8" "$usagePlanId" "D8pFwdTRMXRSa2nm:mS6Xq6w0ZJaOIlPc7iiPNgYsqgUm0MSmBg8Q7M+O74Y09NRwns5gk/OAAZEdmOO71/0kCJWnOHbJt3Re" true
create_customer_api_key "HUBPC7NFHXS6H3LKZCEW" "$usagePlanId" "VitXWo4eiEphvzqR:6TDaBB66dTv5GGwtjkzZREPEJNOvDoUxr7xyuziSJLLA6+MdoyDZyzdFDTg+3dHvhIIHC2ov8Hn0bQaF" true
create_customer_api_key "HUBP4NMDNBUKZ168SQTL" "$usagePlanId" "CBsaehhd3Q/qDusw:yGfSGJgPtL8xMMpN9SFc+s+ZZXugnA1DOeu8A7HofaOR7qZMYN5pTZtjFs3HBM29ER4KA2tj9OnoKjUC" false
8 changes: 6 additions & 2 deletions .env.development
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
export AWS_ACCESS_KEY_ID="test"
export AWS_DEFAULT_REGION="eu-west-2"
export AWS_ENDPOINT=http://localhost:4566
export AWS_REGION=eu-west-2
export AWS_SECRET_ACCESS_KEY="test"
export CUSTOMER_API_KEYS_TABLE_NAME=CustomerApiKeys
export ENCRYPTION_KEY=bSTK97jzd4gMC89mpRe1mL5Lx1v8I4zhWZmOxHEmzCo=
export PORT=5001
export USAGE_PLAN_ID=1ju1kk
export CUSTOMER_API_KEYS_TABLE_NAME=CustomerApiKeys
export USAGE_PLAN_ID=4511tb7tv5
50 changes: 26 additions & 24 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,32 +1,34 @@
.PHONY: default build run clean

IMAGE_NAME := trade-tariff-dev-hub-backend
SHELL := /usr/bin/env bash

default: build run
run: clean build
source .env.development && yarn run start

build:
docker build -t $(IMAGE_NAME) .

run:
docker run \
--network=host \
--rm \
--name $(IMAGE_NAME) \
-e DEBUG=express:* \
-e NODE_ENV=test \
--env-file .env.development \
-it \
$(IMAGE_NAME) \
test:
yarn run test

localstack: clean-localstack
$(eval USAGE_PLAN_ID=$(shell docker-compose up | grep -m1 'usagePlanId=' | awk -F'=' '{print $$2}'))
@if [ -z "$(USAGE_PLAN_ID)" ]; then \
echo "Failed to extract USAGE_PLAN_ID"; \
exit 1; \
fi
@sed -i'' -e 's/export USAGE_PLAN_ID=.*/export USAGE_PLAN_ID=$(USAGE_PLAN_ID)/' .env.development
@echo "Updated USAGE_PLAN_ID in .env.development to $(USAGE_PLAN_ID)"
@echo "Localstack is running. Run make stop-localstack to stop it."

clean-localstack: stop-localstack
docker-compose rm -f -s -v

stop-localstack:
docker-compose down

clean:
docker rmi $(IMAGE_NAME)
yarn run clean

shell:
docker run \
--rm \
--name $(IMAGE_NAME)-shell \
--no-healthcheck \
-it $(IMAGE_NAME) /bin/sh
source:
source .env.development

test:
yarn run test
build:
yarn run build
39 changes: 39 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,38 @@

Express app giving FPO operators the ability to manage their own API credentials.

## Local development

### Prerequisites

- [docker-compose]
- [docker app][docker-app]
- [nodejs]
- [aws cli (optional)][awscli]
- [aws local cli (optional)][awscli-local]

### Running localstack

To reduce feedback loops and improve developer efficiency, we simulate aws services with [localstack]. You can bring up localstack using docker-compose with a simple `Makefile` wrapper

```sh
make localstack
```

### Starting the application

> Make sure you have started localstack before running the application.
```sh
make run
```

### Running tests

```sh
make test
```

## Authentication

When this backend app is deployed to ECS we enable client credentials authentication using Cognito User Pools
Expand Down Expand Up @@ -36,3 +68,10 @@ Once the application is running, open a web browser and navigate to the Swagger
This has been disabled to only run in development mode as the endpoints are designed to be be internal.

[pitr-manual]: https://docs.trade-tariff.service.gov.uk/manual/how-to-backup-and-restore-in-aws-dynamodb-pitr.html

[docker-compose]: https://formulae.brew.sh/formula/docker-compose
[nodejs]: https://formulae.brew.sh/formula/node
[docker-app]: https://docs.docker.com/desktop/install/mac-install/
[awscli]: https://formulae.brew.sh/formula/awscli
[awscli-local]: https://formulae.brew.sh/formula/awscli-local
[localstack]: https://www.localstack.cloud/
10 changes: 10 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
version: "2"
services:
localstack:
image: localstack/localstack:latest
ports:
- 4566:4566 # LocalStack edge port
- 4510-4559:4510-4559 # maps various AWS services ports
volumes:
# Initialization Hook (see https://docs.localstack.cloud/references/init-hooks/)
- .bin/seed.sh:/etc/localstack/init/ready.d/script.sh
16 changes: 14 additions & 2 deletions src/routes/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,20 @@ import { CustomerApiKeyRepository } from '../repositories/customerApiKeyReposito
import { DynamoDBClient } from '@aws-sdk/client-dynamodb'
import { APIGatewayClient } from '@aws-sdk/client-api-gateway'

const dynamodbClient = new DynamoDBClient({ region: process.env.AWS_REGION })
const apiGatewayClient = new APIGatewayClient({ region: process.env.AWS_REGION })
const endpoint = process.env.AWS_ENDPOINT

const dynamodbClient = new DynamoDBClient(
{
region: process.env.AWS_REGION,
endpoint
}
)
const apiGatewayClient = new APIGatewayClient(
{
region: process.env.AWS_REGION,
endpoint
}
)
const repository = new CustomerApiKeyRepository(dynamodbClient, apiGatewayClient)

const apiKeyController = new ApiKeyController(repository)
Expand Down

0 comments on commit 264767d

Please sign in to comment.