Browse Source

[ui-serverLogs] Convert the Server Logs page within Administrator server in ReactJS (#3927)

* Making the ServerLogs component

WIP

WIP

Searchbox working

WIP

WIP

WIP

w

f

WIP

r

t

WIP

WIP

WIP

WIP

WIP test

* fix the code

WIP

* Changes after review comments

* review comments changes

* Final changes after review comments

f

* Minor changes

Wip

* Changes

* Fix reverse=true

* Fix unit test

* Tests fixed

---------

Co-authored-by: Mohammed Tabraiz <tabraiz@cloudera.com>
Ananya_Agarwal 9 months ago
parent
commit
c762ef0582

+ 51 - 0
desktop/core/src/desktop/js/apps/admin/Components/HighlightText.tsx

@@ -0,0 +1,51 @@
+// 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 React from 'react';
+import '../ServerLogs/ServerLogsTab.scss';
+
+interface HighlightTextProps {
+  text: string;
+  searchValue: string;
+  highlightClassName?: string;
+}
+
+const HighlightText: React.FC<HighlightTextProps> = ({
+  text,
+  searchValue,
+  highlightClassName = 'server--highlight-word'
+}) => {
+  if (!searchValue) {
+    return <>{text}</>;
+  }
+  const regex = new RegExp(`(${searchValue})`, 'gi');
+  const parts = text.split(regex);
+  return (
+    <>
+      {parts.map((part, index) =>
+        regex.test(part) ? (
+          <mark key={index} className={highlightClassName}>
+            {part}
+          </mark>
+        ) : (
+          part
+        )
+      )}
+    </>
+  );
+};
+
+export default HighlightText;

+ 17 - 0
desktop/core/src/desktop/js/apps/admin/Components/utils.tsx

@@ -0,0 +1,17 @@
+// 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.
+
+export const SERVER_LOGS_API_URL = '/api/v1/logs';

+ 68 - 0
desktop/core/src/desktop/js/apps/admin/ServerLogs/ServerLogsHeader.scss

@@ -0,0 +1,68 @@
+// 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 '../../../components/styles/variables';
+
+.antd.cuix {
+  .hue-server-admin-header {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+
+    .server__input-filter {
+      margin: $font-size-sm;
+      width: 200px;
+      padding: 2px;
+
+      input {
+        box-shadow: none;
+        -webkit-box-shadow: none;
+        margin-top: 3px;
+      }
+      
+      .server__input-filter--prefix {
+        margin: 5px 3px 3px 3px;
+      }
+    }
+
+    .server__filter-arrow {
+      height: 16px;
+      width: 16px;
+      margin: 0;
+    }
+
+    .server--right-actions {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+
+      .server__wrap-logs,
+      .server__download-button,
+      .server__host-text {
+        margin-right: 8px;
+      }
+
+      .server__checkbox-icon {
+        margin-right: 2px;
+      }
+
+      .server__download-button {
+        border: 1px solid $fluidx-gray-600;
+      }
+    }
+  }
+}

+ 92 - 0
desktop/core/src/desktop/js/apps/admin/ServerLogs/ServerLogsHeader.tsx

@@ -0,0 +1,92 @@
+// 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 React, { useState } from 'react';
+import { Input, Checkbox } from 'antd';
+import Button from 'cuix/dist/components/Button';
+import Search from '@cloudera/cuix-core/icons/react/SearchIcon';
+import Download from '@cloudera/cuix-core/icons/react/DownloadIcon';
+import { i18nReact } from '../../../utils/i18nReact';
+import huePubSub from '../../../utils/huePubSub';
+import './ServerLogsHeader.scss';
+
+interface ServerLogsHeaderProps {
+  onFilterChange: (value: string) => void;
+  onWrapLogsChange: (wrap: boolean) => void;
+  hostName: string;
+}
+
+const ServerLogsHeader: React.FC<ServerLogsHeaderProps> = ({
+  onFilterChange,
+  onWrapLogsChange,
+  hostName
+}): JSX.Element => {
+  const { t } = i18nReact.useTranslation();
+  const [filterValue, setFilterValue] = useState('');
+  const [wrapLogs, setWrapLogs] = useState(true);
+
+  const handleFilterChange = (newFilterValue: string) => {
+    setFilterValue(newFilterValue);
+    onFilterChange(newFilterValue);
+  };
+
+  const handleDownloadClick = () => {
+    huePubSub.publish('open.link', '/desktop/download_logs');
+  };
+
+  return (
+    <div className="hue-server-admin-header admin-header">
+      <Input
+        className="server__input-filter"
+        placeholder={t('Search in the logs')}
+        prefix={
+          <span className="server__input-filter--prefix">
+            <Search />
+          </span>
+        }
+        value={filterValue}
+        onChange={e => handleFilterChange(e.target.value)}
+      />
+
+      <div className="server--right-actions">
+        <span className="server__host-text">{t(`Host: ${hostName}`)}</span>
+        <Checkbox
+          onChange={e => {
+            setWrapLogs(e.target.checked);
+            onWrapLogsChange(e.target.checked);
+          }}
+          checked={wrapLogs}
+          className="server__checkbox-icon"
+          id="wrapLogsToggle"
+        />
+        <label className="server__wrap-logs" htmlFor="wrapLogsToggle">
+          {t('Wrap logs')}
+        </label>
+
+        <Button
+          className="server__download-button"
+          data-event="download-button"
+          icon={<Download />}
+          onClick={handleDownloadClick}
+        >
+          {t('Download entire log as zip')}
+        </Button>
+      </div>
+    </div>
+  );
+};
+
+export default ServerLogsHeader;

+ 51 - 0
desktop/core/src/desktop/js/apps/admin/ServerLogs/ServerLogsTab.scss

@@ -0,0 +1,51 @@
+// 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 '../../../components/styles/variables';
+
+.antd.cuix {
+  .hue-server-logs-component {
+    background-color: $fluidx-gray-100;
+    padding: 24px;
+
+    .server__display-logs {
+      overflow: auto;
+      height: calc(100vh - 250px);
+      background-color: $fluidx-white;
+      width: 100%;
+      padding: 10px 0 10px 0;
+
+      .server__log-line {
+        background-color: $fluidx-white;
+        margin: 0;
+        padding: 2px;
+      }
+    }
+
+    .server_wrap {
+      white-space: nowrap;
+    }
+
+    .server__no-logs-found {
+      background-color: $fluidx-white;
+    }
+
+    .server--highlight-word {
+      background-color: $fluidx-pear-050;
+      color: $fluidx-black;
+    }
+  }
+}

+ 103 - 0
desktop/core/src/desktop/js/apps/admin/ServerLogs/ServerLogsTab.test.tsx

@@ -0,0 +1,103 @@
+// 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 React from 'react';
+import { fireEvent, render, screen } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import '@testing-library/jest-dom';
+import ServerLogs from './ServerLogsTab';
+import { mocked } from 'jest-mock';
+import useLoadData from '../../../utils/hooks/useLoadData/useLoadData';
+
+const mockData = jest.fn().mockReturnValue({
+  logs: ['Log entry 1', 'Log entry 2'],
+  hue_hostname: 'test-hostname'
+});
+
+const emptyMockData = jest.fn().mockReturnValue({
+  logs: [],
+  hue_hostname: 'test-hostname'
+});
+
+jest.mock('../../../utils/hooks/useLoadData/useLoadData');
+
+afterEach(() => {
+  jest.clearAllMocks();
+});
+
+describe('ServerLogs Component', () => {
+  it('should render ServerLogs component with fetched logs', () => {
+    mocked(useLoadData).mockImplementation(() => ({
+      data: mockData(),
+      loading: false,
+      reloadData: jest.fn()
+    }));
+
+    render(<ServerLogs />);
+
+    expect(screen.getByText('Log entry 1')).toBeInTheDocument();
+    expect(screen.getByText('Log entry 2')).toBeInTheDocument();
+  });
+
+  test('it should handle the scenario when no logs are found', () => {
+    mocked(useLoadData).mockImplementation(() => ({
+      data: emptyMockData(),
+      loading: false,
+      reloadData: jest.fn()
+    }));
+
+    render(<ServerLogs />);
+
+    expect(screen.getByText('No logs found!')).toBeInTheDocument();
+  });
+
+  test('it should find and highlights the searched value', async () => {
+    mocked(useLoadData).mockImplementation(() => ({
+      data: mockData(),
+      loading: false,
+      reloadData: jest.fn()
+    }));
+
+    render(<ServerLogs />);
+
+    const searchValue = 'entry 1';
+    const searchInput = screen.getByPlaceholderText('Search in the logs');
+
+    await userEvent.type(searchInput, searchValue);
+
+    const highlightedElements = screen.getAllByText(searchValue, { selector: 'mark' });
+    expect(highlightedElements.length).toBeGreaterThan(0);
+    highlightedElements.forEach(element => {
+      expect(element).toHaveClass('server--highlight-word');
+    });
+  });
+
+  test('it should wrap the logs when the user checks "Wrap logs"', async () => {
+    mocked(useLoadData).mockImplementation(() => ({
+      data: mockData(),
+      loading: false,
+      reloadData: jest.fn()
+    }));
+
+    render(<ServerLogs />);
+
+    expect(screen.getByText('Log entry 1')).toHaveClass('server_wrap');
+
+    fireEvent.click(screen.getByLabelText('Wrap logs'));
+
+    expect(screen.getByText('Log entry 1')).not.toHaveClass('server_wrap');
+  });
+});

+ 90 - 0
desktop/core/src/desktop/js/apps/admin/ServerLogs/ServerLogsTab.tsx

@@ -0,0 +1,90 @@
+// 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 React, { useState } from 'react';
+import { Spin, Alert } from 'antd';
+import ServerLogsHeader from './ServerLogsHeader';
+import { i18nReact } from '../../../utils/i18nReact';
+import useLoadData from '../../../utils/hooks/useLoadData/useLoadData';
+import HighlightText from '../Components/HighlightText';
+import { SERVER_LOGS_API_URL } from '../Components/utils';
+import './ServerLogsTab.scss';
+
+interface ServerLogsData {
+  logs: string[];
+  hue_hostname: string;
+}
+
+const ServerLogs: React.FC = (): JSX.Element => {
+  const [filter, setFilter] = useState<string>('');
+  const [wrapLogs, setWrapLogs] = useState(true);
+  const { t } = i18nReact.useTranslation();
+  const {
+    data: logsData,
+    loading,
+    error
+  } = useLoadData<ServerLogsData>(SERVER_LOGS_API_URL, {
+    params: {
+      reverse: true
+    }
+  });
+
+  if (error) {
+    return (
+      <div className="hue-server-logs-component">
+        <Alert
+          message={t(`Error: ${error}`)}
+          description={t('An error occurred while fetching server logs.')}
+          type="error"
+        />
+      </div>
+    );
+  }
+
+  return (
+    <div className="hue-server-logs-component">
+      <Spin spinning={loading}>
+        {!loading && (
+          <>
+            <ServerLogsHeader
+              onFilterChange={setFilter}
+              onWrapLogsChange={setWrapLogs}
+              hostName={logsData?.hue_hostname ?? ''}
+            />
+            {logsData && (logsData.logs.length === 0 || logsData.logs[0] === '') && (
+              <pre className="server__no-logs-found">No logs found!</pre>
+            )}
+
+            {logsData && logsData.logs.length > 0 && logsData.logs[0] !== '' && (
+              <div className="server__display-logs">
+                {logsData.logs.map((line, index) => (
+                  <div
+                    className={`server__log-line ${wrapLogs ? 'server_wrap' : ''}`}
+                    key={'logs_' + index}
+                  >
+                    <HighlightText text={line} searchValue={filter} />
+                  </div>
+                ))}
+              </div>
+            )}
+          </>
+        )}
+      </Spin>
+    </div>
+  );
+};
+
+export default ServerLogs;

+ 2 - 5
desktop/core/src/desktop/js/onePageViewModel.js

@@ -461,8 +461,8 @@ class OnePageViewModel {
               self.extraEmbeddableURLParams('');
               self.extraEmbeddableURLParams('');
               const currentPath = window.location.pathname; // Retrieve the current path from the window location
               const currentPath = window.location.pathname; // Retrieve the current path from the window location
               const basePath = currentPath.split('=')[0];
               const basePath = currentPath.split('=')[0];
-              const inlineScriptsUrls = ['oozie', 'beeswax', 'jobbrowser', 'jobsub', 'logs'].some(
-                segment => basePath.includes(segment)
+              const inlineScriptsUrls = ['oozie', 'beeswax', 'jobbrowser', 'jobsub'].some(segment =>
+                basePath.includes(segment)
               );
               );
               if (inlineScriptsUrls) {
               if (inlineScriptsUrls) {
                 self.processHeaders(response).done($rawHtml => {
                 self.processHeaders(response).done($rawHtml => {
@@ -640,9 +640,6 @@ class OnePageViewModel {
         url: '/desktop/metrics',
         url: '/desktop/metrics',
         app: function () {
         app: function () {
           self.loadApp('metrics');
           self.loadApp('metrics');
-          self.getActiveAppViewModel(viewModel => {
-            viewModel.fetchMetrics();
-          });
         }
         }
       },
       },
       {
       {

+ 3 - 0
desktop/core/src/desktop/js/reactComponents/imports.js

@@ -16,6 +16,9 @@ export async function loadComponent(name) {
     case 'Configuration':
     case 'Configuration':
       return (await import('../apps/admin/Configuration/ConfigurationTab')).default;
       return (await import('../apps/admin/Configuration/ConfigurationTab')).default;
 
 
+    case 'ServerLogs':
+      return (await import('../apps/admin/ServerLogs/ServerLogsTab')).default;
+
     // Application global components here
     // Application global components here
     case 'AppBanner':
     case 'AppBanner':
       return (await import('./AppBanner/AppBanner')).default;
       return (await import('./AppBanner/AppBanner')).default;

+ 3 - 0
desktop/core/src/desktop/static/desktop/js/logs-inline.js

@@ -0,0 +1,3 @@
+(function () {
+  window.createReactComponents('#ServerLogs');
+})();

+ 3 - 194
desktop/core/src/desktop/templates/logs.mako

@@ -16,206 +16,15 @@
 <%!
 <%!
 import re
 import re
 import sys
 import sys
-
-from desktop.lib.conf import BoundConfig
-from desktop.lib.i18n import smart_str
 from desktop.views import commonheader, commonfooter
 from desktop.views import commonheader, commonfooter
-
-if sys.version_info[0] > 2:
-  from django.utils.translation import gettext as _
-else:
-  from django.utils.translation import ugettext as _
 %>
 %>
 
 
-<%namespace name="actionbar" file="actionbar.mako" />
 <%namespace name="layout" file="about_layout.mako" />
 <%namespace name="layout" file="about_layout.mako" />
 
 
-% if not is_embeddable:
-${ commonheader(_('Server Logs'), "about", user, request) | n,unicode }
-% endif
-
 ${ layout.menubar(section='log_view') }
 ${ layout.menubar(section='log_view') }
 
 
-<style type="text/css">
-  pre {
-    margin: 0;
-    padding: 2px;
-    border: 0;
-    white-space: pre-wrap;
-  }
-
-  pre.nowrap {
-    white-space: nowrap;
-  }
-
-  pre.highlighted {
-    background-color: #FFFF88;
-  }
-
-  #hue-logs {
-    overflow: auto;
-    background-color: #F5F5F5;
-    width: 100%;
-  }
-
-  #hue-logs pre:first-child {
-    padding-top: 10px;
-  }
-
-  #hue-logs pre:last-child {
-    padding-bottom: 10px;
-  }
-
-  .notFound {
-    background-color: #f09999 !important;
-  }
-</style>
-
-<div id="logsComponents" class="container-fluid">
-  <div class="card card-small">
-    <%actionbar:render>
-      <%def name="search()">
-        <input type="text" class="input-xlarge" id="hue-logs-search-query" placeholder="${ _('Search in the logs') }" value="${ query }">
-      </%def>
-      <%def name="creation()">
-        <form class="form-inline">
-            <label class="checkbox margin-right-10">
-              ${ _('Host') }: ${ hostname }
-            </label>
-            <label class="checkbox margin-right-10">
-              <input type="checkbox" id="forcedDebug" data-bind="checked: forcedDebug">
-              ${ _('Force DEBUG level') }
-            </label>
-            <label class="checkbox margin-right-10">
-              <input id="wrapLogs" id="wrap" type="checkbox" checked="checked">
-              ${ _('Wrap logs') }
-            </label>
-            <a href="javascript:void(0)" onclick="huePubSub.publish('open.link', '/desktop/download_logs');" class="btn">
-              <i class="fa fa-download"></i> ${ _('Download entire log as zip') }
-            </a>
-        </form>
-      </%def>
-    </%actionbar:render>
-
-    <% log.reverse() %>
-
-    <div id="hue-logs">
-      % for l in log:
-        <pre>${ smart_str(l, errors='ignore') }</pre>
-      % endfor
-    </div>
-
-  </div>
+<script src="${ static('desktop/js/logs-inline.js') }" type="text/javascript"></script>
 
 
+<div id="ServerLogs">
+<ServerLogsTab class='antd cuix' data-reactcomponent='ServerLogs'></ServerLogsTab>
 </div>
 </div>
-
-<script>
-  var LiveDebugging = function () {
-    var self = this;
-
-    self.forcedDebug = ko.observable();
-    self.forcedDebug.subscribe(function(oldValue) {
-      if (oldValue != null) {
-        self.setLogLevel(! oldValue);
-      }
-    }, this, "beforeChange");
-
-    self.getDebugLevel = function() {
-      $.get("/desktop/get_debug_level", function(data) { self.forcedDebug(data.debug_all); });
-    };
-
-    self.setLogLevel = function(set_debug) {
-      var _url = "";
-      if (set_debug) {
-        _url = "/desktop/set_all_debug";
-      } else {
-        _url = "/desktop/reset_all_debug";
-      }
-
-      $.post(_url, {}, function(data) {
-        if (data,status != 0) {
-          huePubSub.publish('hue.global.error', {message: data.message});
-        }
-      }).fail(function (xhr, textStatus, errorThrown) {
-        huePubSub.publish('hue.global.error', {message: xhr.responseText});
-      });
-    };
-  }
-
-  $(document).ready(function () {
-    var viewModel = new LiveDebugging();
-    ko.applyBindings(viewModel, $("#logsComponents")[0]);
-
-    viewModel.getDebugLevel();
-
-    resizeScrollingLogs();
-
-    var resizeTimeout = -1;
-    $(window).resize(function () {
-      window.clearTimeout(resizeTimeout);
-      resizeTimeout = window.setTimeout(function () {
-        resizeScrollingLogs();
-      }, 200);
-    });
-
-    $("#hue-logs-search-query").jHueDelayedInput(function(){
-      filterLogs($("#hue-logs-search-query").val());
-    }, 500);
-
-    if ("${query}" != "") {
-      filterLogs("${query}");
-    }
-
-    function resizeScrollingLogs() {
-      var _el = $("#hue-logs");
-      if (_el.length > 0) {
-        if (!$.browser.msie) {
-          _el.css("overflow-y", "").css("height", "");
-        }
-        var heightAfter = 0;
-        _el.nextAll(":visible").each(function () {
-          heightAfter += $(this).outerHeight(true);
-        });
-        if (_el.height() > ($(window).height() - _el.offset().top - heightAfter)) {
-          _el.css("overflow-y", "auto").height($(window).height() - _el.offset().top - heightAfter - 30);
-        }
-      }
-    }
-
-    function filterLogs(query) {
-      $("#hue-logs-search-query").removeClass("notFound");
-      if ($.trim(query) == "") {
-        $("#hue-logs").scrollTop(0);
-        return false;
-      }
-      $("pre.highlighted").removeClass("highlighted");
-      var found = false;
-      $("#hue-logs pre").each(function () {
-        var _el = $(this);
-        if (_el.text().toLowerCase().replace(/\s/g, "").indexOf(query.toLowerCase().replace(/\s/g, "")) > -1) {
-          _el.addClass("highlighted");
-          $("#hue-logs").scrollTop(_el.offset().top - $("#hue-logs").position().top - 100);
-          found = true;
-          return false;
-        }
-      });
-      if (!found) {
-        $("#hue-logs-search-query").addClass("notFound");
-        $("#hue-logs").scrollTop(0);
-      }
-    }
-
-    $("#wrapLogs").on("change", function(){
-      if ($(this).is(":checked")){
-        $("pre").removeClass("nowrap");
-      }
-      else {
-        $("pre").addClass("nowrap");
-      }
-    });
-  });
-</script>
-
-% if not is_embeddable:
-${ commonfooter(request, messages) | n,unicode }
-% endif

+ 0 - 15
desktop/core/src/desktop/templates/metrics.mako

@@ -15,25 +15,10 @@
 ## limitations under the License.
 ## limitations under the License.
 <%!
 <%!
 import sys
 import sys
-
 from desktop.views import commonheader, commonfooter
 from desktop.views import commonheader, commonfooter
 from desktop import conf
 from desktop import conf
-
-if sys.version_info[0] > 2:
-  from django.utils.translation import gettext as _
-else:
-  from django.utils.translation import ugettext as _
-%>
-
-<%
-MAIN_SCROLLABLE = is_embeddable and "'.page-content'" or "window"
-if conf.CUSTOM.BANNER_TOP_HTML.get():
-  TOP_SNAP = is_embeddable and "78px" or "106px"
-else:
-  TOP_SNAP = is_embeddable and "50px" or "106px"
 %>
 %>
 
 
-<%namespace name="actionbar" file="actionbar.mako" />
 <%namespace name="layout" file="about_layout.mako" />
 <%namespace name="layout" file="about_layout.mako" />
 
 
 %if not is_embeddable:
 %if not is_embeddable:

+ 0 - 35
desktop/core/src/desktop/tests.py

@@ -664,41 +664,6 @@ def test_app_permissions():
     notebook.conf.INTERPRETERS_CACHE = None
     notebook.conf.INTERPRETERS_CACHE = None
 
 
 
 
-@pytest.mark.django_db
-def test_error_handling_failure():
-  # Change rewrite_user to call has_hue_permission
-  # Try to get logs page
-  # test for default 500 page
-  # Restore rewrite_user
-  import desktop.auth.backend
-
-  c = make_logged_in_client()
-
-  restore_django_debug = desktop.conf.DJANGO_DEBUG_MODE.set_for_testing(False)
-  restore_500_debug = desktop.conf.HTTP_500_DEBUG_MODE.set_for_testing(False)
-
-  original_rewrite_user = desktop.auth.backend.rewrite_user
-
-  def rewrite_user(user):
-    user = original_rewrite_user(user)
-    delattr(user, 'has_hue_permission')
-    return user
-
-  original_rewrite_user = desktop.auth.backend.rewrite_user
-  desktop.auth.backend.rewrite_user = rewrite_user
-
-  try:
-    # Make sure we are showing default 500.html page.
-    # See django.test.client#L246
-    with pytest.raises(AttributeError):
-      c.get(reverse('desktop.views.log_view'))
-  finally:
-    # Restore the world
-    restore_django_debug()
-    restore_500_debug()
-    desktop.auth.backend.rewrite_user = original_rewrite_user
-
-
 @pytest.mark.django_db
 @pytest.mark.django_db
 def test_404_handling():
 def test_404_handling():
   pytest.skip("Skipping due to failures with pytest, investigation ongoing.")
   pytest.skip("Skipping due to failures with pytest, investigation ongoing.")