<commit_before>import moment from 'moment';
import 'moment-timezone';
import 'moment-business-time';

import Config from '/server/hardConfig.js';
import { avg4est, asRate, percentOf, quadRegression } from './calcOps.js';
import { syncLocale, countMulti, getEst } from './utility.js';
import { batchTideTime } from './tideGlobalMethods.js';
import { calcShipDay } from './reportCompleted.js';
import { getShipLoad } from '/server/shipOps';

function dryPriorityCalc(bQuTmBdg, mEst, bTide, shipAim, now, shipLoad) {
  const shipAimMmnt = moment(shipAim);

  const mQuote = bQuTmBdg.length === 0 ? 0 : bQuTmBdg[0].timeAsMinutes;
  const estimatedMinutes = avg4est(mQuote, mEst);

  const totalTideMinutes = batchTideTime(bTide);

  const quote2tide = mQuote - totalTideMinutes;
  const ested2tide = estimatedMinutes - totalTideMinutes;
  const est2tide = ested2tide > 0 ? ested2tide : quote2tide > 0 ? quote2tide : 0;

  const overQuote = totalTideMinutes > mQuote;

  const aimAhead = shipAimMmnt.clone().subtract(Config.shipAhead, 'hours');
  const estSoonest = now.clone().addWorkingTime(est2tide, 'minutes');

  const buffer = aimAhead.workingDiff(estSoonest, 'minutes');

  const estEnd2fillBuffer = buffer || null;

  const dayGap = shipAimMmnt.workingDiff(now, 'days', true);
  const shipPull = dayGap <= Config.shipSoon ? shipLoad * 2 : shipLoad;

  const bffrRel = Math.round( ( estEnd2fillBuffer / 100 ) + dayGap - shipPull );

  return { est2tide, estSoonest, bffrRel, estEnd2fillBuffer, overQuote };
}

function collectPriority(privateKey, batchID, mockDay) {
  return new Promise(resolve => {
    const now = moment().tz(Config.clientTZ);

    const b = XBatchDB.findOne({_id: batchID});

    if(!b) {
      resolve(false);
    }else{
      const trc = TraceDB.findOne({batchID: b._id});
      const tgt = trc ? trc.performTgt || 0 : 0;

      const mEst = getEst(b.widgetId, b.quantity, tgt);

      const oRapid = XRapidsDB.findOne({extendBatch: b.batch, live: true});
      const rapIs = oRapid ? oRapid.rapid : false;

      const endDay = rapIs && moment(oRapid.deliverAt).isAfter(b.salesEnd) ?
                      oRapid.deliverAt : b.salesEnd;

      const doneEntry = b.completedAt;

      const future = mockDay ? mockDay : endDay;
      const calcShip = calcShipDay( batchID, now, future );
      const shipAim = calcShip[1];
      const lateLate = calcShip[2];

      const qtBready = !b.quoteTimeBudget ? false : true;

      if(qtBready && b.tide && !doneEntry) {
        const shipLoad = getShipLoad(now);

        const dryCalc = dryPriorityCalc(b.quoteTimeBudget, mEst, b.tide, shipAim, now, shipLoad);

        resolve({
          batch: b.batch,
          batchID: b._id,
          salesOrder: b.salesOrder,
          est2tide: dryCalc.est2tide,
          estSoonest: dryCalc.estSoonest.format(),
          completed: doneEntry,
          bffrRel: dryCalc.bffrRel,
          estEnd2fillBuffer: dryCalc.estEnd2fillBuffer,
          overQuote: dryCalc.overQuote,
          shipAim: shipAim,
          lateLate: lateLate,
          oRapid: rapIs
        });
      }else{
        resolve({
          batch: b.batch,
          batchID: b._id,
          salesOrder: b.salesOrder,
          est2tide: false,
          completed: doneEntry,
          bffrRel: false,
          estEnd2fillBuffer: 0,
          overQuote: false,
          shipAim: shipAim,
          lateLate: lateLate,
          oRapid: rapIs
        });
      }
    }
  });
}

function getFastPriority(bData, now, shipAim) {
  return new Promise(resolve => {
    const doneEntry = bData.completedAt;

    const qtBready = !bData.quoteTimeBudget ? false : true;

    if(qtBready && bData.tide && !doneEntry) {
      const shipLoad = getShipLoad(now);
      const trc = TraceDB.findOne({batchID: bData._id});
      const tgt = trc ? trc.performTgt || 0 : 0;

      const mEst = getEst(bData.widgetId, bData.quantity, tgt);

      const dryCalc = dryPriorityCalc(bData.quoteTimeBudget, mEst, bData.tide, shipAim, now, shipLoad);

      resolve({
        est2tide: dryCalc.est2tide,
        estSoonest: dryCalc.estSoonest.format(),
        bffrRel: dryCalc.bffrRel,
        estEnd2fillBuffer: dryCalc.estEnd2fillBuffer,
        overQuote: dryCalc.overQuote
      });
    }else{
      resolve({
        est2tide: false,
        estSoonest: false,
        bffrRel: false,
        estEnd2fillBuffer: 0,
        overQuote: false
      });
    }
  });
}

function collectNonCon(privateKey, batchID, temp) {
  return new Promise(resolve => {
    let collection = false;
    const bx = XBatchDB.findOne({_id: batchID});
    if(bx) {
      const srs = XSeriesDB.findOne({batch: bx.batch});

      const items = !srs ? [] : srs.items;
      const itemQty = items.length > 0 ? items.reduce((t,i)=> t + i.units, 0) : 0;
      // nonCon relevant
      const rNC = !srs ? [] : srs.nonCon.filter( n => !n.trash );
      // how many nonCons
      const nonConTotal = temp === 'cool' ? 0 : countMulti(rNC);
      // how many are unresolved
      const nonConLeft = countMulti( rNC.filter( x => x.inspect === false ) );
      // nc rate
      const ncRate = asRate(nonConTotal, itemQty, true);
      // how many items have nonCons
      const hasNonCon = temp === 'cool' ? 0 :
        [... new Set( Array.from(rNC, x => x.serial) ) ].length;
      // what percent of items have nonCons
      const percentOfNCitems = temp === 'cool' ? 0 :
        itemQty === 0 ? 0 : hasNonCon >= itemQty ? 100 :
        ((hasNonCon / itemQty) * 100 ).toFixed(0);
      // how many items are scrapped
      const itemIsScrap = temp === 'cool' ? 0 :
        items.filter( x => x.scrapped ).length;
      // how many items with RMA
      let itemHasRapid = temp === 'cool' ? 0 :
        items.filter( x => x.altPath.find( y => y.rapId !== false) ).length;

      collection = {
        batch: bx.batch,
        batchID: bx._id,
        nonConTotal: nonConTotal,
        nonConRate: ncRate,
        nonConLeft: nonConLeft,
        percentOfNCitems: isNaN(percentOfNCitems) ? '0%' : percentOfNCitems + '%',
        itemIsScrap: itemIsScrap,
        itemHasRMA: itemHasRapid
      };

      resolve(collection);
    }else{
      resolve(collection);
    }
  });
}

Meteor.methods({

  priorityRank(batchID, serverAccessKey, mockDay) {
    async function bundlePriority() {
      const accessKey = serverAccessKey || Meteor.user().orgKey;
      syncLocale(accessKey);
      try {
        bundle = await collectPriority(accessKey, batchID, mockDay);
        return bundle;
      }catch (err) {
        throw new Meteor.Error(err);
      }
    }
    return bundlePriority();
  },
  priorityFast(serverAccessKey, bData, now, shipAim) {
    async function bundlePriority() {
      syncLocale(serverAccessKey);
      try {
        bundle = await getFastPriority(bData, now, shipAim);
        return bundle;
      }catch (err) {
        throw new Meteor.Error(err);
      }
    }
    return bundlePriority();
  },
  priorityTrace(batchID) {
    const t = TraceDB.findOne({batchID: batchID});

    if(!t) {
      return false;
    }else{
      return {
        batch: t.batch,
        batchID: t.batchID,
        salesOrder: t.salesOrder,
        est2tide: t.est2tide,
        estSoonest: t.estSoonest,
        completed: t.completed,
        bffrRel: t.bffrRel,
        estEnd2fillBuffer: t.estEnd2fillBuffer,
        overQuote: t.overQuote,
        shipAim: t.shipAim,
        lateLate: t.lateLate,
        oRapid: t.oRapid
      };
    }
  },

  performTarget(batchID) {
    const b = XBatchDB.findOne({_id: batchID});

    if(!b) {
      return false;
    }else{
      const srs = XSeriesDB.findOne({batch: b.batch});
      const items = srs ? srs.items : [];

      const nc = countMulti( srs ? srs.nonCon.filter( n => !n.trash ) : [] );
      const units = items.length > 0 ? items.reduce((t,i)=> t + i.units, 0) : 0;
      const rate = asRate(nc, units);
      const pb = rate < 1 ? Math.round(rate) : Math.ceil(rate*0.1);

      const done = items.filter( i => i.completed );
      const donePer = b.completed ? 100 : percentOf( items.length, done.length );

      const goalTimePer = quadRegression(donePer);

      const tide = b.tide || [];
      const totalTideMinutes = batchTideTime(tide);

      const mEst = getEst(b.widgetId, b.quantity, 0);
      const budget = b.quoteTimeBudget || [];
      const mQuote = budget.length === 0 ? 0 : Number(budget[0].timeAsMinutes);
      const estMinutes = mQuote === 0 ? mEst :
                         mQuote < mEst ? mQuote : avg4est(mQuote, mEst);

      const realTimePer = percentOf( estMinutes, totalTideMinutes );

      const diff = Math.round( goalTimePer - realTimePer );

      const au = !isFinite(diff) || totalTideMinutes === 0 ? null :
                   donePer === 0 ? realTimePer < Config.qregA ? 0 : -1 :
                   Math.round( diff * 0.15 );

      const factor = isFinite(au) && isFinite(pb) ? au - pb : null;
      return factor;
    }
  },

  performTrace(batchID) {
    const b = XBatchDB.findOne({_id: batchID},{fields:{'lockTrunc':1}});
    if(b && b.lockTrunc && b.lockTrunc.performTgt !== undefined) {
      return b.lockTrunc.performTgt;
    }else{
      const t = TraceDB.findOne({batchID: batchID},{fields:{'performTgt':1}});
      if(t && t.performTgt !== undefined) {
        return t.performTgt;
      }else{
        return Meteor.call('performTarget', batchID);
      }
    }
  },

  nonconQuickStats(batchID, temp) {
    async function bundleNonCon(batchID) {
      const accessKey = Meteor.user().orgKey;
      try {
        bundle = await collectNonCon(accessKey, batchID, temp);
        return bundle;
      }catch (err) {
        throw new Meteor.Error(err);
      }
    }
    return bundleNonCon(batchID);
  }

});<commit_msg>lean to est time<commit_after>import moment from 'moment';
import 'moment-timezone';
import 'moment-business-time';

import Config from '/server/hardConfig.js';
import { avg4est, asRate, percentOf, quadRegression } from './calcOps.js';
import { syncLocale, countMulti, getEst } from './utility.js';
import { batchTideTime } from './tideGlobalMethods.js';
import { calcShipDay } from './reportCompleted.js';
import { getShipLoad } from '/server/shipOps';

function dryPriorityCalc(bQuTmBdg, mEst, bTide, shipAim, now, shipLoad) {
  const shipAimMmnt = moment(shipAim);

  const mQuote = bQuTmBdg.length === 0 ? 0 : bQuTmBdg[0].timeAsMinutes;
  const estimatedMinutes = avg4est(mQuote, mEst);

  const totalTideMinutes = batchTideTime(bTide);

  const est2tide = estimatedMinutes - totalTideMinutes;

  const overQuote = totalTideMinutes > mQuote;
  const e2tNice = Math.max(0, est2tide);

  const aimAhead = shipAimMmnt.clone().subtract(Config.shipAhead, 'hours');
  const estSoonest = now.clone().addWorkingTime(e2tNice, 'minutes');

  const buffer = aimAhead.workingDiff(estSoonest, 'minutes');

  const estEnd2fillBuffer = buffer || null;

  const dayGap = shipAimMmnt.workingDiff(now, 'days', true);
  const shipPull = dayGap <= Config.shipSoon ? shipLoad * 2 : shipLoad;

  const bffrRel = Math.round( ( estEnd2fillBuffer / 100 ) + dayGap - shipPull );

  return { est2tide, estSoonest, bffrRel, estEnd2fillBuffer, overQuote };
}

function collectPriority(privateKey, batchID, mockDay) {
  return new Promise(resolve => {
    const now = moment().tz(Config.clientTZ);

    const b = XBatchDB.findOne({_id: batchID});

    if(!b) {
      resolve(false);
    }else{
      const trc = TraceDB.findOne({batchID: b._id});
      const tgt = trc ? trc.performTgt || 0 : 0;

      const mEst = getEst(b.widgetId, b.quantity, tgt);

      const oRapid = XRapidsDB.findOne({extendBatch: b.batch, live: true});
      const rapIs = oRapid ? oRapid.rapid : false;

      const endDay = rapIs && moment(oRapid.deliverAt).isAfter(b.salesEnd) ?
                      oRapid.deliverAt : b.salesEnd;

      const doneEntry = b.completedAt;

      const future = mockDay ? mockDay : endDay;
      const calcShip = calcShipDay( batchID, now, future );
      const shipAim = calcShip[1];
      const lateLate = calcShip[2];

      const qtBready = !b.quoteTimeBudget ? false : true;

      if(qtBready && b.tide && !doneEntry) {
        const shipLoad = getShipLoad(now);

        const dryCalc = dryPriorityCalc(b.quoteTimeBudget, mEst, b.tide, shipAim, now, shipLoad);

        resolve({
          batch: b.batch,
          batchID: b._id,
          salesOrder: b.salesOrder,
          est2tide: dryCalc.est2tide,
          estSoonest: dryCalc.estSoonest.format(),
          completed: doneEntry,
          bffrRel: dryCalc.bffrRel,
          estEnd2fillBuffer: dryCalc.estEnd2fillBuffer,
          overQuote: dryCalc.overQuote,
          shipAim: shipAim,
          lateLate: lateLate,
          oRapid: rapIs
        });
      }else{
        resolve({
          batch: b.batch,
          batchID: b._id,
          salesOrder: b.salesOrder,
          est2tide: false,
          completed: doneEntry,
          bffrRel: false,
          estEnd2fillBuffer: 0,
          overQuote: false,
          shipAim: shipAim,
          lateLate: lateLate,
          oRapid: rapIs
        });
      }
    }
  });
}

function getFastPriority(bData, now, shipAim) {
  return new Promise(resolve => {
    const doneEntry = bData.completedAt;

    const qtBready = !bData.quoteTimeBudget ? false : true;

    if(qtBready && bData.tide && !doneEntry) {
      const shipLoad = getShipLoad(now);
      const trc = TraceDB.findOne({batchID: bData._id});
      const tgt = trc ? trc.performTgt || 0 : 0;

      const mEst = getEst(bData.widgetId, bData.quantity, tgt);

      const dryCalc = dryPriorityCalc(bData.quoteTimeBudget, mEst, bData.tide, shipAim, now, shipLoad);

      resolve({
        est2tide: dryCalc.est2tide,
        estSoonest: dryCalc.estSoonest.format(),
        bffrRel: dryCalc.bffrRel,
        estEnd2fillBuffer: dryCalc.estEnd2fillBuffer,
        overQuote: dryCalc.overQuote
      });
    }else{
      resolve({
        est2tide: false,
        estSoonest: false,
        bffrRel: false,
        estEnd2fillBuffer: 0,
        overQuote: false
      });
    }
  });
}

function collectNonCon(privateKey, batchID, temp) {
  return new Promise(resolve => {
    let collection = false;
    const bx = XBatchDB.findOne({_id: batchID});
    if(bx) {
      const srs = XSeriesDB.findOne({batch: bx.batch});

      const items = !srs ? [] : srs.items;
      const itemQty = items.length > 0 ? items.reduce((t,i)=> t + i.units, 0) : 0;
      // nonCon relevant
      const rNC = !srs ? [] : srs.nonCon.filter( n => !n.trash );
      // how many nonCons
      const nonConTotal = temp === 'cool' ? 0 : countMulti(rNC);
      // how many are unresolved
      const nonConLeft = countMulti( rNC.filter( x => x.inspect === false ) );
      // nc rate
      const ncRate = asRate(nonConTotal, itemQty, true);
      // how many items have nonCons
      const hasNonCon = temp === 'cool' ? 0 :
        [... new Set( Array.from(rNC, x => x.serial) ) ].length;
      // what percent of items have nonCons
      const percentOfNCitems = temp === 'cool' ? 0 :
        itemQty === 0 ? 0 : hasNonCon >= itemQty ? 100 :
        ((hasNonCon / itemQty) * 100 ).toFixed(0);
      // how many items are scrapped
      const itemIsScrap = temp === 'cool' ? 0 :
        items.filter( x => x.scrapped ).length;
      // how many items with RMA
      let itemHasRapid = temp === 'cool' ? 0 :
        items.filter( x => x.altPath.find( y => y.rapId !== false) ).length;

      collection = {
        batch: bx.batch,
        batchID: bx._id,
        nonConTotal: nonConTotal,
        nonConRate: ncRate,
        nonConLeft: nonConLeft,
        percentOfNCitems: isNaN(percentOfNCitems) ? '0%' : percentOfNCitems + '%',
        itemIsScrap: itemIsScrap,
        itemHasRMA: itemHasRapid
      };

      resolve(collection);
    }else{
      resolve(collection);
    }
  });
}

Meteor.methods({

  priorityRank(batchID, serverAccessKey, mockDay) {
    async function bundlePriority() {
      const accessKey = serverAccessKey || Meteor.user().orgKey;
      syncLocale(accessKey);
      try {
        bundle = await collectPriority(accessKey, batchID, mockDay);
        return bundle;
      }catch (err) {
        throw new Meteor.Error(err);
      }
    }
    return bundlePriority();
  },
  priorityFast(serverAccessKey, bData, now, shipAim) {
    async function bundlePriority() {
      syncLocale(serverAccessKey);
      try {
        bundle = await getFastPriority(bData, now, shipAim);
        return bundle;
      }catch (err) {
        throw new Meteor.Error(err);
      }
    }
    return bundlePriority();
  },
  priorityTrace(batchID) {
    const t = TraceDB.findOne({batchID: batchID});

    if(!t) {
      return false;
    }else{
      return {
        batch: t.batch,
        batchID: t.batchID,
        salesOrder: t.salesOrder,
        est2tide: t.est2tide,
        estSoonest: t.estSoonest,
        completed: t.completed,
        bffrRel: t.bffrRel,
        estEnd2fillBuffer: t.estEnd2fillBuffer,
        overQuote: t.overQuote,
        shipAim: t.shipAim,
        lateLate: t.lateLate,
        oRapid: t.oRapid
      };
    }
  },

  performTarget(batchID) {
    const b = XBatchDB.findOne({_id: batchID});

    if(!b) {
      return false;
    }else{
      const srs = XSeriesDB.findOne({batch: b.batch});
      const items = srs ? srs.items : [];

      const nc = countMulti( srs ? srs.nonCon.filter( n => !n.trash ) : [] );
      const units = items.length > 0 ? items.reduce((t,i)=> t + i.units, 0) : 0;
      const rate = asRate(nc, units);
      const pb = rate < 1 ? Math.round(rate) : Math.ceil(rate*0.1);

      const done = items.filter( i => i.completed );
      const donePer = b.completed ? 100 : percentOf( items.length, done.length );

      const goalTimePer = quadRegression(donePer);

      const tide = b.tide || [];
      const totalTideMinutes = batchTideTime(tide);

      const mEst = getEst(b.widgetId, b.quantity, 0);
      const budget = b.quoteTimeBudget || [];
      const mQuote = budget.length === 0 ? 0 : Number(budget[0].timeAsMinutes);
      const estMinutes = mQuote === 0 ? mEst :
                         mQuote < mEst ? mQuote : avg4est(mQuote, mEst);

      const realTimePer = percentOf( estMinutes, totalTideMinutes );

      const diff = Math.round( goalTimePer - realTimePer );

      const au = !isFinite(diff) || totalTideMinutes === 0 ? null :
                   donePer === 0 ? realTimePer < Config.qregA ? 0 : -1 :
                   Math.round( diff * 0.15 );

      const factor = isFinite(au) && isFinite(pb) ? au - pb : null;
      return factor;
    }
  },

  performTrace(batchID) {
    const b = XBatchDB.findOne({_id: batchID},{fields:{'lockTrunc':1}});
    if(b && b.lockTrunc && b.lockTrunc.performTgt !== undefined) {
      return b.lockTrunc.performTgt;
    }else{
      const t = TraceDB.findOne({batchID: batchID},{fields:{'performTgt':1}});
      if(t && t.performTgt !== undefined) {
        return t.performTgt;
      }else{
        return Meteor.call('performTarget', batchID);
      }
    }
  },

  nonconQuickStats(batchID, temp) {
    async function bundleNonCon(batchID) {
      const accessKey = Meteor.user().orgKey;
      try {
        bundle = await collectNonCon(accessKey, batchID, temp);
        return bundle;
      }catch (err) {
        throw new Meteor.Error(err);
      }
    }
    return bundleNonCon(batchID);
  }

});<|endoftext|>


<commit_before>/**
 *
 * EditPage
 *
 */

import React from 'react';
import moment from 'moment';
import { connect } from 'react-redux';
import { bindActionCreators, compose } from 'redux';
import { createStructuredSelector } from 'reselect';
import PropTypes from 'prop-types';
import { cloneDeep, findIndex, get, includes, isEmpty, isObject, toNumber, toString, replace } from 'lodash';
import HTML5Backend from 'react-dnd-html5-backend';
import { DragDropContext } from 'react-dnd';
import cn from 'classnames';
// You can find these components in either
// ./node_modules/strapi-helper-plugin/lib/src
// or strapi/packages/strapi-helper-plugin/lib/src
import BackHeader from 'components/BackHeader';
import EmptyAttributesBlock from 'components/EmptyAttributesBlock';
import LoadingIndicator from 'components/LoadingIndicator';
import PluginHeader from 'components/PluginHeader';
import PopUpWarning from 'components/PopUpWarning';
// Plugin's components
import CustomDragLayer from 'components/CustomDragLayer';
import Edit from 'components/Edit';
import EditRelations from 'components/EditRelations';
// App selectors
import { makeSelectSchema } from 'containers/App/selectors';
import injectReducer from 'utils/injectReducer';
import injectSaga from 'utils/injectSaga';
import getQueryParameters from 'utils/getQueryParameters';
import { bindLayout } from 'utils/bindLayout';
import inputValidations from 'utils/inputsValidations';
import { generateRedirectURI } from 'containers/ListPage/utils';
import { checkFormValidity } from 'utils/formValidations';
import {
  addRelationItem,
  changeData,
  getData,
  initModelProps,
  moveAttr,
  moveAttrEnd,
  onCancel,
  onRemoveRelationItem,
  resetProps,
  setFileRelations,
  setFormErrors,
  submit,
} from './actions';
import reducer from './reducer';
import saga from './saga';
import makeSelectEditPage from './selectors';
import styles from './styles.scss';

export class EditPage extends React.Component {
  state = { showWarning: false };

  componentDidMount() {
    this.initComponent(this.props);
  }

  componentDidUpdate(prevProps) {
    if (prevProps.location.pathname !== this.props.location.pathname) {
      this.props.resetProps();
      this.initComponent(this.props);
    }

    if (prevProps.editPage.submitSuccess !== this.props.editPage.submitSuccess) {
      if (!isEmpty(this.props.location.search) && includes(this.props.location.search, '?redirectUrl')) {
        const redirectUrl = this.props.location.search.split('?redirectUrl=')[1];

        this.props.history.push({
          pathname: redirectUrl.split('?')[0],
          search: redirectUrl.split('?')[1],
        });
      } else {
        this.props.history.push({
          pathname: replace(this.props.location.pathname, '/create', ''),
          search: `?source=${this.getSource()}`,
        });
      }
    }
  }

  componentWillUnmount() {
    this.props.resetProps();
  }

  /**
   * Retrieve the model's displayed relations
   * @return {Array}
   */
  getDisplayedRelations = () => {
    return get(this.getSchema(), ['editDisplay', 'relations'], []);
  }

  /**
   * Retrieve the model's custom layout
   *
   */
  getLayout = () => (
    bindLayout.call(this, get(this.props.schema, ['layout', this.getModelName()], {}))
  )

  /**
   *
   *
   * @type {[type]}
   */
  getAttributeValidations = (name) => get(this.props.editPage.formValidations, [findIndex(this.props.editPage.formValidations, ['name', name]), 'validations'], {})

  getDisplayedFields = () => get(this.getSchema(), ['editDisplay', 'fields'], []);

  /**
   * Retrieve the model
   * @type {Object}
   */
  getModel = () => get(this.props.schema, ['models', this.getModelName()]) || get(this.props.schema, ['models', 'plugins', this.getSource(), this.getModelName()]);

  /**
   * Retrieve specific attribute
   * @type {String} name
   */
  getModelAttribute = (name) => get(this.getModelAttributes(), name);

  /**
   * Retrieve the model's attributes
   * @return {Object}
   */
  getModelAttributes = () => this.getModel().attributes;

  /**
   * Retrieve the model's name
   * @return {String} model's name
   */
  getModelName = () => this.props.match.params.slug.toLowerCase();

  /**
   * Retrieve model's schema
   * @return {Object}
   */
  getSchema = () => this.getSource() !== 'content-manager' ?
    get(this.props.schema, ['models', 'plugins', this.getSource(), this.getModelName()])
    : get(this.props.schema, ['models', this.getModelName()]);

  getPluginHeaderTitle = () => {
    if (this.isCreating()) {
      return toString(this.props.editPage.pluginHeaderTitle);
    }

    return this.props.match.params.id;
  }

  /**
   * Retrieve the model's source
   * @return {String}
   */
  getSource = () => getQueryParameters(this.props.location.search, 'source');

  /**
   * Initialize component
   */
  initComponent = (props) => {
    this.props.initModelProps(this.getModelName(), this.isCreating(), this.getSource(), this.getModelAttributes(), this.getDisplayedFields());

    if (!this.isCreating()) {
      const mainField = get(this.getModel(), 'info.mainField') || this.getModel().primaryKey;
      this.props.getData(props.match.params.id, this.getSource(), mainField);
    }

    // Get all relations made with the upload plugin
    const fileRelations = Object.keys(get(this.getSchema(), 'relations', {})).reduce((acc, current) => {
      const association = get(this.getSchema(), ['relations', current], {});

      if (association.plugin === 'upload' && association[association.type] === 'file') {
        const relation = {
          name: current,
          multiple: association.nature === 'manyToManyMorph',
        };

        acc.push(relation);
      }
      return acc;
    }, []);

    // Update the reducer so we can use it to create the appropriate FormData in the saga
    this.props.setFileRelations(fileRelations);
  }

  handleAddRelationItem = ({ key, value }) => {
    this.props.addRelationItem({
      key,
      value,
    });
  }

  handleBlur = ({ target }) => {
    const defaultValue = get(this.getModelAttribute(target.name), 'default');

    if (isEmpty(target.value) && defaultValue && target.value !== false) {
      return this.props.changeData({
        target: {
          name: `record.${target.name}`,
          value: defaultValue,
        },
      });
    }

    const errorIndex = findIndex(this.props.editPage.formErrors, ['name', target.name]);
    const errors = inputValidations(target.value, this.getAttributeValidations(target.name), target.type);
    const formErrors = cloneDeep(this.props.editPage.formErrors);

    if (errorIndex === -1 && !isEmpty(errors)) {
      formErrors.push({ name: target.name, errors });
    } else if (errorIndex !== -1 && isEmpty(errors)) {
      formErrors.splice(errorIndex, 1);
    } else if (!isEmpty(errors)) {
      formErrors.splice(errorIndex, 1, { name: target.name, errors });
    }

    return this.props.setFormErrors(formErrors);
  }

  handleChange = (e) => {
    let value = e.target.value;
    // Check if date
    if (isObject(e.target.value) && e.target.value._isAMomentObject === true) {
      value = moment(e.target.value, 'YYYY-MM-DD HH:mm:ss').format();
    } else if (['float', 'integer', 'biginteger', 'decimal'].indexOf(get(this.getSchema(), ['fields', e.target.name, 'type'])) !== -1) {
      value = toNumber(e.target.value);
    }

    const target = {
      name: `record.${e.target.name}`,
      value,
    };

    this.props.changeData({ target });
  }

  handleConfirm = () => {
    this.props.onCancel();
    this.toggle();
  }

  handleGoBack = () => this.props.history.goBack();

  handleRedirect = ({ model, id, source = 'content-manager'}) => {
    /* eslint-disable */
    switch (model) {
      case 'permission':
      case 'role':
      case 'file':
        // Exclude special models which are handled by plugins.
        if (source !== 'content-manager') {
          break;
        }
      default:
        const pathname = `${this.props.match.path.replace(':slug', model).replace(':id', id)}`;

        this.props.history.push({
          pathname,
          search: `?source=${source}&redirectURI=${generateRedirectURI({ model, search: `?source=${source}` })}`,
        });
    }
    /* eslint-enable */
  }

  handleSubmit = (e) => {
    e.preventDefault();
    const formErrors = checkFormValidity(this.generateFormFromRecord(), this.props.editPage.formValidations);

    if (isEmpty(formErrors)) {
      this.props.submit();
    }

    this.props.setFormErrors(formErrors);
  }

  hasDisplayedRelations = () => {
    return this.getDisplayedRelations().length > 0;
  }

  hasDisplayedFields = () => {
    return get(this.getModel(), ['editDisplay', 'fields'], []).length > 0;
  }

  isCreating = () => this.props.match.params.id === 'create';

  isRelationComponentNull = () => (
    Object.keys(get(this.getSchema(), 'relations', {})).filter(relation => (
      get(this.getSchema(), ['relations', relation, 'plugin']) !== 'upload' &&
      (!get(this.getSchema(), ['relations', relation, 'nature'], '').toLowerCase().includes('morph') || !get(this.getSchema(), ['relations', relation, relation]))
    )).length === 0
  )

  // NOTE: technical debt that needs to be redone
  generateFormFromRecord = () => (
    Object.keys(this.getModelAttributes()).reduce((acc, current) => {
      acc[current] = get(this.props.editPage.record, current, '');

      return acc;
    }, {})
  )

  pluginHeaderActions =  () => (
    [
      {
        label: 'content-manager.containers.Edit.reset',
        kind: 'secondary',
        onClick: this.toggle,
        type: 'button',
        disabled: this.showLoaders(),
      },
      {
        kind: 'primary',
        label: 'content-manager.containers.Edit.submit',
        onClick: this.handleSubmit,
        type: 'submit',
        loader: this.props.editPage.showLoader,
        style: this.props.editPage.showLoader ? { marginRight: '18px' } : {},
        disabled: this.showLoaders(),
      },
    ]
  );

  showLoaders = () => {
    const { editPage: { isLoading }, schema: { layout } } = this.props;

    return isLoading && !this.isCreating() || isLoading && get(layout, this.getModelName()) === undefined;
  }

  toggle = () => this.setState(prevState => ({ showWarning: !prevState.showWarning }));

  renderEdit = () => {
    const { editPage, location: { search } } = this.props;
    const source = getQueryParameters(search, 'source');
    const basePath = '/plugins/content-manager/ctm-configurations';
    const pathname = source !== 'content-manager'
      ? `${basePath}/plugins/${source}/${this.getModelName()}`
      : `${basePath}/${this.getModelName()}`;

    if (this.showLoaders()) {
      return (

            <LoadingIndicator />

      );
    }

    if (!this.hasDisplayedFields()) {
      return (

          <EmptyAttributesBlock
            description="content-manager.components.EmptyAttributesBlock.description"
            label="content-manager.components.EmptyAttributesBlock.button"
            onClick={() => this.props.history.push(pathname)}
          />

      );
    }

    return (

          <Edit
            attributes={this.getModelAttributes()}
            didCheckErrors={editPage.didCheckErrors}
            formValidations={editPage.formValidations}
            formErrors={editPage.formErrors}
            layout={this.getLayout()}
            modelName={this.getModelName()}
            onBlur={this.handleBlur}
            onChange={this.handleChange}
            record={editPage.record}
            resetProps={editPage.resetProps}
            schema={this.getSchema()}
          />

    );
  }

  render() {
    const { editPage, moveAttr, moveAttrEnd } = this.props;
    const { showWarning } = this.state;

    return (

        <form onSubmit={this.handleSubmit}>
          <BackHeader onClick={this.handleGoBack} />
          <CustomDragLayer />

            <PluginHeader
              actions={this.pluginHeaderActions()}
              title={{ id: this.getPluginHeaderTitle() }}
            />
            <PopUpWarning
              isOpen={showWarning}
              toggleModal={this.toggle}
              content={{
                title: 'content-manager.popUpWarning.title',
                message: 'content-manager.popUpWarning.warning.cancelAllSettings',
                cancel: 'content-manager.popUpWarning.button.cancel',
                confirm: 'content-manager.popUpWarning.button.confirm',
              }}
              popUpWarningType="danger"
              onConfirm={this.handleConfirm}
            />

              {this.renderEdit()}
              {this.hasDisplayedRelations() && (

                    {this.hasDisplayedRelations() && (
                      <EditRelations
                        changeData={this.props.changeData}
                        currentModelName={this.getModelName()}
                        displayedRelations={this.getDisplayedRelations()}
                        isDraggingSibling={editPage.isDraggingSibling}
                        location={this.props.location}
                        moveAttr={moveAttr}
                        moveAttrEnd={moveAttrEnd}
                        onAddRelationalItem={this.handleAddRelationItem}
                        onRedirect={this.handleRedirect}
                        onRemoveRelationItem={this.props.onRemoveRelationItem}
                        record={editPage.record}
                        schema={this.getSchema()}
                      />
                    )}

              )}

        </form>

    );
  }
}

EditPage.contextTypes = {
  plugins: PropTypes.object,
};

EditPage.defaultProps = {
  schema: {},
};

EditPage.propTypes = {
  addRelationItem: PropTypes.func.isRequired,
  changeData: PropTypes.func.isRequired,
  editPage: PropTypes.object.isRequired,
  getData: PropTypes.func.isRequired,
  history: PropTypes.object.isRequired,
  initModelProps: PropTypes.func.isRequired,
  location: PropTypes.object.isRequired,
  match: PropTypes.object.isRequired,
  moveAttr: PropTypes.func.isRequired,
  moveAttrEnd: PropTypes.func.isRequired,
  onCancel: PropTypes.func.isRequired,
  onRemoveRelationItem: PropTypes.func.isRequired,
  resetProps: PropTypes.func.isRequired,
  schema: PropTypes.object,
  setFileRelations: PropTypes.func.isRequired,
  setFormErrors: PropTypes.func.isRequired,
  submit: PropTypes.func.isRequired,
};

function mapDispatchToProps(dispatch) {
  return bindActionCreators(
    {
      addRelationItem,
      changeData,
      getData,
      initModelProps,
      moveAttr,
      moveAttrEnd,
      onCancel,
      onRemoveRelationItem,
      resetProps,
      setFileRelations,
      setFormErrors,
      submit,
    },
    dispatch,
  );
}

const mapStateToProps = createStructuredSelector({
  editPage: makeSelectEditPage(),
  schema: makeSelectSchema(),
});

const withConnect = connect(mapStateToProps, mapDispatchToProps);

const withReducer = injectReducer({ key: 'editPage', reducer });
const withSaga = injectSaga({ key: 'editPage', saga });

export default compose(
  withReducer,
  withSaga,
  withConnect,
)(DragDropContext(HTML5Backend)(EditPage));

<commit_msg>Fix saved date format content manager<commit_after>/**
 *
 * EditPage
 *
 */

import React from 'react';
import moment from 'moment';
import { connect } from 'react-redux';
import { bindActionCreators, compose } from 'redux';
import { createStructuredSelector } from 'reselect';
import PropTypes from 'prop-types';
import { cloneDeep, findIndex, get, includes, isEmpty, isObject, toNumber, toString, replace } from 'lodash';
import HTML5Backend from 'react-dnd-html5-backend';
import { DragDropContext } from 'react-dnd';
import cn from 'classnames';
// You can find these components in either
// ./node_modules/strapi-helper-plugin/lib/src
// or strapi/packages/strapi-helper-plugin/lib/src
import BackHeader from 'components/BackHeader';
import EmptyAttributesBlock from 'components/EmptyAttributesBlock';
import LoadingIndicator from 'components/LoadingIndicator';
import PluginHeader from 'components/PluginHeader';
import PopUpWarning from 'components/PopUpWarning';
// Plugin's components
import CustomDragLayer from 'components/CustomDragLayer';
import Edit from 'components/Edit';
import EditRelations from 'components/EditRelations';
// App selectors
import { makeSelectSchema } from 'containers/App/selectors';
import injectReducer from 'utils/injectReducer';
import injectSaga from 'utils/injectSaga';
import getQueryParameters from 'utils/getQueryParameters';
import { bindLayout } from 'utils/bindLayout';
import inputValidations from 'utils/inputsValidations';
import { generateRedirectURI } from 'containers/ListPage/utils';
import { checkFormValidity } from 'utils/formValidations';
import {
  addRelationItem,
  changeData,
  getData,
  initModelProps,
  moveAttr,
  moveAttrEnd,
  onCancel,
  onRemoveRelationItem,
  resetProps,
  setFileRelations,
  setFormErrors,
  submit,
} from './actions';
import reducer from './reducer';
import saga from './saga';
import makeSelectEditPage from './selectors';
import styles from './styles.scss';

export class EditPage extends React.Component {
  state = { showWarning: false };

  componentDidMount() {
    this.initComponent(this.props);
  }

  componentDidUpdate(prevProps) {
    if (prevProps.location.pathname !== this.props.location.pathname) {
      this.props.resetProps();
      this.initComponent(this.props);
    }

    if (prevProps.editPage.submitSuccess !== this.props.editPage.submitSuccess) {
      if (!isEmpty(this.props.location.search) && includes(this.props.location.search, '?redirectUrl')) {
        const redirectUrl = this.props.location.search.split('?redirectUrl=')[1];

        this.props.history.push({
          pathname: redirectUrl.split('?')[0],
          search: redirectUrl.split('?')[1],
        });
      } else {
        this.props.history.push({
          pathname: replace(this.props.location.pathname, '/create', ''),
          search: `?source=${this.getSource()}`,
        });
      }
    }
  }

  componentWillUnmount() {
    this.props.resetProps();
  }

  /**
   * Retrieve the model's displayed relations
   * @return {Array}
   */
  getDisplayedRelations = () => {
    return get(this.getSchema(), ['editDisplay', 'relations'], []);
  }

  /**
   * Retrieve the model's custom layout
   *
   */
  getLayout = () => (
    bindLayout.call(this, get(this.props.schema, ['layout', this.getModelName()], {}))
  )

  /**
   *
   *
   * @type {[type]}
   */
  getAttributeValidations = (name) => get(this.props.editPage.formValidations, [findIndex(this.props.editPage.formValidations, ['name', name]), 'validations'], {})

  getDisplayedFields = () => get(this.getSchema(), ['editDisplay', 'fields'], []);

  /**
   * Retrieve the model
   * @type {Object}
   */
  getModel = () => get(this.props.schema, ['models', this.getModelName()]) || get(this.props.schema, ['models', 'plugins', this.getSource(), this.getModelName()]);

  /**
   * Retrieve specific attribute
   * @type {String} name
   */
  getModelAttribute = (name) => get(this.getModelAttributes(), name);

  /**
   * Retrieve the model's attributes
   * @return {Object}
   */
  getModelAttributes = () => this.getModel().attributes;

  /**
   * Retrieve the model's name
   * @return {String} model's name
   */
  getModelName = () => this.props.match.params.slug.toLowerCase();

  /**
   * Retrieve model's schema
   * @return {Object}
   */
  getSchema = () => this.getSource() !== 'content-manager' ?
    get(this.props.schema, ['models', 'plugins', this.getSource(), this.getModelName()])
    : get(this.props.schema, ['models', this.getModelName()]);

  getPluginHeaderTitle = () => {
    if (this.isCreating()) {
      return toString(this.props.editPage.pluginHeaderTitle);
    }

    return this.props.match.params.id;
  }

  /**
   * Retrieve the model's source
   * @return {String}
   */
  getSource = () => getQueryParameters(this.props.location.search, 'source');

  /**
   * Initialize component
   */
  initComponent = (props) => {
    this.props.initModelProps(this.getModelName(), this.isCreating(), this.getSource(), this.getModelAttributes(), this.getDisplayedFields());

    if (!this.isCreating()) {
      const mainField = get(this.getModel(), 'info.mainField') || this.getModel().primaryKey;
      this.props.getData(props.match.params.id, this.getSource(), mainField);
    }

    // Get all relations made with the upload plugin
    const fileRelations = Object.keys(get(this.getSchema(), 'relations', {})).reduce((acc, current) => {
      const association = get(this.getSchema(), ['relations', current], {});

      if (association.plugin === 'upload' && association[association.type] === 'file') {
        const relation = {
          name: current,
          multiple: association.nature === 'manyToManyMorph',
        };

        acc.push(relation);
      }
      return acc;
    }, []);

    // Update the reducer so we can use it to create the appropriate FormData in the saga
    this.props.setFileRelations(fileRelations);
  }

  handleAddRelationItem = ({ key, value }) => {
    this.props.addRelationItem({
      key,
      value,
    });
  }

  handleBlur = ({ target }) => {
    const defaultValue = get(this.getModelAttribute(target.name), 'default');

    if (isEmpty(target.value) && defaultValue && target.value !== false) {
      return this.props.changeData({
        target: {
          name: `record.${target.name}`,
          value: defaultValue,
        },
      });
    }

    const errorIndex = findIndex(this.props.editPage.formErrors, ['name', target.name]);
    const errors = inputValidations(target.value, this.getAttributeValidations(target.name), target.type);
    const formErrors = cloneDeep(this.props.editPage.formErrors);

    if (errorIndex === -1 && !isEmpty(errors)) {
      formErrors.push({ name: target.name, errors });
    } else if (errorIndex !== -1 && isEmpty(errors)) {
      formErrors.splice(errorIndex, 1);
    } else if (!isEmpty(errors)) {
      formErrors.splice(errorIndex, 1, { name: target.name, errors });
    }

    return this.props.setFormErrors(formErrors);
  }

  handleChange = (e) => {
    let value = e.target.value;
    // Check if date
    if (isObject(e.target.value) && e.target.value._isAMomentObject === true) {
      value = moment(e.target.value).format('YYYY-MM-DD HH:mm:ss');
    } else if (['float', 'integer', 'biginteger', 'decimal'].indexOf(get(this.getSchema(), ['fields', e.target.name, 'type'])) !== -1) {
      value = toNumber(e.target.value);
    }

    const target = {
      name: `record.${e.target.name}`,
      value,
    };

    this.props.changeData({ target });
  }

  handleConfirm = () => {
    this.props.onCancel();
    this.toggle();
  }

  handleGoBack = () => this.props.history.goBack();

  handleRedirect = ({ model, id, source = 'content-manager'}) => {
    /* eslint-disable */
    switch (model) {
      case 'permission':
      case 'role':
      case 'file':
        // Exclude special models which are handled by plugins.
        if (source !== 'content-manager') {
          break;
        }
      default:
        const pathname = `${this.props.match.path.replace(':slug', model).replace(':id', id)}`;

        this.props.history.push({
          pathname,
          search: `?source=${source}&redirectURI=${generateRedirectURI({ model, search: `?source=${source}` })}`,
        });
    }
    /* eslint-enable */
  }

  handleSubmit = (e) => {
    e.preventDefault();
    const formErrors = checkFormValidity(this.generateFormFromRecord(), this.props.editPage.formValidations);

    if (isEmpty(formErrors)) {
      this.props.submit();
    }

    this.props.setFormErrors(formErrors);
  }

  hasDisplayedRelations = () => {
    return this.getDisplayedRelations().length > 0;
  }

  hasDisplayedFields = () => {
    return get(this.getModel(), ['editDisplay', 'fields'], []).length > 0;
  }

  isCreating = () => this.props.match.params.id === 'create';

  isRelationComponentNull = () => (
    Object.keys(get(this.getSchema(), 'relations', {})).filter(relation => (
      get(this.getSchema(), ['relations', relation, 'plugin']) !== 'upload' &&
      (!get(this.getSchema(), ['relations', relation, 'nature'], '').toLowerCase().includes('morph') || !get(this.getSchema(), ['relations', relation, relation]))
    )).length === 0
  )

  // NOTE: technical debt that needs to be redone
  generateFormFromRecord = () => (
    Object.keys(this.getModelAttributes()).reduce((acc, current) => {
      acc[current] = get(this.props.editPage.record, current, '');

      return acc;
    }, {})
  )

  pluginHeaderActions =  () => (
    [
      {
        label: 'content-manager.containers.Edit.reset',
        kind: 'secondary',
        onClick: this.toggle,
        type: 'button',
        disabled: this.showLoaders(),
      },
      {
        kind: 'primary',
        label: 'content-manager.containers.Edit.submit',
        onClick: this.handleSubmit,
        type: 'submit',
        loader: this.props.editPage.showLoader,
        style: this.props.editPage.showLoader ? { marginRight: '18px' } : {},
        disabled: this.showLoaders(),
      },
    ]
  );

  showLoaders = () => {
    const { editPage: { isLoading }, schema: { layout } } = this.props;

    return isLoading && !this.isCreating() || isLoading && get(layout, this.getModelName()) === undefined;
  }

  toggle = () => this.setState(prevState => ({ showWarning: !prevState.showWarning }));

  renderEdit = () => {
    const { editPage, location: { search } } = this.props;
    const source = getQueryParameters(search, 'source');
    const basePath = '/plugins/content-manager/ctm-configurations';
    const pathname = source !== 'content-manager'
      ? `${basePath}/plugins/${source}/${this.getModelName()}`
      : `${basePath}/${this.getModelName()}`;

    if (this.showLoaders()) {
      return (

            <LoadingIndicator />

      );
    }

    if (!this.hasDisplayedFields()) {
      return (

          <EmptyAttributesBlock
            description="content-manager.components.EmptyAttributesBlock.description"
            label="content-manager.components.EmptyAttributesBlock.button"
            onClick={() => this.props.history.push(pathname)}
          />

      );
    }

    return (

          <Edit
            attributes={this.getModelAttributes()}
            didCheckErrors={editPage.didCheckErrors}
            formValidations={editPage.formValidations}
            formErrors={editPage.formErrors}
            layout={this.getLayout()}
            modelName={this.getModelName()}
            onBlur={this.handleBlur}
            onChange={this.handleChange}
            record={editPage.record}
            resetProps={editPage.resetProps}
            schema={this.getSchema()}
          />

    );
  }

  render() {
    const { editPage, moveAttr, moveAttrEnd } = this.props;
    const { showWarning } = this.state;

    return (

        <form onSubmit={this.handleSubmit}>
          <BackHeader onClick={this.handleGoBack} />
          <CustomDragLayer />

            <PluginHeader
              actions={this.pluginHeaderActions()}
              title={{ id: this.getPluginHeaderTitle() }}
            />
            <PopUpWarning
              isOpen={showWarning}
              toggleModal={this.toggle}
              content={{
                title: 'content-manager.popUpWarning.title',
                message: 'content-manager.popUpWarning.warning.cancelAllSettings',
                cancel: 'content-manager.popUpWarning.button.cancel',
                confirm: 'content-manager.popUpWarning.button.confirm',
              }}
              popUpWarningType="danger"
              onConfirm={this.handleConfirm}
            />

              {this.renderEdit()}
              {this.hasDisplayedRelations() && (

                    {this.hasDisplayedRelations() && (
                      <EditRelations
                        changeData={this.props.changeData}
                        currentModelName={this.getModelName()}
                        displayedRelations={this.getDisplayedRelations()}
                        isDraggingSibling={editPage.isDraggingSibling}
                        location={this.props.location}
                        moveAttr={moveAttr}
                        moveAttrEnd={moveAttrEnd}
                        onAddRelationalItem={this.handleAddRelationItem}
                        onRedirect={this.handleRedirect}
                        onRemoveRelationItem={this.props.onRemoveRelationItem}
                        record={editPage.record}
                        schema={this.getSchema()}
                      />
                    )}

              )}

        </form>

    );
  }
}

EditPage.contextTypes = {
  plugins: PropTypes.object,
};

EditPage.defaultProps = {
  schema: {},
};

EditPage.propTypes = {
  addRelationItem: PropTypes.func.isRequired,
  changeData: PropTypes.func.isRequired,
  editPage: PropTypes.object.isRequired,
  getData: PropTypes.func.isRequired,
  history: PropTypes.object.isRequired,
  initModelProps: PropTypes.func.isRequired,
  location: PropTypes.object.isRequired,
  match: PropTypes.object.isRequired,
  moveAttr: PropTypes.func.isRequired,
  moveAttrEnd: PropTypes.func.isRequired,
  onCancel: PropTypes.func.isRequired,
  onRemoveRelationItem: PropTypes.func.isRequired,
  resetProps: PropTypes.func.isRequired,
  schema: PropTypes.object,
  setFileRelations: PropTypes.func.isRequired,
  setFormErrors: PropTypes.func.isRequired,
  submit: PropTypes.func.isRequired,
};

function mapDispatchToProps(dispatch) {
  return bindActionCreators(
    {
      addRelationItem,
      changeData,
      getData,
      initModelProps,
      moveAttr,
      moveAttrEnd,
      onCancel,
      onRemoveRelationItem,
      resetProps,
      setFileRelations,
      setFormErrors,
      submit,
    },
    dispatch,
  );
}

const mapStateToProps = createStructuredSelector({
  editPage: makeSelectEditPage(),
  schema: makeSelectSchema(),
});

const withConnect = connect(mapStateToProps, mapDispatchToProps);

const withReducer = injectReducer({ key: 'editPage', reducer });
const withSaga = injectSaga({ key: 'editPage', saga });

export default compose(
  withReducer,
  withSaga,
  withConnect,
)(DragDropContext(HTML5Backend)(EditPage));
<|endoftext|>


<commit_before>var OlarkObserver = (function(OO, document, window) {
    'use strict';

    var debug = false,
        statusObserver,
        chatTabObserver,
        chatListObserver,
        linkObserver;

    if (OO instanceof Object) {
        // OlarkObserver is already injected
        // Debug behavior - disconnect old listeners,
        // and proceed to load new OlarkObserver object
        debug = OO.disconnect();
    }

    /* Operator status changes */
    var statusObserver = new MutationObserver(function(mutations) {
        // Multiple mutations take place for each status change.
        // Only the final mutation is important in determining the new status.
        var lastMutation = mutations[mutations.length - 1],
            newNodes = lastMutation.addedNodes,
            newStatus = false;

        // Grab the #status-indicator element, see what class has been assigned to it.
        for (var i = 0; i < newNodes.length; i++) {
            if (newNodes[i].id === 'status-indicator') {
                newStatus = newNodes[i].className;
            }
        }

        if (newStatus) {
            sendXHR('PUT', 'status/' + newStatus);
        }
    })

    /* New unread chats */
    var chatTabObserver = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            // mutation.target is a LIVE element, and mutation callbacks are ASYNC
            // so, ensure the tab has not been closed (mutation.target is not null)
            if (mutation.type === 'attributes' && mutation.target !== null) {
                var displayNameElement = mutation.target.querySelector('.display-name');

                if (displayNameElement === null) {
                    return; // 'continue' with the next forEach, this is a closed chat tab
                }

                var tabName = hashString(displayNameElement.textContent.trim());

                if (mutation.target.classList.contains('unread') && mutation.oldValue.indexOf('unread') === -1) {
                    // mutation.target just added the unread class
                    sendXHR('PUT', 'chats/' + tabName + '/1');
                } else if (mutation.target.classList.contains('unread') === false && mutation.oldValue.indexOf('unread') !== -1) {
                    // mutation.target just removed the unread class
                    sendXHR('PUT', 'chats/' + tabName + '/0');
                }
            }
        });
    });
    var chatListObserver = new MutationObserver(function(mutations) {
        // iterate over mutations to look for new chats (new child added to #active-chats)
        mutations.forEach(function(mutation) {
            if (mutation.type === 'childList' && mutation.target.id === 'active-chats' && mutation.addedNodes.length > 0) {
                sendXHR('PUT', 'chats/new/1')
                var newTab = mutation.addedNodes[0];
                chatTabObserver.observe(newTab, {attributes: true, attributeOldValue: true});
            }
        });
    });

    /* New unread chats, while window is inactive */
    var linkObserver = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                // It seems only one element is added for each mutation
                var newElement = mutation.addedNodes[0];
                // 'continue' with next forEach if the new element is not a link[rel=icon]
                if (newElement.rel !== 'icon') {
                    return;
                }
				var imgHash = hashString(newElement.href);
                var newStatus = identifyImage(imgHash);
                if (newStatus !== false) {
                    sendXHR('PUT', 'status/'+newStatus);
                } else {
                    debugLog('Unidentified status icon', imgHash);
                }
            }
        });
    });
    /* @todo: slim down this list to one image per status */
    function identifyImage(imgHash) {
        switch (imgHash) {
            case '2112346368':
            case '-738135977':
                return 'available';
            case '1748419166':
            case '-2139276908':
            case '-247468270':
                return 'available?withChats=1';
            case '1488560486':
                return 'available?withChats=2';
            case '713115020':
                return 'available?withChats=3';
            case '859112101':
                return 'away';
            case '744595388':
                return 'away?withChats=1';
            /* need base64 image for away?withChats=2 */
            case '872779176':
                return 'away?withChats=3';
            default:
                return false;
        }
    }

    function disconnect() {
        var observers = [statusObserver,chatTabObserver,chatListObserver,linkObserver];
        for (var i=0; i<observers.length; i++) {
            debugLog('Disconnecting', observers[i]);
            observers[i].disconnect();
        }
        return debug;
    }

    function setDebugMode(bool) {
        debug = (bool) ? true : false;
        var prefix = (debug) ? 'en' : 'dis';
        return 'Debug mode ' + prefix + 'abled';
    }

    function debugLog() {
        if (debug) {
            console.log.apply(console, arguments);
        }
    }

    function hashString(string) {
        var hash = 0, i, chr, len;
        if (string.length === 0) return hash;
        for (i = 0, len = string.length; i < len; i++) {
            chr = string.charCodeAt(i);
            hash = ((hash << 5) - hash) + chr;
            hash |= 0;
        }
        return hash;
    }

    function sendXHR(method, newStatus, successCb, errorCb) {
        debugLog(method, newStatus);
        var oReq = new XMLHttpRequest();
        if (successCb instanceof Function) oReq.addEventListener('load', successCb);
        if (errorCb instanceof Function) oReq.addEventListener('error', errorCb);
        oReq.open(method, 'https://localhost.spacenate.com:4443/' + newStatus, true);
        oReq.send();
    }

    var feedbackEl,
        statusIndicator,
        statusText,
        offScreen,
        redColor = '#d65129',
        yellowColor = '#f9cb32',
        greenColor = '#88de68';

    function createFeedbackElement() {
        if ((feedbackEl = document.getElementById('olark-observer')) instanceof Object) {
            statusIndicator = document.getElementById('olark-observer-status-indicator');
            statusText = document.getElementById('olark-observer-status-text');
            offScreen = document.getElementById('olark-observer-offscreen');
            return;
        }
        /* olark-observer-container */
        var container = document.createElement('div');
        container.id = 'olark-observer-container';
        container.style.position = 'absolute';
        container.style.right = '20px';
        container.style.bottom = '0px';
        container.style.paddingBottom = '18px';
        /* olark-observer */
        feedbackEl = document.createElement('div');
        feedbackEl.id = 'olark-observer';
        feedbackEl.style.display = 'inline-block';
        feedbackEl.style.width = '156px';
        feedbackEl.style.padding = '7px 14px';
        feedbackEl.style.borderRadius = '19px';
        feedbackEl.style.backgroundColor = 'rgba(255,255,255,0.1)';
        feedbackEl.style.color = '#fff';
        feedbackEl.style.fontSize = '14px';
        feedbackEl.style.transition = 'transform 1s, width .6s';
        feedbackEl.style.transform = 'translateY(4em)';
        /* status-indicator */
        statusIndicator = document.createElement('span');
        statusIndicator.id = 'olark-observer-status-indicator';
        statusIndicator.style.float = 'right';
        statusIndicator.style.position = 'relative';
        statusIndicator.style.top = '3px';
        statusIndicator.style.left = '4px';
        statusIndicator.style.height = '9px';
        statusIndicator.style.width = '9px';
        statusIndicator.style.marginLeft = '5px';
        statusIndicator.style.borderRadius = '10px';
        statusIndicator.style.backgroundColor = redColor;
        /* status-text */
        statusText = document.createElement('span');
        statusText.id = 'olark-observer-status-text';
        statusText.style.float = 'right';
        statusText.style.height = '16px';
        statusText.style.transition = 'opacity .4s';

        feedbackEl.appendChild(statusIndicator);
        feedbackEl.appendChild(statusText);
        container.appendChild(feedbackEl);
        document.body.appendChild(container);

        /* off screen 'test sizing' area */
        var offScreenContainer = document.createElement('div');
        offScreenContainer.style.position = 'absolute';
        offScreenContainer.style.top = '-100em';
        offScreen = document.createElement('p');
        offScreen.id = 'olark-observer-offscreen';
        offScreen.style.fontSize = '14px';

        offScreenContainer.appendChild(offScreen);
        document.body.appendChild(offScreenContainer);

        statusText.textContent = 'Connecting to server...';
        window.setTimeout(function(){
            feedbackEl.style.transform = 'translateY(0em)';
        }, 0);
    }

    function showFeedback(message) {
        debugLog(message);
        statusText.style.opacity = '0';
        window.setTimeout(function(){
            statusText.textContent = '';
            offScreen.textContent = message;
            feedbackEl.style.width = (offScreen.scrollWidth + 15) + 'px';
            window.setTimeout(function(){
                statusText.textContent = message;
                statusText.style.opacity = '1';
            }, 600);
        }, 400);
    }

    var retryWaitTime = 1200,
        retryIncrement = 1.1;

    function connectToServer() {
        sendXHR('GET', '',
            function(){
                statusIndicator.style.backgroundColor = yellowColor;
                showFeedback('Connecting to device...');
                connectToDevice();
                retryWaitTime = 800;
            },
            function(){
                debugLog('Error connecting to server');
                window.setTimeout(connectToServer, retryWaitTime);
                retryWaitTime *= retryIncrement;
            });
    }

    function connectToDevice() {
        sendXHR('GET', 'device',
            function(e){
                var response = JSON.parse(e.currentTarget.responseText);
                if (response.result === 'ACK') {
                    statusIndicator.style.backgroundColor = greenColor;
                    showFeedback('Connected');
					/* @todo: set current status */
                    retryWaitTime = 1200;
                } else {
                    debugLog(response);
                    window.setTimeout(connectToDevice, retryWaitTime);
                    retryWaitTime *= retryIncrement;
                }
            },
            function(){
                debugLog('Error connecting to server');
                window.setTimeout(connectToServer, retryWaitTime);
            });
    }

    /* Start observers observing */
    var statusPanelEl = document.querySelector('#op-status-panel'),
        activeChatsEl = document.querySelector('#active-chats');

    if (statusPanelEl instanceof Object && activeChatsEl instanceof Object) {
        statusObserver.observe(statusPanelEl, {childList: true});
        chatListObserver.observe(activeChatsEl, { childList: true});
        linkObserver.observe(document.querySelector('head'), {childList: true});
        debugLog('Observing!');
    } else {
        debugLog('Not observing.');
    }

    createFeedbackElement();
    connectToServer();

    return {
        send: sendXHR,
        disconnect: disconnect,
        setDebugMode: setDebugMode,
        showFeedback: showFeedback,
		hashString: hashString
    };
}(OlarkObserver, document, window));
<commit_msg>Add getCurrentStatusIcon function<commit_after>var OlarkObserver = (function(OO, document, window) {
    'use strict';

    var debug = false,
        statusObserver,
        chatTabObserver,
        chatListObserver,
        linkObserver;

    if (OO instanceof Object) {
        // OlarkObserver is already injected
        // Debug behavior - disconnect old listeners,
        // and proceed to load new OlarkObserver object
        debug = OO.disconnect();
    }

    /* Operator status changes */
    var statusObserver = new MutationObserver(function(mutations) {
        // Multiple mutations take place for each status change.
        // Only the final mutation is important in determining the new status.
        var lastMutation = mutations[mutations.length - 1],
            newNodes = lastMutation.addedNodes,
            newStatus = false;

        // Grab the #status-indicator element, see what class has been assigned to it.
        for (var i = 0; i < newNodes.length; i++) {
            if (newNodes[i].id === 'status-indicator') {
                newStatus = newNodes[i].className;
            }
        }

        if (newStatus) {
            sendXHR('PUT', 'status/' + newStatus);
        }
    })

    /* New unread chats */
    var chatTabObserver = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            // mutation.target is a LIVE element, and mutation callbacks are ASYNC
            // so, ensure the tab has not been closed (mutation.target is not null)
            if (mutation.type === 'attributes' && mutation.target !== null) {
                var displayNameElement = mutation.target.querySelector('.display-name');

                if (displayNameElement === null) {
                    return; // 'continue' with the next forEach, this is a closed chat tab
                }

                var tabName = hashString(displayNameElement.textContent.trim());

                if (mutation.target.classList.contains('unread') && mutation.oldValue.indexOf('unread') === -1) {
                    // mutation.target just added the unread class
                    sendXHR('PUT', 'chats/' + tabName + '/1');
                } else if (mutation.target.classList.contains('unread') === false && mutation.oldValue.indexOf('unread') !== -1) {
                    // mutation.target just removed the unread class
                    sendXHR('PUT', 'chats/' + tabName + '/0');
                }
            }
        });
    });
    var chatListObserver = new MutationObserver(function(mutations) {
        // iterate over mutations to look for new chats (new child added to #active-chats)
        mutations.forEach(function(mutation) {
            if (mutation.type === 'childList' && mutation.target.id === 'active-chats' && mutation.addedNodes.length > 0) {
                sendXHR('PUT', 'chats/new/1')
                var newTab = mutation.addedNodes[0];
                chatTabObserver.observe(newTab, {attributes: true, attributeOldValue: true});
            }
        });
    });

    /* New unread chats, while window is inactive */
    var linkObserver = new MutationObserver(function(mutations) {
        mutations.forEach(function(mutation) {
            if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                // It seems only one element is added for each mutation
                var newElement = mutation.addedNodes[0];
                // 'continue' with next forEach if the new element is not a link[rel=icon]
                if (newElement.rel !== 'icon') {
                    return;
                }
				var imgHash = hashString(newElement.href);
                var newStatus = identifyImage(imgHash);
                if (newStatus !== false) {
                    sendXHR('PUT', 'status/'+newStatus);
                } else {
                    debugLog('Unidentified status icon', imgHash);
                }
            }
        });
    });

	function getCurrentStatusIcon() {
		var links = document.head.getElementsByTagName('link');
		return hashString(links[links.length-1]);
	}

    /* @todo: slim down this list to one image per status */
    function identifyImage(imgHash) {
        switch (imgHash) {
            case '2112346368':
            case '-738135977':
                return 'available';
            case '1748419166':
            case '-2139276908':
            case '-247468270':
                return 'available?withChats=1';
            case '1488560486':
                return 'available?withChats=2';
            case '713115020':
                return 'available?withChats=3';
            case '859112101':
                return 'away';
            case '744595388':
                return 'away?withChats=1';
            /* need base64 image for away?withChats=2 */
            case '872779176':
                return 'away?withChats=3';
            default:
                return false;
        }
    }

    function disconnect() {
        var observers = [statusObserver,chatTabObserver,chatListObserver,linkObserver];
        for (var i=0; i<observers.length; i++) {
            debugLog('Disconnecting', observers[i]);
            observers[i].disconnect();
        }
        return debug;
    }

    function setDebugMode(bool) {
        debug = (bool) ? true : false;
        var prefix = (debug) ? 'en' : 'dis';
        return 'Debug mode ' + prefix + 'abled';
    }

    function debugLog() {
        if (debug) {
            console.log.apply(console, arguments);
        }
    }

    function hashString(string) {
        var hash = 0, i, chr, len;
        if (string.length === 0) return hash;
        for (i = 0, len = string.length; i < len; i++) {
            chr = string.charCodeAt(i);
            hash = ((hash << 5) - hash) + chr;
            hash |= 0;
        }
        return hash;
    }

    function sendXHR(method, newStatus, successCb, errorCb) {
        debugLog(method, newStatus);
        var oReq = new XMLHttpRequest();
        if (successCb instanceof Function) oReq.addEventListener('load', successCb);
        if (errorCb instanceof Function) oReq.addEventListener('error', errorCb);
        oReq.open(method, 'https://localhost.spacenate.com:4443/' + newStatus, true);
        oReq.send();
    }

    var feedbackEl,
        statusIndicator,
        statusText,
        offScreen,
        redColor = '#d65129',
        yellowColor = '#f9cb32',
        greenColor = '#88de68';

    function createFeedbackElement() {
        if ((feedbackEl = document.getElementById('olark-observer')) instanceof Object) {
            statusIndicator = document.getElementById('olark-observer-status-indicator');
            statusText = document.getElementById('olark-observer-status-text');
            offScreen = document.getElementById('olark-observer-offscreen');
            return;
        }
        /* olark-observer-container */
        var container = document.createElement('div');
        container.id = 'olark-observer-container';
        container.style.position = 'absolute';
        container.style.right = '20px';
        container.style.bottom = '0px';
        container.style.paddingBottom = '18px';
        /* olark-observer */
        feedbackEl = document.createElement('div');
        feedbackEl.id = 'olark-observer';
        feedbackEl.style.display = 'inline-block';
        feedbackEl.style.width = '156px';
        feedbackEl.style.padding = '7px 14px';
        feedbackEl.style.borderRadius = '19px';
        feedbackEl.style.backgroundColor = 'rgba(255,255,255,0.1)';
        feedbackEl.style.color = '#fff';
        feedbackEl.style.fontSize = '14px';
        feedbackEl.style.transition = 'transform 1s, width .6s';
        feedbackEl.style.transform = 'translateY(4em)';
        /* status-indicator */
        statusIndicator = document.createElement('span');
        statusIndicator.id = 'olark-observer-status-indicator';
        statusIndicator.style.float = 'right';
        statusIndicator.style.position = 'relative';
        statusIndicator.style.top = '3px';
        statusIndicator.style.left = '4px';
        statusIndicator.style.height = '9px';
        statusIndicator.style.width = '9px';
        statusIndicator.style.marginLeft = '5px';
        statusIndicator.style.borderRadius = '10px';
        statusIndicator.style.backgroundColor = redColor;
        /* status-text */
        statusText = document.createElement('span');
        statusText.id = 'olark-observer-status-text';
        statusText.style.float = 'right';
        statusText.style.height = '16px';
        statusText.style.transition = 'opacity .4s';

        feedbackEl.appendChild(statusIndicator);
        feedbackEl.appendChild(statusText);
        container.appendChild(feedbackEl);
        document.body.appendChild(container);

        /* off screen 'test sizing' area */
        var offScreenContainer = document.createElement('div');
        offScreenContainer.style.position = 'absolute';
        offScreenContainer.style.top = '-100em';
        offScreen = document.createElement('p');
        offScreen.id = 'olark-observer-offscreen';
        offScreen.style.fontSize = '14px';

        offScreenContainer.appendChild(offScreen);
        document.body.appendChild(offScreenContainer);

        statusText.textContent = 'Connecting to server...';
        window.setTimeout(function(){
            feedbackEl.style.transform = 'translateY(0em)';
        }, 0);
    }

    function showFeedback(message) {
        debugLog(message);
        statusText.style.opacity = '0';
        window.setTimeout(function(){
            statusText.textContent = '';
            offScreen.textContent = message;
            feedbackEl.style.width = (offScreen.scrollWidth + 15) + 'px';
            window.setTimeout(function(){
                statusText.textContent = message;
                statusText.style.opacity = '1';
            }, 600);
        }, 400);
    }

    var retryWaitTime = 1200,
        retryIncrement = 1.1;

    function connectToServer() {
        sendXHR('GET', '',
            function(){
                statusIndicator.style.backgroundColor = yellowColor;
                showFeedback('Connecting to device...');
                connectToDevice();
                retryWaitTime = 800;
            },
            function(){
                debugLog('Error connecting to server');
                window.setTimeout(connectToServer, retryWaitTime);
                retryWaitTime *= retryIncrement;
            });
    }

    function connectToDevice() {
        sendXHR('GET', 'device',
            function(e){
                var response = JSON.parse(e.currentTarget.responseText);
                if (response.result === 'ACK') {
                    statusIndicator.style.backgroundColor = greenColor;
                    showFeedback('Connected');
					/* @todo: set current status */
                    retryWaitTime = 1200;
                } else {
                    debugLog(response);
                    window.setTimeout(connectToDevice, retryWaitTime);
                    retryWaitTime *= retryIncrement;
                }
            },
            function(){
                debugLog('Error connecting to server');
                window.setTimeout(connectToServer, retryWaitTime);
            });
    }

    /* Start observers observing */
    var statusPanelEl = document.querySelector('#op-status-panel'),
        activeChatsEl = document.querySelector('#active-chats');

    if (statusPanelEl instanceof Object && activeChatsEl instanceof Object) {
        statusObserver.observe(statusPanelEl, {childList: true});
        chatListObserver.observe(activeChatsEl, { childList: true});
        linkObserver.observe(document.querySelector('head'), {childList: true});
        debugLog('Observing!');
    } else {
        debugLog('Not observing.');
    }

    createFeedbackElement();
    connectToServer();

    return {
        send: sendXHR,
        disconnect: disconnect,
        setDebugMode: setDebugMode,
        showFeedback: showFeedback,
		hashString: hashString,
		getIcon: getCurrentStatusIcon
    };
}(OlarkObserver, document, window));
<|endoftext|>


<commit_before>// @flow
import type {
  Id,
  Operation,
  OperationResult,
  PhenylClient,
  AclHandler,
  ValidationHandler,
  SessionClient,
  CustomQueryHandler,
  CustomCommandHandler,
} from 'phenyl-interfaces'

type LambdaEvent = Object // TODO
type LambdaContext = Object // TODO
type LambdaResponse = Object // TODO
type LambdaHandler = (event: LambdaEvent, context: LambdaContext) => Promise<LambdaResponse>

type ServerlessParams = {
  aclHandler: AclHandler,
  validationHandler: ValidationHandler,
  client: PhenylClient,
  sessionClient: SessionClient,
  custom?: {
    query?: CustomQueryHandler,
    command?: CustomCommandHandler,
  }
}

/**
 *
 */
export default class PhenylServerless {
  aclHandler: AclHandler
  validationHandler: ValidationHandler
  client: PhenylClient
  sessionClient: SessionClient
  custom: {
    query: CustomQueryHandler,
    command: CustomCommandHandler,
  }

  constructor(params: ServerlessParams) {
    this.aclHandler = params.aclHandler
    this.validationHandler = params.validationHandler
    this.client = params.client
    this.sessionClient = params.sessionClient
    this.custom = {}
    if (params.custom != null) {
      if (params.custom.query != null) {
        this.custom.query = params.custom.query
      }
      if (params.custom.command != null) {
        this.custom.command = params.custom.command
      }
    }
  }

  /**
   * @public
   */
  getLambdaHandler(): LambdaHandler {
    return (event: LambdaEvent, context: LambdaContext): Promise<LambdaResponse> => {
      const { operation, sessionId } = context
      return this.run(operation, sessionId)
    }
  }

  /**
   *
   */
  async run(operation: Operation, sessionId: ?Id): Promise<OperationResult> {
    const session = await this.sessionClient.get(sessionId)

    this.assertTypeValid(operation)

    const isAccessible = await this.aclHandler(operation, session, this.client)
    if (!isAccessible) {
      throw new Error('Authorization Required.')
    }

    const isValid = await this.validationHandler(operation, session, this.client)
    if (!isValid) {
      throw new Error('Params are not valid.')
    }

    if (operation.find != null) {
      const result = await this.client.find(operation.find)
      return { find: result }
    }
    if (operation.findOne != null) {
      const result = await this.client.findOne(operation.findOne)
      return { findOne: result }
    }
    if (operation.get != null) {
      const result = await this.client.get(operation.get)
      return { get: result }
    }
    if (operation.getByIds != null) {
      const result = await this.client.getByIds(operation.getByIds)
      return { getByIds: result }
    }
    if (operation.insert != null) {
      const result = await this.client.insert(operation.insert)
      return { insert: result }
    }
    if (operation.insertAndGet != null) {
      const result = await this.client.insertAndGet(operation.insertAndGet)
      return { insertAndGet: result }
    }
    if (operation.insertAndFetch != null) {
      const result = await this.client.insertAndFetch(operation.insertAndFetch)
      return { insertAndFetch: result }
    }
    if (operation.update != null) {
      const result = await this.client.update(operation.update)
      return { update: result }
    }
    if (operation.updateAndGet != null) {
      const result = await this.client.updateAndGet(operation.updateAndGet)
      return { updateAndGet: result }

    }
    if (operation.updateAndFetch != null) {
      const result = await this.client.updateAndFetch(operation.updateAndFetch)
      return { updateAndFetch: result }
    }
    if (operation.delete != null) {
      const result = await this.client.delete(operation.delete)
      return { delete: result }
    }

    if (operation.runCustomQuery != null) {
      const result = await this.custom.query(operation.runCustomQuery)
      return { runCustomQuery: result }
    }
    if (operation.runCustomCommand != null) {
      const result = await this.custom.command(operation.runCustomCommand)
      return { runCustomCommand: result }
    }

    throw new Error('Invalid operation.')
  }

  assertTypeValid(operation: Operation): void {
    if (operation == null) throw new Error('operation is not an object.')
    if (operation.find != null) {
      // assertValidWhereQuery(operation.find)
    }
  }
}
<commit_msg>phenyl-serverless: use phenyl-utils::assertValidOperation()<commit_after>// @flow
import {
  assertValidOperation,
} from 'phenyl-utils'

import type {
  Id,
  Operation,
  OperationResult,
  PhenylClient,
  AclHandler,
  ValidationHandler,
  SessionClient,
  CustomQueryHandler,
  CustomCommandHandler,
} from 'phenyl-interfaces'

type LambdaEvent = Object // TODO
type LambdaContext = Object // TODO
type LambdaResponse = Object // TODO
type LambdaHandler = (event: LambdaEvent, context: LambdaContext) => Promise<LambdaResponse>

type ServerlessParams = {
  aclHandler: AclHandler,
  validationHandler: ValidationHandler,
  client: PhenylClient,
  sessionClient: SessionClient,
  custom?: {
    query?: CustomQueryHandler,
    command?: CustomCommandHandler,
  }
}

/**
 *
 */
export default class PhenylServerless {
  aclHandler: AclHandler
  validationHandler: ValidationHandler
  client: PhenylClient
  sessionClient: SessionClient
  custom: {
    query: CustomQueryHandler,
    command: CustomCommandHandler,
  }

  constructor(params: ServerlessParams) {
    this.aclHandler = params.aclHandler
    this.validationHandler = params.validationHandler
    this.client = params.client
    this.sessionClient = params.sessionClient
    this.custom = {}
    if (params.custom != null) {
      if (params.custom.query != null) {
        this.custom.query = params.custom.query
      }
      if (params.custom.command != null) {
        this.custom.command = params.custom.command
      }
    }
  }

  /**
   * @public
   */
  getLambdaHandler(): LambdaHandler {
    return (event: LambdaEvent, context: LambdaContext): Promise<LambdaResponse> => {
      const { operation, sessionId } = context
      return this.run(operation, sessionId)
    }
  }

  /**
   *
   */
  async run(operation: Operation, sessionId: ?Id): Promise<OperationResult> {
    const session = await this.sessionClient.get(sessionId)

    assertValidOperation(operation)

    const isAccessible = await this.aclHandler(operation, session, this.client)
    if (!isAccessible) {
      throw new Error('Authorization Required.')
    }

    const isValid = await this.validationHandler(operation, session, this.client)
    if (!isValid) {
      throw new Error('Params are not valid.')
    }

    if (operation.find != null) {
      const result = await this.client.find(operation.find)
      return { find: result }
    }
    if (operation.findOne != null) {
      const result = await this.client.findOne(operation.findOne)
      return { findOne: result }
    }
    if (operation.get != null) {
      const result = await this.client.get(operation.get)
      return { get: result }
    }
    if (operation.getByIds != null) {
      const result = await this.client.getByIds(operation.getByIds)
      return { getByIds: result }
    }
    if (operation.insert != null) {
      const result = await this.client.insert(operation.insert)
      return { insert: result }
    }
    if (operation.insertAndGet != null) {
      const result = await this.client.insertAndGet(operation.insertAndGet)
      return { insertAndGet: result }
    }
    if (operation.insertAndFetch != null) {
      const result = await this.client.insertAndFetch(operation.insertAndFetch)
      return { insertAndFetch: result }
    }
    if (operation.update != null) {
      const result = await this.client.update(operation.update)
      return { update: result }
    }
    if (operation.updateAndGet != null) {
      const result = await this.client.updateAndGet(operation.updateAndGet)
      return { updateAndGet: result }

    }
    if (operation.updateAndFetch != null) {
      const result = await this.client.updateAndFetch(operation.updateAndFetch)
      return { updateAndFetch: result }
    }
    if (operation.delete != null) {
      const result = await this.client.delete(operation.delete)
      return { delete: result }
    }

    if (operation.runCustomQuery != null) {
      const result = await this.custom.query(operation.runCustomQuery)
      return { runCustomQuery: result }
    }
    if (operation.runCustomCommand != null) {
      const result = await this.custom.command(operation.runCustomCommand)
      return { runCustomCommand: result }
    }

    throw new Error('Invalid operation.')
  }
}
<|endoftext|>