Browse Source

Added some improved warning/error messaging for remove fields and fixed a small bug with web services

Stephen Ficklin 6 years ago
parent
commit
d18597aeaa

+ 73 - 33
tripal_ws/api/tripal_ws.api.inc

@@ -194,6 +194,36 @@ function tripal_remove_site($record_id) {
   return FALSE;
 }
 
+/**
+ * Constructs a URL for a remote Tripal web service.
+ * 
+ * @param $remote_site
+ *   A remote Tripal site object.
+ * @param $path
+ *   The web service path for the content (excluding
+ *   'web-servcies/vX.x/content').  To retrieve the full content listing
+ *   leave this paramter empty.
+ * @param $query
+ *   An query string to appear after the ? in a URL.
+ *   
+ * @return  
+ *   The full URL within the content service.
+ */
+function tripal_build_remote_content_url($remote_site, $path = '', $query = '') {
+  // Build the URL to the remote web services.
+  $ws_version = $remote_site->version;
+  $ws_url = $remote_site->url;
+  $ws_url = trim($ws_url, '/');
+  $ws_url .= '/web-services/content/' . $ws_version . '/' . $path;
+  
+  // Build the Query and make and substitions needed.
+  if ($query) {
+    $ws_url = $ws_url . '?' . $query;
+  }
+  
+  return $ws_url;
+}
+
 /**
  * Makes a request to the "content" service of a remote Tripal web site.
  *
@@ -225,55 +255,61 @@ function tripal_get_remote_content($site_id, $path = '', $query = '') {
     ->fetchObject();
 
   if (!$remote_site) {
-    tripal_report_error('tripal_ws', TRIPAL_ERROR,
-      t('Could not find a remote tripal site using the id provided: !id.',
-        array('!id' => $site_id)));
-    return FALSE;
-  }
-
-  // Build the URL to the remote web services.
-  $ws_version = $remote_site->version;
-  $ws_url = $remote_site->url;
-  $ws_url = trim($ws_url, '/');
-  $ws_url .= '/web-services/content/' . $ws_version . '/' . $path;
-
-  // Build the Query and make and substitions needed.
-  if ($query) {
-    $ws_url = $ws_url . '?' . $query;
-  }
-
-  // TODO: something is wrong here, the query is not being recognized on
-  // the remote Tripal site. It's just returning the default.
+    $data = [
+      'error' => t('Could not find a remote tripal site using the id provided: !id.',
+        array('!id' => $site_id))
+    ];
+    _tripal_report_ws_error($data);
+    return $data;
+  }
+  
+  // Make the remote query.
+  $ws_url = tripal_build_remote_content_url($remote_site, $path, $query);
   $data = drupal_http_request($ws_url);
   if (!$data) {
-    tripal_report_error('tripal_ws', TRIPAL_ERROR,
-        t('Could not connect to the remote web service.'));
-    return FALSE;
+    $data = [
+      'error' => t('Could not connect to the remote web service using the url: !url',
+        ['!url' => $ws_url])
+    ];
+    _tripal_report_ws_error($data);
+    return $data;
   }
 
   // If the data object has an error then this is some sort of
   // connection error (not a Tripal web servcies error).
   if (property_exists($data, 'error')) {
-    $error = '</pre>' . print_r($data->error, TRUE) . '</pre>';
-    tripal_report_error('tripal_ws', TRIPAL_ERROR,
-        'Remote web services reports the following error: !error. Using URL: !url',
-        array('!error' => $error, '!url' => $ws_url));
-    return FALSE;
+    $data = [
+      'error' => $data->error
+    ];
+    _tripal_report_ws_error($data);
+    return $data;
   }
 
   // We got a response, so convert it to a PHP array.
   $data = drupal_json_decode($data->data);
+  
   // Check if there was a Tripal Web Services error.
   if (array_key_exists('error', $data)) {
-    $error = '</pre>' . print_r($data['error'], TRUE) . '</pre>';
-    tripal_report_error('tripal_ws', TRIPAL_ERROR,
-        'Tripal remote web services reports the following error: !error. Using URL: !url',
-        array('!error' => $error, '!url' => $ws_url));
+    _tripal_report_ws_error($data);
   }
 
   return $data;
 }
 
+/**
+ * A helper function for reporting an error when retrieving remote content.
+ * 
+ * @param $data
+ *   A data array containing at a minimum the 'error' key containing the
+ *   error message.
+ */
+function _tripal_report_ws_error($data) {  
+  $error = '</pre>' . print_r($data['error'], TRUE) . '</pre>';
+  tripal_report_error('tripal_ws', TRIPAL_ERROR,
+    'Tripal remote web services reports the following error: !error.',
+    array('!error' => $error));
+}
+
 /**
  * Retrieves the JSON-LD context for any remote Tripal web service.
  *
@@ -495,7 +531,9 @@ function tripal_load_remote_entities($remote_entity_ids, $site_id, $bundle_acces
     '&fields=' . urlencode(implode(",", $field_ids));
 
   $results = tripal_get_remote_content($site_id, $bundle_accession, $query);
-  if (!$results) {
+  
+  // If we encountered an error just return;
+  if (array_key_exists('error', $results)) {
     return FALSE;
   }
 
@@ -563,7 +601,9 @@ function tripal_load_remote_entity($remote_entity_id, $site_id, $bundle_accessio
 
   // Get the remote entity and create the fake entity.
   $remote_entity = tripal_get_remote_content($site_id, $bundle_accession . '/' . $remote_entity_id);
-  if (!$remote_entity) {
+  
+  // If we encountered an error just return;
+  if (array_key_exists('error', $results)) {
     return FALSE;
   }
 

+ 68 - 37
tripal_ws/includes/TripalFields/remote__data/remote__data.inc

@@ -89,7 +89,7 @@ class remote__data extends WebServicesField {
   // should exclude the field from web services or downloads.  An example
   // could be a quick search field that appears on the page that redirects
   // the user but otherwise provides no data.
-  public static $no_data = FALSE;
+  public static $no_data = TRUE;
 
   // Holds an object describing the remote site that tihs field connects to.
   private $remote_site = NULL;
@@ -105,9 +105,8 @@ class remote__data extends WebServicesField {
     // We don't want remote content to be available in web services.  There
     // is an if statement to not show this field in the web services but the
     // entity_load function doesn't know this field shouldn't be loaded so
-    // we need to short-circuit that.
-    $_SERVER['REQUEST_URI'];
-    if (preg_match('/^web-services/', $_SERVER['REQUEST_URI'])) {
+    // we need to short-circuit that.  
+    if (preg_match('/web-services/', $_SERVER['REQUEST_URI'])) {
       $this->loaded_via_ws = TRUE;
       return;
     }
@@ -128,23 +127,25 @@ class remote__data extends WebServicesField {
    * @see WebServicesField::load()
    */
   public function load($entity) {
+    
+    // If this field is being loaded via web services then just return.
+    if ($this->loaded_via_ws == TRUE) {
+      return;
+    }
 
     $field_name = $this->field['field_name'];
     $field_type = $this->field['type'];
 
     // Set some defaults for the empty record.
     $entity->{$field_name}['und'][0] = array(
-      'value' => array(),
-      'remote_entity' => array(),
+      'value' => '',
+      'remote_entity' => NULL,
       'error' => FALSE,
       'warning' => FALSE,
+      'admin_message' => '',
+      'query_str' => '',
     );
 
-    // If this field is being loaded via web services then just return.
-    if ($this->loaded_via_ws == TRUE) {
-      return;
-    }
-
     // Get the query set by the admin for this field and replace any tokens
     $query_str = $this->instance['settings']['data_info']['query'];
     $bundle = tripal_load_bundle_entity(array('name' => $entity->bundle));
@@ -152,24 +153,41 @@ class remote__data extends WebServicesField {
 
     // Make the request.
     $data = $this->makeRemoteRequest($query_str);
-    dpm($data);
     if(!$data){
+      $entity->{$field_name}['und'][0]['value'] = 'ERROR: there was a problem retrieving content for this field.';
+      $entity->{$field_name}['und'][0]['admin_message'] =  "The remote service returned no data.";
+      $entity->{$field_name}['und'][0]['remote_entity'] = NULL;
+      $entity->{$field_name}['und'][0]['error'] = TRUE;
+      $entity->{$field_name}['und'][0]['warning'] = FALSE;
+      $entity->{$field_name}['und'][0]['query_str'] = $this->buildRemoteURL($this->remote_site, $query_str);      
+      return;
+    }
+    // Make sure we didn't have a problem
+    if (array_key_exists('error', $data)) {
+      $entity->{$field_name}['und'][0]['value'] = 'ERROR: there was a problem retrieving content for this field.';
+      $entity->{$field_name}['und'][0]['admin_message'] = "The  content is currently not available because the " .
+          "remote service reported the following error: " . $data['error'] . ".";
+      $entity->{$field_name}['und'][0]['remote_entity'] = NULL;
+      $entity->{$field_name}['und'][0]['error'] = TRUE;
+      $entity->{$field_name}['und'][0]['warning'] = FALSE;
+      $entity->{$field_name}['und'][0]['query_str'] = $this->buildRemoteURL($this->remote_site, $query_str);
       return;
     }
 
-    $total_items = $data['totalItems'];
-    if ($total_items == 0) {
-      $entity->{$field_name}['und'][0]['value'] = 'Content from the remote site is currently not available'  .
-        tripal_set_message("The remote content is currently not available. " .
-          "It could be that remote services are unavailable or the query " .
-          "string does not match content appropriate for this content. Please check.",
-          TRIPAL_WARNING, ['return_html' => TRUE]);
+    $num_items = count($data['member']);
+    if ($num_items == 0) {
+      $entity->{$field_name}['und'][0]['value'] = 'Content is unavailable on the remote service.';
+      $entity->{$field_name}['und'][0]['admin_message'] = "The query to the remote service returned an empty result set. If you " .
+          "think this is an error, please check the query string and the remote service to verify. ";
       $entity->{$field_name}['und'][0]['warning'] = TRUE;
+      $entity->{$field_name}['und'][0]['error'] = FALSE;
       $entity->{$field_name}['und'][0]['remote_entity'] = NULL;
+      $entity->{$field_name}['und'][0]['query_str'] = $this->buildRemoteURL($this->remote_site, $query_str);
+      return;
     }
-
+    
     // Iterate through the members returned and save those for the field.
-    for ($i = 0; $i < $total_items; $i++) {
+    for ($i = 0; $i < $num_items; $i++) {
       $member = $data['member'][$i];
 
       // Get the cotent type and remote entity id
@@ -177,11 +195,6 @@ class remote__data extends WebServicesField {
       $remote_entity_id = $member['@id'];
       $remote_entity_id = preg_replace('/^.*\/(\d+)/', '$1', $remote_entity_id);
 
-      // Save the member information for use later.
-      $entity->{$field_name}['und'][$i]['remote_entity'] = $member;
-      $entity->{$field_name}['und'][$i]['error'] = FALSE;
-      $entity->{$field_name}['und'][$i]['warning'] = FALSE;
-
       // Separate the query_field if it has subfields.
       $rd_field_name = $this->instance['settings']['data_info']['rd_field_name'];
       $subfields = explode(',', $rd_field_name);
@@ -190,22 +203,42 @@ class remote__data extends WebServicesField {
       // Next get the the details about this member.
       $query_field_url =  $content_type . '/' . $remote_entity_id . '/' . $query_field;
       $field_data = $this->makeRemoteRequest($query_field_url);
-
-      if(!$field_data){
-        // If we encounter any type of error, we'll reset the field and return.
-        $entity->{$field_name}['und'][$i]['value'] = 'Unable to retrieve remote content' .
-          tripal_set_message("Something is wrong with the remote site. It returned " . 
-            "a list of matches for the query but did not return details about each match.",
-            TRIPAL_ERROR, ['return_html' => TRUE]);
-        $entity->{$field_name}['und'][$i]['error'] = TRUE;
+      
+      // If we encounter any type of error, we'll reset the field and return.
+      if (array_key_exists('error', $field_data)) {
+        $entity->{$field_name} = [];
+        $entity->{$field_name}['und'][0]['value'] = 'ERROR: there was a problem retrieving secific content for this field.';
+        $entity->{$field_name}['und'][0]['admin_message'] = "While iterating through the list of results, the " .
+          "remote service reported the following error: " . $field_data['error'] . ". " ;
+        $entity->{$field_name}['und'][0]['remote_entity'] = NULL;
+        $entity->{$field_name}['und'][0]['error'] = TRUE;
+        $entity->{$field_name}['und'][0]['warning'] = FALSE;
+        $entity->{$field_name}['und'][0]['query_str'] = $this->buildRemoteURL($this->remote_site, $query_field_url);
         return;
       }
       
       // Set the field data as the value.
       $field_data_type = $field_data['@type'];
       $entity->{$field_name}['und'][$i]['value'] = $field_data;
+      $entity->{$field_name}['und'][$i]['remote_entity'] = $member;
+      $entity->{$field_name}['und'][$i]['error'] = FALSE;
+      $entity->{$field_name}['und'][$i]['warning'] = FALSE;
+      $entity->{$field_name}['und'][$i]['admin_message'] = '';
+      $entity->{$field_name}['und'][$i]['query_str'] = $this->buildRemoteURL($this->remote_site, $query_field_url);;
     }
-   }
+  }
+  
+  /**
+   * Used to build the full URL for the query.
+   */
+  private function buildRemoteURL($remote_site, $query) {
+    $path = $query;
+    $q = '';
+    if (preg_match('/\?/', $query)) {
+      list($path, $q) = explode('?', $query);
+    }
+    return tripal_build_remote_content_url($remote_site, $path, $q);
+  }
    /**
     * Makes a request to a remote Tripal web services site.
     *
@@ -219,8 +252,6 @@ class remote__data extends WebServicesField {
      if (preg_match('/\?/', $query)) {
        list($path, $q) = explode('?', $query);
      }
-     dpm($path);
-     dpm($q);
      $data = tripal_get_remote_content($this->remote_site->id, $path, $q);
 
      return $data;

+ 39 - 85
tripal_ws/includes/TripalFields/remote__data/remote__data_formatter.inc

@@ -11,68 +11,11 @@ class remote__data_formatter extends WebServicesFieldFormatter {
     'setting1' => 'default_value',
   );
   /**
-   * Provides the field's setting form.
-   *
-   * This function corresponds to the hook_field_formatter_settings_form()
-   * function of the Drupal Field API.
-   *
-   * The settings form appears on the 'Manage Display' page of the content
-   * type administration page. This function provides the form that will
-   * appear on that page.
-   *
-   * To add a validate function, please create a static function in the
-   * implementing class, and indicate that this function should be used
-   * in the form array that is returned by this function.
-   *
-   * This form will not be displayed if the formatter_settings_summary()
-   * function does not return anything.
-   *
-   * param $field
-   *   The field structure being configured.
-   * param $instance
-   *   The instance structure being configured.
-   * param $view_mode
-   *   The view mode being configured.
-   * param $form
-   *   The (entire) configuration form array, which will usually have no use
-   *   here.  Typically for reference only.
-   * param $form_state
-   *   The form state of the (entire) configuration form.
-   *
-   * @return
-   *   A Drupal Form array containing the settings form for this field.
-   */
-  public function settingsForm($view_mode, $form, &$form_state) {
-  }
-  /**
-   *  Provides the display for a field
-   *
-   * This function corresponds to the hook_field_formatter_view()
-   * function of the Drupal Field API.
-   *
-   *  This function provides the display for a field when it is viewed on
-   *  the web page.  The content returned by the formatter should only include
-   *  what is present in the $items[$delta]['values] array. This way, the
-   *  contents that are displayed on the page, via webservices and downloaded
-   *  into a CSV file will always be identical.  The view need not show all
-   *  of the data in the 'values' array.
-   *
-   *  @param $element
-   *  @param $entity_type
-   *  @param $entity
-   *  @param $langcode
-   *  @param $items
-   *  @param $display
-   *
-   *  @return
-   *    An element array compatible with that returned by the
-   *    hook_field_formatter_view() function.
+   * @see TripalFieldFormatter::view()
    */
   public function view(&$element, $entity_type, $entity, $langcode, $items, $display) {
     $content = '';
     
-    dpm($items);
-    
     // Get the settings
     $settings = $display['settings'];
     $field_name = $this->field['field_name'];
@@ -108,48 +51,55 @@ class remote__data_formatter extends WebServicesFieldFormatter {
       $value = $item['value'];
       $error = $item['error'];
       $warning = $item['warning'];
-      if ($error) {
-        $rows[] = [$value];
-        continue;
-      }      
-      if ($warning) {
+      
+      // If there is an error or warning then clear the cache for this field
+      // so that next time the page is loaded it will try to reload again.
+      if ($error or $warning) {
+        $cid = "field:TripalEntity:" . $entity->id . ':' . $field_name;
+        cache_clear_all($cid, 'cache_field');
+        if ($item['admin_message']) {
+          $severity = TRIPAL_ERROR;
+          if ($warning) {
+            $severity = TRIPAL_WARNING;
+          }
+          $value .= tripal_set_message($item['admin_message'] . 'The query URL was: ' . l($item['query_str'], $item['query_str'], ['attributes' => ['target' => '_blank']]),
+            $severity, ['return_html' => TRUE]);
+        }
         $rows[] = [$value];
         continue;
       }
       
-      
       $remote_entity_label = array_key_exists('label', $item) ? $item['remote_entity']['label'] : '';
       $remote_entity_page = $item['remote_entity']['ItemPage'];
-      $link = t('View !data on %site',
-          array('!data' => l('this content', $remote_entity_page, array('attributes' => array('target' => '_blank'))),
+      $remote_entity_link = t('View !data on %site',
+          array('!data' => l('this data', $remote_entity_page, array('attributes' => array('target' => '_blank'))),
             '%site' => $site->name));
 
-
       // If this is a collection then handle it as a list of members.
-      if (is_array($value)) {
-        if (array_key_exists('members', $value)) {
-          foreach ($value['members'] as $subvalue) {
-            $subvalue = $this->refineSubValue($subvalue, $subfields, $header_label);
-            $rows[] = array($subvalue);
-          }
+      if (array_key_exists('members', $value)) {
+        foreach ($value['members'] as $subvalue) {
+          $subvalue = $this->refineSubValue($subvalue, $subfields, $header_label);
+          $rows[] = array($subvalue);
+        }
+      }
+      else {
+        if (count($subfields) > 0) {
+          $subvalue = $this->refineSubValue($value, $subfields, $header_label);
+          $rows[] = array($subvalue);
         }
         else {
-          if (count($subfields) > 0) {
-            $subvalue = $this->refineSubValue($value, $subfields, $header_label);
-            $rows[] = array($subvalue);
+          if (array_key_exists($flabel, $value)) {
+            $rows[] = array($value[$flabel]);
           }
           else {
-            if (array_key_exists($flabel, $value)) {
-              $rows[] = array($value[$flabel]);
-            }
-            else {
-              $value['Link'] = l('View content on ' . $site->name, $remote_entity_page, array('attributes' => array('target' => '_blank')));
-              $rows[] = array($value);
-            }
+            $value['Link'] = l('View content on ' . $site->name, $remote_entity_page, array('attributes' => array('target' => '_blank')));
+            $rows[] = array($value);
           }
         }
       }
     }
+    
+    // TODO: we need to handle paged elements.
 
     $has_sub_tables = FALSE;
     for ($i = 0; $i < count($rows); $i++) {
@@ -191,11 +141,15 @@ class remote__data_formatter extends WebServicesFieldFormatter {
     }
     
     $content .= '<p>';
+
+    $content .=  t('This content provided by !site.',
+      array('!site' => l($site->name, $site->url, array('attributes' => array("target" => '_blank')))));
     if (is_object($site_logo)) {
       $content .= '<img class="tripal-remote--data-field-logo" src="' . file_create_url($site_logo->uri) . '"><br/>';
     }
-    $content .=  t('This content provided by !site.',
-      array('!site' => l($site->name, $site->url, array('attributes' => array("target" => '_blank')))));
+    if (count($items) == 1) {
+      $content .= $remote_entity_link;
+    }
     $content .= '</p>';
     
     // Return the content for this field.

+ 14 - 8
tripal_ws/includes/TripalWebService/TripalContentService_v0_1.inc

@@ -71,7 +71,7 @@ class TripalContentService_v0_1 extends TripalWebService {
         // Check the label by replacing non alpha-numeric characters with 
         // an underscore and is case-insensitive
         $label = preg_replace('/[^\w]/', '_', $bundle->label);
-        if (preg_match("/$label/i", $ctype)) {
+        if (preg_match("/^$label$/i", $ctype)) {
           $ctype = $bundle->label;
           $found = TRUE;
         }
@@ -278,8 +278,16 @@ class TripalContentService_v0_1 extends TripalWebService {
       }
       // Get the information about this field.
       $field = field_info_field($field_name);
+      
+      // If the field has the $no_data turned on then we should exclude it.
+      if (tripal_load_include_field_class($field['type'])) {
+        $field_class = $field['type'];
+        if ($field_class::$no_data) {
+          return;
+        }
+      }
 
-      // Skip the remote__data field that is provided by the tripal_Ws
+      // Skip the remote__data field that is provided by the tripal_ws
       // module.
       if ($field['type'] == 'remote__data') {
         continue;
@@ -950,12 +958,10 @@ class TripalContentService_v0_1 extends TripalWebService {
       $entity = $entity[$entity_id];
 
       // Add in any requested fields
-      foreach ($fields as $expfield) {
-        if (array_key_exists($expfield, $add_fields)) {
-          $this->addEntityField($member, $add_fields[$expfield]['term'], $entity,
-              $bundle, $add_fields[$expfield]['field'], $add_fields[$expfield]['instance'],
-              $service_path);
-        }
+      foreach ($add_fields as $expfield => $expfield_details) {
+        $this->addEntityField($member, $expfield_details['term'], $entity,
+            $bundle, $expfield_details['field'], $expfield_details['instance'],
+            $service_path);
       }
       $this->resource->addMember($member);
     }