diff --git a/apps/backend/db/db_setup.sql b/apps/backend/db/db_setup.sql index 0cef946..3520255 100644 --- a/apps/backend/db/db_setup.sql +++ b/apps/backend/db/db_setup.sql @@ -14,6 +14,7 @@ CREATE TABLE users ( CREATE TABLE projects ( project_id SERIAL PRIMARY KEY, name VARCHAR(100) NOT NULL, + description TEXT, total_budget NUMERIC(12,2), start_date DATE, end_date DATE, @@ -71,10 +72,10 @@ INSERT INTO users (name, email, is_admin) VALUES ('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 projects (name, description, total_budget, start_date, end_date, currency) VALUES +('Clinician Communication Study', 'Study of clinician-patient communication patterns', 500000, '2025-01-01', '2026-01-01', 'USD'), +('Health Education Initiative', 'Community health education and outreach', 300000, '2025-03-01', '2026-03-01', 'USD'), +('Policy Advocacy Program', 'Advocacy and policy change efforts', 200000, '2025-06-01', '2026-06-01', 'USD'); INSERT INTO donors (organization, contact_name, contact_email) VALUES ('NIH', 'Dr. Sarah Lee', 'sarah@nih.gov'), diff --git a/apps/backend/lambdas/auth/db-types.d.ts b/apps/backend/lambdas/auth/db-types.d.ts index 3d190d8..2d83baa 100644 --- a/apps/backend/lambdas/auth/db-types.d.ts +++ b/apps/backend/lambdas/auth/db-types.d.ts @@ -53,6 +53,7 @@ export interface BranchProjects { created_at: Generated; currency: Generated; end_date: Timestamp | null; + description: string | null; name: string; project_id: Generated; start_date: Timestamp | null; diff --git a/apps/backend/lambdas/donors/db-types.d.ts b/apps/backend/lambdas/donors/db-types.d.ts index b2bc948..e5fb1f7 100644 --- a/apps/backend/lambdas/donors/db-types.d.ts +++ b/apps/backend/lambdas/donors/db-types.d.ts @@ -53,6 +53,7 @@ export interface BranchProjects { created_at: Generated; currency: Generated; end_date: Timestamp | null; + description: string | null; name: string; project_id: Generated; start_date: Timestamp | null; diff --git a/apps/backend/lambdas/expenditures/db-types.d.ts b/apps/backend/lambdas/expenditures/db-types.d.ts index b2bc948..e5fb1f7 100644 --- a/apps/backend/lambdas/expenditures/db-types.d.ts +++ b/apps/backend/lambdas/expenditures/db-types.d.ts @@ -53,6 +53,7 @@ export interface BranchProjects { created_at: Generated; currency: Generated; end_date: Timestamp | null; + description: string | null; name: string; project_id: Generated; start_date: Timestamp | null; diff --git a/apps/backend/lambdas/projects/db-types.d.ts b/apps/backend/lambdas/projects/db-types.d.ts index b2bc948..e5fb1f7 100644 --- a/apps/backend/lambdas/projects/db-types.d.ts +++ b/apps/backend/lambdas/projects/db-types.d.ts @@ -53,6 +53,7 @@ export interface BranchProjects { created_at: Generated; currency: Generated; end_date: Timestamp | null; + description: string | null; name: string; project_id: Generated; start_date: Timestamp | null; diff --git a/apps/backend/lambdas/projects/handler.ts b/apps/backend/lambdas/projects/handler.ts index 49ccc05..4a73593 100644 --- a/apps/backend/lambdas/projects/handler.ts +++ b/apps/backend/lambdas/projects/handler.ts @@ -73,11 +73,15 @@ export const handler = async (event: any): Promise => { if (!currencyResult.isValid) return json(400, { message: currencyResult.error }); if (currencyResult.value !== null) values.currency = currencyResult.value; + const descriptionResult = ProjectValidationUtils.validateDescription(body.description); + if (!descriptionResult.isValid) return json(400, { message: descriptionResult.error }); + if (descriptionResult.value !== null) values.description = descriptionResult.value; + try { const inserted = await db .insertInto('branch.projects') .values(values) - .returning(['project_id','name','total_budget','currency','start_date','end_date','created_at']) + .returning(['project_id','name','description','total_budget','currency','start_date','end_date','created_at']) .executeTakeFirst(); return json(201, inserted); diff --git a/apps/backend/lambdas/projects/openapi.yaml b/apps/backend/lambdas/projects/openapi.yaml index 7b45fbb..7e60194 100644 --- a/apps/backend/lambdas/projects/openapi.yaml +++ b/apps/backend/lambdas/projects/openapi.yaml @@ -35,6 +35,8 @@ paths: type: string total_budget: type: number + description: + type: string responses: '200': description: OK diff --git a/apps/backend/lambdas/projects/test/projects.e2e.test.ts b/apps/backend/lambdas/projects/test/projects.e2e.test.ts index cacc763..ecd3979 100644 --- a/apps/backend/lambdas/projects/test/projects.e2e.test.ts +++ b/apps/backend/lambdas/projects/test/projects.e2e.test.ts @@ -36,6 +36,7 @@ describe('POST /projects (e2e)', () => { start_date: '2025-03-01', end_date: '2025-09-30', currency: 'EUR', + description: 'End-to-end project description', }), }); expect(res.status).toBe(201); @@ -45,6 +46,7 @@ describe('POST /projects (e2e)', () => { expect(json.start_date).toContain('2025-03-01'); expect(json.end_date).toContain('2025-09-30'); expect(json.currency).toBe('EUR'); + expect(json.description).toBe('End-to-end project description'); }); test('400 when name missing', async () => { diff --git a/apps/backend/lambdas/projects/test/projects.unit.test.ts b/apps/backend/lambdas/projects/test/projects.unit.test.ts index 04ac428..a41d9da 100644 --- a/apps/backend/lambdas/projects/test/projects.unit.test.ts +++ b/apps/backend/lambdas/projects/test/projects.unit.test.ts @@ -45,8 +45,9 @@ test('201: creates project with all fields', async () => { name: 'AllFieldsUnit', total_budget: 12345.67, start_date: '2025-01-01', - end_date: '2025-12-31', - currency: 'USD', + end_date: '2025-12-31', + currency: 'USD', + description: 'Unit test project description', }), } as any); expect(res.statusCode).toBe(201); @@ -56,6 +57,7 @@ test('201: creates project with all fields', async () => { expect(json.start_date).toContain('2025-01-01'); expect(json.end_date).toContain('2025-12-31'); expect(json.currency).toBe('USD'); + expect(json.description).toBe('Unit test project description'); }); // Validation errors (400) diff --git a/apps/backend/lambdas/projects/validation-utils.ts b/apps/backend/lambdas/projects/validation-utils.ts index b2124ec..9b53b34 100644 --- a/apps/backend/lambdas/projects/validation-utils.ts +++ b/apps/backend/lambdas/projects/validation-utils.ts @@ -72,4 +72,19 @@ export class ProjectValidationUtils { } return { isValid: true, value: c }; } + + // validates optional description field - if provided, trims the whitespace + static validateDescription(input: unknown): ValidationResult { + if (input === undefined || input === null) { + return { isValid: true, value: null }; + } + if (typeof input !== 'string') { + return { isValid: true, value: null }; + } + const d = input.trim(); + if (d.length === 0) { + return { isValid: true, value: null }; + } + return { isValid: true, value: d }; + } } diff --git a/apps/backend/lambdas/users/db-types.d.ts b/apps/backend/lambdas/users/db-types.d.ts index b2bc948..e5fb1f7 100644 --- a/apps/backend/lambdas/users/db-types.d.ts +++ b/apps/backend/lambdas/users/db-types.d.ts @@ -53,6 +53,7 @@ export interface BranchProjects { created_at: Generated; currency: Generated; end_date: Timestamp | null; + description: string | null; name: string; project_id: Generated; start_date: Timestamp | null;