Merge pull request #5746 from influxdata/5745/write_data_v2
feat(ui/explorer): use v2 write API with buckets when Flux tab is selectedpull/5748/head
commit
ceef251ab6
|
@ -20,6 +20,7 @@
|
||||||
1. [#5726](https://github.com/influxdata/chronograf/pull/5726): Allow to setup InfluxDB v2 connection from chronograf command-line.
|
1. [#5726](https://github.com/influxdata/chronograf/pull/5726): Allow to setup InfluxDB v2 connection from chronograf command-line.
|
||||||
1. [#5735](https://github.com/influxdata/chronograf/pull/5735): Allow to add custom auto-refresh intervals.
|
1. [#5735](https://github.com/influxdata/chronograf/pull/5735): Allow to add custom auto-refresh intervals.
|
||||||
1. [#5737](https://github.com/influxdata/chronograf/pull/5737): Allow to send multiple queries to dashboard.
|
1. [#5737](https://github.com/influxdata/chronograf/pull/5737): Allow to send multiple queries to dashboard.
|
||||||
|
1. [#5746](https://github.com/influxdata/chronograf/pull/5746): Write to buckets when Flux tab is selected.
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
|
|
||||||
|
|
|
@ -120,8 +120,19 @@ func (s *Service) Write(w http.ResponseWriter, r *http.Request) {
|
||||||
Error(w, http.StatusUnprocessableEntity, msg, s.Logger)
|
Error(w, http.StatusUnprocessableEntity, msg, s.Logger)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
u.Path = "/write"
|
query := r.URL.Query()
|
||||||
u.RawQuery = r.URL.RawQuery
|
version := query.Get("v")
|
||||||
|
query.Del("v")
|
||||||
|
if strings.HasPrefix(version, "2") {
|
||||||
|
u.Path = "/api/v2/write"
|
||||||
|
// v2 organization name is stored in username (org does not matter against v1)
|
||||||
|
query.Set("org", src.Username)
|
||||||
|
query.Set("bucket", query.Get("db"))
|
||||||
|
query.Del("db")
|
||||||
|
} else {
|
||||||
|
u.Path = "/write"
|
||||||
|
}
|
||||||
|
u.RawQuery = query.Encode()
|
||||||
|
|
||||||
director := func(req *http.Request) {
|
director := func(req *http.Request) {
|
||||||
// Set the Host header of the original source URL
|
// Set the Host header of the original source URL
|
||||||
|
|
|
@ -495,15 +495,15 @@ func paramID(key string, r *http.Request) (int, error) {
|
||||||
return id, nil
|
return id, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func paramInt64(key string, r *http.Request) (int64, error) {
|
// func paramInt64(key string, r *http.Request) (int64, error) {
|
||||||
ctx := r.Context()
|
// ctx := r.Context()
|
||||||
param := httprouter.GetParamFromContext(ctx, key)
|
// param := httprouter.GetParamFromContext(ctx, key)
|
||||||
v, err := strconv.ParseInt(param, 10, 64)
|
// v, err := strconv.ParseInt(param, 10, 64)
|
||||||
if err != nil {
|
// if err != nil {
|
||||||
return -1, fmt.Errorf("Error converting parameter %s", param)
|
// return -1, fmt.Errorf("Error converting parameter %s", param)
|
||||||
}
|
// }
|
||||||
return v, nil
|
// return v, nil
|
||||||
}
|
// }
|
||||||
|
|
||||||
func paramStr(key string, r *http.Request) (string, error) {
|
func paramStr(key string, r *http.Request) (string, error) {
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
|
|
|
@ -12,13 +12,18 @@ export const writeLineProtocolAsync = (
|
||||||
source: Source,
|
source: Source,
|
||||||
db: string,
|
db: string,
|
||||||
data: string,
|
data: string,
|
||||||
precision?: string
|
precision?: string,
|
||||||
|
v2?: boolean
|
||||||
) => async (dispatch): Promise<void> => {
|
) => async (dispatch): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
await writeLineProtocolAJAX(source, db, data, precision)
|
await writeLineProtocolAJAX(source, db, data, precision, v2)
|
||||||
dispatch(notify(notifyDataWritten()))
|
dispatch(notify(notifyDataWritten()))
|
||||||
} catch (response) {
|
} catch (response) {
|
||||||
dispatch(notify(notifyDataWriteFailed(response.data.error)))
|
dispatch(
|
||||||
|
notify(
|
||||||
|
notifyDataWriteFailed(response.data?.error || response.data?.message)
|
||||||
|
)
|
||||||
|
)
|
||||||
throw response
|
throw response
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,11 @@ export const writeLineProtocol = async (
|
||||||
source: Source,
|
source: Source,
|
||||||
db: string,
|
db: string,
|
||||||
data: string,
|
data: string,
|
||||||
precision?: string
|
precision?: string,
|
||||||
|
v2?: boolean
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
const url = `${source.links.write}?db=${db}&precision=${
|
const url = `${source.links.write}?db=${db}&precision=${
|
||||||
precision ? precision : 'ns'
|
precision ? precision : 'ns'
|
||||||
}`
|
}${v2 ? '&v=2' : ''}`
|
||||||
await AJAX({url, method: 'POST', data})
|
await AJAX({url, method: 'POST', data})
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,11 +22,13 @@ interface Props {
|
||||||
selectedDatabase: string
|
selectedDatabase: string
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
errorThrown: () => void
|
errorThrown: () => void
|
||||||
|
useV2: boolean
|
||||||
writeLineProtocol: (
|
writeLineProtocol: (
|
||||||
source: Source,
|
source: Source,
|
||||||
database: string,
|
database: string,
|
||||||
content: string,
|
content: string,
|
||||||
precision?: string
|
precision?: string,
|
||||||
|
useV2?: boolean
|
||||||
) => void
|
) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -62,7 +64,7 @@ class WriteDataForm extends PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const {onClose, errorThrown, source} = this.props
|
const {onClose, errorThrown, source, useV2} = this.props
|
||||||
const {dragClass} = this.state
|
const {dragClass} = this.state
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -81,6 +83,7 @@ class WriteDataForm extends PureComponent<Props, State> {
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
errorThrown={errorThrown}
|
errorThrown={errorThrown}
|
||||||
onToggleMode={this.handleToggleMode}
|
onToggleMode={this.handleToggleMode}
|
||||||
|
useBuckets={useV2}
|
||||||
handlePrecisionChange={this.handlePrecisionChange}
|
handlePrecisionChange={this.handlePrecisionChange}
|
||||||
handleSelectDatabase={this.handleSelectDatabase}
|
handleSelectDatabase={this.handleSelectDatabase}
|
||||||
/>
|
/>
|
||||||
|
@ -119,7 +122,7 @@ class WriteDataForm extends PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleSubmit = async () => {
|
private handleSubmit = async () => {
|
||||||
const {onClose, source, writeLineProtocol} = this.props
|
const {onClose, source, writeLineProtocol, useV2} = this.props
|
||||||
const {
|
const {
|
||||||
inputContent,
|
inputContent,
|
||||||
uploadContent,
|
uploadContent,
|
||||||
|
@ -135,13 +138,19 @@ class WriteDataForm extends PureComponent<Props, State> {
|
||||||
this.setState({isUploading: true})
|
this.setState({isUploading: true})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await writeLineProtocol(source, selectedDatabase, content, precision)
|
await writeLineProtocol(
|
||||||
|
source,
|
||||||
|
selectedDatabase,
|
||||||
|
content,
|
||||||
|
precision,
|
||||||
|
useV2
|
||||||
|
)
|
||||||
this.setState({isUploading: false})
|
this.setState({isUploading: false})
|
||||||
onClose()
|
onClose()
|
||||||
window.location.reload()
|
window.location.reload()
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
this.setState({isUploading: false})
|
this.setState({isUploading: false})
|
||||||
console.error(error.data.error)
|
console.error(error.data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ interface Props {
|
||||||
onClose: () => void
|
onClose: () => void
|
||||||
mode: string
|
mode: string
|
||||||
source: Source
|
source: Source
|
||||||
|
useBuckets: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
class WriteDataHeader extends PureComponent<Props> {
|
class WriteDataHeader extends PureComponent<Props> {
|
||||||
|
@ -26,6 +27,7 @@ class WriteDataHeader extends PureComponent<Props> {
|
||||||
onClose,
|
onClose,
|
||||||
source,
|
source,
|
||||||
precision,
|
precision,
|
||||||
|
useBuckets,
|
||||||
} = this.props
|
} = this.props
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -36,6 +38,7 @@ class WriteDataHeader extends PureComponent<Props> {
|
||||||
source={source}
|
source={source}
|
||||||
onSelectDatabase={handleSelectDatabase}
|
onSelectDatabase={handleSelectDatabase}
|
||||||
database={selectedDatabase}
|
database={selectedDatabase}
|
||||||
|
useBuckets={useBuckets}
|
||||||
onErrorThrown={errorThrown}
|
onErrorThrown={errorThrown}
|
||||||
/>
|
/>
|
||||||
{this.modeSelector}
|
{this.modeSelector}
|
||||||
|
|
|
@ -297,7 +297,7 @@ export class DataExplorer extends PureComponent<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private get writeDataForm(): JSX.Element {
|
private get writeDataForm(): JSX.Element {
|
||||||
const {source, errorThrownAction, writeLineProtocol} = this.props
|
const {source, errorThrownAction, writeLineProtocol, queryType} = this.props
|
||||||
|
|
||||||
const {isWriteFormVisible} = this.state
|
const {isWriteFormVisible} = this.state
|
||||||
return (
|
return (
|
||||||
|
@ -308,6 +308,7 @@ export class DataExplorer extends PureComponent<Props, State> {
|
||||||
selectedDatabase={this.selectedDatabase}
|
selectedDatabase={this.selectedDatabase}
|
||||||
onClose={this.handleCloseWriteData}
|
onClose={this.handleCloseWriteData}
|
||||||
writeLineProtocol={writeLineProtocol}
|
writeLineProtocol={writeLineProtocol}
|
||||||
|
useV2={queryType === 'flux'}
|
||||||
/>
|
/>
|
||||||
</OverlayTechnology>
|
</OverlayTechnology>
|
||||||
)
|
)
|
||||||
|
|
|
@ -5,6 +5,7 @@ import {showDatabases} from 'src/shared/apis/metaQuery'
|
||||||
import parsers from 'src/shared/parsing'
|
import parsers from 'src/shared/parsing'
|
||||||
import {Source} from 'src/types/sources'
|
import {Source} from 'src/types/sources'
|
||||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||||
|
import {getBuckets} from 'src/flux/components/DatabaseList'
|
||||||
const {databases: showDatabasesParser} = parsers
|
const {databases: showDatabasesParser} = parsers
|
||||||
|
|
||||||
interface Database {
|
interface Database {
|
||||||
|
@ -17,10 +18,12 @@ interface Props {
|
||||||
onStartEdit?: () => void
|
onStartEdit?: () => void
|
||||||
onErrorThrown: (error: string) => void
|
onErrorThrown: (error: string) => void
|
||||||
source: Source
|
source: Source
|
||||||
|
// true loads buckets in place of databases
|
||||||
|
useBuckets?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
databases: Database[]
|
databases: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
@ErrorHandling
|
@ErrorHandling
|
||||||
|
@ -58,30 +61,60 @@ class DatabaseDropdown extends Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private getDatabasesAsync = async (): Promise<void> => {
|
private getDatabasesAsync = async (): Promise<void> => {
|
||||||
const {source, database, onSelectDatabase, onErrorThrown} = this.props
|
const {
|
||||||
const proxy = source.links.proxy
|
source,
|
||||||
try {
|
database,
|
||||||
const {data} = await showDatabases(proxy)
|
onSelectDatabase,
|
||||||
const {databases, errors} = showDatabasesParser(data)
|
onErrorThrown,
|
||||||
if (errors.length > 0) {
|
useBuckets,
|
||||||
throw errors[0] // only one error can come back from this, but it's returned as an array
|
} = this.props
|
||||||
|
let databases: string[]
|
||||||
|
if (useBuckets) {
|
||||||
|
try {
|
||||||
|
databases = await getBuckets(source)
|
||||||
|
databases.sort((a, b) => {
|
||||||
|
// databases starting with '_' are the last
|
||||||
|
if (b.startsWith('_')) {
|
||||||
|
if (a.startsWith('_')) {
|
||||||
|
return a.localeCompare(b)
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
return a.localeCompare(b)
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
|
onErrorThrown(e)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
const proxy = source.links.proxy
|
||||||
|
const {data} = await showDatabases(proxy)
|
||||||
|
const parserResult = showDatabasesParser(data)
|
||||||
|
if (parserResult.errors.length > 0) {
|
||||||
|
throw parserResult.errors[0] // only one error can come back from this, but it's returned as an array
|
||||||
|
}
|
||||||
|
databases = parserResult.databases
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
onErrorThrown(error)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const nonSystemDatabases = databases.filter(name => name !== '_internal')
|
|
||||||
|
|
||||||
this.setState({
|
|
||||||
databases: nonSystemDatabases,
|
|
||||||
})
|
|
||||||
const selectedDatabaseText = nonSystemDatabases.includes(database)
|
|
||||||
? database
|
|
||||||
: nonSystemDatabases[0] || 'No databases'
|
|
||||||
onSelectDatabase({
|
|
||||||
text: selectedDatabaseText,
|
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
onErrorThrown(error)
|
|
||||||
}
|
}
|
||||||
|
const nonSystemDatabases = databases.filter(
|
||||||
|
name => !name.startsWith('_internal')
|
||||||
|
)
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
databases: nonSystemDatabases,
|
||||||
|
})
|
||||||
|
const selectedDatabaseText = nonSystemDatabases.includes(database)
|
||||||
|
? database
|
||||||
|
: nonSystemDatabases[0] || 'No databases'
|
||||||
|
onSelectDatabase({
|
||||||
|
text: selectedDatabaseText,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue