From 871a281636bc45d33f2c97630d15be10c7de4d10 Mon Sep 17 00:00:00 2001 From: Abdullah Almsaeed Date: Wed, 27 Feb 2019 08:43:26 -0500 Subject: [PATCH 1/2] Add an API endpoint to share categories --- includes/Common/BundleHelper.php | 48 +++ includes/Common/FieldsHelper.php | 7 + ...pal_elasticsearch.advanced_search.form.inc | 50 ++-- tripal_elasticsearch.ws.inc | 273 ++++++++++-------- 4 files changed, 241 insertions(+), 137 deletions(-) create mode 100644 includes/Common/FieldsHelper.php diff --git a/includes/Common/BundleHelper.php b/includes/Common/BundleHelper.php index 97be6b38..430cb3d7 100644 --- a/includes/Common/BundleHelper.php +++ b/includes/Common/BundleHelper.php @@ -135,4 +135,52 @@ public function getFieldsByBundleTerm($cv_name, $accession) { return $fields; } + + /** + * Gets node types in the same format as bundles. + * + * @return array + * A list of node types. + */ + public function getNodeTypes() { + $node_types = db_select('node_type', 'nt') + ->fields('nt', ['type', 'name']) + ->execute() + ->fetchAll(); + + $categories = []; + + foreach ($node_types as $node_type) { + $categories[] = [ + 'name' => $node_type->type, + 'label' => $node_type->name, + 'type' => 'node', + 'term_id' => NULL, + 'cv_term' => NULL, + 'accession' => NULL, + 'fields' => [], + ]; + } + + return $categories; + } + + /** + * Get a list of bundles and attach fields. + * + * @return array + * A list of bundles with fields attached. + */ + public function getBundlesWithFields() { + $bundles = $this->getBundles(); + + $categories = []; + foreach ($bundles as $bundle) { + $fields = $this->getFieldsByBundle($bundle); + $bundle->fields = $fields; + $categories[] = $bundle; + } + + return $categories; + } } diff --git a/includes/Common/FieldsHelper.php b/includes/Common/FieldsHelper.php new file mode 100644 index 00000000..c0d94c7e --- /dev/null +++ b/includes/Common/FieldsHelper.php @@ -0,0 +1,7 @@ + trim($_GET['term'] ?? $form_vals['term'] ?? ''), + 'category' => $_GET['category'] ?? $form_vals['category'] ?? '', + 'field' => $_GET['field'] ?? $form_vals['field'] ?? '', + ]; + $helper = new \ES\Common\BundleHelper(); $form['wrapper'] = [ - '#prefix' => '
', + '#prefix' => '
', '#suffix' => '
', ]; - $category = trim( - $_GET['category'] ?? $form_state['values']['category'] ?? '' - ); - $form['wrapper']['category'] = [ '#type' => 'select', '#options' => ['' => 'Any Category'] + $bundles, @@ -32,11 +36,11 @@ function tripal_elasticsearch_advanced_search_form($form, &$form_state) { 'callback' => 'tripal_elasticsearch_advanced_search_form_callback', 'wrapper' => 'advanced_search_wrapper', ], - '#default_value' => $category, + '#default_value' => $values['category'], ]; - if (!empty($category)) { - $fields = $helper->getFieldsByBundle($helper->getBundleByName($category)); + if (!empty($values['category'])) { + $fields = $helper->getFieldsByBundle($helper->getBundleByName($values['category'])); $field_options = [ '' => 'Any Field', ]; @@ -48,13 +52,13 @@ function tripal_elasticsearch_advanced_search_form($form, &$form_state) { $form['wrapper']['field'] = [ '#type' => 'select', '#options' => $field_options, - '#default_value' => trim($_GET['field'] ?? ''), + '#default_value' => $values['field'], ]; } $form['wrapper']['term'] = [ '#type' => 'textfield', - '#default_value' => $_GET['term'] ?? '', + '#default_value' => $values['term'], ]; $form['wrapper']['submit'] = [ @@ -64,11 +68,9 @@ function tripal_elasticsearch_advanced_search_form($form, &$form_state) { $form['#method'] = 'GET'; - //if (isset($_GET['term']) && !empty($_GET['term'])) { $form['wrapper']['results'] = [ - '#markup' => tripal_elasticsearch_advanced_search_results($_GET), + '#markup' => tripal_elasticsearch_advanced_search_results($values), ]; - //} return $form; } @@ -86,7 +88,7 @@ function tripal_elasticsearch_advanced_search_results(array $values) { try { $instance = new \ES\Common\Instance(); $results = tripal_elasticsearch_perform_advanced_search($values, 15); - if($results['total'] == 0 && !empty($values['term'])) { + if ($results['total'] == 0 && !empty($values['term'])) { $values['term'] = strtolower($values['term']); $results = tripal_elasticsearch_perform_advanced_search($values, 15); } @@ -101,13 +103,19 @@ function tripal_elasticsearch_advanced_search_results(array $values) { ); } - $content = theme('elasticsearch_results_header', [ - 'page' => $results['page'], - 'total' => $results['total'], - 'pages' => $results['pages'], - 'time' => $results['time'] - ]); - $content .= tripal_elasticsearch_get_website_search_result_table($hits, false); + $content = theme( + 'elasticsearch_results_header', + [ + 'page' => $results['page'], + 'total' => $results['total'], + 'pages' => $results['pages'], + 'time' => $results['time'], + ] + ); + $content .= tripal_elasticsearch_get_website_search_result_table( + $hits, + FALSE + ); $content .= $results['pager']; return $content; diff --git a/tripal_elasticsearch.ws.inc b/tripal_elasticsearch.ws.inc index fbfd2757..6de66128 100644 --- a/tripal_elasticsearch.ws.inc +++ b/tripal_elasticsearch.ws.inc @@ -14,12 +14,12 @@ function tripal_elasticsearch_api_v1_status() { try { $params['v'] = TRUE; - $es = new \ES\Common\Instance(); - $health = current($es->client->cat()->health($params)); + $es = new \ES\Common\Instance(); + $health = current($es->client->cat()->health($params)); } catch (Exception $exception) { return \ES\Common\Response::error( [ - 'status' => 'Inactive', + 'status' => 'Inactive', 'healthy' => FALSE, ] ); @@ -28,7 +28,7 @@ function tripal_elasticsearch_api_v1_status() { if (empty($health)) { return \ES\Common\Response::error( [ - 'status' => 'Inactive', + 'status' => 'Inactive', 'healthy' => FALSE, ] ); @@ -36,7 +36,7 @@ function tripal_elasticsearch_api_v1_status() { return \ES\Common\Response::success( [ - 'status' => 'Active', + 'status' => 'Active', 'healthy' => TRUE, ] ); @@ -78,7 +78,10 @@ function tripal_elasticsearch_api_v1_remote_status($remote_id) { try { $response = \ES\Common\Request::get($url); } catch (Exception $exception) { - return \ES\Common\Response::error(['server' => $exception->getMessage()], 500); + return \ES\Common\Response::error( + ['server' => $exception->getMessage()], + 500 + ); } return \ES\Common\Response::success($response->data); @@ -95,7 +98,9 @@ function tripal_elasticsearch_api_v1_search($remote_host_id) { $remote_host_id = abs(intval(trim($remote_host_id))); if (!isset($_GET['terms'])) { - return \ES\Common\Response::error(['terms' => ['Please provide search terms']]); + return \ES\Common\Response::error( + ['terms' => ['Please provide search terms']] + ); } $size = 0; @@ -105,37 +110,43 @@ function tripal_elasticsearch_api_v1_search($remote_host_id) { if ($remote_host_id === 0) { $response = tripal_elasticsearch_api_v1_local_search(FALSE); - $host = tripal_elasticsearch_get_local_server_entry(); + $host = tripal_elasticsearch_get_local_server_entry(); } else { $host = db_query( 'SELECT * FROM {tripal_elasticsearch_servers} WHERE id=:id', [':id' => $remote_host_id] )->fetchObject(); - $url = rtrim(trim($host->url), '/') . '/elasticsearch/api/v1/local-search'; + $url = rtrim(trim($host->url), '/') . '/elasticsearch/api/v1/local-search'; try { $response = \ES\Common\Request::get( - $url, [ - 'terms' => $_GET['terms'], + $url, + [ + 'terms' => $_GET['terms'], 'category' => isset($_GET['category']) ? $_GET['category'] : '', - 'size' => $size, + 'size' => $size, ] ); } catch (Exception $exception) { - return \ES\Common\Response::error(['server' => $exception->getMessage()], 500); + return \ES\Common\Response::error( + ['server' => $exception->getMessage()], + 500 + ); } } $markup = tripal_elasticsearch_get_website_search_result_table( - $response->data->results, FALSE, $host->url + $response->data->results, + FALSE, + $host->url ); // Render results into fields and send them back return \ES\Common\Response::success( [ - 'count' => $response->data->count, - 'url' => $response->data->url, + 'count' => $response->data->count, + 'url' => $response->data->url, 'markup' => $markup, ] ); @@ -146,7 +157,7 @@ function tripal_elasticsearch_api_v1_search($remote_host_id) { * This method is normally called by other sites looking for * results from our site. * - * @param bool $http Whether this is an http request or an internal + * @param bool $http Whether this is an http request or an internal * request. (TRUE for http) * Set this option to FALSE to retrieve an object instead * of printing a JSON string. @@ -159,13 +170,14 @@ function tripal_elasticsearch_api_v1_local_search($http = TRUE) { if (!$exposed) { return \ES\Common\Response::error( '403 Forbidden. The request index is not available - for cross site search. Contact the admin to mark the index as public', 403 + for cross site search. Contact the admin to mark the index as public', + 403 ); } // Validate the request - $terms = isset($_GET['terms']) ? $_GET['terms'] : ''; - $size = isset($_GET['size']) ? abs(intval($_GET['size'])) : 0; + $terms = isset($_GET['terms']) ? $_GET['terms'] : ''; + $size = isset($_GET['size']) ? abs(intval($_GET['size'])) : 0; $category = isset($_GET['category']) ? $_GET['category'] : NULL; if ($size !== 0 && $size > 20) { @@ -181,7 +193,7 @@ function tripal_elasticsearch_api_v1_local_search($http = TRUE) { } try { - $es = new \ES\Common\Instance(); + $es = new \ES\Common\Instance(); $results = $es->searchWebIndices($terms, $size, $category); } catch (Exception $exception) { return \ES\Common\Response::error($exception->getMessage(), 500); @@ -189,18 +201,23 @@ function tripal_elasticsearch_api_v1_local_search($http = TRUE) { return \ES\Common\Response::success( array_merge( - $results, [ + $results, + [ 'url' => url( - rtrim($base_url, '/') . '/tripal_elasticsearch/search_website/' - . $category, [ + rtrim( + $base_url, + '/' + ) . '/tripal_elasticsearch/search_website/' . $category, + [ 'query' => [ 'search_box' => $terms, - 'category' => $category, + 'category' => $category, ], ] ), ] - ), $http + ), + $http ); } @@ -212,15 +229,14 @@ function tripal_elasticsearch_api_v1_local_search($http = TRUE) { * * @return array|bool|object */ -function tripal_elasticsearch_api_v1_table_index_search( - $index_name, $remote_host_id -) { +function tripal_elasticsearch_api_v1_table_index_search($index_name, $remote_host_id) { $remote_id = abs(intval(trim($remote_host_id))); if (!tripal_elasticsearch_is_index_exposed($index_name)) { return \ES\Common\Response::error( '403 Forbidden. The request index is not available - for cross site search. Contact the admin to mark the index as public', 403 + for cross site search. Contact the admin to mark the index as public', + 403 ); } @@ -228,7 +244,8 @@ function tripal_elasticsearch_api_v1_table_index_search( $host = tripal_elasticsearch_get_local_server_entry(); try { $results = tripal_elasticsearch_api_v1_table_index_local_search( - $index_name, FALSE + $index_name, + FALSE ); } catch (Exception $exception) { return \ES\Common\Response::error( @@ -252,8 +269,10 @@ function tripal_elasticsearch_api_v1_table_index_search( ); } - $url = trim($host->url, '/') . '/elasticsearch/api/v1/index-search/' - . $index_name; + $url = trim( + $host->url, + '/' + ) . '/elasticsearch/api/v1/index-search/' . $index_name; try { $data = $_GET; if (isset($data['q'])) { @@ -273,13 +292,15 @@ function tripal_elasticsearch_api_v1_table_index_search( } $markup = tripal_elasticsearch_results_formatter( - $results->data->results, $index_name, $host + $results->data->results, + $index_name, + $host ); return \ES\Common\Response::success( [ - 'url' => $results->data->url, + 'url' => $results->data->url, 'markup' => $markup, - 'count' => $results->data->count, + 'count' => $results->data->count, ] ); } @@ -293,12 +314,10 @@ function tripal_elasticsearch_api_v1_table_index_search( * @return array|bool|object * @throws \Exception */ -function tripal_elasticsearch_api_v1_table_index_local_search( - $index_name, $http = TRUE -) { +function tripal_elasticsearch_api_v1_table_index_local_search($index_name, $http = TRUE) { try { - $es = new \ES\Common\Instance(); - $fields = $es->getIndexFields($index_name); + $es = new \ES\Common\Instance(); + $fields = $es->getIndexFields($index_name); $field_content_pairs = []; foreach ($fields as $field) { @@ -309,10 +328,10 @@ function tripal_elasticsearch_api_v1_table_index_local_search( if (function_exists( 'tripal_elasticsearch_' . $index_name . '_query_mapper' - ) - ) { + )) { $query = call_user_func( - 'tripal_elasticsearch_' . $index_name . '_query_mapper', $_GET + 'tripal_elasticsearch_' . $index_name . '_query_mapper', + $_GET ); } else { @@ -335,7 +354,7 @@ function tripal_elasticsearch_api_v1_table_index_local_search( } } - $count = $results->count(); + $count = $results->count(); $search_results = $results->search(); // Get the url for the index @@ -351,11 +370,14 @@ function tripal_elasticsearch_api_v1_table_index_local_search( unset($query['q']); return \ES\Common\Response::success( [ - 'count' => $count, - 'url' => $url ? url($base_url . '/' . $url, ['query' => $query]) - : $base_url, + 'count' => $count, + 'url' => $url ? url( + $base_url . '/' . $url, + ['query' => $query] + ) : $base_url, 'results' => $search_results, - ], $http + ], + $http ); } @@ -367,17 +389,14 @@ function tripal_elasticsearch_api_v1_table_index_local_search( * * @return mixed|string */ -function tripal_elasticsearch_results_formatter( - $search_results, $index_name = NULL, $host = NULL -) { - if ($index_name !== NULL - && function_exists( +function tripal_elasticsearch_results_formatter($search_results, $index_name = NULL, $host = NULL) { + if ($index_name !== NULL && function_exists( 'tripal_elasticsearch_' . $index_name . '_results_formatter' - ) - ) { + )) { return call_user_func( 'tripal_elasticsearch_' . $index_name . '_results_formatter', - $search_results, $host + $search_results, + $host ); } @@ -396,10 +415,12 @@ function tripal_elasticsearch_results_formatter( } return theme( - 'table', [ - 'header' => isset($search_results[0]) ? array_keys($search_results[0]) - : [], - 'rows' => $rows, + 'table', + [ + 'header' => isset($search_results[0]) ? array_keys( + $search_results[0] + ) : [], + 'rows' => $rows, ] ); } @@ -425,7 +446,7 @@ function elasticsearch_recursive_implode($glue, $array) { /** * Flatten a nested array. * - * @param array $array The input nested array + * @param array $array The input nested array * @param array $result The resulting flattened array */ function elasticsearch_recursive_flatten($array, &$result) { @@ -447,25 +468,15 @@ function elasticsearch_recursive_flatten($array, &$result) { /** * Get local categories. * - * @return array|bool|object + * @return array */ function tripal_elasticsearch_api_v1_categories() { - return \ES\Common\Response::success( - [ - [ - 'label' => 'Gene', - 'table' => 'chado.feature', - ], - [ - 'label' => 'mRNA', - 'table' => 'chado.feature', - ], - [ - 'label' => 'Organism', - 'table' => 'chado.organism', - ], - ] - ); + $helper = new \ES\Common\BundleHelper(); + + $categories = $helper->getBundlesWithFields(); + $categories = array_merge($categories, $helper->getNodeTypes()); + + return \ES\Common\Response::success($categories); } /** @@ -479,8 +490,8 @@ function tripal_elasticsearch_api_v1_categories() { function tripal_elasticsearch_cross_site_search_form($form, &$form_state) { global $base_url; - $remotes = - db_query('SELECT * FROM {tripal_elasticsearch_servers}')->fetchAll(); + $remotes = db_query('SELECT * FROM {tripal_elasticsearch_servers}')->fetchAll( + ); // Add local server as the first remote array_unshift($remotes, tripal_elasticsearch_get_local_server_entry()); @@ -488,7 +499,7 @@ function tripal_elasticsearch_cross_site_search_form($form, &$form_state) { tripal_elasticsearch_load_logos($remotes); $form['options'] = [ - '#type' => 'fieldset', + '#type' => 'fieldset', '#attributes' => [ 'class' => [ 'container-inline', @@ -499,46 +510,54 @@ function tripal_elasticsearch_cross_site_search_form($form, &$form_state) { $default_category = ['Any Type' => 'Any Type']; try { - $es = new \ES\Common\Instance(); + $es = new \ES\Common\Instance(); $categories = drupal_map_assoc($es->getAllCategories()); } catch (Exception $exception) { $categories = []; } $form['options']['category'] = [ - '#type' => 'select', - '#attributes' => [ + '#type' => 'select', + '#attributes' => [ 'id' => 'tripal-elasticsearch-search-category', ], - '#options' => array_merge($default_category, $categories), + '#options' => array_merge($default_category, $categories), '#default_value' => 'Any Type', - '#required' => TRUE, + '#required' => TRUE, ]; $form['options']['search_term'] = [ - '#type' => 'textfield', - '#size' => '50', + '#type' => 'textfield', + '#size' => '50', '#attributes' => [ 'placeholder' => t('E,g. Fraxinus Excelsior mRNA'), - 'id' => 'tripal-elasticsearch-search-field', + 'id' => 'tripal-elasticsearch-search-field', ], - '#required' => TRUE, + '#required' => TRUE, ]; $form['options']['search_button'] = [ - '#type' => 'submit', - '#value' => t('Search'), + '#type' => 'submit', + '#value' => t('Search'), '#attributes' => [ 'id' => 'tripal-elasticsearch-search-button', ], ]; + try { + $logos_table = tripal_elasticsearch_make_logos_table($remotes); - $logos_table = tripal_elasticsearch_make_logos_table($remotes); - - $form['results_block'] = [ - '#type' => 'markup', - '#markup' => '
'.$logos_table.'
', - ]; + $form['results_block'] = [ + '#type' => 'markup', + '#markup' => '
' . $logos_table . '
', + ]; + } catch (Exception $exception) { + watchdog( + 'tripal_elasticsearch', + $exception->getMessage(), + [], + WATCHDOG_ERROR + ); + } $form['#atached']['js'][] = [ 'data' => drupal_add_js( @@ -548,15 +567,19 @@ function tripal_elasticsearch_cross_site_search_form($form, &$form_state) { ]; drupal_add_js( - drupal_get_path('module', 'tripal_elasticsearch') - . '/js/tripal_elasticsearch.js' + drupal_get_path( + 'module', + 'tripal_elasticsearch' + ) . '/js/tripal_elasticsearch.js' ); + drupal_add_js( [ 'remotes' => $remotes, - 'action' => 'setupSearchPage', - 'base' => $base_url, - ], 'setting' + 'action' => 'setupSearchPage', + 'base' => $base_url, + ], + 'setting' ); return $form; @@ -571,17 +594,17 @@ function tripal_elasticsearch_get_local_server_entry() { global $base_url; return (object) [ - 'id' => 0, - 'label' => variable_get('site_name', 'Drupal'), + 'id' => 0, + 'label' => variable_get('site_name', 'Drupal'), 'description' => '', - 'url' => $base_url, + 'url' => $base_url, ]; } /** * Checks if an index is exposed. * - * @param $index_name + * @param string $index_name The name of index. * * @return bool */ @@ -605,6 +628,11 @@ function tripal_elasticsearch_is_index_exposed($index_name) { return $exposed; } +/** + * Loads and attaches logos to the remote sites. + * + * @param array $remotes The list of remote sites. + */ function tripal_elasticsearch_load_logos(array &$remotes) { foreach ($remotes as $remote) { if ($remote->id === 0) { @@ -612,29 +640,42 @@ function tripal_elasticsearch_load_logos(array &$remotes) { continue; } - $remote->logo_url = - $remote->logo ? file_create_url(file_load($remote->logo)->uri) : ''; + $remote->logo_url = $remote->logo ? file_create_url( + file_load($remote->logo)->uri + ) : ''; } } +/** + * Get a table of logos. + * + * @param array $remotes A list of registered remote sites. + * + * @return string + * The table. + * @throws \Exception + */ function tripal_elasticsearch_make_logos_table($remotes) { $header = ['Logo', 'Database']; - $rows = []; + $rows = []; foreach ($remotes as $remote) { $img = ''; if (!empty($remote->logo_url)) { - $img = ''; + $img = ''; } - $rows[] = [$img, l($remote->label, !empty($remote->url) ? $remote->url : '')]; + $rows[] = [ + $img, + l($remote->label, !empty($remote->url) ? $remote->url : ''), + ]; } return '

Available Databases

' . theme( - 'table', [ + 'table', + [ 'header' => $header, - 'rows' => $rows, + 'rows' => $rows, ] ); } From 4300c712a457f7bd5b3905a75fc97e1471a36314 Mon Sep 17 00:00:00 2001 From: Abdullah Almsaeed Date: Wed, 27 Feb 2019 08:48:34 -0500 Subject: [PATCH 2/2] Add tests for get bundles with fields --- tests/Feature/BundleHelperTest.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/Feature/BundleHelperTest.php b/tests/Feature/BundleHelperTest.php index a4a409d0..5de34bc2 100644 --- a/tests/Feature/BundleHelperTest.php +++ b/tests/Feature/BundleHelperTest.php @@ -68,4 +68,19 @@ public function testGettingFieldsByTerm() { $this->assertObjectHasAttribute('name', $field); $this->assertObjectHasAttribute('label', $field); } + + /** @test */ + public function testGettingBundlesWithFields() { + $helper = new BundleHelper(); + + $bundles = $helper->getBundlesWithFields(); + $this->assertNotEmpty($bundles); + + $bundle = $bundles[0]; + $this->assertObjectHasAttribute('fields', $bundle); + $this->assertObjectHasAttribute('name', $bundle); + $this->assertObjectHasAttribute('label', $bundle); + $this->assertObjectHasAttribute('cv_name', $bundle); + $this->assertObjectHasAttribute('accession', $bundle); + } }