Эх сурвалжийг харах

HUE-8758 [connectors] Switch from type to connector/dialect in editor v2

Johan Ahlen 5 жил өмнө
parent
commit
404854554e

+ 0 - 1
apps/metastore/src/metastore/templates/metastore.mako

@@ -65,7 +65,6 @@ ${ commonheader(_("Metastore"), app_name, user, request) | n,unicode }
 <link rel="stylesheet" href="${ static('desktop/ext/css/bootstrap-wysihtml5-0.0.2.css') }">
 <link rel="stylesheet" href="${ static('notebook/css/notebook.css') }">
 
-## ${ render_bundle('vendors~tableBrowser') | n,unicode }
 ${ render_bundle('tableBrowser') | n,unicode }
 
 <span class="notebook">

+ 4 - 3
desktop/core/src/desktop/js/apps/notebook2/app.js

@@ -32,8 +32,9 @@ import {
   REDRAW_FIXED_HEADERS_EVENT,
   SHOW_GRID_SEARCH_EVENT,
   SHOW_NORMAL_RESULT_EVENT,
-  REDRAW_CHART_EVENT
+  REDRAW_CHART_EVENT, ACTIVE_SNIPPET_DIALECT_CHANGED_EVENT
 } from 'apps/notebook2/events';
+import {DIALECT} from 'apps/notebook2/snippet';
 
 export const initNotebook2 = () => {
   window.Clipboard = Clipboard;
@@ -502,7 +503,7 @@ export const initNotebook2 = () => {
           if (app === 'editor') {
             huePubSub.publish(REDRAW_FIXED_HEADERS_EVENT);
             huePubSub.publish('hue.scrollleft.show');
-            huePubSub.publish('active.snippet.type.changed', {
+            huePubSub.publish(ACTIVE_SNIPPET_DIALECT_CHANGED_EVENT, {
               type: viewModel.editorType(),
               isSqlDialect: viewModel.getSnippetViewSettings(viewModel.editorType()).sqlDialect
             });
@@ -713,7 +714,7 @@ export const initNotebook2 = () => {
         'jobbrowser.data',
         jobs => {
           const snippet = viewModel.selectedNotebook().snippets()[0];
-          if (!snippet || snippet.type() === 'impala') {
+          if (!snippet || snippet.dialect() === DIALECT.impala) {
             return;
           }
           if (jobs.length > 0) {

+ 5 - 5
desktop/core/src/desktop/js/apps/notebook2/components/ko.queryHistory.js

@@ -138,7 +138,7 @@ const TEMPLATE = `
                   $parent.openNotebook(uuid)
                 },
                 clickBubble: false
-              "><div data-bind="highlight: { value: query, dialect: $parent.type }"></div></td>
+              "><div data-bind="highlight: { value: query, dialect: $parent.dialect }"></div></td>
           </tr>
         </tbody>
       </table>
@@ -167,7 +167,7 @@ class QueryHistory extends DisposableComponent {
   constructor(params, element) {
     super();
     this.currentNotebook = params.currentNotebook;
-    this.type = params.type;
+    this.dialect = params.dialect;
     this.openFunction = params.openFunction;
     this.element = element;
 
@@ -211,7 +211,7 @@ class QueryHistory extends DisposableComponent {
     apiHelper
       .clearNotebookHistory({
         notebookJson: await this.currentNotebook.toContextJson(),
-        docType: this.type()
+        docType: this.dialect()
       })
       .then(() => {
         this.history.removeAll();
@@ -230,7 +230,7 @@ class QueryHistory extends DisposableComponent {
   }
 
   async exportHistory() {
-    const historyResponse = await apiHelper.getHistory({ type: this.type(), limit: 500 });
+    const historyResponse = await apiHelper.getHistory({ type: this.dialect(), limit: 500 });
 
     if (historyResponse && historyResponse.history) {
       window.location.href =
@@ -246,7 +246,7 @@ class QueryHistory extends DisposableComponent {
 
     try {
       const historyData = await apiHelper.getHistory({
-        type: this.type(),
+        type: this.dialect(),
         limit: QUERIES_PER_PAGE,
         page: this.historyCurrentPage(),
         docFilter: this.historyFilter()

+ 2 - 2
desktop/core/src/desktop/js/apps/notebook2/components/ko.savedQueries.js

@@ -109,7 +109,7 @@ class SavedQueries extends DisposableComponent {
 
     this.currentNotebook = params.currentNotebook;
     this.openFunction = params.openFunction;
-    this.type = params.type;
+    this.dialect = params.dialect;
     this.currentTab = params.currentTab;
 
     this.loading = ko.observable(true);
@@ -174,7 +174,7 @@ class SavedQueries extends DisposableComponent {
       },
       page: this.currentPage(),
       limit: QUERIES_PER_PAGE,
-      type: 'query-' + this.type(),
+      type: 'query-' + this.dialect(),
       query: this.filter(),
       include_trashed: false
     });

+ 3 - 3
desktop/core/src/desktop/js/apps/notebook2/components/ko.snippetEditorActions.js

@@ -24,7 +24,7 @@ import hueAnalytics from 'utils/hueAnalytics';
 import huePubSub from 'utils/huePubSub';
 import I18n from 'utils/i18n';
 import { SHOW_EVENT as SHOW_GIST_MODAL_EVENT } from 'ko/components/ko.shareGistModal';
-import { STATUS } from 'apps/notebook2/snippet';
+import { DIALECT, STATUS } from 'apps/notebook2/snippet';
 
 const TEMPLATE = `
 <div class="snippet-editor-actions">
@@ -101,7 +101,7 @@ class SnippetEditorActions {
     this.clearEnabled = this.snippet.isReady;
 
     this.compatibilityEnabled = ko.pureComputed(
-      () => this.snippet.type() === 'hive' || this.snippet.type() === 'impala'
+      () => this.snippet.dialect() === DIALECT.hive || this.snippet.dialect() === DIALECT.impala
     );
 
     this.createGistEnabled = ko.pureComputed(
@@ -169,7 +169,7 @@ class SnippetEditorActions {
         this.snippet.ace().getSelectedText() != ''
           ? this.snippet.ace().getSelectedText()
           : this.snippet.statement_raw(),
-      doc_type: this.snippet.type(),
+      doc_type: this.snippet.dialect(),
       name: this.snippet.name(),
       description: ''
     });

+ 9 - 8
desktop/core/src/desktop/js/apps/notebook2/editorViewModel.js

@@ -27,6 +27,7 @@ import Notebook from 'apps/notebook2/notebook';
 import Snippet from 'apps/notebook2/snippet';
 import SqlExecutable from 'apps/notebook2/execution/sqlExecutable';
 import { UPDATE_HISTORY_EVENT } from 'apps/notebook2/components/ko.queryHistory';
+import {ACTIVE_SNIPPET_DIALECT_CHANGED_EVENT} from 'apps/notebook2/events';
 
 class EditorViewModel {
   constructor(editorId, notebooks, options, CoordinatorEditorViewModel, RunningCoordinatorModel) {
@@ -174,9 +175,9 @@ class EditorViewModel {
       callback => {
         this.withActiveSnippet(activeSnippet => {
           if (callback) {
-            callback(activeSnippet.type());
+            callback(activeSnippet.dialect());
           } else {
-            huePubSub.publish('set.active.snippet.type', activeSnippet.type());
+            huePubSub.publish('set.active.snippet.type', activeSnippet.dialect());
           }
         });
       },
@@ -270,7 +271,7 @@ class EditorViewModel {
   getSnippetName(snippetType) {
     const availableSnippets = this.availableSnippets();
     for (let i = 0; i < availableSnippets.length; i++) {
-      if (availableSnippets[i].type() === snippetType) {
+      if (availableSnippets[i].dialect() === snippetType) {
         return availableSnippets[i].name();
       }
     }
@@ -306,7 +307,7 @@ class EditorViewModel {
 
     if (notebook.snippets().length > 0) {
       huePubSub.publish('detach.scrolls', notebook.snippets()[0]);
-      notebook.selectedSnippet(notebook.snippets()[notebook.snippets().length - 1].type());
+      notebook.selectedSnippet(notebook.snippets()[notebook.snippets().length - 1].dialect());
       notebook.snippets().forEach(snippet => {
         snippet.aceAutoExpand = false;
         snippet.statement_raw.valueHasMutated();
@@ -349,7 +350,7 @@ class EditorViewModel {
 
   async newNotebook(editorType, callback, queryTab) {
     return new Promise((resolve, reject) => {
-      huePubSub.publish('active.snippet.type.changed', {
+      huePubSub.publish(ACTIVE_SNIPPET_DIALECT_CHANGED_EVENT, {
         type: editorType,
         isSqlDialect: editorType ? this.getSnippetViewSettings(editorType).sqlDialect : undefined
       });
@@ -375,7 +376,7 @@ class EditorViewModel {
             if (window.location.getParameter('type') === '') {
               hueUtils.changeURLParameter('type', this.editorType());
             }
-            huePubSub.publish('active.snippet.type.changed', {
+            huePubSub.publish(ACTIVE_SNIPPET_DIALECT_CHANGED_EVENT, {
               type: editorType,
               isSqlDialect: editorType
                 ? this.getSnippetViewSettings(editorType).sqlDialect
@@ -410,7 +411,7 @@ class EditorViewModel {
       if (typeof skipUrlChange === 'undefined' && !this.isNotificationManager()) {
         if (this.editorMode()) {
           this.editorType(docData.document.type.substring('query-'.length));
-          huePubSub.publish('active.snippet.type.changed', {
+          huePubSub.publish(ACTIVE_SNIPPET_DIALECT_CHANGED_EVENT, {
             type: this.editorType(),
             isSqlDialect: this.getSnippetViewSettings(this.editorType()).sqlDialect
           });
@@ -498,7 +499,7 @@ class EditorViewModel {
   showSessionPanel() {
     this.withActiveSnippet(
       snippet => {
-        huePubSub.publish('session.panel.show', snippet.type());
+        huePubSub.publish('session.panel.show', snippet.dialect());
       },
       () => {
         huePubSub.publish('session.panel.show');

+ 1 - 0
desktop/core/src/desktop/js/apps/notebook2/events.js

@@ -1,3 +1,4 @@
+export const ACTIVE_SNIPPET_DIALECT_CHANGED_EVENT = 'active.snippet.type.changed';
 export const REDRAW_CHART_EVENT = 'result.chart.redraw';
 export const HIDE_FIXED_HEADERS_EVENT = 'result.grid.hide.fixed.headers';
 export const REDRAW_FIXED_HEADERS_EVENT = 'result.grid.redraw.fixed.headers';

+ 2 - 2
desktop/core/src/desktop/js/apps/notebook2/notebook.js

@@ -136,7 +136,7 @@ export default class Notebook {
         if (this.type().indexOf('query') === 0) {
           const whenDatabaseAvailable = function(snippet) {
             huePubSub.publish('assist.set.database', {
-              source: snippet.type(),
+              source: snippet.dialect(),
               namespace: snippet.namespace(),
               name: snippet.database()
             });
@@ -216,7 +216,7 @@ export default class Notebook {
   }
 
   getSnippets(type) {
-    return this.snippets().filter(snippet => snippet.type() === type);
+    return this.snippets().filter(snippet => snippet.dialect() === type);
   }
 
   loadScheduler() {

+ 5 - 5
desktop/core/src/desktop/js/apps/notebook2/notebook.test.js

@@ -39,21 +39,21 @@ describe('notebook.js', () => {
 
   it('should serialize a notebook to JSON', async () => {
     const notebook = new Notebook(viewModel, {});
-    notebook.addSnippet({ type: 'hive' });
-    notebook.addSnippet({ type: 'impala' });
+    notebook.addSnippet({ connector: { dialect: 'hive' } });
+    notebook.addSnippet({ connector: { dialect: 'impala' } });
 
     const notebookJSON = await notebook.toJson();
 
     const notebookRaw = JSON.parse(notebookJSON);
 
     expect(notebookRaw.snippets.length).toEqual(2);
-    expect(notebookRaw.snippets[0].type).toEqual('hive');
-    expect(notebookRaw.snippets[1].type).toEqual('impala');
+    expect(notebookRaw.snippets[0].connector.dialect).toEqual('hive');
+    expect(notebookRaw.snippets[1].connector.dialect).toEqual('impala');
   });
 
   it('should serialize a notebook context to JSON', async () => {
     const notebook = new Notebook(viewModel, {});
-    notebook.addSnippet({ type: 'hive' });
+    notebook.addSnippet({ connector: { dialect: 'hive' } });
 
     const notebookContextJSON = await notebook.toContextJson();
 

+ 117 - 117
desktop/core/src/desktop/js/apps/notebook2/snippet.js

@@ -35,7 +35,10 @@ import huePubSub from 'utils/huePubSub';
 import hueUtils from 'utils/hueUtils';
 import sessionManager from 'apps/notebook2/execution/sessionManager';
 import SqlExecutable from 'apps/notebook2/execution/sqlExecutable';
-import { REDRAW_FIXED_HEADERS_EVENT } from 'apps/notebook2/events';
+import {
+  ACTIVE_SNIPPET_DIALECT_CHANGED_EVENT,
+  REDRAW_FIXED_HEADERS_EVENT
+} from 'apps/notebook2/events';
 import { EXECUTABLE_UPDATED_EVENT, EXECUTION_STATUS } from 'apps/notebook2/execution/executable';
 import {
   ACTIVE_STATEMENT_CHANGED_EVENT,
@@ -43,6 +46,7 @@ import {
 } from 'ko/bindings/ace/aceLocationHandler';
 import { EXECUTE_ACTIVE_EXECUTABLE_EVENT } from 'apps/notebook2/components/ko.executableActions';
 import { UPDATE_HISTORY_EVENT } from 'apps/notebook2/components/ko.queryHistory';
+import { GET_KNOWN_CONFIG_EVENT } from 'utils/hueConfig';
 
 // TODO: Remove for ENABLE_NOTEBOOK_2. Temporary here for debug
 window.SqlExecutable = SqlExecutable;
@@ -50,7 +54,7 @@ window.Executor = Executor;
 
 export const CURRENT_QUERY_TAB_SWITCHED_EVENT = 'current.query.tab.switched';
 
-const TYPE = {
+export const DIALECT = {
   hive: 'hive',
   impala: 'impala',
   jar: 'jar',
@@ -107,68 +111,68 @@ const COMPATIBILITY_SOURCE_PLATFORMS = {
 };
 
 const COMPATIBILITY_TARGET_PLATFORMS = {
-  hive: { name: 'Hive', value: TYPE.hive },
-  impala: { name: 'Impala', value: TYPE.impala }
+  hive: { name: 'Hive', value: DIALECT.hive },
+  impala: { name: 'Impala', value: DIALECT.impala }
 };
 
 const getDefaultSnippetProperties = snippetType => {
   const properties = {};
 
-  if (snippetType === TYPE.jar || snippetType === TYPE.py) {
+  if (snippetType === DIALECT.jar || snippetType === DIALECT.py) {
     properties['driverCores'] = '';
     properties['executorCores'] = '';
     properties['numExecutors'] = '';
     properties['queue'] = '';
     properties['archives'] = [];
     properties['files'] = [];
-  } else if (snippetType === TYPE.java) {
+  } else if (snippetType === DIALECT.java) {
     properties['archives'] = [];
     properties['files'] = [];
     properties['capture_output'] = false;
-  } else if (snippetType === TYPE.shell) {
+  } else if (snippetType === DIALECT.shell) {
     properties['archives'] = [];
     properties['files'] = [];
-  } else if (snippetType === TYPE.mapreduce) {
+  } else if (snippetType === DIALECT.mapreduce) {
     properties['app_jar'] = '';
     properties['hadoopProperties'] = [];
     properties['jars'] = [];
     properties['files'] = [];
     properties['archives'] = [];
-  } else if (snippetType === TYPE.spark2) {
+  } else if (snippetType === DIALECT.spark2) {
     properties['app_name'] = '';
     properties['class'] = '';
     properties['jars'] = [];
     properties['spark_opts'] = [];
     properties['spark_arguments'] = [];
     properties['files'] = [];
-  } else if (snippetType === TYPE.sqoop1) {
+  } else if (snippetType === DIALECT.sqoop1) {
     properties['files'] = [];
-  } else if (snippetType === TYPE.hive) {
+  } else if (snippetType === DIALECT.hive) {
     properties['settings'] = [];
     properties['files'] = [];
     properties['functions'] = [];
     properties['arguments'] = [];
-  } else if (snippetType === TYPE.impala) {
+  } else if (snippetType === DIALECT.impala) {
     properties['settings'] = [];
-  } else if (snippetType === TYPE.pig) {
+  } else if (snippetType === DIALECT.pig) {
     properties['parameters'] = [];
     properties['hadoopProperties'] = [];
     properties['resources'] = [];
-  } else if (snippetType === TYPE.distcp) {
+  } else if (snippetType === DIALECT.distcp) {
     properties['source_path'] = '';
     properties['destination_path'] = '';
-  } else if (snippetType === TYPE.shell) {
+  } else if (snippetType === DIALECT.shell) {
     properties['command_path'] = '';
     properties['arguments'] = [];
     properties['env_var'] = [];
     properties['capture_output'] = true;
   }
 
-  if (snippetType === TYPE.jar || snippetType === TYPE.java) {
+  if (snippetType === DIALECT.jar || snippetType === DIALECT.java) {
     properties['app_jar'] = '';
     properties['class'] = '';
     properties['arguments'] = [];
-  } else if (snippetType === TYPE.py) {
+  } else if (snippetType === DIALECT.py) {
     properties['py_file'] = '';
     properties['arguments'] = [];
   }
@@ -185,23 +189,46 @@ export default class Snippet {
 
     this.id = ko.observable(snippetRaw.id || hueUtils.UUID());
     this.name = ko.observable(snippetRaw.name || '');
-    this.type = ko.observable();
-    this.type.subscribe(newValue => {
-      // TODO: Add session disposal for ENABLE_NOTEBOOK_2
-      // Pre-create a session to speed up execution
-      sessionManager.getSession({ type: newValue }).then(() => {
+
+    this.connector = ko.observable();
+
+    this.dialect = ko.pureComputed(() => this.connector() && this.connector().dialect);
+
+    this.isSqlDialect = ko.pureComputed(() => this.connector() && this.connector().is_sql);
+
+    this.connector.subscribe(newValue => {
+      sessionManager.getSession({ type: newValue.type }).then(() => {
         this.status(STATUS.ready);
       });
     });
-    this.type(snippetRaw.type || TYPE.hive);
+
+    huePubSub.publish(GET_KNOWN_CONFIG_EVENT, config => {
+      if (config && config.app_config && config.app_config.editor) {
+        const connectors = config.app_config.editor.interpreters;
+        if (snippetRaw.connector) {
+          this.connector(
+            connectors.find(connector => connector.type === snippetRaw.connector.type) ||
+              connectors.find(connector => connector.dialect === snippetRaw.connector.dialect)
+          );
+          if (!this.connector()) {
+            // Could happen if a connector is removed after a doc has been saved.
+            this.connector(snippetRaw.connector);
+          }
+        } else if (snippetRaw.type) {
+          // In the past "type" was used to denote dialect.
+          this.connector(connectors.find(connector => connector.dialect === snippetRaw.type));
+        }
+      }
+    });
+
     this.isBatchable = ko.pureComputed(
       () =>
-        this.type() === this.hive ||
-        this.type() === this.impala ||
+        this.dialect() === DIALECT.hive ||
+        this.dialect() === DIALECT.impala ||
         this.parentVm.availableLanguages.some(
           language =>
-            language.type === this.type() &&
-            (language.interface == 'oozie' || language.interface == 'sqlalchemy')
+            language.type === this.dialect() && // TODO: language.type = dialect ?
+            (language.interface === 'oozie' || language.interface === 'sqlalchemy')
         )
     );
 
@@ -231,8 +258,8 @@ export default class Snippet {
 
     this.inFocus.subscribe(newValue => {
       if (newValue) {
-        huePubSub.publish('active.snippet.type.changed', {
-          type: this.type(),
+        huePubSub.publish(ACTIVE_SNIPPET_DIALECT_CHANGED_EVENT, {
+          type: this.dialect(),
           isSqlDialect: this.isSqlDialect()
         });
       }
@@ -242,16 +269,12 @@ export default class Snippet {
 
     this.explanation = ko.observable();
 
-    this.getAceMode = () => this.parentVm.getSnippetViewSettings(this.type()).aceMode;
+    this.getAceMode = () => this.parentVm.getSnippetViewSettings(this.dialect()).aceMode;
 
     this.dbSelectionVisible = ko.observable(false);
 
     this.showExecutionAnalysis = ko.observable(false);
 
-    this.isSqlDialect = ko.pureComputed(
-      () => this.parentVm.getSnippetViewSettings(this.type()).sqlDialect
-    );
-
     // namespace and compute might be initialized as empty object {}
     this.namespace = ko.observable(
       snippetRaw.namespace && snippetRaw.namespace.id ? snippetRaw.namespace : undefined
@@ -283,8 +306,8 @@ export default class Snippet {
     huePubSub.subscribeOnce(
       'assist.source.set',
       source => {
-        if (source !== this.type()) {
-          huePubSub.publish('assist.set.source', this.type());
+        if (source !== this.dialect()) {
+          huePubSub.publish('assist.set.source', this.dialect());
         }
       },
       this.parentVm.huePubSubId
@@ -296,7 +319,7 @@ export default class Snippet {
 
     if (!this.database()) {
       huePubSub.publish('assist.get.database.callback', {
-        source: this.type(),
+        source: this.dialect(),
         callback: databaseDef => {
           this.handleAssistSelection(databaseDef);
         }
@@ -395,16 +418,16 @@ export default class Snippet {
     this.statusForButtons = ko.observable(STATUS_FOR_BUTTONS.executed);
 
     this.properties = ko.observable(
-      komapping.fromJS(snippetRaw.properties || getDefaultSnippetProperties(this.type()))
+      komapping.fromJS(snippetRaw.properties || getDefaultSnippetProperties(this.dialect()))
     );
     this.hasProperties = ko.pureComputed(
       () => Object.keys(komapping.toJS(this.properties())).length > 0
     );
 
-    this.viewSettings = ko.pureComputed(() => this.parentVm.getSnippetViewSettings(this.type()));
+    this.viewSettings = ko.pureComputed(() => this.parentVm.getSnippetViewSettings(this.dialect()));
 
     const previousProperties = {};
-    this.type.subscribe(
+    this.dialect.subscribe(
       oldValue => {
         previousProperties[oldValue] = this.properties();
       },
@@ -412,7 +435,7 @@ export default class Snippet {
       'beforeChange'
     );
 
-    this.type.subscribe(newValue => {
+    this.dialect.subscribe(newValue => {
       if (typeof previousProperties[newValue] !== 'undefined') {
         this.properties(previousProperties[newValue]);
       } else {
@@ -443,13 +466,13 @@ export default class Snippet {
     this.variables.subscribe(() => {
       $(document).trigger('updateResultHeaders', this);
     });
-    this.hasCurlyBracketParameters = ko.pureComputed(() => this.type() !== TYPE.pig);
+    this.hasCurlyBracketParameters = ko.pureComputed(() => this.dialect() !== DIALECT.pig);
 
     this.variableNames = ko.pureComputed(() => {
       let match,
         matches = {},
         matchList;
-      if (this.type() === TYPE.pig) {
+      if (this.dialect() === DIALECT.pig) {
         matches = this.getPigParameters();
       } else {
         const re = /(?:^|\W)\${(\w*)=?([^{}]*)}/g;
@@ -738,7 +761,7 @@ export default class Snippet {
 
     this.isLoading = ko.pureComputed(() => this.status() === STATUS.loading);
 
-    this.resultsKlass = ko.pureComputed(() => 'results ' + this.type());
+    this.resultsKlass = ko.pureComputed(() => 'results ' + this.dialect());
 
     this.errorsKlass = ko.pureComputed(() => this.resultsKlass() + ' alert alert-error');
 
@@ -769,11 +792,13 @@ export default class Snippet {
       this.compatibilitySourcePlatforms.push(COMPATIBILITY_SOURCE_PLATFORMS[key]);
     });
 
-    this.compatibilitySourcePlatform = ko.observable(COMPATIBILITY_SOURCE_PLATFORMS[this.type()]);
+    this.compatibilitySourcePlatform = ko.observable(
+      COMPATIBILITY_SOURCE_PLATFORMS[this.dialect()]
+    );
     this.compatibilitySourcePlatform.subscribe(newValue => {
-      if (newValue && newValue.value !== this.type()) {
+      if (newValue && newValue.value !== this.dialect()) {
         this.hasSuggestion(null);
-        this.compatibilityTargetPlatform(COMPATIBILITY_TARGET_PLATFORMS[this.type()]);
+        this.compatibilityTargetPlatform(COMPATIBILITY_TARGET_PLATFORMS[this.dialect()]);
         this.queryCompatibility();
       }
     });
@@ -782,7 +807,9 @@ export default class Snippet {
     Object.keys(COMPATIBILITY_TARGET_PLATFORMS).forEach(key => {
       this.compatibilityTargetPlatforms.push(COMPATIBILITY_TARGET_PLATFORMS[key]);
     });
-    this.compatibilityTargetPlatform = ko.observable(COMPATIBILITY_TARGET_PLATFORMS[this.type()]);
+    this.compatibilityTargetPlatform = ko.observable(
+      COMPATIBILITY_TARGET_PLATFORMS[this.dialect()]
+    );
 
     this.showOptimizer = ko.observable(
       apiHelper.getFromTotalStorage('editor', 'show.optimizer', false)
@@ -903,7 +930,7 @@ export default class Snippet {
         }
       };
 
-      if (this.type() === TYPE.hive || this.type() === TYPE.impala) {
+      if (this.dialect() === DIALECT.hive || this.dialect() === DIALECT.impala) {
         if (this.statement_raw()) {
           window.setTimeout(() => {
             this.checkComplexity();
@@ -920,14 +947,15 @@ export default class Snippet {
       () =>
         (this.statementType() === 'text' &&
           ((this.isSqlDialect() && this.statement() !== '') ||
-            ([TYPE.jar, TYPE.java, TYPE.spark2, TYPE.distcp].indexOf(this.type()) === -1 &&
+            ([DIALECT.jar, DIALECT.java, DIALECT.spark2, DIALECT.distcp].indexOf(this.dialect()) ===
+              -1 &&
               this.statement() !== '') ||
-            ([TYPE.jar, TYPE.java].indexOf(this.type()) !== -1 &&
+            ([DIALECT.jar, DIALECT.java].indexOf(this.dialect()) !== -1 &&
               (this.properties().app_jar() !== '' && this.properties().class() !== '')) ||
-            (TYPE.spark2 === this.type() && this.properties().jars().length > 0) ||
-            (TYPE.shell === this.type() && this.properties().command_path().length > 0) ||
-            (TYPE.mapreduce === this.type() && this.properties().app_jar().length > 0) ||
-            (TYPE.distcp === this.type() &&
+            (DIALECT.spark2 === this.dialect() && this.properties().jars().length > 0) ||
+            (DIALECT.shell === this.dialect() && this.properties().command_path().length > 0) ||
+            (DIALECT.mapreduce === this.dialect() && this.properties().app_jar().length > 0) ||
+            (DIALECT.distcp === this.dialect() &&
               this.properties().source_path().length > 0 &&
               this.properties().destination_path().length > 0))) ||
         (this.statementType() === 'file' && this.statementPath().length > 0) ||
@@ -963,10 +991,11 @@ export default class Snippet {
 
     this.activeExecutable = ko.observable();
 
+    // TODO: User connector instead of compute, namespace, sourceType, isOptimizerEnabled, isSqlEngine
     this.executor = new Executor({
       compute: this.compute,
       database: this.database,
-      sourceType: this.type,
+      sourceType: this.dialect,
       namespace: this.namespace,
       isOptimizerEnabled: this.parentVm.isOptimizerEnabled(),
       snippet: this,
@@ -1011,6 +1040,19 @@ export default class Snippet {
     huePubSub.publish(REFRESH_STATEMENT_LOCATIONS_EVENT, this);
   }
 
+  changeDialect(dialect) {
+    huePubSub.publish(GET_KNOWN_CONFIG_EVENT, config => {
+      if (config && config.app_config && config.app_config.editor) {
+        const foundConnector = config.app_config.editor.interpreters.find(
+          connector => connector.dialect === dialect
+        );
+        if (foundConnector) {
+          this.connector(foundConnector);
+        }
+      }
+    });
+  }
+
   updateFromExecutable(executable) {
     if (executable) {
       if (
@@ -1049,9 +1091,11 @@ export default class Snippet {
 
   checkCompatibility() {
     this.hasSuggestion(null);
-    this.compatibilitySourcePlatform(COMPATIBILITY_SOURCE_PLATFORMS[this.type()]);
+    this.compatibilitySourcePlatform(COMPATIBILITY_SOURCE_PLATFORMS[this.dialect()]);
     this.compatibilityTargetPlatform(
-      COMPATIBILITY_TARGET_PLATFORMS[this.type() === TYPE.hive ? TYPE.impala : TYPE.hive]
+      COMPATIBILITY_TARGET_PLATFORMS[
+        this.dialect() === DIALECT.hive ? DIALECT.impala : DIALECT.hive
+      ]
     );
     this.queryCompatibility();
   }
@@ -1063,59 +1107,13 @@ export default class Snippet {
     });
   }
 
-  // async executeNext() {
-  //   hueAnalytics.log('notebook', 'execute/' + this.type());
-  //
-  //   const now = new Date().getTime();
-  //   if (now - this.lastExecuted() < 1000) {
-  //     return; // Prevent fast clicks
-  //   }
-  //   this.lastExecuted(now);
-  //
-  //   if (this.type() === TYPE.impala) {
-  //     this.showExecutionAnalysis(false);
-  //     huePubSub.publish('editor.clear.execution.analysis');
-  //   }
-  //
-  //   // Editor based execution
-  //   if (this.ace()) {
-  //     const selectionRange = this.ace().getSelectionRange();
-  //
-  //     huePubSub.publish('ace.set.autoexpand', { autoExpand: false, snippet: this });
-  //     this.lastAceSelectionRowOffset(Math.min(selectionRange.start.row, selectionRange.end.row));
-  //   }
-  //
-  //   const $snip = $('#snippet_' + this.id());
-  //   $snip.find('.progress-snippet').animate(
-  //     {
-  //       height: '3px'
-  //     },
-  //     100
-  //   );
-  //
-  //   $('.jHueNotify').remove();
-  //   this.parentNotebook.forceHistoryInitialHeight(true);
-  //   this.errors([]);
-  //   huePubSub.publish('editor.clear.highlighted.errors', this.ace());
-  //   this.jobs([]);
-  //
-  //   this.parentNotebook.historyCurrentPage(1);
-  //
-  //   this.startLongOperationTimeout();
-  //
-  //   try {
-  //     await this.executor.executeNext();
-  //   } catch (error) {}
-  //   this.stopLongOperationTimeout();
-  // }
-
   execute() {
     // From ctrl + enter
     huePubSub.publish(EXECUTE_ACTIVE_EXECUTABLE_EVENT, this.activeExecutable());
   }
 
   fetchExecutionAnalysis() {
-    if (this.type() === TYPE.impala) {
+    if (this.dialect() === DIALECT.impala) {
       // TODO: Use real query ID
       huePubSub.publish('editor.update.execution.analysis', {
         analysisPossible: true,
@@ -1210,7 +1208,7 @@ export default class Snippet {
   }
 
   getPlaceHolder() {
-    return this.parentVm.getSnippetViewSettings(this.type()).placeHolder;
+    return this.parentVm.getSnippetViewSettings(this.dialect()).placeHolder;
   }
 
   async getSimilarQueries() {
@@ -1220,7 +1218,7 @@ export default class Snippet {
       .statementSimilarity({
         notebookJson: await this.parentNotebook.toContextJson(),
         snippetJson: this.toContextJson(),
-        sourcePlatform: this.type()
+        sourcePlatform: this.dialect()
       })
       .then(data => {
         if (data.status === 0) {
@@ -1259,7 +1257,7 @@ export default class Snippet {
       // Auth required
       this.status(STATUS.expired);
       $(document).trigger('showAuthModal', {
-        type: this.type(),
+        type: this.dialect(),
         callback: this.execute,
         message: data.message
       });
@@ -1305,7 +1303,7 @@ export default class Snippet {
   handleAssistSelection(databaseDef) {
     if (this.ignoreNextAssistDatabaseUpdate) {
       this.ignoreNextAssistDatabaseUpdate = false;
-    } else if (databaseDef.sourceType === this.type()) {
+    } else if (databaseDef.sourceType === this.dialect()) {
       if (this.namespace() !== databaseDef.namespace) {
         this.namespace(databaseDef.namespace);
       }
@@ -1345,7 +1343,7 @@ export default class Snippet {
     apiHelper.cancelActiveRequest(this.lastCompatibilityRequest);
 
     hueAnalytics.log('notebook', 'compatibility');
-    this.compatibilityCheckRunning(targetPlatform !== this.type());
+    this.compatibilityCheckRunning(targetPlatform !== this.dialect());
     this.hasSuggestion(null);
     const positionStatement = this.positionStatement();
 
@@ -1432,7 +1430,8 @@ export default class Snippet {
   toContextJson() {
     return JSON.stringify({
       id: this.id(),
-      type: this.type(),
+      type: this.dialect(),
+      connector: this.connector(),
       status: this.status(),
       statementType: this.statementType(),
       statement: this.statement(),
@@ -1455,6 +1454,7 @@ export default class Snippet {
       associatedDocumentUuid: this.associatedDocumentUuid(),
       executor: this.executor.toJs(),
       compute: this.compute(),
+      connector: this.connector(),
       currentQueryTab: this.currentQueryTab(),
       database: this.database(),
       id: this.id(),
@@ -1471,7 +1471,7 @@ export default class Snippet {
       statementPath: this.statementPath(),
       statementType: this.statementType(),
       status: this.status(),
-      type: this.type(),
+      type: this.dialect(), // TODO: Drop once connectors are stable
       variables: this.variables().map(variable => ({
         meta: variable.meta && {
           options: variable.meta.options && variable.meta.options(), // TODO: Map?
@@ -1493,7 +1493,7 @@ export default class Snippet {
   uploadQuery(query_id) {
     $.post('/metadata/api/optimizer/upload/query', {
       query_id: query_id,
-      sourcePlatform: this.type()
+      sourcePlatform: this.dialect()
     });
   }
 
@@ -1502,15 +1502,15 @@ export default class Snippet {
 
     $.post('/metadata/api/optimizer/upload/history', {
       n: typeof n != 'undefined' ? n : null,
-      sourcePlatform: this.type()
+      sourcePlatform: this.dialect()
     }).then(data => {
       if (data.status === 0) {
         $(document).trigger(
           'info',
-          data.upload_history[this.type()].count +
+          data.upload_history[this.dialect()].count +
             ' queries uploaded successfully. Processing them...'
         );
-        this.watchUploadStatus(data.upload_history[this.type()].status.workloadId);
+        this.watchUploadStatus(data.upload_history[this.dialect()].status.workloadId);
       } else {
         $(document).trigger('error', data.message);
       }
@@ -1529,7 +1529,7 @@ export default class Snippet {
         db_tables: JSON.stringify(
           options.activeTables.map(table => table.databaseName + '.' + table.tableName)
         ),
-        sourcePlatform: JSON.stringify(this.type()),
+        sourcePlatform: JSON.stringify(this.dialect()),
         with_ddl: JSON.stringify(true),
         with_table_stats: JSON.stringify(true),
         with_columns_stats: JSON.stringify(true)

+ 2 - 2
desktop/core/src/desktop/js/apps/notebook2/snippet.test.js

@@ -39,13 +39,13 @@ describe('snippet.js', () => {
 
   it('should serialize a snippet context to JSON', async () => {
     const notebook = new Notebook(viewModel, {});
-    const snippet = notebook.addSnippet({ type: 'hive' });
+    const snippet = notebook.addSnippet({ connector: { dialect: 'hive' } });
 
     const snippetContextJSON = snippet.toContextJson();
 
     const snippetContextRaw = JSON.parse(snippetContextJSON);
 
     expect(snippetContextRaw.id).toEqual(snippet.id());
-    expect(snippetContextRaw.type).toEqual('hive');
+    expect(snippetContextRaw.connector.dialect).toEqual('hive');
   });
 });

+ 1 - 1
desktop/core/src/desktop/templates/common_header.mako

@@ -151,7 +151,7 @@ if USE_NEW_EDITOR.get():
     ${ render_bundle('login', config='LOGIN') | n,unicode }
   %else:
     ${ render_bundle('vendors~hue~notebook~tableBrowser') | n,unicode }
-    ${ render_bundle('vendors~hue~tableBrowser') | n,unicode }
+    ${ render_bundle('vendors~hue~notebook') | n,unicode }
     ${ render_bundle('vendors~hue') | n,unicode }
     ${ render_bundle('hue~notebook') | n,unicode }
     ${ render_bundle('hue~notebook~tableBrowser') | n,unicode }

+ 1 - 1
desktop/core/src/desktop/templates/hue.mako

@@ -324,7 +324,7 @@ ${ hueIcons.symbols() }
 ${ commonshare() | n,unicode }
 
 ${ render_bundle('vendors~hue~notebook~tableBrowser') | n,unicode }
-${ render_bundle('vendors~hue~tableBrowser') | n,unicode }
+${ render_bundle('vendors~hue~notebook') | n,unicode }
 ${ render_bundle('vendors~hue') | n,unicode }
 ${ render_bundle('hue~notebook') | n,unicode }
 ${ render_bundle('hue~notebook~tableBrowser') | n,unicode }

+ 34 - 34
desktop/libs/notebook/src/notebook/templates/editor_components2.mako

@@ -502,7 +502,7 @@
           <li data-bind="click: function() { currentQueryTab('queryResults'); }, css: {'active': currentQueryTab() == 'queryResults'}">
             <a class="inactive-action" style="display:inline-block" href="#queryResults" data-toggle="tab">${_('Results')}
 ##               <!-- ko if: result.rows() != null  -->
-##               (<span data-bind="text: result.rows().toLocaleString() + (type() == 'impala' && result.rows() == 1024 ? '+' : '')" title="${ _('Number of rows') }"></span>)
+##               (<span data-bind="text: result.rows().toLocaleString() + (dialect() == 'impala' && result.rows() == 1024 ? '+' : '')" title="${ _('Number of rows') }"></span>)
 ##               <!-- /ko -->
             </a>
           </li>
@@ -520,7 +520,7 @@
           </li>
           <!-- /ko -->
 
-          <!-- ko if: HAS_WORKLOAD_ANALYTICS && type() === 'impala' -->
+          <!-- ko if: HAS_WORKLOAD_ANALYTICS && dialect() === 'impala' -->
           <li data-bind="visible: showExecutionAnalysis, click: function(){ currentQueryTab('executionAnalysis'); }, css: {'active': currentQueryTab() == 'executionAnalysis'}"><a class="inactive-action" href="#executionAnalysis" data-toggle="tab" data-bind="click: function(){ $('a[href=\'#executionAnalysis\']').tab('show'); }, event: {'shown': fetchExecutionAnalysis }"><span>${_('Execution Analysis')} </span><span></span></a></li>
           <!-- /ko -->
         </ul>
@@ -532,7 +532,7 @@
               params: {
                 currentNotebook: parentNotebook,
                 openFunction: parentVm.openNotebook.bind(parentVm),
-                type: type
+                dialect: dialect
               }
             } --><!-- /ko -->
           </div>
@@ -543,7 +543,7 @@
               params: {
                 currentNotebook: parentNotebook,
                 openFunction: parentVm.openNotebook.bind(parentVm),
-                type: type,
+                dialect: dialect,
                 currentTab: currentQueryTab
               }
             } --><!-- /ko -->
@@ -570,7 +570,7 @@
           % endif
 
           <div class="tab-pane" id="queryResults" data-bind="css: {'active': currentQueryTab() == 'queryResults'}">
-            <!-- ko if: ['text', 'jar', 'py', 'markdown'].indexOf(type()) === -1 -->
+            <!-- ko if: ['text', 'jar', 'py', 'markdown'].indexOf(dialect()) === -1 -->
               <!-- ko component: { name: 'snippet-results', params: {
                 activeExecutable: activeExecutable,
                 editorMode: parentVm.editorMode,
@@ -588,7 +588,7 @@
           </div>
           <!-- /ko -->
 
-          <!-- ko if: HAS_WORKLOAD_ANALYTICS && type() === 'impala' -->
+          <!-- ko if: HAS_WORKLOAD_ANALYTICS && dialect() === 'impala' -->
           <div class="tab-pane" id="executionAnalysis" data-bind="css: {'active': currentQueryTab() == 'executionAnalysis'}" style="padding: 10px;">
             <!-- ko component: { name: 'hue-execution-analysis' } --><!-- /ko -->
           </div>
@@ -644,7 +644,7 @@
     <div class="hover-actions inline pull-right" style="font-size: 15px;">
       <!-- ko template: { name: 'query-redacted${ suffix }' } --><!-- /ko -->
       <!-- ko template: { name: 'longer-operation${ suffix }' } --><!-- /ko -->
-##       <span class="execution-timer" data-bind="visible: type() != 'text' && status() != 'ready' && status() != 'loading', text: result.executionTime().toHHMMSS()" title="${ _('Execution time') }"></span>
+##       <span class="execution-timer" data-bind="visible: dialect() != 'text' && status() != 'ready' && status() != 'loading', text: result.executionTime().toHHMMSS()" title="${ _('Execution time') }"></span>
 
       <!-- ko template: { name: 'snippet-header-database-selection' } --><!-- /ko -->
 
@@ -659,7 +659,7 @@
     <div class="hover-actions inline pull-right" style="font-size: 15px; position: relative;" data-bind="style: { 'marginRight': $root.isPresentationMode() || $root.isResultFullScreenMode() ? '40px' : '0' }">
       <!-- ko template: { name: 'query-redacted${ suffix }' } --><!-- /ko -->
       <!-- ko template: { name: 'longer-operation${ suffix }' } --><!-- /ko -->
-##       <span class="execution-timer" data-bind="visible: type() != 'text' && status() != 'ready' && status() != 'loading', text: result.executionTime().toHHMMSS()" title="${ _('Execution time') }"></span>
+##       <span class="execution-timer" data-bind="visible: dialect() != 'text' && status() != 'ready' && status() != 'loading', text: result.executionTime().toHHMMSS()" title="${ _('Execution time') }"></span>
 
       <!-- ko template: { name: 'snippet-header-database-selection' } --><!-- /ko -->
 
@@ -668,11 +668,11 @@
   </script>
 
   <script type="text/html" id="snippet-header-database-selection">
-    <!-- ko if: isSqlDialect() || type() == 'spark2' -->
+    <!-- ko if: isSqlDialect() || dialect() === 'spark2' -->
     <!-- ko component: {
       name: 'hue-context-selector',
       params: {
-        sourceType: type,
+        sourceType: dialect,
         compute: compute,
         namespace: namespace,
         availableDatabases: availableDatabases,
@@ -686,7 +686,7 @@
   <script type="text/html" id="snippet${ suffix }">
     <div data-bind="visibleOnHover: { override: inFocus() || settingsVisible() || dbSelectionVisible() || $root.editorMode() || saveResultsModalVisible(), selector: '.hover-actions' }">
       <div class="snippet-container row-fluid" data-bind="visibleOnHover: { override: $root.editorMode() || inFocus() || saveResultsModalVisible(), selector: '.snippet-actions' }">
-        <div class="snippet card card-widget" data-bind="css: {'notebook-snippet' : ! $root.editorMode(), 'editor-mode': $root.editorMode(), 'active-editor': inFocus, 'snippet-text' : type() == 'text'}, attr: {'id': 'snippet_' + id()}, clickForAceFocus: ace">
+        <div class="snippet card card-widget" data-bind="css: {'notebook-snippet' : ! $root.editorMode(), 'editor-mode': $root.editorMode(), 'active-editor': inFocus, 'snippet-text' : dialect() === 'text'}, attr: {'id': 'snippet_' + id()}, clickForAceFocus: ace">
           <div style="position: relative;">
             <div class="snippet-row" style="position: relative;">
               <div class="snippet-body" data-bind="clickForAceFocus: ace, visible: ! $root.isResultFullScreenMode()">
@@ -696,20 +696,20 @@
                   <!-- ko template: { if: $root.editorMode(), name: 'editor-snippet-header${ suffix }' } --><!-- /ko -->
                   <!-- ko template: { if: ! $root.editorMode(), name: 'notebook-snippet-header${ suffix }' } --><!-- /ko -->
                 </h2>
-                <!-- ko template: { if: ['text', 'jar', 'java', 'spark2', 'distcp', 'shell', 'mapreduce', 'py', 'markdown'].indexOf(type()) == -1, name: 'code-editor-snippet-body${ suffix }' } --><!-- /ko -->
-                <!-- ko template: { if: type() == 'text', name: 'text-snippet-body${ suffix }' } --><!-- /ko -->
-                <!-- ko template: { if: type() == 'markdown', name: 'markdown-snippet-body${ suffix }' } --><!-- /ko -->
-                <!-- ko template: { if: ['java', 'distcp', 'shell', 'mapreduce', 'jar', 'py', 'spark2'].indexOf(type()) != -1, name: 'executable-snippet-body${ suffix }' } --><!-- /ko -->
+                <!-- ko template: { if: ['text', 'jar', 'java', 'spark2', 'distcp', 'shell', 'mapreduce', 'py', 'markdown'].indexOf(dialect()) == -1, name: 'code-editor-snippet-body${ suffix }' } --><!-- /ko -->
+                <!-- ko template: { if: dialect() == 'text', name: 'text-snippet-body${ suffix }' } --><!-- /ko -->
+                <!-- ko template: { if: dialect() == 'markdown', name: 'markdown-snippet-body${ suffix }' } --><!-- /ko -->
+                <!-- ko template: { if: ['java', 'distcp', 'shell', 'mapreduce', 'jar', 'py', 'spark2'].indexOf(dialect()) != -1, name: 'executable-snippet-body${ suffix }' } --><!-- /ko -->
               </div>
               <div style="position: absolute; top:25px; width: 100%" data-bind="style: { 'z-index': 400 - $index() }">
                 <!-- ko template: 'snippet-settings${ suffix }' --><!-- /ko -->
               </div>
             </div>
-            <!-- ko template: { if: ['text', 'markdown'].indexOf(type()) == -1, name: 'snippet-execution-status${ suffix }' } --><!-- /ko -->
-            <!-- ko template: { if: $root.editorMode() && ! $root.isResultFullScreenMode() && ['jar', 'java', 'spark2', 'distcp', 'shell', 'mapreduce', 'py'].indexOf(type()) == -1, name: 'snippet-code-resizer${ suffix }' } --><!-- /ko -->
+            <!-- ko template: { if: ['text', 'markdown'].indexOf(dialect()) == -1, name: 'snippet-execution-status${ suffix }' } --><!-- /ko -->
+            <!-- ko template: { if: $root.editorMode() && ! $root.isResultFullScreenMode() && ['jar', 'java', 'spark2', 'distcp', 'shell', 'mapreduce', 'py'].indexOf(dialect()) == -1, name: 'snippet-code-resizer${ suffix }' } --><!-- /ko -->
             <div class="snippet-footer-actions">
               <!-- ko template: { if: ! $root.editorMode() && ! $root.isPresentationMode() && ! $root.isResultFullScreenMode(), name: 'notebook-snippet-type-controls${ suffix }' } --><!-- /ko -->
-              <!-- ko template: { if: ['text', 'markdown'].indexOf(type()) == -1 && ! $root.isResultFullScreenMode(), name: 'snippet-execution-controls${ suffix }' } --><!-- /ko -->
+              <!-- ko template: { if: ['text', 'markdown'].indexOf(dialect()) == -1 && ! $root.isResultFullScreenMode(), name: 'snippet-execution-controls${ suffix }' } --><!-- /ko -->
             </div>
             <!-- ko if: !$root.isResultFullScreenMode() -->
             <!-- ko component: { name: 'executable-logs', params: {
@@ -723,7 +723,7 @@
             <!-- ko if: $root.editorMode() -->
             <!-- ko template: 'query-tabs${ suffix }' --><!-- /ko -->
             <!-- /ko -->
-            <!-- ko if: !$root.editorMode() && ['text', 'jar', 'java', 'distcp', 'shell', 'mapreduce', 'py', 'markdown'].indexOf(type()) === -1 -->
+            <!-- ko if: !$root.editorMode() && ['text', 'jar', 'java', 'distcp', 'shell', 'mapreduce', 'py', 'markdown'].indexOf(dialect()) === -1 -->
               <!-- ko component: { name: 'snippet-results', params: {
                 activeExecutable: activeExecutable,
                 editorMode: parentVm.editorMode,
@@ -780,12 +780,12 @@
   </script>
 
   <script type="text/html" id="code-editor-snippet-body${ suffix }">
-    <!-- ko if: HAS_OPTIMIZER && (type() == 'impala' || type() == 'hive') && ! $root.isPresentationMode() && ! $root.isResultFullScreenMode() -->
+    <!-- ko if: HAS_OPTIMIZER && (dialect() == 'impala' || dialect() == 'hive') && ! $root.isPresentationMode() && ! $root.isResultFullScreenMode() -->
     <div class="optimizer-container" data-bind="css: { 'active': showOptimizer }">
       <!-- ko if: hasSuggestion() -->
       <!-- ko with: suggestion() -->
       <!-- ko if: parseError -->
-      <!-- ko if: $parent.compatibilityTargetPlatform().value === $parent.type() && $parent.compatibilitySourcePlatform().value === $parent.type() -->
+      <!-- ko if: $parent.compatibilityTargetPlatform().value === $parent.dialect() && $parent.compatibilitySourcePlatform().value === $parent.dialect() -->
       <div class="optimizer-icon error" data-bind="click: function(){ $parent.showOptimizer(! $parent.showOptimizer()) }, attr: { 'title': $parent.showOptimizer() ? '${ _ko('Close Validator') }' : '${ _ko('Open Validator') }'}">
         <i class="fa fa-exclamation"></i>
       </div>
@@ -794,7 +794,7 @@
       <!-- /ko -->
       <!-- /ko -->
       ## Oracle, MySQL compatibility... as they return a parseError and not encounteredString.
-          <!-- ko if: $parent.compatibilityTargetPlatform().value !== $parent.type() || $parent.type() !== $parent.compatibilitySourcePlatform().value -->
+          <!-- ko if: $parent.compatibilityTargetPlatform().value !== $parent.dialect() || $parent.dialect() !== $parent.compatibilitySourcePlatform().value -->
       <div class="optimizer-icon warning" data-bind="click: function(){ $parent.showOptimizer(! $parent.showOptimizer()) }, attr: { 'title': $parent.showOptimizer() ? '${ _ko('Close Validator') }' : '${ _ko('Open Validator') }'}">
         <i class="fa fa-exclamation"></i>
       </div>
@@ -803,7 +803,7 @@
       <!-- /ko -->
       <!-- /ko -->
       <!-- /ko -->
-      <!-- ko if: !parseError() && ($parent.compatibilityTargetPlatform().value !== $parent.type() || $parent.compatibilitySourcePlatform().value !== $parent.type()) -->
+      <!-- ko if: !parseError() && ($parent.compatibilityTargetPlatform().value !== $parent.dialect() || $parent.compatibilitySourcePlatform().value !== $parent.dialect()) -->
       <!-- ko if: queryError.encounteredString().length == 0 -->
       <div class="optimizer-icon success" data-bind="click: function(){ $parent.showOptimizer(! $parent.showOptimizer()) }, attr: { 'title': $parent.showOptimizer() ? '${ _ko('Close Validator') }' : '${ _ko('Open Validator') }'}">
         <i class="fa fa-check"></i>
@@ -811,11 +811,11 @@
       <!-- ko if: $parent.showOptimizer -->
       <span class="optimizer-explanation alert-success alert-neutral">
               ${ _('The ') } <div data-bind="component: { name: 'hue-drop-down', params: { value: $parent.compatibilitySourcePlatform, entries: $parent.compatibilitySourcePlatforms, labelAttribute: 'name' } }" style="display: inline-block"></div>
-        <!-- ko if: $parent.compatibilitySourcePlatform().value === $parent.type() -->
+        <!-- ko if: $parent.compatibilitySourcePlatform().value === $parent.dialect() -->
         ${ _(' query is compatible with ') } <span data-bind="text: $parent.compatibilityTargetPlatform().name"></span>.
-                <a href="javascript:void(0)" data-bind="click: function() { $parent.type($parent.compatibilityTargetPlatform().value); }">${ _('Execute it with ') } <span data-bind="text: $parent.compatibilityTargetPlatform().name"></span></a>.
+                <a href="javascript:void(0)" data-bind="click: function() { $parent.dialect($parent.compatibilityTargetPlatform().value); }">${ _('Execute it with ') } <span data-bind="text: $parent.compatibilityTargetPlatform().name"></span></a>.
         <!-- /ko -->
-        <!-- ko if: $parent.compatibilitySourcePlatform().value !== $parent.type() -->
+        <!-- ko if: $parent.compatibilitySourcePlatform().value !== $parent.dialect() -->
         ${ _(' query is compatible with ') } <span data-bind="text: $parent.compatibilityTargetPlatform().name"></span>.
         <!-- /ko -->
             </span>
@@ -884,7 +884,7 @@
           <!-- /ko -->
           <label class="pull-left" style="margin-top: 6px;margin-right: 10px;" data-bind="visible: !associatedDocumentLoading()">${_('Document')}</label>
           <div class="selectize-wrapper" style="width: 300px;" data-bind="visible: !associatedDocumentLoading()">
-            <select placeholder="${ _('Search your documents...') }" data-bind="documentChooser: { loading: associatedDocumentLoading, value: associatedDocumentUuid, document: associatedDocument, type: type }"></select>
+            <select placeholder="${ _('Search your documents...') }" data-bind="documentChooser: { loading: associatedDocumentLoading, value: associatedDocumentUuid, document: associatedDocument, type: dialect }"></select>
           </div>
           <!-- ko if: associatedDocument() -->
           <div class="pull-left" style="margin-top: 4px">
@@ -1059,7 +1059,7 @@
   <script type="text/html" id="executable-snippet-body${ suffix }">
     <div style="padding:10px;">
       <form class="form-horizontal">
-        <!-- ko if: type() == 'distcp' -->
+        <!-- ko if: dialect() == 'distcp' -->
         <div class="control-group">
           <label class="control-label">${_('Source')}</label>
           <div class="controls">
@@ -1079,7 +1079,7 @@
           </div>
         </div>
         <!-- /ko -->
-        <!-- ko if: type() == 'shell' -->
+        <!-- ko if: dialect() == 'shell' -->
         <div class="control-group">
           <label class="control-label">${_('Script path')}</label>
           <div class="controls">
@@ -1094,7 +1094,7 @@
           </div>
         </div>
         <!-- /ko -->
-        <!-- ko if: type() == 'mapreduce' -->
+        <!-- ko if: dialect() == 'mapreduce' -->
         <div class="control-group">
           <label class="control-label">${_('Jar path')}</label>
           <div class="controls">
@@ -1108,7 +1108,7 @@
           </div>
         </div>
         <!-- /ko -->
-        <!-- ko if: type() == 'jar' || type() == 'java' -->
+        <!-- ko if: dialect() === 'jar' || dialect() === 'java' -->
         <div class="control-group">
           <label class="control-label">${_('Path')}</label>
           <div class="controls">
@@ -1128,7 +1128,7 @@
           </div>
         </div>
         <!-- /ko -->
-        <!-- ko if: type() == 'py'-->
+        <!-- ko if: dialect() === 'py'-->
         <div class="control-group">
           <label class="control-label">${_('Path')}</label>
           <div class="controls">
@@ -1136,7 +1136,7 @@
           </div>
         </div>
         <!-- /ko -->
-        <!-- ko if: type() == 'spark2' -->
+        <!-- ko if: dialect() === 'spark2' -->
         <div class="control-group">
           <!-- ko template: { if: typeof properties().jars != 'undefined', name: 'property', data: { type: 'csv-hdfs-files', label: '${ _ko('Libs') }', value: properties().jars, title: '${ _ko('Path to jar or python files.') }', placeholder: '${ _ko('e.g. /user/hue/pi.py') }'}} --><!-- /ko -->
         </div>
@@ -1225,7 +1225,7 @@
       </a>
 
       <ul class="dropdown-menu" data-bind="foreach: $root.availableSnippets">
-        <li><a class="pointer" data-bind="click: function(){ $parent.type($data.type()); }, text: name"></a></li>
+        <li><a class="pointer" data-bind="click: function(){ $parent.changeDialect($data.type()); }, text: name"></a></li>
       </ul>
     </div>
   </script>