Browse Source

[editor] Simplify the SQL Scratchpad web component

This fixes an issue with regenerator-runtime when used outside babel and adds a couple of attributes to the element:

- user and password for login
- optional auth attribute to enable different login types
- api_url added for the base url
Johan Åhlén 4 years ago
parent
commit
26d363de96

+ 48 - 0
desktop/core/src/desktop/js/api/auth.ts

@@ -0,0 +1,48 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { get, post } from 'api/utils';
+
+export enum AuthType {
+  hue = 'hue',
+  jwt = 'jwt'
+}
+
+const LOGOUT_URL = '/hue/accounts/logout';
+const LOGIN_URL = '/hue/accounts/login';
+
+const JWT_URL = 'iam/v1/get/auth-token/';
+
+export const login = async (
+  username: string,
+  password: string,
+  auth = AuthType.jwt
+): Promise<void> => {
+  if (auth === AuthType.jwt) {
+    return post(JWT_URL, { username, password });
+  }
+
+  if (auth === AuthType.hue) {
+    await get(LOGOUT_URL);
+    const response = await get<string>(LOGIN_URL);
+    const csrfMatch = response.match(/name='csrfmiddlewaretoken'\s+value='([^']+)'/);
+    if (csrfMatch) {
+      return post(LOGIN_URL, { username, password, csrfmiddlewaretoken: csrfMatch[1] });
+    }
+    throw new Error('No csrf middleware token found!');
+  }
+  throw new Error('Unknown auth method specified!');
+};

+ 48 - 11
desktop/core/src/desktop/js/apps/editor/components/sqlScratchpad/SqlScratchpad.vue

@@ -43,7 +43,7 @@
       </div>
     </div>
     <div v-else-if="!loading && !executor">
-      Failed loading the SQL Scratchpad!
+      {{ errorMessage || 'Failed loading the SQL Scratchpad!' }}
     </div>
   </div>
 </template>
@@ -66,6 +66,8 @@
   import ResultTable from '../result/ResultTable.vue';
   import Executor from '../../execution/executor';
   import SqlExecutable from '../../execution/sqlExecutable';
+  import { AuthType, login } from 'api/auth';
+  import { setBaseUrl } from 'api/utils';
   import contextCatalog from 'catalog/contextCatalog';
   import HueIcons from 'components/icons/HueIcons.vue';
   import Spinner from 'components/Spinner.vue';
@@ -88,13 +90,30 @@
       dialect: {
         type: String as PropType<string | null>,
         default: null
+      },
+      auth: {
+        type: String as PropType<AuthType | null>,
+        default: AuthType.jwt
+      },
+      user: {
+        type: String as PropType<string | null>,
+        default: null
+      },
+      password: {
+        type: String as PropType<string | null>,
+        default: null
+      },
+      apiUrl: {
+        type: String as PropType<string | null>,
+        default: null
       }
     },
     setup(props) {
-      const { dialect } = toRefs(props);
-      const activeExecutable = ref<SqlExecutable | null>(null);
-      const executor = ref<Executor | null>(null);
+      const { apiUrl, auth, dialect, pass, user } = toRefs(props);
+      const activeExecutable = ref<SqlExecutable>(null);
+      const executor = ref<Executor>(null);
       const loading = ref<boolean>(true);
+      const errorMessage = ref<string>(null);
       const id = UUID();
 
       const sqlParserProvider: SqlParserProvider = {
@@ -116,11 +135,26 @@
         minLines: null
       };
 
-      const initializeExecutor = async (): Promise<void> => {
+      const initialize = async (): Promise<void> => {
+        if (apiUrl.value) {
+          setBaseUrl(apiUrl.value);
+        }
+
+        if (user.value !== null && pass.value !== null) {
+          try {
+            await login(user.value, pass.value, auth.value);
+          } catch (err) {
+            errorMessage.value = 'Login failed!';
+            console.error(err);
+            return;
+          }
+        }
+
         try {
           await getConfig();
-        } catch {
-          console.warn('Failed loading Hue config!');
+        } catch (err) {
+          errorMessage.value = 'Failed loading the Hue config!';
+          console.error(err);
           return;
         }
 
@@ -128,7 +162,7 @@
           connector => !dialect.value || connector.dialect === dialect.value
         );
         if (!connector) {
-          console.warn('No connector found!');
+          errorMessage.value = 'No connector found!';
           return;
         }
 
@@ -136,7 +170,7 @@
           const { namespaces } = await contextCatalog.getNamespaces({ connector });
 
           if (!namespaces.length || !namespaces[0].computes.length) {
-            console.warn('No namespaces or computes found!');
+            errorMessage.value = 'No namespaces or computes found!';
             return;
           }
 
@@ -162,14 +196,17 @@
       onMounted(async () => {
         loading.value = true;
         try {
-          await initializeExecutor();
-        } catch {}
+          await initialize();
+        } catch (err) {
+          console.error(err);
+        }
         loading.value = false;
       });
 
       return {
         aceOptions,
         activeExecutable,
+        errorMessage,
         executor,
         id,
         onActiveStatementChanged,

+ 1 - 0
desktop/core/src/desktop/js/apps/editor/components/sqlScratchpad/SqlScratchpadWebComponent.ts

@@ -14,6 +14,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import 'regenerator-runtime/runtime';
 import SqlScratchpad from './SqlScratchpad.vue';
 import { wrap } from 'vue/webComponentWrap';
 import { post, setBaseUrl, setBearerToken } from 'api/utils';

+ 1 - 1
desktop/core/src/desktop/js/ext/ace.d.ts

@@ -92,7 +92,7 @@ declare namespace Ace {
     useHueAutocompleter: boolean;
   }
 
-  export type OptionValue = string | boolean | number;
+  export type OptionValue = string | boolean | number | null;
 
   export interface Options {
     [option: string]: OptionValue;