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: 2 additions & 0 deletions apps/backend/src/config/typeorm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { RemoveMultipleVolunteerTypes1764811878152 } from '../migrations/1764811
import { RemoveUnusedStatuses1764816885341 } from '../migrations/1764816885341-RemoveUnusedStatuses';
import { UpdatePantryFields1763762628431 } from '../migrations/1763762628431-UpdatePantryFields';
import { PopulateDummyData1768501812134 } from '../migrations/1768501812134-populateDummyData';
import { RemovePantryFromOrders1769316004958 } from '../migrations/1769316004958-RemovePantryFromOrders';

const config = {
type: 'postgres',
Expand Down Expand Up @@ -67,6 +68,7 @@ const config = {
RemoveMultipleVolunteerTypes1764811878152,
RemoveUnusedStatuses1764816885341,
PopulateDummyData1768501812134,
RemovePantryFromOrders1769316004958,
],
};

Expand Down
1 change: 0 additions & 1 deletion apps/backend/src/foodRequests/request.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import { AWSS3Service } from '../aws/aws-s3.service';
import { FilesInterceptor } from '@nestjs/platform-express';
import * as multer from 'multer';
import { OrdersService } from '../orders/order.service';
import { Order } from '../orders/order.entity';
import { RequestSize } from './types';
import { OrderStatus } from '../orders/types';

Expand Down
2 changes: 0 additions & 2 deletions apps/backend/src/foodRequests/request.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,6 @@ describe('RequestsService', () => {
it('should update and return the food request with new delivery details', async () => {
const mockOrder: Partial<Order> = {
orderId: 1,
pantry: null,
request: null,
requestId: 1,
foodManufacturer: null,
Expand Down Expand Up @@ -315,7 +314,6 @@ describe('RequestsService', () => {
it('should throw an error if the order does not have a food manufacturer', async () => {
const mockOrder: Partial<Order> = {
orderId: 1,
pantry: null,
request: null,
requestId: 1,
foodManufacturer: null,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { MigrationInterface, QueryRunner } from 'typeorm';

export class RemovePantryFromOrders1769316004958 implements MigrationInterface {
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE orders
DROP CONSTRAINT IF EXISTS fk_pantry,
DROP COLUMN IF EXISTS pantry_id;
`);
}

public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(`
ALTER TABLE orders
ADD COLUMN pantry_id INT;
UPDATE orders o
SET pantry_id = fr.pantry_id
FROM food_requests fr
WHERE o.request_id = fr.request_id;
ALTER TABLE orders
ALTER COLUMN pantry_id SET NOT NULL,
ADD CONSTRAINT fk_pantry FOREIGN KEY(pantry_id) REFERENCES pantries(pantry_id);
`);
}
}
8 changes: 0 additions & 8 deletions apps/backend/src/orders/order.entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {
JoinColumn,
} from 'typeorm';
import { FoodRequest } from '../foodRequests/request.entity';
import { Pantry } from '../pantries/pantries.entity';
import { FoodManufacturer } from '../foodManufacturers/manufacturer.entity';
import { OrderStatus } from './types';

Expand All @@ -16,13 +15,6 @@ export class Order {
@PrimaryGeneratedColumn({ name: 'order_id' })
orderId: number;

@ManyToOne(() => Pantry, { nullable: false })
@JoinColumn({
name: 'pantry_id',
referencedColumnName: 'pantryId',
})
pantry: Pantry;

@ManyToOne(() => FoodRequest, { nullable: false })
@JoinColumn({
name: 'request_id',
Expand Down
3 changes: 2 additions & 1 deletion apps/backend/src/orders/order.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import { Order } from './order.entity';
import { OrdersService } from './order.service';
import { JwtStrategy } from '../auth/jwt.strategy';
import { AuthService } from '../auth/auth.service';
import { Pantry } from '../pantries/pantries.entity';
import { AllocationModule } from '../allocations/allocations.module';

@Module({
imports: [TypeOrmModule.forFeature([Order]), AllocationModule],
imports: [TypeOrmModule.forFeature([Order, Pantry]), AllocationModule],
controllers: [OrdersController],
providers: [OrdersService, AuthService, JwtStrategy],
exports: [OrdersService],
Expand Down
114 changes: 108 additions & 6 deletions apps/backend/src/orders/order.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ import {
ServeAllergicChildren,
} from '../pantries/types';
import { OrderStatus } from './types';
import { FoodRequest } from '../foodRequests/request.entity';

const mockOrdersRepository = mock<Repository<Order>>();
const mockPantryRepository = mock<Repository<Pantry>>();

const mockPantry: Partial<Pantry> = {
pantryId: 1,
Expand Down Expand Up @@ -54,6 +56,10 @@ describe('OrdersService', () => {
provide: getRepositoryToken(Order),
useValue: mockOrdersRepository,
},
{
provide: getRepositoryToken(Pantry),
useValue: mockPantryRepository,
},
],
}).compile();

Expand All @@ -63,6 +69,7 @@ describe('OrdersService', () => {
beforeEach(() => {
qb = {
leftJoinAndSelect: jest.fn().mockReturnThis(),
leftJoin: jest.fn().mockReturnThis(),
select: jest.fn().mockReturnThis(),
andWhere: jest.fn().mockReturnThis(),
getMany: jest.fn().mockResolvedValue([]),
Expand Down Expand Up @@ -108,17 +115,14 @@ describe('OrdersService', () => {
{
orderId: 3,
status: OrderStatus.DELIVERED,
pantry: { ...(mockPantry as Pantry), pantryName: 'Test Pantry' },
},
{
orderId: 4,
status: OrderStatus.DELIVERED,
pantry: { ...(mockPantry as Pantry), pantryName: 'Test Pantry 2' },
},
{
orderId: 5,
status: OrderStatus.DELIVERED,
pantry: { ...(mockPantry as Pantry), pantryName: 'Test Pantry 3' },
},
];

Expand Down Expand Up @@ -156,17 +160,14 @@ describe('OrdersService', () => {
{
orderId: 3,
status: OrderStatus.DELIVERED,
pantry: { ...(mockPantry as Pantry), pantryName: 'Test Pantry 1' },
},
{
orderId: 4,
status: OrderStatus.DELIVERED,
pantry: { ...(mockPantry as Pantry), pantryName: 'Test Pantry 2' },
},
{
orderId: 5,
status: OrderStatus.DELIVERED,
pantry: { ...(mockPantry as Pantry), pantryName: 'Test Pantry 2' },
},
];

Expand All @@ -189,4 +190,105 @@ describe('OrdersService', () => {
);
});
});

describe('findOrderPantry', () => {
it('should return pantry for given order', async () => {
const mockFoodRequest: Partial<FoodRequest> = {
requestId: 1,
pantryId: 1,
};

const mockOrder: Partial<Order> = {
orderId: 1,
requestId: 1,
request: mockFoodRequest as FoodRequest,
};

(mockOrdersRepository.findOne as jest.Mock).mockResolvedValue(mockOrder);
(mockPantryRepository.findOneBy as jest.Mock).mockResolvedValue(
mockPantry as Pantry,
);

const result = await service.findOrderPantry(1);

expect(result).toEqual(mockPantry);
expect(mockPantryRepository.findOneBy).toHaveBeenCalledWith({
pantryId: 1,
});
});

it('should throw NotFoundException if order not found', async () => {
(mockOrdersRepository.findOne as jest.Mock).mockResolvedValue(null);

await expect(service.findOrderPantry(999)).rejects.toThrow(
'Order 999 not found',
);
});

it('should throw NotFoundException if pantry not found', async () => {
const mockFoodRequest: Partial<FoodRequest> = {
requestId: 1,
pantryId: 999,
};

const mockOrder: Partial<Order> = {
orderId: 1,
requestId: 1,
request: mockFoodRequest as FoodRequest,
};

(mockOrdersRepository.findOne as jest.Mock).mockResolvedValue(mockOrder);
(mockPantryRepository.findOneBy as jest.Mock).mockResolvedValue(null);

await expect(service.findOrderPantry(1)).rejects.toThrow(
'Pantry 999 not found',
);
});
});

describe('getOrdersByPantry', () => {
it('should return orders for given pantry', async () => {
const mockOrders: Partial<Order>[] = [
{ orderId: 1, requestId: 1 },
{ orderId: 2, requestId: 2 },
];

(mockPantryRepository.findOneBy as jest.Mock).mockResolvedValue(
mockPantry as Pantry,
);
(mockOrdersRepository.find as jest.Mock).mockResolvedValue(
mockOrders as Order[],
);

const result = await service.getOrdersByPantry(1);

expect(result).toEqual(mockOrders);
expect(mockPantryRepository.findOneBy).toHaveBeenCalledWith({
pantryId: 1,
});
expect(mockOrdersRepository.find).toHaveBeenCalledWith({
where: { request: { pantryId: 1 } },
relations: ['request'],
});
});

it('should throw NotFoundException if pantry does not exist', async () => {
(mockPantryRepository.findOneBy as jest.Mock).mockResolvedValue(null);

await expect(service.getOrdersByPantry(999)).rejects.toThrow(
'Pantry 999 not found',
);
});

it('should return empty array if pantry has no orders', async () => {
(mockPantryRepository.findOneBy as jest.Mock).mockResolvedValue(
mockPantry as Pantry,
);
(mockOrdersRepository.find as jest.Mock).mockResolvedValue([]);

const result = await service.getOrdersByPantry(1);

expect(result).toEqual([]);
});
});
});
30 changes: 19 additions & 11 deletions apps/backend/src/orders/order.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,16 @@ import { OrderStatus } from './types';

@Injectable()
export class OrdersService {
constructor(@InjectRepository(Order) private repo: Repository<Order>) {}
constructor(
@InjectRepository(Order) private repo: Repository<Order>,
@InjectRepository(Pantry) private pantryRepo: Repository<Pantry>,
) {}

async getAll(filters?: { status?: string; pantryNames?: string[] }) {
const qb = this.repo
.createQueryBuilder('order')
.leftJoinAndSelect('order.pantry', 'pantry')
.leftJoinAndSelect('order.request', 'request')
Copy link

@dburkhart07 dburkhart07 Jan 26, 2026

Choose a reason for hiding this comment

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

This current call when I tested it returned no info on the volunteers or pantry. With this relation being removed, however, I wonder if we still even need them. Can you check with Sam on this, and whether we even still want the pantryNames aspect of the filter, or just reclarify what fields we would want this endpoint to return?

.leftJoin('pantries', 'pantry', 'pantry.pantryId = request.pantryId')
.leftJoinAndSelect('pantry.volunteers', 'volunteers')
.select([
'order.orderId',
Expand Down Expand Up @@ -82,17 +86,16 @@ export class OrdersService {
}

async findOrderPantry(orderId: number): Promise<Pantry> {
validateId(orderId, 'Order');

Choose a reason for hiding this comment

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

Why did we delete this validation?

Copy link
Member Author

Choose a reason for hiding this comment

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

it is validated when i call this.findOrderFoodRequest


const order = await this.repo.findOne({
where: { orderId },
relations: ['pantry'],
const request = await this.findOrderFoodRequest(orderId);
const pantry = await this.pantryRepo.findOneBy({
pantryId: request.pantryId,
});

if (!order) {
throw new NotFoundException(`Order ${orderId} not found`);
if (!pantry) {
throw new NotFoundException(`Pantry ${request.pantryId} not found`);
}
return order.pantry;

return pantry;
}

async findOrderFoodRequest(orderId: number): Promise<FoodRequest> {
Expand Down Expand Up @@ -140,8 +143,13 @@ export class OrdersService {
async getOrdersByPantry(pantryId: number): Promise<Order[]> {
validateId(pantryId, 'Pantry');

const pantry = await this.pantryRepo.findOneBy({ pantryId });
if (!pantry) {
throw new NotFoundException(`Pantry ${pantryId} not found`);
}

const orders = await this.repo.find({
where: { pantry: { pantryId } },
where: { request: { pantryId } },
relations: ['request'],
});

Expand Down
Loading