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 [
{
name: 'Explore',
headerButtons: [
<div key="analyze" className="btn btn-primary btn-xs">
Analyze
</div>,
],
headerButtons: [],
menuOptions: [],
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 _ from 'lodash'
import TimeMachine from 'src/ifql/components/TimeMachine'
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 {InputArg, Handlers, DeleteFuncNodeArgs, Func} from 'src/types/ifql'
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 {Suggestion, FlatBody, Links} from 'src/types/ifql'
import {Service} from 'src/types'
interface Status {
type: string
@ -24,6 +26,7 @@ interface Status {
interface Props {
links: Links
services: Service[]
notify: (message: Notification) => void
}
@ -76,39 +79,41 @@ export class IFQLPage extends PureComponent<Props, State> {
const {suggestions, script, data, body, status} = this.state
return (
<IFQLContext.Provider value={this.handlers}>
<KeyboardShortcuts onControlEnter={this.getTimeSeries}>
<div className="page hosts-list-page">
<div className="page-header full-width">
<div className="page-header__container">
<div className="page-header__left">
<h1 className="page-header__title">Time Machine</h1>
</div>
<div className="page-header__right">
<button
className="btn btn-sm btn-primary"
onClick={this.getTimeSeries}
>
Get Data!
</button>
<CheckServices>
<IFQLContext.Provider value={this.handlers}>
<KeyboardShortcuts onControlEnter={this.getTimeSeries}>
<div className="page hosts-list-page">
<div className="page-header full-width">
<div className="page-header__container">
<div className="page-header__left">
<h1 className="page-header__title">Time Machine</h1>
</div>
<div className="page-header__right">
<button
className="btn btn-sm btn-primary"
onClick={this.getTimeSeries}
>
Get Data!
</button>
</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>
<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>
</KeyboardShortcuts>
</IFQLContext.Provider>
</KeyboardShortcuts>
</IFQLContext.Provider>
</CheckServices>
)
}
@ -416,8 +421,8 @@ export class IFQLPage extends PureComponent<Props, State> {
}
}
const mapStateToProps = ({links}) => {
return {links: links.ifql}
const mapStateToProps = ({links, services}) => {
return {links: links.ifql, services}
}
const mapDispatchToProps = dispatch => ({

View File

@ -1,3 +1,4 @@
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'
import {AdminChronografPage, AdminInfluxDBPage} from 'src/admin'
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 {getLinksAsync} from 'src/shared/actions/links'

View File

@ -1,4 +1,7 @@
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 =
| ActionLoadServices
@ -94,3 +97,16 @@ export const setActiveService = (
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 {AlertTypes} from 'src/kapacitor/constants'
import {Kapacitor} from 'src/types'
import {Kapacitor, Service} from 'src/types'
export function getSources() {
return AJAX({
@ -303,3 +303,17 @@ export const getQueryConfigAndStatus = (url, queries, tempVars = []) =>
method: 'POST',
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,
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
}
case 'SET_ACTIVE_SERVICE': {
}
}
return state

View File

@ -13,7 +13,6 @@ const InfluxTableHead: SFC<{}> = (): ReactElement<
<th className="source-table--connect-col" />
<th>InfluxDB Connection</th>
<th className="text-right" />
<th>Services Connection</th>
<th>
Kapacitor Connection
<QuestionMarkTooltip

View File

@ -45,7 +45,6 @@ class InfluxTableRow extends PureComponent<Props> {
/>
</Authorized>
</td>
<td>Services Connection</td>
<td className="source-table--kapacitor">
<KapacitorDropdown
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 dashTimeV1 from 'src/dashboards/reducers/dashTimeV1'
import persistStateEnhancer from './persistStateEnhancer'
import servicesReducer from 'src/shared/reducers/services'
const rootReducer = combineReducers({
...statusReducers,
@ -28,6 +29,7 @@ const rootReducer = combineReducers({
overlayTechnology,
dashTimeV1,
routing: routerReducer,
services: servicesReducer,
})
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose

View File

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