Skip to content

Commit

Permalink
Making the ServerLogs component
Browse files Browse the repository at this point in the history
WIP

WIP

Searchbox working

WIP

WIP

WIP

w

f

WIP

r

t

WIP

WIP

WIP

WIP
  • Loading branch information
ananya-agarwal committed Jan 3, 2025
1 parent ca87369 commit acab827
Show file tree
Hide file tree
Showing 11 changed files with 390 additions and 207 deletions.
Original file line number Diff line number Diff line change
@@ -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 {
.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;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// 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="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"
/>
<span className="server__wrap-logs">Wrap logs</span>
<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;
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// 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 {
.server-logs-component {
background-color: $fluidx-gray-100;
padding: 24px;

.server__display-logs {
overflow: auto;
background-color: $fluidx-white;
width: 100%;
padding: 10px 0 10px 0;

.server__log-line {
background-color: $fluidx-white;
margin: 0;
padding: 2px;
}
}

.server_nowrap {
white-space: nowrap;
}

.server__no-logs-found {
background-color: $fluidx-white;
}

.server--highlight-word {
background-color: $fluidx-pear-050;
color: $fluidx-black;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// 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 { render, waitFor, screen } from '@testing-library/react';
import '@testing-library/jest-dom';
import ServerLogs from './ServerLogsTab';

global.fetch = jest.fn(() =>
Promise.resolve({
ok: true,
json: () =>
Promise.resolve({
logs: ['Log entry 1', 'Log entry 2'],
hue_hostname: 'test-hostname'
})
})
) as jest.Mock;

beforeEach(() => {
jest.clearAllMocks();
});

describe('ServerLogs Component', () => {
test('Render ServerLogs component with fetched logs', async () => {
render(<ServerLogs />);

await waitFor(() => {
expect(screen.getByText('Log entry 1')).toBeInTheDocument();
expect(screen.getByText('Log entry 2')).toBeInTheDocument();
expect(global.fetch).toHaveBeenCalledWith('/api/v1/logs');
});
});

test('Handles no logs found scenario', async () => {
(global.fetch as jest.Mock).mockImplementationOnce(() =>
Promise.resolve({
ok: true,
json: () => Promise.resolve({ logs: [] })
})
);

render(<ServerLogs />);

await waitFor(() => {
expect(screen.getByText('No logs found!')).toBeInTheDocument();
});
});
});
109 changes: 109 additions & 0 deletions desktop/core/src/desktop/js/apps/admin/ServerLogs/ServerLogsTab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
// 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, useEffect } from 'react';
import { Spin, Alert } from 'antd';
import ServerLogsHeader from './ServerLogsHeader';
import useLoadData from '../../../utils/hooks/useLoadData';
import './ServerLogsTab.scss';

const ServerLogs: React.FC = (): JSX.Element => {
const [filter, setFilter] = useState<string>('');
const [wrapLogs, setWrapLogs] = useState(true);

const {
data: logsData,
loading,
error,

Check failure on line 30 in desktop/core/src/desktop/js/apps/admin/ServerLogs/ServerLogsTab.tsx

View workflow job for this annotation

GitHub Actions / build

Delete `,`
//reloadData
} = useLoadData<{ logs: string[]; hue_hostname: string }>('/api/v1/logs');

useEffect(() => {
const updateSize = () => {
const newHeight = document.documentElement.clientHeight - 250;
const logsComponent = document.querySelector('.server__display-logs') as HTMLElement;
if (logsComponent) {
logsComponent.style.height = `${newHeight}px`;
}
};
updateSize();
window.addEventListener('resize', updateSize);
return () => window.removeEventListener('resize', updateSize);
}, []);

const highlightText = (text: string, searchValue: string) => {
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={'parts_' + index} className="server--highlight-word">
{part}
</mark>
) : (
part
)
);
};

if (error) {
return (
<div className="server-logs-component">
<Alert
message={`Error: ${error}`}
description="An error occurred while fetching server logs."
type="error"
/>
</div>
);
}

return (
<div className="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>

Check failure on line 87 in desktop/core/src/desktop/js/apps/admin/ServerLogs/ServerLogsTab.tsx

View workflow job for this annotation

GitHub Actions / build

Delete `··`
)}

Check failure on line 88 in desktop/core/src/desktop/js/apps/admin/ServerLogs/ServerLogsTab.tsx

View workflow job for this annotation

GitHub Actions / build

Delete `··`

{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_nowrap' : ''}`}
key={'logs_' + index}
>
{highlightText(line, filter)}
</div>
))}
</div>
)}
</>
)}
</Spin>
</div>
);
};

export default ServerLogs;
Loading

0 comments on commit acab827

Please sign in to comment.