Skip to content
Merged
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
20 changes: 0 additions & 20 deletions .github/ISSUE_TEMPLATE/feature_request.md

This file was deleted.

20 changes: 20 additions & 0 deletions .github/ISSUE_TEMPLATE/standard_ticket.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
name: Standard Ticket
about: Standard Ticket format
title: ''
labels: ''
assignees: ''

---

**User Story:**
As a ...

**Motivation:**
This ticket ...

**Acceptance Crtieria:**
- [ ]


**Dependencies / Notes:**
2 changes: 1 addition & 1 deletion apps/backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ import { AuthModule } from './auth/auth.module';
AuthModule,
],
controllers: [AppController],
providers: [AppService]
providers: [AppService],
})
export class AppModule {}
5 changes: 2 additions & 3 deletions apps/backend/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ type AuthenticatedRequest = {

/**
* Auth controller to handle authentication-related endpoints.
*
*
* The execution chain is:
* 1. JwtAuthGuard calls Passport (package) with strategy name 'jwt'.
* 2. Passport (package) finds the registered JwtStrategy (in jwt.strategy.ts)
Expand All @@ -25,10 +25,9 @@ type AuthenticatedRequest = {
@ApiBearerAuth()
@Controller('auth')
export class AuthController {

/**
* Retrieves the authenticated user's identity.
*
*
* This endpoint is protected by the JwtAuthGuard, ensuring that only authenticated requests can access it.
* The JwtStrategy autoinjects the validated user into request.user.
*
Expand Down
2 changes: 1 addition & 1 deletion apps/backend/src/auth/authenticated-identity.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/**
* Represents the identity of an authenticated user (Cognito ID Token)
*
*
* sub: Unique Cognito Subject (user identifier)
* email: The email address of the authenticated user.
*/
Expand Down
6 changes: 3 additions & 3 deletions apps/backend/src/auth/jwt-auth.guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import { AuthGuard } from '@nestjs/passport';

/**
* JWT authentication guard to protect routes requiring a valid JWT token.
*
*
* Extends the default AuthGuard provided by Passport Package to use the JWT strategy.
* Hence all of the logic is handled by the underlying Passport JWT strategy
* which is why this file is nearly blank.
*
*
* This is used in other files to protect routes that require JWT authentication.
* E.g. @UseGuards(JwtAuthGuard) put above a controller route (method).
*
*
* The execution chain is:
* 1. JwtAuthGuard calls Passport (package) with strategy name 'jwt'.
* 2. Passport (package) finds the registered JwtStrategy (in jwt.strategy.ts)
Expand Down
10 changes: 5 additions & 5 deletions apps/backend/src/auth/jwt.strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { AuthenticatedIdentity } from './authenticated-identity';

/**
* Represents the payload of a Cognito ID token (Contains the JWT).
*
*
* sub: The unique identifier for the user.
* email: The email address of the user.
* token_use: Indicates the type of token (should be 'id' for ID tokens).
Expand All @@ -19,10 +19,10 @@ type CognitoIdTokenPayload = {

/**
* JWT strategy for validating Cognito ID tokens (Which contain JWTs).
*
*
* This strategy extracts the JWT from the Authorization header, verifies it using the JWKS endpoint,
* and validates the payload to ensure it contains the required claims (sub and email).
*
*
* The execution chain is:
* 1. JwtAuthGuard calls Passport (package) with strategy name 'jwt'.
* 2. Passport (package) finds the registered JwtStrategy (in jwt.strategy.ts)
Expand Down Expand Up @@ -54,7 +54,7 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
const jwksUri = `${issuer}/.well-known/jwks.json`;

/**
* Configures the Passport JWT strategy with the necessary options for
* Configures the Passport JWT strategy with the necessary options for
* Cognito ID token validation (which contains the JWT)
*/
super({
Expand Down Expand Up @@ -82,7 +82,7 @@ export class JwtStrategy extends PassportStrategy(Strategy) {
* Ensures that the token is an ID token and contains the required claims (sub and email).
* Does not validate the JWT
* the JWT is validated separately by the Passport (parent) JWT strategy BEFORE this method is called.
*
*
* @param payload The decoded Cognito ID token payload.
* @returns The authenticated identity containing the sub and email claims.
*/
Expand Down
31 changes: 18 additions & 13 deletions apps/backend/src/migrations/1790210931556-CreateResourcesTable.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import { MigrationInterface, QueryRunner } from "typeorm";
import { MigrationInterface, QueryRunner } from 'typeorm';

export class CreateResourcesTable1790210931556 implements MigrationInterface {
name = 'CreateResourcesTable1790210931556'
name = 'CreateResourcesTable1790210931556';

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`CREATE TYPE "public"."resources_category_enum" AS ENUM('food_access', 'housing', 'other')`);
await queryRunner.query(`CREATE TYPE "public"."resources_county_enum" AS ENUM('Suffolk', 'Middlesex', 'Norfolk', 'Essex', 'Worcester', 'Plymouth', 'Hampden', 'Hampshire', 'Berkshire', 'Barnstable', 'Bristol', 'Dukes', 'Nantucket')`);
await queryRunner.query(`CREATE TABLE "resources" ("resource_id" SERIAL NOT NULL, "score_id" integer NOT NULL, "name" character varying(255) NOT NULL, "category" "public"."resources_category_enum" array NOT NULL, "description" text, "address" character varying(255), "county" "public"."resources_county_enum" NOT NULL, "zip_code" character varying(10) NOT NULL, "phone" character varying(20), "email" character varying(255), "website" character varying(255), "eligibility" text, "last_verified_date" date NOT NULL, "vetting_status" character varying(255) NOT NULL, "reviewer_notes" text, "tags" integer array NOT NULL DEFAULT '{}', CONSTRAINT "PK_b9fa5542581d6107e9bf2f49ac0" PRIMARY KEY ("resource_id"))`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE "resources"`);
await queryRunner.query(`DROP TYPE "public"."resources_county_enum"`);
await queryRunner.query(`DROP TYPE "public"."resources_category_enum"`);
}
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TYPE "public"."resources_category_enum" AS ENUM('food_access', 'housing', 'other')`,
);
await queryRunner.query(
`CREATE TYPE "public"."resources_county_enum" AS ENUM('Suffolk', 'Middlesex', 'Norfolk', 'Essex', 'Worcester', 'Plymouth', 'Hampden', 'Hampshire', 'Berkshire', 'Barnstable', 'Bristol', 'Dukes', 'Nantucket')`,
);
await queryRunner.query(
`CREATE TABLE "resources" ("resource_id" SERIAL NOT NULL, "score_id" integer NOT NULL, "name" character varying(255) NOT NULL, "category" "public"."resources_category_enum" array NOT NULL, "description" text, "address" character varying(255), "county" "public"."resources_county_enum" NOT NULL, "zip_code" character varying(10) NOT NULL, "phone" character varying(20), "email" character varying(255), "website" character varying(255), "eligibility" text, "last_verified_date" date NOT NULL, "vetting_status" character varying(255) NOT NULL, "reviewer_notes" text, "tags" integer array NOT NULL DEFAULT '{}', CONSTRAINT "PK_b9fa5542581d6107e9bf2f49ac0" PRIMARY KEY ("resource_id"))`,
);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`DROP TABLE "resources"`);
await queryRunner.query(`DROP TYPE "public"."resources_county_enum"`);
await queryRunner.query(`DROP TYPE "public"."resources_category_enum"`);
}
}
41 changes: 26 additions & 15 deletions apps/backend/src/migrations/1790285415419-CreateResourcesTable.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
import { MigrationInterface, QueryRunner } from "typeorm";
import { MigrationInterface, QueryRunner } from 'typeorm';

export class CreateResourcesTable1790285415419 implements MigrationInterface {
name = 'CreateResourcesTable1790285415419'
name = 'CreateResourcesTable1790285415419';

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`ALTER TYPE "public"."resources_county_enum" RENAME TO "resources_county_enum_old"`);
await queryRunner.query(`CREATE TYPE "public"."resources_county_enum" AS ENUM('Suffolk', 'Middlesex', 'Norfolk', 'Essex', 'Worcester', 'Plymouth', 'Hampden', 'Hampshire', 'Berkshire', 'Barnstable', 'Bristol', 'Dukes', 'Nantucket', 'Franklin')`);
await queryRunner.query(`ALTER TABLE "resources" ALTER COLUMN "county" TYPE "public"."resources_county_enum" USING "county"::"text"::"public"."resources_county_enum"`);
await queryRunner.query(`DROP TYPE "public"."resources_county_enum_old"`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`CREATE TYPE "public"."resources_county_enum_old" AS ENUM('Suffolk', 'Middlesex', 'Norfolk', 'Essex', 'Worcester', 'Plymouth', 'Hampden', 'Hampshire', 'Berkshire', 'Barnstable', 'Bristol', 'Dukes', 'Nantucket')`);
await queryRunner.query(`ALTER TABLE "resources" ALTER COLUMN "county" TYPE "public"."resources_county_enum_old" USING "county"::"text"::"public"."resources_county_enum_old"`);
await queryRunner.query(`DROP TYPE "public"."resources_county_enum"`);
await queryRunner.query(`ALTER TYPE "public"."resources_county_enum_old" RENAME TO "resources_county_enum"`);
}
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TYPE "public"."resources_county_enum" RENAME TO "resources_county_enum_old"`,
);
await queryRunner.query(
`CREATE TYPE "public"."resources_county_enum" AS ENUM('Suffolk', 'Middlesex', 'Norfolk', 'Essex', 'Worcester', 'Plymouth', 'Hampden', 'Hampshire', 'Berkshire', 'Barnstable', 'Bristol', 'Dukes', 'Nantucket', 'Franklin')`,
);
await queryRunner.query(
`ALTER TABLE "resources" ALTER COLUMN "county" TYPE "public"."resources_county_enum" USING "county"::"text"::"public"."resources_county_enum"`,
);
await queryRunner.query(`DROP TYPE "public"."resources_county_enum_old"`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TYPE "public"."resources_county_enum_old" AS ENUM('Suffolk', 'Middlesex', 'Norfolk', 'Essex', 'Worcester', 'Plymouth', 'Hampden', 'Hampshire', 'Berkshire', 'Barnstable', 'Bristol', 'Dukes', 'Nantucket')`,
);
await queryRunner.query(
`ALTER TABLE "resources" ALTER COLUMN "county" TYPE "public"."resources_county_enum_old" USING "county"::"text"::"public"."resources_county_enum_old"`,
);
await queryRunner.query(`DROP TYPE "public"."resources_county_enum"`);
await queryRunner.query(
`ALTER TYPE "public"."resources_county_enum_old" RENAME TO "resources_county_enum"`,
);
}
}
2 changes: 1 addition & 1 deletion apps/backend/src/resources/resources.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export class ResourcesController {
* Creates a new resource.
* @param createResourceDto The validated request body describing the resource.
*
* @returns 201 with the newly created Resource entity.
* @returns 201 with the newly created Resource entity.
* Returns 400 with a message naming the invalid field if the body fails validation,
* or 500 with a descriptive message if the resource can't be saved to the database.
*/
Expand Down
2 changes: 1 addition & 1 deletion apps/backend/src/score/score.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { ScoreService } from './score.service';
import { Score } from './score.entity';
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';

// TODO: Make tests once the entity shape is actually finalized.
// TODO: Make tests once the entity shape is actually finalized.

/**
* Controller for managing scores. Provides endpoints for retrieving and managing scores in the system.
Expand Down
19 changes: 7 additions & 12 deletions apps/backend/src/tags/tags.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Body, Controller, Get, Param, ParseIntPipe, Query } from '@nestjs/common';
import { Controller, Get, Param, ParseIntPipe, Query } from '@nestjs/common';
import { TagsService } from './tags.service';
import { Tag } from './tag.entity';
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
Expand All @@ -16,13 +16,11 @@ export class TagsController {
/**
* Retrieves tags by a list of ids
* @param ids an array of tag IDs to retreive
*
*
* @returns an array of tags matching the provided IDs
*/
@Get('/findById')
async getTagsByIds(
@Query('ids') ids?: string,
): Promise<Tag[]> {
async getTagsByIds(@Query('ids') ids?: string): Promise<Tag[]> {
const tagIds = ids ? ids.split(',').map(Number) : [];

return this.tagsService.getTagsByIds(tagIds);
Expand All @@ -31,27 +29,24 @@ export class TagsController {
/**
* Retrieves a single tag by its ID.
* @param tagId the unique ID of a tag to get
*
*
* @returns The tag matching the provided ID
*/
@Get('/:tagId')
async getTagById(
@Param('tagId', ParseIntPipe) tagId: number,
): Promise<Tag> {
async getTagById(@Param('tagId', ParseIntPipe) tagId: number): Promise<Tag> {
return this.tagsService.getTagById(tagId);
}


/**
* Retrieves tags by their category
* @param category the category used to get tags
*
*
* @returns an array of tags matching the provided category
*/
@Get('/category/:category')
async getTagsByCategory(
@Param('category') category: Category,
) : Promise<Tag[]> {
): Promise<Tag[]> {
return this.tagsService.getTagsByCategory(category);
}
}
Loading
Loading