|
|
@@ -0,0 +1,190 @@
|
|
|
+// Licensed to Cloudera, Inc. under one
|
|
|
+// or more contributor license agreements. See the NOTICE file
|
|
|
+// distributed with this work for additional information
|
|
|
+// regarding copyright ownership. Cloudera, Inc. licenses this file
|
|
|
+// to you under the Apache License, Version 2.0 (the
|
|
|
+// "License"); you may not use this file except in compliance
|
|
|
+// with the License. You may obtain a copy of the License at
|
|
|
+//
|
|
|
+// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
+//
|
|
|
+// Unless required by applicable law or agreed to in writing, software
|
|
|
+// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
+// See the License for the specific language governing permissions and
|
|
|
+// limitations under the License.
|
|
|
+
|
|
|
+import * as ko from 'knockout';
|
|
|
+
|
|
|
+import componentUtils from 'ko/components/componentUtils';
|
|
|
+import DisposableComponent from 'ko/components/DisposableComponent';
|
|
|
+import I18n from 'utils/i18n';
|
|
|
+import { NAME as PAGINATOR_COMPONENT } from './ko.paginator';
|
|
|
+import apiHelper from 'api/apiHelper';
|
|
|
+
|
|
|
+export const UPDATE_SAVED_QUERIES_EVENT = 'update.saved.queries';
|
|
|
+export const NAME = 'saved-queries';
|
|
|
+
|
|
|
+// prettier-ignore
|
|
|
+const TEMPLATE = `
|
|
|
+<div class="snippet-tab-actions">
|
|
|
+ <form autocomplete="off" class="inline-block">
|
|
|
+ <input class="input-small search-input" type="text" ${ window.PREVENT_AUTOFILL_INPUT_ATTRS } placeholder="${ I18n('Search...') }" data-bind="
|
|
|
+ textInput: filter,
|
|
|
+ clearable: filter
|
|
|
+ "/>
|
|
|
+ </form>
|
|
|
+</div>
|
|
|
+
|
|
|
+<div class="snippet-tab-body">
|
|
|
+ <!-- ko if: loading -->
|
|
|
+ <div>
|
|
|
+ <h1 class="empty"><i class="fa fa-spinner fa-spin"></i> ${ I18n('Loading...') }</h1>
|
|
|
+ </div>
|
|
|
+ <!-- /ko -->
|
|
|
+
|
|
|
+ <!-- ko ifnot: loading -->
|
|
|
+ <!-- ko if: hasErrors -->
|
|
|
+ <div class="margin-top-10 margin-left-10" style="font-style: italic">${ I18n("Error loading the queries.") }</div>
|
|
|
+ <!-- /ko -->
|
|
|
+ <!-- ko if: !hasErrors() && !loading() && queries().length === 0 && filter() === '' -->
|
|
|
+ <div class="margin-top-10 margin-left-10" style="font-style: italic">${ I18n("You don't have any saved queries.") }</div>
|
|
|
+ <!-- /ko -->
|
|
|
+ <!-- ko if: !hasErrors() && !loading() && queries().length === 0 && filter() !== '' -->
|
|
|
+ <div class="margin-top-10 margin-left-10" style="font-style: italic">${ I18n('No queries found for') } <strong data-bind="text: filter"></strong>.</div>
|
|
|
+ <!-- /ko -->
|
|
|
+ <!-- ko if: !hasErrors() && !loading() && queries().length > 0 -->
|
|
|
+ <table class="table table-condensed margin-top-10 history-table">
|
|
|
+ <thead>
|
|
|
+ <tr>
|
|
|
+ <th style="width: 16%">${ I18n("Name") }</th>
|
|
|
+ <th style="width: 50%">${ I18n("Description") }</th>
|
|
|
+ <th style="width: 18%">${ I18n("Owner") }</th>
|
|
|
+ <th style="width: 16%">${ I18n("Last Modified") }</th>
|
|
|
+ </tr>
|
|
|
+ </thead>
|
|
|
+ <tbody data-bind="foreach: queries">
|
|
|
+ <tr data-bind="
|
|
|
+ click: function() {
|
|
|
+ $parent.openNotebook(uuid);
|
|
|
+ },
|
|
|
+ css: {
|
|
|
+ 'highlight': uuid == $parent.currentNotebook.uuid(),
|
|
|
+ 'pointer': uuid != $parent.currentNotebook.uuid()
|
|
|
+ }">
|
|
|
+ <td style="width: 16%"><span data-bind="ellipsis: { data: name, length: 50 }"></span></td>
|
|
|
+ <td style="width: 50%; white-space: normal"><span data-bind="text: description"></span></td>
|
|
|
+ <td style="width: 18%"><span data-bind="text: owner"></span></td>
|
|
|
+ <td style="width: 16%"><span data-bind="text: localeFormat(lastModified())"></span></td>
|
|
|
+ </tr>
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ <!-- /ko -->
|
|
|
+
|
|
|
+ <!-- ko component: {
|
|
|
+ name: '${ PAGINATOR_COMPONENT }',
|
|
|
+ params: {
|
|
|
+ currentPage: currentPage,
|
|
|
+ totalPages: totalPages,
|
|
|
+ onPageChange: fetchQueries.bind($data)
|
|
|
+ }
|
|
|
+ } --><!-- /ko -->
|
|
|
+ <!-- /ko -->
|
|
|
+</div>
|
|
|
+`;
|
|
|
+
|
|
|
+const QUERIES_PER_PAGE = 50;
|
|
|
+
|
|
|
+const adaptRawQuery = rawQuery => ({
|
|
|
+ uuid: rawQuery.uuid,
|
|
|
+ name: ko.observable(rawQuery.name),
|
|
|
+ owner: rawQuery.owner,
|
|
|
+ description: ko.observable(rawQuery.description),
|
|
|
+ lastModified: ko.observable(rawQuery.last_modified)
|
|
|
+});
|
|
|
+
|
|
|
+class SavedQueries extends DisposableComponent {
|
|
|
+ constructor(params) {
|
|
|
+ super();
|
|
|
+
|
|
|
+ this.currentNotebook = params.currentNotebook;
|
|
|
+ this.openFunction = params.openFunction;
|
|
|
+ this.type = params.type;
|
|
|
+ this.currentTab = params.currentTab;
|
|
|
+
|
|
|
+ this.loading = ko.observable(true);
|
|
|
+ this.hasErrors = ko.observable(false);
|
|
|
+ this.queries = ko.observableArray();
|
|
|
+
|
|
|
+ this.currentPage = ko.observable(1);
|
|
|
+ this.totalPages = ko.observable(1);
|
|
|
+
|
|
|
+ this.lastFetchQueriesRequest = undefined;
|
|
|
+
|
|
|
+ this.filter = ko.observable().extend({ rateLimit: 900 });
|
|
|
+ this.filter.subscribe(() => {
|
|
|
+ this.fetchQueries();
|
|
|
+ });
|
|
|
+
|
|
|
+ this.subscribe(UPDATE_SAVED_QUERIES_EVENT, details => {
|
|
|
+ if (!details.save_as) {
|
|
|
+ this.queries().some(item => {
|
|
|
+ if (item.uuid === details.uuid) {
|
|
|
+ item.name(details.name);
|
|
|
+ item.description(details.description);
|
|
|
+ item.lastModified(details.last_modified);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } else if (this.queries().length) {
|
|
|
+ this.queries.unshift(adaptRawQuery(details));
|
|
|
+ } else {
|
|
|
+ this.fetchQueries();
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ if (this.currentTab() === 'savedQueries') {
|
|
|
+ this.fetchQueries();
|
|
|
+ } else {
|
|
|
+ const sub = this.subscribe(this.currentTab, tab => {
|
|
|
+ if (tab === 'savedQueries') {
|
|
|
+ this.fetchQueries();
|
|
|
+ sub.dispose();
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ async fetchQueries() {
|
|
|
+ apiHelper.cancelActiveRequest(this.lastFetchQueriesRequest);
|
|
|
+
|
|
|
+ this.loading(true);
|
|
|
+ this.hasErrors(false);
|
|
|
+
|
|
|
+ this.lastFetchQueriesRequest = apiHelper.searchDocuments({
|
|
|
+ successCallback: foundDocuments => {
|
|
|
+ this.totalPages(Math.ceil(foundDocuments.count / QUERIES_PER_PAGE));
|
|
|
+ this.queries(foundDocuments.documents.map(adaptRawQuery));
|
|
|
+ this.loading(false);
|
|
|
+ this.hasErrors(false);
|
|
|
+ },
|
|
|
+ errorCallback: () => {
|
|
|
+ this.loading(false);
|
|
|
+ this.hasErrors(true);
|
|
|
+ },
|
|
|
+ page: this.currentPage(),
|
|
|
+ limit: QUERIES_PER_PAGE,
|
|
|
+ type: 'query-' + this.type(),
|
|
|
+ query: this.filter(),
|
|
|
+ include_trashed: false
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ openNotebook(uuid) {
|
|
|
+ if (window.getSelection().toString() === '' && uuid !== this.currentNotebook.uuid()) {
|
|
|
+ this.openFunction(uuid);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+componentUtils.registerComponent(NAME, SavedQueries, TEMPLATE);
|