From e467cd91a583b2f089886888beaeef7624714b00 Mon Sep 17 00:00:00 2001 From: Estian Yssel <38258302+EYssel@users.noreply.github.com> Date: Sat, 5 Oct 2024 21:13:39 +0200 Subject: [PATCH 01/15] feat: add basic api project structure --- lib/lambda/bootstrap.ts | 17 +++++++++++++ lib/lambda/common/constants.ts | 1 + lib/lambda/common/types.ts | 13 ++++++++++ lib/lambda/index.ts | 44 ++++++++++------------------------ lib/lambda/user/index.ts | 22 +++++++++++++++++ lib/lambda/users/index.ts | 14 +++++++++++ 6 files changed, 79 insertions(+), 32 deletions(-) create mode 100644 lib/lambda/bootstrap.ts create mode 100644 lib/lambda/common/constants.ts create mode 100644 lib/lambda/common/types.ts create mode 100644 lib/lambda/user/index.ts create mode 100644 lib/lambda/users/index.ts 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..07d0926 --- /dev/null +++ b/lib/lambda/common/constants.ts @@ -0,0 +1 @@ +export const ENDPOINT_NOT_SUPPORTED = "Endpoint Not Supported"; diff --git a/lib/lambda/common/types.ts b/lib/lambda/common/types.ts new file mode 100644 index 0000000..4c167f8 --- /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: any; +}; + +export type ApiContext = { + db: Database; +}; diff --git a/lib/lambda/index.ts b/lib/lambda/index.ts index 314aa47..03e35c8 100644 --- a/lib/lambda/index.ts +++ b/lib/lambda/index.ts @@ -1,37 +1,17 @@ -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, ctx: ApiContext) => { + ctx.db = db; - bootstrap(); - - createUser(event.name, event.age); - - const users = getUsers(); - - console.log(JSON.stringify({ users })); + routeHandlers[event.route](event.data, ctx); }; -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(); -} - -function getUsers() { - return db.prepare("SELECT * FROM users").all(); -} +const routeHandlers: Record any> = { + "/user": () => userRoute, + "/users": () => usersRoute, +}; diff --git a/lib/lambda/user/index.ts b/lib/lambda/user/index.ts new file mode 100644 index 0000000..45e4dbb --- /dev/null +++ b/lib/lambda/user/index.ts @@ -0,0 +1,22 @@ +import { ENDPOINT_NOT_SUPPORTED } from "../common/constants"; +import { ApiContext, ApiEvent } from "../common/types"; + +export const userRoute = (event: ApiEvent, ctx: ApiContext) => { + if (event.method === "POST") { + return createUser(ctx, event.data.name, event.data.age); + } + + 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(); +} From 271d78aceb446e888a1744553a5e4768817ee9dd Mon Sep 17 00:00:00 2001 From: Estian Yssel <38258302+EYssel@users.noreply.github.com> Date: Sat, 5 Oct 2024 21:16:40 +0200 Subject: [PATCH 02/15] feat: integrate ci for all branches --- .github/workflows/ci.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) 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 }} From 61ddb804bb1d6be0074cc95557bed0ada9fcc4b9 Mon Sep 17 00:00:00 2001 From: Estian Yssel <38258302+EYssel@users.noreply.github.com> Date: Sat, 5 Oct 2024 21:19:51 +0200 Subject: [PATCH 03/15] feat: create stack based on branch name --- bin/lambda-sqlite-efs.ts | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/bin/lambda-sqlite-efs.ts b/bin/lambda-sqlite-efs.ts index 7dfe873..4454e29 100644 --- a/bin/lambda-sqlite-efs.ts +++ b/bin/lambda-sqlite-efs.ts @@ -4,22 +4,32 @@ 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 getBranchSuffix = (branchName: string) => { + return branchName !== `master` ? `-${branchName || "".replace("/", "_")}` : ""; +}; + 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, + `LambdaSqliteEfsStack${getBranchSuffix(GIT_BRANCH)}`, + { + /* 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 */ + } +); From cca2f1b2158abe624630609f955fa05f9dd05b42 Mon Sep 17 00:00:00 2001 From: Estian Yssel <38258302+EYssel@users.noreply.github.com> Date: Sat, 5 Oct 2024 21:32:01 +0200 Subject: [PATCH 04/15] feat: return response --- lib/lambda/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/lambda/index.ts b/lib/lambda/index.ts index 03e35c8..c4b9d61 100644 --- a/lib/lambda/index.ts +++ b/lib/lambda/index.ts @@ -8,7 +8,7 @@ const { db } = bootstrap(); export const handler = async (event: ApiEvent, ctx: ApiContext) => { ctx.db = db; - routeHandlers[event.route](event.data, ctx); + return routeHandlers[event.route](event.data, ctx); }; const routeHandlers: Record any> = { From fa1101c2387496a9912fd70870d094678696deb8 Mon Sep 17 00:00:00 2001 From: Estian Yssel <38258302+EYssel@users.noreply.github.com> Date: Sun, 6 Oct 2024 08:21:16 +0200 Subject: [PATCH 05/15] feat: use prefix --- bin/lambda-sqlite-efs.ts | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/bin/lambda-sqlite-efs.ts b/bin/lambda-sqlite-efs.ts index 4454e29..65ead94 100644 --- a/bin/lambda-sqlite-efs.ts +++ b/bin/lambda-sqlite-efs.ts @@ -6,14 +6,27 @@ const process = require("process"); const GIT_BRANCH = process.env.GIT_BRANCH; -const getBranchSuffix = (branchName: string) => { - return branchName !== `master` ? `-${branchName || "".replace("/", "_")}` : ""; +// Sample string +let text = "This is a feat. We need to fix this. It's just a chore."; + +// Regular expression to match "feat", "fix", and "chore" +let regex = /\b(feat|fix|chore)\b/g; + +// Replace matched words with a placeholder (e.g., "[REPLACED]") +let result = text.replace(regex, "[REPLACED]"); + +console.log(result); // Output: "This is a [REPLACED]. We need to [REPLACED] this. It's just a [REPLACED]." + +const getBranchprefix = (branchName: string) => { + return branchName !== `master` + ? `${branchName.replace(/\b(feat|fix|chore)\/?\b/g, "").substring(10)}-` + : ""; }; const app = new cdk.App(); new LambdaSqliteEfsStack( app, - `LambdaSqliteEfsStack${getBranchSuffix(GIT_BRANCH)}`, + `${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, From 653e4e03ed4565191c3b648d4093f4cd9c2f2014 Mon Sep 17 00:00:00 2001 From: Estian Yssel <38258302+EYssel@users.noreply.github.com> Date: Sun, 6 Oct 2024 08:25:16 +0200 Subject: [PATCH 06/15] fix: remove example --- bin/lambda-sqlite-efs.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/bin/lambda-sqlite-efs.ts b/bin/lambda-sqlite-efs.ts index 65ead94..9a65c3b 100644 --- a/bin/lambda-sqlite-efs.ts +++ b/bin/lambda-sqlite-efs.ts @@ -6,17 +6,6 @@ const process = require("process"); const GIT_BRANCH = process.env.GIT_BRANCH; -// Sample string -let text = "This is a feat. We need to fix this. It's just a chore."; - -// Regular expression to match "feat", "fix", and "chore" -let regex = /\b(feat|fix|chore)\b/g; - -// Replace matched words with a placeholder (e.g., "[REPLACED]") -let result = text.replace(regex, "[REPLACED]"); - -console.log(result); // Output: "This is a [REPLACED]. We need to [REPLACED] this. It's just a [REPLACED]." - const getBranchprefix = (branchName: string) => { return branchName !== `master` ? `${branchName.replace(/\b(feat|fix|chore)\/?\b/g, "").substring(10)}-` From 6353e2ed0e0e64a4d2d99d8c92394f637196285f Mon Sep 17 00:00:00 2001 From: Estian Yssel <38258302+EYssel@users.noreply.github.com> Date: Sun, 6 Oct 2024 08:26:59 +0200 Subject: [PATCH 07/15] fix: adds null check and logs branchName --- bin/lambda-sqlite-efs.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/bin/lambda-sqlite-efs.ts b/bin/lambda-sqlite-efs.ts index 9a65c3b..03814f5 100644 --- a/bin/lambda-sqlite-efs.ts +++ b/bin/lambda-sqlite-efs.ts @@ -7,7 +7,8 @@ const process = require("process"); const GIT_BRANCH = process.env.GIT_BRANCH; const getBranchprefix = (branchName: string) => { - return branchName !== `master` + console.log("branchName", branchName); + return branchName && branchName !== `master` ? `${branchName.replace(/\b(feat|fix|chore)\/?\b/g, "").substring(10)}-` : ""; }; From 780cbee08183b6f05259bcde584850368b92321d Mon Sep 17 00:00:00 2001 From: Estian Yssel <38258302+EYssel@users.noreply.github.com> Date: Sun, 6 Oct 2024 08:29:38 +0200 Subject: [PATCH 08/15] fix: use substring start and end correctly --- bin/lambda-sqlite-efs.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/bin/lambda-sqlite-efs.ts b/bin/lambda-sqlite-efs.ts index 03814f5..6edc708 100644 --- a/bin/lambda-sqlite-efs.ts +++ b/bin/lambda-sqlite-efs.ts @@ -9,7 +9,9 @@ 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(10)}-` + ? `${branchName + .replace(/\b(feat|fix|chore)\/?\b/g, "") + .substring(0, 5)}-` : ""; }; From 74ce9fd31ed599e378dc2ed7b382e3f63a784ffc Mon Sep 17 00:00:00 2001 From: Estian Yssel <38258302+EYssel@users.noreply.github.com> Date: Sun, 6 Oct 2024 16:37:43 +0200 Subject: [PATCH 09/15] fix: use correct handler structure --- lib/lambda/index.ts | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/lib/lambda/index.ts b/lib/lambda/index.ts index c4b9d61..5426d7b 100644 --- a/lib/lambda/index.ts +++ b/lib/lambda/index.ts @@ -5,13 +5,26 @@ import { usersRoute } from "./users"; const { db } = bootstrap(); -export const handler = async (event: ApiEvent, ctx: ApiContext) => { - ctx.db = db; +export const handler = async (event: ApiEvent) => { + try { + const ctx: ApiContext = { + db, + }; - return routeHandlers[event.route](event.data, ctx); + const res = routeHandlers[event.route](event.data, ctx); + + console.log(res); + + return true; + } catch (e) { + return JSON.stringify(e); + } }; -const routeHandlers: Record any> = { +const routeHandlers: Record< + Route, + (event: ApiEvent, ctx: ApiContext) => unknown +> = { "/user": () => userRoute, "/users": () => usersRoute, }; From 19f4b402118b5547b9a8a8147c783de453100036 Mon Sep 17 00:00:00 2001 From: Estian Yssel <38258302+EYssel@users.noreply.github.com> Date: Sun, 6 Oct 2024 16:38:02 +0200 Subject: [PATCH 10/15] fix: only replace feat/ fix/ etc. --- bin/lambda-sqlite-efs.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/lambda-sqlite-efs.ts b/bin/lambda-sqlite-efs.ts index 6edc708..fc22965 100644 --- a/bin/lambda-sqlite-efs.ts +++ b/bin/lambda-sqlite-efs.ts @@ -10,7 +10,7 @@ const getBranchprefix = (branchName: string) => { console.log("branchName", branchName); return branchName && branchName !== `master` ? `${branchName - .replace(/\b(feat|fix|chore)\/?\b/g, "") + .replace(/\b(feat\/|fix\/|chore\/)\b/g, "") .substring(0, 5)}-` : ""; }; From de8a648cb19a53cadcfbefeea6abe9d0995f7bf1 Mon Sep 17 00:00:00 2001 From: Estian Yssel <38258302+EYssel@users.noreply.github.com> Date: Sun, 6 Oct 2024 18:39:07 +0200 Subject: [PATCH 11/15] fix: use correct route handler --- lib/lambda/index.ts | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/lambda/index.ts b/lib/lambda/index.ts index 5426d7b..fc35038 100644 --- a/lib/lambda/index.ts +++ b/lib/lambda/index.ts @@ -11,7 +11,9 @@ export const handler = async (event: ApiEvent) => { db, }; - const res = routeHandlers[event.route](event.data, ctx); + const routeHandler = routeHandlers[event.route]; + + const res = routeHandler(event, ctx); console.log(res); @@ -21,10 +23,9 @@ export const handler = async (event: ApiEvent) => { } }; -const routeHandlers: Record< - Route, - (event: ApiEvent, ctx: ApiContext) => unknown -> = { +const routeHandlers: Record = { "/user": () => userRoute, "/users": () => usersRoute, }; + +type RouteFunction = (event: ApiEvent, ctx: ApiContext) => unknown; From 6a2475f64e69cd6b75cbbfa095d71911c3abff00 Mon Sep 17 00:00:00 2001 From: Estian Yssel <38258302+EYssel@users.noreply.github.com> Date: Sun, 6 Oct 2024 18:39:13 +0200 Subject: [PATCH 12/15] feat: improve types --- lib/lambda/common/types.ts | 2 +- lib/lambda/user/index.ts | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/lambda/common/types.ts b/lib/lambda/common/types.ts index 4c167f8..fd2eddc 100644 --- a/lib/lambda/common/types.ts +++ b/lib/lambda/common/types.ts @@ -5,7 +5,7 @@ export type Route = "/user" | "/users"; export type ApiEvent = { route: Route; method: "POST" | "GET" | "PUT" | "DELETE" | "PATCH"; - data: any; + data: Record; }; export type ApiContext = { diff --git a/lib/lambda/user/index.ts b/lib/lambda/user/index.ts index 45e4dbb..49e2007 100644 --- a/lib/lambda/user/index.ts +++ b/lib/lambda/user/index.ts @@ -3,7 +3,17 @@ import { ApiContext, ApiEvent } from "../common/types"; export const userRoute = (event: ApiEvent, ctx: ApiContext) => { if (event.method === "POST") { - return createUser(ctx, event.data.name, event.data.age); + 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; From 0f046976a4ec99540cfbddb87fa20181ebeabcf7 Mon Sep 17 00:00:00 2001 From: Estian Yssel <38258302+EYssel@users.noreply.github.com> Date: Sun, 6 Oct 2024 18:43:47 +0200 Subject: [PATCH 13/15] fix: use function directly --- lib/lambda/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/lambda/index.ts b/lib/lambda/index.ts index fc35038..ee75e9f 100644 --- a/lib/lambda/index.ts +++ b/lib/lambda/index.ts @@ -24,8 +24,8 @@ export const handler = async (event: ApiEvent) => { }; const routeHandlers: Record = { - "/user": () => userRoute, - "/users": () => usersRoute, + "/user": userRoute, + "/users": usersRoute, }; type RouteFunction = (event: ApiEvent, ctx: ApiContext) => unknown; From 1de985a93eab4b064d281e499593552eaba29ed0 Mon Sep 17 00:00:00 2001 From: Estian Yssel <38258302+EYssel@users.noreply.github.com> Date: Sun, 6 Oct 2024 18:54:51 +0200 Subject: [PATCH 14/15] feat: return response --- lib/lambda/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/lambda/index.ts b/lib/lambda/index.ts index ee75e9f..ccb535f 100644 --- a/lib/lambda/index.ts +++ b/lib/lambda/index.ts @@ -17,7 +17,7 @@ export const handler = async (event: ApiEvent) => { console.log(res); - return true; + return res; } catch (e) { return JSON.stringify(e); } From cf487d59c2073f0aac5b536b9b97f15907d48771 Mon Sep 17 00:00:00 2001 From: Estian Yssel <38258302+EYssel@users.noreply.github.com> Date: Sat, 26 Oct 2024 07:41:46 +0200 Subject: [PATCH 15/15] chore: adds constant --- lib/lambda/common/constants.ts | 1 + lib/lambda/user/index.ts | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/lambda/common/constants.ts b/lib/lambda/common/constants.ts index 07d0926..884fa44 100644 --- a/lib/lambda/common/constants.ts +++ b/lib/lambda/common/constants.ts @@ -1 +1,2 @@ export const ENDPOINT_NOT_SUPPORTED = "Endpoint Not Supported"; +export const INVALID_INPUT = "Invalid Input"; diff --git a/lib/lambda/user/index.ts b/lib/lambda/user/index.ts index 49e2007..7abc579 100644 --- a/lib/lambda/user/index.ts +++ b/lib/lambda/user/index.ts @@ -1,4 +1,4 @@ -import { ENDPOINT_NOT_SUPPORTED } from "../common/constants"; +import { ENDPOINT_NOT_SUPPORTED, INVALID_INPUT } from "../common/constants"; import { ApiContext, ApiEvent } from "../common/types"; export const userRoute = (event: ApiEvent, ctx: ApiContext) => { @@ -12,7 +12,7 @@ export const userRoute = (event: ApiEvent, ctx: ApiContext) => { ) { return createUser(ctx, event.data.name, event.data.age); } else { - return "Invalid input"; + return INVALID_INPUT; } }