<commit_before>import { fromJS } from 'immutable';
import { normalize, arrayOf } from 'normalizr';
import when from 'when';

import CurrentPlanActions from '../actions/CurrentPlanActions';
import { browserHistory } from 'react-router';
import MistralApiService from '../services/MistralApiService';
import MistralApiErrorHandler from '../services/MistralApiErrorHandler';
import NotificationActions from '../actions/NotificationActions';
import PlansConstants from '../constants/PlansConstants';
import { planFileSchema } from '../normalizrSchemas/plans';
import StackActions from '../actions/StacksActions';
import SwiftApiErrorHandler from '../services/SwiftApiErrorHandler';
import SwiftApiService from '../services/SwiftApiService';
import MistralConstants from '../constants/MistralConstants';

export default {
  requestPlans() {
    return {
      type: PlansConstants.REQUEST_PLANS
    };
  },

  receivePlans(plans) {
    return {
      type: PlansConstants.RECEIVE_PLANS,
      payload: plans
    };
  },

  fetchPlans() {
    return dispatch => {
      dispatch(this.requestPlans());
      MistralApiService.runAction(MistralConstants.PLAN_LIST).then((response) => {
        let plans = JSON.parse(response.output).result || [];
        dispatch(this.receivePlans(plans));
        dispatch(CurrentPlanActions.detectPlan(plans));
      }).catch((error) => {
        let errorHandler = new MistralApiErrorHandler(error);
        errorHandler.errors.forEach((error) => {
          dispatch(NotificationActions.notify(error));
        });
      });
    };
  },

  requestPlan() {
    return {
      type: PlansConstants.REQUEST_PLAN
    };
  },

  receivePlan(planName, planFiles) {
    return {
      type: PlansConstants.RECEIVE_PLAN,
      payload: {
        planName: planName,
        planFiles: planFiles
      }
    };
  },

  fetchPlan(planName) {
    return dispatch => {
      dispatch(this.requestPlan());
      SwiftApiService.getContainer(planName).then(response => {
        dispatch(this.receivePlan(planName,
                                  normalize(response, arrayOf(planFileSchema)).entities.planFiles));
      }).catch(error => {
        console.error('Error retrieving plan PlansActions.fetchPlan', error); //eslint-disable-line no-console
        let errorHandler = new SwiftApiErrorHandler(error);
        errorHandler.errors.forEach((error) => {
          dispatch(NotificationActions.notify(error));
        });
      });
    };
  },

  updatePlanPending(planName) {
    return {
      type: PlansConstants.UPDATE_PLAN_PENDING,
      payload: planName
    };
  },

  updatePlanSuccess(planName) {
    return {
      type: PlansConstants.UPDATE_PLAN_SUCCESS,
      payload: planName
    };
  },

  updatePlanFailed(planName) {
    return {
      type: PlansConstants.UPDATE_PLAN_FAILED,
      payload: planName
    };
  },

  updatePlan(planName, planFiles) {
    return dispatch => {
      dispatch(this.updatePlanPending(planName));
      this._uploadFilesToContainer(planName, fromJS(planFiles), dispatch).then(() => {
        dispatch(this.updatePlanSuccess(planName));
        browserHistory.push('/plans/list');
        dispatch(NotificationActions.notify({
          title: 'Plan Updated',
          message: `The plan ${planName} was successfully updated.`,
          type: 'success'
        }));
        dispatch(this.fetchPlans());
      }).catch((errors) => {
        errors.forEach((error) => {
          dispatch(NotificationActions.notify(error));
        });
        dispatch(this.updatePlanFailed(planName));
      });
    };
  },

  updatePlanFromTarball(planName, file) {
    return (dispatch) => {
      dispatch(this.updatePlanPending(planName));
      SwiftApiService.uploadTarball(planName, file).then((response) => {
        dispatch(this.updatePlanSuccess(planName));
        browserHistory.push('/plans/list');
        dispatch(NotificationActions.notify({
          title: 'Plan Updated',
          message: `The plan ${planName} was successfully updated.`,
          type: 'success'
        }));
        dispatch(this.fetchPlans());
      }).catch((error) => {
        console.error('Error in PlansActions.updatePlanFromTarball', error); //eslint-disable-line no-console
        let errorHandler = new SwiftApiErrorHandler(error);
        errorHandler.errors.forEach((error) => {
          dispatch(NotificationActions.notify(error));
        });
        dispatch(this.updatePlanFailed(planName));
      });
    };
  },

  cancelCreatePlan () {
    return {
      type: PlansConstants.CANCEL_CREATE_PLAN
    };
  },

  createPlanPending() {
    return {
      type: PlansConstants.CREATE_PLAN_PENDING
    };
  },

  createPlanSuccess() {
    return {
      type: PlansConstants.CREATE_PLAN_SUCCESS
    };
  },

  createPlanFailed(errors) {
    return {
      type: PlansConstants.CREATE_PLAN_FAILED,
      payload: errors
    };
  },

  /*
   * Uploads a number of files to a container.
   * Returns a promise which gets resolved when all files are uploaded
   * or rejected if >= 1 objects fail.
   * @container: String
   * @files: Immutable Map
   */
  _uploadFilesToContainer(container, files, dispatch) {
    let uploadedFiles = 0;
    return when.promise((resolve, reject) => {
      files.forEach((value, key) => {
        SwiftApiService.createObject(container, key, value.get('contents')).then((response) => {
          // On success increase nr of uploaded files.
          // If this is the last file in the map, resolve the promise.
          if(uploadedFiles === files.size - 1) {
            resolve();
          }
          uploadedFiles += 1;
        }).catch((error) => {
          // Reject the promise on the first file that fails.
          console.error('Error in PlansActions.createPlan', error); //eslint-disable-line no-console
          let errorHandler = new SwiftApiErrorHandler(error);
          reject(errorHandler.errors);
        });
      });
    });
  },

  createPlan(planName, planFiles) {
    return (dispatch, getState) => {
      dispatch(this.createPlanPending());
      SwiftApiService.createContainer(planName).then((response) => {
        // Upload all files to container first.
        this._uploadFilesToContainer(planName, fromJS(planFiles), dispatch).then(() => {

          // Once all files are uploaded, start plan creation workflow.
          MistralApiService.runWorkflow(
            MistralConstants.PLAN_CREATE,
            { container: planName }
          ).then((response) => {
            if(response.state === 'ERROR') {
              console.error('Error in PlansActions.createPlan', response); //eslint-disable-line no-console
              dispatch(NotificationActions.notify({
                title: 'Error',
                message: response.state_info
              }));
              dispatch(this.createPlanFailed());
            }
          }).catch((error) => {
            console.error('Error in PlansActions.createPlan', error); //eslint-disable-line no-console
            let errorHandler = new MistralApiErrorHandler(error);
            errorHandler.errors.forEach((error) => {
              dispatch(NotificationActions.notify(error));
            });
            dispatch(this.createPlanFailed());
          });

        }).catch((errors) => {
          // If the file upload fails, just notify the user
          errors.forEach((error) => {
            dispatch(NotificationActions.notify(error));
          });
          dispatch(this.createPlanFailed());
        });

      }).catch((error) => {
        console.error('Error in PlansActions.createPlan', error); //eslint-disable-line no-console
        let errorHandler = new SwiftApiErrorHandler(error);
        errorHandler.errors.forEach((error) => {
          dispatch(NotificationActions.notify(error));
        });
        dispatch(this.createPlanFailed());
      });
    };
  },

  createPlanFinished(payload) {
    return (dispatch) => {
      if(payload.status === 'SUCCESS') {
        dispatch(this.createPlanSuccess());
        dispatch(NotificationActions.notify({
          type: 'success',
          title: 'Plan was created',
          message: `The plan ${payload.execution.input.container} was successfully created`
        }));
        dispatch(this.fetchPlans());
        browserHistory.push('/plans/list');
      }
      else {
        dispatch(this.createPlanFailed());
        dispatch(NotificationActions.notify({
          type: 'error',
          title: 'Plan creation error',
          message: payload.message
        }));
      }
    };
  },

  createPlanFromTarball(planName, file) {
    return (dispatch) => {
      dispatch(this.createPlanPending());
      SwiftApiService.uploadTarball(planName, file).then((response) => {
        MistralApiService.runWorkflow(
          MistralConstants.PLAN_CREATE,
          { container: planName }
        ).then((response) => {
          if(response.state === 'ERROR') {
            console.error('Error in PlansActions.createPlanFromTarball', response); //eslint-disable-line no-console
            dispatch(this.createPlanFailed([{ title: 'Error', message: response.state_info }]));
          }
          else {
            dispatch(this.pollForPlanCreationWorkflow(planName, response.id));
          }
        }).catch((error) => {
          let errorHandler = new MistralApiErrorHandler(error);
          dispatch(this.createPlanFailed(errorHandler.errors));
        });
      }).catch((error) => {
        console.error('Error in PlansActions.createPlanFromTarball', error); //eslint-disable-line no-console
        let errorHandler = new SwiftApiErrorHandler(error);
        dispatch(this.createPlanFailed(errorHandler.errors));
      });
    };
  },

  pollForPlanCreationWorkflow(planName, workflowExecutionId) {
    return (dispatch, getState) => {
      MistralApiService.getWorkflowExecution(workflowExecutionId)
      .then((response) => {
        if(response.state === 'RUNNING') {
          setTimeout(() => {
            dispatch(this.pollForPlanCreationWorkflow(planName, workflowExecutionId));
          }, 5000);
        }
        else if(response.state === 'ERROR') {
          dispatch(this.createPlanFailed([{ title: 'Error', message: response.state_info }]));
        }
        else {
          dispatch(this.createPlanSuccess(planName));
          dispatch(this.fetchPlans());
          dispatch(NotificationActions.notify({
            type: 'success',
            title: 'Plan was created',
            message: `The plan ${planName} was successfully created`
          }));
          browserHistory.push('/plans/list');
        }
      }).catch((error) => {
        let errorHandler = new MistralApiErrorHandler(error);
        dispatch(this.createPlanFailed(errorHandler.errors));
        dispatch(this.finishOperation());
      });
    };
  },

  deletePlanPending(planName) {
    return {
      type: PlansConstants.DELETE_PLAN_PENDING,
      payload: planName
    };
  },

  deletePlanSuccess(planName) {
    return {
      type: PlansConstants.DELETE_PLAN_SUCCESS,
      payload: planName
    };
  },

  deletePlanFailed(planName) {
    return {
      type: PlansConstants.DELETE_PLAN_FAILED,
      payload: planName
    };
  },

  deletePlan(planName) {
    return dispatch => {
      dispatch(this.deletePlanPending(planName));
      browserHistory.push('/plans/list');<commit_msg>Use Zaqar messages on plan creation (not polling)<commit_after>import { fromJS } from 'immutable';
import { normalize, arrayOf } from 'normalizr';
import when from 'when';

import CurrentPlanActions from '../actions/CurrentPlanActions';
import { browserHistory } from 'react-router';
import MistralApiService from '../services/MistralApiService';
import MistralApiErrorHandler from '../services/MistralApiErrorHandler';
import NotificationActions from '../actions/NotificationActions';
import PlansConstants from '../constants/PlansConstants';
import { planFileSchema } from '../normalizrSchemas/plans';
import StackActions from '../actions/StacksActions';
import SwiftApiErrorHandler from '../services/SwiftApiErrorHandler';
import SwiftApiService from '../services/SwiftApiService';
import MistralConstants from '../constants/MistralConstants';

export default {
  requestPlans() {
    return {
      type: PlansConstants.REQUEST_PLANS
    };
  },

  receivePlans(plans) {
    return {
      type: PlansConstants.RECEIVE_PLANS,
      payload: plans
    };
  },

  fetchPlans() {
    return dispatch => {
      dispatch(this.requestPlans());
      MistralApiService.runAction(MistralConstants.PLAN_LIST).then((response) => {
        let plans = JSON.parse(response.output).result || [];
        dispatch(this.receivePlans(plans));
        dispatch(CurrentPlanActions.detectPlan(plans));
      }).catch((error) => {
        let errorHandler = new MistralApiErrorHandler(error);
        errorHandler.errors.forEach((error) => {
          dispatch(NotificationActions.notify(error));
        });
      });
    };
  },

  requestPlan() {
    return {
      type: PlansConstants.REQUEST_PLAN
    };
  },

  receivePlan(planName, planFiles) {
    return {
      type: PlansConstants.RECEIVE_PLAN,
      payload: {
        planName: planName,
        planFiles: planFiles
      }
    };
  },

  fetchPlan(planName) {
    return dispatch => {
      dispatch(this.requestPlan());
      SwiftApiService.getContainer(planName).then(response => {
        dispatch(this.receivePlan(planName,
                                  normalize(response, arrayOf(planFileSchema)).entities.planFiles));
      }).catch(error => {
        console.error('Error retrieving plan PlansActions.fetchPlan', error); //eslint-disable-line no-console
        let errorHandler = new SwiftApiErrorHandler(error);
        errorHandler.errors.forEach((error) => {
          dispatch(NotificationActions.notify(error));
        });
      });
    };
  },

  updatePlanPending(planName) {
    return {
      type: PlansConstants.UPDATE_PLAN_PENDING,
      payload: planName
    };
  },

  updatePlanSuccess(planName) {
    return {
      type: PlansConstants.UPDATE_PLAN_SUCCESS,
      payload: planName
    };
  },

  updatePlanFailed(planName) {
    return {
      type: PlansConstants.UPDATE_PLAN_FAILED,
      payload: planName
    };
  },

  updatePlan(planName, planFiles) {
    return dispatch => {
      dispatch(this.updatePlanPending(planName));
      this._uploadFilesToContainer(planName, fromJS(planFiles), dispatch).then(() => {
        dispatch(this.updatePlanSuccess(planName));
        browserHistory.push('/plans/list');
        dispatch(NotificationActions.notify({
          title: 'Plan Updated',
          message: `The plan ${planName} was successfully updated.`,
          type: 'success'
        }));
        dispatch(this.fetchPlans());
      }).catch((errors) => {
        errors.forEach((error) => {
          dispatch(NotificationActions.notify(error));
        });
        dispatch(this.updatePlanFailed(planName));
      });
    };
  },

  updatePlanFromTarball(planName, file) {
    return (dispatch) => {
      dispatch(this.updatePlanPending(planName));
      SwiftApiService.uploadTarball(planName, file).then((response) => {
        dispatch(this.updatePlanSuccess(planName));
        browserHistory.push('/plans/list');
        dispatch(NotificationActions.notify({
          title: 'Plan Updated',
          message: `The plan ${planName} was successfully updated.`,
          type: 'success'
        }));
        dispatch(this.fetchPlans());
      }).catch((error) => {
        console.error('Error in PlansActions.updatePlanFromTarball', error); //eslint-disable-line no-console
        let errorHandler = new SwiftApiErrorHandler(error);
        errorHandler.errors.forEach((error) => {
          dispatch(NotificationActions.notify(error));
        });
        dispatch(this.updatePlanFailed(planName));
      });
    };
  },

  cancelCreatePlan () {
    return {
      type: PlansConstants.CANCEL_CREATE_PLAN
    };
  },

  createPlanPending() {
    return {
      type: PlansConstants.CREATE_PLAN_PENDING
    };
  },

  createPlanSuccess() {
    return {
      type: PlansConstants.CREATE_PLAN_SUCCESS
    };
  },

  createPlanFailed(errors) {
    return {
      type: PlansConstants.CREATE_PLAN_FAILED,
      payload: errors
    };
  },

  /*
   * Uploads a number of files to a container.
   * Returns a promise which gets resolved when all files are uploaded
   * or rejected if >= 1 objects fail.
   * @container: String
   * @files: Immutable Map
   */
  _uploadFilesToContainer(container, files, dispatch) {
    let uploadedFiles = 0;
    return when.promise((resolve, reject) => {
      files.forEach((value, key) => {
        SwiftApiService.createObject(container, key, value.get('contents')).then((response) => {
          // On success increase nr of uploaded files.
          // If this is the last file in the map, resolve the promise.
          if(uploadedFiles === files.size - 1) {
            resolve();
          }
          uploadedFiles += 1;
        }).catch((error) => {
          // Reject the promise on the first file that fails.
          console.error('Error in PlansActions.createPlan', error); //eslint-disable-line no-console
          let errorHandler = new SwiftApiErrorHandler(error);
          reject(errorHandler.errors);
        });
      });
    });
  },

  createPlan(planName, planFiles) {
    return (dispatch, getState) => {
      dispatch(this.createPlanPending());
      SwiftApiService.createContainer(planName).then((response) => {
        // Upload all files to container first.
        this._uploadFilesToContainer(planName, fromJS(planFiles), dispatch).then(() => {

          // Once all files are uploaded, start plan creation workflow.
          MistralApiService.runWorkflow(
            MistralConstants.PLAN_CREATE,
            { container: planName }
          ).then((response) => {
            if(response.state === 'ERROR') {
              console.error('Error in PlansActions.createPlan', response); //eslint-disable-line no-console
              dispatch(NotificationActions.notify({
                title: 'Error',
                message: response.state_info
              }));
              dispatch(this.createPlanFailed());
            }
          }).catch((error) => {
            console.error('Error in PlansActions.createPlan', error); //eslint-disable-line no-console
            let errorHandler = new MistralApiErrorHandler(error);
            errorHandler.errors.forEach((error) => {
              dispatch(NotificationActions.notify(error));
            });
            dispatch(this.createPlanFailed());
          });

        }).catch((errors) => {
          // If the file upload fails, just notify the user
          errors.forEach((error) => {
            dispatch(NotificationActions.notify(error));
          });
          dispatch(this.createPlanFailed());
        });

      }).catch((error) => {
        console.error('Error in PlansActions.createPlan', error); //eslint-disable-line no-console
        let errorHandler = new SwiftApiErrorHandler(error);
        errorHandler.errors.forEach((error) => {
          dispatch(NotificationActions.notify(error));
        });
        dispatch(this.createPlanFailed());
      });
    };
  },

  createPlanFinished(payload) {
    return (dispatch) => {
      if(payload.status === 'SUCCESS') {
        dispatch(this.createPlanSuccess());
        dispatch(NotificationActions.notify({
          type: 'success',
          title: 'Plan was created',
          message: `The plan ${payload.execution.input.container} was successfully created`
        }));
        dispatch(this.fetchPlans());
        browserHistory.push('/plans/list');
      }
      else {
        dispatch(this.createPlanFailed([{ title: 'Error', message: payload.message }]));
      }
    };
  },

  createPlanFromTarball(planName, file) {
    return (dispatch) => {
      dispatch(this.createPlanPending());
      SwiftApiService.uploadTarball(planName, file).then((response) => {
        MistralApiService.runWorkflow(
          MistralConstants.PLAN_CREATE,
          { container: planName }
        ).then((response) => {
          if(response.state === 'ERROR') {
            console.error('Error in PlansActions.createPlanFromTarball', response); //eslint-disable-line no-console
            dispatch(this.createPlanFailed([{ title: 'Error', message: response.state_info }]));
          }
        }).catch((error) => {
          let errorHandler = new MistralApiErrorHandler(error);
          dispatch(this.createPlanFailed(errorHandler.errors));
        });
      }).catch((error) => {
        console.error('Error in PlansActions.createPlanFromTarball', error); //eslint-disable-line no-console
        let errorHandler = new SwiftApiErrorHandler(error);
        dispatch(this.createPlanFailed(errorHandler.errors));
      });
    };
  },

  deletePlanPending(planName) {
    return {
      type: PlansConstants.DELETE_PLAN_PENDING,
      payload: planName
    };
  },

  deletePlanSuccess(planName) {
    return {
      type: PlansConstants.DELETE_PLAN_SUCCESS,
      payload: planName
    };
  },

  deletePlanFailed(planName) {
    return {
      type: PlansConstants.DELETE_PLAN_FAILED,
      payload: planName
    };
  },

  deletePlan(planName) {
    return dispatch => {
      dispatch(this.deletePlanPending(planName));
      browserHistory.push('/plans/list');
{'fraction_non_alphanumeric': 0.12717948717948718, 'fraction_numerical': 0.0005594405594405594, 'mean_word_length': 2.615540198887578, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 83, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2951312', 'n_tokens_mistral': 5861, 'n_tokens_neox': 5748, 'n_words': 1044}