Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
name: Deploy API
on:
push:
branches:
- "master"

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
Expand All @@ -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: |
Expand All @@ -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 }}
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,7 @@ node_modules
.cdk.staging
cdk.out

.env
.env

dist
dist.db
70 changes: 70 additions & 0 deletions api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { Prisma, PrismaClient } from "@prisma/client";
import { Context } from "aws-lambda";

const prisma = new PrismaClient({
log: ["query", "info", `warn`, `error`],
});

const BAD_EVENT = "Bad Event";

enum Routes {
CREATE_USER = "createUser",
GET_USERS = "getUsers",
}

interface CreateUserEvent {
route: Routes.CREATE_USER;
data: Omit<Prisma.UserCreateInput, "posts">;
}

const isCreateUserEvent = (event: ApiEvent): event is CreateUserEvent => {
if (event && event.data) {
return false;
}
return (
Prisma.validator(prisma, "user", "create")(event.data) &&
event.route === Routes.CREATE_USER
);
};

const isGetUsersEvent = (event: ApiEvent): event is GetUsersEvent => {
return event.route === Routes.GET_USERS && event.data === null;
};

interface GetUsersEvent extends Omit<ApiEvent, "data"> {
route: Routes.GET_USERS;
data: null;
}

interface ApiEvent {
route: Routes;
data: any;
}

export const handler = async (event: ApiEvent, context: Context) => {
console.log(event);
console.log(context);
const route: Routes = event.route;

switch (route) {
case Routes.CREATE_USER:
if (!isCreateUserEvent(event)) {
return BAD_EVENT;
}

return await prisma.user.create({
data: {
email: event.data.email,
name: event.data.name,
},
});
case Routes.GET_USERS:
if (!isGetUsersEvent(event)) {
return BAD_EVENT;
}

return await prisma.user.findMany();
default:
return "Unknown Route";
}
};
13 changes: 13 additions & 0 deletions api/prisma/generated/helpers/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

type EnumLike = Array<unknown> | Record<string, unknown>;

export function getEnumValues<T extends EnumLike>(enumType: T): Array<string> {
return [
...new Set(
Object.entries(enumType)
.filter(([key]) => !~~key)
.flatMap((item) => item)
),
] as Array<string>;
}

27 changes: 27 additions & 0 deletions api/prisma/generated/models/Post.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { IsInt, IsDefined, IsString, IsOptional, IsBoolean } from "class-validator";
import { User } from "./";

export class Post {
@IsDefined()
@IsInt()
id!: number;

@IsDefined()
@IsString()
title!: string;

@IsOptional()
@IsString()
content?: string;

@IsDefined()
@IsBoolean()
published!: boolean;

@IsDefined()
author!: User;

@IsDefined()
@IsInt()
authorId!: number;
}
19 changes: 19 additions & 0 deletions api/prisma/generated/models/User.model.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { IsInt, IsDefined, IsString, IsOptional } from "class-validator";
import { Post } from "./";

export class User {
@IsDefined()
@IsInt()
id!: number;

@IsDefined()
@IsString()
email!: string;

@IsOptional()
@IsString()
name?: string;

@IsDefined()
posts!: Post[];
}
2 changes: 2 additions & 0 deletions api/prisma/generated/models/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { Post } from "./Post.model";
export { User } from "./User.model";
19 changes: 19 additions & 0 deletions api/prisma/migrations/20240930204150_init/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
-- CreateTable
CREATE TABLE "User" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"email" TEXT NOT NULL,
"name" TEXT
);

-- CreateTable
CREATE TABLE "Post" (
"id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
"title" TEXT NOT NULL,
"content" TEXT,
"published" BOOLEAN NOT NULL DEFAULT false,
"authorId" INTEGER NOT NULL,
CONSTRAINT "Post_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User" ("id") ON DELETE RESTRICT ON UPDATE CASCADE
);

-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
3 changes: 3 additions & 0 deletions api/prisma/migrations/migration_lock.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (i.e. Git)
provider = "sqlite"
28 changes: 28 additions & 0 deletions api/prisma/schema.prisma
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
datasource db {
provider = "sqlite"
url = env("EFS_DB_PATH")
}

generator class_validator {
provider = "prisma-class-validator-generator"
}

generator client {
provider = "prisma-client-js"
}

model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
}

model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId Int
}
40 changes: 25 additions & 15 deletions bin/lambda-sqlite-efs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
}
);
Loading