Source form and page
parent
36355f18d9
commit
08bf9bcd27
|
@ -450,8 +450,7 @@ export const populateNamespacesAsync = (
|
|||
export const getSourceAndPopulateNamespacesAsync = (sourceID: string) => async (
|
||||
dispatch
|
||||
): Promise<void> => {
|
||||
const response = await getSource(sourceID)
|
||||
const source = response.data
|
||||
const source = await getSource(sourceID)
|
||||
|
||||
const proxyLink = getDeep<string | null>(source, 'links.proxy', null)
|
||||
|
||||
|
|
|
@ -9,29 +9,51 @@ export function getSources() {
|
|||
})
|
||||
}
|
||||
|
||||
export function getSource(id) {
|
||||
return AJAX({
|
||||
url: null,
|
||||
resource: 'sources',
|
||||
id,
|
||||
})
|
||||
export const getSource = async (id: string): Promise<Source> => {
|
||||
try {
|
||||
const {data: source} = await AJAX({
|
||||
url: null,
|
||||
resource: 'sources',
|
||||
id,
|
||||
})
|
||||
|
||||
return source
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export function createSource(attributes) {
|
||||
return AJAX({
|
||||
url: null,
|
||||
resource: 'sources',
|
||||
method: 'POST',
|
||||
data: attributes,
|
||||
})
|
||||
export const createSource = async (
|
||||
attributes: Partial<Source>
|
||||
): Promise<Source> => {
|
||||
try {
|
||||
const {data: source} = await AJAX({
|
||||
url: null,
|
||||
resource: 'sources',
|
||||
method: 'POST',
|
||||
data: attributes,
|
||||
})
|
||||
|
||||
return source
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export function updateSource(newSource) {
|
||||
return AJAX({
|
||||
url: newSource.links.self,
|
||||
method: 'PATCH',
|
||||
data: newSource,
|
||||
})
|
||||
export const updateSource = async (
|
||||
newSource: Partial<Source>
|
||||
): Promise<Source> => {
|
||||
try {
|
||||
const {data: source} = await AJAX({
|
||||
url: newSource.links.self,
|
||||
method: 'PATCH',
|
||||
data: newSource,
|
||||
})
|
||||
|
||||
return source
|
||||
} catch (error) {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
export function deleteSource(source) {
|
||||
|
|
|
@ -155,8 +155,10 @@ export const notifyUnableToRetrieveSources = () => 'Unable to retrieve sources.'
|
|||
export const notifyUnableToConnectSource = sourceName =>
|
||||
`Unable to connect to source ${sourceName}.`
|
||||
|
||||
export const notifyErrorConnectingToSource = errorMessage =>
|
||||
`Unable to connect to InfluxDB source: ${errorMessage}`
|
||||
export const notifyErrorConnectingToSource = errorMessage => ({
|
||||
...defaultErrorNotification,
|
||||
message: `Unable to connect to InfluxDB source: ${errorMessage}`,
|
||||
})
|
||||
|
||||
// Multitenancy User Notifications
|
||||
// ----------------------------------------------------------------------------
|
||||
|
|
|
@ -1,208 +0,0 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import classnames from 'classnames'
|
||||
import {connect} from 'react-redux'
|
||||
import _ from 'lodash'
|
||||
|
||||
import {insecureSkipVerifyText} from 'shared/copy/tooltipText'
|
||||
|
||||
import {SUPERADMIN_ROLE} from 'src/auth/Authorized'
|
||||
|
||||
export const SourceForm = ({
|
||||
source,
|
||||
editMode,
|
||||
onSubmit,
|
||||
onInputChange,
|
||||
onBlurSourceURL,
|
||||
isUsingAuth,
|
||||
gotoPurgatory,
|
||||
isInitialSource,
|
||||
me,
|
||||
}) => (
|
||||
<div className="panel-body">
|
||||
{isUsingAuth && isInitialSource ? (
|
||||
<div className="text-center">
|
||||
{me.role === SUPERADMIN_ROLE ? (
|
||||
<h3>
|
||||
<strong>{me.currentOrganization.name}</strong> has no connections
|
||||
</h3>
|
||||
) : (
|
||||
<h3>
|
||||
<strong>{me.currentOrganization.name}</strong> has no connections
|
||||
available to <em>{me.role}s</em>
|
||||
</h3>
|
||||
)}
|
||||
<h6>Add a Connection below:</h6>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<form onSubmit={onSubmit}>
|
||||
<div className="form-group col-xs-12 col-sm-6">
|
||||
<label htmlFor="connect-string">Connection String</label>
|
||||
<input
|
||||
type="text"
|
||||
name="url"
|
||||
className="form-control"
|
||||
id="connect-string"
|
||||
placeholder="Address of InfluxDB"
|
||||
onChange={onInputChange}
|
||||
value={source.url}
|
||||
onBlur={onBlurSourceURL}
|
||||
required={true}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group col-xs-12 col-sm-6">
|
||||
<label htmlFor="name">Name</label>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
className="form-control"
|
||||
id="name"
|
||||
placeholder="Name this source"
|
||||
onChange={onInputChange}
|
||||
value={source.name}
|
||||
required={true}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group col-xs-12 col-sm-6">
|
||||
<label htmlFor="username">Username</label>
|
||||
<input
|
||||
type="text"
|
||||
name="username"
|
||||
className="form-control"
|
||||
id="username"
|
||||
onChange={onInputChange}
|
||||
value={source.username}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group col-xs-12 col-sm-6">
|
||||
<label htmlFor="password">Password</label>
|
||||
<input
|
||||
type="password"
|
||||
name="password"
|
||||
className="form-control"
|
||||
id="password"
|
||||
onChange={onInputChange}
|
||||
value={source.password}
|
||||
/>
|
||||
</div>
|
||||
{_.get(source, 'type', '').includes('enterprise') ? (
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="meta-url">Meta Service Connection URL</label>
|
||||
<input
|
||||
type="text"
|
||||
name="metaUrl"
|
||||
className="form-control"
|
||||
id="meta-url"
|
||||
placeholder="http://localhost:8091"
|
||||
onChange={onInputChange}
|
||||
value={source.metaUrl}
|
||||
/>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="form-group col-xs-12 col-sm-6">
|
||||
<label htmlFor="telegraf">Telegraf Database</label>
|
||||
<input
|
||||
type="text"
|
||||
name="telegraf"
|
||||
className="form-control"
|
||||
id="telegraf"
|
||||
onChange={onInputChange}
|
||||
value={source.telegraf}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group col-xs-12 col-sm-6">
|
||||
<label htmlFor="defaultRP">Default Retention Policy</label>
|
||||
<input
|
||||
type="text"
|
||||
name="defaultRP"
|
||||
className="form-control"
|
||||
id="defaultRP"
|
||||
onChange={onInputChange}
|
||||
value={source.defaultRP}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group col-xs-12">
|
||||
<div className="form-control-static">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="defaultConnectionCheckbox"
|
||||
name="default"
|
||||
checked={source.default}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
<label htmlFor="defaultConnectionCheckbox">
|
||||
Make this the default connection
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{_.get(source, 'url', '').startsWith('https') ? (
|
||||
<div className="form-group col-xs-12">
|
||||
<div className="form-control-static">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="insecureSkipVerifyCheckbox"
|
||||
name="insecureSkipVerify"
|
||||
checked={source.insecureSkipVerify}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
<label htmlFor="insecureSkipVerifyCheckbox">Unsafe SSL</label>
|
||||
</div>
|
||||
<label className="form-helper">{insecureSkipVerifyText}</label>
|
||||
</div>
|
||||
) : null}
|
||||
<div className="form-group form-group-submit text-center col-xs-12 col-sm-6 col-sm-offset-3">
|
||||
<button
|
||||
className={classnames('btn btn-block', {
|
||||
'btn-primary': editMode,
|
||||
'btn-success': !editMode,
|
||||
})}
|
||||
type="submit"
|
||||
>
|
||||
<span className={`icon ${editMode ? 'checkmark' : 'plus'}`} />
|
||||
{editMode ? 'Save Changes' : 'Add Connection'}
|
||||
</button>
|
||||
|
||||
<br />
|
||||
{isUsingAuth ? (
|
||||
<button className="btn btn-link btn-sm" onClick={gotoPurgatory}>
|
||||
<span className="icon shuffle" /> Switch Orgs
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
|
||||
const {bool, func, shape, string} = PropTypes
|
||||
|
||||
SourceForm.propTypes = {
|
||||
source: shape({
|
||||
url: string.isRequired,
|
||||
name: string.isRequired,
|
||||
username: string.isRequired,
|
||||
password: string.isRequired,
|
||||
telegraf: string.isRequired,
|
||||
insecureSkipVerify: bool.isRequired,
|
||||
default: bool.isRequired,
|
||||
metaUrl: string.isRequired,
|
||||
}).isRequired,
|
||||
editMode: bool.isRequired,
|
||||
onInputChange: func.isRequired,
|
||||
onSubmit: func.isRequired,
|
||||
onBlurSourceURL: func.isRequired,
|
||||
me: shape({
|
||||
role: string,
|
||||
currentOrganization: shape({
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
}),
|
||||
}),
|
||||
isUsingAuth: bool,
|
||||
isInitialSource: bool,
|
||||
gotoPurgatory: func,
|
||||
}
|
||||
|
||||
const mapStateToProps = ({auth: {isUsingAuth, me}}) => ({isUsingAuth, me})
|
||||
|
||||
export default connect(mapStateToProps)(SourceForm)
|
|
@ -0,0 +1,224 @@
|
|||
import React, {PureComponent, FocusEvent, MouseEvent, ChangeEvent} from 'react'
|
||||
import classnames from 'classnames'
|
||||
import {connect} from 'react-redux'
|
||||
import _ from 'lodash'
|
||||
|
||||
import {insecureSkipVerifyText} from 'src/shared/copy/tooltipText'
|
||||
|
||||
import {SUPERADMIN_ROLE} from 'src/auth/Authorized'
|
||||
import {Source, Me} from 'src/types'
|
||||
|
||||
interface Props {
|
||||
me: Me
|
||||
source: Partial<Source>
|
||||
editMode: boolean
|
||||
isUsingAuth: boolean
|
||||
gotoPurgatory: () => void
|
||||
isInitialSource: boolean
|
||||
onSubmit: (e: MouseEvent<HTMLFormElement>) => void
|
||||
onInputChange: (e: ChangeEvent<HTMLInputElement>) => void
|
||||
onBlurSourceURL: (e: FocusEvent<HTMLInputElement>) => void
|
||||
}
|
||||
|
||||
export class SourceForm extends PureComponent<Props> {
|
||||
public render() {
|
||||
const {
|
||||
source,
|
||||
onSubmit,
|
||||
isUsingAuth,
|
||||
onInputChange,
|
||||
gotoPurgatory,
|
||||
onBlurSourceURL,
|
||||
isInitialSource,
|
||||
} = this.props
|
||||
return (
|
||||
<div className="panel-body">
|
||||
{isUsingAuth && isInitialSource && this.authIndicatior}
|
||||
<form onSubmit={onSubmit}>
|
||||
<div className="form-group col-xs-12 col-sm-6">
|
||||
<label htmlFor="connect-string">Connection String</label>
|
||||
<input
|
||||
type="text"
|
||||
name="url"
|
||||
className="form-control"
|
||||
id="connect-string"
|
||||
placeholder="Address of InfluxDB"
|
||||
onChange={onInputChange}
|
||||
value={source.url}
|
||||
onBlur={onBlurSourceURL}
|
||||
required={true}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group col-xs-12 col-sm-6">
|
||||
<label htmlFor="name">Name</label>
|
||||
<input
|
||||
type="text"
|
||||
name="name"
|
||||
className="form-control"
|
||||
id="name"
|
||||
placeholder="Name this source"
|
||||
onChange={onInputChange}
|
||||
value={source.name}
|
||||
required={true}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group col-xs-12 col-sm-6">
|
||||
<label htmlFor="username">Username</label>
|
||||
<input
|
||||
type="text"
|
||||
name="username"
|
||||
className="form-control"
|
||||
id="username"
|
||||
onChange={onInputChange}
|
||||
value={source.username}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group col-xs-12 col-sm-6">
|
||||
<label htmlFor="password">Password</label>
|
||||
<input
|
||||
type="password"
|
||||
name="password"
|
||||
className="form-control"
|
||||
id="password"
|
||||
onChange={onInputChange}
|
||||
value={source.password}
|
||||
/>
|
||||
</div>
|
||||
{this.isEnterprise && (
|
||||
<div className="form-group col-xs-12">
|
||||
<label htmlFor="meta-url">Meta Service Connection URL</label>
|
||||
<input
|
||||
type="text"
|
||||
name="metaUrl"
|
||||
className="form-control"
|
||||
id="meta-url"
|
||||
placeholder="http://localhost:8091"
|
||||
onChange={onInputChange}
|
||||
value={source.metaUrl}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className="form-group col-xs-12 col-sm-6">
|
||||
<label htmlFor="telegraf">Telegraf Database</label>
|
||||
<input
|
||||
type="text"
|
||||
name="telegraf"
|
||||
className="form-control"
|
||||
id="telegraf"
|
||||
onChange={onInputChange}
|
||||
value={source.telegraf}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group col-xs-12 col-sm-6">
|
||||
<label htmlFor="defaultRP">Default Retention Policy</label>
|
||||
<input
|
||||
type="text"
|
||||
name="defaultRP"
|
||||
className="form-control"
|
||||
id="defaultRP"
|
||||
onChange={onInputChange}
|
||||
value={source.defaultRP}
|
||||
/>
|
||||
</div>
|
||||
<div className="form-group col-xs-12">
|
||||
<div className="form-control-static">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="defaultConnectionCheckbox"
|
||||
name="default"
|
||||
checked={source.default}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
<label htmlFor="defaultConnectionCheckbox">
|
||||
Make this the default connection
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
{this.isHTTPS && (
|
||||
<div className="form-group col-xs-12">
|
||||
<div className="form-control-static">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="insecureSkipVerifyCheckbox"
|
||||
name="insecureSkipVerify"
|
||||
checked={source.insecureSkipVerify}
|
||||
onChange={onInputChange}
|
||||
/>
|
||||
<label htmlFor="insecureSkipVerifyCheckbox">Unsafe SSL</label>
|
||||
</div>
|
||||
<label className="form-helper">{insecureSkipVerifyText}</label>
|
||||
</div>
|
||||
)}
|
||||
<div className="form-group form-group-submit text-center col-xs-12 col-sm-6 col-sm-offset-3">
|
||||
<button className={this.submitClass} type="submit">
|
||||
<span className={this.submitIconClass} />
|
||||
{this.submitText}
|
||||
</button>
|
||||
|
||||
<br />
|
||||
{isUsingAuth && (
|
||||
<button className="btn btn-link btn-sm" onClick={gotoPurgatory}>
|
||||
<span className="icon shuffle" /> Switch Orgs
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private get authIndicatior(): JSX.Element {
|
||||
const {me} = this.props
|
||||
return (
|
||||
<div className="text-center">
|
||||
{me.role.name === SUPERADMIN_ROLE ? (
|
||||
<h3>
|
||||
<strong>{me.currentOrganization.name}</strong> has no connections
|
||||
</h3>
|
||||
) : (
|
||||
<h3>
|
||||
<strong>{me.currentOrganization.name}</strong> has no connections
|
||||
available to <em>{me.role}s</em>
|
||||
</h3>
|
||||
)}
|
||||
<h6>Add a Connection below:</h6>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private get submitText(): string {
|
||||
const {editMode} = this.props
|
||||
if (editMode) {
|
||||
return 'Save Changes'
|
||||
}
|
||||
|
||||
return 'Add Connection'
|
||||
}
|
||||
|
||||
private get submitIconClass(): string {
|
||||
const {editMode} = this.props
|
||||
return `icon ${editMode ? 'checkmark' : 'plus'}`
|
||||
}
|
||||
|
||||
private get submitClass(): string {
|
||||
const {editMode} = this.props
|
||||
return classnames('btn btn-block', {
|
||||
'btn-primary': editMode,
|
||||
'btn-success': !editMode,
|
||||
})
|
||||
}
|
||||
|
||||
private get isEnterprise(): boolean {
|
||||
const {source} = this.props
|
||||
return _.get(source, 'type', '').includes('enterprise')
|
||||
}
|
||||
|
||||
private get isHTTPS(): boolean {
|
||||
const {source} = this.props
|
||||
return _.get(source, 'url', '').startsWith('https')
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = ({auth: {isUsingAuth, me}}) => ({isUsingAuth, me})
|
||||
|
||||
export default connect(mapStateToProps)(SourceForm)
|
|
@ -1,47 +1,67 @@
|
|||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {withRouter} from 'react-router'
|
||||
import React, {PureComponent, MouseEvent, ChangeEvent} from 'react'
|
||||
import {withRouter, WithRouterProps} from 'react-router'
|
||||
import _ from 'lodash'
|
||||
import {getSource} from 'shared/apis'
|
||||
import {createSource, updateSource} from 'shared/apis'
|
||||
import {getSource} from 'src/shared/apis'
|
||||
import {createSource, updateSource} from 'src/shared/apis'
|
||||
import {
|
||||
addSource as addSourceAction,
|
||||
updateSource as updateSourceAction,
|
||||
} from 'shared/actions/sources'
|
||||
import {notify as notifyAction} from 'shared/actions/notifications'
|
||||
AddSource,
|
||||
UpdateSource,
|
||||
} from 'src/shared/actions/sources'
|
||||
import {
|
||||
notify as notifyAction,
|
||||
PubishNotification,
|
||||
} from 'src/shared/actions/notifications'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import Notifications from 'shared/components/Notifications'
|
||||
import Notifications from 'src/shared/components/Notifications'
|
||||
import SourceForm from 'src/sources/components/SourceForm'
|
||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
import SourceIndicator from 'shared/components/SourceIndicator'
|
||||
import {DEFAULT_SOURCE} from 'shared/constants'
|
||||
const initialPath = '/sources/new'
|
||||
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
|
||||
import SourceIndicator from 'src/shared/components/SourceIndicator'
|
||||
import {DEFAULT_SOURCE} from 'src/shared/constants'
|
||||
import {Source} from 'src/types'
|
||||
|
||||
const INITIAL_PATH = '/sources/new'
|
||||
|
||||
import {
|
||||
notifyErrorConnectingToSource,
|
||||
notifySourceCreationSucceeded,
|
||||
notifySourceCreationFailed,
|
||||
notifySourceUdpated,
|
||||
notifySourceUdpateFailed,
|
||||
} from 'shared/copy/notifications'
|
||||
notifySourceCreationFailed,
|
||||
notifyErrorConnectingToSource,
|
||||
notifySourceCreationSucceeded,
|
||||
} from 'src/shared/copy/notifications'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
interface Props extends WithRouterProps {
|
||||
notify: PubishNotification
|
||||
addSource: AddSource
|
||||
updateSource: UpdateSource
|
||||
}
|
||||
|
||||
interface State {
|
||||
isCreated: boolean
|
||||
isLoading: boolean
|
||||
source: Partial<Source>
|
||||
editMode: boolean
|
||||
isInitialSource: boolean
|
||||
}
|
||||
|
||||
@ErrorHandling
|
||||
class SourcePage extends Component {
|
||||
class SourcePage extends PureComponent<Props, State> {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
isLoading: true,
|
||||
isCreated: false,
|
||||
source: DEFAULT_SOURCE,
|
||||
editMode: props.params.id !== undefined,
|
||||
isInitialSource: props.router.location.pathname === initialPath,
|
||||
isInitialSource: props.router.location.pathname === INITIAL_PATH,
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
public async componentDidMount() {
|
||||
const {editMode} = this.state
|
||||
const {params, notify} = this.props
|
||||
|
||||
|
@ -49,156 +69,19 @@ class SourcePage extends Component {
|
|||
return this.setState({isLoading: false})
|
||||
}
|
||||
|
||||
getSource(params.id)
|
||||
.then(({data: source}) => {
|
||||
this.setState({
|
||||
source: {...DEFAULT_SOURCE, ...source},
|
||||
isLoading: false,
|
||||
})
|
||||
try {
|
||||
const source = await getSource(params.id)
|
||||
this.setState({
|
||||
source: {...DEFAULT_SOURCE, ...source},
|
||||
isLoading: false,
|
||||
})
|
||||
.catch(error => {
|
||||
notify(notifyErrorConnectingToSource(this._parseError(error)))
|
||||
this.setState({isLoading: false})
|
||||
})
|
||||
}
|
||||
|
||||
handleInputChange = e => {
|
||||
let val = e.target.value
|
||||
const name = e.target.name
|
||||
|
||||
if (e.target.type === 'checkbox') {
|
||||
val = e.target.checked
|
||||
} catch (error) {
|
||||
notify(notifyErrorConnectingToSource(this.parseError(error)))
|
||||
this.setState({isLoading: false})
|
||||
}
|
||||
|
||||
this.setState(prevState => {
|
||||
const source = {
|
||||
...prevState.source,
|
||||
[name]: val,
|
||||
}
|
||||
|
||||
return {...prevState, source}
|
||||
})
|
||||
}
|
||||
|
||||
handleBlurSourceURL = () => {
|
||||
const {source, editMode} = this.state
|
||||
if (editMode) {
|
||||
this.setState(this._normalizeSource)
|
||||
return
|
||||
}
|
||||
|
||||
if (!source.url) {
|
||||
return
|
||||
}
|
||||
|
||||
this.setState(this._normalizeSource, this._createSourceOnBlur)
|
||||
}
|
||||
|
||||
handleSubmit = e => {
|
||||
e.preventDefault()
|
||||
const {isCreated, editMode} = this.state
|
||||
const isNewSource = !editMode
|
||||
|
||||
if (!isCreated && isNewSource) {
|
||||
return this.setState(this._normalizeSource, this._createSource)
|
||||
}
|
||||
|
||||
this.setState(this._normalizeSource, this._updateSource)
|
||||
}
|
||||
|
||||
gotoPurgatory = () => {
|
||||
const {router} = this.props
|
||||
router.push('/purgatory')
|
||||
}
|
||||
|
||||
_normalizeSource({source}) {
|
||||
const url = source.url.trim()
|
||||
if (source.url.startsWith('http')) {
|
||||
return {source: {...source, url}}
|
||||
}
|
||||
return {source: {...source, url: `http://${url}`}}
|
||||
}
|
||||
|
||||
_createSourceOnBlur = () => {
|
||||
const {source} = this.state
|
||||
// if there is a type on source it has already been created
|
||||
if (source.type) {
|
||||
return
|
||||
}
|
||||
createSource(source)
|
||||
.then(({data: sourceFromServer}) => {
|
||||
this.props.addSource(sourceFromServer)
|
||||
this.setState({
|
||||
source: {...DEFAULT_SOURCE, ...sourceFromServer},
|
||||
isCreated: true,
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
// dont want to flash this until they submit
|
||||
const error = this._parseError(err)
|
||||
console.error('Error creating InfluxDB connection: ', error)
|
||||
})
|
||||
}
|
||||
|
||||
_createSource = () => {
|
||||
const {source} = this.state
|
||||
const {notify} = this.props
|
||||
createSource(source)
|
||||
.then(({data: sourceFromServer}) => {
|
||||
this.props.addSource(sourceFromServer)
|
||||
this._redirect(sourceFromServer)
|
||||
notify(notifySourceCreationSucceeded(source.name))
|
||||
})
|
||||
.catch(error => {
|
||||
notify(notifySourceCreationFailed(source.name, this._parseError(error)))
|
||||
})
|
||||
}
|
||||
|
||||
_updateSource = () => {
|
||||
const {source} = this.state
|
||||
const {notify} = this.props
|
||||
updateSource(source)
|
||||
.then(({data: sourceFromServer}) => {
|
||||
this.props.updateSource(sourceFromServer)
|
||||
this._redirect(sourceFromServer)
|
||||
notify(notifySourceUdpated(source.name))
|
||||
})
|
||||
.catch(error => {
|
||||
notify(notifySourceUdpateFailed(source.name, this._parseError(error)))
|
||||
})
|
||||
}
|
||||
|
||||
_redirect = source => {
|
||||
const {isInitialSource} = this.state
|
||||
const {params, router} = this.props
|
||||
|
||||
if (isInitialSource) {
|
||||
return this._redirectToApp(source)
|
||||
}
|
||||
|
||||
router.push(`/sources/${params.sourceID}/manage-sources`)
|
||||
}
|
||||
|
||||
_redirectToApp = source => {
|
||||
const {location, router} = this.props
|
||||
const {redirectPath} = location.query
|
||||
|
||||
if (!redirectPath) {
|
||||
return router.push(`/sources/${source.id}/hosts`)
|
||||
}
|
||||
|
||||
const fixedPath = redirectPath.replace(
|
||||
/\/sources\/[^/]*/,
|
||||
`/sources/${source.id}`
|
||||
)
|
||||
return router.push(fixedPath)
|
||||
}
|
||||
|
||||
_parseError = error => {
|
||||
return _.get(error, ['data', 'message'], error)
|
||||
}
|
||||
|
||||
render() {
|
||||
public render() {
|
||||
const {isLoading, source, editMode, isInitialSource} = this.state
|
||||
|
||||
if (isLoading) {
|
||||
|
@ -248,31 +131,147 @@ class SourcePage extends Component {
|
|||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private handleSubmit = (e: MouseEvent<HTMLFormElement>): void => {
|
||||
e.preventDefault()
|
||||
const {isCreated, editMode} = this.state
|
||||
const isNewSource = !editMode
|
||||
|
||||
if (!isCreated && isNewSource) {
|
||||
return this.setState(this.normalizeSource, this.createSource)
|
||||
}
|
||||
|
||||
this.setState(this.normalizeSource, this.updateSource)
|
||||
}
|
||||
|
||||
private gotoPurgatory = (): void => {
|
||||
const {router} = this.props
|
||||
router.push('/purgatory')
|
||||
}
|
||||
|
||||
private normalizeSource({source}) {
|
||||
const url = source.url.trim()
|
||||
if (source.url.startsWith('http')) {
|
||||
return {source: {...source, url}}
|
||||
}
|
||||
return {source: {...source, url: `http://${url}`}}
|
||||
}
|
||||
|
||||
private createSourceOnBlur = async () => {
|
||||
const {source} = this.state
|
||||
// if there is a type on source it has already been created
|
||||
if (source.type) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const sourceFromServer = await createSource(source)
|
||||
this.props.addSource(sourceFromServer)
|
||||
this.setState({
|
||||
source: {...DEFAULT_SOURCE, ...sourceFromServer},
|
||||
isCreated: true,
|
||||
})
|
||||
} catch (err) {
|
||||
// dont want to flash this until they submit
|
||||
const error = this.parseError(err)
|
||||
console.error('Error creating InfluxDB connection: ', error)
|
||||
}
|
||||
}
|
||||
|
||||
private createSource = async () => {
|
||||
const {source} = this.state
|
||||
const {notify} = this.props
|
||||
try {
|
||||
const sourceFromServer = await createSource(source)
|
||||
this.props.addSource(sourceFromServer)
|
||||
this.redirect(sourceFromServer)
|
||||
notify(notifySourceCreationSucceeded(source.name))
|
||||
} catch (err) {
|
||||
// dont want to flash this until they submit
|
||||
notify(notifySourceCreationFailed(source.name, this.parseError(err)))
|
||||
}
|
||||
}
|
||||
|
||||
private updateSource = async () => {
|
||||
const {source} = this.state
|
||||
const {notify} = this.props
|
||||
try {
|
||||
const sourceFromServer = await updateSource(source)
|
||||
this.props.updateSource(sourceFromServer)
|
||||
this.redirect(sourceFromServer)
|
||||
notify(notifySourceUdpated(source.name))
|
||||
} catch (error) {
|
||||
notify(notifySourceUdpateFailed(source.name, this.parseError(error)))
|
||||
}
|
||||
}
|
||||
|
||||
private redirect = source => {
|
||||
const {isInitialSource} = this.state
|
||||
const {params, router} = this.props
|
||||
|
||||
if (isInitialSource) {
|
||||
return this.redirectToApp(source)
|
||||
}
|
||||
|
||||
router.push(`/sources/${params.sourceID}/manage-sources`)
|
||||
}
|
||||
|
||||
private parseError = (error): string => {
|
||||
return _.get(error, ['data', 'message'], error)
|
||||
}
|
||||
|
||||
private redirectToApp = source => {
|
||||
const {location, router} = this.props
|
||||
const {redirectPath} = location.query
|
||||
|
||||
if (!redirectPath) {
|
||||
return router.push(`/sources/${source.id}/hosts`)
|
||||
}
|
||||
|
||||
const fixedPath = redirectPath.replace(
|
||||
/\/sources\/[^/]*/,
|
||||
`/sources/${source.id}`
|
||||
)
|
||||
return router.push(fixedPath)
|
||||
}
|
||||
|
||||
private handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
let val = e.target.value
|
||||
const name = e.target.name
|
||||
|
||||
if (e.target.type === 'checkbox') {
|
||||
val = e.target.checked as any
|
||||
}
|
||||
|
||||
this.setState(prevState => {
|
||||
const source = {
|
||||
...prevState.source,
|
||||
[name]: val,
|
||||
}
|
||||
|
||||
return {...prevState, source}
|
||||
})
|
||||
}
|
||||
|
||||
private handleBlurSourceURL = () => {
|
||||
const {source, editMode} = this.state
|
||||
if (editMode) {
|
||||
this.setState(this.normalizeSource)
|
||||
return
|
||||
}
|
||||
|
||||
if (!source.url) {
|
||||
return
|
||||
}
|
||||
|
||||
this.setState(this.normalizeSource, this.createSourceOnBlur)
|
||||
}
|
||||
}
|
||||
|
||||
const {func, shape, string} = PropTypes
|
||||
|
||||
SourcePage.propTypes = {
|
||||
params: shape({
|
||||
id: string,
|
||||
sourceID: string,
|
||||
}),
|
||||
router: shape({
|
||||
push: func.isRequired,
|
||||
}).isRequired,
|
||||
location: shape({
|
||||
query: shape({
|
||||
redirectPath: string,
|
||||
}).isRequired,
|
||||
}).isRequired,
|
||||
notify: func.isRequired,
|
||||
addSource: func.isRequired,
|
||||
updateSource: func.isRequired,
|
||||
const mdtp = {
|
||||
notify: notifyAction,
|
||||
addSource: addSourceAction,
|
||||
updateSource: updateSourceAction,
|
||||
}
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
notify: bindActionCreators(notifyAction, dispatch),
|
||||
addSource: bindActionCreators(addSourceAction, dispatch),
|
||||
updateSource: bindActionCreators(updateSourceAction, dispatch),
|
||||
})
|
||||
export default connect(null, mapDispatchToProps)(withRouter(SourcePage))
|
||||
export default connect(null, mdtp)(withRouter(SourcePage))
|
|
@ -25,7 +25,7 @@ import {
|
|||
TagValues,
|
||||
} from './query'
|
||||
import {AlertRule, Kapacitor, Task, RuleValues} from './kapacitor'
|
||||
import {Source, SourceLinks} from './sources'
|
||||
import {NewSource, Source, SourceLinks} from './sources'
|
||||
import {DropdownAction, DropdownItem, Constructable} from './shared'
|
||||
import {
|
||||
Notification,
|
||||
|
@ -70,6 +70,7 @@ export {
|
|||
TagValues,
|
||||
AlertRule,
|
||||
Kapacitor,
|
||||
NewSource,
|
||||
Source,
|
||||
SourceLinks,
|
||||
DropdownAction,
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import {Kapacitor, Service} from './'
|
||||
|
||||
export type NewSource = Pick<Source, Exclude<keyof Source, 'id'>>
|
||||
|
||||
export interface Source {
|
||||
id: string
|
||||
name: string
|
||||
|
|
|
@ -2,6 +2,7 @@ import React from 'react'
|
|||
import {shallow} from 'enzyme'
|
||||
|
||||
import {SourceForm} from 'src/sources/components/SourceForm'
|
||||
import {me} from 'test/resources'
|
||||
|
||||
const setup = (override = {}) => {
|
||||
const noop = () => {}
|
||||
|
@ -23,7 +24,7 @@ const setup = (override = {}) => {
|
|||
isUsingAuth: false,
|
||||
gotoPurgatory: noop,
|
||||
isInitialSource: false,
|
||||
me: {},
|
||||
me,
|
||||
...override,
|
||||
}
|
||||
|
Loading…
Reference in New Issue