Introduce CheckServices component
parent
cd66eae659
commit
3d186b1f93
|
@ -82,11 +82,7 @@ class TimeMachine extends PureComponent<Props> {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
name: 'Explore',
|
name: 'Explore',
|
||||||
headerButtons: [
|
headerButtons: [],
|
||||||
<div key="analyze" className="btn btn-primary btn-xs">
|
|
||||||
Analyze
|
|
||||||
</div>,
|
|
||||||
],
|
|
||||||
menuOptions: [],
|
menuOptions: [],
|
||||||
render: () => <SchemaExplorer />,
|
render: () => <SchemaExplorer />,
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
import {PureComponent, ReactChildren} from 'react'
|
||||||
|
import {connect} from 'react-redux'
|
||||||
|
import {withRouter, WithRouterProps} from 'react-router'
|
||||||
|
import {Source} from 'src/types'
|
||||||
|
|
||||||
|
import * as actions from 'src/shared/actions/services'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
sources: Source[]
|
||||||
|
children: ReactChildren
|
||||||
|
fetchServicesAsync: actions.FetchServicesAsync
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CheckServices extends PureComponent<Props & WithRouterProps> {
|
||||||
|
public async componentDidMount() {
|
||||||
|
const source = this.props.sources.find(
|
||||||
|
s => s.id === this.props.params.sourceID
|
||||||
|
)
|
||||||
|
|
||||||
|
if (!source) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.props.fetchServicesAsync(source)
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
return this.props.children
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mdtp = {
|
||||||
|
fetchServicesAsync: actions.fetchServicesAsync,
|
||||||
|
}
|
||||||
|
|
||||||
|
const mstp = ({sources}) => ({sources})
|
||||||
|
|
||||||
|
export default connect(mstp, mdtp)(withRouter(CheckServices))
|
|
@ -3,8 +3,9 @@ import {bindActionCreators} from 'redux'
|
||||||
import {connect} from 'react-redux'
|
import {connect} from 'react-redux'
|
||||||
import _ from 'lodash'
|
import _ from 'lodash'
|
||||||
|
|
||||||
import TimeMachine from 'src/ifql/components/TimeMachine'
|
|
||||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
|
import CheckServices from 'src/ifql/containers/CheckServices'
|
||||||
|
import TimeMachine from 'src/ifql/components/TimeMachine'
|
||||||
import KeyboardShortcuts from 'src/shared/components/KeyboardShortcuts'
|
import KeyboardShortcuts from 'src/shared/components/KeyboardShortcuts'
|
||||||
import {InputArg, Handlers, DeleteFuncNodeArgs, Func} from 'src/types/ifql'
|
import {InputArg, Handlers, DeleteFuncNodeArgs, Func} from 'src/types/ifql'
|
||||||
import {notify as notifyAction} from 'src/shared/actions/notifications'
|
import {notify as notifyAction} from 'src/shared/actions/notifications'
|
||||||
|
@ -16,6 +17,7 @@ import {builder, argTypes} from 'src/ifql/constants'
|
||||||
|
|
||||||
import {Notification} from 'src/types'
|
import {Notification} from 'src/types'
|
||||||
import {Suggestion, FlatBody, Links} from 'src/types/ifql'
|
import {Suggestion, FlatBody, Links} from 'src/types/ifql'
|
||||||
|
import {Service} from 'src/types'
|
||||||
|
|
||||||
interface Status {
|
interface Status {
|
||||||
type: string
|
type: string
|
||||||
|
@ -24,6 +26,7 @@ interface Status {
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
links: Links
|
links: Links
|
||||||
|
services: Service[]
|
||||||
notify: (message: Notification) => void
|
notify: (message: Notification) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,39 +79,41 @@ export class IFQLPage extends PureComponent<Props, State> {
|
||||||
const {suggestions, script, data, body, status} = this.state
|
const {suggestions, script, data, body, status} = this.state
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IFQLContext.Provider value={this.handlers}>
|
<CheckServices>
|
||||||
<KeyboardShortcuts onControlEnter={this.getTimeSeries}>
|
<IFQLContext.Provider value={this.handlers}>
|
||||||
<div className="page hosts-list-page">
|
<KeyboardShortcuts onControlEnter={this.getTimeSeries}>
|
||||||
<div className="page-header full-width">
|
<div className="page hosts-list-page">
|
||||||
<div className="page-header__container">
|
<div className="page-header full-width">
|
||||||
<div className="page-header__left">
|
<div className="page-header__container">
|
||||||
<h1 className="page-header__title">Time Machine</h1>
|
<div className="page-header__left">
|
||||||
</div>
|
<h1 className="page-header__title">Time Machine</h1>
|
||||||
<div className="page-header__right">
|
</div>
|
||||||
<button
|
<div className="page-header__right">
|
||||||
className="btn btn-sm btn-primary"
|
<button
|
||||||
onClick={this.getTimeSeries}
|
className="btn btn-sm btn-primary"
|
||||||
>
|
onClick={this.getTimeSeries}
|
||||||
Get Data!
|
>
|
||||||
</button>
|
Get Data!
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<TimeMachine
|
||||||
|
data={data}
|
||||||
|
body={body}
|
||||||
|
script={script}
|
||||||
|
status={status}
|
||||||
|
suggestions={suggestions}
|
||||||
|
onAnalyze={this.handleAnalyze}
|
||||||
|
onAppendFrom={this.handleAppendFrom}
|
||||||
|
onAppendJoin={this.handleAppendJoin}
|
||||||
|
onChangeScript={this.handleChangeScript}
|
||||||
|
onSubmitScript={this.handleSubmitScript}
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<TimeMachine
|
</KeyboardShortcuts>
|
||||||
data={data}
|
</IFQLContext.Provider>
|
||||||
body={body}
|
</CheckServices>
|
||||||
script={script}
|
|
||||||
status={status}
|
|
||||||
suggestions={suggestions}
|
|
||||||
onAnalyze={this.handleAnalyze}
|
|
||||||
onAppendFrom={this.handleAppendFrom}
|
|
||||||
onAppendJoin={this.handleAppendJoin}
|
|
||||||
onChangeScript={this.handleChangeScript}
|
|
||||||
onSubmitScript={this.handleSubmitScript}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</KeyboardShortcuts>
|
|
||||||
</IFQLContext.Provider>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -416,8 +421,8 @@ export class IFQLPage extends PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = ({links}) => {
|
const mapStateToProps = ({links, services}) => {
|
||||||
return {links: links.ifql}
|
return {links: links.ifql, services}
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapDispatchToProps = dispatch => ({
|
const mapDispatchToProps = dispatch => ({
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
import IFQLPage from 'src/ifql/containers/IFQLPage'
|
import IFQLPage from 'src/ifql/containers/IFQLPage'
|
||||||
|
import CheckServices from 'src/ifql/containers/CheckServices'
|
||||||
|
|
||||||
export {IFQLPage}
|
export {IFQLPage, CheckServices}
|
||||||
|
|
|
@ -35,7 +35,7 @@ import {
|
||||||
} from 'src/kapacitor'
|
} from 'src/kapacitor'
|
||||||
import {AdminChronografPage, AdminInfluxDBPage} from 'src/admin'
|
import {AdminChronografPage, AdminInfluxDBPage} from 'src/admin'
|
||||||
import {SourcePage, ManageSources} from 'src/sources'
|
import {SourcePage, ManageSources} from 'src/sources'
|
||||||
import {IFQLPage} from 'src/ifql/index'
|
import {IFQLPage} from 'src/ifql'
|
||||||
import NotFound from 'src/shared/components/NotFound'
|
import NotFound from 'src/shared/components/NotFound'
|
||||||
|
|
||||||
import {getLinksAsync} from 'src/shared/actions/links'
|
import {getLinksAsync} from 'src/shared/actions/links'
|
||||||
|
|
|
@ -1,4 +1,7 @@
|
||||||
import {Source, Service} from 'src/types'
|
import {Source, Service} from 'src/types'
|
||||||
|
import {getServices as getServicesAJAX} from 'src/shared/apis'
|
||||||
|
import {notify} from './notifications'
|
||||||
|
import {couldNotGetServices} from 'src/shared/copy/notifications'
|
||||||
|
|
||||||
export type Action =
|
export type Action =
|
||||||
| ActionLoadServices
|
| ActionLoadServices
|
||||||
|
@ -94,3 +97,16 @@ export const setActiveService = (
|
||||||
service,
|
service,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export type FetchServicesAsync = (source: Source) => (dispatch) => Promise<void>
|
||||||
|
|
||||||
|
export const fetchServicesAsync = (source: Source) => async (
|
||||||
|
dispatch
|
||||||
|
): Promise<void> => {
|
||||||
|
try {
|
||||||
|
const services = await getServicesAJAX(source.links.services)
|
||||||
|
dispatch(loadServices(services))
|
||||||
|
} catch (err) {
|
||||||
|
dispatch(notify(couldNotGetServices))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import AJAX from 'src/utils/ajax'
|
import AJAX from 'src/utils/ajax'
|
||||||
import {AlertTypes} from 'src/kapacitor/constants'
|
import {AlertTypes} from 'src/kapacitor/constants'
|
||||||
import {Kapacitor} from 'src/types'
|
import {Kapacitor, Service} from 'src/types'
|
||||||
|
|
||||||
export function getSources() {
|
export function getSources() {
|
||||||
return AJAX({
|
return AJAX({
|
||||||
|
@ -303,3 +303,17 @@ export const getQueryConfigAndStatus = (url, queries, tempVars = []) =>
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: {queries, tempVars},
|
data: {queries, tempVars},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const getServices = async (url: string): Promise<Service[]> => {
|
||||||
|
try {
|
||||||
|
const {data} = await AJAX({
|
||||||
|
url,
|
||||||
|
method: 'GET',
|
||||||
|
})
|
||||||
|
|
||||||
|
return data
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -614,3 +614,9 @@ export const analyzeSuccess = {
|
||||||
...defaultSuccessNotification,
|
...defaultSuccessNotification,
|
||||||
message: 'No errors found. Happy Happy Joy Joy!',
|
message: 'No errors found. Happy Happy Joy Joy!',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Service notifications
|
||||||
|
export const couldNotGetServices = {
|
||||||
|
...defaultErrorNotification,
|
||||||
|
message: 'We could not get services',
|
||||||
|
}
|
||||||
|
|
|
@ -32,6 +32,9 @@ const servicesReducer = (state = initialState, action: Action): Service[] => {
|
||||||
|
|
||||||
return newState
|
return newState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case 'SET_ACTIVE_SERVICE': {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return state
|
return state
|
||||||
|
|
|
@ -13,7 +13,6 @@ const InfluxTableHead: SFC<{}> = (): ReactElement<
|
||||||
<th className="source-table--connect-col" />
|
<th className="source-table--connect-col" />
|
||||||
<th>InfluxDB Connection</th>
|
<th>InfluxDB Connection</th>
|
||||||
<th className="text-right" />
|
<th className="text-right" />
|
||||||
<th>Services Connection</th>
|
|
||||||
<th>
|
<th>
|
||||||
Kapacitor Connection
|
Kapacitor Connection
|
||||||
<QuestionMarkTooltip
|
<QuestionMarkTooltip
|
||||||
|
|
|
@ -45,7 +45,6 @@ class InfluxTableRow extends PureComponent<Props> {
|
||||||
/>
|
/>
|
||||||
</Authorized>
|
</Authorized>
|
||||||
</td>
|
</td>
|
||||||
<td>Services Connection</td>
|
|
||||||
<td className="source-table--kapacitor">
|
<td className="source-table--kapacitor">
|
||||||
<KapacitorDropdown
|
<KapacitorDropdown
|
||||||
source={source}
|
source={source}
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
import React, {PureComponent, ReactElement} from 'react'
|
||||||
|
import {Link, withRouter, RouteComponentProps} from 'react-router'
|
||||||
|
|
||||||
|
import Dropdown from 'src/shared/components/Dropdown'
|
||||||
|
import Authorized, {EDITOR_ROLE} from 'src/auth/Authorized'
|
||||||
|
|
||||||
|
import {Source, Service} from 'src/types'
|
||||||
|
import {SetActiveService} from 'src/shared/actions/services'
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
source: Source
|
||||||
|
services: Service[]
|
||||||
|
setActiveService: SetActiveService
|
||||||
|
deleteService: (service: Service) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ServiceItem {
|
||||||
|
text: string
|
||||||
|
resource: string
|
||||||
|
service: Service
|
||||||
|
}
|
||||||
|
|
||||||
|
class ServiceDropdown extends PureComponent<
|
||||||
|
Props & RouteComponentProps<any, any>
|
||||||
|
> {
|
||||||
|
public render() {
|
||||||
|
const {source, router, setActiveService, deleteService} = this.props
|
||||||
|
|
||||||
|
if (this.isServicesEmpty) {
|
||||||
|
return (
|
||||||
|
<Authorized requiredRole={EDITOR_ROLE}>
|
||||||
|
<Link
|
||||||
|
to={`/sources/${source.id}/services/new`}
|
||||||
|
className="btn btn-xs btn-default"
|
||||||
|
>
|
||||||
|
<span className="icon plus" /> Add Service Connection
|
||||||
|
</Link>
|
||||||
|
</Authorized>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Authorized
|
||||||
|
requiredRole={EDITOR_ROLE}
|
||||||
|
replaceWithIfNotAuthorized={this.UnauthorizedDropdown}
|
||||||
|
>
|
||||||
|
<Dropdown
|
||||||
|
className="dropdown-260"
|
||||||
|
buttonColor="btn-primary"
|
||||||
|
buttonSize="btn-xs"
|
||||||
|
items={this.serviceItems}
|
||||||
|
onChoose={setActiveService}
|
||||||
|
addNew={{
|
||||||
|
url: `/sources/${source.id}/services/new`,
|
||||||
|
text: 'Add Service Connection',
|
||||||
|
}}
|
||||||
|
actions={[
|
||||||
|
{
|
||||||
|
icon: 'pencil',
|
||||||
|
text: 'edit',
|
||||||
|
handler: item => {
|
||||||
|
router.push(`${item.resource}/edit`)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
icon: 'trash',
|
||||||
|
text: 'delete',
|
||||||
|
handler: item => {
|
||||||
|
deleteService(item.service)
|
||||||
|
},
|
||||||
|
confirmable: true,
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
selected={this.selected}
|
||||||
|
/>
|
||||||
|
</Authorized>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private get UnauthorizedDropdown(): ReactElement<HTMLDivElement> {
|
||||||
|
return (
|
||||||
|
<div className="source-table--service__view-only">{this.selected}</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private get isServicesEmpty(): boolean {
|
||||||
|
const {services} = this.props
|
||||||
|
return !services || services.length === 0
|
||||||
|
}
|
||||||
|
|
||||||
|
private get serviceItems(): ServiceItem[] {
|
||||||
|
const {services, source} = this.props
|
||||||
|
|
||||||
|
return services.map(service => {
|
||||||
|
return {
|
||||||
|
text: service.name,
|
||||||
|
resource: `/sources/${source.id}/services/${service.id}`,
|
||||||
|
service,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private get activeService(): Service {
|
||||||
|
return this.props.services.find(s => s.active)
|
||||||
|
}
|
||||||
|
|
||||||
|
private get selected(): string {
|
||||||
|
let selected = ''
|
||||||
|
if (this.activeService) {
|
||||||
|
selected = this.activeService.name
|
||||||
|
} else {
|
||||||
|
selected = this.serviceItems[0].text
|
||||||
|
}
|
||||||
|
|
||||||
|
return selected
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default withRouter<Props>(ServiceDropdown)
|
|
@ -16,6 +16,7 @@ import cellEditorOverlay from 'src/dashboards/reducers/cellEditorOverlay'
|
||||||
import overlayTechnology from 'src/shared/reducers/overlayTechnology'
|
import overlayTechnology from 'src/shared/reducers/overlayTechnology'
|
||||||
import dashTimeV1 from 'src/dashboards/reducers/dashTimeV1'
|
import dashTimeV1 from 'src/dashboards/reducers/dashTimeV1'
|
||||||
import persistStateEnhancer from './persistStateEnhancer'
|
import persistStateEnhancer from './persistStateEnhancer'
|
||||||
|
import servicesReducer from 'src/shared/reducers/services'
|
||||||
|
|
||||||
const rootReducer = combineReducers({
|
const rootReducer = combineReducers({
|
||||||
...statusReducers,
|
...statusReducers,
|
||||||
|
@ -28,6 +29,7 @@ const rootReducer = combineReducers({
|
||||||
overlayTechnology,
|
overlayTechnology,
|
||||||
dashTimeV1,
|
dashTimeV1,
|
||||||
routing: routerReducer,
|
routing: routerReducer,
|
||||||
|
services: servicesReducer,
|
||||||
})
|
})
|
||||||
|
|
||||||
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
|
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose
|
||||||
|
|
|
@ -32,4 +32,5 @@ export interface SourceLinks {
|
||||||
databases: string
|
databases: string
|
||||||
annotations: string
|
annotations: string
|
||||||
health: string
|
health: string
|
||||||
|
services: string
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue