diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6e0c70d..aaaa118 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,8 +1,6 @@ name: Deploy API on: push: - branches: - - "*" concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -28,6 +26,11 @@ jobs: aws-region: eu-west-1 - name: Check out repository code uses: actions/checkout@v3 + + - name: Extract branch name + shell: bash + run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT + id: extract_branch - name: Install dependencies run: | @@ -42,3 +45,4 @@ jobs: env: AWS_REGION: ${{ secrets.AWS_REGION }} AWS_ACCOUNT_NUMBER: ${{ secrets.AWS_ACCOUNT_NUMBER }} + GIT_BRANCH: ${{ steps.extract_branch.outputs.branch }} diff --git a/bin/lambda-sqlite-efs.ts b/bin/lambda-sqlite-efs.ts index 7dfe873..fc22965 100644 --- a/bin/lambda-sqlite-efs.ts +++ b/bin/lambda-sqlite-efs.ts @@ -4,22 +4,37 @@ import "source-map-support/register"; import { LambdaSqliteEfsStack } from "../lib/lambda-sqlite-efs-stack"; const process = require("process"); +const GIT_BRANCH = process.env.GIT_BRANCH; + +const getBranchprefix = (branchName: string) => { + console.log("branchName", branchName); + return branchName && branchName !== `master` + ? `${branchName + .replace(/\b(feat\/|fix\/|chore\/)\b/g, "") + .substring(0, 5)}-` + : ""; +}; + const app = new cdk.App(); -new LambdaSqliteEfsStack(app, "LambdaSqliteEfsStack", { - /* If you don't specify 'env', this stack will be environment-agnostic. - * Account/Region-dependent features and context lookups will not work, - * but a single synthesized template can be deployed anywhere. */ +new LambdaSqliteEfsStack( + app, + `${getBranchprefix(GIT_BRANCH)}LambdaSqliteEfsStack`, + { + /* If you don't specify 'env', this stack will be environment-agnostic. + * Account/Region-dependent features and context lookups will not work, + * but a single synthesized template can be deployed anywhere. */ - /* Uncomment the next line to specialize this stack for the AWS Account - * and Region that are implied by the current CLI configuration. */ - // env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, + /* Uncomment the next line to specialize this stack for the AWS Account + * and Region that are implied by the current CLI configuration. */ + // env: { account: process.env.CDK_DEFAULT_ACCOUNT, region: process.env.CDK_DEFAULT_REGION }, - /* Uncomment the next line if you know exactly what Account and Region you - * want to deploy the stack to. */ - env: { - account: process.env.AWS_ACCOUNT_NUMBER, - region: process.env.AWS_REGION, - }, + /* Uncomment the next line if you know exactly what Account and Region you + * want to deploy the stack to. */ + env: { + account: process.env.AWS_ACCOUNT_NUMBER, + region: process.env.AWS_REGION, + }, - /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */ -}); + /* For more information, see https://docs.aws.amazon.com/cdk/latest/guide/environments.html */ + } +); diff --git a/lib/lambda/bootstrap.ts b/lib/lambda/bootstrap.ts new file mode 100644 index 0000000..4056bc1 --- /dev/null +++ b/lib/lambda/bootstrap.ts @@ -0,0 +1,17 @@ +import SQLLite = require("better-sqlite3"); +import { ApiContext } from "./common/types"; + +const EFS_PATH = process.env.EFS_PATH; + +export default function bootstrap(): ApiContext { + const db = new SQLLite(EFS_PATH + "/lambda-efs.db", {}); + setupDB(db); + + return { db }; +} + +function setupDB(db: SQLLite.Database) { + db.prepare( + "create table if not exists users (name TEXT, age INTEGER)" + ).run(); +} diff --git a/lib/lambda/common/constants.ts b/lib/lambda/common/constants.ts new file mode 100644 index 0000000..884fa44 --- /dev/null +++ b/lib/lambda/common/constants.ts @@ -0,0 +1,2 @@ +export const ENDPOINT_NOT_SUPPORTED = "Endpoint Not Supported"; +export const INVALID_INPUT = "Invalid Input"; diff --git a/lib/lambda/common/types.ts b/lib/lambda/common/types.ts new file mode 100644 index 0000000..fd2eddc --- /dev/null +++ b/lib/lambda/common/types.ts @@ -0,0 +1,13 @@ +import { Database } from "better-sqlite3"; + +export type Route = "/user" | "/users"; + +export type ApiEvent = { + route: Route; + method: "POST" | "GET" | "PUT" | "DELETE" | "PATCH"; + data: Record; +}; + +export type ApiContext = { + db: Database; +}; diff --git a/lib/lambda/index.ts b/lib/lambda/index.ts index 314aa47..ccb535f 100644 --- a/lib/lambda/index.ts +++ b/lib/lambda/index.ts @@ -1,37 +1,31 @@ -import Database = require("better-sqlite3"); -const EFS_PATH = process.env.EFS_PATH; +import bootstrap from "./bootstrap"; +import { ApiContext, ApiEvent, Route } from "./common/types"; +import { userRoute } from "./user"; +import { usersRoute } from "./users"; -const db = new Database(EFS_PATH + "/lambda-efs.db", {}); +const { db } = bootstrap(); -export const handler = async (event: any) => { - console.log(JSON.stringify({ event })); +export const handler = async (event: ApiEvent) => { + try { + const ctx: ApiContext = { + db, + }; - bootstrap(); + const routeHandler = routeHandlers[event.route]; - createUser(event.name, event.age); + const res = routeHandler(event, ctx); - const users = getUsers(); + console.log(res); - console.log(JSON.stringify({ users })); + return res; + } catch (e) { + return JSON.stringify(e); + } }; -function bootstrap() { - console.log("Bootstrapping function"); - setupDB(); -} - -function setupDB() { - console.log("Setting up DB"); - - db.prepare( - "create table if not exists users (name TEXT, age INTEGER)" - ).run(); -} - -function createUser(name: string, age: number) { - db.prepare(`INSERT into users (name, age) VALUES ('${name}', ${age})`).run(); -} +const routeHandlers: Record = { + "/user": userRoute, + "/users": usersRoute, +}; -function getUsers() { - return db.prepare("SELECT * FROM users").all(); -} +type RouteFunction = (event: ApiEvent, ctx: ApiContext) => unknown; diff --git a/lib/lambda/user/index.ts b/lib/lambda/user/index.ts new file mode 100644 index 0000000..7abc579 --- /dev/null +++ b/lib/lambda/user/index.ts @@ -0,0 +1,32 @@ +import { ENDPOINT_NOT_SUPPORTED, INVALID_INPUT } from "../common/constants"; +import { ApiContext, ApiEvent } from "../common/types"; + +export const userRoute = (event: ApiEvent, ctx: ApiContext) => { + if (event.method === "POST") { + if ( + event.data && + event.data.name && + typeof event.data.name === "string" && + event.data.age && + typeof event.data.age === "number" + ) { + return createUser(ctx, event.data.name, event.data.age); + } else { + return INVALID_INPUT; + } + } + + return ENDPOINT_NOT_SUPPORTED; +}; + +function createUser(ctx: ApiContext, name: string, age: number) { + const res = ctx.db + .prepare(`INSERT into users (name, age) VALUES ('${name}', ${age})`) + .run(); + + if (res) { + return true; + } else { + return false; + } +} diff --git a/lib/lambda/users/index.ts b/lib/lambda/users/index.ts new file mode 100644 index 0000000..d42a5f3 --- /dev/null +++ b/lib/lambda/users/index.ts @@ -0,0 +1,14 @@ +import { ENDPOINT_NOT_SUPPORTED } from "../common/constants"; +import { ApiContext, ApiEvent } from "../common/types"; + +export const usersRoute = (event: ApiEvent, ctx: ApiContext) => { + if (event.method === "GET") { + return getUsers(ctx); + } + + return ENDPOINT_NOT_SUPPORTED; +}; + +function getUsers(ctx: ApiContext) { + return ctx.db.prepare("SELECT * FROM users").all(); +}