| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121 |
- import { Request, Response, NextFunction } from 'express'
- import { Recipe, IIngredient, IngredientSuggestion } from './model';
- import { IMongoService } from '../mongo';
- import { FilterQuery } from 'mongodb';
- import Scrape from './scrape/index';
- import { ScrapedRecipe } from './model';
- import Ingredient from '../ingredients/model';
- import { dissoc } from 'ramda';
- function validRecipe(data: any): Promise<Recipe> {
- return new Promise(function(resolve, reject) {
- return data.name !== undefined ? resolve(data) : reject('Missing data');
- })
- }
- type Controller = (db: IMongoService) => (req: Request, res: Response, next: NextFunction) => Promise<void|Response>;
- type ChainPController<T, U> = (req: Request, res: Response, next: NextFunction) => (result: T) => Promise<U>;
- const getSuggestions = (db: IMongoService) => async function(ingredient: IIngredient): Promise<IngredientSuggestion> {
- const suggestions = await db.find<Ingredient>({$text: {$search: ingredient.name}}, { score: { $meta: "textScore" } });
- return {
- ...ingredient,
- suggestions,
- }
- }
- const scrapeRecipe: (db: IMongoService) => ChainPController<void, ScrapedRecipe> = (db) => (req) => () => {
- return Scrape(req.body.url)
- .then(async scrapedRecipe => {
- const recipeIngredients = await Promise.all(scrapedRecipe.ingredients.map(async subGroup => {
- const subgroupIngredients = await Promise.all(subGroup.ingredients.map(getSuggestions(db)));
- return ({
- name: subGroup.name,
- ingredients: subgroupIngredients
- });
- }));
- return {
- ...scrapedRecipe,
- ingredients: recipeIngredients
- }
- })
- }
- const save: Controller = (db) => ({ body }, res) => {
- return validRecipe(body).then(
- recipe => db.insertOne(recipe).then(
- dbRecipe => res.json(dbRecipe)
- ));
- };
- const get: Controller = (db) => ({ query }, res) => {
- return db.findOne<Recipe>(query).then(result => res.json(result));
- }
- const getById: Controller = (db) => ({ params }, res) => {
- return db.findOneById<Recipe>(params.id).then(recipe => res.json(recipe));
- }
- const getAll: Controller = (db) => ({ query }, res) => {
- const _query = Object.keys(query).reduce((q, field) => {
- return {
- ...q,
- ...buildSearchQuery(field, query[field])
- }
- }, {});
- return db.find<Recipe>(_query).then(recipes => res.json(recipes));
- }
- type RegexQuery = {
- '$regex': string,
- '$options': 'i'
- };
- const buildSearchQuery = (fieldName: string, value: string): { [field: string] : RegexQuery} => ({
- [fieldName]: {
- '$regex': value,
- '$options': 'i'
- }
- });
- async function buildQuery(tryQuery:()=>FilterQuery<any>): Promise<FilterQuery<any>> {
- try {
- return Promise.resolve(tryQuery());
- } catch(error) {
- return Promise.reject(error);
- }
- }
- const getByIngredients: Controller = (db) => ({ query }, res) => {
- const ingredientsQuery = (query: FilterQuery<any>) => () => {
- if(query.ingredients) {
- const ingredients: string[] = query.ingredients.split(',');
- return {
- '$or': ingredients.map((ing) => buildSearchQuery('ingredients.ingredients.name', ing))
- };
- } else {
- throw new Error('Invalid query key');
- }
- }
- return buildQuery(ingredientsQuery(query)).then(
- builtQuery => db.find<Recipe>(builtQuery).then(
- recipes => res.json(recipes)
- )
- )
- }
- const update: Controller = (db) => ({params, body}, res) => {
- const newRecipe = dissoc('_id', body) as Recipe;
- return db.update<Recipe>(params.id, newRecipe).then(result => res.json(result))
- }
- export {
- save,
- scrapeRecipe,
- getById,
- get,
- getAll,
- getByIngredients,
- update,
- }
|