Skip to content

Commit

Permalink
add favorites
Browse files Browse the repository at this point in the history
  • Loading branch information
ezzabuzaid committed Aug 2, 2019
1 parent fd1ff76 commit 98f40b8
Show file tree
Hide file tree
Showing 23 changed files with 393 additions and 138 deletions.
214 changes: 198 additions & 16 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Expand Up @@ -17,6 +17,7 @@
"dependencies": {
"@sentry/node": "^5.5.0",
"@types/ansi-colors": "^3.2.0",
"@types/bcrypt": "^3.0.0",
"@types/bcryptjs": "^2.4.2",
"@types/body-parser": "^1.17.0",
"@types/cheerio": "^0.22.12",
Expand All @@ -39,6 +40,7 @@
"@types/socket.io": "^2.1.2",
"@types/ws": "^6.0.1",
"ansi-colors": "^3.2.4",
"bcrypt": "^3.0.6",
"bcryptjs": "^2.4.3",
"body-parser": "^1.18.3",
"cheerio": "^1.0.0-rc.3",
Expand Down
29 changes: 29 additions & 0 deletions src/app/api/favorites/favorites.model.ts
@@ -0,0 +1,29 @@
import { BaseModel, Entity, Field } from '@lib/mongoose';
import { Types, models, modelNames } from 'mongoose';
import { Constants } from '@core/helpers';

@Entity(Constants.Schemas.favorites)
export class FavoritesSchema {
@Field({ lowercase: false }) public user_id: Types.ObjectId;
@Field({ lowercase: false }) item_id: Types.ObjectId;
@Field({
enum: [Constants.Schemas.meals, Constants.Schemas.menus]
}) public type: string;

// @Virtual('assigned', {
// ref: doc => doc.type,
// localField: 'item_id',
// foreignField: '_id',
// justOne: false
// })
// t(){}
}
export const FavoritesModel = BaseModel<FavoritesSchema>(FavoritesSchema);
FavoritesModel.schema.virtual('items', {
ref: doc => doc.type,
localField: 'item_id',
foreignField: '_id',
justOne: true
});


31 changes: 31 additions & 0 deletions src/app/api/favorites/favorites.repo.ts
@@ -0,0 +1,31 @@
import { Body } from '@lib/mongoose';
import { FavoritesModel, FavoritesSchema } from './favorites.model';
import { models } from 'mongoose';

type FavoritesModelBody = Body<FavoritesSchema>;

export class FavoritesRepo extends FavoritesModel {

public static async createEntity(doc: FavoritesModelBody) {
const entity = new FavoritesRepo(doc);
const type = doc.type;
if (!models[type]) {
return { passed: false, msg: 'is not one of the supported type' }
}
const isThere = await models[type].findById(doc.item_id).lean();
if (!isThere) {
return { passed: false, msg: 'type is not associated with the item_id' }
}
console.warn('passed');
return { passed: true, entity: entity.save() };
}

public static deleteEntity(id: string) {
return this.findOneAndDelete({ _id: id });
}

public static fetchEntities(obj?: Partial<FavoritesModelBody>, ...args) {
return this.find(obj, ...args);
}

}
51 changes: 51 additions & 0 deletions src/app/api/favorites/favorites.routes.ts
@@ -0,0 +1,51 @@
import { Auth } from '@api/portal';
import { ErrorResponse, NetworkStatus, SuccessResponse, tokenService } from '@core/helpers';
import { Logger } from '@core/utils';
import { Delete, Get, Post, Router } from '@lib/methods';
import { translate } from '@lib/translation';
import { Request, Response } from 'express';
import { FavoritesRepo } from './favorites.repo';
const log = new Logger('FavoritesRouter');
import { Constants } from '@core/helpers';
import { Types } from 'mongoose';

@Router(Constants.Endpoints.favorites)
export class FavoritesRouter {
private repo = FavoritesRepo;

@Post('', Auth.isAuthenticated)
public async create(req: Request, res: Response) {
const { item_id, type } = req.body;
// TODO: make an interface for user token
const decodedToken = await tokenService.decodeToken<any>(req.headers.authorization);
const result = await this.repo.createEntity({ type, user_id: decodedToken.id, item_id });
let response;
if (result.passed) {
response = new SuccessResponse(result.entity, translate('success'), NetworkStatus.CREATED);
} else {
response = new ErrorResponse(result.msg, NetworkStatus.BAD_REQUEST);
}
res.status(response.code).json(response);
}

@Delete(':id', Auth.isAuthenticated)
public async delete(req: Request, res: Response) {
const { id } = req.params;
const entity = await this.repo.deleteEntity(id);
if (!entity) {
throw new ErrorResponse(translate('entity_not_found'), NetworkStatus.NOT_ACCEPTABLE);
}
const response = new SuccessResponse(null, translate('success'), NetworkStatus.OK);
res.status(response.code).json(response);
}

@Get(':type')
public async fetchFavMeals(req: Request, res: Response) {
const decodedToken = await tokenService.decodeToken<any>(req.headers.authorization);
const entites = await this.repo.fetchEntities({ user_id: decodedToken.id, type: req.params.type }).populate('items');
const response = new SuccessResponse(entites, translate('success'), NetworkStatus.OK);
response['count'] = entites.length;
res.status(response.code).json(response);
}

}
3 changes: 3 additions & 0 deletions src/app/api/favorites/index.ts
@@ -0,0 +1,3 @@
export * from './favorites.model';
export * from './favorites.repo';
export * from './favorites.routes';
3 changes: 2 additions & 1 deletion src/app/api/meals/meals.model.ts
@@ -1,7 +1,8 @@
import { BaseModel, Entity, Field } from '@lib/mongoose';
import { Types } from 'mongoose';
import { Constants } from '@core/helpers';

@Entity('meals')
@Entity(Constants.Schemas.meals)
export class MealsSchema {
@Field() public name: string;
@Field() public recipe: string;
Expand Down
10 changes: 5 additions & 5 deletions src/app/api/meals/meals.routes.ts
@@ -1,13 +1,14 @@
import { Auth } from '@api/portal';
import { ErrorResponse, NetworkStatus, SuccessResponse } from '@core/helpers';
import { Logger } from '@core/utils';
import { Constants } from '@core/helpers';
import { Delete, Get, Post, Put, Router } from '@lib/methods';
import { translate } from '@lib/translation';
import { Request, Response } from 'express';
import { MealsRepo } from './meals.repo';
const log = new Logger('MealsRouter');

@Router('meals')
@Router(Constants.Endpoints.meals)
export class MealsRouter {
private repo = MealsRepo;

Expand Down Expand Up @@ -55,20 +56,19 @@ export class MealsRouter {

@Get()
public async fetchEntities(req: Request, res: Response) {
const response = await this.emitValue();
const response = await this.populateAllEntityQuery();
res.status(response.code).json(response);
}

@Get('menu/:menu_id')
public async fetchEntitiesByMealID(req: Request, res: Response) {
const { menu_id } = req.params;
const response = await this.emitValue({ menu_id });
const response = await this.populateAllEntityQuery({ menu_id });
res.status(response.code).json(response);

}

public async emitValue(query = {}) {
log.debug(query);
public async populateAllEntityQuery(query = {}) {
const entites = await this.repo.fetchEntities(query);
const response = new SuccessResponse(entites, translate('success'), NetworkStatus.OK);
response['count'] = entites.length;
Expand Down
82 changes: 0 additions & 82 deletions src/app/api/meals/meals.spe.ts

This file was deleted.

3 changes: 2 additions & 1 deletion src/app/api/menus/menus.model.ts
@@ -1,6 +1,7 @@
import { BaseModel, Entity, Field } from '@lib/mongoose';
import { Constants } from '@core/helpers';

@Entity('menus')
@Entity(Constants.Schemas.menus)
export class MenusSchema {
@Field() public name: string;
@Field() public description: string;
Expand Down
3 changes: 2 additions & 1 deletion src/app/api/menus/menus.routes.ts
Expand Up @@ -6,8 +6,9 @@ import { translate } from '@lib/translation';
import { Request, Response } from 'express';
import { MenusRepo } from './menus.repo';
const log = new Logger('MenusRouter');
import { Constants } from '@core/helpers';

@Router('menus')
@Router(Constants.Endpoints.menus)
export class MenusRouter {
private repo = MenusRepo;

Expand Down
21 changes: 2 additions & 19 deletions src/app/api/portal/auth.ts
@@ -1,7 +1,7 @@
import { ErrorResponse, NetworkStatus } from '@core/helpers';
import { Logger } from '@core/utils';
import { tokenService } from '@core/helpers';
import { NextFunction, Request, Response } from 'express';
import jwt = require('jsonwebtoken');

const log = new Logger('Auth Module');

Expand All @@ -11,7 +11,7 @@ export class Auth {
const unauth = new ErrorResponse('Not authorized', NetworkStatus.UNAUTHORIZED);
try {
const token = req.headers.authorization;
const decodedToken = await verify(token);
const decodedToken = await tokenService.decodeToken(token);
log.info('Start checking JWT');
if (!decodedToken) {
throw unauth;
Expand All @@ -22,21 +22,4 @@ export class Auth {

next();
}
/**
*
* @param data token payload
* @returns the encrypted token
*/
public static generateToken(data) {
return jwt.sign(data, process.env.JWT_SECRET_KEY);
}
}

function verify(token: string) {
return new Promise((resolve, reject) => {
jwt.verify(token, process.env.JWT_SECRET_KEY, function(err, decodedToken) {
if (err) { reject(err); }
resolve(decodedToken);
});
});
}
6 changes: 3 additions & 3 deletions src/app/api/portal/portal.routes.ts
@@ -1,4 +1,4 @@
import { ErrorResponse, NetworkStatus, SuccessResponse } from '@core/helpers';
import { ErrorResponse, NetworkStatus, SuccessResponse, tokenService } from '@core/helpers';
import { Post, Router } from '@lib/methods';
import { translate } from '@lib/translation';
import { Request, Response } from 'express';
Expand All @@ -22,7 +22,7 @@ export class PortalRoutes {
if (isPasswordEqual) {
const response = new SuccessResponse(entity, translate('success'), NetworkStatus.OK);
log.debug('Start generateToken');
response['token'] = Auth.generateToken({ id: entity.id });
response['token'] = tokenService.generateToken({ id: entity.id });
return res.status(response.code).json(response);
}
}
Expand All @@ -40,7 +40,7 @@ export class PortalRoutes {
if (isPasswordEqual) {
const response = new SuccessResponse(entity, translate('success'), NetworkStatus.OK);
log.debug('Start generateToken');
response['token'] = Auth.generateToken({ id: entity.id });
response['token'] = tokenService.generateToken({ id: entity.id });
return res.status(response.code).json(response);
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/app/api/users/users.model.ts
Expand Up @@ -5,7 +5,7 @@ import { parsePhoneNumberFromString } from 'libphonenumber-js';

@Entity('users')
export class UsersSchema {
@Field() public password: string;
@Field({ lowercase: false }) public password: string;
@Field({
match: [ValidationPatterns.NoSpecialChar, 'Value contain special char'],
unique: true,
Expand Down
3 changes: 2 additions & 1 deletion src/app/app.ts
Expand Up @@ -2,16 +2,17 @@ import en from '@assets/languages/en.json';
import { ErrorHandling, stage, StageLevel } from '@core/helpers';
import { Logger } from '@core/utils';
import { translation } from '@lib/translation';
// import Sentry = require('@sentry/node');
import compression = require('compression');
import express = require('express');
import helmet = require('helmet');
import morgan = require('morgan');
import { Wrapper } from './wrapper';
import path from 'path';

// import Sentry = require('@sentry/node');
// import monitor = require('express-status-monitor');
// https://github.com/RafalWilinski/express-status-monitor

const log = new Logger('Application instance');

// Stage.tests(StageLevel.DEV, () => {
Expand Down
13 changes: 13 additions & 0 deletions src/app/core/helpers/constants.ts
@@ -0,0 +1,13 @@
export namespace Constants {
export class Endpoints {
static readonly meals = 'meals';
static readonly favorites = 'favorites';
static readonly menus = 'menus';

}
export class Schemas {
static readonly favorites = 'favorites';
static readonly meals = 'meals';
static readonly menus = 'menus';
}
}

0 comments on commit 98f40b8

Please sign in to comment.