Skip to content

Commit ac622e1

Browse files
committed
fix pantry api body and add manufacturer app backend
1 parent c80fe92 commit ac622e1

20 files changed

Lines changed: 932 additions & 59 deletions

apps/backend/src/app.module.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { ConfigModule, ConfigService } from '@nestjs/config';
1010
import { MulterModule } from '@nestjs/platform-express';
1111
import typeorm from './config/typeorm';
1212
import { OrdersModule } from './orders/order.module';
13-
import { ManufacturerModule } from './foodManufacturers/manufacturer.module';
13+
import { ManufacturerModule } from './foodManufacturers/manufacturers.module';
1414
import { DonationModule } from './donations/donations.module';
1515
import { DonationItemsModule } from './donationItems/donationItems.module';
1616
import { AllocationModule } from './allocations/allocations.module';

apps/backend/src/config/typeorm.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { RemoveMultipleVolunteerTypes1764811878152 } from '../migrations/1764811
2727
import { RemoveUnusedStatuses1764816885341 } from '../migrations/1764816885341-RemoveUnusedStatuses';
2828
import { UpdatePantryFields1763762628431 } from '../migrations/1763762628431-UpdatePantryFields';
2929
import { PopulateDummyData1768501812134 } from '../migrations/1768501812134-populateDummyData';
30+
import { UpdateManufacturerEntity1768680807820 } from '../migrations/1768680807820-UpdateManufacturerEntity';
3031

3132
const config = {
3233
type: 'postgres',
@@ -67,6 +68,7 @@ const config = {
6768
RemoveMultipleVolunteerTypes1764811878152,
6869
RemoveUnusedStatuses1764816885341,
6970
PopulateDummyData1768501812134,
71+
UpdateManufacturerEntity1768680807820,
7072
],
7173
};
7274

apps/backend/src/donations/donations.entity.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import {
66
JoinColumn,
77
ManyToOne,
88
} from 'typeorm';
9-
import { FoodManufacturer } from '../foodManufacturers/manufacturer.entity';
9+
import { FoodManufacturer } from '../foodManufacturers/manufacturers.entity';
1010
import { DonationStatus } from './types';
1111

1212
@Entity('donations')

apps/backend/src/donations/donations.module.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import { AuthService } from '../auth/auth.service';
55
import { Donation } from './donations.entity';
66
import { DonationService } from './donations.service';
77
import { DonationsController } from './donations.controller';
8-
import { ManufacturerModule } from '../foodManufacturers/manufacturer.module';
9-
import { FoodManufacturer } from '../foodManufacturers/manufacturer.entity';
8+
import { ManufacturerModule } from '../foodManufacturers/manufacturers.module';
9+
import { FoodManufacturer } from '../foodManufacturers/manufacturers.entity';
1010

1111
@Module({
1212
imports: [

apps/backend/src/donations/donations.service.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Repository } from 'typeorm';
44
import { Donation } from './donations.entity';
55
import { DonationService } from './donations.service';
66
import { mock } from 'jest-mock-extended';
7-
import { FoodManufacturer } from '../foodManufacturers/manufacturer.entity';
7+
import { FoodManufacturer } from '../foodManufacturers/manufacturers.entity';
88

99
const mockDonationRepository = mock<Repository<Donation>>();
1010
const mockFoodManufacturerRepository = mock<Repository<FoodManufacturer>>();

apps/backend/src/donations/donations.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { InjectRepository } from '@nestjs/typeorm';
33
import { Repository } from 'typeorm';
44
import { Donation } from './donations.entity';
55
import { validateId } from '../utils/validation.utils';
6-
import { FoodManufacturer } from '../foodManufacturers/manufacturer.entity';
6+
import { FoodManufacturer } from '../foodManufacturers/manufacturers.entity';
77
import { DonationStatus } from './types';
88

99
@Injectable()
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import {
2+
ArrayNotEmpty,
3+
IsBoolean,
4+
IsEmail,
5+
IsEnum,
6+
IsNotEmpty,
7+
IsOptional,
8+
IsPhoneNumber,
9+
IsString,
10+
Length,
11+
MaxLength,
12+
} from 'class-validator';
13+
import { Allergen, DonateWastedFood, ManufacturerAttribute } from '../types';
14+
15+
export class FoodManufacturerApplicationDto {
16+
@IsString()
17+
@IsNotEmpty()
18+
@Length(1, 255)
19+
foodManufacturerName: string;
20+
21+
@IsString()
22+
@IsNotEmpty()
23+
@Length(1, 255)
24+
foodManufacturerWebsite: string;
25+
26+
@IsString()
27+
@IsNotEmpty()
28+
@Length(1, 255)
29+
contactFirstName: string;
30+
31+
@IsString()
32+
@IsNotEmpty()
33+
@Length(1, 255)
34+
contactLastName: string;
35+
36+
@IsEmail()
37+
@Length(1, 255)
38+
contactEmail: string;
39+
40+
@IsString()
41+
@IsNotEmpty()
42+
@IsPhoneNumber('US', {
43+
message:
44+
'contactPhone must be a valid phone number (make sure all the digits are correct)',
45+
})
46+
contactPhone: string;
47+
48+
@IsOptional()
49+
@IsString()
50+
@IsNotEmpty()
51+
@MaxLength(255)
52+
secondaryContactFirstName?: string;
53+
54+
@IsOptional()
55+
@IsString()
56+
@IsNotEmpty()
57+
@MaxLength(255)
58+
secondaryContactLastName?: string;
59+
60+
@IsOptional()
61+
@IsEmail()
62+
@IsNotEmpty()
63+
@MaxLength(255)
64+
secondaryContactEmail?: string;
65+
66+
@IsOptional()
67+
@IsString()
68+
@IsPhoneNumber('US', {
69+
message:
70+
'secondaryContactPhone must be a valid phone number (make sure all the digits are correct)',
71+
})
72+
@IsNotEmpty()
73+
secondaryContactPhone?: string;
74+
75+
@ArrayNotEmpty()
76+
@IsEnum(Allergen, { each: true })
77+
unlistedProductAllergens: Allergen[];
78+
79+
@ArrayNotEmpty()
80+
@IsEnum(Allergen, { each: true })
81+
facilityFreeAllergens: Allergen[];
82+
83+
@IsBoolean()
84+
productsGlutenFree: boolean;
85+
86+
@IsBoolean()
87+
productsContainSulfites: boolean;
88+
89+
@IsString()
90+
@IsNotEmpty()
91+
@Length(1, 255)
92+
productsSustainableExplanation: string;
93+
94+
@IsBoolean()
95+
inKindDonations: boolean;
96+
97+
@IsEnum(DonateWastedFood)
98+
donateWastedFood: DonateWastedFood;
99+
100+
@IsEnum(ManufacturerAttribute)
101+
manufacturerAttribute?: ManufacturerAttribute;
102+
103+
@IsOptional()
104+
@IsString()
105+
@IsNotEmpty()
106+
@MaxLength(255)
107+
additionalComments?: string;
108+
109+
@IsOptional()
110+
@IsBoolean()
111+
newsletterSubscription?: boolean;
112+
}

apps/backend/src/foodManufacturers/manufacturer.entity.ts

Lines changed: 0 additions & 29 deletions
This file was deleted.

apps/backend/src/foodManufacturers/manufacturer.module.ts

Lines changed: 0 additions & 8 deletions
This file was deleted.
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import { mock } from 'jest-mock-extended';
2+
import { FoodManufacturersService } from './manufacturers.service';
3+
import { FoodManufacturersController } from './manufacturers.controller';
4+
import { Test, TestingModule } from '@nestjs/testing';
5+
import { FoodManufacturer } from './manufacturers.entity';
6+
import { Allergen, DonateWastedFood, ManufacturerStatus } from './types';
7+
import { FoodManufacturerApplicationDto } from './dtos/manufacturer-application.dto';
8+
9+
const mockManufacturersService = mock<FoodManufacturersService>();
10+
11+
const mockManufacturer1: Partial<FoodManufacturer> = {
12+
foodManufacturerId: 1,
13+
foodManufacturerName: 'Good Foods Inc',
14+
status: ManufacturerStatus.PENDING,
15+
};
16+
17+
const mockManufacturer2: Partial<FoodManufacturer> = {
18+
foodManufacturerId: 2,
19+
foodManufacturerName: 'Healthy Eats LLC',
20+
status: ManufacturerStatus.PENDING,
21+
};
22+
23+
describe('FoodManufacturersController', () => {
24+
let controller: FoodManufacturersController;
25+
26+
beforeEach(async () => {
27+
jest.clearAllMocks();
28+
29+
const module: TestingModule = await Test.createTestingModule({
30+
controllers: [FoodManufacturersController],
31+
providers: [
32+
{
33+
provide: FoodManufacturersService,
34+
useValue: mockManufacturersService,
35+
},
36+
],
37+
}).compile();
38+
39+
controller = module.get<FoodManufacturersController>(
40+
FoodManufacturersController,
41+
);
42+
});
43+
44+
it('should be defined', () => {
45+
expect(controller).toBeDefined();
46+
});
47+
48+
describe('GET /pending', () => {
49+
it('should return pending food manufacturers', async () => {
50+
const mockManufacturers: Partial<FoodManufacturer>[] = [
51+
mockManufacturer1,
52+
mockManufacturer2,
53+
];
54+
55+
mockManufacturersService.getPendingManufacturers.mockResolvedValue(
56+
mockManufacturers as FoodManufacturer[],
57+
);
58+
59+
const result = await controller.getPendingManufacturers();
60+
61+
expect(result).toEqual(mockManufacturers);
62+
expect(result).toHaveLength(2);
63+
expect(result[0].foodManufacturerId).toBe(1);
64+
expect(result[1].foodManufacturerId).toBe(2);
65+
expect(
66+
mockManufacturersService.getPendingManufacturers,
67+
).toHaveBeenCalled();
68+
});
69+
});
70+
71+
describe('GET /:id', () => {
72+
it('should return a food manufacturer by id', async () => {
73+
mockManufacturersService.findOne.mockResolvedValue(
74+
mockManufacturer1 as FoodManufacturer,
75+
);
76+
77+
const result = await controller.getFoodManufacturer(1);
78+
79+
expect(result).toEqual(mockManufacturer1);
80+
expect(mockManufacturersService.findOne).toHaveBeenCalledWith(1);
81+
});
82+
});
83+
84+
describe('POST /api/manufacturers', () => {
85+
it('should submit a food manufacturer application', async () => {
86+
const mockApplicationData: FoodManufacturerApplicationDto = {
87+
foodManufacturerName: 'Good Foods Inc',
88+
foodManufacturerWebsite: 'https://goodfoods.example.com',
89+
contactFirstName: 'Alice',
90+
contactLastName: 'Johnson',
91+
contactEmail: 'alice.johnson@goodfoods.example.com',
92+
contactPhone: '555-123-4567',
93+
unlistedProductAllergens: [Allergen.EGG],
94+
facilityFreeAllergens: [Allergen.EGG],
95+
productsGlutenFree: true,
96+
productsContainSulfites: false,
97+
productsSustainableExplanation: 'We use eco-friendly packaging.',
98+
inKindDonations: true,
99+
donateWastedFood: DonateWastedFood.SOMETIMES,
100+
};
101+
102+
mockManufacturersService.addFoodManufacturer.mockResolvedValue();
103+
104+
await controller.submitFoodManufacturerApplication(mockApplicationData);
105+
106+
expect(mockManufacturersService.addFoodManufacturer).toHaveBeenCalledWith(
107+
mockApplicationData,
108+
);
109+
});
110+
});
111+
112+
describe('POST /approve/:id', () => {
113+
it('should approve a food manufacturer', async () => {
114+
mockManufacturersService.approve.mockResolvedValue();
115+
116+
await controller.approveManufacturer(1);
117+
118+
expect(mockManufacturersService.approve).toHaveBeenCalledWith(1);
119+
});
120+
});
121+
122+
describe('POST /deny/:id', () => {
123+
it('should deny a food manufacturer', async () => {
124+
mockManufacturersService.deny.mockResolvedValue();
125+
126+
await controller.denyManufacturer(1);
127+
128+
expect(mockManufacturersService.deny).toHaveBeenCalledWith(1);
129+
});
130+
});
131+
});

0 commit comments

Comments
 (0)