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
2 changes: 1 addition & 1 deletion apps/backend/db/.env.dev
Original file line number Diff line number Diff line change
@@ -1 +1 @@
DATABASE_URL=postgresql://branch_dev:password@localhost:5432/branch_db
DATABASE_URL=postgresql://branch_dev:password@localhost:5433/branch_db
89 changes: 89 additions & 0 deletions apps/backend/db/db_setup.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
DROP SCHEMA IF EXISTS branch CASCADE;
CREATE SCHEMA branch;
SET search_path TO branch;

CREATE TABLE users (
user_id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(150) UNIQUE NOT NULL,
is_admin BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE projects (
project_id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
total_budget NUMERIC(12,2),
start_date DATE,
end_date DATE,
currency VARCHAR(10) DEFAULT 'USD',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE project_memberships (
membership_id SERIAL PRIMARY KEY,
project_id INT NOT NULL REFERENCES projects(project_id) ON DELETE CASCADE,
user_id INT NOT NULL REFERENCES users(user_id) ON DELETE CASCADE,
role VARCHAR(30) NOT NULL CHECK (role IN ('PI', 'Accountant', 'Staff', 'Admin')),
start_date DATE,
hours NUMERIC(6,2),
UNIQUE (project_id, user_id)
);

CREATE TABLE donors (
donor_id SERIAL PRIMARY KEY,
organization VARCHAR(150) NOT NULL,
contact_name VARCHAR(100),
contact_email VARCHAR(150),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE project_donations (
donation_id SERIAL PRIMARY KEY,
donor_id INT NOT NULL REFERENCES donors(donor_id) ON DELETE CASCADE,
project_id INT NOT NULL REFERENCES projects(project_id) ON DELETE CASCADE,
amount NUMERIC(12,2) NOT NULL CHECK (amount >= 0),
donated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE (donor_id, project_id)
);

CREATE TABLE expenditures (
expenditure_id SERIAL PRIMARY KEY,
project_id INT NOT NULL REFERENCES projects(project_id) ON DELETE CASCADE,
entered_by INT REFERENCES users(user_id) ON DELETE SET NULL,
amount NUMERIC(12,2) NOT NULL CHECK (amount >= 0),
category VARCHAR(50),
description TEXT,
spent_on DATE NOT NULL DEFAULT CURRENT_DATE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO users (name, email, is_admin) VALUES
('Ashley Duggan', 'ashley@branch.org', TRUE),
('Renee Reddy', 'renee@branch.org', TRUE),
('Nour Shoreibah', 'nour@branch.org', TRUE);

INSERT INTO projects (name, total_budget, start_date, end_date, currency) VALUES
('Clinician Communication Study', 500000, '2025-01-01', '2026-01-01', 'USD'),
('Health Education Initiative', 300000, '2025-03-01', '2026-03-01', 'USD'),
('Policy Advocacy Program', 200000, '2025-06-01', '2026-06-01', 'USD');

INSERT INTO donors (organization, contact_name, contact_email) VALUES
('NIH', 'Dr. Sarah Lee', 'sarah@nih.gov'),
('Harvard Medical', 'John Smith', 'john@harvard.edu'),
('Wellcome Trust', 'Anna Johnson', 'anna@wellcome.org');

INSERT INTO project_donations (donor_id, project_id, amount, donated_at) VALUES
(1, 1, 150000, '2025-01-10'),
(2, 2, 120000, '2025-03-15'),
(3, 3, 90000, '2025-06-20');

INSERT INTO project_memberships (project_id, user_id, role, start_date, hours) VALUES
(1, 1, 'PI', '2025-01-01', 100.00),
(1, 2, 'Accountant', '2025-02-01', 80.00),
(2, 3, 'Staff', '2025-03-15', 60.00);

INSERT INTO expenditures (project_id, entered_by, amount, category, description, spent_on) VALUES
(1, 1, 5000, 'Travel', 'Conference attendance', '2025-02-10'),
(2, 2, 3000, 'Equipment', 'Purchase of recording devices', '2025-04-05'),
(3, 3, 2500, 'Supplies', 'Educational materials', '2025-07-12');
5 changes: 3 additions & 2 deletions apps/backend/lambdas/tools/lambda-cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ function templatePackageJson() {
"@types/node": "^20.11.30",
"ts-node": "^10.9.2",
"typescript": "^5.4.5",
"js-yaml": "^4.1.0"
"js-yaml": "^4.1.0",
"start-server-and-test": "^2.1.1"
},
"dependencies": {
"jest":"^30.2.0"
Expand Down Expand Up @@ -139,7 +140,7 @@ export function getSwaggerHtml(specUrl: string): string {
`;
}

function templateJestSetup(handlerName){
function templateJestSetup(handlerName) {
return `
test("health test 🌞", async () => {
let res = await fetch("http://localhost:3000/${handlerName}/health")
Expand Down
4 changes: 2 additions & 2 deletions apps/backend/lambdas/users/db.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@

import { Kysely, PostgresDialect } from 'kysely'
import { Pool } from 'pg'
import type { DB } from './db-types'


const db = new Kysely<DB>({
dialect: new PostgresDialect({
pool: new Pool({
Expand All @@ -16,4 +16,4 @@ const db = new Kysely<DB>({
}),
})

export default db
export default db
56 changes: 53 additions & 3 deletions apps/backend/lambdas/users/handler.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
import db from './db';
import db from './db'


export const handler = async (event: any): Promise<APIGatewayProxyResult> => {
try {
Expand All @@ -10,6 +11,8 @@ export const handler = async (event: any): Promise<APIGatewayProxyResult> => {
const normalizedPath = rawPath.replace(/\/$/, '');
const method = (event.requestContext?.http?.method || event.httpMethod || 'GET').toUpperCase();

console.log('DEBUG - rawPath:', rawPath, 'normalizedPath:', normalizedPath, 'method:', method);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe remove if not needed anymore


// Health check
if ((normalizedPath.endsWith('/health') || normalizedPath === '/health') && method === 'GET') {
return json(200, { ok: true, timestamp: new Date().toISOString() });
Expand All @@ -18,7 +21,53 @@ export const handler = async (event: any): Promise<APIGatewayProxyResult> => {
// >>> ROUTES-START (do not remove this marker)
// CLI-generated routes will be inserted here

// GET /{userId} (dev server strips /users prefix)

// GET /users
if ((normalizedPath === '/users' || normalizedPath === '' || normalizedPath === '/') && method === 'GET') {
// TODO: Add your business logic here
const queryParams = event.queryStringParameters || {};
const page = queryParams.page ? parseInt(queryParams.page, 10) : null;
const limit = queryParams.limit ? parseInt(queryParams.limit, 10) : null;

if (page && limit) {
const offset = (page - 1) * limit;

const totalCount = await db
.selectFrom('branch.users')
.select(db.fn.count('user_id').as('count'))
.executeTakeFirst();

const totalUsers = Number(totalCount?.count || 0);
const totalPages = Math.ceil(totalUsers / limit);

const users = await db
.selectFrom('branch.users')
.selectAll()
.orderBy('user_id', 'asc')
.limit(limit)
.offset(offset)
.execute();
return json(200, {
users,
pagination: {
page,
limit,
totalUsers,
totalPages
}
});
}

const users = await db
.selectFrom('branch.users')
.selectAll()
.execute();

console.log(users);
return json(200, { users });
}

// GET /{userId}
if (normalizedPath.startsWith('/') && normalizedPath.split('/').length === 2 && method === 'GET') {
const userId = normalizedPath.split('/')[1];
if (!userId) return json(400, { message: 'userId is required' });
Expand Down Expand Up @@ -65,6 +114,7 @@ export const handler = async (event: any): Promise<APIGatewayProxyResult> => {

return json(200, { ok: true, route: 'PATCH /users/{userId}', pathParams: { userId }, body: { email: updatedUser!.email, name: updatedUser!.name, isAdmin: updatedUser!.is_admin } });
}

// <<< ROUTES-END

return json(404, { message: 'Not Found', path: normalizedPath, method });
Expand All @@ -85,4 +135,4 @@ function json(statusCode: number, body: unknown): APIGatewayProxyResult {
},
body: JSON.stringify(body)
};
}
}
5 changes: 5 additions & 0 deletions apps/backend/lambdas/users/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wait why was i able to run jest tests without this

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i got an error when i tried to test and it worked when i deleted it

preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/*.test.ts'],
};
19 changes: 17 additions & 2 deletions apps/backend/lambdas/users/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,24 @@ paths:
ok:
type: boolean

/users/{userId}:
/users:
get:
summary: GET /users
parameters:
- name: page
in: query
schema:
type: integer
- name: limit
in: query
schema:
type: integer
responses:
'200':
description: OK
/{userId}:
patch:
summary: PATCH /users/{userId}
summary: PATCH /{userId}
parameters:
- in: path
name: userId
Expand Down
Loading