blob: 31cd0fdbf5c4c6acf8f9328c06a357903e12b83f [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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 {
Alignment,
AnchorButton,
Button,
Intent,
Menu,
MenuItem,
Navbar,
NavbarDivider,
NavbarGroup,
Popover,
Position,
} from '@blueprintjs/core';
import { IconNames } from '@blueprintjs/icons';
import React, { useState } from 'react';
import {
AboutDialog,
CoordinatorDynamicConfigDialog,
DoctorDialog,
OverlordDynamicConfigDialog,
} from '../../dialogs';
import { getLink } from '../../links';
import { Capabilities } from '../../utils/capabilities';
import { ExternalLink } from '../external-link/external-link';
import { PopoverText } from '../popover-text/popover-text';
import './header-bar.scss';
export type HeaderActiveTab =
| null
| 'load-data'
| 'ingestion'
| 'datasources'
| 'segments'
| 'services'
| 'query'
| 'lookups';
const DruidLogo = React.memo(function DruidLogo() {
return (
<div className="druid-logo">
<svg
version="1.1"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 288 134"
width="288"
height="134"
>
<path
fill="#FFFFFF"
d="M136.7,67.5c0.5-6.1,5-10.4,10.6-10.4c3.9,0,6.5,2,7.4,4.3l1.1-12.4c0-0.1,0.3-0.2,0.7-0.2
c0.7,0,1.3,0.4,1.2,2l-2.3,25.9c-0.1,0.7-0.5,1-1,1h-0.2c-0.6,0-0.9-0.3-0.8-1l0.3-3.2c-1.7,2.7-4.5,4.5-8.3,4.5
C139.9,77.9,136.2,73.7,136.7,67.5z M154,68.9l0.4-4.7c-0.9-3.3-3.3-5.4-7.2-5.4c-4.5,0-8.1,3.6-8.5,8.6
c-0.4,5.1,2.5,8.7,6.9,8.7C150,76.1,153.7,72.9,154,68.9z"
/>
<path
fill="#FFFFFF"
d="M161.2,76.6l1.7-19.1c0,0,0.3-0.2,0.7-0.2c0.7,0,1.3,0.4,1.1,2l-0.2,2.5c1.1-3.3,3.3-4.8,6-4.8
c1.6,0,2.7,0.7,2.6,1.7c-0.1,0.8-0.6,1.1-0.7,1.1c-0.5-0.5-1.3-0.8-2.3-0.8c-3.6,0-5.6,3.6-6.1,9l-0.8,8.7c-0.1,0.7-0.5,1-1,1
h-0.2C161.5,77.6,161.2,77.4,161.2,76.6z"
/>
<path
fill="#FFFFFF"
d="M175.6,69l0.9-10.7c0.1-0.8,0.5-1,1-1h0.3c0.5,0,0.9,0.2,0.8,1l-0.9,10.5c-0.4,4.4,1.5,7.2,5.5,7.2
c3.3,0,6-1.9,7.5-4.7l1.1-13c0.1-0.8,0.5-1,1-1h0.3c0.5,0,0.9,0.2,0.8,1l-1.7,19.1c0,0-0.4,0.2-0.7,0.2c-0.7,0-1.2-0.4-1.1-2
l0.2-1.8c-1.6,2.4-4.2,4.1-7.6,4.1C177.6,77.9,175.2,74.4,175.6,69z"
/>
<path
fill="#FFFFFF"
d="M200.1,50.7c0.1-1,0.6-1.4,1.6-1.4c0.9,0,1.4,0.5,1.3,1.4c-0.1,0.9-0.6,1.4-1.6,1.4
C200.5,52.1,200,51.6,200.1,50.7z M198.2,76.6l1.6-18.3c0.1-0.8,0.5-1,1-1h0.3c0.5,0,0.9,0.2,0.8,1l-1.6,18.3
c-0.1,0.8-0.5,1-1,1H199C198.5,77.6,198.2,77.4,198.2,76.6z"
/>
<path
fill="#FFFFFF"
d="M205.8,67.5c0.5-6.1,5-10.4,10.6-10.4c3.9,0,6.5,2,7.4,4.3l1.1-12.4c0-0.1,0.3-0.2,0.7-0.2
c0.7,0,1.3,0.4,1.2,2l-2.3,25.9c-0.1,0.7-0.5,1-1,1h-0.2c-0.5,0-0.9-0.3-0.8-1l0.3-3.2c-1.7,2.7-4.5,4.5-8.3,4.5
C209,77.9,205.2,73.7,205.8,67.5z M223.1,68.9l0.4-4.7c-0.9-3.3-3.3-5.4-7.2-5.4c-4.5,0-8.1,3.6-8.5,8.6
c-0.4,5.1,2.5,8.7,6.9,8.7C219,76.1,222.7,72.9,223.1,68.9z"
/>
<path
fill="#2CEEFB"
d="M96.2,89.8h-2.7c-0.7,0-1.3-0.6-1.3-1.3c0-0.7,0.6-1.3,1.3-1.3h2.7c11.5,0,23.8-7.4,23.8-23.7
c0-9.1-6.9-15.8-16.4-15.8H80c-0.7,0-1.3-0.6-1.3-1.3c0-0.7,0.6-1.3,1.3-1.3h23.6c5.3,0,10.1,1.9,13.6,5.3
c3.5,3.4,5.4,8,5.4,13.1c0,6.6-2.3,13-6.3,17.7C111.5,86.8,104.5,89.8,96.2,89.8z M87.1,89.8h-5.8c-0.7,0-1.3-0.6-1.3-1.3
c0-0.7,0.6-1.3,1.3-1.3h5.8c0.7,0,1.3,0.6,1.3,1.3C88.4,89.2,87.8,89.8,87.1,89.8z M97.7,79.5h-26c-0.7,0-1.3-0.6-1.3-1.3
c0-0.7,0.6-1.3,1.3-1.3h26c7.5,0,11.5-5.8,11.5-11.5c0-4.2-3.2-7.3-7.7-7.3h-26c-0.7,0-1.3-0.6-1.3-1.3c0-0.7,0.6-1.3,1.3-1.3
h26c5.9,0,10.3,4.3,10.3,9.9c0,3.7-1.3,7.2-3.7,9.8C105.5,78,101.9,79.5,97.7,79.5z M69.2,58h-6.3c-0.7,0-1.3-0.6-1.3-1.3
c0-0.7,0.6-1.3,1.3-1.3h6.3c0.7,0,1.3,0.6,1.3,1.3C70.5,57.4,69.9,58,69.2,58z"
/>
</svg>
</div>
);
});
interface RestrictedModeProps {
capabilities: Capabilities;
}
const RestrictedMode = React.memo(function RestrictedMode(props: RestrictedModeProps) {
const { capabilities } = props;
const mode = capabilities.getModeExtended();
let label: string;
let message: JSX.Element;
switch (mode) {
case 'full':
return null; // Do not show anything
case 'no-sql':
label = 'No SQL mode';
message = (
<p>
It appears that the SQL endpoint is disabled. The console will fall back to{' '}
<ExternalLink href={getLink('DOCS_API')}>native Druid APIs</ExternalLink> and will be
limited in functionality. Look at{' '}
<ExternalLink href={getLink('DOCS_SQL')}>the SQL docs</ExternalLink> to enable the SQL
endpoint.
</p>
);
break;
case 'no-proxy':
label = 'No management proxy mode';
message = (
<p>
It appears that the management proxy is not enabled, the console will operate with limited
functionality.
</p>
);
break;
case 'no-sql-no-proxy':
label = 'No SQL mode';
message = (
<p>
It appears that the SQL endpoint and management proxy are disabled. The console can only
be used to make queries.
</p>
);
break;
case 'coordinator':
label = 'Coordinator mode';
message = (
<p>
It appears that you are accessing the console on the Coordinator service. Due to the lack
of access to some APIs on this service the console will operate in a limited mode. The
full version of the console can be accessed on the Router service.
</p>
);
break;
case 'overlord':
label = 'Overlord mode';
message = (
<p>
It appears that you are accessing the console on the Overlord service. Due to the lack of
access to some APIs on this service the console will operate in a limited mode. The full
version of the console can be accessed on the Router service.
</p>
);
break;
default:
label = 'Restricted mode';
message = (
<p>
Due to the lack of access to some APIs on this service the console will operate in a
limited mode. The full version of the console can be accessed on the Router service.
</p>
);
break;
}
return (
<Popover
content={
<PopoverText>
<p>The console is running in restricted mode.</p>
{message}
<p>
For more info check out the{' '}
<ExternalLink href={`${getLink('DOCS')}/operations/management-uis.html#druid-console`}>
console documentation
</ExternalLink>
.
</p>
</PopoverText>
}
position={Position.BOTTOM_RIGHT}
>
<Button icon={IconNames.WARNING_SIGN} text={label} intent={Intent.WARNING} minimal />
</Popover>
);
});
export interface HeaderBarProps {
active: HeaderActiveTab;
capabilities: Capabilities;
}
export const HeaderBar = React.memo(function HeaderBar(props: HeaderBarProps) {
const { active, capabilities } = props;
const [aboutDialogOpen, setAboutDialogOpen] = useState(false);
const [doctorDialogOpen, setDoctorDialogOpen] = useState(false);
const [coordinatorDynamicConfigDialogOpen, setCoordinatorDynamicConfigDialogOpen] = useState(
false,
);
const [overlordDynamicConfigDialogOpen, setOverlordDynamicConfigDialogOpen] = useState(false);
const loadDataPrimary = false;
const helpMenu = (
<Menu>
<MenuItem icon={IconNames.GRAPH} text="About" onClick={() => setAboutDialogOpen(true)} />
<MenuItem icon={IconNames.TH} text="Docs" href={getLink('DOCS')} target="_blank" />
<MenuItem
icon={IconNames.USER}
text="User group"
href={getLink('USER_GROUP')}
target="_blank"
/>
<MenuItem
icon={IconNames.CHAT}
text="ASF Slack channel"
href={getLink('SLACK')}
target="_blank"
/>
<MenuItem
icon={IconNames.GIT_BRANCH}
text="GitHub"
href={getLink('GITHUB')}
target="_blank"
/>
</Menu>
);
const configMenu = (
<Menu>
<MenuItem
icon={IconNames.PULSE}
text="Druid Doctor"
onClick={() => setDoctorDialogOpen(true)}
disabled={!capabilities.hasEverything()}
/>
<MenuItem
icon={IconNames.SETTINGS}
text="Coordinator dynamic config"
onClick={() => setCoordinatorDynamicConfigDialogOpen(true)}
disabled={!capabilities.hasCoordinatorAccess()}
/>
<MenuItem
icon={IconNames.WRENCH}
text="Overlord dynamic config"
onClick={() => setOverlordDynamicConfigDialogOpen(true)}
disabled={!capabilities.hasOverlordAccess()}
/>
<MenuItem
icon={IconNames.PROPERTIES}
active={active === 'lookups'}
text="Lookups"
href="#lookups"
disabled={!capabilities.hasCoordinatorAccess()}
/>
</Menu>
);
return (
<Navbar className="header-bar">
<NavbarGroup align={Alignment.LEFT}>
<a href="#">
<DruidLogo />
</a>
<NavbarDivider />
<AnchorButton
icon={IconNames.CLOUD_UPLOAD}
text="Load data"
active={active === 'load-data'}
href="#load-data"
minimal={!loadDataPrimary}
intent={loadDataPrimary ? Intent.PRIMARY : Intent.NONE}
disabled={!capabilities.hasEverything()}
/>
<NavbarDivider />
<AnchorButton
minimal
active={active === 'ingestion'}
icon={IconNames.GANTT_CHART}
text="Ingestion"
href="#ingestion"
disabled={!capabilities.hasSqlOrOverlordAccess()}
/>
<AnchorButton
minimal
active={active === 'datasources'}
icon={IconNames.MULTI_SELECT}
text="Datasources"
href="#datasources"
disabled={!capabilities.hasSqlOrCoordinatorAccess()}
/>
<AnchorButton
minimal
active={active === 'segments'}
icon={IconNames.STACKED_CHART}
text="Segments"
href="#segments"
disabled={!capabilities.hasSqlOrCoordinatorAccess()}
/>
<AnchorButton
minimal
active={active === 'services'}
icon={IconNames.DATABASE}
text="Services"
href="#services"
disabled={!capabilities.hasSqlOrCoordinatorAccess()}
/>
<NavbarDivider />
<AnchorButton
minimal
active={active === 'query'}
icon={IconNames.APPLICATION}
text="Query"
href="#query"
disabled={!capabilities.hasQuerying()}
/>
</NavbarGroup>
<NavbarGroup align={Alignment.RIGHT}>
<RestrictedMode capabilities={capabilities} />
<Popover content={configMenu} position={Position.BOTTOM_RIGHT}>
<Button minimal icon={IconNames.COG} />
</Popover>
<Popover content={helpMenu} position={Position.BOTTOM_RIGHT}>
<Button minimal icon={IconNames.HELP} />
</Popover>
</NavbarGroup>
{aboutDialogOpen && <AboutDialog onClose={() => setAboutDialogOpen(false)} />}
{doctorDialogOpen && <DoctorDialog onClose={() => setDoctorDialogOpen(false)} />}
{coordinatorDynamicConfigDialogOpen && (
<CoordinatorDynamicConfigDialog
onClose={() => setCoordinatorDynamicConfigDialogOpen(false)}
/>
)}
{overlordDynamicConfigDialogOpen && (
<OverlordDynamicConfigDialog onClose={() => setOverlordDynamicConfigDialogOpen(false)} />
)}
</Navbar>
);
});