<commit_before>import React, {Component, PropTypes} from 'react';
import { connect } from 'react-redux';
import SelectFormGroup from '../common/formItems/formGroups/SelectFormGroup';
import TextFormGroup from '../common/formItems/formGroups/TextFormGroup';
import MultiInputFormGroup from '../common/formItems/formGroups/MultiInputFormGroup';
import CheckBoxFormGroup from '../common/formItems/formGroups/CheckBoxFormGroup';
import { modifyField } from '../../actions/form';
import {SaveAction} from '../../actions/api/deploy';
import { FIELDS, ARTIFACT_FIELDS, DOCKER_PORT_MAPPING_FIELDS, DOCKER_VOLUME_FIELDS, INDEXED_FIELDS, INDEXED_ARTIFACT_FIELDS,
  INDEXED_DOCKER_PORT_MAPPING_FIELDS, INDEXED_DOCKER_VOLUME_FIELDS, INDEXED_ALL_FIELDS, INDEXED_CUSTOM_EXECUTOR_FIELDS,
  INDEXED_DEFAULT_EXECUTOR_FIELDS, INDEXED_DOCKER_CONTAINER_FIELDS, INDEXED_LOAD_BALANCER_FIELDS, INDEXED_HEALTH_CHECKER_FIELDS,
  INDEXED_ALL_ARTIFACT_FIELDS, INDEXED_EMBEDDED_ARTIFACT_FIELDS, INDEXED_EXTERNAL_ARTIFACT_FIELDS, INDEXED_S3_ARTIFACT_FIELDS } from './fields';


const FORM_ID = 'newDeployForm';

const DEFAULT_EXECUTOR_TYPE = 'default';
const CUSTOM_EXECUTOR_TYPE = 'custom';
const ARTIFACT_SHAPE = PropTypes.shape({
  name: PropTypes.string,
  type: PropTypes.oneOf(['embedded', 'external', 's3']).isRequired,
  filename: PropTypes.string,
  md5Sum: PropTypes.string,
  content: PropTypes.string,
  url: PropTypes.string,
  filesize: PropTypes.string,
  s3Bucket: PropTypes.string,
  s3ObjectKey: PropTypes.string
});

class NewDeployForm extends Component {

  static propTypes = {
    form: PropTypes.shape({
      arguments: PropTypes.arrayOf(PropTypes.string),
      uris: PropTypes.arrayOf(PropTypes.string),
      embeddedArtifacts: PropTypes.arrayOf(ARTIFACT_SHAPE),
      externalArtifacts: PropTypes.arrayOf(ARTIFACT_SHAPE),
      s3Artifacts: PropTypes.arrayOf(ARTIFACT_SHAPE),
      cmd: PropTypes.string,
      extraCmdLineArgs: PropTypes.arrayOf(PropTypes.string),
      user: PropTypes.string,
      sigKillProcessesAfterMillis: PropTypes.string,
      successfulExitCodes: PropTypes.arrayOf(PropTypes.string),
      maxTaskThreads: PropTypes.string,
      loggingTag: PropTypes.string,
      loggingExtraFields: PropTypes.arrayOf(PropTypes.string),
      preserveTaskSandboxAfterFinish: PropTypes.bool,
      skipLogrotateAndCompress: PropTypes.bool,
      loggingS3Bucket: PropTypes.string,
      maxOpenFiles: PropTypes.string,
      runningSentinel: PropTypes.string,
      portMappings: PropTypes.arrayOf(PropTypes.shape({
        containerPortType: PropTypes.string,
        containerPort: PropTypes.string,
        hostPortType: PropTypes.string,
        hostPort: PropTypes.string,
        protocol: PropTypes.string
      })),
      volumes: PropTypes.arrayOf(PropTypes.shape({
        containerPath: PropTypes.string,
        hostPath: PropTypes.string,
        mode: PropTypes.string
      })),
      image: PropTypes.string,
      privileged: PropTypes.bool,
      forcePullImage: PropTypes.bool,
      parameters: PropTypes.arrayOf(PropTypes.string),
      id: PropTypes.string,
      command: PropTypes.string,
      type: PropTypes.string,
      cpus: PropTypes.string,
      memoryMb: PropTypes.string,
      numPorts: PropTypes.string,
      env: PropTypes.arrayOf(PropTypes.string),
      healthcheckUri: PropTypes.string,
      healthcheckIntervalSeconds: PropTypes.string,
      healthcheckTimeoutSeconds: PropTypes.string,
      healthcheckPortIndex: PropTypes.string,
      healthcheckMaxTotalTimeoutSeconds: PropTypes.string,
      deployHealthTimeoutSeconds: PropTypes.string,
      skipHealthchecksOnDeploy: PropTypes.bool,
      considerHealthyAfterRunningForSeconds: PropTypes.string,
      serviceBasePath: PropTypes.string,
      loadBalancerGroups: PropTypes.arrayOf(PropTypes.string),
      loadBalancerOptions: PropTypes.arrayOf(PropTypes.string),
      loadBalancerPortIndex: PropTypes.string,
      unpauseOnSuccessfulDeploy: PropTypes.bool
    }).isRequired,
    request: PropTypes.shape({
      state: PropTypes.string.isRequired,
      request: PropTypes.shape({
        requestType: PropTypes.string.isRequired,
        id: PropTypes.string.isRequired,
        loadBalanced: PropTypes.bool
      }).isRequired
    }).isRequired,
    saveApiCall: PropTypes.shape({
      error: PropTypes.shape({
        message: PropTypes.string
      }),
      data: PropTypes.shape({
        message: PropTypes.string,
        activeDeploy: PropTypes.shape({
          id: PropTypes.string,
          requestId: PropTypes.string
        })
      })
    }),
    update: PropTypes.func.isRequired,
    save: PropTypes.func.isRequired
  };

  updateField(fieldId, newValue) {
    this.props.update(FORM_ID, fieldId, newValue);
  }

  getValueOrDefault(fieldId) {
    return this.props.form[fieldId] || INDEXED_FIELDS[fieldId].default;
  }

  isRequestDaemon() {
    return ['SERVICE', 'WORKER'].indexOf(this.props.request.request.requestType) !== -1;
  }

  // Returns true unless the object is falsey or an empty array.
  hasValue(value) {
    if (!value) {
      return false;
    }
    if (Array.isArray(value) && _.isEmpty(value)) {
      return false;
    }
    return true;
  }

  validateValue(value, type, arrayType) {
    if (!value) {
      return true;
    }
    if (type === 'number') {
      const number = parseInt(value, 10);
      return number === 0 || number; // NaN is invalid
    } else if (type === 'map') {
      for (const element of value) {
        if (element.split('=').length !== 2) {
          return false;
        }
      }
    } else if (type === 'array') {
      for (const element of value) {
        if (!this.validateValue(element, arrayType)) {
          return false;
        }
      }
    } else if (type === 'mapPair') {
      if (value.split('=').length !== 2) {
        return false;
      }
    }
    return true;
  }

  errorsInArrayField(field, valueGetter) {
    const errorIndices = [];
    const arrayFieldValue = valueGetter(field.id);
    if (field.required && _.isEmpty(arrayFieldValue)) {
      return [0];
    }
    const type = field.type === ('map' && 'mapPair') || field.arrayType;
    for (const idx in arrayFieldValue) {
      if (!this.validateValue(arrayFieldValue[idx], type)) {
        errorIndices.push(parseInt(idx, 10));
      }
    }
    return errorIndices;
  }

  validateField(field, valueGetter) {
    const type = field.type;
    if (type === 'object') {
      for (const subField of field.values) {
        if (!this.validateField(subField)) {
          return false;
        }
      }
      return true;
    }
    const value = valueGetter(field.id);
    if (field.required && !this.hasValue(value)) {
      return false;
    }
    return this.validateValue(value, type, field.arrayType);
  }

  formFieldFeedback(field, value) {
    if (!field.required && !value) {
      return null;
    }
    if (field.required && !value) {
      return 'ERROR';<commit_msg>Fix bug that caused maps to not check for '='<commit_after>import React, {Component, PropTypes} from 'react';
import { connect } from 'react-redux';
import SelectFormGroup from '../common/formItems/formGroups/SelectFormGroup';
import TextFormGroup from '../common/formItems/formGroups/TextFormGroup';
import MultiInputFormGroup from '../common/formItems/formGroups/MultiInputFormGroup';
import CheckBoxFormGroup from '../common/formItems/formGroups/CheckBoxFormGroup';
import { modifyField } from '../../actions/form';
import {SaveAction} from '../../actions/api/deploy';
import { FIELDS, ARTIFACT_FIELDS, DOCKER_PORT_MAPPING_FIELDS, DOCKER_VOLUME_FIELDS, INDEXED_FIELDS, INDEXED_ARTIFACT_FIELDS,
  INDEXED_DOCKER_PORT_MAPPING_FIELDS, INDEXED_DOCKER_VOLUME_FIELDS, INDEXED_ALL_FIELDS, INDEXED_CUSTOM_EXECUTOR_FIELDS,
  INDEXED_DEFAULT_EXECUTOR_FIELDS, INDEXED_DOCKER_CONTAINER_FIELDS, INDEXED_LOAD_BALANCER_FIELDS, INDEXED_HEALTH_CHECKER_FIELDS,
  INDEXED_ALL_ARTIFACT_FIELDS, INDEXED_EMBEDDED_ARTIFACT_FIELDS, INDEXED_EXTERNAL_ARTIFACT_FIELDS, INDEXED_S3_ARTIFACT_FIELDS } from './fields';


const FORM_ID = 'newDeployForm';

const DEFAULT_EXECUTOR_TYPE = 'default';
const CUSTOM_EXECUTOR_TYPE = 'custom';
const ARTIFACT_SHAPE = PropTypes.shape({
  name: PropTypes.string,
  type: PropTypes.oneOf(['embedded', 'external', 's3']).isRequired,
  filename: PropTypes.string,
  md5Sum: PropTypes.string,
  content: PropTypes.string,
  url: PropTypes.string,
  filesize: PropTypes.string,
  s3Bucket: PropTypes.string,
  s3ObjectKey: PropTypes.string
});

class NewDeployForm extends Component {

  static propTypes = {
    form: PropTypes.shape({
      arguments: PropTypes.arrayOf(PropTypes.string),
      uris: PropTypes.arrayOf(PropTypes.string),
      embeddedArtifacts: PropTypes.arrayOf(ARTIFACT_SHAPE),
      externalArtifacts: PropTypes.arrayOf(ARTIFACT_SHAPE),
      s3Artifacts: PropTypes.arrayOf(ARTIFACT_SHAPE),
      cmd: PropTypes.string,
      extraCmdLineArgs: PropTypes.arrayOf(PropTypes.string),
      user: PropTypes.string,
      sigKillProcessesAfterMillis: PropTypes.string,
      successfulExitCodes: PropTypes.arrayOf(PropTypes.string),
      maxTaskThreads: PropTypes.string,
      loggingTag: PropTypes.string,
      loggingExtraFields: PropTypes.arrayOf(PropTypes.string),
      preserveTaskSandboxAfterFinish: PropTypes.bool,
      skipLogrotateAndCompress: PropTypes.bool,
      loggingS3Bucket: PropTypes.string,
      maxOpenFiles: PropTypes.string,
      runningSentinel: PropTypes.string,
      portMappings: PropTypes.arrayOf(PropTypes.shape({
        containerPortType: PropTypes.string,
        containerPort: PropTypes.string,
        hostPortType: PropTypes.string,
        hostPort: PropTypes.string,
        protocol: PropTypes.string
      })),
      volumes: PropTypes.arrayOf(PropTypes.shape({
        containerPath: PropTypes.string,
        hostPath: PropTypes.string,
        mode: PropTypes.string
      })),
      image: PropTypes.string,
      privileged: PropTypes.bool,
      forcePullImage: PropTypes.bool,
      parameters: PropTypes.arrayOf(PropTypes.string),
      id: PropTypes.string,
      command: PropTypes.string,
      type: PropTypes.string,
      cpus: PropTypes.string,
      memoryMb: PropTypes.string,
      numPorts: PropTypes.string,
      env: PropTypes.arrayOf(PropTypes.string),
      healthcheckUri: PropTypes.string,
      healthcheckIntervalSeconds: PropTypes.string,
      healthcheckTimeoutSeconds: PropTypes.string,
      healthcheckPortIndex: PropTypes.string,
      healthcheckMaxTotalTimeoutSeconds: PropTypes.string,
      deployHealthTimeoutSeconds: PropTypes.string,
      skipHealthchecksOnDeploy: PropTypes.bool,
      considerHealthyAfterRunningForSeconds: PropTypes.string,
      serviceBasePath: PropTypes.string,
      loadBalancerGroups: PropTypes.arrayOf(PropTypes.string),
      loadBalancerOptions: PropTypes.arrayOf(PropTypes.string),
      loadBalancerPortIndex: PropTypes.string,
      unpauseOnSuccessfulDeploy: PropTypes.bool
    }).isRequired,
    request: PropTypes.shape({
      state: PropTypes.string.isRequired,
      request: PropTypes.shape({
        requestType: PropTypes.string.isRequired,
        id: PropTypes.string.isRequired,
        loadBalanced: PropTypes.bool
      }).isRequired
    }).isRequired,
    saveApiCall: PropTypes.shape({
      error: PropTypes.shape({
        message: PropTypes.string
      }),
      data: PropTypes.shape({
        message: PropTypes.string,
        activeDeploy: PropTypes.shape({
          id: PropTypes.string,
          requestId: PropTypes.string
        })
      })
    }),
    update: PropTypes.func.isRequired,
    save: PropTypes.func.isRequired
  };

  updateField(fieldId, newValue) {
    this.props.update(FORM_ID, fieldId, newValue);
  }

  getValueOrDefault(fieldId) {
    return this.props.form[fieldId] || INDEXED_FIELDS[fieldId].default;
  }

  isRequestDaemon() {
    return ['SERVICE', 'WORKER'].indexOf(this.props.request.request.requestType) !== -1;
  }

  // Returns true unless the object is falsey or an empty array.
  hasValue(value) {
    if (!value) {
      return false;
    }
    if (Array.isArray(value) && _.isEmpty(value)) {
      return false;
    }
    return true;
  }

  validateValue(value, type, arrayType) {
    if (!value) {
      return true;
    }
    if (type === 'number') {
      const number = parseInt(value, 10);
      return number === 0 || number; // NaN is invalid
    } else if (type === 'map') {
      for (const element of value) {
        if (element.split('=').length !== 2) {
          return false;
        }
      }
    } else if (type === 'array') {
      for (const element of value) {
        if (!this.validateValue(element, arrayType)) {
          return false;
        }
      }
    } else if (type === 'mapPair') {
      if (value.split('=').length !== 2) {
        return false;
      }
    }
    return true;
  }

  errorsInArrayField(field, valueGetter) {
    const errorIndices = [];
    const arrayFieldValue = valueGetter(field.id);
    if (field.required && _.isEmpty(arrayFieldValue)) {
      return [0];
    }
    const type = field.type === 'map' && 'mapPair' || field.arrayType;
    for (const idx in arrayFieldValue) {
      if (!this.validateValue(arrayFieldValue[idx], type)) {
        errorIndices.push(parseInt(idx, 10));
      }
    }
    return errorIndices;
  }

  validateField(field, valueGetter) {
    const type = field.type;
    if (type === 'object') {
      for (const subField of field.values) {
        if (!this.validateField(subField)) {
          return false;
        }
      }
      return true;
    }
    const value = valueGetter(field.id);
    if (field.required && !this.hasValue(value)) {
      return false;
    }
    return this.validateValue(value, type, field.arrayType);
  }

  formFieldFeedback(field, value) {
    if (!field.required && !value) {
      return null;
    }
    if (field.required && !value) {
      return 'ERROR';
{'fraction_non_alphanumeric': 0.11153349591163392, 'fraction_numerical': 0.0022952230669918234, 'mean_word_length': 3.338207840696951, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4151795', 'n_tokens_mistral': 4233, 'n_tokens_neox': 4338, 'n_words': 816}