Browse Source

[editor] Handle query history additions in the Query Table instead of snippet for editor v2

Johan Ahlen 4 years ago
parent
commit
a9a087cf80
20 changed files with 175 additions and 130 deletions
  1. 10 6
      desktop/core/src/desktop/js/apps/editor/components/ExecutableProgressBar.test.ts
  2. 6 2
      desktop/core/src/desktop/js/apps/editor/components/ExecutableProgressBar.vue
  3. 6 2
      desktop/core/src/desktop/js/apps/editor/components/ExecuteButton.test.ts
  4. 3 2
      desktop/core/src/desktop/js/apps/editor/components/ExecuteButton.vue
  5. 2 3
      desktop/core/src/desktop/js/apps/editor/components/ExecuteLimitInput.vue
  6. 22 18
      desktop/core/src/desktop/js/apps/editor/components/QueryHistoryTable.vue
  7. 3 3
      desktop/core/src/desktop/js/apps/editor/components/aceEditor/AceGutterHandler.ts
  8. 2 2
      desktop/core/src/desktop/js/apps/editor/components/executableStateHandler.js
  9. 15 12
      desktop/core/src/desktop/js/apps/editor/components/executionAnalysis/ExecutionAnalysisPanel.vue
  10. 8 6
      desktop/core/src/desktop/js/apps/editor/components/ko.executableLogs.js
  11. 15 14
      desktop/core/src/desktop/js/apps/editor/components/result/ResultTable.vue
  12. 5 4
      desktop/core/src/desktop/js/apps/editor/components/resultChart/ko.resultChart.js
  13. 28 0
      desktop/core/src/desktop/js/apps/editor/execution/events.ts
  14. 14 9
      desktop/core/src/desktop/js/apps/editor/execution/executable.ts
  15. 6 4
      desktop/core/src/desktop/js/apps/editor/execution/executionLogs.ts
  16. 5 3
      desktop/core/src/desktop/js/apps/editor/execution/executionResult.ts
  17. 3 28
      desktop/core/src/desktop/js/apps/editor/snippet.js
  18. 5 5
      desktop/core/src/desktop/js/components/utils/SubscriptionTracker.ts
  19. 2 2
      desktop/core/src/desktop/js/ko/bindings/ace/aceGutterHandler.js
  20. 15 5
      desktop/core/src/desktop/js/utils/huePubSub.ts

+ 10 - 6
desktop/core/src/desktop/js/apps/editor/components/ExecutableProgressBar.test.ts

@@ -14,12 +14,10 @@
 // See the License for the specific language governing permissions and
 // See the License for the specific language governing permissions and
 // limitations under the License.
 // limitations under the License.
 
 
+import { EXECUTABLE_UPDATED_TOPIC, ExecutableUpdatedEvent } from 'apps/editor/execution/events';
 import { nextTick } from 'vue';
 import { nextTick } from 'vue';
 import { shallowMount, mount } from '@vue/test-utils';
 import { shallowMount, mount } from '@vue/test-utils';
-import Executable, {
-  EXECUTABLE_UPDATED_EVENT,
-  ExecutionStatus
-} from 'apps/editor/execution/executable';
+import Executable, { ExecutionStatus } from 'apps/editor/execution/executable';
 import ExecutableProgressBar from './ExecutableProgressBar.vue';
 import ExecutableProgressBar from './ExecutableProgressBar.vue';
 import huePubSub from 'utils/huePubSub';
 import huePubSub from 'utils/huePubSub';
 
 
@@ -49,7 +47,10 @@ describe('ExecutableProgressBar.vue', () => {
 
 
     mockExecutable.status = ExecutionStatus.failed;
     mockExecutable.status = ExecutionStatus.failed;
     mockExecutable.progress = 10;
     mockExecutable.progress = 10;
-    huePubSub.publish(EXECUTABLE_UPDATED_EVENT, mockExecutable);
+    huePubSub.publish<ExecutableUpdatedEvent>(
+      EXECUTABLE_UPDATED_TOPIC,
+      mockExecutable as Executable
+    );
     await nextTick();
     await nextTick();
 
 
     expect(progressDiv.style['width']).toEqual('100%');
     expect(progressDiv.style['width']).toEqual('100%');
@@ -75,7 +76,10 @@ describe('ExecutableProgressBar.vue', () => {
 
 
     mockExecutable.status = ExecutionStatus.running;
     mockExecutable.status = ExecutionStatus.running;
     mockExecutable.progress = 10;
     mockExecutable.progress = 10;
-    huePubSub.publish(EXECUTABLE_UPDATED_EVENT, mockExecutable);
+    huePubSub.publish<ExecutableUpdatedEvent>(
+      EXECUTABLE_UPDATED_TOPIC,
+      mockExecutable as Executable
+    );
     await nextTick();
     await nextTick();
 
 
     expect(progressDiv.style['width']).toEqual('10%');
     expect(progressDiv.style['width']).toEqual('10%');

+ 6 - 2
desktop/core/src/desktop/js/apps/editor/components/ExecutableProgressBar.vue

@@ -29,10 +29,11 @@
 </template>
 </template>
 
 
 <script lang="ts">
 <script lang="ts">
+  import { EXECUTABLE_UPDATED_TOPIC, ExecutableUpdatedEvent } from 'apps/editor/execution/events';
   import { defineComponent, PropType, ref, toRefs, watch } from 'vue';
   import { defineComponent, PropType, ref, toRefs, watch } from 'vue';
 
 
   import SubscriptionTracker from 'components/utils/SubscriptionTracker';
   import SubscriptionTracker from 'components/utils/SubscriptionTracker';
-  import Executable, { EXECUTABLE_UPDATED_EVENT, ExecutionStatus } from '../execution/executable';
+  import Executable, { ExecutionStatus } from '../execution/executable';
 
 
   export default defineComponent({
   export default defineComponent({
     name: 'ExecutableProgressBar',
     name: 'ExecutableProgressBar',
@@ -73,7 +74,7 @@
         { immediate: true }
         { immediate: true }
       );
       );
 
 
-      subTracker.subscribe(EXECUTABLE_UPDATED_EVENT, updated => {
+      subTracker.subscribe<ExecutableUpdatedEvent>(EXECUTABLE_UPDATED_TOPIC, updated => {
         if (executable.value && executable.value.id === updated.id) {
         if (executable.value && executable.value.id === updated.id) {
           updateFromExecutable(updated);
           updateFromExecutable(updated);
         }
         }
@@ -129,15 +130,18 @@
 
 
       .executable-progress-bar {
       .executable-progress-bar {
         background-color: $fluid-white;
         background-color: $fluid-white;
+
         @include ease-transition(height);
         @include ease-transition(height);
 
 
         @include keyframes(pulsate) {
         @include keyframes(pulsate) {
           0% {
           0% {
             margin-left: 0;
             margin-left: 0;
           }
           }
+
           50% {
           50% {
             margin-left: 30px;
             margin-left: 30px;
           }
           }
+
           100% {
           100% {
             margin-left: 0;
             margin-left: 0;
           }
           }

+ 6 - 2
desktop/core/src/desktop/js/apps/editor/components/ExecuteButton.test.ts

@@ -14,11 +14,12 @@
 // See the License for the specific language governing permissions and
 // See the License for the specific language governing permissions and
 // limitations under the License.
 // limitations under the License.
 
 
+import { EXECUTABLE_UPDATED_TOPIC, ExecutableUpdatedEvent } from 'apps/editor/execution/events';
 import SqlExecutable from 'apps/editor/execution/sqlExecutable';
 import SqlExecutable from 'apps/editor/execution/sqlExecutable';
 import huePubSub from 'utils/huePubSub';
 import huePubSub from 'utils/huePubSub';
 import { nextTick } from 'vue';
 import { nextTick } from 'vue';
 import { mount, shallowMount } from '@vue/test-utils';
 import { mount, shallowMount } from '@vue/test-utils';
-import { EXECUTABLE_UPDATED_EVENT, ExecutionStatus } from 'apps/editor/execution/executable';
+import Executable, { ExecutionStatus } from 'apps/editor/execution/executable';
 import sessionManager from 'apps/editor/execution/sessionManager';
 import sessionManager from 'apps/editor/execution/sessionManager';
 import ExecuteButton from './ExecuteButton.vue';
 import ExecuteButton from './ExecuteButton.vue';
 import { noop } from 'utils/hueUtils';
 import { noop } from 'utils/hueUtils';
@@ -112,7 +113,10 @@ describe('ExecuteButton.vue', () => {
 
 
     expect(executeCalled).toBeTruthy();
     expect(executeCalled).toBeTruthy();
     mockExecutable.status = ExecutionStatus.running;
     mockExecutable.status = ExecutionStatus.running;
-    huePubSub.publish(EXECUTABLE_UPDATED_EVENT, mockExecutable);
+    huePubSub.publish<ExecutableUpdatedEvent>(
+      EXECUTABLE_UPDATED_TOPIC,
+      mockExecutable as Executable
+    );
 
 
     await nextTick();
     await nextTick();
 
 

+ 3 - 2
desktop/core/src/desktop/js/apps/editor/components/ExecuteButton.vue

@@ -55,6 +55,7 @@
 </template>
 </template>
 
 
 <script lang="ts">
 <script lang="ts">
+  import { EXECUTABLE_UPDATED_TOPIC, ExecutableUpdatedEvent } from 'apps/editor/execution/events';
   import { defineComponent, PropType, ref, toRefs, watch } from 'vue';
   import { defineComponent, PropType, ref, toRefs, watch } from 'vue';
 
 
   import SqlExecutable from 'apps/editor/execution/sqlExecutable';
   import SqlExecutable from 'apps/editor/execution/sqlExecutable';
@@ -64,7 +65,7 @@
   import I18n from 'utils/i18n';
   import I18n from 'utils/i18n';
 
 
   import { Session } from 'apps/editor/execution/api';
   import { Session } from 'apps/editor/execution/api';
-  import { EXECUTABLE_UPDATED_EVENT, ExecutionStatus } from 'apps/editor/execution/executable';
+  import { ExecutionStatus } from 'apps/editor/execution/executable';
   import sessionManager from 'apps/editor/execution/sessionManager';
   import sessionManager from 'apps/editor/execution/sessionManager';
 
 
   const EXECUTE_ACTIVE_EXECUTABLE_EVENT = 'executable.active.executable';
   const EXECUTE_ACTIVE_EXECUTABLE_EVENT = 'executable.active.executable';
@@ -137,7 +138,7 @@
         { immediate: true }
         { immediate: true }
       );
       );
 
 
-      subTracker.subscribe(EXECUTABLE_UPDATED_EVENT, updatedExecutable => {
+      subTracker.subscribe<ExecutableUpdatedEvent>(EXECUTABLE_UPDATED_TOPIC, updatedExecutable => {
         if (executable.value && executable.value.id === updatedExecutable.id) {
         if (executable.value && executable.value.id === updatedExecutable.id) {
           updateFromExecutable(updatedExecutable);
           updateFromExecutable(updatedExecutable);
         }
         }

+ 2 - 3
desktop/core/src/desktop/js/apps/editor/components/ExecuteLimitInput.vue

@@ -33,14 +33,13 @@
 </template>
 </template>
 
 
 <script lang="ts">
 <script lang="ts">
+  import { EXECUTABLE_UPDATED_TOPIC, ExecutableUpdatedEvent } from 'apps/editor/execution/events';
   import { defineComponent, PropType, ref, toRefs, watch } from 'vue';
   import { defineComponent, PropType, ref, toRefs, watch } from 'vue';
 
 
   import SqlExecutable from 'apps/editor/execution/sqlExecutable';
   import SqlExecutable from 'apps/editor/execution/sqlExecutable';
   import SubscriptionTracker from 'components/utils/SubscriptionTracker';
   import SubscriptionTracker from 'components/utils/SubscriptionTracker';
   import I18n from 'utils/i18n';
   import I18n from 'utils/i18n';
 
 
-  import { EXECUTABLE_UPDATED_EVENT } from 'apps/editor/execution/executable';
-
   export default defineComponent({
   export default defineComponent({
     name: 'ExecuteLimitInput',
     name: 'ExecuteLimitInput',
     props: {
     props: {
@@ -61,7 +60,7 @@
           null;
           null;
       };
       };
 
 
-      subTracker.subscribe(EXECUTABLE_UPDATED_EVENT, updatedExecutable => {
+      subTracker.subscribe<ExecutableUpdatedEvent>(EXECUTABLE_UPDATED_TOPIC, updatedExecutable => {
         if (executable.value && executable.value.id === updatedExecutable.id) {
         if (executable.value && executable.value.id === updatedExecutable.id) {
           updateFromExecutable(updatedExecutable);
           updateFromExecutable(updatedExecutable);
         }
         }

+ 22 - 18
desktop/core/src/desktop/js/apps/editor/components/QueryHistoryTable.vue

@@ -99,6 +99,10 @@
   import { fetchHistory, FetchHistoryResponse } from '../api';
   import { fetchHistory, FetchHistoryResponse } from '../api';
   import { ExecutionStatus } from '../execution/executable';
   import { ExecutionStatus } from '../execution/executable';
   import { CancellablePromise } from 'api/cancellablePromise';
   import { CancellablePromise } from 'api/cancellablePromise';
+  import {
+    EXECUTABLE_TRANSITIONED_TOPIC,
+    ExecutableTransitionedEvent
+  } from 'apps/editor/execution/events';
   import { Connector } from 'config/types';
   import { Connector } from 'config/types';
   import HueButton from 'components/HueButton.vue';
   import HueButton from 'components/HueButton.vue';
   import { Column } from 'components/HueTable';
   import { Column } from 'components/HueTable';
@@ -124,7 +128,6 @@
     uuid: string;
     uuid: string;
   }
   }
 
 
-  const ADD_TO_HISTORY_EVENT = 'query.history.add';
   const IGNORE_NEXT_UNLOAD_EVENT = 'ignore.next.unload';
   const IGNORE_NEXT_UNLOAD_EVENT = 'ignore.next.unload';
 
 
   const trimEllipsis = (str: string): string =>
   const trimEllipsis = (str: string): string =>
@@ -235,25 +238,26 @@
       watch(connector, () => debouncedUpdate(), { immediate: true });
       watch(connector, () => debouncedUpdate(), { immediate: true });
       watch(searchFilter, () => debouncedUpdate());
       watch(searchFilter, () => debouncedUpdate());
 
 
-      subTracker.subscribe(
-        ADD_TO_HISTORY_EVENT,
-        (details: {
-          absoluteUrl?: string;
-          lastExecuted: number;
-          name: string;
-          statement: string;
-          status: ExecutionStatus;
-          uuid: string;
-        }) => {
-          if (!history.value.some(entry => entry.uuid === details.uuid)) {
+      subTracker.subscribe<ExecutableTransitionedEvent>(
+        EXECUTABLE_TRANSITIONED_TOPIC,
+        ({ newStatus, executable }) => {
+          if (
+            (newStatus === ExecutionStatus.available ||
+              newStatus === ExecutionStatus.failed ||
+              newStatus === ExecutionStatus.success) &&
+            executable.history &&
+            executable.handle &&
+            executable.executor.connector().id === connector.value?.id &&
+            !history.value.some(entry => entry.uuid === executable.history!.uuid)
+          ) {
             history.value = [
             history.value = [
               {
               {
-                absoluteUrl: details.absoluteUrl,
-                lastExecuted: details.lastExecuted,
-                name: details.name,
-                query: details.statement,
-                status: details.status,
-                uuid: details.uuid
+                absoluteUrl: undefined,
+                lastExecuted: executable.executeStarted,
+                name: executable.executor.snippet?.name() || '',
+                query: executable.handle.statement!,
+                status: executable.status,
+                uuid: executable.history.uuid!
               },
               },
               ...history.value
               ...history.value
             ];
             ];

+ 3 - 3
desktop/core/src/desktop/js/apps/editor/components/aceEditor/AceGutterHandler.ts

@@ -14,11 +14,11 @@
 // See the License for the specific language governing permissions and
 // See the License for the specific language governing permissions and
 // limitations under the License.
 // limitations under the License.
 
 
+import { EXECUTABLE_UPDATED_TOPIC, ExecutableUpdatedEvent } from 'apps/editor/execution/events';
 import { Ace } from 'ext/ace';
 import { Ace } from 'ext/ace';
 
 
 import AceAnchoredRange from './AceAnchoredRange';
 import AceAnchoredRange from './AceAnchoredRange';
 import { ACTIVE_STATEMENT_CHANGED_EVENT } from './AceLocationHandler';
 import { ACTIVE_STATEMENT_CHANGED_EVENT } from './AceLocationHandler';
-import { EXECUTABLE_UPDATED_EVENT } from 'apps/editor/execution/executable';
 import Executor from 'apps/editor/execution/executor';
 import Executor from 'apps/editor/execution/executor';
 import SqlExecutable from 'apps/editor/execution/sqlExecutable';
 import SqlExecutable from 'apps/editor/execution/sqlExecutable';
 import SubscriptionTracker, { Disposable } from 'components/utils/SubscriptionTracker';
 import SubscriptionTracker, { Disposable } from 'components/utils/SubscriptionTracker';
@@ -72,7 +72,7 @@ export default class AceGutterHandler implements Disposable {
     this.subTracker.addDisposable(activeStatementAnchor);
     this.subTracker.addDisposable(activeStatementAnchor);
 
 
     if (this.executor) {
     if (this.executor) {
-      this.subTracker.subscribe(EXECUTABLE_UPDATED_EVENT, (executable: SqlExecutable) => {
+      this.subTracker.subscribe<ExecutableUpdatedEvent>(EXECUTABLE_UPDATED_TOPIC, executable => {
         if (executable.executor === this.executor) {
         if (executable.executor === this.executor) {
           let anchor = this.trackedAnchors.get(executable.id);
           let anchor = this.trackedAnchors.get(executable.id);
           if (!anchor) {
           if (!anchor) {
@@ -89,7 +89,7 @@ export default class AceGutterHandler implements Disposable {
           anchor.removeGutterCss();
           anchor.removeGutterCss();
           anchor.removeMarkerRowCss();
           anchor.removeMarkerRowCss();
 
 
-          const statement = executable.parsedStatement;
+          const statement = (executable as SqlExecutable).parsedStatement;
           const leadingEmptyLineCount = getLeadingEmptyLineCount(statement);
           const leadingEmptyLineCount = getLeadingEmptyLineCount(statement);
           anchor.move(statement.location, leadingEmptyLineCount);
           anchor.move(statement.location, leadingEmptyLineCount);
 
 

+ 2 - 2
desktop/core/src/desktop/js/apps/editor/components/executableStateHandler.js

@@ -15,8 +15,8 @@
 // limitations under the License.
 // limitations under the License.
 
 
 import * as ko from 'knockout';
 import * as ko from 'knockout';
-import { RESULT_UPDATED_EVENT } from 'apps/editor/execution/executionResult';
 import huePubSub from 'utils/huePubSub';
 import huePubSub from 'utils/huePubSub';
+import { EXECUTABLE_RESULT_UPDATED_TOPIC } from '../execution/events';
 
 
 export const trackResult = (activeExecutable, onChange) => {
 export const trackResult = (activeExecutable, onChange) => {
   if (!activeExecutable) {
   if (!activeExecutable) {
@@ -39,7 +39,7 @@ export const trackResult = (activeExecutable, onChange) => {
     return { dispose: () => {} };
     return { dispose: () => {} };
   }
   }
 
 
-  const updateSub = huePubSub.subscribe(RESULT_UPDATED_EVENT, executionResult => {
+  const updateSub = huePubSub.subscribe(EXECUTABLE_RESULT_UPDATED_TOPIC, executionResult => {
     if (executionResult === executable.result) {
     if (executionResult === executable.result) {
       onChange(executionResult);
       onChange(executionResult);
     }
     }

+ 15 - 12
desktop/core/src/desktop/js/apps/editor/components/executionAnalysis/ExecutionAnalysisPanel.vue

@@ -46,19 +46,19 @@
 </template>
 </template>
 
 
 <script lang="ts">
 <script lang="ts">
+  import {
+    EXECUTABLE_LOGS_UPDATED_TOPIC,
+    EXECUTABLE_UPDATED_TOPIC,
+    ExecutableLogsUpdatedEvent,
+    ExecutableUpdatedEvent
+  } from 'apps/editor/execution/events';
   import { debounce } from 'lodash';
   import { debounce } from 'lodash';
   import { defineComponent, computed, onBeforeUnmount, ref, reactive } from 'vue';
   import { defineComponent, computed, onBeforeUnmount, ref, reactive } from 'vue';
 
 
   import { ExecutionJob } from 'apps/editor/execution/api';
   import { ExecutionJob } from 'apps/editor/execution/api';
-  import Executable, {
-    EXECUTABLE_UPDATED_EVENT,
-    ExecutionStatus
-  } from 'apps/editor/execution/executable';
+  import Executable, { ExecutionStatus } from 'apps/editor/execution/executable';
   import SqlExecutable from 'apps/editor/execution/sqlExecutable';
   import SqlExecutable from 'apps/editor/execution/sqlExecutable';
-  import ExecutionLogs, {
-    ExecutionError,
-    LOGS_UPDATED_EVENT
-  } from 'apps/editor/execution/executionLogs';
+  import { ExecutionError } from 'apps/editor/execution/executionLogs';
   import HueLink from 'components/HueLink.vue';
   import HueLink from 'components/HueLink.vue';
   import LogsPanel from 'components/LogsPanel.vue';
   import LogsPanel from 'components/LogsPanel.vue';
   import SubscriptionTracker from 'components/utils/SubscriptionTracker';
   import SubscriptionTracker from 'components/utils/SubscriptionTracker';
@@ -115,11 +115,14 @@
 
 
       updateFromExecutable(props.executable);
       updateFromExecutable(props.executable);
 
 
-      subTracker.subscribe(EXECUTABLE_UPDATED_EVENT, updateFromExecutable);
+      subTracker.subscribe<ExecutableUpdatedEvent>(EXECUTABLE_UPDATED_TOPIC, updateFromExecutable);
 
 
-      subTracker.subscribe(LOGS_UPDATED_EVENT, (executionLogs: ExecutionLogs) => {
-        updateFromExecutable(executionLogs.executable);
-      });
+      subTracker.subscribe<ExecutableLogsUpdatedEvent>(
+        EXECUTABLE_LOGS_UPDATED_TOPIC,
+        executionLogs => {
+          updateFromExecutable(executionLogs.executable);
+        }
+      );
 
 
       return {
       return {
         analysisAvailable,
         analysisAvailable,

+ 8 - 6
desktop/core/src/desktop/js/apps/editor/components/ko.executableLogs.js

@@ -19,11 +19,13 @@ import * as ko from 'knockout';
 import 'ko/bindings/ko.publish';
 import 'ko/bindings/ko.publish';
 
 
 import componentUtils from 'ko/components/componentUtils';
 import componentUtils from 'ko/components/componentUtils';
-import { EXECUTABLE_UPDATED_EVENT } from 'apps/editor/execution/executable';
 import DisposableComponent from 'ko/components/DisposableComponent';
 import DisposableComponent from 'ko/components/DisposableComponent';
 import I18n from 'utils/i18n';
 import I18n from 'utils/i18n';
-import { RESULT_UPDATED_EVENT } from 'apps/editor/execution/executionResult';
-import { LOGS_UPDATED_EVENT } from 'apps/editor/execution/executionLogs';
+import {
+  EXECUTABLE_LOGS_UPDATED_TOPIC,
+  EXECUTABLE_RESULT_UPDATED_TOPIC,
+  EXECUTABLE_UPDATED_TOPIC
+} from '../execution/events';
 
 
 export const NAME = 'executable-logs';
 export const NAME = 'executable-logs';
 
 
@@ -114,7 +116,7 @@ class ExecutableLogs extends DisposableComponent {
     this.errors = ko.observableArray();
     this.errors = ko.observableArray();
     this.logs = ko.observable();
     this.logs = ko.observable();
 
 
-    this.subscribe(EXECUTABLE_UPDATED_EVENT, executable => {
+    this.subscribe(EXECUTABLE_UPDATED_TOPIC, executable => {
       if (this.activeExecutable() === executable) {
       if (this.activeExecutable() === executable) {
         this.updateFromExecutable(executable);
         this.updateFromExecutable(executable);
       }
       }
@@ -122,13 +124,13 @@ class ExecutableLogs extends DisposableComponent {
 
 
     this.subscribe(this.activeExecutable, this.updateFromExecutable.bind(this));
     this.subscribe(this.activeExecutable, this.updateFromExecutable.bind(this));
 
 
-    this.subscribe(RESULT_UPDATED_EVENT, executionResult => {
+    this.subscribe(EXECUTABLE_RESULT_UPDATED_TOPIC, executionResult => {
       if (this.activeExecutable() === executionResult.executable) {
       if (this.activeExecutable() === executionResult.executable) {
         this.updateFromResult(executionResult);
         this.updateFromResult(executionResult);
       }
       }
     });
     });
 
 
-    this.subscribe(LOGS_UPDATED_EVENT, executionLogs => {
+    this.subscribe(EXECUTABLE_LOGS_UPDATED_TOPIC, executionLogs => {
       if (this.activeExecutable() === executionLogs.executable) {
       if (this.activeExecutable() === executionLogs.executable) {
         this.updateFromLogs(executionLogs);
         this.updateFromLogs(executionLogs);
       }
       }

+ 15 - 14
desktop/core/src/desktop/js/apps/editor/components/result/ResultTable.vue

@@ -48,18 +48,16 @@
 </template>
 </template>
 
 
 <script lang="ts">
 <script lang="ts">
+  import {
+    EXECUTABLE_RESULT_UPDATED_TOPIC,
+    EXECUTABLE_UPDATED_TOPIC,
+    ExecutableResultUpdatedEvent
+  } from 'apps/editor/execution/events';
   import { defineComponent, PropType } from 'vue';
   import { defineComponent, PropType } from 'vue';
 
 
   import { ResultMeta } from 'apps/editor/execution/api';
   import { ResultMeta } from 'apps/editor/execution/api';
-  import Executable, {
-    EXECUTABLE_UPDATED_EVENT,
-    ExecutionStatus
-  } from 'apps/editor/execution/executable';
-  import ExecutionResult, {
-    RESULT_UPDATED_EVENT,
-    ResultRow,
-    ResultType
-  } from 'apps/editor/execution/executionResult';
+  import Executable, { ExecutionStatus } from 'apps/editor/execution/executable';
+  import ExecutionResult, { ResultRow, ResultType } from 'apps/editor/execution/executionResult';
   import { Column } from 'components/HueTable';
   import { Column } from 'components/HueTable';
   import HueTable from 'components/HueTable.vue';
   import HueTable from 'components/HueTable.vue';
   import SubscriptionTracker from 'components/utils/SubscriptionTracker';
   import SubscriptionTracker from 'components/utils/SubscriptionTracker';
@@ -149,17 +147,20 @@
     },
     },
 
 
     mounted(): void {
     mounted(): void {
-      this.subTracker.subscribe(EXECUTABLE_UPDATED_EVENT, (executable: Executable) => {
+      this.subTracker.subscribe<ExecutableUpdatedEvent>(EXECUTABLE_UPDATED_TOPIC, executable => {
         if (this.executable && this.executable.id === executable.id) {
         if (this.executable && this.executable.id === executable.id) {
           this.updateFromExecutable(executable);
           this.updateFromExecutable(executable);
         }
         }
       });
       });
 
 
-      this.subTracker.subscribe(RESULT_UPDATED_EVENT, (executionResult: ExecutionResult) => {
-        if (this.executable && this.executable.id === executionResult.executable.id) {
-          this.handleResultChange();
+      this.subTracker.subscribe<ExecutableResultUpdatedEvent>(
+        EXECUTABLE_RESULT_UPDATED_TOPIC,
+        (executionResult: ExecutionResult) => {
+          if (this.executable && this.executable.id === executionResult.executable.id) {
+            this.handleResultChange();
+          }
         }
         }
-      });
+      );
     },
     },
 
 
     unmounted(): void {
     unmounted(): void {

+ 5 - 4
desktop/core/src/desktop/js/apps/editor/components/resultChart/ko.resultChart.js

@@ -16,6 +16,7 @@
 
 
 import * as ko from 'knockout';
 import * as ko from 'knockout';
 import $ from 'jquery';
 import $ from 'jquery';
+import { EXECUTABLE_RESULT_UPDATED_TOPIC, EXECUTABLE_UPDATED_TOPIC } from '../../execution/events';
 
 
 import {
 import {
   leafletMapChartTransformer,
   leafletMapChartTransformer,
@@ -27,8 +28,8 @@ import {
 } from './chartTransformers';
 } from './chartTransformers';
 import { attachTracker } from 'apps/editor/components/executableStateHandler';
 import { attachTracker } from 'apps/editor/components/executableStateHandler';
 import { REDRAW_CHART_EVENT } from 'apps/editor/events';
 import { REDRAW_CHART_EVENT } from 'apps/editor/events';
-import { EXECUTABLE_UPDATED_EVENT, ExecutionStatus } from 'apps/editor/execution/executable';
-import { RESULT_TYPE, RESULT_UPDATED_EVENT } from 'apps/editor/execution/executionResult';
+import { ExecutionStatus } from 'apps/editor/execution/executable';
+import { RESULT_TYPE } from 'apps/editor/execution/executionResult';
 import { CURRENT_QUERY_TAB_SWITCHED_EVENT } from 'apps/editor/snippet';
 import { CURRENT_QUERY_TAB_SWITCHED_EVENT } from 'apps/editor/snippet';
 import componentUtils from 'ko/components/componentUtils';
 import componentUtils from 'ko/components/componentUtils';
 import DisposableComponent from 'ko/components/DisposableComponent';
 import DisposableComponent from 'ko/components/DisposableComponent';
@@ -564,7 +565,7 @@ class ResultChart extends DisposableComponent {
     this.cleanedStringMeta = ko.observableArray();
     this.cleanedStringMeta = ko.observableArray();
     this.cleanedNumericMeta = ko.observableArray();
     this.cleanedNumericMeta = ko.observableArray();
 
 
-    this.subscribe(EXECUTABLE_UPDATED_EVENT, executable => {
+    this.subscribe(EXECUTABLE_UPDATED_TOPIC, executable => {
       if (this.activeExecutable() === executable) {
       if (this.activeExecutable() === executable) {
         this.updateFromExecutable(executable);
         this.updateFromExecutable(executable);
       }
       }
@@ -581,7 +582,7 @@ class ResultChart extends DisposableComponent {
       }
       }
     };
     };
 
 
-    this.subscribe(RESULT_UPDATED_EVENT, executionResult => {
+    this.subscribe(EXECUTABLE_RESULT_UPDATED_TOPIC, executionResult => {
       if (this.activeExecutable() === executionResult.executable) {
       if (this.activeExecutable() === executionResult.executable) {
         handleResultChange();
         handleResultChange();
       }
       }

+ 28 - 0
desktop/core/src/desktop/js/apps/editor/execution/events.ts

@@ -0,0 +1,28 @@
+import { Session } from 'apps/editor/execution/api';
+import ExecutionResult from './executionResult';
+import Executable, { ExecutionStatus } from './executable';
+import ExecutionLogs from './executionLogs';
+
+export const EXECUTABLE_TRANSITIONED_TOPIC = 'hue.executable.status.transitioned';
+export interface ExecutableTransitionedEvent {
+  newStatus: ExecutionStatus;
+  oldStatus: ExecutionStatus;
+  executable: Executable;
+}
+
+export const EXECUTABLE_UPDATED_TOPIC = 'hue.executable.updated';
+export type ExecutableUpdatedEvent = Executable;
+
+export const EXECUTABLE_LOGS_UPDATED_TOPIC = 'hue.executable.logs.updated';
+export type ExecutableLogsUpdatedEvent = ExecutionLogs;
+
+export const EXECUTABLE_RESULT_UPDATED_TOPIC = 'hue.executable.result.updated';
+export type ExecutableResultUpdatedEvent = ExecutionResult;
+
+export const SHOW_SESSION_AUTH_MODAL_TOPIC = 'show.session.auth.modal';
+export interface ShowSessionAuthModalEvent {
+  message?: string;
+  session: Session;
+  resolve(): void;
+  reject(): void;
+}

+ 14 - 9
desktop/core/src/desktop/js/apps/editor/execution/executable.ts

@@ -16,6 +16,12 @@
 
 
 import $ from 'jquery';
 import $ from 'jquery';
 
 
+import {
+  EXECUTABLE_TRANSITIONED_TOPIC,
+  EXECUTABLE_UPDATED_TOPIC,
+  ExecutableTransitionedEvent,
+  ExecutableUpdatedEvent
+} from 'apps/editor/execution/events';
 import { Cancellable } from 'api/cancellablePromise';
 import { Cancellable } from 'api/cancellablePromise';
 import {
 import {
   checkExecutionStatus,
   checkExecutionStatus,
@@ -33,7 +39,7 @@ import ExecutionLogs, {
   ExecutionError,
   ExecutionError,
   ExecutionLogsRaw
   ExecutionLogsRaw
 } from 'apps/editor/execution/executionLogs';
 } from 'apps/editor/execution/executionLogs';
-import hueUtils, { UUID } from 'utils/hueUtils';
+import { UUID } from 'utils/hueUtils';
 import Executor from 'apps/editor/execution/executor';
 import Executor from 'apps/editor/execution/executor';
 
 
 /**
 /**
@@ -55,9 +61,6 @@ export enum ExecutionStatus {
   closed = 'closed'
   closed = 'closed'
 }
 }
 
 
-export const EXECUTABLE_UPDATED_EVENT = 'hue.executable.updated';
-export const EXECUTABLE_STATUS_TRANSITION_EVENT = 'hue.executable.status.transitioned';
-
 export interface ExecutableRaw {
 export interface ExecutableRaw {
   executeEnded: number;
   executeEnded: number;
   executeStarted: number;
   executeStarted: number;
@@ -102,7 +105,7 @@ export default abstract class Executable {
     const oldStatus = this.status;
     const oldStatus = this.status;
     this.status = status;
     this.status = status;
     if (oldStatus !== status) {
     if (oldStatus !== status) {
-      huePubSub.publish(EXECUTABLE_STATUS_TRANSITION_EVENT, {
+      huePubSub.publish<ExecutableTransitionedEvent>(EXECUTABLE_TRANSITIONED_TOPIC, {
         executable: this,
         executable: this,
         oldStatus: oldStatus,
         oldStatus: oldStatus,
         newStatus: status
         newStatus: status
@@ -123,10 +126,10 @@ export default abstract class Executable {
   notify(sync?: boolean): void {
   notify(sync?: boolean): void {
     window.clearTimeout(this.notifyThrottle);
     window.clearTimeout(this.notifyThrottle);
     if (sync) {
     if (sync) {
-      huePubSub.publish(EXECUTABLE_UPDATED_EVENT, this);
+      huePubSub.publish<ExecutableUpdatedEvent>(EXECUTABLE_UPDATED_TOPIC, this);
     } else {
     } else {
       this.notifyThrottle = window.setTimeout(() => {
       this.notifyThrottle = window.setTimeout(() => {
-        huePubSub.publish(EXECUTABLE_UPDATED_EVENT, this);
+        huePubSub.publish<ExecutableUpdatedEvent>(EXECUTABLE_UPDATED_TOPIC, this);
       }, 1);
       }, 1);
     }
     }
   }
   }
@@ -441,7 +444,9 @@ export default abstract class Executable {
       result: {
       result: {
         handle: this.handle
         handle: this.handle
       },
       },
+      connector: this.executor.connector(),
       executor: this.executor.toJs(),
       executor: this.executor.toJs(),
+      defaultLimit: (this.executor.defaultLimit && this.executor.defaultLimit()) || null,
       status: this.status,
       status: this.status,
       id: id || UUID(),
       id: id || UUID(),
       statement_raw: this.getRawStatement(),
       statement_raw: this.getRawStatement(),
@@ -455,9 +460,9 @@ export default abstract class Executable {
     };
     };
 
 
     const notebook = {
     const notebook = {
-      type: this.executor.connector().id,
+      type: `query-${this.executor.connector().id}`,
       snippets: [snippet],
       snippets: [snippet],
-      uuid: hueUtils.UUID(),
+      uuid: UUID(),
       name: '',
       name: '',
       isSaved: false,
       isSaved: false,
       sessions: [session],
       sessions: [session],

+ 6 - 4
desktop/core/src/desktop/js/apps/editor/execution/executionLogs.ts

@@ -15,11 +15,13 @@
 // limitations under the License.
 // limitations under the License.
 
 
 import { ExecutionJob, fetchLogs } from 'apps/editor/execution/api';
 import { ExecutionJob, fetchLogs } from 'apps/editor/execution/api';
+import {
+  EXECUTABLE_LOGS_UPDATED_TOPIC,
+  ExecutableLogsUpdatedEvent
+} from 'apps/editor/execution/events';
 import huePubSub from 'utils/huePubSub';
 import huePubSub from 'utils/huePubSub';
 import Executable, { ExecutionStatus } from './executable';
 import Executable, { ExecutionStatus } from './executable';
 
 
-export const LOGS_UPDATED_EVENT = 'hue.executable.logs.updated';
-
 export interface ExecutionError {
 export interface ExecutionError {
   row: number;
   row: number;
   column: number;
   column: number;
@@ -43,7 +45,7 @@ export default class ExecutionLogs {
   }
   }
 
 
   notify(): void {
   notify(): void {
-    huePubSub.publish(LOGS_UPDATED_EVENT, this);
+    huePubSub.publish<ExecutableLogsUpdatedEvent>(EXECUTABLE_LOGS_UPDATED_TOPIC, this);
   }
   }
 
 
   reset(): void {
   reset(): void {
@@ -79,7 +81,7 @@ export default class ExecutionLogs {
       this.jobs = [];
       this.jobs = [];
     }
     }
 
 
-    huePubSub.publish(LOGS_UPDATED_EVENT, this);
+    huePubSub.publish<ExecutableLogsUpdatedEvent>(EXECUTABLE_LOGS_UPDATED_TOPIC, this);
 
 
     if (!finalFetch) {
     if (!finalFetch) {
       const delay = this.executable.getExecutionTime() > 45000 ? 5000 : 1000;
       const delay = this.executable.getExecutionTime() > 45000 ? 5000 : 1000;

+ 5 - 3
desktop/core/src/desktop/js/apps/editor/execution/executionResult.ts

@@ -21,6 +21,10 @@ import {
   ResultMeta,
   ResultMeta,
   ResultSizeApiResponse
   ResultSizeApiResponse
 } from 'apps/editor/execution/api';
 } from 'apps/editor/execution/api';
+import {
+  EXECUTABLE_RESULT_UPDATED_TOPIC,
+  ExecutableResultUpdatedEvent
+} from 'apps/editor/execution/events';
 import { Observable } from 'knockout';
 import { Observable } from 'knockout';
 import * as ko from 'knockout';
 import * as ko from 'knockout';
 
 
@@ -28,8 +32,6 @@ import huePubSub from 'utils/huePubSub';
 import { sleep } from 'utils/hueUtils';
 import { sleep } from 'utils/hueUtils';
 import Executable, { ExecutionStatus } from './executable';
 import Executable, { ExecutionStatus } from './executable';
 
 
-export const RESULT_UPDATED_EVENT = 'hue.executable.result.updated';
-
 export const RESULT_TYPE = {
 export const RESULT_TYPE = {
   TABLE: 'table'
   TABLE: 'table'
 };
 };
@@ -200,6 +202,6 @@ export default class ExecutionResult {
   }
   }
 
 
   notify(): void {
   notify(): void {
-    huePubSub.publish(RESULT_UPDATED_EVENT, this);
+    huePubSub.publish<ExecutableResultUpdatedEvent>(EXECUTABLE_RESULT_UPDATED_TOPIC, this);
   }
   }
 }
 }

+ 3 - 28
desktop/core/src/desktop/js/apps/editor/snippet.js

@@ -43,11 +43,7 @@ import { getFromLocalStorage, setInLocalStorage } from 'utils/storageUtils';
 import sessionManager from 'apps/editor/execution/sessionManager';
 import sessionManager from 'apps/editor/execution/sessionManager';
 import SqlExecutable from 'apps/editor/execution/sqlExecutable';
 import SqlExecutable from 'apps/editor/execution/sqlExecutable';
 import { HIDE_FIXED_HEADERS_EVENT, REDRAW_FIXED_HEADERS_EVENT } from 'apps/editor/events';
 import { HIDE_FIXED_HEADERS_EVENT, REDRAW_FIXED_HEADERS_EVENT } from 'apps/editor/events';
-import {
-  EXECUTABLE_STATUS_TRANSITION_EVENT,
-  EXECUTABLE_UPDATED_EVENT,
-  ExecutionStatus
-} from 'apps/editor/execution/executable';
+import { ExecutionStatus } from 'apps/editor/execution/executable';
 import {
 import {
   ACTIVE_STATEMENT_CHANGED_EVENT,
   ACTIVE_STATEMENT_CHANGED_EVENT,
   REFRESH_STATEMENT_LOCATIONS_EVENT
   REFRESH_STATEMENT_LOCATIONS_EVENT
@@ -60,12 +56,12 @@ import {
   ASSIST_GET_SOURCE_EVENT,
   ASSIST_GET_SOURCE_EVENT,
   ASSIST_SET_SOURCE_EVENT
   ASSIST_SET_SOURCE_EVENT
 } from 'ko/components/assist/events';
 } from 'ko/components/assist/events';
+import { EXECUTABLE_UPDATED_TOPIC } from './execution/events';
 
 
 // TODO: Remove for ENABLE_NOTEBOOK_2. Temporary here for debug
 // TODO: Remove for ENABLE_NOTEBOOK_2. Temporary here for debug
 window.SqlExecutable = SqlExecutable;
 window.SqlExecutable = SqlExecutable;
 window.Executor = Executor;
 window.Executor = Executor;
 
 
-const ADD_TO_HISTORY_EVENT = 'query.history.add';
 export const CURRENT_QUERY_TAB_SWITCHED_EVENT = 'current.query.tab.switched';
 export const CURRENT_QUERY_TAB_SWITCHED_EVENT = 'current.query.tab.switched';
 
 
 export const DIALECT = {
 export const DIALECT = {
@@ -627,33 +623,12 @@ export default class Snippet {
       }
       }
     }
     }
 
 
-    huePubSub.subscribe(EXECUTABLE_UPDATED_EVENT, executable => {
+    huePubSub.subscribe(EXECUTABLE_UPDATED_TOPIC, executable => {
       if (this.activeExecutable() === executable) {
       if (this.activeExecutable() === executable) {
         this.updateFromExecutable(executable);
         this.updateFromExecutable(executable);
       }
       }
     });
     });
 
 
-    huePubSub.subscribe(EXECUTABLE_STATUS_TRANSITION_EVENT, transitionDetails => {
-      if (this.activeExecutable() === transitionDetails.executable) {
-        if (
-          (transitionDetails.newStatus === ExecutionStatus.available ||
-            transitionDetails.newStatus === ExecutionStatus.failed ||
-            transitionDetails.newStatus === ExecutionStatus.success) &&
-          this.activeExecutable().history &&
-          this.activeExecutable().handle
-        ) {
-          huePubSub.publish(ADD_TO_HISTORY_EVENT, {
-            absoluteUrl: undefined,
-            statement: this.activeExecutable().handle.statement,
-            lastExecuted: this.activeExecutable().executeStarted,
-            status: this.activeExecutable().status,
-            name: this.parentNotebook.name(),
-            uuid: this.activeExecutable().history.uuid
-          });
-        }
-      }
-    });
-
     this.activeExecutable.subscribe(this.updateFromExecutable.bind(this));
     this.activeExecutable.subscribe(this.updateFromExecutable.bind(this));
 
 
     this.refreshHistory = notebook.fetchHistory;
     this.refreshHistory = notebook.fetchHistory;

+ 5 - 5
desktop/core/src/desktop/js/components/utils/SubscriptionTracker.ts

@@ -48,10 +48,10 @@ export default class SubscriptionTracker {
     });
     });
   }
   }
 
 
-  subscribe(
-    subscribable: string | KnockoutSubscribable<unknown>,
-    // eslint-disable-next-line @typescript-eslint/no-explicit-any
-    callback: (...args: any[]) => any
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  subscribe<T = any>(
+    subscribable: string | KnockoutSubscribable<T>,
+    callback: (event: T) => void
   ): void {
   ): void {
     if (typeof subscribable === 'string') {
     if (typeof subscribable === 'string') {
       const pubSub = huePubSub.subscribe(subscribable, callback);
       const pubSub = huePubSub.subscribe(subscribable, callback);
@@ -91,7 +91,7 @@ export default class SubscriptionTracker {
         return;
         return;
       }
       }
       vueRef.value = valueOrNull(observable());
       vueRef.value = valueOrNull(observable());
-      this.subscribe(observable, (newVal?: T) => {
+      this.subscribe<T | undefined | null>(observable, (newVal?: T | null) => {
         vueRef.value = valueOrNull(newVal);
         vueRef.value = valueOrNull(newVal);
       });
       });
     });
     });

+ 2 - 2
desktop/core/src/desktop/js/ko/bindings/ace/aceGutterHandler.js

@@ -15,9 +15,9 @@
 // limitations under the License.
 // limitations under the License.
 
 
 import huePubSub from 'utils/huePubSub';
 import huePubSub from 'utils/huePubSub';
-import { EXECUTABLE_UPDATED_EVENT } from 'apps/editor/execution/executable';
 import { ACTIVE_STATEMENT_CHANGED_EVENT } from 'ko/bindings/ace/aceLocationHandler';
 import { ACTIVE_STATEMENT_CHANGED_EVENT } from 'ko/bindings/ace/aceLocationHandler';
 import AceAnchoredRange from 'ko/bindings/ace/aceAnchoredRange';
 import AceAnchoredRange from 'ko/bindings/ace/aceAnchoredRange';
+import { EXECUTABLE_UPDATED_TOPIC } from '../../../apps/editor/execution/events';
 
 
 // TODO: depends on Ace
 // TODO: depends on Ace
 
 
@@ -73,7 +73,7 @@ export default class AceGutterHandler {
     });
     });
 
 
     if (this.executor) {
     if (this.executor) {
-      const executableSub = huePubSub.subscribe(EXECUTABLE_UPDATED_EVENT, executable => {
+      const executableSub = huePubSub.subscribe(EXECUTABLE_UPDATED_TOPIC, executable => {
         if (executable.executor === this.executor) {
         if (executable.executor === this.executor) {
           if (executable.lost) {
           if (executable.lost) {
             if (executable.observerState.aceAnchor) {
             if (executable.observerState.aceAnchor) {

+ 15 - 5
desktop/core/src/desktop/js/utils/huePubSub.ts

@@ -18,7 +18,9 @@
 
 
 /* eslint-disable @typescript-eslint/no-explicit-any */
 /* eslint-disable @typescript-eslint/no-explicit-any */
 
 
-type listener = (data: any) => void;
+interface Listener<T> {
+  (data: T): void;
+}
 
 
 enum PubSubState {
 enum PubSubState {
   RUNNING,
   RUNNING,
@@ -27,7 +29,7 @@ enum PubSubState {
 
 
 const topics: {
 const topics: {
   [topic: string]: {
   [topic: string]: {
-    listener: listener;
+    listener: Listener<any>;
     app?: string;
     app?: string;
     state: PubSubState;
     state: PubSubState;
   }[];
   }[];
@@ -39,7 +41,11 @@ export interface HueSubscription {
   remove: () => void;
   remove: () => void;
 }
 }
 
 
-const subscribe = (topic: string, listener: listener, app?: string): HueSubscription => {
+const subscribe = <T = any>(
+  topic: string,
+  listener: Listener<T>,
+  app?: string
+): HueSubscription => {
   if (!hOP.call(topics, topic)) {
   if (!hOP.call(topics, topic)) {
     topics[topic] = [];
     topics[topic] = [];
   }
   }
@@ -63,7 +69,7 @@ const removeAll = (topic: string): void => {
 };
 };
 
 
 // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
 // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
-const publish = (topic: string, info?: any): void => {
+const publish = <T = any>(topic: string, info?: T): void => {
   if (!hOP.call(topics, topic)) {
   if (!hOP.call(topics, topic)) {
     return;
     return;
   }
   }
@@ -75,7 +81,11 @@ const publish = (topic: string, info?: any): void => {
   });
   });
 };
 };
 
 
-const subscribeOnce = (topic: string, listener: listener, app: string): HueSubscription => {
+const subscribeOnce = <T = any>(
+  topic: string,
+  listener: Listener<T>,
+  app: string
+): HueSubscription => {
   const ephemeral = subscribe(
   const ephemeral = subscribe(
     topic,
     topic,
     arg => {
     arg => {