Skip to content
This repository has been archived by the owner on Dec 6, 2024. It is now read-only.

Commit

Permalink
fixed conflict
Browse files Browse the repository at this point in the history
  • Loading branch information
ShantelPeters committed Nov 28, 2024
2 parents a19afe3 + 1e140fc commit a90a319
Show file tree
Hide file tree
Showing 43 changed files with 1,479 additions and 5,882 deletions.
6 changes: 3 additions & 3 deletions backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions backend/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,10 @@ model manufacturer {
isVerified Boolean @default(false)
createdAt DateTime @default(now())
}

model flaggedProduct {
id String @id @default(uuid())
name String
image String
reason String
}
4 changes: 2 additions & 2 deletions backend/src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule } from '@nestjs/config';
import { ProductsModule } from './products/products.module';
import { ProductModule } from './product/product.module';
import { getEnvPath } from './common/utils/getEnvPath';
import { ManufacturerModule } from './manufacturer/manufacturer.module';
import { PrismaModule } from './prisma/prisma.module';
Expand All @@ -13,7 +13,7 @@ import { PrismaModule } from './prisma/prisma.module';
envFilePath: getEnvPath(),
isGlobal: true,
}),
ProductsModule,
ProductModule,
ManufacturerModule,
PrismaModule,
],
Expand Down
19 changes: 18 additions & 1 deletion backend/src/manufacturer/manufacturer.controller.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
import { Controller } from '@nestjs/common';
import { Controller, Post, Body, Get, Param } from '@nestjs/common';
import { ManufacturerService } from './manufacturer.service';
import { CreateManufacturerDto } from './dto/create-manufacturer.dto';

@Controller('manufacturer')
export class ManufacturerController {
constructor(private readonly manufacturerService: ManufacturerService) {}

@Post('register')
async registerManufacturer(
@Body() createManufacturerDto: CreateManufacturerDto
) {
return this.manufacturerService.registerManufacturer(createManufacturerDto);
}
@Get()
async getAllManufacturers() {
return this.manufacturerService.getAllManufacturers();
}

@Get(':id')
async getManufacturerById(@Param('id') id: string) {
return await this.manufacturerService.getManufacturerById(id);
}
}
54 changes: 53 additions & 1 deletion backend/src/manufacturer/manufacturer.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,59 @@
import { Injectable } from '@nestjs/common';
import {
Injectable,
ConflictException,
InternalServerErrorException,
NotFoundException,
} from '@nestjs/common';
import { PrismaService } from 'src/prisma/prisma.service';
import { CreateManufacturerDto } from './dto/create-manufacturer.dto';

@Injectable()
export class ManufacturerService {
constructor(private readonly prisma: PrismaService) {}

async registerManufacturer(createManufacturerDto: CreateManufacturerDto) {
const { name, address, email, rc, phone } = createManufacturerDto;

try {
return await this.prisma.manufacturer.create({
data: {
name,
address,
email,
rc,
phone,
},
});
} catch (error) {
if (error.code === 'P2002') {
throw new ConflictException(
'Manufacturer with this email or name already exists'
);
}
throw new InternalServerErrorException('Failed to register manufacturer');
}
}

async getAllManufacturers() {
try {
return await this.prisma.manufacturer.findMany();
} catch (error) {
throw new InternalServerErrorException(
'Failed to get manufacturers:',
error
);
}
}

async getManufacturerById(id: string) {
const manufacturer = await this.prisma.manufacturer.findUnique({
where: { id },
});

if (!manufacturer) {
throw new NotFoundException(`Manufacturer with ID ${id} not found`);
}

return manufacturer;
}
}
15 changes: 15 additions & 0 deletions backend/src/product/dto/flag-product.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { IsNotEmpty, IsString } from '@nestjs/class-validator';

export class FlagProductDto {
@IsNotEmpty()
@IsString()
name: string;

@IsNotEmpty()
@IsString()
image: string;

@IsNotEmpty()
@IsString()
reason: string;
}
119 changes: 119 additions & 0 deletions backend/src/product/product.controller.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { Test, TestingModule } from '@nestjs/testing';
import { ProductController } from './product.controller';
import { ProductService } from './product.service';
import { ProductDto } from './dto/product.dto';
import { HttpException } from '@nestjs/common';

describe('ProductsController', () => {
let controller: ProductController;
let service: ProductService;

beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [ProductController],
providers: [
{
provide: ProductService,
useValue: {
submitProduct: jest.fn(),
flagProduct: jest.fn(),
getFlaggedProducts: jest.fn(),
},
},
],
}).compile();

controller = module.get<ProductController>(ProductController);
service = module.get<ProductService>(ProductService);
});

it('should be defined', () => {
expect(controller).toBeDefined();
});

describe('submitProduct', () => {
it('should return IPFS hash on successful submission', async () => {
const dto: ProductDto = {
name: 'Test Product',
image: 'http://example.com/image.jpg',
manufacturer: 'Test Manufacturer',
manufactureDate: '2023-01-01',
expiryDate: '2025-01-01',
};

jest
.spyOn(service, 'uploadProduct')
.mockResolvedValue('QmUiPq1dRygSjwCBAqxvwaJxbGVFyHmPmSrL4YiytJFfkh');

const result = await controller.uploadProduct(dto);
expect(result).toEqual({
ipfs_hash: 'QmUiPq1dRygSjwCBAqxvwaJxbGVFyHmPmSrL4YiytJFfkh',
});
});

it('should throw an error if submission fails', async () => {
const dto: ProductDto = {
name: 'Test Product',
image: 'http://example.com/image.jpg',
manufacturer: 'Test Manufacturer',
manufactureDate: '2023-01-01',
expiryDate: '2025-01-01',
};

jest
.spyOn(service, 'uploadProduct')
.mockRejectedValue(new Error('Error uploading to IPFS'));

await expect(controller.uploadProduct(dto)).rejects.toThrow();
});
});

describe('flagProduct', () => {
const mockFlagProductDto = {
name: 'Suspicious Product',
image: 'http://example.com/suspicious.jpg',
reason: 'Fake product',
};

it('should successfully flag a product', async () => {
const mockResponse = {
id: '1',
...mockFlagProductDto,
};
jest.spyOn(service, 'flagProduct').mockResolvedValue(mockResponse);

const result = await controller.flagProduct(mockFlagProductDto);

expect(result).toEqual(mockResponse);
expect(service.flagProduct).toHaveBeenCalledWith(mockFlagProductDto);
});

it('should throw HttpException when flagging fails', async () => {
jest
.spyOn(service, 'flagProduct')
.mockRejectedValue(new Error('Failed to flag'));

await expect(controller.flagProduct(mockFlagProductDto)).rejects.toThrow(
HttpException
);
});
});

describe('getFlaggedProducts', () => {
it('should return all flagged products', async () => {
const mockFlaggedProducts = [
{ id: '1', name: 'Product 1', image: 'image1.jpg', reason: 'Fake' },
{ id: '2', name: 'Product 2', image: 'image2.jpg', reason: 'Expired' },
];

jest
.spyOn(service, 'getFlaggedProducts')
.mockResolvedValue(mockFlaggedProducts);

const result = await controller.getFlaggedProducts();

expect(result).toEqual(mockFlaggedProducts);
expect(service.getFlaggedProducts).toHaveBeenCalled();
});
});
});
65 changes: 65 additions & 0 deletions backend/src/product/product.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import {
Controller,
Body,
Post,
HttpException,
HttpStatus,
Get,
Param,
} from '@nestjs/common';
import { ProductDto } from './dto/product.dto';
import { FlagProductDto } from './dto/flag-product.dto';
import { ProductService } from './product.service';

@Controller('product')
export class ProductController {
constructor(private readonly productService: ProductService) {}

@Post('upload')
async uploadProduct(@Body() productDto: ProductDto) {
try {
const ipfsHash = await this.productService.uploadProduct(productDto);
console.log('Service Result:', ipfsHash);
return { ipfs_hash: ipfsHash };
} catch (error) {
console.error('Error:', error.message);

throw new HttpException(
{ error: error.message },
HttpStatus.INTERNAL_SERVER_ERROR
);
}
}

@Get(':productId')
async getProductDetails(@Param('productId') productId: string) {
try {
return this.productService.getProductDetails(productId);
} catch (error) {
console.error('Error:', error.message);

throw new HttpException(
{ error: error.message },
HttpStatus.INTERNAL_SERVER_ERROR
);
}
}

@Get()
async getFlaggedProducts() {
return this.productService.getFlaggedProducts();
}

@Post('flag')
async flagProduct(@Body() flagProductDto: FlagProductDto) {
try {
return await this.productService.flagProduct(flagProductDto);
} catch (error) {
console.error('Error:', error.message);
throw new HttpException(
{ error: error.message },
HttpStatus.INTERNAL_SERVER_ERROR
);
}
}
}
9 changes: 9 additions & 0 deletions backend/src/product/product.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { Module } from '@nestjs/common';
import { ProductController } from './product.controller';
import { ProductService } from './product.service';

@Module({
controllers: [ProductController],
providers: [ProductService],
})
export class ProductModule {}
Loading

0 comments on commit a90a319

Please sign in to comment.