Skip to content

Commit

Permalink
Development (#246)
Browse files Browse the repository at this point in the history
* update github workflow yaml file

* rename the frontend folder

* create the checkout button

* create dynamic route

* update the routing definition and getmenubyId API call

* Merge branch 'development' of github.com:olasunkanmi-SE/restaurant into development

* remove the styling for menu name

* optimize the components

* conditionally render the checkout component

* remove code smell

* Merge branch 'release' into development

* update the menu item page and clean up the navigation

* create the delete menu repo method, service and controller

* create the delete menu API
  • Loading branch information
olasunkanmi-SE authored Mar 26, 2023
1 parent 80d79ad commit 0cffaf0
Show file tree
Hide file tree
Showing 15 changed files with 72 additions and 33 deletions.
1 change: 1 addition & 0 deletions backend/src/application/constants/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export const APIResponseMessage = {
emailHeaderError: 'user email is required',
correlationIdHeaderError: 'correlationId is required',
invalidEmailHeaderError: 'Invalid user email address',
invalidCorrelationId: 'Invalid correlationId',
emailHeader: 'x-user-email',
correlationIdHeader: 'x-correlation-id',
authorizationHeader: 'authorization',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ export interface IMenuRepository {
getMenus(filterQuery: FilterQuery<Menu>): Promise<any | any[]>;
getMenuById(id: Types.ObjectId): Promise<any>;
createMenu(menuModel: MenuDataModel): Promise<Result<any>>;
deleteMenu(id: Types.ObjectId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@ import { Connection, FilterQuery, Model, Types } from 'mongoose';
import { GenericDocumentRepository } from '../../../infrastructure/database';
import { Item } from '../../../item';
import { IMenuRepository } from '../repositories/interfaces/menu-repository.interface';
import { AddonMapper } from './../../../addon/addon.mapper';
import { TYPES } from './../../../application/constants/types';
import { Result } from './../../../domain/result/result';
import { ItemMapper } from './../../../item/item.mapper';
import { Menu } from './../../../menu/menu';
import { MenuMapper } from './../../../menu/menu.mapper';
import { IAddonRepository, IItemRepository } from './interfaces';
import { IItemRepository } from './interfaces';
import { MenuDataModel, MenuDocument } from './schemas/menu.schema';

@Injectable()
Expand All @@ -19,10 +17,7 @@ export class MenuRepository extends GenericDocumentRepository<Menu, MenuDocument
constructor(
@InjectModel(MenuDataModel.name) menuDataModel: Model<MenuDocument>,
@Inject(TYPES.IItemRepository) private readonly itemRepository: IItemRepository,
@Inject(TYPES.IAddonRepository) private readonly addonsRepository: IAddonRepository,
private readonly addonMapper: AddonMapper,
private readonly itemMapper: ItemMapper,
@InjectConnection() connection: Connection,
@InjectConnection() readonly connection: Connection,
menuMapper: MenuMapper,
) {
super(menuDataModel, connection, menuMapper);
Expand Down Expand Up @@ -115,4 +110,21 @@ export class MenuRepository extends GenericDocumentRepository<Menu, MenuDocument
const menu = (await this.getMenuById(document.id)).getValue();
return menu;
}

async deleteMenu(id: Types.ObjectId): Promise<boolean> {
const session = await this.startSession();
try {
session.startTransaction();
const response = await this.getMenuById(id);
const menu = response.getValue();
const itemIds = menu.items.map((item) => item.id);
await Promise.all([this.deleteOne({ _id: id }), this.itemRepository.deleteMany({ _id: { $in: itemIds } })]);
session.commitTransaction();
return true;
} catch (error) {
session.abortTransaction();
} finally {
session.endSession();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ export interface IGenericDocument<TEntity, T> {
insertMany(docs: any): Promise<Result<TEntity[]>>;

updateOne(filter: any, query: any): Promise<Result<TEntity>>;

deleteOne(filterQuery: FilterQuery<T>): Promise<boolean>;
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { IGenericDocument } from './generic-document.interface';
export abstract class GenericDocumentRepository<TEntity, T extends Document> implements IGenericDocument<TEntity, T> {
constructor(
protected readonly DocumentModel: Model<T>,
private readonly connection: Connection,
readonly connection: Connection,
private readonly mapper: any,
) {}

Expand Down Expand Up @@ -92,6 +92,11 @@ export abstract class GenericDocumentRepository<TEntity, T extends Document> imp
return (await result).deletedCount >= 1;
}

async deleteOne(filterQuery: FilterQuery<T>): Promise<boolean> {
const result = this.DocumentModel.deleteOne(filterQuery);
return (await result).deletedCount === 1;
}

async startSession(): Promise<ClientSession> {
return await this.connection.startSession();
}
Expand Down
31 changes: 18 additions & 13 deletions backend/src/infrastructure/middlewares/context.middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,25 @@ export class ContextMiddleWare implements NestMiddleware {
for (const [key, value] of Object.entries(headers)) {
if (key === APIResponseMessage.emailHeader) {
const isValidEmail = Regex.isEmail(value.toString());
if (!isValidEmail) {
errors.email = APIResponseMessage.invalidEmailHeaderError;
}
if (!isValidEmail) errors.email = APIResponseMessage.invalidEmailHeaderError;
}

if (key === APIResponseMessage.correlationIdHeader) {
const isValidUUID = Regex.isUUID(value);
if (!isValidUUID) errors.correlationId = APIResponseMessage.invalidCorrelationId;
}

if (Object.getOwnPropertyNames(errors).length) {
throwApplicationError(HttpStatus.BAD_REQUEST, errors);
}

const email = req.headers[APIResponseMessage.emailHeader] as string;
const correlationId = req.headers[APIResponseMessage.correlationIdHeader] as string;
const token = (req.headers[APIResponseMessage.authorizationHeader] as string) ?? '';
const role = (req.headers[APIResponseMessage.roleHeader] as string) ?? '';
const context: Context = new Context(email, correlationId, token, role);
this.contextService.setContext(context);
next();
}
if (Object.getOwnPropertyNames(errors).length) {
throwApplicationError(HttpStatus.BAD_REQUEST, errors);
}
const email = req.headers[APIResponseMessage.emailHeader] as string;
const correlationId = req.headers[APIResponseMessage.correlationIdHeader] as string;
const token = (req.headers[APIResponseMessage.authorizationHeader] as string) ?? '';
const role = (req.headers[APIResponseMessage.roleHeader] as string) ?? '';
const context: Context = new Context(email, correlationId, token, role);
this.contextService.setContext(context);
next();
}
}
7 changes: 6 additions & 1 deletion backend/src/infrastructure/utilities/regex.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
export class Regex {
static isEmail(prop: string): boolean {
const regex = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/;
return prop.match(regex) ? true : false;
return Boolean(prop.match(regex));
}

static isUUID(prop): boolean {
const regex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-5][0-9a-f]{3}-[089ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
return Boolean(prop.match(regex));
}
}
3 changes: 0 additions & 3 deletions backend/src/item/item.mapper.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
import { MenuMapper } from './../menu/menu.mapper';
import { Injectable } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core';
import { AuditMapper } from '../audit/audit.mapper';
import { IMapper } from '../domain/mapper/mapper';
import { ItemDataModel } from '../infrastructure/data_access/repositories/schemas/item.schema';
import { Item } from './item';

@Injectable()
export class ItemMapper implements IMapper<Item, ItemDataModel> {
private menuMapper: MenuMapper;
constructor(private readonly auditMapper: AuditMapper) {}
toPersistence(entity: Item): ItemDataModel {
const { id, name, description, price, maximumPermitted } = entity;
Expand Down
3 changes: 1 addition & 2 deletions backend/src/menu/create-menu.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@ export class CreateMenuDTO {
@IsString()
readonly imageUrl: string;

@IsOptional()
@IsArray()
readonly itemIds?: Types.ObjectId[];
readonly itemIds: Types.ObjectId[];

@IsNotEmpty()
readonly categoryId: Types.ObjectId;
Expand Down
1 change: 1 addition & 0 deletions backend/src/menu/menu-service.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ export interface IMenuService {
getMenus(): Promise<Result<IMenuResponseDTO[]>>;
getMenuById(id: Types.ObjectId): Promise<Result<IMenuResponseDTO>>;
updateMenu(props: any, id: Types.ObjectId): Promise<Result<IMenuResponseDTO>>;
deleteMenu(id: Types.ObjectId): Promise<Result<boolean>>;
}
7 changes: 6 additions & 1 deletion backend/src/menu/menu.controller.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Result } from './../domain/result/result';
import { IMenuResponseDTO } from './menu-response.dto';
import { CreateMenuDTO } from './create-menu.schema';
import { Body, Controller, Get, Inject, Param, Patch, Post } from '@nestjs/common';
import { Body, Controller, Delete, Get, Inject, Param, Patch, Post } from '@nestjs/common';
import { IMenuService } from './menu-service.interface';
import { TYPES } from '../application';
import { Types } from 'mongoose';
Expand Down Expand Up @@ -33,4 +33,9 @@ export class MenuController {
): Promise<Result<IMenuResponseDTO>> {
return this.menuService.updateMenu(req, menuId);
}

@Delete('/:id')
async deleteMenu(@Param('id') menuId: Types.ObjectId): Promise<Result<boolean>> {
return this.menuService.deleteMenu(menuId);
}
}
9 changes: 8 additions & 1 deletion backend/src/menu/menu.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ export class MenuService implements IMenuService {
private readonly contextService: IContextService,
@Inject(TYPES.IMerchantService) private readonly merchantService: IMerchantService,
@Inject(TYPES.IItemRepository) private readonly itemRepository: IItemRepository,
@Inject(TYPES.IAddonRepository) private readonly addonRepository: IAddonRepository,
private readonly categoryRepository: CategoryRepository,
private readonly menuMapper: MenuMapper,
) {
Expand Down Expand Up @@ -108,4 +107,12 @@ export class MenuService implements IMenuService {
: throwApplicationError(HttpStatus.INTERNAL_SERVER_ERROR, 'Could not update menu');
return response;
}

async deleteMenu(id: Types.ObjectId): Promise<Result<boolean>> {
const response = await this.menuRepository.deleteMenu(id);
if (!response) {
throwApplicationError(HttpStatus.INTERNAL_SERVER_ERROR, 'Menu code not be deleted');
}
return Result.ok(true);
}
}
1 change: 1 addition & 0 deletions backend/src/restaurant/restaurant.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ export class RestaurantService implements IRestaurantService {
async createRestaurant(props: CreateRestaurantDTO): Promise<Result<IRestaurantResponseDTO>> {
const session = await this.connection.startSession();
try {
session.startTransaction();
await this.merchantService.validateContext();
const restaurantEntity: Result<Restaurant[]> = await this.restaurantRepository.find({});
const restaurants = restaurantEntity.getValue();
Expand Down
2 changes: 0 additions & 2 deletions frontend/src/components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import Navbar from "react-bootstrap/Navbar";
import { NavLink, Outlet, useNavigate } from "react-router-dom";
import { useShoppingCart } from "../contexts";

type NavStyleFunction = ({ isActive }: { isActive: boolean }) => React.CSSProperties;

export const Navigation = () => {
const { quantity } = useShoppingCart();
const navigate = useNavigate();
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/components/StoreItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ export const StoreItem = ({ name, description, imageUrl, basePrice, items }: sto
<i style={{ backgroundColor: "#f7a278", color: "#fff", padding: "4px" }}>More Portion</i>
</p>
</div>
{/* <>
<>
{items.map((item) => (
<div className="mb-5" key={item.id}>
<FoodItemList name={item.name} price={item.price} />
</div>
))}
</> */}
</>
</Col>
</Row>
</>
Expand Down

0 comments on commit 0cffaf0

Please sign in to comment.