Procházet zdrojové kódy

Remade save planner logic: Deletes all week content and create new planner from request. Support delete plan

Tatiana Inama před 7 roky
rodič
revize
5ff404eac0

+ 8 - 4
ktchn/src/mongo.ts

@@ -1,4 +1,4 @@
-import { Db, Collection, ObjectID, FilterQuery, ObjectId, WriteOpResult, FindOneOptions } from 'mongodb';
+import { Db, Collection, ObjectID, FilterQuery, ObjectId, WriteOpResult, FindOneOptions, BulkWriteOpResultObject, CollectionBulkWriteOptions, UpdateOneOptions, DeleteWriteOpResultObject } from 'mongodb';
 
 export interface IDDocument {
   _id: ObjectID
@@ -13,8 +13,10 @@ export interface IMongoService {
   findOneById<T>(idParam: string): Promise<IDBDocument<T>|null>,
   find<T>(query: FilterQuery<T>, optionalQuery?: FindOneOptions): Promise<IDBDocument<T>[]>,
   update<T>(id: string, data: T): Promise<WriteOpResult>,
-  updateOne<T>(filter: FilterQuery<T>, data: T, options?: {upsert?: boolean}): Promise<WriteOpResult>,
-  aggregate<T>(pipeline: Object[]): Promise<T[]>
+  updateOne<T>(filter: FilterQuery<T>, data: T, options?: UpdateOneOptions): Promise<WriteOpResult>,
+  aggregate<T>(pipeline: Object[]): Promise<T[]>,
+  bulkWrite<T>(operations: Object[], options?: CollectionBulkWriteOptions): Promise<BulkWriteOpResultObject>,
+  deleteMany<T>(filter: FilterQuery<T>): Promise<DeleteWriteOpResultObject>
 }
 
 export const mongoService = (db: Db) => (col: string): IMongoService => {
@@ -28,7 +30,9 @@ export const mongoService = (db: Db) => (col: string): IMongoService => {
     find: (query, optionalQuery) => collection.find(query, optionalQuery).toArray(),
     update: (id, data) => collection.update({_id: new ObjectId(id)}, data),
     updateOne: (filter, data, options) => collection.update(filter, data, options), 
-    aggregate: pipeline => collection.aggregate(pipeline).toArray()
+    aggregate: pipeline => collection.aggregate(pipeline).toArray(),
+    bulkWrite: (operations, options) => collection.bulkWrite(operations, options),
+    deleteMany: (filter) => collection.deleteMany(filter),
   }
 };
 

+ 38 - 11
ktchn/src/planner/controller.ts

@@ -1,9 +1,10 @@
 import { WriteOpResult } from 'mongodb';
-import { IMongoService } from '../mongo';
+import { IMongoService, IDBDocument } from '../mongo';
 import { ChainPController } from '../promise-all-middleware';
 import PlanDB, { WeeklyPlanner, CompletePlanDB, Weekday, Plan, CompactWeeklyPlanner } from './model';
 import moment, { Moment } from 'moment';
 import { ObjectId } from 'bson';
+import { Request, NextFunction, Response } from 'express';
 
 type Controller<U, T> = (db: IMongoService) => ChainPController<U, T>;
 
@@ -110,16 +111,41 @@ const savePlan: Controller<void, PlanDB> = db => req => prevResult => {
   return validPlan(plan).then(db.insertOne)
 }
 
-const saveManyPlans: Controller<void, WriteOpResult[]> = db => req => async () => {
-  const mbPlanner = req.body;
-  if (Array.isArray(mbPlanner)) {
-    const plans = await Promise.all(mbPlanner.map(validPlan));
-    return await Promise.all(plans.map(plan => db.updateOne({
-      date: new Date(plan.date),
-      meal: plan.meal
-    }, plan, { upsert: true })));
+type PlannerRequest = {
+  planner: Plan[],
+  from: Date,
+  to: Date
+};
+
+const toPlan = (plan: any): Plan => ({
+  ...plan,
+  date: new Date(plan.date),
+  recipe: new ObjectId(plan.recipe),
+})
+
+const validatePlanner: Controller<any, PlannerRequest> = () => ({ body }) => async () => {
+  if (body.planner && Array.isArray(body.planner) && new Date(body.from) && new Date(body.to)) {
+    const planner: Plan[] = body.planner.map(toPlan);
+    return Promise.resolve({
+      planner,
+      from: new Date(body.from),
+      to: new Date(body.to)
+    })
   } else {
-    return Promise.reject('Invalid type: body content must be a Plan Array. Use /day/ endpoint instead')
+    return Promise.reject('Invalid request: Body must have planner, from and to properties')
+  }
+}
+
+const saveWeekPlanner: Controller<PlannerRequest, IDBDocument<Plan[]>> = db => req => async ({ planner, from , to }) => {
+  try {
+    return db.deleteMany({
+      'date': {'$gte': new Date(from), '$lte': new Date(to)}
+    }).then(result => {
+      return planner.length ? db.insertMany<Plan>(planner) : []
+    })
+  } catch(error) {
+    console.error(error)
+    return error;
   }
 }
 
@@ -128,5 +154,6 @@ export {
   completePlanner,
   compactPlanner,
   savePlan,
-  saveManyPlans,
+  saveWeekPlanner,
+  validatePlanner
 }

+ 2 - 2
ktchn/src/planner/routes.ts

@@ -2,7 +2,7 @@ import { Router } from 'express';
 import mongoService, { IMongoService } from '../mongo';
 import MongoClient from 'mongodb';
 import { chainP } from '../promise-all-middleware';
-import { getWeekPlanner, completePlanner, compactPlanner, savePlan, saveManyPlans } from './controller';
+import { getWeekPlanner, completePlanner, compactPlanner, savePlan, saveWeekPlanner, validatePlanner } from './controller';
 
 class PlannerRoutes {
   public router: Router;
@@ -19,8 +19,8 @@ class PlannerRoutes {
     this.router.get('/week/:week', chainP([getWeekPlanner(this.PlannerDB)]));
     this.router.get('/week/:week/complete', chainP([completePlanner(this.PlannerDB)]));
     this.router.get('/week/:week/compact', chainP([compactPlanner(this.PlannerDB)]));
+    this.router.post('/week', chainP([validatePlanner(this.PlannerDB), saveWeekPlanner(this.PlannerDB)]));
     this.router.post('/day', chainP([savePlan(this.PlannerDB)]));
-    this.router.post('/', chainP([saveManyPlans(this.PlannerDB)]));
   }
 }
 

+ 9 - 6
recipes/src/containers/Planner/actions.ts

@@ -3,7 +3,6 @@ import { Meal, RecipePlan, Weekday, DBPlanner, PlannerMode, WeekPlan, DBDayPlan
 import { Dispatch, ActionCreator, Action } from 'redux';
 import { getPlanner, savePlanner } from './services'
 import { ThunkAction } from 'redux-thunk';
-import { async } from 'q';
 
 export const ADD_TO_BACKLOG = 'ADD_TO_BACKLOG';
 export const REMOVE_FROM_BACKLOG = 'REMOVE_FROM_BACKLOG';
@@ -110,11 +109,15 @@ export const editPlanner = (): EditPlannerAction => ({
 });
 
 export interface PendingSavePlannerAction extends Action<'PENDING_SAVE_PLANNER'> {
-  planner: WeekPlan
+  planner: WeekPlan,
+  from: Date,
+  to: Date
 }
-export const pendingSavePlanner = (planner: WeekPlan): PendingSavePlannerAction => ({
+export const pendingSavePlanner = (planner: WeekPlan, from: Date, to: Date): PendingSavePlannerAction => ({
   type: PENDING_SAVE_PLANNER,
   planner,
+  from,
+  to,
 })
 
 export interface ConfirmSavePlannerAction extends Action<'CONFIRM_SAVE_PLANNER'> {
@@ -140,11 +143,11 @@ export const savePlannerActionCreator: ActionCreator<
     WeekPlan,
     ConfirmSavePlannerAction|RejectSavePlannerAction
   >
-> = (weekplan: WeekPlan) => {
+> = (weekplan: WeekPlan, from: Date, to: Date) => {
   return async (dispatch: Dispatch) => {
-    dispatch(pendingSavePlanner(weekplan))
+    dispatch(pendingSavePlanner(weekplan, from, to))
     try {
-      const result = await savePlanner(weekplan);
+      const result = await savePlanner(weekplan, from, to);
       return dispatch(confirmSavePlanner(result))
     } catch(error) {
       return dispatch(rejectSavePlanner(error))

+ 2 - 2
recipes/src/containers/Planner/index.tsx

@@ -73,7 +73,7 @@ class PlannerContainer extends Component<PlannerContainerProps, PlannerContainer
               ) : (
               <Button outlined raised onClick={() => {
                 if (this.props.edit) {
-                  this.props.save(this.props.planner)
+                  this.props.save(this.props.planner, this.props.from, this.props.to)
                 }
                 this.changeMode('view')
               }}>Save</Button>
@@ -211,7 +211,7 @@ const mapStateToProps = (state: AppState) => {
 
 const mapDispatchToProps = (dispatch: ThunkDispatch<AppState, any, any>) => ({
   fetch: (week: number) => dispatch(fetchPlannerActionCreator(week)),
-  save: (plan: WeekPlan) => dispatch(savePlannerActionCreator(plan)),
+  save: (plan: WeekPlan, from: Date, to: Date) => dispatch(savePlannerActionCreator(plan, from, to)),
   ...bindActionCreators(PlannerActions, dispatch)
 })
 

+ 6 - 2
recipes/src/containers/Planner/services.ts

@@ -29,8 +29,12 @@ export const getPlanner = (week: number): Promise<DBPlanner> =>
   axios.get(`${PLANNER}/week/${week}/compact`)
   .then(response => response.data)
 
-export const savePlanner = (weekPlan: WeekPlan): Promise<Array<DBDayPlan>> => {
-  return axios.post(`${PLANNER}/`, toDBPlan(weekPlan))
+export const savePlanner = (weekPlan: WeekPlan, from: Date, to: Date): Promise<Array<DBDayPlan>> => {
+  return axios.post(`${PLANNER}/week`, {
+    planner: toDBPlan(weekPlan),
+    from,
+    to,
+  })
     .then(response => response.data)
 }