From b327215533fbc2b81f3f843e0d9ec9a7785805d4 Mon Sep 17 00:00:00 2001
From: Jaap Jansma <jaap.jansma@civicoop.org>
Date: Thu, 2 May 2019 17:06:09 +0200
Subject: [PATCH] refactored API class. So it will implement the get options

---
 Civi/DataProcessor/Output/Api.php | 248 ++++++++++++++++++++----------
 sql/alter.sql                     |  27 ----
 2 files changed, 169 insertions(+), 106 deletions(-)
 delete mode 100644 sql/alter.sql

diff --git a/Civi/DataProcessor/Output/Api.php b/Civi/DataProcessor/Output/Api.php
index 55c3f3d1..2df9acaa 100644
--- a/Civi/DataProcessor/Output/Api.php
+++ b/Civi/DataProcessor/Output/Api.php
@@ -47,12 +47,14 @@ class Api implements OutputInterface, API_ProviderInterface, EventSubscriberInte
     $form->add('text', 'api_action', E::ts('API Action Name'), true);
     $form->add('text', 'api_count_action', E::ts('API GetCount Action Name'), true);
 
-    if ($output) {
+    if ($output && isset($output['id']) && $output['id']) {
       $defaults['permission'] = $output['permission'];
       $defaults['api_entity'] = $output['api_entity'];
       $defaults['api_action'] = $output['api_action'];
       $defaults['api_count_action'] = $output['api_count_action'];
     } else {
+      $defaults['api_action'] = 'get';
+      $defaults['api_count_action'] = 'getcount';
       $defaults['permission'] = 'access CiviCRM';
     }
     $form->setDefaults($defaults);
@@ -99,8 +101,13 @@ class Api implements OutputInterface, API_ProviderInterface, EventSubscriberInte
     // So instead we use the Respond event and check in the respond event whether the action is getFields and
     // if so do our getfields stuff there.
     return array(
-      Events::RESOLVE => array('onApiResolve'),
-      Events::RESPOND => array('onGetFieldsRepsonse'), // we use this method to add our field definition to the getFields action.
+      Events::RESOLVE => array(
+        array('onApiResolve', Events::W_EARLY),
+      ),
+      /*Events::RESPOND => array(
+        'onGetFieldsRepsonse', // we use this method to add our field definition to the getFields action.
+        'onGetOptionsRepsonse', // we use this method to add our field definition to the getFields action.
+      ),*/
     );
   }
 
@@ -110,104 +117,166 @@ class Api implements OutputInterface, API_ProviderInterface, EventSubscriberInte
     foreach($entities as $entity) {
       if (strtolower($apiRequest['entity']) == strtolower($entity)) {
         $event->setApiProvider($this);
+        if (strtolower($apiRequest['action']) == 'getfields' || strtolower($apiRequest['action']) == 'getoptions') {
+          $event->stopPropagation();
+        }
       }
     }
   }
 
   /**
-   * Event listener on the ResponddEvent to handle the getfields actions.
-   * So the fields defined by the user are availble in the api explorer for example.
+   * Invoke the GetFields action
+   *
+   * @param $apiRequest
+   *
+   * @return array
    */
-  public function onGetFieldsRepsonse(RespondEvent $event) {
-    $apiRequest = $event->getApiRequest();
+  protected function invokeGetFields($apiRequest) {
     $params = $apiRequest['params'];
-    $result = $event->getResponse();
-    $types = \CRM_Utils_Type::getValidTypes();
-    $types['Memo'] = \CRM_Utils_Type::T_TEXT;
+    $result = array();
 
-    // First check whether the entity is dataprocessorapi and the action is getfields.
-    // If not return this function.
-    if (strtolower($apiRequest['action']) != 'getfields') {
-      return;
-    }
-    // Now check whether the action param is set. With the action param we can find the data processor.
     if (isset($params['action'])) {
       try {
-        // Find the data processor
-        $dataProcessorIdSql = "
+        $fields = $this->getFields($apiRequest['entity'], $params);
+        if (strtolower($params['action']) != 'getoptions') {
+          $result['values'] = $fields;
+        } else {
+          $fieldNames = array();
+          foreach($fields as $field) {
+            $fieldNames[$field['name']] = $field['title'];
+          }
+          $result['values']['field']['title'] = E::ts('Field');
+          $result['values']['field']['name'] = 'field';
+          $result['values']['field']['api.required'] = TRUE;
+          $result['values']['field']['options'] = $fieldNames;
+          $result['values']['field']['type'] = \CRM_Utils_Type::T_STRING;
+
+          $result['values']['context']['title'] = E::ts('Context');
+          $result['values']['context']['name'] = 'context';
+          $result['values']['context']['api.required'] = FALSE;
+          $result['values']['context']['options'] = \CRM_Core_DAO::buildOptionsContext();
+          $result['values']['context']['type'] = \CRM_Utils_Type::T_STRING;
+
+          $result['values']['api_action']['title'] = E::ts('Action');
+          $result['values']['api_action']['name'] = 'api_action';
+          $result['values']['api_action']['api.required'] = FALSE;
+          $result['values']['api_action']['options'] = $this->getActionNames(3, $apiRequest['entity']);
+          $result['values']['api_action']['type'] = \CRM_Utils_Type::T_STRING;
+
+        }
+      } catch(\Exception $e) {
+        // Do nothing.
+      }
+
+      $result['count'] = count($result['values']);
+    }
+    return $result;
+  }
+
+  protected function getFields($entity, $params) {
+    $types = \CRM_Utils_Type::getValidTypes();
+    $types['Memo'] = \CRM_Utils_Type::T_TEXT;
+    $fields = array();
+
+    // Find the data processor
+    $dataProcessorIdSql = "
           SELECT *
           FROM civicrm_data_processor_output o 
           INNER JOIN civicrm_data_processor p ON o.data_processor_id = p.id 
           WHERE p.is_active = 1 AND LOWER(o.api_entity) = LOWER(%1)
-          AND (LOWER(o.api_action) = LOWER(%2) OR LOWER(o.api_count_action) = LOWER(%2))
         ";
 
-        $dataProcessorIdParams[1] = array($apiRequest['entity'], 'String');
-        $dataProcessorIdParams[2] = array($params['action'], 'String');
-        $dao = \CRM_Core_DAO::executeQuery($dataProcessorIdSql, $dataProcessorIdParams);
-        if (!$dao->fetch()) {
-          throw new \API_Exception("Could not find a data processor");
-        }
-        $dataProcessor = civicrm_api3('DataProcessor', 'getsingle', array('id' => $dao->data_processor_id));
-        $dataProcessorClass = \CRM_Dataprocessor_BAO_DataProcessor::dataProcessorToClass($dataProcessor);
+    $dataProcessorIdParams[1] = array($entity, 'String');
+    if (isset($params['action']) && strtolower($params['action']) != 'getoptions') {
+      $dataProcessorIdSql .= " AND (LOWER(o.api_action) = LOWER(%2) OR LOWER(o.api_count_action) = LOWER(%2))";
+      $dataProcessorIdParams[2] = array($params['action'], 'String');
+    }
+    $dao = \CRM_Core_DAO::executeQuery($dataProcessorIdSql, $dataProcessorIdParams);
+    if (!$dao->fetch()) {
+      throw new \API_Exception("Could not find a data processor");
+    }
+    $dataProcessor = civicrm_api3('DataProcessor', 'getsingle', array('id' => $dao->data_processor_id));
+    $dataProcessorClass = \CRM_Dataprocessor_BAO_DataProcessor::dataProcessorToClass($dataProcessor);
 
 
-        foreach ($dataProcessorClass->getDataFlow()->getOutputFieldHandlers() as $outputFieldHandler) {
-          $fieldSpec = $outputFieldHandler->getOutputFieldSpecification();
-          $type = \CRM_Utils_Type::T_STRING;
-          if (isset($types[$fieldSpec->type])) {
-            $type = $types[$fieldSpec->type];
-          }
-          $field = [
-            'name' => $fieldSpec->alias,
-            'title' => $fieldSpec->title,
-            'description' => '',
-            'type' => $type,
-            'api.required' => FALSE,
-            'api.aliases' => [],
-            'api.filter' => FALSE,
-            'api.return' => TRUE,
-          ];
-          if ($fieldSpec->getOptions()) {
-            $field['options'] = $fieldSpec->getOptions();
-          }
-          $result['values'][$fieldSpec->alias] = $field;
-        }
-        foreach($dataProcessorClass->getFilterHandlers() as $filterHandler) {
-          $fieldSpec = $filterHandler->getFieldSpecification();
-          $type = \CRM_Utils_Type::T_STRING;
-          if (isset($types[$fieldSpec->type])) {
-            $type = $types[$fieldSpec->type];
-          }
-          if (!$fieldSpec) {
-            continue;
-          }
-          $field = [
-            'name' => $fieldSpec->alias,
-            'title' => $fieldSpec->title,
-            'description' => '',
-            'type' => $type,
-            'api.required' => $filterHandler->isRequired(),
-            'api.aliases' => [],
-            'api.filter' => TRUE,
-            'api.return' => isset($result['values'][$fieldSpec->alias]) ? $result['values'][$fieldSpec->alias]['api.return'] : FALSE,
-          ];
-          if ($fieldSpec->getOptions()) {
-            $field['options'] = $fieldSpec->getOptions();
-          }
-          if (!isset($result['values'][$fieldSpec->alias])) {
-            $result['values'][$fieldSpec->alias] = $field;
-          } else {
-            $result['values'][$fieldSpec->alias] = array_merge($result['values'][$fieldSpec->alias], $field);
-          }
+    foreach ($dataProcessorClass->getDataFlow()->getOutputFieldHandlers() as $outputFieldHandler) {
+      $fieldSpec = $outputFieldHandler->getOutputFieldSpecification();
+      $type = \CRM_Utils_Type::T_STRING;
+      if (isset($types[$fieldSpec->type])) {
+        $type = $types[$fieldSpec->type];
+      }
+      $field = [
+        'name' => $fieldSpec->alias,
+        'title' => $fieldSpec->title,
+        'description' => '',
+        'type' => $type,
+        'api.required' => FALSE,
+        'api.aliases' => [],
+        'api.filter' => FALSE,
+        'api.return' => TRUE,
+      ];
+      if ($fieldSpec->getOptions()) {
+        $field['options'] = $fieldSpec->getOptions();
+      }
+      $fields[$fieldSpec->alias] = $field;
+    }
+    foreach($dataProcessorClass->getFilterHandlers() as $filterHandler) {
+      $fieldSpec = $filterHandler->getFieldSpecification();
+      $type = \CRM_Utils_Type::T_STRING;
+      if (isset($types[$fieldSpec->type])) {
+        $type = $types[$fieldSpec->type];
+      }
+      if (!$fieldSpec) {
+        continue;
+      }
+      $field = [
+        'name' => $fieldSpec->alias,
+        'title' => $fieldSpec->title,
+        'description' => '',
+        'type' => $type,
+        'api.required' => $filterHandler->isRequired(),
+        'api.aliases' => [],
+        'api.filter' => TRUE,
+        'api.return' => isset($fields[$fieldSpec->alias]) ? $fields[$fieldSpec->alias]['api.return'] : FALSE,
+      ];
+      if ($fieldSpec->getOptions()) {
+        $field['options'] = $fieldSpec->getOptions();
+      }
+
+      if (!isset($fields[$fieldSpec->alias])) {
+        $fields[$fieldSpec->alias] = $field;
+      } else {
+        $fields[$fieldSpec->alias] = array_merge($fields[$fieldSpec->alias], $field);
+      }
+    }
+    return $fields;
+  }
+
+  /**
+   * Invoke the GetOptions api call
+   *
+   * @param $apiRequest
+   * @return array
+   */
+  protected function invokeGetOptions($apiRequest) {
+    $params = $apiRequest['params'];
+    $result = array();
+    $result['values'] = array();
+    // Now check whether the action param is set. With the action param we can find the data processor.
+    if (isset($params['field'])) {
+      try {
+        $fieldName = $params['field'];
+        $fields = $this->getFields($apiRequest['entity'], $params);
+        if (isset($fields[$fieldName]) && isset($fields[$fieldName]['options'])) {
+          $result['values'] = $fields[$fieldName]['options'];
         }
       } catch(\Exception $e) {
         // Do nothing.
       }
 
       $result['count'] = count($result['values']);
-      $event->setResponse($result);
     }
+    return $result;
   }
 
 
@@ -222,6 +291,22 @@ class Api implements OutputInterface, API_ProviderInterface, EventSubscriberInte
   public function invoke($apiRequest) {
     $isCountAction = FALSE;
 
+    switch (strtolower($apiRequest['action'])) {
+      case 'getfields':
+        // Do get fields
+        return $this->invokeGetFields($apiRequest);
+        break;
+      case 'getoptions':
+        // Do get options
+        return $this->invokeGetOptions($apiRequest);
+        break;
+      default:
+        return $this->invokeDataProcessor($apiRequest);
+        break;
+    }
+  }
+
+  protected function invokeDataProcessor($apiRequest) {
     $dataProcessorIdSql = "
       SELECT *
       FROM civicrm_data_processor_output o 
@@ -242,6 +327,10 @@ class Api implements OutputInterface, API_ProviderInterface, EventSubscriberInte
     $dataProcessorClass = \CRM_Dataprocessor_BAO_DataProcessor::dataProcessorToClass($dataProcessor);
 
     $params = $apiRequest['params'];
+    return $this->runDataProcessor($dataProcessorClass, $params, $isCountAction);
+  }
+
+  protected function runDataProcessor(AbstractProcessorType $dataProcessorClass, $params, $isCount) {
     foreach($dataProcessorClass->getFilterHandlers() as $filter) {
       $filterSpec = $filter->getFieldSpecification();
       if ($filter->isRequired() && !isset($params[$filterSpec->alias])) {
@@ -264,11 +353,11 @@ class Api implements OutputInterface, API_ProviderInterface, EventSubscriberInte
       }
     }
 
-    if ($isCountAction) {
+    if ($isCount) {
       $count = $dataProcessorClass->getDataFlow()->recordCount();
       return array('result' => $count, 'is_error' => 0);
     } else {
-      $options = _civicrm_api3_get_options_from_params($apiRequest['params']);
+      $options = _civicrm_api3_get_options_from_params($params);
 
       if (isset($options['limit']) && $options['limit'] > 0) {
         $dataProcessorClass->getDataFlow()->setLimit($options['limit']);
@@ -297,7 +386,7 @@ class Api implements OutputInterface, API_ProviderInterface, EventSubscriberInte
         'count' => count($values),
         'is_error' => 0,
       );
-      if (isset($apiRequest['params']['debug']) && $apiRequest['params']['debug']) {
+      if (isset($params['debug']) && $params['debug']) {
         $return['debug_info'] = $dataProcessorClass->getDataFlow()->getDebugInformation();
       }
       return $return;
@@ -347,6 +436,7 @@ class Api implements OutputInterface, API_ProviderInterface, EventSubscriberInte
       $actions[] = $dao->api_count_action;
     }
     $actions[] = 'getfields';
+    $actions[] = 'getoptions';
 
     return $actions;
   }
diff --git a/sql/alter.sql b/sql/alter.sql
deleted file mode 100644
index 3d1612f4..00000000
--- a/sql/alter.sql
+++ /dev/null
@@ -1,27 +0,0 @@
-ALTER TABLE `civicrm_data_processor_field`
-CHANGE `name` `name` VARCHAR(255) NOT NULL,
-CHANGE `type` `type` VARCHAR(255) NOT NULL,
-CHANGE `title` `title` VARCHAR(255) NOT NULL;
-
-ALTER TABLE `civicrm_data_processor_filter`
-CHANGE `name` `name` VARCHAR(255) NOT NULL,
-CHANGE `type` `type` VARCHAR(255) NOT NULL,
-CHANGE `title` `title` VARCHAR(255) NOT NULL,
-ADD `weight` int NULL;
-
-UPDATE `civicrm_data_processor_filter` SET `weight` = `id`;
-
-ALTER TABLE `civicrm_data_processor_source`
-CHANGE `name` `name` VARCHAR(255) NOT NULL,
-CHANGE `type` `type` VARCHAR(255) NOT NULL,
-CHANGE `title` `title` VARCHAR(255) NOT NULL,
-CHANGE `join_type` `join_type` VARCHAR(255) NULL,
-ADD `weight` int NULL;
-
-UPDATE `civicrm_data_processor_source` SET `weight` = `id`;
-
-ALTER TABLE `civicrm_data_processor_filter`
-CHANGE `name` `name` VARCHAR(255) NOT NULL,
-CHANGE `type` `type` VARCHAR(255) NOT NULL,
-CHANGE `title` `title` VARCHAR(255) NOT NULL,
-ADD `storage_type` VARCHAR(255) NULL;
\ No newline at end of file
-- 
GitLab