浏览代码

HUE-9143 [frontend] Create initial quick query component with an ace editor

Johan Ahlen 5 年之前
父节点
当前提交
63649cd82e

+ 45 - 0
desktop/core/src/desktop/js/jest/jest.init.js

@@ -27,6 +27,12 @@ import komapping from 'knockout.mapping';
 
 ko.mapping = komapping;
 
+class Tooltip {}
+
+class AceRange {}
+
+class Autocomplete {}
+
 const globalVars = {
   ko: ko,
   AUTOCOMPLETE_TIMEOUT: 1,
@@ -47,6 +53,45 @@ const globalVars = {
   },
   SQL_COLUMNS_KNOWN_FACET_VALUES: {
     type: { array: -1, boolean: -1 }
+  },
+  ace: {
+    edit: () => ({
+      setOptions: () => {},
+      getSession: () => ({
+        setMode: () => {},
+        doc: {
+          createAnchor: () => ({
+            on: () => {},
+            getPosition: () => ({
+              row: 0
+            }),
+            setPosition: () => {},
+            detach: () => {}
+          })
+        },
+        getTextRange: () => {},
+        addGutterDecoration: () => {},
+        removeGutterDecoration: () => {}
+      }),
+      setTheme: () => {},
+      getValue: () => '',
+      getSelectionRange: () => ({ start: { row: 0, column: 0 }, end: { row: 0, column: 0 } }),
+      on: () => {},
+      off: () => {},
+      commands: {
+        on: () => {},
+        off: () => {}
+      },
+      container: {
+        addEventListener: () => {},
+        removeEventListener: () => {}
+      }
+    }),
+    require: () => ({
+      Tooltip: Tooltip,
+      Range: AceRange,
+      Autocomplete: Autocomplete
+    })
   }
 };
 

+ 1 - 1
desktop/core/src/desktop/js/ko/bindings/ace/aceLocationHandler.js

@@ -1340,7 +1340,7 @@ class AceLocationHandler {
             huePubSub.publish('ace.sql.location.worker.post', {
               id: self.snippet.id(),
               statementDetails: statementDetails,
-              type: self.dialect(),
+              type: self.snippet && self.snippet.dialect ? self.snippet.dialect() : self.dialect(),
               namespace: self.snippet.namespace(),
               compute: self.snippet.compute(),
               defaultDatabase: self.snippet.database()

+ 27 - 0
desktop/core/src/desktop/js/ko/components/contextPopover/__snapshots__/ko.quickQueryContext.test.js.snap

@@ -0,0 +1,27 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ko.quickQueryContext.js should render component 1`] = `
+"<div data-bind=\\"descendantsComplete: descendantsComplete, component: { name: &quot;quick-query-context&quot;, params: params }\\"><div class=\\"context-popover-flex-fill\\" style=\\"overflow: auto;\\">
+  <div style=\\"margin: 10px;\\" data-bind=\\"
+    component: { 
+      name: 'hue-simple-ace-editor-multi',
+      params: {
+        autocomplete: autocomplete,
+        value: statement,
+        lines: 5,
+        aceOptions: {
+          minLines: 10,
+          maxLines: 25
+        },
+        mode: dialect,
+        database: database,
+        namespace: namespace,
+        compute: compute,
+        temporaryOnly: true
+      }
+    }
+  \\" id=\\"uuid-uuid\\"><div class=\\"simple-ace-multi-line\\">
+    <div class=\\"ace-editor\\"></div>
+  </div></div>
+</div></div>"
+`;

+ 10 - 2
desktop/core/src/desktop/js/ko/components/contextPopover/ko.contextPopover.js

@@ -518,10 +518,13 @@ const CONTEXT_POPOVER_TEMPLATE = `
       </div>
     </div>
     <!-- /ko -->
+    <!-- ko if: typeof contentsComponent !== 'undefined' -->
+    <!-- ko component: { name: contentsComponent, params: data } --><!-- /ko -->
+    <!-- /ko -->
     <!-- ko if: typeof contentsTemplate !== 'undefined' -->
     <!-- ko template: { name: contentsTemplate, data: contents } --><!-- /ko -->
     <!-- /ko -->
-    <!-- ko if: typeof contentsTemplate === 'undefined' -->
+    <!-- ko if: typeof contentsTemplate === 'undefined' && typeof contentsComponent === 'undefined' -->
     <!-- ko template: 'context-popover-contents' --><!-- /ko -->
     <!-- /ko -->
   </div>
@@ -698,7 +701,12 @@ class ContextPopoverViewModel {
       !self.isStorageEntry &&
       !self.isCatalogEntry;
 
-    if (self.isCatalogEntry) {
+    if (params.data.type === 'quickQuery') {
+      self.contentsComponent = 'quick-query-context';
+      self.title = I18n('Quick Query');
+      self.iconClass = 'fa-play';
+      self.pinEnabled = false;
+    } else if (self.isCatalogEntry) {
       self.contents = new DataCatalogContext({
         popover: self,
         catalogEntry: params.data.catalogEntry

+ 67 - 0
desktop/core/src/desktop/js/ko/components/contextPopover/ko.quickQueryContext.js

@@ -0,0 +1,67 @@
+// 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 ko from 'knockout';
+
+import 'ko/components/simpleAceEditor/ko.simpleAceEditor';
+
+import componentUtils from 'ko/components/componentUtils';
+import DisposableComponent from 'ko/components/DisposableComponent';
+
+export const NAME = 'quick-query-context';
+
+// prettier-ignore
+const TEMPLATE = `
+<div class="context-popover-flex-fill" style="overflow: auto;">
+  <div style="margin: 10px;" data-bind="
+    component: { 
+      name: 'hue-simple-ace-editor-multi',
+      params: {
+        autocomplete: autocomplete,
+        value: statement,
+        lines: 5,
+        aceOptions: {
+          minLines: 10,
+          maxLines: 25
+        },
+        mode: dialect,
+        database: database,
+        namespace: namespace,
+        compute: compute,
+        temporaryOnly: true
+      }
+    }
+  "></div>
+</div>
+`;
+
+class QuickQueryContext extends DisposableComponent {
+  constructor(params) {
+    super(params);
+
+    this.database = ko.observable('default');
+    this.statement = ko.observable();
+
+    // TODO: Switch over to connector in ko.simpleAceEditor
+    this.dialect = ko.observable('impala');
+    this.namespace = ko.observable({ id: 'foo' });
+    this.compute = ko.observable({ id: 'foo' });
+
+    this.autocomplete = ko.pureComputed(() => ({ type: this.dialect() }));
+  }
+}
+
+componentUtils.registerComponent(NAME, QuickQueryContext, TEMPLATE);

+ 30 - 0
desktop/core/src/desktop/js/ko/components/contextPopover/ko.quickQueryContext.test.js

@@ -0,0 +1,30 @@
+// 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 { koSetup } from 'jest/koTestUtils';
+import { NAME } from './ko.quickQueryContext';
+
+describe('ko.quickQueryContext.js', () => {
+  const setup = koSetup();
+
+  it('should render component', async () => {
+    const element = await setup.renderComponent(NAME, {});
+
+    expect(
+      element.innerHTML.replace(/[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}/gi, 'uuid-uuid')
+    ).toMatchSnapshot();
+  });
+});

+ 6 - 6
desktop/core/src/desktop/js/ko/components/simpleAceEditor/ko.simpleAceEditor.js

@@ -80,10 +80,11 @@ class SimpleAceEditor {
     self.ace(editor);
 
     if (params.autocomplete) {
+      const autocomplete = ko.unwrap(params.autocomplete);
       const sourceType =
-        params.autocomplete.type.indexOf('Query') !== -1
-          ? params.autocomplete.type.replace('Query', '')
-          : params.autocomplete.type;
+        autocomplete.type.indexOf('Query') !== -1
+          ? autocomplete.type.replace('Query', '')
+          : autocomplete.type;
 
       const snippet = {
         autocompleteSettings: {
@@ -159,11 +160,10 @@ class SimpleAceEditor {
         snippet: snippet,
         fixedPrefix: params.fixedPrefix,
         fixedPostfix: params.fixedPostfix,
-        support: params.autocomplete.support
+        support: autocomplete.support
       };
 
-      const AutocompleterClass =
-        AVAILABLE_AUTOCOMPLETERS[params.autocomplete.type] || SqlAutocompleter;
+      const AutocompleterClass = AVAILABLE_AUTOCOMPLETERS[autocomplete.type] || SqlAutocompleter;
       self.autocompleter = new AutocompleterClass(autocompleteArgs);
     } else {
       self.autocompleter = null;

+ 1 - 0
desktop/core/src/desktop/js/ko/ko.all.js

@@ -141,6 +141,7 @@ import 'ko/components/assist/ko.assistSchedulePanel';
 import 'ko/components/assist/ko.assistStoragePanel';
 import 'ko/components/assist/ko.rightAssistPanel';
 import 'ko/components/contextPopover/ko.contextPopover';
+import 'ko/components/contextPopover/ko.quickQueryContext';
 import 'ko/components/simpleAceEditor/ko.simpleAceEditor';
 import 'ko/components/ko.catalogEntriesList';
 import 'ko/components/ko.contextSelector';