123 lines
3.4 KiB
Go
123 lines
3.4 KiB
Go
package query
|
|
|
|
import (
|
|
"io"
|
|
"net/http"
|
|
|
|
"github.com/influxdata/flux"
|
|
"github.com/influxdata/flux/csv"
|
|
)
|
|
|
|
const (
|
|
NoContentDialectType = "no-content"
|
|
NoContentWErrDialectType = "no-content-with-error"
|
|
)
|
|
|
|
// NoContentDialect is a dialect that provides an Encoder that discards query results.
|
|
// When invoking `dialect.Encoder().Encode(writer, results)`, `results` get consumed,
|
|
// while the `writer` is left intact.
|
|
// It is an HTTPDialect that sets the response status code to 204 NoContent.
|
|
type NoContentDialect struct{}
|
|
|
|
func NewNoContentDialect() *NoContentDialect {
|
|
return &NoContentDialect{}
|
|
}
|
|
|
|
func (d *NoContentDialect) Encoder() flux.MultiResultEncoder {
|
|
return &NoContentEncoder{}
|
|
}
|
|
|
|
func (d *NoContentDialect) DialectType() flux.DialectType {
|
|
return NoContentDialectType
|
|
}
|
|
|
|
func (d *NoContentDialect) SetHeaders(w http.ResponseWriter) {
|
|
w.WriteHeader(http.StatusNoContent)
|
|
}
|
|
|
|
type NoContentEncoder struct{}
|
|
|
|
func (e *NoContentEncoder) Encode(w io.Writer, results flux.ResultIterator) (int64, error) {
|
|
defer results.Release()
|
|
// Consume and discard results.
|
|
for results.More() {
|
|
if err := results.Next().Tables().Do(func(tbl flux.Table) error {
|
|
return tbl.Do(func(cr flux.ColReader) error {
|
|
return nil
|
|
})
|
|
}); err != nil {
|
|
return 0, err
|
|
}
|
|
}
|
|
// Do not write anything.
|
|
return 0, nil
|
|
}
|
|
|
|
// NoContentWithErrorDialect is a dialect that provides an Encoder that discards query results,
|
|
// but it encodes runtime errors from the Flux query in CSV format.
|
|
// To discover if there was any runtime error in the query, one should check the response size.
|
|
// If it is equal to zero, then no error was present.
|
|
// Otherwise one can decode the response body to get the error. For example:
|
|
// ```
|
|
// _, err = csv.NewResultDecoder(csv.ResultDecoderConfig{}).Decode(bytes.NewReader(res))
|
|
//
|
|
// if err != nil {
|
|
// // we got some runtime error
|
|
// }
|
|
//
|
|
// ```
|
|
type NoContentWithErrorDialect struct {
|
|
csv.ResultEncoderConfig
|
|
}
|
|
|
|
func NewNoContentWithErrorDialect() *NoContentWithErrorDialect {
|
|
return &NoContentWithErrorDialect{
|
|
ResultEncoderConfig: csv.DefaultEncoderConfig(),
|
|
}
|
|
}
|
|
|
|
func (d *NoContentWithErrorDialect) Encoder() flux.MultiResultEncoder {
|
|
return &NoContentWithErrorEncoder{
|
|
errorEncoder: csv.NewResultEncoder(d.ResultEncoderConfig),
|
|
}
|
|
}
|
|
|
|
func (d *NoContentWithErrorDialect) DialectType() flux.DialectType {
|
|
return NoContentWErrDialectType
|
|
}
|
|
|
|
func (d *NoContentWithErrorDialect) SetHeaders(w http.ResponseWriter) {
|
|
w.Header().Set("Content-Type", "text/csv; charset=utf-8")
|
|
w.Header().Set("Transfer-Encoding", "chunked")
|
|
}
|
|
|
|
type NoContentWithErrorEncoder struct {
|
|
errorEncoder *csv.ResultEncoder
|
|
}
|
|
|
|
func (e *NoContentWithErrorEncoder) Encode(w io.Writer, results flux.ResultIterator) (int64, error) {
|
|
// Make sure we release results.
|
|
// Remember, it is safe to call `Release` multiple times.
|
|
defer results.Release()
|
|
// Consume and discard results, but keep an eye on errors.
|
|
for results.More() {
|
|
if err := results.Next().Tables().Do(func(tbl flux.Table) error {
|
|
return tbl.Do(func(cr flux.ColReader) error {
|
|
return nil
|
|
})
|
|
}); err != nil {
|
|
// If there is an error, then encode it in the response.
|
|
if encErr := e.errorEncoder.EncodeError(w, err); encErr != nil {
|
|
return 0, encErr
|
|
}
|
|
}
|
|
}
|
|
// Now Release in order to populate the error, if present.
|
|
results.Release()
|
|
err := results.Err()
|
|
if err != nil {
|
|
return 0, e.errorEncoder.EncodeError(w, err)
|
|
}
|
|
return 0, nil
|
|
}
|