diff --git a/app/assets/javascripts/controllers/queryParams.js b/app/assets/javascripts/controllers/queryParams.js index cb3b6c683..618ce44e8 100644 --- a/app/assets/javascripts/controllers/queryParams.js +++ b/app/assets/javascripts/controllers/queryParams.js @@ -2,11 +2,11 @@ angular.module('QuepidApp') .controller('QueryParamsCtrl', [ - '$scope','$location', '$window', - 'esUrlSvc','caseTryNavSvc', + '$scope', + 'esUrlSvc','caseTryNavSvc','searchEndpointSvc', 'TryFactory', - function ($scope, $location, $window, - esUrlSvc, caseTryNavSvc, + function ($scope, + esUrlSvc, caseTryNavSvc,searchEndpointSvc, TryFactory) { $scope.qp = {}; @@ -20,7 +20,8 @@ angular.module('QuepidApp') $scope.showESTemplateWarning = false; $scope.showTLSChangeWarning = false; - + + $scope.searchEndpoints = searchEndpointSvc.list(); $scope.validateSearchEngineUrl = function() { if (!angular.isUndefined($scope.settings.searchUrl)){ diff --git a/app/assets/javascripts/controllers/wizardModal.js b/app/assets/javascripts/controllers/wizardModal.js index 993da3e8f..c5a726cec 100644 --- a/app/assets/javascripts/controllers/wizardModal.js +++ b/app/assets/javascripts/controllers/wizardModal.js @@ -7,12 +7,12 @@ angular.module('QuepidApp') '$rootScope', '$scope', '$uibModalInstance', '$log', '$window', '$q', '$location', 'WizardHandler', 'settingsSvc', 'SettingsValidatorFactory', - 'docCacheSvc', 'queriesSvc', 'caseTryNavSvc', 'caseSvc', 'userSvc', + 'docCacheSvc', 'queriesSvc', 'caseTryNavSvc', 'caseSvc', 'userSvc','searchEndpointSvc', function ( $rootScope, $scope, $uibModalInstance, $log, $window, $q, $location, WizardHandler, settingsSvc, SettingsValidatorFactory, - docCacheSvc, queriesSvc, caseTryNavSvc, caseSvc, userSvc + docCacheSvc, queriesSvc, caseTryNavSvc, caseSvc, userSvc, searchEndpointSvc ) { $log.debug('Init Wizard settings ctrl'); $scope.wizardSettingsModel = {}; @@ -20,14 +20,38 @@ angular.module('QuepidApp') $scope.wizardSettingsModel.settingsId = function() { return settingsSvc.settingsId(); }; + + $scope.haveSearchEndpoints = false; + + + //$scope.wizardSettingsModel.searchEndpoints = function() { + //return "hi"; + // return searchEndpointSvc.list(); + //} + + searchEndpointSvc.list() + .then(function() { + console.log("okay, got end points") + $scope.searchEndpoints = searchEndpointSvc.searchEndpoints; + if ($scope.searchEndpoints.length > 0) { + $scope.haveSearchEndpoints = true; + } + else { + $scope.haveSearchEndpoints = false; + } + }); + + $scope.filterEndpointsByType = function() { + return searchEndpointSvc.filteredEndpoints($scope.pendingWizardSettings.searchEngine); + } - // used when you swap radio buttons for the search engine. - $scope.changeSearchEngine = function() { + // used when we first launch the wizard, and it handles reloading from http to https + $scope.updateSettingsDefaults = function() { if (angular.isUndefined($scope.pendingWizardSettings)){ - // When we run the case wizard, we assume that you want to use our Solr based TMDB demo setup. - // We then give you options to change from there. - $scope.pendingWizardSettings = angular.copy(settingsSvc.tmdbSettings['solr']); + // When we run the case wizard, we assume that you want to use our Solr based TMDB demo setup. + // We then give you options to change from there. + $scope.pendingWizardSettings = angular.copy(settingsSvc.tmdbSettings['solr']); } var settings = settingsSvc.pickSettingsToUse($scope.pendingWizardSettings.searchEngine, $scope.pendingWizardSettings.searchUrl); $scope.pendingWizardSettings.additionalFields = settings.additionalFields; @@ -38,7 +62,9 @@ angular.module('QuepidApp') $scope.pendingWizardSettings.customHeaders = settings.customHeaders; $scope.pendingWizardSettings.queryParams = settings.queryParams; $scope.pendingWizardSettings.titleField = settings.titleField; - $scope.pendingWizardSettings.urlFormat = settings.urlFormat; + $scope.pendingWizardSettings.urlFormat = settings.urlFormat; + $scope.pendingWizardSettings.searchEndpointId = null; + $scope.searchEndpoints = searchEndpointSvc.list(); var quepidStartsWithHttps = $location.protocol() === 'https'; @@ -54,11 +80,26 @@ angular.module('QuepidApp') $scope.pendingWizardSettings.searchUrl = settings.searchUrl; } + // if we have restarted the wizard, then grab the searchUrl, searchEngine, apiMethod, + // and caseName from the params and override the default values. + // We should pass this stuff in externally, not do it here. + if (angular.isDefined($location.search().searchEngine)){ + $scope.pendingWizardSettings.searchEngine = $location.search().searchEngine; + } + if (angular.isDefined($location.search().searchUrl)){ + $scope.pendingWizardSettings.searchUrl = $location.search().searchUrl; + } + if (angular.isDefined($location.search().caseName)){ + $scope.pendingWizardSettings.caseName = $location.search().caseName; + } + if (angular.isDefined($location.search().apiMethod)){ + $scope.pendingWizardSettings.apiMethod = $location.search().apiMethod; + } $scope.reset(); }; - // used when we first launch the wizard, and it handles reloading from http to https - $scope.updateSettingsDefaults = function() { + // used when you swap radio buttons for the search engine. + $scope.changeSearchEngine = function() { if (angular.isUndefined($scope.pendingWizardSettings)){ // When we run the case wizard, we assume that you want to use our Solr based TMDB demo setup. @@ -90,28 +131,19 @@ angular.module('QuepidApp') $scope.pendingWizardSettings.searchUrl = settings.searchUrl; } - // if we have restarted the wizard, then grab the searchUrl, searchEngine, apiMethod, - // and caseName from the params and override the default values. - // We should pass this stuff in externally, not do it here. - if (angular.isDefined($location.search().searchEngine)){ - $scope.pendingWizardSettings.searchEngine = $location.search().searchEngine; -// $scope.pendingWizardSettings.queryParams = settingsSvc.defaults[$scope.pendingWizardSettings.searchEngine].queryParams; - } - if (angular.isDefined($location.search().searchUrl)){ - $scope.pendingWizardSettings.searchUrl = $location.search().searchUrl; - } - if (angular.isDefined($location.search().caseName)){ - $scope.pendingWizardSettings.caseName = $location.search().caseName; - } - if (angular.isDefined($location.search().apiMethod)){ - $scope.pendingWizardSettings.apiMethod = $location.search().apiMethod; - } - $scope.reset(); }; - + + // used when you click the accordion for new search endpoint + $scope.switchToCreateNewSearchEndpoint = function() { + console.log("I was just clicked"); + $scope.pendingWizardSettings.searchEndpointId = null; + + }; + $scope.validate = validate; $scope.skipValidation = skipValidation; + $scope.pickSearchEndpoint = pickSearchEndpoint; $scope.setupDefaults = setupDefaults; $scope.submit = submit; $scope.reset = reset; @@ -121,7 +153,6 @@ angular.module('QuepidApp') $scope.validateHeaders = validateHeaders; $scope.searchFields = []; - $scope.extractSolrConfigApiUrl = function(searchUrl) { return searchUrl.substring(0, searchUrl.lastIndexOf('/')) + '/config'; }; @@ -146,6 +177,29 @@ angular.module('QuepidApp') WizardHandler.wizard().next(); } } + + function pickSearchEndpoint() { + console.log("In pickSearchEndpoint"); + searchEndpointSvc.get($scope.pendingWizardSettings.searchEndpointId) + .then( function (searchEndpoint){ + console.log("Got a search endpoint"); + + // Maybe we should refactor to have searchEndpoint a first class object and use it + // Everywhere? + + $scope.pendingWizardSettings.searchEngine = searchEndpoint.searchEngine; + $scope.pendingWizardSettings.searchUrl = searchEndpoint.endpointUrl; + $scope.pendingWizardSettings.apiMethod = searchEndpoint.apiMethod; + $scope.pendingWizardSettings.customHeaders = searchEndpoint.customHeaders; + + // Are there any places wehre we do validate(true) + validate(); + + }); + // populate $scope.pendingWizardSettings + // validate() + + } function skipValidation() { var validator = new SettingsValidatorFactory($scope.pendingWizardSettings); @@ -174,7 +228,6 @@ angular.module('QuepidApp') if ($scope.showTLSChangeWarning || $scope.invalidHeaders){ return; } - var validator = new SettingsValidatorFactory($scope.pendingWizardSettings); validator.validateUrl() .then(function () { @@ -337,10 +390,12 @@ angular.module('QuepidApp') var tempSearchUrl = $scope.pendingWizardSettings.searchUrl; var tempApiMethod = $scope.pendingWizardSettings.apiMethod; var tempQueryParams = $scope.pendingWizardSettings.queryParams; + var tempSearchEngine = $scope.pendingWizardSettings.searchEngine; angular.merge($scope.pendingWizardSettings, settingsSvc.editableSettings()); $scope.pendingWizardSettings.searchUrl = tempSearchUrl; $scope.pendingWizardSettings.apiMethod = tempApiMethod; $scope.pendingWizardSettings.queryParams = tempQueryParams; + $scope.pendingWizardSettings.searchEngine = tempSearchEngine; $scope.pendingWizardSettings.newQueries = []; if(userSvc.getUser().completedCaseWizard===false){ diff --git a/app/assets/javascripts/services/searchEndpointSvc.js b/app/assets/javascripts/services/searchEndpointSvc.js new file mode 100644 index 000000000..70daf787f --- /dev/null +++ b/app/assets/javascripts/services/searchEndpointSvc.js @@ -0,0 +1,101 @@ +'use strict'; +/*jshint camelcase: false */ + +angular.module('QuepidApp') + // AngularJS will instantiate a singleton by calling "new" on this function + .service('searchEndpointSvc', [ + '$http', + function searchEndpointSvc($http) { + this.searchEndpoints = []; + + var SearchEndpoint = function(id, name, searchEngine, endpointUrl, apiMethod, customHeaders) { + this.id = id; + this.name = name; + this.searchEngine = searchEngine; + this.endpointUrl = endpointUrl; + this.apiMethod = apiMethod; + this.customHeaders= customHeaders; + }; + + this.constructFromShallowData = function(data) { + return new SearchEndpoint( + data.search_endpoint_id, + data.name, + data.search_engine, + null, + null, + null + ); + }; + + + this.constructFromData = function(data) { + return new SearchEndpoint( + data.search_endpoint_id, + data.name, + data.search_engine, + data.endpoint_url, + data.api_method, + data.custom_headers + ); + }; + + var contains = function(list, searchEndpoint) { + return list.filter(function(item) { return item.id === searchEndpoint.id; }).length > 0; + }; + + this.get = function(searchEndpointId) { + // http GET /api/search_endpoints// + var url = 'api/search_endpoints/' + searchEndpointId; + var self = this; + + + return $http.get(url) + .then(function(response) { + var searchEndpoint = self.constructFromData(response.data); + + return searchEndpoint; + }); + }; + + + this.list = function() { + // http GET /api/search_endpoints + var url = 'api/search_endpoints?shallow=true'; + var self = this; + + // Clear the list just in case the data on the server changed, + // we want to have the latest list. + // TODO: write tests for this. + self.searchEndpoints = []; + + return $http.get(url) + .then(function(response) { + angular.forEach(response.data.search_endpoints, function(dataSearchEndpoint) { + var searchEndpoint = self.constructFromShallowData(dataSearchEndpoint); + + //if(!contains(self.searchEndpoint, search_endpoint)) { + self.searchEndpoints.push(searchEndpoint); + //} + }); + }); + }; + + this.shareEndpoint = function(team, searchEndpoint) { + // http POST api/teams//cases + var url = 'api/teams/' + team.id + '/search_endpoints'; + var data = { + id: searchEndpoint.id + }; + + return $http.post(url, data) + .then(function(response) { + team.searchEndpoints.push(response.data); + }); + }; + + this.filteredEndpoints = function(searchEngine){ + return this.searchEndpoints.filter(function(item) { return item.searchEngine === searchEngine; }) + } + } + ]); diff --git a/app/assets/javascripts/services/settingsSvc.js b/app/assets/javascripts/services/settingsSvc.js index 2aea217cd..4b0754023 100644 --- a/app/assets/javascripts/services/settingsSvc.js +++ b/app/assets/javascripts/services/settingsSvc.js @@ -192,7 +192,7 @@ angular.module('QuepidApp') this.demoSettingsChosen = function(searchEngine, newUrl){ var useTMDBDemoSettings = false; - if (searchEngine === 'solr'){ + if (searchEngine === 'solr'){ if (newUrl === null || angular.isUndefined(newUrl)){ useTMDBDemoSettings = true; } @@ -204,6 +204,7 @@ angular.module('QuepidApp') } } else { + console.log("The serach engine is " + searchEngine); if (newUrl === this.tmdbSettings[searchEngine].searchUrl) { useTMDBDemoSettings = true; } diff --git a/app/assets/templates/views/devQueryParams.html b/app/assets/templates/views/devQueryParams.html index 7c6de310c..e989a3e0e 100644 --- a/app/assets/templates/views/devQueryParams.html +++ b/app/assets/templates/views/devQueryParams.html @@ -109,18 +109,21 @@

Tuning Knobs

- +
- Search Engine Request Handler + Search Endpoint
-
+

Tip: Your URL should look like this {{settings.urlFormat}}.

+

+ Manage Search Endpoints

+

You have specified a search engine url that is on a different protocol ( {{protocolToSwitchTo}} ) than Quepid is currently on, @@ -128,7 +131,7 @@

Tuning Knobs

- +
diff --git a/app/assets/templates/views/wizardModal.html b/app/assets/templates/views/wizardModal.html index a1b0cca89..4e35cc119 100644 --- a/app/assets/templates/views/wizardModal.html +++ b/app/assets/templates/views/wizardModal.html @@ -28,64 +28,87 @@

Name Your Case!

Solr, Elasticsearch, or OpenSearch?

- -
- - - -
- -
-

Paste the URL to the endpoint of your search engine for Quepid to search (you can also just experiment with the demo servers):

-
-
- - Custom Headers must be a valid JSON object - + +
+ + + SEP:{{pendingWizardSettings.searchEndpointId}} + + + +
-
- - You have specified a search engine url that is on a different protocol ( {{protocolToSwitchTo}} ) than Quepid is currently on, - so you need to reload Quepid on that protocol first, and then continue the Wizard. This is to comply with browser security issues. - +
+
+ + +
-
- Sorry, we're not getting any search results from your - Elasticsearch - OpenSearch - Solr. - -
    -
  • Is your Elasticsearch behind a firewall or proxy? Try acessing the URL directly and see if you get a response
  • -
  • Does your Elasticsearch accept HTTP POST requests?
  • -
  • Is your Elasticsearch setup for CORS to talk to Quepid's web app (http://app.quepid.com)?
  • -
  • Do you need an API Key? If so, set one up under Advanced pane --> Custom Headers.
  • -
  • Double check if you have an ad blocker blocking your queries.
  • -
  • See the Troubleshooting Elasticsearch and Quepid wiki page for more help!
  • -
- -
    -
  • Is your OpenSearch behind a firewall or proxy? Try acessing the URL directly and see if you get a response
  • -
  • Does your OpenSearch accept HTTP POST requests?
  • -
  • Is your OpenSearch setup for CORS to talk to Quepid's web app (http://app.quepid.com)?
  • -
  • Double check if you have an ad blocker blocking your queries.
  • -
  • See the Troubleshooting OpenSearch and Quepid wiki page for more help!
  • -
- -
    -
  • Do you see any errors when you visit your Solr URL directly?
  • -
  • - If you are using a version of Solr between 8.4 and 9.0, you may need to tweak the content-type being set by wt=json. - You can do this via the below curl command: + + + + +
    +

    Paste the URL to the endpoint of your search engine for Quepid to search (you can also just experiment with the demo servers):

    +
    +
    + + Custom Headers must be a valid JSON object + +
    +
    + + You have specified a search engine url that is on a different protocol ( {{protocolToSwitchTo}} ) than Quepid is currently on, + so you need to reload Quepid on that protocol first, and then continue the Wizard. This is to comply with browser security issues. + +
    +
    + Sorry, we're not getting any search results from your + Elasticsearch + OpenSearch + Solr. + +
      +
    • Is your Elasticsearch behind a firewall or proxy? Try acessing the URL directly and see if you get a response
    • +
    • Does your Elasticsearch accept HTTP POST requests?
    • +
    • Is your Elasticsearch setup for CORS to talk to Quepid's web app (http://app.quepid.com)?
    • +
    • Do you need an API Key? If so, set one up under Advanced pane --> Custom Headers.
    • +
    • Double check if you have an ad blocker blocking your queries.
    • +
    • See the Troubleshooting Elasticsearch and Quepid wiki page for more help!
    • +
    + +
      +
    • Is your OpenSearch behind a firewall or proxy? Try acessing the URL directly and see if you get a response
    • +
    • Does your OpenSearch accept HTTP POST requests?
    • +
    • Is your OpenSearch setup for CORS to talk to Quepid's web app (http://app.quepid.com)?
    • +
    • Double check if you have an ad blocker blocking your queries.
    • +
    • See the Troubleshooting OpenSearch and Quepid wiki page for more help!
    • +
    + +
      +
    • Do you see any errors when you visit your Solr URL directly?
    • +
    • + If you are using a version of Solr between 8.4 and 9.0, you may need to tweak the content-type being set by wt=json. + You can do this via the below curl command:
       curl -X POST -H 'Content-type:application/json' -d '{
         "update-queryresponsewriter": {
      @@ -95,87 +118,90 @@ 

      Solr, Elasticsearch, or OpenSearch?

      } }' {{ extractSolrConfigApiUrl(pendingWizardSettings.searchUrl) }}
      - If you get a error message back, change update-queryresponsewriter to create-queryresponsewriter and re-run. - -
    • -
    • If Solr responds, check if you have an ad blocker blocking your queries.
    • -
    • Chrome does not accept self signed certificates on Solr, try Firefox.
    • -
    • See the Troubleshooting Solr and Quepid wiki page for more help!
    • -
    - -
    -
    Quepid can search this! Hit 'Continue' to keep working through setup.
    -
    - -
    -
    - -
    - - -

    Unsure? Learn More about Quepid's light touch with your search engine & data. - -

    - Tip: Your URL should look like this {{pendingWizardSettings.urlFormat}}. -

    + If you get a error message back, change update-queryresponsewriter to create-queryresponsewriter and re-run. + +
  • +
  • If Solr responds, check if you have an ad blocker blocking your queries.
  • +
  • Chrome does not accept self signed certificates on Solr, try Firefox.
  • +
  • See the Troubleshooting Solr and Quepid wiki page for more help!
  • +
+
+
Quepid can search this! Hit 'Continue' to keep working through setup.
- - -

- -

-
-
-
- -
- -

Use JSONP if you are talking directly to Solr, otherwise if you are talking to an API you can use GET.

+ + +
+ +
+ + +

Unsure? Learn More about Quepid's light touch with your search engine & data. + +

+ Tip: Your URL should look like this {{pendingWizardSettings.urlFormat}}. +

+
+
+ + +

+ +

+
+
+
+ +
+ +

Use JSONP if you are talking directly to Solr, otherwise if you are talking to an API you can use GET.

+
-
- - - - -

- -

-
- -
-
- -
-
- -
-
- - - - - - - Reload Quepid in {{protocolToSwitchTo}} Protocol - - - + + + + +

+ +

+
+ +
+
+ +
+
+ +
+
+ + + + + + + Reload Quepid in {{protocolToSwitchTo}} Protocol + + + +
+ + +
- - +



diff --git a/app/controllers/search_endpoints_controller.rb b/app/controllers/search_endpoints_controller.rb new file mode 100644 index 000000000..6162c9d72 --- /dev/null +++ b/app/controllers/search_endpoints_controller.rb @@ -0,0 +1,50 @@ +# frozen_string_literal: true + +class SearchEndpointsController < ApplicationController + before_action :set_search_endpoint, only: [ :show, :edit, :update, :destroy ] + + respond_to :html + + def index + @search_endpoints = SearchEndpoint.all + respond_with(@search_endpoints) + end + + def show + respond_with(@search_endpoint) + end + + def new + @search_endpoint = SearchEndpoint.new + respond_with(@search_endpoint) + end + + def edit + end + + def create + @search_endpoint = SearchEndpoint.new(search_endpoint_params) + @search_endpoint.save + respond_with(@search_endpoint) + end + + def update + @search_endpoint.update(search_endpoint_params) + respond_with(@search_endpoint) + end + + def destroy + @search_endpoint.destroy + respond_with(@search_endpoint) + end + + private + + def set_search_endpoint + @search_endpoint = SearchEndpoint.find(params[:id]) + end + + def search_endpoint_params + params.require(:search_endpoint).permit(:name, :endpoint_url, :search_engine, :custom_headers, :api_method) + end +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 98e54b219..74c079b3b 100755 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,6 +1,14 @@ # frozen_string_literal: true module ApplicationHelper + def book_title book + if book.name.downcase.starts_with?('book') + book.name + else + "Book #{book.name}" + end + end + def make_active? options if options.key?(:path) request.fullpath.include?(options[:path]) diff --git a/app/helpers/home_helper.rb b/app/helpers/home_helper.rb index e864ce326..c52b4d02e 100644 --- a/app/helpers/home_helper.rb +++ b/app/helpers/home_helper.rb @@ -44,14 +44,6 @@ def greeting2 # rubocop:enable Metrics/AbcSize # rubocop:enable Metrics/MethodLength - def book_title book - if book.name.downcase.starts_with?('book') - book.name - else - "Book #{book.name}" - end - end - def case_title kase if kase.case_name.downcase.starts_with?('case') kase.case_name diff --git a/app/helpers/search_endpoints_helper.rb b/app/helpers/search_endpoints_helper.rb new file mode 100644 index 000000000..61f69b084 --- /dev/null +++ b/app/helpers/search_endpoints_helper.rb @@ -0,0 +1,4 @@ +# frozen_string_literal: true + +module SearchEndpointsHelper +end diff --git a/app/views/books/edit.html.erb b/app/views/books/edit.html.erb index b091d71ed..1b9bb2120 100644 --- a/app/views/books/edit.html.erb +++ b/app/views/books/edit.html.erb @@ -1,17 +1,16 @@ -
-
-

Edit Book <%= @book.name %>

-
-
- <%= link_to 'New Book', new_book_path, class: "btn btn-sm btn-outline-secondary" %> - -
- +
+

Edit <%= book_title @book %>

+
+
+ <%= link_to 'New Book', new_book_path, class: "btn btn-sm btn-outline-secondary" %> +
+
+
<%= render 'form', book: @book %> @@ -23,4 +22,4 @@ <%= button_to 'Delete Book', @book, method: :delete %> -
+
diff --git a/app/views/books/index.html.erb b/app/views/books/index.html.erb index 01f65bcfc..159fd302b 100644 --- a/app/views/books/index.html.erb +++ b/app/views/books/index.html.erb @@ -16,7 +16,7 @@

<%= notice %>

<% if @books.size == 0 %> - Create your first book by clicking on the New Book button below. + Create your first book by clicking on the New Book button above.
<% end %> @@ -45,6 +45,3 @@ <% end %> - - - diff --git a/app/views/books/new.html.erb b/app/views/books/new.html.erb index 27fb395e2..d29105a04 100644 --- a/app/views/books/new.html.erb +++ b/app/views/books/new.html.erb @@ -1,16 +1,16 @@
-

New Book

-
-
- <%= link_to 'New Book', new_book_path, class: "btn btn-sm btn-outline-secondary" %> - -
- +

New Book

+
+
+ <%= link_to 'New Book', new_book_path, class: "btn btn-sm btn-outline-secondary" %> +
+
+
<%= render 'form', book: @book %>
diff --git a/app/views/books/show.html.erb b/app/views/books/show.html.erb index 8dbfba452..8ec1af5a8 100644 --- a/app/views/books/show.html.erb +++ b/app/views/books/show.html.erb @@ -1,5 +1,5 @@
-

Book <%= @book.name %>

+

<%= book_title @book %>

<%= link_to 'New Book', new_book_path, class: "btn btn-sm btn-outline-secondary" %> @@ -40,8 +40,8 @@ Books organize all the query/doc pairs that are needed for evaluating your searc

-Related cases: -<%= render(partial: 'books/case', collection: @cases, as: :kase) || "There are no cases available." %> + Related cases: + <%= render(partial: 'books/case', collection: @cases, as: :kase) || "There are no cases available." %>

<%= button_to 'Show Judgements', book_judgements_path(@book), method: :get %> @@ -59,7 +59,6 @@ Books organize all the query/doc pairs that are needed for evaluating your searc <%= button_to 'Back to Books', books_path, method: :get %> -

Export Data

You want to export your judgements? I hear you! You can do so in CSV and JSON ;-) diff --git a/app/views/layouts/_sidebar.html.erb b/app/views/layouts/_sidebar.html.erb index a5d210701..281f54ff6 100644 --- a/app/views/layouts/_sidebar.html.erb +++ b/app/views/layouts/_sidebar.html.erb @@ -119,7 +119,6 @@ > <% end %> -

  • <%= link_to '/notebooks/lab/index.html', target: '_blank', class: "nav-link py-3 border-bottom rounded-0", 'data-bs-toggle':'tooltip', 'data-bs-placement':'right', 'aria-label':'Notebooks', 'data-bs-original-title':'Notebooks' do %> <% end %> +
  • +
  • + <%= link_to search_endpoints_path, class: "nav-link #{make_active?({path: 'search_endpoint'}) ? 'active' : ''} py-3 border-bottom rounded-0", 'data-bs-toggle':'tooltip', 'data-bs-placement':'right', 'aria-label':'Search Endpoints', 'data-bs-original-title':'Search Endpoints' do %> + + <% end %>
  • diff --git a/app/views/search_endpoints/index.html.erb b/app/views/search_endpoints/index.html.erb new file mode 100644 index 000000000..cb28376af --- /dev/null +++ b/app/views/search_endpoints/index.html.erb @@ -0,0 +1,46 @@ +
    +

    Search Endpoints

    +
    +
    + <%= link_to 'New Search Endpoint', new_search_endpoint_path, class: "btn btn-sm btn-outline-secondary" %> +
    +
    +
    + +Search Endpoints represents an API (or search engine!) that is receiving queries and responding with documents. + + +

    <%= notice %>

    + +<% if @search_endpoints.size == 0 %> + Create your first book by clicking on the New Search Endpoint button above. +
    +<% end %> + +

    <%= notice %>

    + + + + + + + + + + + + + + + + <% @search_endpoints.each do |search_endpoint| %> + + + + + + + + <% end %> + +
    NameSearch EngineEndpoint UrlCustom HeadersApi Method
    <%= link_to search_endpoint.fullname, search_endpoint_path(search_endpoint) %><%= search_endpoint.search_engine %><%= search_endpoint.endpoint_url %> <%= search_endpoint.custom_headers %> <%= search_endpoint.api_method %>
    diff --git a/app/views/search_endpoints/index.json.jbuilder b/app/views/search_endpoints/index.json.jbuilder new file mode 100644 index 000000000..524eb61b7 --- /dev/null +++ b/app/views/search_endpoints/index.json.jbuilder @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +json.array! @search_endpoints, partial: 'search_endpoints/search_endpoint', as: :search_endpoint diff --git a/app/views/search_endpoints/new.html.erb b/app/views/search_endpoints/new.html.erb new file mode 100644 index 000000000..ab127e50d --- /dev/null +++ b/app/views/search_endpoints/new.html.erb @@ -0,0 +1,9 @@ +

    New search endpoint

    + +<%= render "form", search_endpoint: @search_endpoint %> + +
    + +
    + <%= link_to "Back to search endpoints", search_endpoints_path %> +
    diff --git a/app/views/search_endpoints/show.html.erb b/app/views/search_endpoints/show.html.erb new file mode 100644 index 000000000..e0d2cf6b1 --- /dev/null +++ b/app/views/search_endpoints/show.html.erb @@ -0,0 +1,21 @@ +
    +

    Search Endpoint <%= @search_endpoint.fullname %>

    +
    +
    + <%= link_to 'New Search Endpoint', new_search_endpoint_path, class: "btn btn-sm btn-outline-secondary" %> +
    +
    +
    + +Search Endpoints represents an API (or search engine!) that is receiving queries and responding with documents. + +

    <%= notice %>

    + +<%= render @search_endpoint %> + +
    + <%= link_to "Edit this search endpoint", edit_search_endpoint_path(@search_endpoint) %> | + <%= link_to "Back to search endpoints", search_endpoints_path %> + + <%= button_to "Destroy this search endpoint", @search_endpoint, method: :delete %> +
    diff --git a/app/views/search_endpoints/show.json.jbuilder b/app/views/search_endpoints/show.json.jbuilder new file mode 100644 index 000000000..a4573306e --- /dev/null +++ b/app/views/search_endpoints/show.json.jbuilder @@ -0,0 +1,3 @@ +# frozen_string_literal: true + +json.partial! 'search_endpoints/search_endpoint', search_endpoint: @search_endpoint diff --git a/config/routes.rb b/config/routes.rb index 850ea65f9..2f7eecf45 100755 --- a/config/routes.rb +++ b/config/routes.rb @@ -4,6 +4,7 @@ # rubocop:disable Metrics/BlockLength Rails.application.routes.draw do + resources :search_endpoints # get 'home/show' root 'home#show' # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html diff --git a/db/migrate/20230816195233_drop_search_endpoint_fields_from_try.rb b/db/migrate/20230816195233_drop_search_endpoint_fields_from_try.rb index 93db05905..c4110c850 100644 --- a/db/migrate/20230816195233_drop_search_endpoint_fields_from_try.rb +++ b/db/migrate/20230816195233_drop_search_endpoint_fields_from_try.rb @@ -1,10 +1,11 @@ class DropSearchEndpointFieldsFromTry < ActiveRecord::Migration[7.0] def change + # I'm going to comment this out to take this go live process slower for now. # Now that we've moved all the fields over to SearchEndpoint, lets # clean up after ourselves. - remove_column :tries, :search_engine - remove_column :tries, :search_url - remove_column :tries, :api_method - remove_column :tries, :custom_headers + #remove_column :tries, :search_engine + #remove_column :tries, :search_url + #remove_column :tries, :api_method + #remove_column :tries, :custom_headers end end diff --git a/test/controllers/search_endpoints_controller_test.rb b/test/controllers/search_endpoints_controller_test.rb new file mode 100644 index 000000000..c4c5cdab5 --- /dev/null +++ b/test/controllers/search_endpoints_controller_test.rb @@ -0,0 +1,64 @@ +# frozen_string_literal: true + +require 'test_helper' + +class SearchEndpointsControllerTest < ActionDispatch::IntegrationTest + setup do + @search_endpoint = search_endpoints(:one) + end + + test 'should get index' do + get search_endpoints_url + assert_response :success + end + + test 'should get new' do + get new_search_endpoint_url + assert_response :success + end + + test 'should create search_endpoint' do + assert_difference('SearchEndpoint.count') do + post search_endpoints_url, + params: { search_endpoint: { + api_method: @search_endpoint.api_method, + custom_headers: @search_endpoint.custom_headers, + endpoint_url: @search_endpoint.endpoint_url, + name: @search_endpoint.name, + search_engine: @search_endpoint.search_engine, + } } + end + + assert_redirected_to search_endpoint_url(SearchEndpoint.last) + end + + test 'should show search_endpoint' do + get search_endpoint_url(@search_endpoint) + assert_response :success + end + + test 'should get edit' do + get edit_search_endpoint_url(@search_endpoint) + assert_response :success + end + + test 'should update search_endpoint' do + patch search_endpoint_url(@search_endpoint), + params: { search_endpoint: { + api_method: @search_endpoint.api_method, + custom_headers: @search_endpoint.custom_headers, + endpoint_url: @search_endpoint.endpoint_url, + name: @search_endpoint.name, + search_engine: @search_endpoint.search_engine, + } } + assert_redirected_to search_endpoint_url(@search_endpoint) + end + + test 'should destroy search_endpoint' do + assert_difference('SearchEndpoint.count', -1) do + delete search_endpoint_url(@search_endpoint) + end + + assert_redirected_to search_endpoints_url + end +end diff --git a/test/models/search_endpoint_test.rb b/test/models/search_endpoint_test.rb new file mode 100644 index 000000000..43cb3ab57 --- /dev/null +++ b/test/models/search_endpoint_test.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +require 'test_helper' + +class SearchEndpointTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/system/search_endpoints_test.rb b/test/system/search_endpoints_test.rb new file mode 100644 index 000000000..7ddac30ef --- /dev/null +++ b/test/system/search_endpoints_test.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require 'application_system_test_case' + +class SearchEndpointsTest < ApplicationSystemTestCase + setup do + @search_endpoint = search_endpoints(:one) + end + + test 'visiting the index' do + visit search_endpoints_url + assert_selector 'h1', text: 'Search endpoints' + end + + test 'should create search endpoint' do + visit search_endpoints_url + click_on 'New search endpoint' + + fill_in 'Api method', with: @search_endpoint.api_method + fill_in 'Custom headers', with: @search_endpoint.custom_headers + fill_in 'Endpoint url', with: @search_endpoint.endpoint_url + fill_in 'Name', with: @search_endpoint.name + fill_in 'Search engine', with: @search_endpoint.search_engine + click_on 'Create Search endpoint' + + assert_text 'Search endpoint was successfully created' + click_on 'Back' + end + + test 'should update Search endpoint' do + visit search_endpoint_url(@search_endpoint) + click_on 'Edit this search endpoint', match: :first + + fill_in 'Api method', with: @search_endpoint.api_method + fill_in 'Custom headers', with: @search_endpoint.custom_headers + fill_in 'Endpoint url', with: @search_endpoint.endpoint_url + fill_in 'Name', with: @search_endpoint.name + fill_in 'Search engine', with: @search_endpoint.search_engine + click_on 'Update Search endpoint' + + assert_text 'Search endpoint was successfully updated' + click_on 'Back' + end + + test 'should destroy Search endpoint' do + visit search_endpoint_url(@search_endpoint) + click_on 'Destroy this search endpoint', match: :first + + assert_text 'Search endpoint was successfully destroyed' + end +end