Introduce CheckServices component

pull/10616/head
Andrew Watkins 2018-05-18 12:34:40 -07:00
parent cd66eae659
commit 3d186b1f93
14 changed files with 242 additions and 43 deletions

View File

@ -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 />,
}, },

View File

@ -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))

View File

@ -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 => ({

View File

@ -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}

View File

@ -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'

View File

@ -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))
}
}

View File

@ -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
}
}

View File

@ -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',
}

View File

@ -32,6 +32,9 @@ const servicesReducer = (state = initialState, action: Action): Service[] => {
return newState return newState
} }
case 'SET_ACTIVE_SERVICE': {
}
} }
return state return state

View File

@ -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

View File

@ -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}

View File

@ -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)

View File

@ -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

View File

@ -32,4 +32,5 @@ export interface SourceLinks {
databases: string databases: string
annotations: string annotations: string
health: string health: string
services: string
} }