moahmedwafy commited on
Commit
21a9b1b
·
1 Parent(s): 8a85206

feat: eat custom meal

Browse files
src/common/enums/activity-type.enum.ts CHANGED
@@ -1 +1,3 @@
1
- export enum ActivityType {}
 
 
 
1
+ export enum ActivityType {
2
+ EAT_CUSTOM_MEAL = "eat-custom-meal",
3
+ }
src/common/models/activity.model.ts CHANGED
@@ -5,24 +5,26 @@ import { WorkoutDocument } from "./workout.model";
5
  import { ExerciseDocument } from "./exercise.model";
6
  import { ActivityType } from "@common/enums/activity-type.enum";
7
  const { Schema } = mongoose;
8
- //related_item
9
- //meta_data
10
- //activity type
11
- //related id
12
- //user id
13
- //created_//at
14
- //
15
 
16
- export type RelatedItem = MealDocument | MealPlanDocument | WorkoutDocument | ExerciseDocument;
 
 
 
 
 
 
 
 
 
17
  export type AMetaData = {};
18
 
19
  export interface IActivity {
20
  related_item?: RelatedItem;
21
- meta_data: AMetaData;
22
  activity_type: ActivityType;
23
- related_id: string | Types.ObjectId;
24
  user_id: string | Types.ObjectId;
25
- created_at: Date;
26
  }
27
 
28
  const activitySchema = new Schema({
@@ -40,7 +42,7 @@ const activitySchema = new Schema({
40
  },
41
  related_id: {
42
  type: Schema.Types.ObjectId,
43
- required: true,
44
  },
45
  user_id: {
46
  type: Schema.Types.ObjectId,
 
5
  import { ExerciseDocument } from "./exercise.model";
6
  import { ActivityType } from "@common/enums/activity-type.enum";
7
  const { Schema } = mongoose;
 
 
 
 
 
 
 
8
 
9
+ export type RelatedItem = | MealDocument
10
+ | MealPlanDocument
11
+ | WorkoutDocument
12
+ | ExerciseDocument
13
+ | {
14
+ ingredients: {
15
+ id: string,
16
+ noServings: number
17
+ }[];
18
+ }
19
  export type AMetaData = {};
20
 
21
  export interface IActivity {
22
  related_item?: RelatedItem;
23
+ meta_data?: AMetaData;
24
  activity_type: ActivityType;
25
+ related_id?: string | Types.ObjectId;
26
  user_id: string | Types.ObjectId;
27
+ created_at?: Date;
28
  }
29
 
30
  const activitySchema = new Schema({
 
42
  },
43
  related_id: {
44
  type: Schema.Types.ObjectId,
45
+ required: false,
46
  },
47
  user_id: {
48
  type: Schema.Types.ObjectId,
src/lib/events/events-manager.ts CHANGED
@@ -1,3 +1,4 @@
 
1
  import EventEmitter from 'node:events';
2
 
3
  /**
@@ -12,7 +13,7 @@ export class EventsManager {
12
  this.emitter = new EventEmitter();
13
  }
14
 
15
- private static getInstance(): EventsManager {
16
  if (!EventsManager.instance) {
17
  EventsManager.instance = new EventsManager();
18
  }
@@ -25,8 +26,12 @@ export class EventsManager {
25
  * @param event The event name.
26
  * @param listener The listener function.
27
  */
28
- public on(event: string, listener: (...args: any[]) => void): void {
29
- this.emitter.on(event, listener);
 
 
 
 
30
  }
31
 
32
  /**
@@ -34,13 +39,14 @@ export class EventsManager {
34
  * @param event The event name.
35
  * @param listener The listener function.
36
  */
37
- public emit(event: string, ...args: any[]): void {
38
- this.emitter.emit(event, ...args);
39
  }
40
 
41
  /**
42
  * Create a queue to store events.
43
  * @returns An EventsQueue object.
 
44
  */
45
  public createQueue() {
46
  return new class EventsQueue {
@@ -56,7 +62,7 @@ export class EventsManager {
56
 
57
  public process() {
58
  this.queue.forEach((item) => {
59
- EventsManager.getInstance().emit(item.event, ...item.args);
60
  });
61
 
62
  this.clear();
 
1
+ import { asyncHandler } from '@helpers/async-handler';
2
  import EventEmitter from 'node:events';
3
 
4
  /**
 
13
  this.emitter = new EventEmitter();
14
  }
15
 
16
+ public static getInstance(): EventsManager {
17
  if (!EventsManager.instance) {
18
  EventsManager.instance = new EventsManager();
19
  }
 
26
  * @param event The event name.
27
  * @param listener The listener function.
28
  */
29
+ public static on(event: string, listener: (...args: any[]) => void | Promise<void>): void {
30
+ try {
31
+ EventsManager.getInstance().emitter.on(event, asyncHandler(listener));
32
+ } catch (error) {
33
+ console.error(error);
34
+ }
35
  }
36
 
37
  /**
 
39
  * @param event The event name.
40
  * @param listener The listener function.
41
  */
42
+ public static emit(event: string, ...args: any[]): void {
43
+ EventsManager.getInstance().emitter.emit(event, ...args);
44
  }
45
 
46
  /**
47
  * Create a queue to store events.
48
  * @returns An EventsQueue object.
49
+ * @see EventsQueue
50
  */
51
  public createQueue() {
52
  return new class EventsQueue {
 
62
 
63
  public process() {
64
  this.queue.forEach((item) => {
65
+ EventsManager.emit(item.event, ...item.args);
66
  });
67
 
68
  this.clear();
src/modules/users/modules/meals/controller/meals.controller.ts CHANGED
@@ -9,11 +9,15 @@ import { Controller } from "@lib/decorators/controller.decorator";
9
  import { serialize } from "@helpers/serialize";
10
  import { ControllerMiddleware } from "@lib/decorators/controller-middleware.decorator";
11
  import { UsersGuardMiddleware } from "modules/users/common/guards/users.guard";
12
- import { SwaggerGet } from "@lib/decorators/swagger-routes.decorator";
13
  import { SwaggerResponse } from "@lib/decorators/swagger-response.decorator";
14
  import { SwaggerSummary } from "@lib/decorators/swagger-summary.decorator";
15
  import { SwaggerDescription } from "@lib/decorators/swagger-description.decorator";
16
  import { SwaggerQuery } from "@lib/decorators/swagger-query.decorator";
 
 
 
 
17
 
18
 
19
  @Controller("/user/meals")
@@ -23,6 +27,7 @@ export class UsersMealsController extends BaseController {
23
 
24
  setRoutes(): void {
25
  this.router.get("/", asyncHandler(this.list));
 
26
  }
27
 
28
  @SwaggerGet()
@@ -52,4 +57,21 @@ export class UsersMealsController extends BaseController {
52
  res
53
  );
54
  };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  }
 
 
9
  import { serialize } from "@helpers/serialize";
10
  import { ControllerMiddleware } from "@lib/decorators/controller-middleware.decorator";
11
  import { UsersGuardMiddleware } from "modules/users/common/guards/users.guard";
12
+ import { SwaggerGet, SwaggerPost } from "@lib/decorators/swagger-routes.decorator";
13
  import { SwaggerResponse } from "@lib/decorators/swagger-response.decorator";
14
  import { SwaggerSummary } from "@lib/decorators/swagger-summary.decorator";
15
  import { SwaggerDescription } from "@lib/decorators/swagger-description.decorator";
16
  import { SwaggerQuery } from "@lib/decorators/swagger-query.decorator";
17
+ import { IUserRequest } from "@common/interfaces/user-request.interface";
18
+ import { IEatCustomMeal, eatCustomMealSchema } from "../validations/eat-custom-meal.validation";
19
+ import { bodyValidator } from "@helpers/validation.helper";
20
+ import { SwaggerRequest } from "@lib/decorators/swagger-request.decorator";
21
 
22
 
23
  @Controller("/user/meals")
 
27
 
28
  setRoutes(): void {
29
  this.router.get("/", asyncHandler(this.list));
30
+ this.router.post("/eat-custom-meal", bodyValidator(eatCustomMealSchema), asyncHandler(this.eatCustomMeal));
31
  }
32
 
33
  @SwaggerGet()
 
57
  res
58
  );
59
  };
60
+
61
+ @SwaggerPost('/eat-custom-meal')
62
+ @SwaggerResponse({})
63
+ @SwaggerRequest(eatCustomMealSchema)
64
+ @SwaggerSummary("Eat custom meal")
65
+ @SwaggerDescription("Eat custom meal")
66
+ eatCustomMeal = async (req: IUserRequest, res: Response) => {
67
+ await this.mealsService.eatCustomMeal(req.jwtPayload.id, req.body as IEatCustomMeal);
68
+
69
+ return JsonResponse.success(
70
+ {
71
+ message: "Meal created successfully",
72
+ },
73
+ res
74
+ );
75
+ };
76
  }
77
+
src/modules/users/modules/meals/events/eat-custom-meal.event.ts ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { ActivityType } from "@common/enums/activity-type.enum";
2
+ import { Activity, IActivity } from "@common/models/activity.model";
3
+ import { EventsManager } from "@lib/events/events-manager";
4
+ import { IEatCustomMeal } from "../validations/eat-custom-meal.validation";
5
+
6
+ export class EatCustomMealEvent {
7
+ constructor(public userId: string, public ingredients: IEatCustomMeal['ingredients']) {}
8
+ }
9
+
10
+ EventsManager.on(ActivityType.EAT_CUSTOM_MEAL, (event: EatCustomMealEvent) => {
11
+ console.log(`User with id ${event.userId} ate a custom meal with ingredients: ${event.ingredients.map(i => i.id).join(", ")}`);
12
+
13
+ Activity.create({
14
+ user_id: event.userId,
15
+ activity_type: ActivityType.EAT_CUSTOM_MEAL,
16
+ related_item: { ingredients: event.ingredients },
17
+ } satisfies IActivity).catch(console.error);
18
+ });
src/modules/users/modules/meals/services/meals.service.ts CHANGED
@@ -1,4 +1,27 @@
1
  import { Meal } from "@common/models/meal.model";
2
  import { CrudService } from "@lib/services/crud.service";
 
 
 
 
 
 
 
3
 
4
- export class MealsService extends CrudService(Meal) {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import { Meal } from "@common/models/meal.model";
2
  import { CrudService } from "@lib/services/crud.service";
3
+ import { IEatCustomMeal } from "../validations/eat-custom-meal.validation";
4
+ import { EventsManager } from "@lib/events/events-manager";
5
+ import { Ingredient } from "@common/models/ingredient.model";
6
+ import { HttpError } from "@lib/error-handling/http-error";
7
+ import { ActivityType } from "@common/enums/activity-type.enum";
8
+ import { EatCustomMealEvent } from "../events/eat-custom-meal.event";
9
+ import { Types } from "mongoose";
10
 
11
+ export class MealsService extends CrudService(Meal) {
12
+ async eatCustomMeal(id: string, body: IEatCustomMeal) {
13
+ // Validate ingredients
14
+ await Promise.all(
15
+ body.ingredients.map(async i => {
16
+ const ing = await Ingredient.findOne({ _id: new Types.ObjectId(i.id) });
17
+ if(!ing) throw new HttpError(404, `Ingredient with id ${i} not found`);
18
+ return {
19
+ ingredient: ing,
20
+ noServings: i.noServings,
21
+ };
22
+ })
23
+ )
24
+
25
+ EventsManager.emit(ActivityType.EAT_CUSTOM_MEAL, new EatCustomMealEvent(id, body.ingredients));
26
+ }
27
+ }
src/modules/users/modules/meals/validations/eat-custom-meal.validation.ts ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import * as joi from "joi";
2
+ import { createSchema } from "@helpers/create-schema";
3
+
4
+ export interface IEatCustomMeal {
5
+ ingredients: {
6
+ id: string;
7
+ noServings: number;
8
+ }[];
9
+ }
10
+
11
+ export const eatCustomMealKeys = {
12
+ ingredients: joi.array().items(joi.object({
13
+ id: joi.string().required(),
14
+ noServings: joi.number().required(),
15
+ })).required(),
16
+ };
17
+
18
+ export const eatCustomMealSchema = createSchema<IEatCustomMeal>(eatCustomMealKeys);