From 0a63b42b62d7e669eb2b9390fe4b58a87f89682c Mon Sep 17 00:00:00 2001
From: Jaap Jansma <jaap.jansma@civicoop.org>
Date: Tue, 2 Jul 2019 08:12:51 +0200
Subject: [PATCH] better error handling

---
 .../Selector/DataProcessorContactSearch.php   |  2 +-
 CRM/Dataprocessor/BAO/DataProcessor.php       | 22 ++++++++---
 CRM/Dataprocessor/Form/AggregateField.php     |  2 +-
 CRM/Dataprocessor/Form/DataProcessor.php      |  2 +-
 CRM/Dataprocessor/Form/Field.php              |  2 +-
 .../Form/Output/AbstractUIOutputForm.php      |  2 +-
 CRM/Dataprocessor/Form/Source.php             |  2 +-
 .../Form/AbstractSearch.php                   | 20 ++++++----
 .../DataSpecification/DataSpecification.php   |  5 ++-
 .../Exception/DataSourceNotFoundException.php | 11 ++++++
 .../Exception/FieldNotFoundException.php      | 11 ++++++
 .../InvalidConfigurationException.php         | 11 ++++++
 .../CaseRolesFieldOutputHandler.php           | 12 ++++++
 .../ContactLinkFieldOutputHandler.php         | 28 ++++++++++++++
 .../EventRepeatingInfoFieldOutputHandler.php  | 12 ++++++
 .../FileFieldOutputHandler.php                | 12 ++++++
 .../GroupsOfContactFieldOutputHandler.php     | 12 ++++++
 .../RawFieldOutputHandler.php                 | 12 ++++++
 .../FilterHandler/CaseRoleFilter.php          | 22 ++++++++---
 .../FilterHandler/ContactInGroupFilter.php    | 20 +++++++---
 .../FilterHandler/SimpleSqlFilter.php         | 22 ++++++++---
 Civi/DataProcessor/Output/Api.php             | 38 +++++++++++++++++++
 22 files changed, 248 insertions(+), 34 deletions(-)
 create mode 100644 Civi/DataProcessor/Exception/DataSourceNotFoundException.php
 create mode 100644 Civi/DataProcessor/Exception/FieldNotFoundException.php
 create mode 100644 Civi/DataProcessor/Exception/InvalidConfigurationException.php

diff --git a/CRM/Contact/Selector/DataProcessorContactSearch.php b/CRM/Contact/Selector/DataProcessorContactSearch.php
index 55c29f7d..23226896 100644
--- a/CRM/Contact/Selector/DataProcessorContactSearch.php
+++ b/CRM/Contact/Selector/DataProcessorContactSearch.php
@@ -130,7 +130,7 @@ class CRM_Contact_Selector_DataProcessorContactSearch {
       }
 
       $this->dataProcessor = civicrm_api3('DataProcessor', 'getsingle', array('id' => $dao->data_processor_id));
-      $this->dataProcessorClass = \CRM_Dataprocessor_BAO_DataProcessor::dataProcessorToClass($this->dataProcessor);
+      $this->dataProcessorClass = \CRM_Dataprocessor_BAO_DataProcessor::dataProcessorToClass($this->dataProcessor, true);
       $this->dataProcessorId = $dao->data_processor_id;
 
       $this->dataProcessorOutput = civicrm_api3('DataProcessorOutput', 'getsingle', array('id' => $dao->output_id));
diff --git a/CRM/Dataprocessor/BAO/DataProcessor.php b/CRM/Dataprocessor/BAO/DataProcessor.php
index 829243b2..cdda1c46 100644
--- a/CRM/Dataprocessor/BAO/DataProcessor.php
+++ b/CRM/Dataprocessor/BAO/DataProcessor.php
@@ -60,7 +60,11 @@ class CRM_Dataprocessor_BAO_DataProcessor extends CRM_Dataprocessor_DAO_DataProc
     $dataProcessorClass = $factory->getDataProcessorTypeByName($dataProcessor['type']);
     $sources = civicrm_api3('DataProcessorSource', 'get', array('data_processor_id' => $dataProcessor['id'], 'options' => array('limit' => 0)));
     foreach($sources['values'] as $sourceDao) {
-      CRM_Dataprocessor_BAO_DataProcessorSource::addSourceToDataProcessor($sourceDao, $dataProcessorClass);
+      try {
+        CRM_Dataprocessor_BAO_DataProcessorSource::addSourceToDataProcessor($sourceDao, $dataProcessorClass);
+      } catch (\Exception $e) {
+        CRM_Core_Session::setStatus($e->getMessage(), E::ts("Could not add data source"), 'error');
+      }
     }
 
     $aggregationFields = array();
@@ -79,8 +83,12 @@ class CRM_Dataprocessor_BAO_DataProcessor extends CRM_Dataprocessor_DAO_DataProc
       $filterHandler = $factory->getFilterByName($filter['type']);
       if ($filterHandler) {
         $filterHandler->setDataProcessor($dataProcessorClass);
-        $filterHandler->initialize($filter['name'], $filter['title'], $filter['is_required'], $filter['configuration']);
-        $dataProcessorClass->addFilterHandler($filterHandler);
+        try {
+          $filterHandler->initialize($filter['name'], $filter['title'], $filter['is_required'], $filter['configuration']);
+          $dataProcessorClass->addFilterHandler($filterHandler);
+        } catch (\Exception $e) {
+          CRM_Core_Session::setStatus($e->getMessage(), E::ts("Invalid filter"), 'error');
+        }
       }
     }
 
@@ -89,8 +97,12 @@ class CRM_Dataprocessor_BAO_DataProcessor extends CRM_Dataprocessor_DAO_DataProc
       $outputHandler = $factory->getOutputHandlerByName($field['type']);
       if ($outputHandler) {
         $outputHandler->setDataProcessor($dataProcessorClass);
-        $outputHandler->initialize($field['name'], $field['title'], $field['configuration']);
-        $dataProcessorClass->addOutputFieldHandlers($outputHandler);
+        try {
+          $outputHandler->initialize($field['name'], $field['title'], $field['configuration']);
+          $dataProcessorClass->addOutputFieldHandlers($outputHandler);
+        } catch (\Exception $e) {
+          CRM_Core_Session::setStatus($e->getMessage(), E::ts("Invalid field"), 'error');
+        }
       }
     }
     return $dataProcessorClass;
diff --git a/CRM/Dataprocessor/Form/AggregateField.php b/CRM/Dataprocessor/Form/AggregateField.php
index de74d9af..3782e899 100644
--- a/CRM/Dataprocessor/Form/AggregateField.php
+++ b/CRM/Dataprocessor/Form/AggregateField.php
@@ -23,7 +23,7 @@ class CRM_Dataprocessor_Form_AggregateField extends CRM_Core_Form {
   function preProcess() {
     $this->dataProcessorId = CRM_Utils_Request::retrieve('id', 'Integer');
     $this->dataProcessor = civicrm_api3('DataProcessor', 'getsingle', array('id' => $this->dataProcessorId));
-    $this->dataProcessorClass = CRM_Dataprocessor_BAO_DataProcessor::dataProcessorToClass($this->dataProcessor);
+    $this->dataProcessorClass = CRM_Dataprocessor_BAO_DataProcessor::dataProcessorToClass($this->dataProcessor, true);
     $this->assign('data_processor_id', $this->dataProcessorId);
 
     $title = E::ts('Data Processor Field');
diff --git a/CRM/Dataprocessor/Form/DataProcessor.php b/CRM/Dataprocessor/Form/DataProcessor.php
index bfad9fdb..281520ff 100644
--- a/CRM/Dataprocessor/Form/DataProcessor.php
+++ b/CRM/Dataprocessor/Form/DataProcessor.php
@@ -29,7 +29,7 @@ class CRM_Dataprocessor_Form_DataProcessor extends CRM_Core_Form {
     $this->dataProcessorId = CRM_Utils_Request::retrieve('id', 'Integer');
     if ($this->dataProcessorId) {
       $this->dataProcessor = civicrm_api3('DataProcessor', 'getsingle', ['id' => $this->dataProcessorId]);
-      $this->dataProcessorClass = CRM_Dataprocessor_BAO_DataProcessor::dataProcessorToClass($this->dataProcessor);
+      $this->dataProcessorClass = CRM_Dataprocessor_BAO_DataProcessor::dataProcessorToClass($this->dataProcessor, true);
     }
     $this->currentUrl = CRM_Utils_System::url('civicrm/dataprocessor/form/edit', array('reset' => 1, 'action' => 'update', 'id' => $this->dataProcessorId));
     $this->assign('data_processor_id', $this->dataProcessorId);
diff --git a/CRM/Dataprocessor/Form/Field.php b/CRM/Dataprocessor/Form/Field.php
index e1662f2c..4e815a76 100644
--- a/CRM/Dataprocessor/Form/Field.php
+++ b/CRM/Dataprocessor/Form/Field.php
@@ -51,7 +51,7 @@ class CRM_Dataprocessor_Form_Field extends CRM_Core_Form {
     $this->assign('data_processor_id', $this->dataProcessorId);
     if ($this->dataProcessorId) {
       $this->dataProcessor = civicrm_api3('DataProcessor', 'getsingle', array('id' => $this->dataProcessorId));
-      $this->dataProcessorClass = CRM_Dataprocessor_BAO_DataProcessor::dataProcessorToClass($this->dataProcessor);
+      $this->dataProcessorClass = CRM_Dataprocessor_BAO_DataProcessor::dataProcessorToClass($this->dataProcessor, true);
     }
 
     $this->id = CRM_Utils_Request::retrieve('id', 'Integer');
diff --git a/CRM/Dataprocessor/Form/Output/AbstractUIOutputForm.php b/CRM/Dataprocessor/Form/Output/AbstractUIOutputForm.php
index 53e3e473..9f1b8cd5 100644
--- a/CRM/Dataprocessor/Form/Output/AbstractUIOutputForm.php
+++ b/CRM/Dataprocessor/Form/Output/AbstractUIOutputForm.php
@@ -76,7 +76,7 @@ abstract class CRM_Dataprocessor_Form_Output_AbstractUIOutputForm extends CRM_Co
       }
 
       $this->dataProcessor = civicrm_api3('DataProcessor', 'getsingle', array('id' => $dao->data_processor_id));
-      $this->dataProcessorClass = \CRM_Dataprocessor_BAO_DataProcessor::dataProcessorToClass($this->dataProcessor);
+      $this->dataProcessorClass = \CRM_Dataprocessor_BAO_DataProcessor::dataProcessorToClass($this->dataProcessor, true);
       $this->dataProcessorId = $dao->data_processor_id;
 
       $this->dataProcessorOutput = civicrm_api3('DataProcessorOutput', 'getsingle', array('id' => $dao->output_id));
diff --git a/CRM/Dataprocessor/Form/Source.php b/CRM/Dataprocessor/Form/Source.php
index 8824acab..6850093b 100644
--- a/CRM/Dataprocessor/Form/Source.php
+++ b/CRM/Dataprocessor/Form/Source.php
@@ -56,7 +56,7 @@ class CRM_Dataprocessor_Form_Source extends CRM_Core_Form {
     $this->assign('data_processor_id', $this->dataProcessorId);
     if ($this->dataProcessorId) {
       $this->dataProcessor = civicrm_api3('DataProcessor', 'getsingle', array('id' => $this->dataProcessorId));
-      $this->dataProcessorClass = CRM_Dataprocessor_BAO_DataProcessor::dataProcessorToClass($this->dataProcessor);
+      $this->dataProcessorClass = CRM_Dataprocessor_BAO_DataProcessor::dataProcessorToClass($this->dataProcessor, true);
     }
 
     $this->id = CRM_Utils_Request::retrieve('id', 'Integer');
diff --git a/CRM/DataprocessorSearch/Form/AbstractSearch.php b/CRM/DataprocessorSearch/Form/AbstractSearch.php
index d9b78440..c186c0ff 100644
--- a/CRM/DataprocessorSearch/Form/AbstractSearch.php
+++ b/CRM/DataprocessorSearch/Form/AbstractSearch.php
@@ -246,7 +246,11 @@ abstract class CRM_DataprocessorSearch_Form_AbstractSearch extends CRM_Dataproce
       while($record = $this->dataProcessorClass->getDataFlow()->nextRecord()) {
         $i ++;
         $row = array();
-        $row['id'] = $record[$id_field]->formattedValue;
+
+        $row['id'] = null;
+        if (isset($record[$id_field])) {
+          $row['id'] = $record[$id_field]->formattedValue;
+        }
         $row['checkbox'] = CRM_Core_Form::CB_PREFIX.$row['id'];
         $row['record'] = $record;
 
@@ -258,12 +262,14 @@ abstract class CRM_DataprocessorSearch_Form_AbstractSearch extends CRM_Dataproce
 
         $this->addElement('checkbox', $row['checkbox'], NULL, NULL, ['class' => 'select-row']);
 
-        $prevnextData[] = array(
-          'entity_id1' => $row['id'],
-          'entity_table' => $this->getEntityTable(),
-          'data' => $record,
-        );
-        $ids[] = $row['id'];
+        if ($row['id']) {
+          $prevnextData[] = array(
+            'entity_id1' => $row['id'],
+            'entity_table' => $this->getEntityTable(),
+            'data' => $record,
+          );
+          $ids[] = $row['id'];
+        }
 
         $rows[] = $row;
       }
diff --git a/Civi/DataProcessor/DataSpecification/DataSpecification.php b/Civi/DataProcessor/DataSpecification/DataSpecification.php
index 04b81b71..88a7b95f 100644
--- a/Civi/DataProcessor/DataSpecification/DataSpecification.php
+++ b/Civi/DataProcessor/DataSpecification/DataSpecification.php
@@ -48,7 +48,10 @@ class DataSpecification {
    * @return \Civi\DataProcessor\DataSpecification\FieldSpecification
    */
   public function getFieldSpecificationByName($name) {
-    return $this->fields[$name];
+    if (isset($this->fields[$name])) {
+      return $this->fields[$name];
+    }
+    return null;
   }
 
   /**
diff --git a/Civi/DataProcessor/Exception/DataSourceNotFoundException.php b/Civi/DataProcessor/Exception/DataSourceNotFoundException.php
new file mode 100644
index 00000000..aa5d99d8
--- /dev/null
+++ b/Civi/DataProcessor/Exception/DataSourceNotFoundException.php
@@ -0,0 +1,11 @@
+<?php
+/**
+ * @author Jaap Jansma <jaap.jansma@civicoop.org>
+ * @license AGPL-3.0
+ */
+
+namespace Civi\DataProcessor\Exception;
+
+class DataSourceNotFoundException extends \Exception {
+
+}
\ No newline at end of file
diff --git a/Civi/DataProcessor/Exception/FieldNotFoundException.php b/Civi/DataProcessor/Exception/FieldNotFoundException.php
new file mode 100644
index 00000000..fa7277ff
--- /dev/null
+++ b/Civi/DataProcessor/Exception/FieldNotFoundException.php
@@ -0,0 +1,11 @@
+<?php
+/**
+ * @author Jaap Jansma <jaap.jansma@civicoop.org>
+ * @license AGPL-3.0
+ */
+
+namespace Civi\DataProcessor\Exception;
+
+class FieldNotFoundException extends \Exception {
+
+}
\ No newline at end of file
diff --git a/Civi/DataProcessor/Exception/InvalidConfigurationException.php b/Civi/DataProcessor/Exception/InvalidConfigurationException.php
new file mode 100644
index 00000000..db9941be
--- /dev/null
+++ b/Civi/DataProcessor/Exception/InvalidConfigurationException.php
@@ -0,0 +1,11 @@
+<?php
+/**
+ * @author Jaap Jansma <jaap.jansma@civicoop.org>
+ * @license AGPL-3.0
+ */
+
+namespace Civi\DataProcessor\Exception;
+
+class InvalidConfigurationException extends \Exception {
+
+}
\ No newline at end of file
diff --git a/Civi/DataProcessor/FieldOutputHandler/CaseRolesFieldOutputHandler.php b/Civi/DataProcessor/FieldOutputHandler/CaseRolesFieldOutputHandler.php
index 5f051529..3749f8e8 100644
--- a/Civi/DataProcessor/FieldOutputHandler/CaseRolesFieldOutputHandler.php
+++ b/Civi/DataProcessor/FieldOutputHandler/CaseRolesFieldOutputHandler.php
@@ -10,6 +10,8 @@ use Civi\DataProcessor\ProcessorType\AbstractProcessorType;
 use CRM_Dataprocessor_ExtensionUtil as E;
 use Civi\DataProcessor\Source\SourceInterface;
 use Civi\DataProcessor\DataSpecification\FieldSpecification;
+use Civi\DataProcessor\Exception\DataSourceNotFoundException;
+use Civi\DataProcessor\Exception\FieldNotFoundException;
 use Civi\DataProcessor\FieldOutputHandler\FieldOutput;
 
 class CaseRolesFieldOutputHandler extends AbstractFieldOutputHandler {
@@ -68,7 +70,17 @@ class CaseRolesFieldOutputHandler extends AbstractFieldOutputHandler {
   public function initialize($alias, $title, $configuration) {
     $this->outputFieldSpecification = new FieldSpecification($alias, 'String', $title, null, $alias);
     $this->caseIdSource = $this->dataProcessor->getDataSourceByName($configuration['datasource']);
+    if (!$this->caseIdSource) {
+      throw new DataSourceNotFoundException(E::ts("Field %1 requires data source '%2' which could not be found. Did you rename or deleted the data source?", array(1=>$title, 2=>$configuration['datasource'])));
+    }
     $this->caseIdField = $this->caseIdSource->getAvailableFields()->getFieldSpecificationByName($configuration['field']);
+    if (!$this->caseIdField) {
+      throw new FieldNotFoundException(E::ts("Field %1 requires a field with the name '%2' in the data source '%3'. Did you change the data source type?", array(
+        1 => $title,
+        2 => $configuration['field'],
+        3 => $configuration['datasource']
+      )));
+    }
     $this->caseIdSource->ensureFieldInSource($this->caseIdField);
 
     $this->outputFieldSpecification = new FieldSpecification($this->caseIdField->name, 'String', $title, null, $alias);
diff --git a/Civi/DataProcessor/FieldOutputHandler/ContactLinkFieldOutputHandler.php b/Civi/DataProcessor/FieldOutputHandler/ContactLinkFieldOutputHandler.php
index d9893d20..be7e9ec0 100644
--- a/Civi/DataProcessor/FieldOutputHandler/ContactLinkFieldOutputHandler.php
+++ b/Civi/DataProcessor/FieldOutputHandler/ContactLinkFieldOutputHandler.php
@@ -11,6 +11,8 @@ use CRM_Dataprocessor_ExtensionUtil as E;
 use Civi\DataProcessor\Source\SourceInterface;
 use Civi\DataProcessor\DataSpecification\FieldSpecification;
 use Civi\DataProcessor\FieldOutputHandler\FieldOutput;
+use Civi\DataProcessor\Exception\DataSourceNotFoundException;
+use Civi\DataProcessor\Exception\FieldNotFoundException;
 
 class ContactLinkFieldOutputHandler extends AbstractFieldOutputHandler implements OutputHandlerSortable {
 
@@ -78,11 +80,37 @@ class ContactLinkFieldOutputHandler extends AbstractFieldOutputHandler implement
   public function initialize($alias, $title, $configuration) {
     $this->outputFieldSpecification = new FieldSpecification($alias, 'String', $title, null, $alias);
     $this->contactIdSource = $this->dataProcessor->getDataSourceByName($configuration['contact_id_datasource']);
+    if (!$this->contactIdSource) {
+      throw new DataSourceNotFoundException(E::ts("Field %1 requires data source '%2' which could not be found. Did you rename or deleted the data source?", array(
+        1=>$title,
+        2=>$configuration['datasource'])
+      ));
+    }
     $this->contactIdField = $this->contactIdSource->getAvailableFields()->getFieldSpecificationByName($configuration['contact_id_field']);
+    if (!$this->contactIdField) {
+      throw new FieldNotFoundException(E::ts("Field %1 requires a field with the name '%2' in the data source '%3'. Did you change the data source type?", array(
+        1 => $title,
+        2 => $configuration['contact_id_field'],
+        3 => $configuration['contact_id_datasource']
+      )));
+    }
     $this->contactIdSource->ensureFieldInSource($this->contactIdField);
 
     $this->contactNameSource = $this->dataProcessor->getDataSourceByName($configuration['contact_name_datasource']);
+    if (!$this->contactIdSource) {
+      throw new DataSourceNotFoundException(E::ts("Field %1 requires data source '%2' which could not be found. Did you rename or deleted the data source?", array(
+        1=>$title,
+        2=>$configuration['contact_name_datasource'])
+      ));
+    }
     $this->contactNameField = $this->contactNameSource->getAvailableFields()->getFieldSpecificationByName($configuration['contact_name_field']);
+    if (!$this->contactNameField) {
+      throw new FieldNotFoundException(E::ts("Field %1 requires a field with the name '%2' in the data source '%3'. Did you change the data source type?", array(
+        1 => $title,
+        2 => $configuration['contact_name_field'],
+        3 => $configuration['contact_name_datasource']
+      )));
+    }
     $this->contactNameSource->ensureFieldInSource($this->contactNameField);
 
     $this->outputFieldSpecification = new FieldSpecification($this->contactIdField->name, 'String', $title, null, $alias);
diff --git a/Civi/DataProcessor/FieldOutputHandler/EventRepeatingInfoFieldOutputHandler.php b/Civi/DataProcessor/FieldOutputHandler/EventRepeatingInfoFieldOutputHandler.php
index a0a4254d..6f31c742 100644
--- a/Civi/DataProcessor/FieldOutputHandler/EventRepeatingInfoFieldOutputHandler.php
+++ b/Civi/DataProcessor/FieldOutputHandler/EventRepeatingInfoFieldOutputHandler.php
@@ -9,6 +9,8 @@ namespace Civi\DataProcessor\FieldOutputHandler;
 use CRM_Dataprocessor_ExtensionUtil as E;
 use Civi\DataProcessor\Source\SourceInterface;
 use Civi\DataProcessor\DataSpecification\FieldSpecification;
+use Civi\DataProcessor\Exception\DataSourceNotFoundException;
+use Civi\DataProcessor\Exception\FieldNotFoundException;
 
 class EventRepeatingInfoFieldOutputHandler extends AbstractFieldOutputHandler implements OutputHandlerSortable{
 
@@ -60,7 +62,17 @@ class EventRepeatingInfoFieldOutputHandler extends AbstractFieldOutputHandler im
    */
   public function initialize($alias, $title, $configuration) {
     $this->dataSource = $this->dataProcessor->getDataSourceByName($configuration['datasource']);
+    if (!$this->dataSource) {
+      throw new DataSourceNotFoundException(E::ts("Field %1 requires data source '%2' which could not be found. Did you rename or deleted the data source?", array(1=>$title, 2=>$configuration['datasource'])));
+    }
     $this->inputFieldSpec = $this->dataSource->getAvailableFields()->getFieldSpecificationByName($configuration['field']);
+    if (!$this->inputFieldSpec) {
+      throw new FieldNotFoundException(E::ts("Field %1 requires a field with the name '%2' in the data source '%3'. Did you change the data source type?", array(
+        1 => $title,
+        2 => $configuration['field'],
+        3 => $configuration['datasource']
+      )));
+    }
     $this->dataSource->ensureFieldInSource($this->inputFieldSpec);
 
     $this->outputFieldSpec = new FieldSpecification($alias, 'String', $title, $alias);
diff --git a/Civi/DataProcessor/FieldOutputHandler/FileFieldOutputHandler.php b/Civi/DataProcessor/FieldOutputHandler/FileFieldOutputHandler.php
index ce0c3a56..a17678d8 100644
--- a/Civi/DataProcessor/FieldOutputHandler/FileFieldOutputHandler.php
+++ b/Civi/DataProcessor/FieldOutputHandler/FileFieldOutputHandler.php
@@ -10,6 +10,8 @@ use CRM_Dataprocessor_ExtensionUtil as E;
 use Civi\DataProcessor\Source\SourceInterface;
 use Civi\DataProcessor\DataSpecification\FieldSpecification;
 use Civi\DataProcessor\FieldOutputHandler\FieldOutput;
+use Civi\DataProcessor\Exception\DataSourceNotFoundException;
+use Civi\DataProcessor\Exception\FieldNotFoundException;
 
 class FileFieldOutputHandler extends AbstractFieldOutputHandler {
 
@@ -88,7 +90,17 @@ class FileFieldOutputHandler extends AbstractFieldOutputHandler {
    */
   public function initialize($alias, $title, $configuration) {
     $this->dataSource = $this->dataProcessor->getDataSourceByName($configuration['datasource']);
+    if (!$this->dataSource) {
+      throw new DataSourceNotFoundException(E::ts("Field %1 requires data source '%2' which could not be found. Did you rename or deleted the data source?", array(1=>$title, 2=>$configuration['datasource'])));
+    }
     $this->inputFieldSpec = $this->dataSource->getAvailableFields()->getFieldSpecificationByName($configuration['field']);
+    if (!$this->inputFieldSpec) {
+      throw new FieldNotFoundException(E::ts("Field %1 requires a field with the name '%2' in the data source '%3'. Did you change the data source type?", array(
+        1 => $title,
+        2 => $configuration['field'],
+        3 => $configuration['datasource']
+      )));
+    }
     $this->dataSource->ensureFieldInSource($this->inputFieldSpec);
 
     $this->outputFieldSpec = clone $this->inputFieldSpec;
diff --git a/Civi/DataProcessor/FieldOutputHandler/GroupsOfContactFieldOutputHandler.php b/Civi/DataProcessor/FieldOutputHandler/GroupsOfContactFieldOutputHandler.php
index 34d63ea6..194bbb4e 100644
--- a/Civi/DataProcessor/FieldOutputHandler/GroupsOfContactFieldOutputHandler.php
+++ b/Civi/DataProcessor/FieldOutputHandler/GroupsOfContactFieldOutputHandler.php
@@ -11,6 +11,8 @@ use CRM_Dataprocessor_ExtensionUtil as E;
 use Civi\DataProcessor\Source\SourceInterface;
 use Civi\DataProcessor\DataSpecification\FieldSpecification;
 use Civi\DataProcessor\FieldOutputHandler\FieldOutput;
+use Civi\DataProcessor\Exception\DataSourceNotFoundException;
+use Civi\DataProcessor\Exception\FieldNotFoundException;
 
 class GroupsOfContactFieldOutputHandler extends AbstractFieldOutputHandler {
 
@@ -66,7 +68,17 @@ class GroupsOfContactFieldOutputHandler extends AbstractFieldOutputHandler {
   public function initialize($alias, $title, $configuration) {
     $this->outputFieldSpecification = new FieldSpecification($alias, 'String', $title, null, $alias);
     $this->contactIdSource = $this->dataProcessor->getDataSourceByName($configuration['datasource']);
+    if (!$this->dataSource) {
+      throw new DataSourceNotFoundException(E::ts("Field %1 requires data source '%2' which could not be found. Did you rename or deleted the data source?", array(1=>$title, 2=>$configuration['datasource'])));
+    }
     $this->contactIdField = $this->contactIdSource->getAvailableFields()->getFieldSpecificationByName($configuration['field']);
+    if (!$this->inputFieldSpec) {
+      throw new FieldNotFoundException(E::ts("Field %1 requires a field with the name '%2' in the data source '%3'. Did you change the data source type?", array(
+        1 => $title,
+        2 => $configuration['field'],
+        3 => $configuration['datasource']
+      )));
+    }
     $this->contactIdSource->ensureFieldInSource($this->contactIdField);
 
     $this->outputFieldSpecification = new FieldSpecification($this->contactIdField->name, 'String', $title, null, $alias);
diff --git a/Civi/DataProcessor/FieldOutputHandler/RawFieldOutputHandler.php b/Civi/DataProcessor/FieldOutputHandler/RawFieldOutputHandler.php
index 66b63ddc..f8a0d93b 100644
--- a/Civi/DataProcessor/FieldOutputHandler/RawFieldOutputHandler.php
+++ b/Civi/DataProcessor/FieldOutputHandler/RawFieldOutputHandler.php
@@ -6,6 +6,8 @@
 
 namespace Civi\DataProcessor\FieldOutputHandler;
 
+use Civi\DataProcessor\Exception\DataSourceNotFoundException;
+use Civi\DataProcessor\Exception\FieldNotFoundException;
 use CRM_Dataprocessor_ExtensionUtil as E;
 use Civi\DataProcessor\Source\SourceInterface;
 use Civi\DataProcessor\DataSpecification\FieldSpecification;
@@ -60,7 +62,17 @@ class RawFieldOutputHandler extends AbstractFieldOutputHandler implements Output
    */
   public function initialize($alias, $title, $configuration) {
     $this->dataSource = $this->dataProcessor->getDataSourceByName($configuration['datasource']);
+    if (!$this->dataSource) {
+      throw new DataSourceNotFoundException(E::ts("Field %1 requires data source '%2' which could not be found. Did you rename or deleted the data source?", array(1=>$title, 2=>$configuration['datasource'])));
+    }
     $this->inputFieldSpec = $this->dataSource->getAvailableFields()->getFieldSpecificationByName($configuration['field']);
+    if (!$this->inputFieldSpec) {
+      throw new FieldNotFoundException(E::ts("Field %1 requires a field with the name '%2' in the data source '%3'. Did you change the data source type?", array(
+        1 => $title,
+        2 => $configuration['field'],
+        3 => $configuration['datasource']
+      )));
+    }
     $this->dataSource->ensureFieldInSource($this->inputFieldSpec);
 
     $this->outputFieldSpec = clone $this->inputFieldSpec;
diff --git a/Civi/DataProcessor/FilterHandler/CaseRoleFilter.php b/Civi/DataProcessor/FilterHandler/CaseRoleFilter.php
index ec3fc3e1..bb235428 100644
--- a/Civi/DataProcessor/FilterHandler/CaseRoleFilter.php
+++ b/Civi/DataProcessor/FilterHandler/CaseRoleFilter.php
@@ -10,6 +10,9 @@ use Civi\DataProcessor\DataFlow\SqlDataFlow;
 use Civi\DataProcessor\DataFlow\SqlTableDataFlow;
 use Civi\DataProcessor\DataSpecification\CustomFieldSpecification;
 use Civi\DataProcessor\DataSpecification\FieldSpecification;
+use Civi\DataProcessor\Exception\DataSourceNotFoundException;
+use Civi\DataProcessor\Exception\FieldNotFoundException;
+use Civi\DataProcessor\Exception\InvalidConfigurationException;
 use Civi\DataProcessor\Source\SourceInterface;
 use CRM_Dataprocessor_ExtensionUtil as E;
 
@@ -47,17 +50,26 @@ class CaseRoleFilter extends AbstractFilterHandler {
       return; // Already initialized.
     }
     if (!isset($configuration['datasource']) || !isset($configuration['field'])) {
-      return; // Invalid configuration
+      throw new InvalidConfigurationException(E::ts("Filter %1 requires a field to filter on. None given.", array(1=>$title)));
     }
 
     $this->is_required = $is_required;
 
     $this->dataSource = $this->data_processor->getDataSourceByName($configuration['datasource']);
-    if ($this->dataSource) {
-      $this->fieldSpecification  =  clone $this->dataSource->getAvailableFilterFields()->getFieldSpecificationByName($configuration['field']);
-      $this->fieldSpecification->alias = $alias;
-      $this->fieldSpecification->title = $title;
+    if (!$this->dataSource) {
+      throw new DataSourceNotFoundException(E::ts("Filter %1 requires data source '%2' which could not be found. Did you rename or deleted the data source?", array(1=>$title, 2=>$configuration['datasource'])));
     }
+    $this->fieldSpecification  =  clone $this->dataSource->getAvailableFilterFields()->getFieldSpecificationByName($configuration['field']);
+    if (!$this->fieldSpecification) {
+      throw new FieldNotFoundException(E::ts("Filter %1 requires a field with the name '%2' in the data source '%3'. Did you change the data source type?", array(
+        1 => $title,
+        2 => $configuration['field'],
+        3 => $configuration['datasource']
+      )));
+    }
+    $this->fieldSpecification->alias = $alias;
+    $this->fieldSpecification->title = $title;
+
 
     if (isset($configuration['relationship_types']) && is_array($configuration['relationship_types'])) {
       $this->relationship_type_ids = array();
diff --git a/Civi/DataProcessor/FilterHandler/ContactInGroupFilter.php b/Civi/DataProcessor/FilterHandler/ContactInGroupFilter.php
index 04f3e630..14aa02c1 100644
--- a/Civi/DataProcessor/FilterHandler/ContactInGroupFilter.php
+++ b/Civi/DataProcessor/FilterHandler/ContactInGroupFilter.php
@@ -10,6 +10,8 @@ use Civi\DataProcessor\DataFlow\SqlDataFlow;
 use Civi\DataProcessor\DataFlow\SqlTableDataFlow;
 use Civi\DataProcessor\DataSpecification\CustomFieldSpecification;
 use Civi\DataProcessor\DataSpecification\FieldSpecification;
+use Civi\DataProcessor\Exception\DataSourceNotFoundException;
+use Civi\DataProcessor\Exception\FieldNotFoundException;
 use Civi\DataProcessor\Source\SourceInterface;
 use CRM_Dataprocessor_ExtensionUtil as E;
 
@@ -47,17 +49,25 @@ class ContactInGroupFilter extends AbstractFilterHandler {
       return; // Already initialized.
     }
     if (!isset($configuration['datasource']) || !isset($configuration['field'])) {
-      return; // Invalid configuration
+      throw new InvalidConfigurationException(E::ts("Filter %1 requires a field to filter on. None given.", array(1=>$title)));
     }
 
     $this->is_required = $is_required;
 
     $this->dataSource = $this->data_processor->getDataSourceByName($configuration['datasource']);
-    if ($this->dataSource) {
-      $this->fieldSpecification  =  clone $this->dataSource->getAvailableFilterFields()->getFieldSpecificationByName($configuration['field']);
-      $this->fieldSpecification->alias = $alias;
-      $this->fieldSpecification->title = $title;
+    if (!$this->dataSource) {
+      throw new DataSourceNotFoundException(E::ts("Filter %1 requires data source '%2' which could not be found. Did you rename or deleted the data source?", array(1=>$title, 2=>$configuration['datasource'])));
     }
+    $this->fieldSpecification  =  clone $this->dataSource->getAvailableFilterFields()->getFieldSpecificationByName($configuration['field']);
+    if (!$this->fieldSpecification) {
+      throw new FieldNotFoundException(E::ts("Filter %1 requires a field with the name '%2' in the data source '%3'. Did you change the data source type?", array(
+        1 => $title,
+        2 => $configuration['field'],
+        3 => $configuration['datasource']
+      )));
+    }
+    $this->fieldSpecification->alias = $alias;
+    $this->fieldSpecification->title = $title;
 
     if (isset($configuration['parent_group']) && $configuration['parent_group']) {
       $this->parent_group_id = civicrm_api3('Group', 'getvalue', array('return' => 'id', 'name' => $configuration['parent_group']));
diff --git a/Civi/DataProcessor/FilterHandler/SimpleSqlFilter.php b/Civi/DataProcessor/FilterHandler/SimpleSqlFilter.php
index a16ac201..eb50a34d 100644
--- a/Civi/DataProcessor/FilterHandler/SimpleSqlFilter.php
+++ b/Civi/DataProcessor/FilterHandler/SimpleSqlFilter.php
@@ -10,6 +10,9 @@ use Civi\DataProcessor\DataFlow\SqlDataFlow;
 use Civi\DataProcessor\DataFlow\SqlTableDataFlow;
 use Civi\DataProcessor\DataSpecification\CustomFieldSpecification;
 use Civi\DataProcessor\DataSpecification\FieldSpecification;
+use Civi\DataProcessor\Exception\DataSourceNotFoundException;
+use Civi\DataProcessor\Exception\FieldNotFoundException;
+use Civi\DataProcessor\Exception\InvalidConfigurationException;
 use Civi\DataProcessor\Source\SourceInterface;
 use CRM_Dataprocessor_ExtensionUtil as E;
 
@@ -42,17 +45,26 @@ class SimpleSqlFilter extends AbstractFilterHandler {
       return; // Already initialized.
     }
     if (!isset($configuration['datasource']) || !isset($configuration['field'])) {
-      return; // Invalid configuration
+      throw new InvalidConfigurationException(E::ts("Filter %1 requires a field to filter on. None given.", array(1=>$title)));
     }
 
     $this->is_required = $is_required;
 
     $this->dataSource = $this->data_processor->getDataSourceByName($configuration['datasource']);
-    if ($this->dataSource) {
-      $this->fieldSpecification  =  clone $this->dataSource->getAvailableFilterFields()->getFieldSpecificationByName($configuration['field']);
-      $this->fieldSpecification->alias = $alias;
-      $this->fieldSpecification->title = $title;
+    if (!$this->dataSource) {
+      throw new DataSourceNotFoundException(E::ts("Filter %1 requires data source '%2' which could not be found. Did you rename or deleted the data source?", array(1=>$title, 2=>$configuration['datasource'])));
     }
+    $this->fieldSpecification  =  clone $this->dataSource->getAvailableFilterFields()->getFieldSpecificationByName($configuration['field']);
+    if (!$this->fieldSpecification) {
+      throw new FieldNotFoundException(E::ts("Filter %1 requires a field with the name '%2' in the data source '%3'. Did you change the data source type?", array(
+        1 => $title,
+        2 => $configuration['field'],
+        3 => $configuration['datasource']
+      )));
+    }
+    $this->fieldSpecification->alias = $alias;
+    $this->fieldSpecification->title = $title;
+
   }
 
   /**
diff --git a/Civi/DataProcessor/Output/Api.php b/Civi/DataProcessor/Output/Api.php
index 9817db18..762cee94 100644
--- a/Civi/DataProcessor/Output/Api.php
+++ b/Civi/DataProcessor/Output/Api.php
@@ -166,6 +166,9 @@ class Api implements OutputInterface, API_ProviderInterface, EventSubscriberInte
 
       $result['count'] = count($result['values']);
     }
+
+    $result = $this->checkForErrors($result);
+
     return $result;
   }
 
@@ -273,6 +276,9 @@ class Api implements OutputInterface, API_ProviderInterface, EventSubscriberInte
 
       $result['count'] = count($result['values']);
     }
+
+    $result = $this->checkForErrors($result);
+
     return $result;
   }
 
@@ -383,14 +389,46 @@ class Api implements OutputInterface, API_ProviderInterface, EventSubscriberInte
         'count' => count($values),
         'is_error' => 0,
       );
+
       if (isset($params['debug']) && $params['debug']) {
         $return['debug_info'] = $dataProcessorClass->getDataFlow()->getDebugInformation();
       }
 
+      $return = $this->checkForErrors($return);
+
       return $return;
     }
   }
 
+  /**
+   * Check for errors in the CiviCRM Status messages list
+   * and if errors are present create a civicrm api return error with the messages in
+   * the status list
+   *
+   * @param $return
+   *
+   * @return mixed
+   */
+  protected function checkForErrors($return) {
+    $session = \CRM_Core_Session::singleton();
+    $statuses = $session->getStatus(true);
+    foreach($statuses as $status) {
+      if ($status['type'] == 'error') {
+        $return['is_error'] = 1;
+        unset($return['values']);
+        unset($return['count']);
+        if (!isset($return['error_message'])) {
+          $return['error_message'] = "";
+        }
+        $return['error_message'] .= " ".$status['text'];
+      }
+    }
+    if (isset($return['error_message'])) {
+      $return['error_message'] = trim($return['error_message']);
+    }
+    return $return;
+  }
+
   /**
    * @param int $version
    *   API version.
-- 
GitLab