You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@drill.apache.org by gp...@apache.org on 2019/01/04 06:54:00 UTC

[drill] 05/10: DRILL-6921: Add Clear button for /options filter

This is an automated email from the ASF dual-hosted git repository.

gparai pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/drill.git

commit 8a85879507866a83293812205ceab2591391c590
Author: Kunal Khatua <kk...@maprtech.com>
AuthorDate: Tue Jan 1 10:10:07 2019 -0800

    DRILL-6921: Add Clear button for /options filter
    
    This commit adds the following search enhancements:
    1. Addition of a clear ('x') button in the search field
    2. If a filter term has been entered in the search box, on clicking the Update or Reset-to-Default button of any option, the reloaded page will re-apply the last entered filter term in the search box.
    3. If the search box is empty, on clicking the Update or Reset-to-Default button of any option, the reloaded page will automatically filter out everything except the updated/reset option to show the changed value.
    4. Customization of the quick search terms. (using defaults in drill-module.conf)
    closes #1588
---
 .../java/org/apache/drill/exec/ExecConstants.java  |  2 +
 .../drill/exec/server/rest/StatusResources.java    | 55 ++++++++++++++++++----
 .../java-exec/src/main/resources/drill-module.conf |  3 +-
 exec/java-exec/src/main/resources/rest/options.ftl | 50 ++++++++++++++------
 4 files changed, 86 insertions(+), 24 deletions(-)

diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/ExecConstants.java b/exec/java-exec/src/main/java/org/apache/drill/exec/ExecConstants.java
index afa29d9..77cfb9f 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/ExecConstants.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/ExecConstants.java
@@ -224,6 +224,8 @@ public final class ExecConstants {
   public static final String HTTP_AUTHENTICATION_MECHANISMS = "drill.exec.http.auth.mechanisms";
   public static final String HTTP_SPNEGO_PRINCIPAL = "drill.exec.http.auth.spnego.principal";
   public static final String HTTP_SPNEGO_KEYTAB = "drill.exec.http.auth.spnego.keytab";
+  //Customize filters in options
+  public static final String HTTP_WEB_OPTIONS_FILTERS = "drill.exec.http.web.options.filters";
   public static final String SYS_STORE_PROVIDER_CLASS = "drill.exec.sys.store.provider.class";
   public static final String SYS_STORE_PROVIDER_LOCAL_PATH = "drill.exec.sys.store.provider.local.path";
   public static final String SYS_STORE_PROVIDER_LOCAL_ENABLE_WRITE = "drill.exec.sys.store.provider.local.write";
diff --git a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/StatusResources.java b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/StatusResources.java
index 1ebef31..9123b65 100644
--- a/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/StatusResources.java
+++ b/exec/java-exec/src/main/java/org/apache/drill/exec/server/rest/StatusResources.java
@@ -17,6 +17,7 @@
  */
 package org.apache.drill.exec.server.rest;
 
+import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
 import java.util.LinkedList;
@@ -31,13 +32,16 @@ import javax.ws.rs.GET;
 import javax.ws.rs.POST;
 import javax.ws.rs.Path;
 import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
 import javax.ws.rs.core.MediaType;
 import javax.ws.rs.core.SecurityContext;
+import javax.ws.rs.core.UriInfo;
 import javax.xml.bind.annotation.XmlRootElement;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
 import org.apache.commons.lang3.tuple.ImmutablePair;
 import org.apache.commons.lang3.tuple.Pair;
+import org.apache.drill.exec.ExecConstants;
 import org.apache.drill.exec.server.options.OptionList;
 import org.apache.drill.exec.server.options.OptionManager;
 import org.apache.drill.exec.server.options.OptionValue;
@@ -63,6 +67,8 @@ public class StatusResources {
   public static final String PATH_INTERNAL_OPTIONS_JSON = "/internal_options" + REST_API_SUFFIX;
   public static final String PATH_OPTIONS = "/options";
   public static final String PATH_INTERNAL_OPTIONS = "/internal_options";
+  //Used to access current filter state in WebUI
+  private static final String CURRENT_FILTER_PARAM = "filter";
 
   @Inject UserAuthEnabled authEnabled;
   @Inject WorkManager work;
@@ -118,27 +124,35 @@ public class StatusResources {
     return getSystemOptionsJSONHelper(true);
   }
 
-  private Viewable getSystemOptionsHelper(boolean internal) {
+  //Generate model-view for WebUI (PATH_OPTIONS and PATH_INTERNAL_OPTIONS)
+  private Viewable getSystemOptionsHelper(boolean internal, UriInfo uriInfo) {
+    List<OptionWrapper> options = getSystemOptionsJSONHelper(internal);
+    List<String> fltrList = new ArrayList<>(work.getContext().getConfig().getStringList(ExecConstants.HTTP_WEB_OPTIONS_FILTERS));
+    String currFilter = (uriInfo != null) ? uriInfo.getQueryParameters().getFirst(CURRENT_FILTER_PARAM) : null;
+    if (currFilter == null) {
+      currFilter = "";
+    }
+
     return ViewableWithPermissions.create(authEnabled.get(),
       "/rest/options.ftl",
       sc,
-      getSystemOptionsJSONHelper(internal));
+      new OptionsListing(options, fltrList, currFilter));
   }
 
   @GET
   @Path(StatusResources.PATH_OPTIONS)
   @RolesAllowed(DrillUserPrincipal.AUTHENTICATED_ROLE)
   @Produces(MediaType.TEXT_HTML)
-  public Viewable getSystemPublicOptions() {
-    return getSystemOptionsHelper(false);
+  public Viewable getSystemPublicOptions(@Context UriInfo uriInfo) {
+    return getSystemOptionsHelper(false, uriInfo);
   }
 
   @GET
   @Path(StatusResources.PATH_INTERNAL_OPTIONS)
   @RolesAllowed(DrillUserPrincipal.AUTHENTICATED_ROLE)
   @Produces(MediaType.TEXT_HTML)
-  public Viewable getSystemInternalOptions() {
-    return getSystemOptionsHelper(true);
+  public Viewable getSystemInternalOptions(@Context UriInfo uriInfo) {
+    return getSystemOptionsHelper(true, uriInfo);
   }
 
   @SuppressWarnings("resource")
@@ -160,9 +174,34 @@ public class StatusResources {
     }
 
     if (optionManager.getOptionDefinition(name).getMetaData().isInternal()) {
-      return getSystemInternalOptions();
+      return getSystemInternalOptions(null);
     } else {
-      return getSystemPublicOptions();
+      return getSystemPublicOptions(null);
+    }
+  }
+
+  /**
+   * Data Model for rendering /options on webUI
+   */
+  public static class OptionsListing {
+    private final List<OptionWrapper> options;
+    private final List<String> filters;
+    private final String dynamicFilter;
+
+    public OptionsListing(List<OptionWrapper> optList, List<String> fltrList, String currFilter) {
+      this.options = optList;
+      this.filters = fltrList;
+      this.dynamicFilter = currFilter;
+    }
+
+    public List<OptionWrapper> getOptions() {
+      return options;
+    }
+    public List<String> getFilters() {
+      return filters;
+    }
+    public String getDynamicFilter() {
+      return dynamicFilter;
     }
   }
 
diff --git a/exec/java-exec/src/main/resources/drill-module.conf b/exec/java-exec/src/main/resources/drill-module.conf
index 3682a85..e792b20 100644
--- a/exec/java-exec/src/main/resources/drill-module.conf
+++ b/exec/java-exec/src/main/resources/drill-module.conf
@@ -155,7 +155,8 @@ drill.exec: {
             reservation: 0,
             maximum: 9223372036854775807
         }
-    }
+    },
+    web.options.filters: ["planner", "store", "parquet", "hashagg", "hashjoin"]
   },
   //setting javax variables for ssl configurations is being deprecated.
   ssl: {
diff --git a/exec/java-exec/src/main/resources/rest/options.ftl b/exec/java-exec/src/main/resources/rest/options.ftl
index a8e03fa..1fce03c 100644
--- a/exec/java-exec/src/main/resources/rest/options.ftl
+++ b/exec/java-exec/src/main/resources/rest/options.ftl
@@ -24,8 +24,18 @@
     <script>
     //Alter System Values
     function alterSysOption(optionName, optionValue, optionKind) {
+        var currHref = location.href;
+        var redirectHref = currHref.replace(/(.?filter=).*/,"");
+        //Read filter value and apply to reload with new filter
+        var reApplyFilter = $("#searchBox").val();
+        if (reApplyFilter != null && reApplyFilter.trim().length > 0) {
+            redirectHref = redirectHref + "?filter=" + reApplyFilter.trim();
+        } else { //Apply filter for updated field
+            redirectHref = redirectHref + "?filter=" + optionName;
+        }
         $.post("/option/"+optionName, {kind: optionKind, name: optionName, value: optionValue}, function () {
-            location.reload(true);
+            //Remove existing filters
+            location.href=redirectHref;
         });
     }
 
@@ -71,16 +81,19 @@ table.sortable thead .sorting_desc { background-image: url("/static/img/black-de
 <#macro page_body>
   <div class="page-header">
   </div>
-  <div class="btn-group btn-group-sm" style="display:inline-block;">
-  <button type="button" class="btn" style="cursor:default;font-weight:bold;" > Quick Filters </button>
-  <button type="button" class="btn btn-info" onclick="inject(this.innerHTML);">planner</button>
-  <button type="button" class="btn btn-info" onclick="inject(this.innerHTML);">store</button>
-  <button type="button" class="btn btn-info" onclick="inject(this.innerHTML);">parquet</button>
-  <button type="button" class="btn btn-info" onclick="inject(this.innerHTML);">hashagg</button>
-  <button type="button" class="btn btn-info" onclick="inject(this.innerHTML);">hashjoin</button>
-  </div>
   <div class="col-xs-4">
-  <input id="searchBox"  name="searchBox" class="form-control" type="text" value="" placeholder="Search options...">
+    <div class="input-group input-sm" >
+      <input id="searchBox" name="searchBox" class="form-control" type="text" value="" placeholder="Search options...">
+        <div class="input-group-btn">
+          <button class="btn btn-default" type="button" onclick="$('#searchBox').val('').focus();" title="Clear search" style="font-weight:bold">&times;</button>
+        </div> 
+    </div>
+  </div>
+  <div class="btn-group btn-group-sm" style="padding-top:0.5%;">
+  <button type="button" class="btn" style="cursor:default;font-weight:bold;" > Quick Filters </button>
+  <#list model.getFilters() as filter>
+  <button type="button" class="btn btn-info" onclick="inject(this.innerHTML);">${filter}</button>
+  </#list>
   </div>
 
   <div class="table-responsive">
@@ -92,9 +105,7 @@ table.sortable thead .sorting_desc { background-image: url("/static/img/black-de
           <th style="width:45%">DESCRIPTION</th>
         </tr>
       </thead>
-      <tbody>
-        <#assign i = 1>
-        <#list model as option>
+      <tbody><#assign i = 1><#list model.getOptions() as option>
           <tr id="row-${i}">
             <td style="font-family:Courier New; vertical-align:middle" id='optionName'>${option.getName()}</td>
             <td>
@@ -142,7 +153,7 @@ table.sortable thead .sorting_desc { background-image: url("/static/img/black-de
             "infoEmpty": "No options available",
             "infoFiltered": ""
         }
-      } );
+      });
 
     //Draw when the table is ready
     $(document).ready(function() {
@@ -156,10 +167,19 @@ table.sortable thead .sorting_desc { background-image: url("/static/img/black-de
 
       // Draw DataTable
       optTable.rows().invalidate().draw();
+
+      //Re-Inject Filter keyword here
+      let explicitFltr = "";
+      if (window.location.search.indexOf("filter=") >= 1) {
+        //Select 1st occurrence (Chrome accepts 1st of duplicates)
+        let kvPair=window.location.search.substr(1).split('&')[0];
+        explicitFltr=kvPair.split('=')[1]
+        inject(explicitFltr);
+      }
     });
 
     //EventListener to update table when changes are detected
-    $('#searchBox').on('keyup change', function () {
+    $('#searchBox').on('keyup focus change', function () {
       optTable.search(this.value).draw().toString();
     });