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 <[email protected]> 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">×</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(); });
