Skip to content
2 changes: 1 addition & 1 deletion apps/backend/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export class AuthController {
signUpDto.firstName,
signUpDto.lastName,
signUpDto.phone,
Role.STANDARD_VOLUNTEER,
Role.VOLUNTEER,
);

return user;
Expand Down
2 changes: 1 addition & 1 deletion apps/backend/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ export class AuthService {

async signup(
{ firstName, lastName, email, password }: SignUpDto,
role: Role = Role.STANDARD_VOLUNTEER,
role: Role = Role.VOLUNTEER,
): Promise<boolean> {
// Needs error handling
const signUpCommand = new SignUpCommand({
Expand Down
2 changes: 2 additions & 0 deletions apps/backend/src/config/typeorm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import { RemoveOrdersDonationId1761500262238 } from '../migrations/1761500262238
import { AddVolunteerPantryUniqueConstraint1760033134668 } from '../migrations/1760033134668-AddVolunteerPantryUniqueConstraint';
import { AllergyFriendlyToBoolType1763963056712 } from '../migrations/1763963056712-AllergyFriendlyToBoolType';
import { UpdatePantryUserFieldsFixed1764350314832 } from '../migrations/1764350314832-UpdatePantryUserFieldsFixed';
import { RemoveMultipleVolunteerTypes1764811878152 } from '../migrations/1764811878152-RemoveMultipleVolunteerTypes';
import { RemoveUnusedStatuses1764816885341 } from '../migrations/1764816885341-RemoveUnusedStatuses';
import { UpdatePantryFields1763762628431 } from '../migrations/1763762628431-UpdatePantryFields';

Expand Down Expand Up @@ -62,6 +63,7 @@ const config = {
AddVolunteerPantryUniqueConstraint1760033134668,
AllergyFriendlyToBoolType1763963056712,
UpdatePantryUserFieldsFixed1764350314832,
RemoveMultipleVolunteerTypes1764811878152,
RemoveUnusedStatuses1764816885341,
],
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class RemoveMultipleVolunteerTypes1764811878152
implements MigrationInterface
{
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE users
ALTER COLUMN role DROP DEFAULT;

CREATE TYPE users_role_enum_new AS ENUM (
'admin',
'volunteer',
'pantry',
'food_manufacturer'
);

ALTER TABLE users
ALTER COLUMN role
TYPE users_role_enum_new
USING (
CASE
WHEN role IN ('standard_volunteer', 'lead_volunteer')
THEN 'volunteer'
ELSE role::text
END
)::users_role_enum_new;

DROP TYPE users_role_enum;

ALTER TYPE users_role_enum_new
RENAME TO users_role_enum;

ALTER TABLE users
ALTER COLUMN role
SET DEFAULT 'volunteer';
`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE users
ALTER COLUMN role DROP DEFAULT;

CREATE TYPE users_role_enum_old AS ENUM (
'admin',
'lead_volunteer',
'standard_volunteer',
'pantry',
'food_manufacturer'
);

ALTER TABLE users
ALTER COLUMN role
TYPE users_role_enum_old
USING (
CASE
WHEN role = 'volunteer'
THEN 'standard_volunteer'
ELSE role::text
END
)::users_role_enum_old;

DROP TYPE users_role_enum;

ALTER TYPE users_role_enum_old
RENAME TO users_role_enum;

ALTER TABLE users
ALTER COLUMN role
SET DEFAULT 'standard_volunteer';
`);
}
}
27 changes: 13 additions & 14 deletions apps/backend/src/migrations/1764816885341-RemoveUnusedStatuses.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
import { MigrationInterface, QueryRunner } from "typeorm";
import { MigrationInterface, QueryRunner } from 'typeorm';

export class RemoveUnusedStatuses1764816885341 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE allocations DROP COLUMN IF EXISTS status;`,
);
await queryRunner.query(
`ALTER TABLE donation_items DROP COLUMN IF EXISTS status;`,
);
}

public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE allocations DROP COLUMN IF EXISTS status;`
);
await queryRunner.query(
`ALTER TABLE donation_items DROP COLUMN IF EXISTS status;`
);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE allocations
ADD COLUMN status VARCHAR(25) NOT NULL DEFAULT 'pending';
`);

await queryRunner.query(`
await queryRunner.query(`
ALTER TABLE donation_items
ADD COLUMN status VARCHAR(25) NOT NULL DEFAULT 'available';
`);
}
}
}
2 changes: 1 addition & 1 deletion apps/backend/src/pantries/dtos/pantry-application.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export class PantryApplicationDto {
@IsNotEmpty()
@MaxLength(255)
emailContactOther?: string;

@IsOptional()
@IsString()
@IsNotEmpty()
Expand Down
10 changes: 1 addition & 9 deletions apps/backend/src/users/types.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
export enum Role {
ADMIN = 'admin',
LEAD_VOLUNTEER = 'lead_volunteer',
STANDARD_VOLUNTEER = 'standard_volunteer',
VOLUNTEER = 'volunteer',
PANTRY = 'pantry',
FOODMANUFACTURER = 'food_manufacturer',
}

export const VOLUNTEER_ROLES: Role[] = [
Role.LEAD_VOLUNTEER,
Role.STANDARD_VOLUNTEER,
];

export type VolunteerType = (typeof VOLUNTEER_ROLES)[number];
2 changes: 1 addition & 1 deletion apps/backend/src/users/user.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export class User {
name: 'role',
enum: Role,
enumName: 'users_role_enum',
default: Role.STANDARD_VOLUNTEER,
default: Role.VOLUNTEER,
})
role: Role;

Expand Down
55 changes: 51 additions & 4 deletions apps/backend/src/users/users.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,25 @@ const mockUserService = mock<UsersService>();

const mockUser1: Partial<User> = {
id: 1,
role: Role.STANDARD_VOLUNTEER,
email: 'john@example.com',
firstName: 'John',
lastName: 'Doe',
phone: '1234567890',
role: Role.VOLUNTEER,
};

const mockUser2: Partial<User> = {
id: 2543210,
role: Role.LEAD_VOLUNTEER,
email: 'bobsmith@example.com',
firstName: 'Bob',
lastName: 'Smith',
phone: '9876',
role: Role.VOLUNTEER,
};

const mockUser3: Partial<User> = {
id: 3,
role: Role.STANDARD_VOLUNTEER,
role: Role.VOLUNTEER,
};

const mockPantries: Partial<Pantry>[] = [
Expand Down Expand Up @@ -68,6 +76,45 @@ describe('UsersController', () => {
expect(controller).toBeDefined();
});

describe('GET /volunteers', () => {
it('should return all volunteers', async () => {
const users: (Omit<Partial<User>, 'pantries'> & {
pantryIds: number[];
})[] = [
{
id: 1,
role: Role.VOLUNTEER,
pantryIds: [1],
},
{
id: 2,
role: Role.VOLUNTEER,
pantryIds: [2],
},
{
id: 3,
role: Role.ADMIN,
pantryIds: [3],
},
];

const volunteers = users.slice(0, 2);

mockUserService.getVolunteersAndPantryAssignments.mockResolvedValue(
volunteers as (Omit<User, 'pantries'> & { pantryIds: number[] })[],
);

const result = await controller.getAllVolunteers();

expect(result).toEqual(volunteers);
expect(result.length).toBe(2);
expect(result.every((u) => u.role === Role.VOLUNTEER)).toBe(true);
expect(
mockUserService.getVolunteersAndPantryAssignments,
).toHaveBeenCalled();
});
});

describe('GET /:id', () => {
it('should return a user by id', async () => {
mockUserService.findOne.mockResolvedValue(mockUser1 as User);
Expand Down Expand Up @@ -142,7 +189,7 @@ describe('UsersController', () => {
firstName: 'Jane',
lastName: 'Smith',
phone: '9876543210',
role: Role.STANDARD_VOLUNTEER,
role: Role.VOLUNTEER,
};

const error = new Error('Database error');
Expand Down
8 changes: 4 additions & 4 deletions apps/backend/src/users/users.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ import { PantriesService } from '../pantries/pantries.service';
const mockUserRepository = mock<Repository<User>>();
const mockPantriesService = mock<PantriesService>();

const mockUser = {
const mockUser: User = {
id: 1,
email: 'test@example.com',
firstName: 'John',
lastName: 'Doe',
phone: '1234567890',
role: Role.STANDARD_VOLUNTEER,
} as User;
role: Role.VOLUNTEER,
};

describe('UsersService', () => {
let service: UsersService;
Expand Down Expand Up @@ -203,7 +203,7 @@ describe('UsersService', () => {

describe('findUsersByRoles', () => {
it('should return users by roles', async () => {
const roles = [Role.ADMIN, Role.LEAD_VOLUNTEER];
const roles = [Role.ADMIN, Role.VOLUNTEER];
const users = [mockUser];
mockUserRepository.find.mockResolvedValue(users);

Expand Down
6 changes: 3 additions & 3 deletions apps/backend/src/users/users.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { InjectRepository } from '@nestjs/typeorm';
import { In, Repository } from 'typeorm';

import { User } from './user.entity';
import { Role, VOLUNTEER_ROLES } from './types';
import { Role } from './types';
import { validateId } from '../utils/validation.utils';
import { Pantry } from '../pantries/pantries.entity';
import { PantriesService } from '../pantries/pantries.service';
Expand Down Expand Up @@ -60,7 +60,7 @@ export class UsersService {

if (!volunteer)
throw new NotFoundException(`User ${volunteerId} not found`);
if (!VOLUNTEER_ROLES.includes(volunteer.role)) {
if (volunteer.role !== Role.VOLUNTEER) {
throw new BadRequestException(`User ${volunteerId} is not a volunteer`);
}
return volunteer;
Expand Down Expand Up @@ -106,7 +106,7 @@ export class UsersService {
async getVolunteersAndPantryAssignments(): Promise<
(Omit<User, 'pantries'> & { pantryIds: number[] })[]
> {
const volunteers = await this.findUsersByRoles(VOLUNTEER_ROLES);
const volunteers = await this.findUsersByRoles([Role.VOLUNTEER]);

return volunteers.map((v) => {
const { pantries, ...volunteerWithoutPantries } = v;
Expand Down
Loading
Loading