Merge branch 'master' into logs-viewer/filter-bar
commit
1791e4ec53
|
@ -3,6 +3,7 @@
|
|||
### Features
|
||||
|
||||
1. [#3522](https://github.com/influxdata/chronograf/pull/3522): Add support for Template Variables in Cell Titles
|
||||
1. [#3559](https://github.com/influxdata/chronograf/pull/3559): Add ability to export and import dashboards
|
||||
|
||||
### UI Improvements
|
||||
|
||||
|
@ -11,6 +12,7 @@
|
|||
### Bug Fixes
|
||||
|
||||
1. [#3527](https://github.com/influxdata/chronograf/pull/3527): Ensure cell queries use constraints from TimeSelector
|
||||
1. [#3573](https://github.com/influxdata/chronograf/pull/3573): Fix Gauge color selection bug
|
||||
|
||||
## v1.5.0.0 [2018-05-15-RC]
|
||||
|
||||
|
|
|
@ -121,7 +121,7 @@ message Server {
|
|||
bool Active = 7; // is this the currently active server for the source
|
||||
string Organization = 8; // Organization is the organization ID that resource belongs to
|
||||
bool InsecureSkipVerify = 9; // InsecureSkipVerify accepts any certificate from the client
|
||||
string Type = 10; // Type is the kind of the server (e.g. ifql)
|
||||
string Type = 10; // Type is the kind of the server (e.g. flux)
|
||||
string MetadataJSON = 11; // JSON byte representation of the metadata
|
||||
}
|
||||
|
||||
|
|
|
@ -368,7 +368,7 @@ type Server struct {
|
|||
InsecureSkipVerify bool `json:"insecureSkipVerify"` // InsecureSkipVerify as true means any certificate presented by the server is accepted.
|
||||
Active bool `json:"active"` // Is this the active server for the source?
|
||||
Organization string `json:"organization"` // Organization is the organization ID that resource belongs to
|
||||
Type string `json:"type"` // Type is the kind of service (e.g. kapacitor or ifql)
|
||||
Type string `json:"type"` // Type is the kind of service (e.g. kapacitor or flux)
|
||||
Metadata map[string]interface{} `json:"metadata"` // Metadata is any other data that the frontend wants to store about this service
|
||||
}
|
||||
|
||||
|
|
|
@ -2730,10 +2730,10 @@ func TestServer(t *testing.T) {
|
|||
"external": {
|
||||
"statusFeed": ""
|
||||
},
|
||||
"ifql": {
|
||||
"ast": "/chronograf/v1/ifql/ast",
|
||||
"self": "/chronograf/v1/ifql",
|
||||
"suggestions": "/chronograf/v1/ifql/suggestions"
|
||||
"flux": {
|
||||
"ast": "/chronograf/v1/flux/ast",
|
||||
"self": "/chronograf/v1/flux",
|
||||
"suggestions": "/chronograf/v1/flux/suggestions"
|
||||
}
|
||||
}
|
||||
`,
|
||||
|
@ -2818,10 +2818,10 @@ func TestServer(t *testing.T) {
|
|||
"external": {
|
||||
"statusFeed": ""
|
||||
},
|
||||
"ifql": {
|
||||
"ast": "/chronograf/v1/ifql/ast",
|
||||
"self": "/chronograf/v1/ifql",
|
||||
"suggestions": "/chronograf/v1/ifql/suggestions"
|
||||
"flux": {
|
||||
"ast": "/chronograf/v1/flux/ast",
|
||||
"self": "/chronograf/v1/flux",
|
||||
"suggestions": "/chronograf/v1/flux/suggestions"
|
||||
}
|
||||
}
|
||||
`,
|
||||
|
|
|
@ -12,41 +12,41 @@ import (
|
|||
|
||||
type Params map[string]string
|
||||
|
||||
// SuggestionsResponse provides a list of available IFQL functions
|
||||
// SuggestionsResponse provides a list of available Flux functions
|
||||
type SuggestionsResponse struct {
|
||||
Functions []SuggestionResponse `json:"funcs"`
|
||||
}
|
||||
|
||||
// SuggestionResponse provides the parameters available for a given IFQL function
|
||||
// SuggestionResponse provides the parameters available for a given Flux function
|
||||
type SuggestionResponse struct {
|
||||
Name string `json:"name"`
|
||||
Params Params `json:"params"`
|
||||
}
|
||||
|
||||
type ifqlLinks struct {
|
||||
type fluxLinks struct {
|
||||
Self string `json:"self"` // Self link mapping to this resource
|
||||
Suggestions string `json:"suggestions"` // URL for ifql builder function suggestions
|
||||
Suggestions string `json:"suggestions"` // URL for flux builder function suggestions
|
||||
}
|
||||
|
||||
type ifqlResponse struct {
|
||||
Links ifqlLinks `json:"links"`
|
||||
type fluxResponse struct {
|
||||
Links fluxLinks `json:"links"`
|
||||
}
|
||||
|
||||
// IFQL returns a list of links for the IFQL API
|
||||
func (s *Service) IFQL(w http.ResponseWriter, r *http.Request) {
|
||||
httpAPIIFQL := "/chronograf/v1/ifql"
|
||||
res := ifqlResponse{
|
||||
Links: ifqlLinks{
|
||||
Self: fmt.Sprintf("%s", httpAPIIFQL),
|
||||
Suggestions: fmt.Sprintf("%s/suggestions", httpAPIIFQL),
|
||||
// Flux returns a list of links for the Flux API
|
||||
func (s *Service) Flux(w http.ResponseWriter, r *http.Request) {
|
||||
httpAPIFlux := "/chronograf/v1/flux"
|
||||
res := fluxResponse{
|
||||
Links: fluxLinks{
|
||||
Self: fmt.Sprintf("%s", httpAPIFlux),
|
||||
Suggestions: fmt.Sprintf("%s/suggestions", httpAPIFlux),
|
||||
},
|
||||
}
|
||||
|
||||
encodeJSON(w, http.StatusOK, res, s.Logger)
|
||||
}
|
||||
|
||||
// IFQLSuggestions returns a list of available IFQL functions for the IFQL Builder
|
||||
func (s *Service) IFQLSuggestions(w http.ResponseWriter, r *http.Request) {
|
||||
// FluxSuggestions returns a list of available Flux functions for the Flux Builder
|
||||
func (s *Service) FluxSuggestions(w http.ResponseWriter, r *http.Request) {
|
||||
completer := query.DefaultCompleter()
|
||||
names := completer.FunctionNames()
|
||||
var functions []SuggestionResponse
|
||||
|
@ -76,8 +76,8 @@ func (s *Service) IFQLSuggestions(w http.ResponseWriter, r *http.Request) {
|
|||
encodeJSON(w, http.StatusOK, res, s.Logger)
|
||||
}
|
||||
|
||||
// IFQLSuggestion returns the function parameters for the requested function
|
||||
func (s *Service) IFQLSuggestion(w http.ResponseWriter, r *http.Request) {
|
||||
// FluxSuggestion returns the function parameters for the requested function
|
||||
func (s *Service) FluxSuggestion(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
name := httprouter.GetParamFromContext(ctx, "name")
|
||||
completer := query.DefaultCompleter()
|
||||
|
@ -94,7 +94,7 @@ type ASTRequest struct {
|
|||
Body string `json:"body"`
|
||||
}
|
||||
|
||||
func (s *Service) IFQLAST(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *Service) FluxAST(w http.ResponseWriter, r *http.Request) {
|
||||
var request ASTRequest
|
||||
err := json.NewDecoder(r.Body).Decode(&request)
|
||||
if err != nil {
|
|
@ -5,7 +5,7 @@ import (
|
|||
"net/url"
|
||||
)
|
||||
|
||||
type getIFQLLinksResponse struct {
|
||||
type getFluxLinksResponse struct {
|
||||
AST string `json:"ast"`
|
||||
Self string `json:"self"`
|
||||
Suggestions string `json:"suggestions"`
|
||||
|
|
|
@ -156,11 +156,11 @@ func NewMux(opts MuxOpts, service Service) http.Handler {
|
|||
router.DELETE("/chronograf/v1/sources/:id", EnsureEditor(service.RemoveSource))
|
||||
router.GET("/chronograf/v1/sources/:id/health", EnsureViewer(service.SourceHealth))
|
||||
|
||||
// IFQL
|
||||
router.GET("/chronograf/v1/ifql", EnsureViewer(service.IFQL))
|
||||
router.POST("/chronograf/v1/ifql/ast", EnsureViewer(service.IFQLAST))
|
||||
router.GET("/chronograf/v1/ifql/suggestions", EnsureViewer(service.IFQLSuggestions))
|
||||
router.GET("/chronograf/v1/ifql/suggestions/:name", EnsureViewer(service.IFQLSuggestion))
|
||||
// Flux
|
||||
router.GET("/chronograf/v1/flux", EnsureViewer(service.Flux))
|
||||
router.POST("/chronograf/v1/flux/ast", EnsureViewer(service.FluxAST))
|
||||
router.GET("/chronograf/v1/flux/suggestions", EnsureViewer(service.FluxSuggestions))
|
||||
router.GET("/chronograf/v1/flux/suggestions/:name", EnsureViewer(service.FluxSuggestion))
|
||||
|
||||
// Source Proxy to Influx; Has gzip compression around the handler
|
||||
influx := gziphandler.GzipHandler(http.HandlerFunc(EnsureViewer(service.Influx)))
|
||||
|
|
|
@ -44,7 +44,7 @@ type getRoutesResponse struct {
|
|||
Auth []AuthRoute `json:"auth"` // Location of all auth routes.
|
||||
Logout *string `json:"logout,omitempty"` // Location of the logout route for all auth routes
|
||||
ExternalLinks getExternalLinksResponse `json:"external"` // All external links for the client to use
|
||||
IFQL getIFQLLinksResponse `json:"ifql"`
|
||||
Flux getFluxLinksResponse `json:"flux"`
|
||||
}
|
||||
|
||||
// AllRoutes is a handler that returns all links to resources in Chronograf server, as well as
|
||||
|
@ -95,10 +95,10 @@ func (a *AllRoutes) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||
StatusFeed: &a.StatusFeed,
|
||||
CustomLinks: customLinks,
|
||||
},
|
||||
IFQL: getIFQLLinksResponse{
|
||||
Self: "/chronograf/v1/ifql",
|
||||
AST: "/chronograf/v1/ifql/ast",
|
||||
Suggestions: "/chronograf/v1/ifql/suggestions",
|
||||
Flux: getFluxLinksResponse{
|
||||
Self: "/chronograf/v1/flux",
|
||||
AST: "/chronograf/v1/flux/ast",
|
||||
Suggestions: "/chronograf/v1/flux/suggestions",
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ func TestAllRoutes(t *testing.T) {
|
|||
if err := json.Unmarshal(body, &routes); err != nil {
|
||||
t.Error("TestAllRoutes not able to unmarshal JSON response")
|
||||
}
|
||||
want := `{"layouts":"/chronograf/v1/layouts","users":"/chronograf/v1/organizations/default/users","allUsers":"/chronograf/v1/users","organizations":"/chronograf/v1/organizations","mappings":"/chronograf/v1/mappings","sources":"/chronograf/v1/sources","me":"/chronograf/v1/me","environment":"/chronograf/v1/env","dashboards":"/chronograf/v1/dashboards","config":{"self":"/chronograf/v1/config","auth":"/chronograf/v1/config/auth"},"auth":[],"external":{"statusFeed":""},"ifql":{"ast":"/chronograf/v1/ifql/ast","self":"/chronograf/v1/ifql","suggestions":"/chronograf/v1/ifql/suggestions"}}
|
||||
want := `{"layouts":"/chronograf/v1/layouts","users":"/chronograf/v1/organizations/default/users","allUsers":"/chronograf/v1/users","organizations":"/chronograf/v1/organizations","mappings":"/chronograf/v1/mappings","sources":"/chronograf/v1/sources","me":"/chronograf/v1/me","environment":"/chronograf/v1/env","dashboards":"/chronograf/v1/dashboards","config":{"self":"/chronograf/v1/config","auth":"/chronograf/v1/config/auth"},"auth":[],"external":{"statusFeed":""},"flux":{"ast":"/chronograf/v1/flux/ast","self":"/chronograf/v1/flux","suggestions":"/chronograf/v1/flux/suggestions"}}
|
||||
`
|
||||
if want != string(body) {
|
||||
t.Errorf("TestAllRoutes\nwanted\n*%s*\ngot\n*%s*", want, string(body))
|
||||
|
@ -67,7 +67,7 @@ func TestAllRoutesWithAuth(t *testing.T) {
|
|||
if err := json.Unmarshal(body, &routes); err != nil {
|
||||
t.Error("TestAllRoutesWithAuth not able to unmarshal JSON response")
|
||||
}
|
||||
want := `{"layouts":"/chronograf/v1/layouts","users":"/chronograf/v1/organizations/default/users","allUsers":"/chronograf/v1/users","organizations":"/chronograf/v1/organizations","mappings":"/chronograf/v1/mappings","sources":"/chronograf/v1/sources","me":"/chronograf/v1/me","environment":"/chronograf/v1/env","dashboards":"/chronograf/v1/dashboards","config":{"self":"/chronograf/v1/config","auth":"/chronograf/v1/config/auth"},"auth":[{"name":"github","label":"GitHub","login":"/oauth/github/login","logout":"/oauth/github/logout","callback":"/oauth/github/callback"}],"logout":"/oauth/logout","external":{"statusFeed":""},"ifql":{"ast":"/chronograf/v1/ifql/ast","self":"/chronograf/v1/ifql","suggestions":"/chronograf/v1/ifql/suggestions"}}
|
||||
want := `{"layouts":"/chronograf/v1/layouts","users":"/chronograf/v1/organizations/default/users","allUsers":"/chronograf/v1/users","organizations":"/chronograf/v1/organizations","mappings":"/chronograf/v1/mappings","sources":"/chronograf/v1/sources","me":"/chronograf/v1/me","environment":"/chronograf/v1/env","dashboards":"/chronograf/v1/dashboards","config":{"self":"/chronograf/v1/config","auth":"/chronograf/v1/config/auth"},"auth":[{"name":"github","label":"GitHub","login":"/oauth/github/login","logout":"/oauth/github/logout","callback":"/oauth/github/callback"}],"logout":"/oauth/logout","external":{"statusFeed":""},"flux":{"ast":"/chronograf/v1/flux/ast","self":"/chronograf/v1/flux","suggestions":"/chronograf/v1/flux/suggestions"}}
|
||||
`
|
||||
if want != string(body) {
|
||||
t.Errorf("TestAllRoutesWithAuth\nwanted\n*%s*\ngot\n*%s*", want, string(body))
|
||||
|
@ -100,7 +100,7 @@ func TestAllRoutesWithExternalLinks(t *testing.T) {
|
|||
if err := json.Unmarshal(body, &routes); err != nil {
|
||||
t.Error("TestAllRoutesWithExternalLinks not able to unmarshal JSON response")
|
||||
}
|
||||
want := `{"layouts":"/chronograf/v1/layouts","users":"/chronograf/v1/organizations/default/users","allUsers":"/chronograf/v1/users","organizations":"/chronograf/v1/organizations","mappings":"/chronograf/v1/mappings","sources":"/chronograf/v1/sources","me":"/chronograf/v1/me","environment":"/chronograf/v1/env","dashboards":"/chronograf/v1/dashboards","config":{"self":"/chronograf/v1/config","auth":"/chronograf/v1/config/auth"},"auth":[],"external":{"statusFeed":"http://pineapple.life/feed.json","custom":[{"name":"cubeapple","url":"https://cube.apple"}]},"ifql":{"ast":"/chronograf/v1/ifql/ast","self":"/chronograf/v1/ifql","suggestions":"/chronograf/v1/ifql/suggestions"}}
|
||||
want := `{"layouts":"/chronograf/v1/layouts","users":"/chronograf/v1/organizations/default/users","allUsers":"/chronograf/v1/users","organizations":"/chronograf/v1/organizations","mappings":"/chronograf/v1/mappings","sources":"/chronograf/v1/sources","me":"/chronograf/v1/me","environment":"/chronograf/v1/env","dashboards":"/chronograf/v1/dashboards","config":{"self":"/chronograf/v1/config","auth":"/chronograf/v1/config/auth"},"auth":[],"external":{"statusFeed":"http://pineapple.life/feed.json","custom":[{"name":"cubeapple","url":"https://cube.apple"}]},"flux":{"ast":"/chronograf/v1/flux/ast","self":"/chronograf/v1/flux","suggestions":"/chronograf/v1/flux/suggestions"}}
|
||||
`
|
||||
if want != string(body) {
|
||||
t.Errorf("TestAllRoutesWithExternalLinks\nwanted\n*%s*\ngot\n*%s*", want, string(body))
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
type postServiceRequest struct {
|
||||
Name *string `json:"name"` // User facing name of service instance.; Required: true
|
||||
URL *string `json:"url"` // URL for the service backend (e.g. http://localhost:9092);/ Required: true
|
||||
Type *string `json:"type"` // Type is the kind of service (e.g. ifql); Required
|
||||
Type *string `json:"type"` // Type is the kind of service (e.g. flux); Required
|
||||
Username string `json:"username,omitempty"` // Username for authentication to service
|
||||
Password string `json:"password,omitempty"`
|
||||
InsecureSkipVerify bool `json:"insecureSkipVerify"` // InsecureSkipVerify as true means any certificate presented by the service is accepted.
|
||||
|
@ -58,7 +58,7 @@ type service struct {
|
|||
Username string `json:"username,omitempty"` // Username for authentication to service
|
||||
Password string `json:"password,omitempty"`
|
||||
InsecureSkipVerify bool `json:"insecureSkipVerify"` // InsecureSkipVerify as true means any certificate presented by the service is accepted.
|
||||
Type string `json:"type"` // Type is the kind of service (e.g. ifql)
|
||||
Type string `json:"type"` // Type is the kind of service (e.g. flux)
|
||||
Metadata map[string]interface{} `json:"metadata"` // Metadata is any other data that the frontend wants to store about this service
|
||||
Links serviceLinks `json:"links"` // Links are URI locations related to service
|
||||
}
|
||||
|
@ -229,7 +229,7 @@ func (s *Service) RemoveService(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
type patchServiceRequest struct {
|
||||
Name *string `json:"name,omitempty"` // User facing name of service instance.
|
||||
Type *string `json:"type,omitempty"` // Type is the kind of service (e.g. ifql)
|
||||
Type *string `json:"type,omitempty"` // Type is the kind of service (e.g. flux)
|
||||
URL *string `json:"url,omitempty"` // URL for the service
|
||||
Username *string `json:"username,omitempty"` // Username for service auth
|
||||
Password *string `json:"password,omitempty"`
|
||||
|
|
|
@ -1,364 +0,0 @@
|
|||
import {
|
||||
getDashboards as getDashboardsAJAX,
|
||||
updateDashboard as updateDashboardAJAX,
|
||||
deleteDashboard as deleteDashboardAJAX,
|
||||
updateDashboardCell as updateDashboardCellAJAX,
|
||||
addDashboardCell as addDashboardCellAJAX,
|
||||
deleteDashboardCell as deleteDashboardCellAJAX,
|
||||
runTemplateVariableQuery,
|
||||
} from 'src/dashboards/apis'
|
||||
|
||||
import {notify} from 'shared/actions/notifications'
|
||||
import {errorThrown} from 'shared/actions/errors'
|
||||
|
||||
import {
|
||||
getNewDashboardCell,
|
||||
getClonedDashboardCell,
|
||||
} from 'src/dashboards/utils/cellGetters'
|
||||
import {
|
||||
notifyDashboardDeleted,
|
||||
notifyDashboardDeleteFailed,
|
||||
notifyCellAdded,
|
||||
notifyCellDeleted,
|
||||
} from 'shared/copy/notifications'
|
||||
|
||||
import {
|
||||
TEMPLATE_VARIABLE_SELECTED,
|
||||
TEMPLATE_VARIABLES_SELECTED_BY_NAME,
|
||||
} from 'shared/constants/actionTypes'
|
||||
import {makeQueryForTemplate} from 'src/dashboards/utils/templateVariableQueryGenerator'
|
||||
import parsers from 'shared/parsing'
|
||||
|
||||
export const loadDashboards = (dashboards, dashboardID) => ({
|
||||
type: 'LOAD_DASHBOARDS',
|
||||
payload: {
|
||||
dashboards,
|
||||
dashboardID,
|
||||
},
|
||||
})
|
||||
|
||||
export const loadDeafaultDashTimeV1 = dashboardID => ({
|
||||
type: 'ADD_DASHBOARD_TIME_V1',
|
||||
payload: {
|
||||
dashboardID,
|
||||
},
|
||||
})
|
||||
|
||||
export const addDashTimeV1 = (dashboardID, timeRange) => ({
|
||||
type: 'ADD_DASHBOARD_TIME_V1',
|
||||
payload: {
|
||||
dashboardID,
|
||||
timeRange,
|
||||
},
|
||||
})
|
||||
|
||||
export const setDashTimeV1 = (dashboardID, timeRange) => ({
|
||||
type: 'SET_DASHBOARD_TIME_V1',
|
||||
payload: {
|
||||
dashboardID,
|
||||
timeRange,
|
||||
},
|
||||
})
|
||||
|
||||
export const setTimeRange = timeRange => ({
|
||||
type: 'SET_DASHBOARD_TIME_RANGE',
|
||||
payload: {
|
||||
timeRange,
|
||||
},
|
||||
})
|
||||
|
||||
export const updateDashboard = dashboard => ({
|
||||
type: 'UPDATE_DASHBOARD',
|
||||
payload: {
|
||||
dashboard,
|
||||
},
|
||||
})
|
||||
|
||||
export const deleteDashboard = dashboard => ({
|
||||
type: 'DELETE_DASHBOARD',
|
||||
payload: {
|
||||
dashboard,
|
||||
dashboardID: dashboard.id,
|
||||
},
|
||||
})
|
||||
|
||||
export const deleteDashboardFailed = dashboard => ({
|
||||
type: 'DELETE_DASHBOARD_FAILED',
|
||||
payload: {
|
||||
dashboard,
|
||||
},
|
||||
})
|
||||
|
||||
export const updateDashboardCells = (dashboard, cells) => ({
|
||||
type: 'UPDATE_DASHBOARD_CELLS',
|
||||
payload: {
|
||||
dashboard,
|
||||
cells,
|
||||
},
|
||||
})
|
||||
|
||||
export const syncDashboardCell = (dashboard, cell) => ({
|
||||
type: 'SYNC_DASHBOARD_CELL',
|
||||
payload: {
|
||||
dashboard,
|
||||
cell,
|
||||
},
|
||||
})
|
||||
|
||||
export const addDashboardCell = (dashboard, cell) => ({
|
||||
type: 'ADD_DASHBOARD_CELL',
|
||||
payload: {
|
||||
dashboard,
|
||||
cell,
|
||||
},
|
||||
})
|
||||
|
||||
export const editDashboardCell = (dashboard, x, y, isEditing) => ({
|
||||
type: 'EDIT_DASHBOARD_CELL',
|
||||
// x and y coords are used as a alternative to cell ids, which are not
|
||||
// universally unique, and cannot be because React depends on a
|
||||
// quasi-predictable ID for keys. Since cells cannot overlap, coordinates act
|
||||
// as a suitable id
|
||||
payload: {
|
||||
dashboard,
|
||||
x, // x-coord of the cell to be edited
|
||||
y, // y-coord of the cell to be edited
|
||||
isEditing,
|
||||
},
|
||||
})
|
||||
|
||||
export const cancelEditCell = (dashboardID, cellID) => ({
|
||||
type: 'CANCEL_EDIT_CELL',
|
||||
payload: {
|
||||
dashboardID,
|
||||
cellID,
|
||||
},
|
||||
})
|
||||
|
||||
export const renameDashboardCell = (dashboard, x, y, name) => ({
|
||||
type: 'RENAME_DASHBOARD_CELL',
|
||||
payload: {
|
||||
dashboard,
|
||||
x, // x-coord of the cell to be renamed
|
||||
y, // y-coord of the cell to be renamed
|
||||
name,
|
||||
},
|
||||
})
|
||||
|
||||
export const deleteDashboardCell = (dashboard, cell) => ({
|
||||
type: 'DELETE_DASHBOARD_CELL',
|
||||
payload: {
|
||||
dashboard,
|
||||
cell,
|
||||
},
|
||||
})
|
||||
|
||||
export const editCellQueryStatus = (queryID, status) => ({
|
||||
type: 'EDIT_CELL_QUERY_STATUS',
|
||||
payload: {
|
||||
queryID,
|
||||
status,
|
||||
},
|
||||
})
|
||||
|
||||
export const templateVariableSelected = (dashboardID, templateID, values) => ({
|
||||
type: TEMPLATE_VARIABLE_SELECTED,
|
||||
payload: {
|
||||
dashboardID,
|
||||
templateID,
|
||||
values,
|
||||
},
|
||||
})
|
||||
|
||||
export const templateVariablesSelectedByName = (dashboardID, query) => ({
|
||||
type: TEMPLATE_VARIABLES_SELECTED_BY_NAME,
|
||||
payload: {
|
||||
dashboardID,
|
||||
query,
|
||||
},
|
||||
})
|
||||
|
||||
export const editTemplateVariableValues = (
|
||||
dashboardID,
|
||||
templateID,
|
||||
values
|
||||
) => ({
|
||||
type: 'EDIT_TEMPLATE_VARIABLE_VALUES',
|
||||
payload: {
|
||||
dashboardID,
|
||||
templateID,
|
||||
values,
|
||||
},
|
||||
})
|
||||
|
||||
export const setHoverTime = hoverTime => ({
|
||||
type: 'SET_HOVER_TIME',
|
||||
payload: {
|
||||
hoverTime,
|
||||
},
|
||||
})
|
||||
|
||||
export const setActiveCell = activeCellID => ({
|
||||
type: 'SET_ACTIVE_CELL',
|
||||
payload: {
|
||||
activeCellID,
|
||||
},
|
||||
})
|
||||
|
||||
// Async Action Creators
|
||||
|
||||
export const getDashboardsAsync = () => async dispatch => {
|
||||
try {
|
||||
const {
|
||||
data: {dashboards},
|
||||
} = await getDashboardsAJAX()
|
||||
dispatch(loadDashboards(dashboards))
|
||||
return dashboards
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
dispatch(errorThrown(error))
|
||||
}
|
||||
}
|
||||
|
||||
const removeUnselectedTemplateValues = dashboard => {
|
||||
const templates = dashboard.templates.map(template => {
|
||||
if (template.type === 'csv') {
|
||||
return template
|
||||
}
|
||||
|
||||
const value = template.values.find(val => val.selected)
|
||||
const values = value ? [value] : []
|
||||
|
||||
return {...template, values}
|
||||
})
|
||||
return templates
|
||||
}
|
||||
|
||||
export const putDashboard = dashboard => async dispatch => {
|
||||
try {
|
||||
// save only selected template values to server
|
||||
const templatesWithOnlySelectedValues = removeUnselectedTemplateValues(
|
||||
dashboard
|
||||
)
|
||||
const {
|
||||
data: dashboardWithOnlySelectedTemplateValues,
|
||||
} = await updateDashboardAJAX({
|
||||
...dashboard,
|
||||
templates: templatesWithOnlySelectedValues,
|
||||
})
|
||||
// save all template values to redux
|
||||
dispatch(
|
||||
updateDashboard({
|
||||
...dashboardWithOnlySelectedTemplateValues,
|
||||
templates: dashboard.templates,
|
||||
})
|
||||
)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
dispatch(errorThrown(error))
|
||||
}
|
||||
}
|
||||
|
||||
export const putDashboardByID = dashboardID => async (dispatch, getState) => {
|
||||
try {
|
||||
const {
|
||||
dashboardUI: {dashboards},
|
||||
} = getState()
|
||||
const dashboard = dashboards.find(d => d.id === +dashboardID)
|
||||
const templates = removeUnselectedTemplateValues(dashboard)
|
||||
await updateDashboardAJAX({...dashboard, templates})
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
dispatch(errorThrown(error))
|
||||
}
|
||||
}
|
||||
|
||||
export const updateDashboardCell = (dashboard, cell) => async dispatch => {
|
||||
try {
|
||||
const {data} = await updateDashboardCellAJAX(cell)
|
||||
dispatch(syncDashboardCell(dashboard, data))
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
dispatch(errorThrown(error))
|
||||
}
|
||||
}
|
||||
|
||||
export const deleteDashboardAsync = dashboard => async dispatch => {
|
||||
dispatch(deleteDashboard(dashboard))
|
||||
try {
|
||||
await deleteDashboardAJAX(dashboard)
|
||||
dispatch(notify(notifyDashboardDeleted(dashboard.name)))
|
||||
} catch (error) {
|
||||
dispatch(
|
||||
errorThrown(
|
||||
error,
|
||||
notifyDashboardDeleteFailed(dashboard.name, error.data.message)
|
||||
)
|
||||
)
|
||||
dispatch(deleteDashboardFailed(dashboard))
|
||||
}
|
||||
}
|
||||
|
||||
export const addDashboardCellAsync = (
|
||||
dashboard,
|
||||
cellType
|
||||
) => async dispatch => {
|
||||
try {
|
||||
const {data} = await addDashboardCellAJAX(
|
||||
dashboard,
|
||||
getNewDashboardCell(dashboard, cellType)
|
||||
)
|
||||
dispatch(addDashboardCell(dashboard, data))
|
||||
dispatch(notify(notifyCellAdded(data.name)))
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
dispatch(errorThrown(error))
|
||||
}
|
||||
}
|
||||
|
||||
export const cloneDashboardCellAsync = (dashboard, cell) => async dispatch => {
|
||||
try {
|
||||
const clonedCell = getClonedDashboardCell(dashboard, cell)
|
||||
const {data} = await addDashboardCellAJAX(dashboard, clonedCell)
|
||||
dispatch(addDashboardCell(dashboard, data))
|
||||
dispatch(notify(notifyCellAdded(clonedCell.name)))
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
dispatch(errorThrown(error))
|
||||
}
|
||||
}
|
||||
|
||||
export const deleteDashboardCellAsync = (dashboard, cell) => async dispatch => {
|
||||
try {
|
||||
await deleteDashboardCellAJAX(cell)
|
||||
dispatch(deleteDashboardCell(dashboard, cell))
|
||||
dispatch(notify(notifyCellDeleted(cell.name)))
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
dispatch(errorThrown(error))
|
||||
}
|
||||
}
|
||||
|
||||
export const updateTempVarValues = (source, dashboard) => async dispatch => {
|
||||
try {
|
||||
const tempsWithQueries = dashboard.templates.filter(
|
||||
({query}) => !!query.influxql
|
||||
)
|
||||
|
||||
const asyncQueries = tempsWithQueries.map(({query}) =>
|
||||
runTemplateVariableQuery(source, {query: makeQueryForTemplate(query)})
|
||||
)
|
||||
|
||||
const results = await Promise.all(asyncQueries)
|
||||
|
||||
results.forEach(({data}, i) => {
|
||||
const {type, query, id} = tempsWithQueries[i]
|
||||
const parsed = parsers[type](data, query.tagKey || query.measurement)
|
||||
const vals = parsed[type]
|
||||
dispatch(editTemplateVariableValues(dashboard.id, id, vals))
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
dispatch(errorThrown(error))
|
||||
}
|
||||
}
|
|
@ -0,0 +1,688 @@
|
|||
import _ from 'lodash'
|
||||
|
||||
import {
|
||||
getDashboards as getDashboardsAJAX,
|
||||
updateDashboard as updateDashboardAJAX,
|
||||
deleteDashboard as deleteDashboardAJAX,
|
||||
updateDashboardCell as updateDashboardCellAJAX,
|
||||
addDashboardCell as addDashboardCellAJAX,
|
||||
deleteDashboardCell as deleteDashboardCellAJAX,
|
||||
runTemplateVariableQuery,
|
||||
createDashboard as createDashboardAJAX,
|
||||
} from 'src/dashboards/apis'
|
||||
import {getMe} from 'src/shared/apis/auth'
|
||||
|
||||
import {notify} from 'src/shared/actions/notifications'
|
||||
import {errorThrown} from 'src/shared/actions/errors'
|
||||
|
||||
import {
|
||||
getNewDashboardCell,
|
||||
getClonedDashboardCell,
|
||||
} from 'src/dashboards/utils/cellGetters'
|
||||
import {
|
||||
notifyDashboardDeleted,
|
||||
notifyDashboardDeleteFailed,
|
||||
notifyCellAdded,
|
||||
notifyCellDeleted,
|
||||
notifyDashboardImportFailed,
|
||||
notifyDashboardImported,
|
||||
} from 'src/shared/copy/notifications'
|
||||
|
||||
import {
|
||||
TEMPLATE_VARIABLE_SELECTED,
|
||||
TEMPLATE_VARIABLES_SELECTED_BY_NAME,
|
||||
} from 'src/shared/constants/actionTypes'
|
||||
import {CellType} from 'src/types/dashboard'
|
||||
import {makeQueryForTemplate} from 'src/dashboards/utils/templateVariableQueryGenerator'
|
||||
import parsers from 'src/shared/parsing'
|
||||
import {getDeep} from 'src/utils/wrappers'
|
||||
|
||||
import {Dashboard, TimeRange, Cell, Query, Source, Template} from 'src/types'
|
||||
|
||||
interface LoadDashboardsAction {
|
||||
type: 'LOAD_DASHBOARDS'
|
||||
payload: {
|
||||
dashboards: Dashboard[]
|
||||
dashboardID: string
|
||||
}
|
||||
}
|
||||
|
||||
export const loadDashboards = (
|
||||
dashboards: Dashboard[],
|
||||
dashboardID?: string
|
||||
): LoadDashboardsAction => ({
|
||||
type: 'LOAD_DASHBOARDS',
|
||||
payload: {
|
||||
dashboards,
|
||||
dashboardID,
|
||||
},
|
||||
})
|
||||
|
||||
interface LoadDeafaultDashTimeV1Action {
|
||||
type: 'ADD_DASHBOARD_TIME_V1'
|
||||
payload: {
|
||||
dashboardID: string
|
||||
}
|
||||
}
|
||||
|
||||
export const loadDeafaultDashTimeV1 = (
|
||||
dashboardID: string
|
||||
): LoadDeafaultDashTimeV1Action => ({
|
||||
type: 'ADD_DASHBOARD_TIME_V1',
|
||||
payload: {
|
||||
dashboardID,
|
||||
},
|
||||
})
|
||||
|
||||
interface AddDashTimeV1Action {
|
||||
type: 'ADD_DASHBOARD_TIME_V1'
|
||||
payload: {
|
||||
dashboardID: string
|
||||
timeRange: TimeRange
|
||||
}
|
||||
}
|
||||
|
||||
export const addDashTimeV1 = (
|
||||
dashboardID: string,
|
||||
timeRange: TimeRange
|
||||
): AddDashTimeV1Action => ({
|
||||
type: 'ADD_DASHBOARD_TIME_V1',
|
||||
payload: {
|
||||
dashboardID,
|
||||
timeRange,
|
||||
},
|
||||
})
|
||||
|
||||
interface SetDashTimeV1Action {
|
||||
type: 'SET_DASHBOARD_TIME_V1'
|
||||
payload: {
|
||||
dashboardID: string
|
||||
timeRange: TimeRange
|
||||
}
|
||||
}
|
||||
|
||||
export const setDashTimeV1 = (
|
||||
dashboardID: string,
|
||||
timeRange: TimeRange
|
||||
): SetDashTimeV1Action => ({
|
||||
type: 'SET_DASHBOARD_TIME_V1',
|
||||
payload: {
|
||||
dashboardID,
|
||||
timeRange,
|
||||
},
|
||||
})
|
||||
|
||||
interface SetTimeRangeAction {
|
||||
type: 'SET_DASHBOARD_TIME_RANGE'
|
||||
payload: {
|
||||
timeRange: TimeRange
|
||||
}
|
||||
}
|
||||
|
||||
export const setTimeRange = (timeRange: TimeRange): SetTimeRangeAction => ({
|
||||
type: 'SET_DASHBOARD_TIME_RANGE',
|
||||
payload: {
|
||||
timeRange,
|
||||
},
|
||||
})
|
||||
|
||||
interface UpdateDashboardAction {
|
||||
type: 'UPDATE_DASHBOARD'
|
||||
payload: {
|
||||
dashboard: Dashboard
|
||||
}
|
||||
}
|
||||
|
||||
export const updateDashboard = (
|
||||
dashboard: Dashboard
|
||||
): UpdateDashboardAction => ({
|
||||
type: 'UPDATE_DASHBOARD',
|
||||
payload: {
|
||||
dashboard,
|
||||
},
|
||||
})
|
||||
|
||||
interface CreateDashboardAction {
|
||||
type: 'CREATE_DASHBOARD'
|
||||
payload: {
|
||||
dashboard: Dashboard
|
||||
}
|
||||
}
|
||||
|
||||
export const createDashboard = (
|
||||
dashboard: Dashboard
|
||||
): CreateDashboardAction => ({
|
||||
type: 'CREATE_DASHBOARD',
|
||||
payload: {
|
||||
dashboard,
|
||||
},
|
||||
})
|
||||
|
||||
interface DeleteDashboardAction {
|
||||
type: 'DELETE_DASHBOARD'
|
||||
payload: {
|
||||
dashboard: Dashboard
|
||||
dashboardID: number
|
||||
}
|
||||
}
|
||||
|
||||
export const deleteDashboard = (
|
||||
dashboard: Dashboard
|
||||
): DeleteDashboardAction => ({
|
||||
type: 'DELETE_DASHBOARD',
|
||||
payload: {
|
||||
dashboard,
|
||||
dashboardID: dashboard.id,
|
||||
},
|
||||
})
|
||||
|
||||
interface DeleteDashboardFailedAction {
|
||||
type: 'DELETE_DASHBOARD_FAILED'
|
||||
payload: {
|
||||
dashboard: Dashboard
|
||||
}
|
||||
}
|
||||
|
||||
export const deleteDashboardFailed = (
|
||||
dashboard: Dashboard
|
||||
): DeleteDashboardFailedAction => ({
|
||||
type: 'DELETE_DASHBOARD_FAILED',
|
||||
payload: {
|
||||
dashboard,
|
||||
},
|
||||
})
|
||||
|
||||
interface UpdateDashboardCellsAction {
|
||||
type: 'UPDATE_DASHBOARD_CELLS'
|
||||
payload: {
|
||||
dashboard: Dashboard
|
||||
cells: Cell[]
|
||||
}
|
||||
}
|
||||
|
||||
export const updateDashboardCells = (
|
||||
dashboard: Dashboard,
|
||||
cells: Cell[]
|
||||
): UpdateDashboardCellsAction => ({
|
||||
type: 'UPDATE_DASHBOARD_CELLS',
|
||||
payload: {
|
||||
dashboard,
|
||||
cells,
|
||||
},
|
||||
})
|
||||
|
||||
interface SyncDashboardCellAction {
|
||||
type: 'SYNC_DASHBOARD_CELL'
|
||||
payload: {
|
||||
dashboard: Dashboard
|
||||
cell: Cell
|
||||
}
|
||||
}
|
||||
|
||||
export const syncDashboardCell = (
|
||||
dashboard: Dashboard,
|
||||
cell: Cell
|
||||
): SyncDashboardCellAction => ({
|
||||
type: 'SYNC_DASHBOARD_CELL',
|
||||
payload: {
|
||||
dashboard,
|
||||
cell,
|
||||
},
|
||||
})
|
||||
|
||||
interface AddDashboardCellAction {
|
||||
type: 'ADD_DASHBOARD_CELL'
|
||||
payload: {
|
||||
dashboard: Dashboard
|
||||
cell: Cell
|
||||
}
|
||||
}
|
||||
|
||||
export const addDashboardCell = (
|
||||
dashboard: Dashboard,
|
||||
cell: Cell
|
||||
): AddDashboardCellAction => ({
|
||||
type: 'ADD_DASHBOARD_CELL',
|
||||
payload: {
|
||||
dashboard,
|
||||
cell,
|
||||
},
|
||||
})
|
||||
|
||||
interface EditDashboardCellAction {
|
||||
type: 'EDIT_DASHBOARD_CELL'
|
||||
payload: {
|
||||
dashboard: Dashboard
|
||||
x: number
|
||||
y: number
|
||||
isEditing: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export const editDashboardCell = (
|
||||
dashboard: Dashboard,
|
||||
x: number,
|
||||
y: number,
|
||||
isEditing: boolean
|
||||
): EditDashboardCellAction => ({
|
||||
type: 'EDIT_DASHBOARD_CELL',
|
||||
// x and y coords are used as a alternative to cell ids, which are not
|
||||
// universally unique, and cannot be because React depends on a
|
||||
// quasi-predictable ID for keys. Since cells cannot overlap, coordinates act
|
||||
// as a suitable id
|
||||
payload: {
|
||||
dashboard,
|
||||
x, // x-coord of the cell to be edited
|
||||
y, // y-coord of the cell to be edited
|
||||
isEditing,
|
||||
},
|
||||
})
|
||||
|
||||
interface CancelEditCellAction {
|
||||
type: 'CANCEL_EDIT_CELL'
|
||||
payload: {
|
||||
dashboardID: string
|
||||
cellID: string
|
||||
}
|
||||
}
|
||||
|
||||
export const cancelEditCell = (
|
||||
dashboardID: string,
|
||||
cellID: string
|
||||
): CancelEditCellAction => ({
|
||||
type: 'CANCEL_EDIT_CELL',
|
||||
payload: {
|
||||
dashboardID,
|
||||
cellID,
|
||||
},
|
||||
})
|
||||
|
||||
interface RenameDashboardCellAction {
|
||||
type: 'RENAME_DASHBOARD_CELL'
|
||||
payload: {
|
||||
dashboard: Dashboard
|
||||
x: number
|
||||
y: number
|
||||
name: string
|
||||
}
|
||||
}
|
||||
|
||||
export const renameDashboardCell = (
|
||||
dashboard: Dashboard,
|
||||
x: number,
|
||||
y: number,
|
||||
name: string
|
||||
): RenameDashboardCellAction => ({
|
||||
type: 'RENAME_DASHBOARD_CELL',
|
||||
payload: {
|
||||
dashboard,
|
||||
x, // x-coord of the cell to be renamed
|
||||
y, // y-coord of the cell to be renamed
|
||||
name,
|
||||
},
|
||||
})
|
||||
|
||||
interface DeleteDashboardCellAction {
|
||||
type: 'DELETE_DASHBOARD_CELL'
|
||||
payload: {
|
||||
dashboard: Dashboard
|
||||
cell: Cell
|
||||
}
|
||||
}
|
||||
|
||||
export const deleteDashboardCell = (
|
||||
dashboard: Dashboard,
|
||||
cell: Cell
|
||||
): DeleteDashboardCellAction => ({
|
||||
type: 'DELETE_DASHBOARD_CELL',
|
||||
payload: {
|
||||
dashboard,
|
||||
cell,
|
||||
},
|
||||
})
|
||||
|
||||
interface EditCellQueryStatusAction {
|
||||
type: 'EDIT_CELL_QUERY_STATUS'
|
||||
payload: {
|
||||
queryID: string
|
||||
status: string
|
||||
}
|
||||
}
|
||||
|
||||
export const editCellQueryStatus = (
|
||||
queryID: string,
|
||||
status: string
|
||||
): EditCellQueryStatusAction => ({
|
||||
type: 'EDIT_CELL_QUERY_STATUS',
|
||||
payload: {
|
||||
queryID,
|
||||
status,
|
||||
},
|
||||
})
|
||||
|
||||
interface TemplateVariableSelectedAction {
|
||||
type: 'TEMPLATE_VARIABLE_SELECTED'
|
||||
payload: {
|
||||
dashboardID: string
|
||||
templateID: string
|
||||
values: any[]
|
||||
}
|
||||
}
|
||||
|
||||
export const templateVariableSelected = (
|
||||
dashboardID: string,
|
||||
templateID: string,
|
||||
values
|
||||
): TemplateVariableSelectedAction => ({
|
||||
type: TEMPLATE_VARIABLE_SELECTED,
|
||||
payload: {
|
||||
dashboardID,
|
||||
templateID,
|
||||
values,
|
||||
},
|
||||
})
|
||||
|
||||
interface TemplateVariablesSelectedByNameAction {
|
||||
type: 'TEMPLATE_VARIABLES_SELECTED_BY_NAME'
|
||||
payload: {
|
||||
dashboardID: string
|
||||
query: Query
|
||||
}
|
||||
}
|
||||
|
||||
export const templateVariablesSelectedByName = (
|
||||
dashboardID: string,
|
||||
query: Query
|
||||
): TemplateVariablesSelectedByNameAction => ({
|
||||
type: TEMPLATE_VARIABLES_SELECTED_BY_NAME,
|
||||
payload: {
|
||||
dashboardID,
|
||||
query,
|
||||
},
|
||||
})
|
||||
|
||||
interface EditTemplateVariableValuesAction {
|
||||
type: 'EDIT_TEMPLATE_VARIABLE_VALUES'
|
||||
payload: {
|
||||
dashboardID: number
|
||||
templateID: string
|
||||
values: any[]
|
||||
}
|
||||
}
|
||||
|
||||
export const editTemplateVariableValues = (
|
||||
dashboardID: number,
|
||||
templateID: string,
|
||||
values
|
||||
): EditTemplateVariableValuesAction => ({
|
||||
type: 'EDIT_TEMPLATE_VARIABLE_VALUES',
|
||||
payload: {
|
||||
dashboardID,
|
||||
templateID,
|
||||
values,
|
||||
},
|
||||
})
|
||||
|
||||
interface SetHoverTimeAction {
|
||||
type: 'SET_HOVER_TIME'
|
||||
payload: {
|
||||
hoverTime: string
|
||||
}
|
||||
}
|
||||
|
||||
export const setHoverTime = (hoverTime: string): SetHoverTimeAction => ({
|
||||
type: 'SET_HOVER_TIME',
|
||||
payload: {
|
||||
hoverTime,
|
||||
},
|
||||
})
|
||||
|
||||
interface SetActiveCellAction {
|
||||
type: 'SET_ACTIVE_CELL'
|
||||
payload: {
|
||||
activeCellID: string
|
||||
}
|
||||
}
|
||||
|
||||
export const setActiveCell = (activeCellID: string): SetActiveCellAction => ({
|
||||
type: 'SET_ACTIVE_CELL',
|
||||
payload: {
|
||||
activeCellID,
|
||||
},
|
||||
})
|
||||
|
||||
// Async Action Creators
|
||||
|
||||
export const getDashboardsAsync = () => async (
|
||||
dispatch
|
||||
): Promise<Dashboard[] | void> => {
|
||||
try {
|
||||
const {
|
||||
data: {dashboards},
|
||||
} = await getDashboardsAJAX()
|
||||
dispatch(loadDashboards(dashboards))
|
||||
return dashboards
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
dispatch(errorThrown(error))
|
||||
}
|
||||
}
|
||||
|
||||
export const getChronografVersion = () => async (): Promise<string | void> => {
|
||||
try {
|
||||
const results = await getMe()
|
||||
const version = _.get(results, 'headers.x-chronograf-version')
|
||||
return version
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
const removeUnselectedTemplateValues = (dashboard: Dashboard): Template[] => {
|
||||
const templates = getDeep<Template[]>(dashboard, 'templates', []).map(
|
||||
template => {
|
||||
if (template.type === 'csv') {
|
||||
return template
|
||||
}
|
||||
|
||||
const value = template.values.find(val => val.selected)
|
||||
const values = value ? [value] : []
|
||||
|
||||
return {...template, values}
|
||||
}
|
||||
)
|
||||
return templates
|
||||
}
|
||||
|
||||
export const putDashboard = (dashboard: Dashboard) => async (
|
||||
dispatch
|
||||
): Promise<void> => {
|
||||
try {
|
||||
// save only selected template values to server
|
||||
const templatesWithOnlySelectedValues = removeUnselectedTemplateValues(
|
||||
dashboard
|
||||
)
|
||||
const {
|
||||
data: dashboardWithOnlySelectedTemplateValues,
|
||||
} = await updateDashboardAJAX({
|
||||
...dashboard,
|
||||
templates: templatesWithOnlySelectedValues,
|
||||
})
|
||||
// save all template values to redux
|
||||
dispatch(
|
||||
updateDashboard({
|
||||
...dashboardWithOnlySelectedTemplateValues,
|
||||
templates: dashboard.templates,
|
||||
})
|
||||
)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
dispatch(errorThrown(error))
|
||||
}
|
||||
}
|
||||
|
||||
export const putDashboardByID = (dashboardID: string) => async (
|
||||
dispatch,
|
||||
getState
|
||||
): Promise<void> => {
|
||||
try {
|
||||
const {
|
||||
dashboardUI: {dashboards},
|
||||
} = getState()
|
||||
const dashboard: Dashboard = dashboards.find(d => d.id === +dashboardID)
|
||||
const templates = removeUnselectedTemplateValues(dashboard)
|
||||
await updateDashboardAJAX({...dashboard, templates})
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
dispatch(errorThrown(error))
|
||||
}
|
||||
}
|
||||
|
||||
export const updateDashboardCell = (dashboard: Dashboard, cell: Cell) => async (
|
||||
dispatch
|
||||
): Promise<void> => {
|
||||
try {
|
||||
const {data} = await updateDashboardCellAJAX(cell)
|
||||
dispatch(syncDashboardCell(dashboard, data))
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
dispatch(errorThrown(error))
|
||||
}
|
||||
}
|
||||
|
||||
export const deleteDashboardAsync = (dashboard: Dashboard) => async (
|
||||
dispatch
|
||||
): Promise<void> => {
|
||||
dispatch(deleteDashboard(dashboard))
|
||||
try {
|
||||
await deleteDashboardAJAX(dashboard)
|
||||
dispatch(notify(notifyDashboardDeleted(dashboard.name)))
|
||||
} catch (error) {
|
||||
dispatch(
|
||||
errorThrown(
|
||||
error,
|
||||
notifyDashboardDeleteFailed(dashboard.name, error.data.message)
|
||||
)
|
||||
)
|
||||
dispatch(deleteDashboardFailed(dashboard))
|
||||
}
|
||||
}
|
||||
|
||||
export const addDashboardCellAsync = (
|
||||
dashboard: Dashboard,
|
||||
cellType: CellType
|
||||
) => async (dispatch): Promise<void> => {
|
||||
try {
|
||||
const {data} = await addDashboardCellAJAX(
|
||||
dashboard,
|
||||
getNewDashboardCell(dashboard, cellType)
|
||||
)
|
||||
dispatch(addDashboardCell(dashboard, data))
|
||||
dispatch(notify(notifyCellAdded(data.name)))
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
dispatch(errorThrown(error))
|
||||
}
|
||||
}
|
||||
|
||||
export const cloneDashboardCellAsync = (
|
||||
dashboard: Dashboard,
|
||||
cell: Cell
|
||||
) => async (dispatch): Promise<void> => {
|
||||
try {
|
||||
const clonedCell = getClonedDashboardCell(dashboard, cell)
|
||||
const {data} = await addDashboardCellAJAX(dashboard, clonedCell)
|
||||
dispatch(addDashboardCell(dashboard, data))
|
||||
dispatch(notify(notifyCellAdded(clonedCell.name)))
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
dispatch(errorThrown(error))
|
||||
}
|
||||
}
|
||||
|
||||
export const deleteDashboardCellAsync = (
|
||||
dashboard: Dashboard,
|
||||
cell: Cell
|
||||
) => async (dispatch): Promise<void> => {
|
||||
try {
|
||||
await deleteDashboardCellAJAX(cell)
|
||||
dispatch(deleteDashboardCell(dashboard, cell))
|
||||
dispatch(notify(notifyCellDeleted(cell.name)))
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
dispatch(errorThrown(error))
|
||||
}
|
||||
}
|
||||
|
||||
export const updateTempVarValues = (
|
||||
source: Source,
|
||||
dashboard: Dashboard
|
||||
) => async (dispatch): Promise<void> => {
|
||||
try {
|
||||
const tempsWithQueries = dashboard.templates.filter(
|
||||
({query}) => !!_.get(query, 'influxql')
|
||||
)
|
||||
|
||||
const asyncQueries = tempsWithQueries.map(({query}) =>
|
||||
runTemplateVariableQuery(source, {
|
||||
query: makeQueryForTemplate(query),
|
||||
db: null,
|
||||
tempVars: null,
|
||||
})
|
||||
)
|
||||
|
||||
const results = await Promise.all(asyncQueries)
|
||||
|
||||
results.forEach(({data}, i) => {
|
||||
const {type, query, id} = tempsWithQueries[i]
|
||||
const parsed = parsers[type](data, query.tagKey || query.measurement)
|
||||
const vals = parsed[type]
|
||||
dispatch(editTemplateVariableValues(dashboard.id, id, vals))
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
dispatch(errorThrown(error))
|
||||
}
|
||||
}
|
||||
|
||||
export const importDashboardAsync = (dashboard: Dashboard) => async (
|
||||
dispatch
|
||||
): Promise<void> => {
|
||||
try {
|
||||
// save only selected template values to server
|
||||
const templatesWithOnlySelectedValues = removeUnselectedTemplateValues(
|
||||
dashboard
|
||||
)
|
||||
|
||||
const results = await createDashboardAJAX({
|
||||
...dashboard,
|
||||
templates: templatesWithOnlySelectedValues,
|
||||
})
|
||||
|
||||
const dashboardWithOnlySelectedTemplateValues = _.get(results, 'data')
|
||||
|
||||
// save all template values to redux
|
||||
dispatch(
|
||||
createDashboard({
|
||||
...dashboardWithOnlySelectedTemplateValues,
|
||||
templates: dashboard.templates,
|
||||
})
|
||||
)
|
||||
|
||||
const {
|
||||
data: {dashboards},
|
||||
} = await getDashboardsAJAX()
|
||||
dispatch(loadDashboards(dashboards))
|
||||
|
||||
dispatch(notify(notifyDashboardImported(name)))
|
||||
} catch (error) {
|
||||
const errorMessage = _.get(
|
||||
error,
|
||||
'data.message',
|
||||
'Could not upload dashboard'
|
||||
)
|
||||
dispatch(notify(notifyDashboardImportFailed('', errorMessage)))
|
||||
console.error(error)
|
||||
dispatch(errorThrown(error))
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
import React from 'react'
|
||||
|
||||
import SourceIndicator from 'shared/components/SourceIndicator'
|
||||
import SourceIndicator from 'src/shared/components/SourceIndicator'
|
||||
|
||||
const DashboardsHeader = () => (
|
||||
const DashboardsHeader = (): JSX.Element => (
|
||||
<div className="page-header">
|
||||
<div className="page-header__container">
|
||||
<div className="page-header__left">
|
|
@ -1,98 +0,0 @@
|
|||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
|
||||
import Authorized, {EDITOR_ROLE} from 'src/auth/Authorized'
|
||||
|
||||
import DashboardsTable from 'src/dashboards/components/DashboardsTable'
|
||||
import SearchBar from 'src/hosts/components/SearchBar'
|
||||
import FancyScrollbar from 'shared/components/FancyScrollbar'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
@ErrorHandling
|
||||
class DashboardsPageContents extends Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
searchTerm: '',
|
||||
}
|
||||
}
|
||||
|
||||
filterDashboards = searchTerm => {
|
||||
this.setState({searchTerm})
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
dashboards,
|
||||
onDeleteDashboard,
|
||||
onCreateDashboard,
|
||||
onCloneDashboard,
|
||||
dashboardLink,
|
||||
} = this.props
|
||||
const {searchTerm} = this.state
|
||||
|
||||
let tableHeader
|
||||
if (dashboards === null) {
|
||||
tableHeader = 'Loading Dashboards...'
|
||||
} else if (dashboards.length === 1) {
|
||||
tableHeader = '1 Dashboard'
|
||||
} else {
|
||||
tableHeader = `${dashboards.length} Dashboards`
|
||||
}
|
||||
const filteredDashboards = dashboards.filter(d =>
|
||||
d.name.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
)
|
||||
|
||||
return (
|
||||
<FancyScrollbar className="page-contents">
|
||||
<div className="container-fluid">
|
||||
<div className="row">
|
||||
<div className="col-md-12">
|
||||
<div className="panel">
|
||||
<div className="panel-heading">
|
||||
<h2 className="panel-title">{tableHeader}</h2>
|
||||
<div className="dashboards-page--actions">
|
||||
<SearchBar
|
||||
placeholder="Filter by Name..."
|
||||
onSearch={this.filterDashboards}
|
||||
/>
|
||||
<Authorized requiredRole={EDITOR_ROLE}>
|
||||
<button
|
||||
className="btn btn-sm btn-primary"
|
||||
onClick={onCreateDashboard}
|
||||
>
|
||||
<span className="icon plus" /> Create Dashboard
|
||||
</button>
|
||||
</Authorized>
|
||||
</div>
|
||||
</div>
|
||||
<div className="panel-body">
|
||||
<DashboardsTable
|
||||
dashboards={filteredDashboards}
|
||||
onDeleteDashboard={onDeleteDashboard}
|
||||
onCreateDashboard={onCreateDashboard}
|
||||
onCloneDashboard={onCloneDashboard}
|
||||
dashboardLink={dashboardLink}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</FancyScrollbar>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {arrayOf, func, shape, string} = PropTypes
|
||||
|
||||
DashboardsPageContents.propTypes = {
|
||||
dashboards: arrayOf(shape()),
|
||||
onDeleteDashboard: func.isRequired,
|
||||
onCreateDashboard: func.isRequired,
|
||||
onCloneDashboard: func.isRequired,
|
||||
dashboardLink: string.isRequired,
|
||||
}
|
||||
|
||||
export default DashboardsPageContents
|
|
@ -0,0 +1,165 @@
|
|||
import React, {Component, MouseEvent} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
import Authorized, {EDITOR_ROLE} from 'src/auth/Authorized'
|
||||
|
||||
import DashboardsTable from 'src/dashboards/components/DashboardsTable'
|
||||
import ImportDashboardOverlay from 'src/dashboards/components/ImportDashboardOverlay'
|
||||
import SearchBar from 'src/hosts/components/SearchBar'
|
||||
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import {
|
||||
showOverlay as showOverlayAction,
|
||||
ShowOverlay,
|
||||
} from 'src/shared/actions/overlayTechnology'
|
||||
import {OverlayContext} from 'src/shared/components/OverlayTechnology'
|
||||
|
||||
import {Dashboard} from 'src/types'
|
||||
import {Notification} from 'src/types/notifications'
|
||||
|
||||
interface Props {
|
||||
dashboards: Dashboard[]
|
||||
onDeleteDashboard: (dashboard: Dashboard) => () => void
|
||||
onCreateDashboard: () => void
|
||||
onCloneDashboard: (
|
||||
dashboard: Dashboard
|
||||
) => (event: MouseEvent<HTMLButtonElement>) => void
|
||||
onExportDashboard: (dashboard: Dashboard) => () => void
|
||||
onImportDashboard: (dashboard: Dashboard) => void
|
||||
notify: (message: Notification) => void
|
||||
showOverlay: ShowOverlay
|
||||
dashboardLink: string
|
||||
}
|
||||
|
||||
interface State {
|
||||
searchTerm: string
|
||||
}
|
||||
|
||||
@ErrorHandling
|
||||
class DashboardsPageContents extends Component<Props, State> {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
searchTerm: '',
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {
|
||||
onDeleteDashboard,
|
||||
onCreateDashboard,
|
||||
onCloneDashboard,
|
||||
onExportDashboard,
|
||||
dashboardLink,
|
||||
} = this.props
|
||||
|
||||
return (
|
||||
<FancyScrollbar className="page-contents">
|
||||
<div className="container-fluid">
|
||||
<div className="row">
|
||||
<div className="col-md-12">
|
||||
<div className="panel">
|
||||
{this.renderPanelHeading}
|
||||
<div className="panel-body">
|
||||
<DashboardsTable
|
||||
dashboards={this.filteredDashboards}
|
||||
onDeleteDashboard={onDeleteDashboard}
|
||||
onCreateDashboard={onCreateDashboard}
|
||||
onCloneDashboard={onCloneDashboard}
|
||||
onExportDashboard={onExportDashboard}
|
||||
dashboardLink={dashboardLink}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</FancyScrollbar>
|
||||
)
|
||||
}
|
||||
|
||||
private get renderPanelHeading(): JSX.Element {
|
||||
const {onCreateDashboard} = this.props
|
||||
|
||||
return (
|
||||
<div className="panel-heading">
|
||||
<h2 className="panel-title">{this.panelTitle}</h2>
|
||||
<div className="panel-controls">
|
||||
<SearchBar
|
||||
placeholder="Filter by Name..."
|
||||
onSearch={this.filterDashboards}
|
||||
/>
|
||||
<Authorized requiredRole={EDITOR_ROLE}>
|
||||
<>
|
||||
<button
|
||||
className="btn btn-sm btn-default"
|
||||
onClick={this.showImportOverlay}
|
||||
>
|
||||
<span className="icon import" /> Import Dashboard
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-sm btn-primary"
|
||||
onClick={onCreateDashboard}
|
||||
>
|
||||
<span className="icon plus" /> Create Dashboard
|
||||
</button>
|
||||
</>
|
||||
</Authorized>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private get filteredDashboards(): Dashboard[] {
|
||||
const {dashboards} = this.props
|
||||
const {searchTerm} = this.state
|
||||
|
||||
return dashboards.filter(d =>
|
||||
d.name.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
)
|
||||
}
|
||||
|
||||
private get panelTitle(): string {
|
||||
const {dashboards} = this.props
|
||||
|
||||
if (dashboards === null) {
|
||||
return 'Loading Dashboards...'
|
||||
} else if (dashboards.length === 1) {
|
||||
return '1 Dashboard'
|
||||
}
|
||||
|
||||
return `${dashboards.length} Dashboards`
|
||||
}
|
||||
|
||||
private filterDashboards = (searchTerm: string): void => {
|
||||
this.setState({searchTerm})
|
||||
}
|
||||
|
||||
private showImportOverlay = (): void => {
|
||||
const {showOverlay, onImportDashboard, notify} = this.props
|
||||
const options = {
|
||||
dismissOnClickOutside: false,
|
||||
dismissOnEscape: false,
|
||||
}
|
||||
|
||||
showOverlay(
|
||||
<OverlayContext.Consumer>
|
||||
{({onDismissOverlay}) => (
|
||||
<ImportDashboardOverlay
|
||||
onDismissOverlay={onDismissOverlay}
|
||||
onImportDashboard={onImportDashboard}
|
||||
notify={notify}
|
||||
/>
|
||||
)}
|
||||
</OverlayContext.Consumer>,
|
||||
options
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = {
|
||||
showOverlay: showOverlayAction,
|
||||
}
|
||||
|
||||
export default connect(null, mapDispatchToProps)(DashboardsPageContents)
|
|
@ -1,116 +0,0 @@
|
|||
import React from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {Link} from 'react-router'
|
||||
import _ from 'lodash'
|
||||
|
||||
import Authorized, {EDITOR_ROLE} from 'src/auth/Authorized'
|
||||
|
||||
import ConfirmButton from 'shared/components/ConfirmButton'
|
||||
|
||||
const AuthorizedEmptyState = ({onCreateDashboard}) => (
|
||||
<div className="generic-empty-state">
|
||||
<h4 style={{marginTop: '90px'}}>
|
||||
Looks like you don’t have any dashboards
|
||||
</h4>
|
||||
<br />
|
||||
<button
|
||||
className="btn btn-sm btn-primary"
|
||||
onClick={onCreateDashboard}
|
||||
style={{marginBottom: '90px'}}
|
||||
>
|
||||
<span className="icon plus" /> Create Dashboard
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
|
||||
const unauthorizedEmptyState = (
|
||||
<div className="generic-empty-state">
|
||||
<h4 style={{margin: '90px 0'}}>Looks like you don’t have any dashboards</h4>
|
||||
</div>
|
||||
)
|
||||
|
||||
const DashboardsTable = ({
|
||||
dashboards,
|
||||
onDeleteDashboard,
|
||||
onCreateDashboard,
|
||||
onCloneDashboard,
|
||||
dashboardLink,
|
||||
}) => {
|
||||
return dashboards && dashboards.length ? (
|
||||
<table className="table v-center admin-table table-highlight">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Template Variables</th>
|
||||
<th />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{_.sortBy(dashboards, d => d.name.toLowerCase()).map(dashboard => (
|
||||
<tr key={dashboard.id}>
|
||||
<td>
|
||||
<Link to={`${dashboardLink}/dashboards/${dashboard.id}`}>
|
||||
{dashboard.name}
|
||||
</Link>
|
||||
</td>
|
||||
<td>
|
||||
{dashboard.templates.length ? (
|
||||
dashboard.templates.map(tv => (
|
||||
<code className="table--temp-var" key={tv.id}>
|
||||
{tv.tempVar}
|
||||
</code>
|
||||
))
|
||||
) : (
|
||||
<span className="empty-string">None</span>
|
||||
)}
|
||||
</td>
|
||||
<Authorized
|
||||
requiredRole={EDITOR_ROLE}
|
||||
replaceWithIfNotAuthorized={<td />}
|
||||
>
|
||||
<td className="text-right">
|
||||
<button
|
||||
className="btn btn-xs btn-default table--show-on-row-hover"
|
||||
onClick={onCloneDashboard(dashboard)}
|
||||
>
|
||||
<span className="icon duplicate" />
|
||||
Clone
|
||||
</button>
|
||||
<ConfirmButton
|
||||
confirmAction={onDeleteDashboard(dashboard)}
|
||||
size="btn-xs"
|
||||
type="btn-danger"
|
||||
text="Delete"
|
||||
customClass="table--show-on-row-hover"
|
||||
/>
|
||||
</td>
|
||||
</Authorized>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
) : (
|
||||
<Authorized
|
||||
requiredRole={EDITOR_ROLE}
|
||||
replaceWithIfNotAuthorized={unauthorizedEmptyState}
|
||||
>
|
||||
<AuthorizedEmptyState onCreateDashboard={onCreateDashboard} />
|
||||
</Authorized>
|
||||
)
|
||||
}
|
||||
|
||||
const {arrayOf, func, shape, string} = PropTypes
|
||||
|
||||
DashboardsTable.propTypes = {
|
||||
dashboards: arrayOf(shape()),
|
||||
onDeleteDashboard: func.isRequired,
|
||||
onCreateDashboard: func.isRequired,
|
||||
onCloneDashboard: func.isRequired,
|
||||
dashboardLink: string.isRequired,
|
||||
}
|
||||
|
||||
AuthorizedEmptyState.propTypes = {
|
||||
onCreateDashboard: func.isRequired,
|
||||
}
|
||||
|
||||
export default DashboardsTable
|
|
@ -0,0 +1,147 @@
|
|||
import React, {PureComponent, MouseEvent} from 'react'
|
||||
import {Link} from 'react-router'
|
||||
import _ from 'lodash'
|
||||
|
||||
import Authorized, {EDITOR_ROLE, VIEWER_ROLE} from 'src/auth/Authorized'
|
||||
import ConfirmButton from 'src/shared/components/ConfirmButton'
|
||||
|
||||
import {getDeep} from 'src/utils/wrappers'
|
||||
|
||||
import {Dashboard, Template} from 'src/types'
|
||||
|
||||
interface Props {
|
||||
dashboards: Dashboard[]
|
||||
onDeleteDashboard: (dashboard: Dashboard) => () => void
|
||||
onCreateDashboard: () => void
|
||||
onCloneDashboard: (
|
||||
dashboard: Dashboard
|
||||
) => (event: MouseEvent<HTMLButtonElement>) => void
|
||||
onExportDashboard: (dashboard: Dashboard) => () => void
|
||||
dashboardLink: string
|
||||
}
|
||||
|
||||
class DashboardsTable extends PureComponent<Props> {
|
||||
public render() {
|
||||
const {
|
||||
dashboards,
|
||||
dashboardLink,
|
||||
onCloneDashboard,
|
||||
onDeleteDashboard,
|
||||
onExportDashboard,
|
||||
} = this.props
|
||||
|
||||
if (!dashboards.length) {
|
||||
return this.emptyStateDashboard
|
||||
}
|
||||
|
||||
return (
|
||||
<table className="table v-center admin-table table-highlight">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Template Variables</th>
|
||||
<th />
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{_.sortBy(dashboards, d => d.name.toLowerCase()).map(dashboard => (
|
||||
<tr key={dashboard.id}>
|
||||
<td>
|
||||
<Link to={`${dashboardLink}/dashboards/${dashboard.id}`}>
|
||||
{dashboard.name}
|
||||
</Link>
|
||||
</td>
|
||||
<td>{this.getDashboardTemplates(dashboard)}</td>
|
||||
<td className="text-right">
|
||||
<Authorized
|
||||
requiredRole={VIEWER_ROLE}
|
||||
replaceWithIfNotAuthorized={<div />}
|
||||
>
|
||||
<button
|
||||
className="btn btn-xs btn-default table--show-on-row-hover"
|
||||
onClick={onExportDashboard(dashboard)}
|
||||
>
|
||||
<span className="icon export" />Export
|
||||
</button>
|
||||
</Authorized>
|
||||
<Authorized
|
||||
requiredRole={EDITOR_ROLE}
|
||||
replaceWithIfNotAuthorized={<div />}
|
||||
>
|
||||
<>
|
||||
<button
|
||||
className="btn btn-xs btn-default table--show-on-row-hover"
|
||||
onClick={onCloneDashboard(dashboard)}
|
||||
>
|
||||
<span className="icon duplicate" />
|
||||
Clone
|
||||
</button>
|
||||
<ConfirmButton
|
||||
confirmAction={onDeleteDashboard(dashboard)}
|
||||
size="btn-xs"
|
||||
type="btn-danger"
|
||||
text="Delete"
|
||||
customClass="table--show-on-row-hover"
|
||||
/>
|
||||
</>
|
||||
</Authorized>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)
|
||||
}
|
||||
|
||||
private getDashboardTemplates = (
|
||||
dashboard: Dashboard
|
||||
): JSX.Element | JSX.Element[] => {
|
||||
const templates = getDeep<Template[]>(dashboard, 'templates', [])
|
||||
|
||||
if (templates.length) {
|
||||
return templates.map(tv => (
|
||||
<code className="table--temp-var" key={tv.id}>
|
||||
{tv.tempVar}
|
||||
</code>
|
||||
))
|
||||
}
|
||||
|
||||
return <span className="empty-string">None</span>
|
||||
}
|
||||
|
||||
private get emptyStateDashboard(): JSX.Element {
|
||||
const {onCreateDashboard} = this.props
|
||||
return (
|
||||
<Authorized
|
||||
requiredRole={EDITOR_ROLE}
|
||||
replaceWithIfNotAuthorized={this.unauthorizedEmptyState}
|
||||
>
|
||||
<div className="generic-empty-state">
|
||||
<h4 style={{marginTop: '90px'}}>
|
||||
Looks like you don’t have any dashboards
|
||||
</h4>
|
||||
<br />
|
||||
<button
|
||||
className="btn btn-sm btn-primary"
|
||||
onClick={onCreateDashboard}
|
||||
style={{marginBottom: '90px'}}
|
||||
>
|
||||
<span className="icon plus" /> Create Dashboard
|
||||
</button>
|
||||
</div>
|
||||
</Authorized>
|
||||
)
|
||||
}
|
||||
|
||||
private get unauthorizedEmptyState(): JSX.Element {
|
||||
return (
|
||||
<div className="generic-empty-state">
|
||||
<h4 style={{margin: '90px 0'}}>
|
||||
Looks like you don’t have any dashboards
|
||||
</h4>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export default DashboardsTable
|
|
@ -72,12 +72,12 @@ class GaugeOptions extends Component {
|
|||
onResetFocus()
|
||||
}
|
||||
|
||||
handleChooseColor = threshold => chosenColor => {
|
||||
handleChooseColor = threshold => {
|
||||
const {handleUpdateGaugeColors} = this.props
|
||||
const gaugeColors = this.props.gaugeColors.map(
|
||||
color =>
|
||||
color.id === threshold.id
|
||||
? {...color, hex: chosenColor.hex, name: chosenColor.name}
|
||||
? {...color, hex: threshold.hex, name: threshold.name}
|
||||
: color
|
||||
)
|
||||
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
import _ from 'lodash'
|
||||
|
||||
import Container from 'src/shared/components/overlay/OverlayContainer'
|
||||
import Heading from 'src/shared/components/overlay/OverlayHeading'
|
||||
import Body from 'src/shared/components/overlay/OverlayBody'
|
||||
import DragAndDrop from 'src/shared/components/DragAndDrop'
|
||||
import {notifyDashboardImportFailed} from 'src/shared/copy/notifications'
|
||||
|
||||
import {Dashboard} from 'src/types'
|
||||
import {Notification} from 'src/types/notifications'
|
||||
|
||||
interface Props {
|
||||
onDismissOverlay: () => void
|
||||
onImportDashboard: (dashboard: Dashboard) => void
|
||||
notify: (message: Notification) => void
|
||||
}
|
||||
|
||||
interface State {
|
||||
isImportable: boolean
|
||||
}
|
||||
|
||||
class ImportDashboardOverlay extends PureComponent<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
isImportable: false,
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {onDismissOverlay} = this.props
|
||||
|
||||
return (
|
||||
<Container maxWidth={800}>
|
||||
<Heading title="Import Dashboard" onDismiss={onDismissOverlay} />
|
||||
<Body>
|
||||
<DragAndDrop
|
||||
submitText="Upload Dashboard"
|
||||
fileTypesToAccept={this.validFileExtension}
|
||||
handleSubmit={this.handleUploadDashboard}
|
||||
/>
|
||||
</Body>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
private get validFileExtension(): string {
|
||||
return '.json'
|
||||
}
|
||||
|
||||
private handleUploadDashboard = (
|
||||
uploadContent: string,
|
||||
fileName: string
|
||||
): void => {
|
||||
const {onImportDashboard, onDismissOverlay} = this.props
|
||||
const fileExtensionRegex = new RegExp(`${this.validFileExtension}$`)
|
||||
if (!fileName.match(fileExtensionRegex)) {
|
||||
this.props.notify(
|
||||
notifyDashboardImportFailed(fileName, 'Please import a JSON file')
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const {dashboard} = JSON.parse(uploadContent)
|
||||
|
||||
if (!_.isEmpty(dashboard)) {
|
||||
onImportDashboard(dashboard)
|
||||
} else {
|
||||
this.props.notify(
|
||||
notifyDashboardImportFailed(fileName, 'No dashboard found in file')
|
||||
)
|
||||
}
|
||||
} catch (error) {
|
||||
this.props.notify(notifyDashboardImportFailed(fileName, error))
|
||||
}
|
||||
|
||||
onDismissOverlay()
|
||||
}
|
||||
}
|
||||
|
||||
export default ImportDashboardOverlay
|
|
@ -100,8 +100,9 @@ type NewDefaultDashboard = Pick<
|
|||
cells: NewDefaultCell[]
|
||||
}
|
||||
>
|
||||
export const DEFAULT_DASHBOARD_NAME = 'Name This Dashboard'
|
||||
export const NEW_DASHBOARD: NewDefaultDashboard = {
|
||||
name: 'Name This Dashboard',
|
||||
name: DEFAULT_DASHBOARD_NAME,
|
||||
cells: [NEW_DEFAULT_DASHBOARD_CELL],
|
||||
}
|
||||
|
||||
|
@ -141,7 +142,15 @@ export const TEMPLATE_VARIABLE_TYPES = {
|
|||
tagValues: 'tagValue',
|
||||
}
|
||||
|
||||
export const TEMPLATE_VARIABLE_QUERIES = {
|
||||
interface TemplateVariableQueries {
|
||||
databases: string
|
||||
measurements: string
|
||||
fieldKeys: string
|
||||
tagKeys: string
|
||||
tagValues: string
|
||||
}
|
||||
|
||||
export const TEMPLATE_VARIABLE_QUERIES: TemplateVariableQueries = {
|
||||
databases: 'SHOW DATABASES',
|
||||
measurements: 'SHOW MEASUREMENTS ON :database:',
|
||||
fieldKeys: 'SHOW FIELD KEYS ON :database: FROM :measurement:',
|
||||
|
@ -172,7 +181,7 @@ export const removeUnselectedTemplateValues = templates => {
|
|||
|
||||
export const TYPE_QUERY_CONFIG: string = 'queryConfig'
|
||||
export const TYPE_SHIFTED: string = 'shifted queryConfig'
|
||||
export const TYPE_IFQL: string = 'ifql'
|
||||
export const TYPE_FLUX: string = 'flux'
|
||||
export const DASHBOARD_NAME_MAX_LENGTH: number = 50
|
||||
export const TEMPLATE_RANGE: TimeRange = {
|
||||
upper: null,
|
||||
|
|
|
@ -1,98 +0,0 @@
|
|||
import React, {Component} from 'react'
|
||||
import PropTypes from 'prop-types'
|
||||
import {withRouter} from 'react-router'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
|
||||
import DashboardsHeader from 'src/dashboards/components/DashboardsHeader'
|
||||
import DashboardsContents from 'src/dashboards/components/DashboardsPageContents'
|
||||
|
||||
import {createDashboard} from 'src/dashboards/apis'
|
||||
import {getDashboardsAsync, deleteDashboardAsync} from 'src/dashboards/actions'
|
||||
|
||||
import {NEW_DASHBOARD} from 'src/dashboards/constants'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
@ErrorHandling
|
||||
class DashboardsPage extends Component {
|
||||
componentDidMount() {
|
||||
this.props.handleGetDashboards()
|
||||
}
|
||||
|
||||
handleCreateDashboard = async () => {
|
||||
const {
|
||||
source: {id},
|
||||
router: {push},
|
||||
} = this.props
|
||||
const {data} = await createDashboard(NEW_DASHBOARD)
|
||||
push(`/sources/${id}/dashboards/${data.id}`)
|
||||
}
|
||||
|
||||
handleCloneDashboard = dashboard => async () => {
|
||||
const {
|
||||
source: {id},
|
||||
router: {push},
|
||||
} = this.props
|
||||
const {data} = await createDashboard({
|
||||
...dashboard,
|
||||
name: `${dashboard.name} (clone)`,
|
||||
})
|
||||
push(`/sources/${id}/dashboards/${data.id}`)
|
||||
}
|
||||
|
||||
handleDeleteDashboard = dashboard => () => {
|
||||
this.props.handleDeleteDashboard(dashboard)
|
||||
}
|
||||
|
||||
render() {
|
||||
const {dashboards} = this.props
|
||||
const dashboardLink = `/sources/${this.props.source.id}`
|
||||
|
||||
return (
|
||||
<div className="page">
|
||||
<DashboardsHeader sourceName={this.props.source.name} />
|
||||
<DashboardsContents
|
||||
dashboardLink={dashboardLink}
|
||||
dashboards={dashboards}
|
||||
onDeleteDashboard={this.handleDeleteDashboard}
|
||||
onCreateDashboard={this.handleCreateDashboard}
|
||||
onCloneDashboard={this.handleCloneDashboard}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const {arrayOf, func, string, shape} = PropTypes
|
||||
|
||||
DashboardsPage.propTypes = {
|
||||
source: shape({
|
||||
id: string.isRequired,
|
||||
name: string.isRequired,
|
||||
type: string,
|
||||
links: shape({
|
||||
proxy: string.isRequired,
|
||||
}).isRequired,
|
||||
telegraf: string.isRequired,
|
||||
}),
|
||||
router: shape({
|
||||
push: func.isRequired,
|
||||
}).isRequired,
|
||||
handleGetDashboards: func.isRequired,
|
||||
handleDeleteDashboard: func.isRequired,
|
||||
dashboards: arrayOf(shape()),
|
||||
}
|
||||
|
||||
const mapStateToProps = ({dashboardUI: {dashboards, dashboard}}) => ({
|
||||
dashboards,
|
||||
dashboard,
|
||||
})
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
handleGetDashboards: bindActionCreators(getDashboardsAsync, dispatch),
|
||||
handleDeleteDashboard: bindActionCreators(deleteDashboardAsync, dispatch),
|
||||
})
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(
|
||||
withRouter(DashboardsPage)
|
||||
)
|
|
@ -0,0 +1,146 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
import {withRouter, InjectedRouter} from 'react-router'
|
||||
import {connect} from 'react-redux'
|
||||
import download from 'src/external/download'
|
||||
import _ from 'lodash'
|
||||
|
||||
import DashboardsHeader from 'src/dashboards/components/DashboardsHeader'
|
||||
import DashboardsContents from 'src/dashboards/components/DashboardsPageContents'
|
||||
|
||||
import {createDashboard} from 'src/dashboards/apis'
|
||||
import {
|
||||
getDashboardsAsync,
|
||||
deleteDashboardAsync,
|
||||
getChronografVersion,
|
||||
importDashboardAsync,
|
||||
} from 'src/dashboards/actions'
|
||||
import {notify as notifyAction} from 'src/shared/actions/notifications'
|
||||
|
||||
import {NEW_DASHBOARD, DEFAULT_DASHBOARD_NAME} from 'src/dashboards/constants'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import {
|
||||
notifyDashboardExported,
|
||||
notifyDashboardExportFailed,
|
||||
} from 'src/shared/copy/notifications'
|
||||
|
||||
import {Source, Dashboard} from 'src/types'
|
||||
import {Notification} from 'src/types/notifications'
|
||||
import {DashboardFile} from 'src/types/dashboard'
|
||||
|
||||
interface Props {
|
||||
source: Source
|
||||
router: InjectedRouter
|
||||
handleGetDashboards: () => void
|
||||
handleGetChronografVersion: () => string
|
||||
handleDeleteDashboard: (dashboard: Dashboard) => void
|
||||
handleImportDashboard: (dashboard: Dashboard) => void
|
||||
notify: (message: Notification) => void
|
||||
dashboards: Dashboard[]
|
||||
}
|
||||
|
||||
@ErrorHandling
|
||||
class DashboardsPage extends PureComponent<Props> {
|
||||
public componentDidMount() {
|
||||
this.props.handleGetDashboards()
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {dashboards, notify} = this.props
|
||||
const dashboardLink = `/sources/${this.props.source.id}`
|
||||
|
||||
return (
|
||||
<div className="page">
|
||||
<DashboardsHeader />
|
||||
<DashboardsContents
|
||||
dashboardLink={dashboardLink}
|
||||
dashboards={dashboards}
|
||||
onDeleteDashboard={this.handleDeleteDashboard}
|
||||
onCreateDashboard={this.handleCreateDashboard}
|
||||
onCloneDashboard={this.handleCloneDashboard}
|
||||
onExportDashboard={this.handleExportDashboard}
|
||||
onImportDashboard={this.handleImportDashboard}
|
||||
notify={notify}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private handleCreateDashboard = async (): Promise<void> => {
|
||||
const {
|
||||
source: {id},
|
||||
router: {push},
|
||||
} = this.props
|
||||
const {data} = await createDashboard(NEW_DASHBOARD)
|
||||
push(`/sources/${id}/dashboards/${data.id}`)
|
||||
}
|
||||
|
||||
private handleCloneDashboard = (dashboard: Dashboard) => async (): Promise<
|
||||
void
|
||||
> => {
|
||||
const {
|
||||
source: {id},
|
||||
router: {push},
|
||||
} = this.props
|
||||
const {data} = await createDashboard({
|
||||
...dashboard,
|
||||
name: `${dashboard.name} (clone)`,
|
||||
})
|
||||
push(`/sources/${id}/dashboards/${data.id}`)
|
||||
}
|
||||
|
||||
private handleDeleteDashboard = (dashboard: Dashboard) => (): void => {
|
||||
this.props.handleDeleteDashboard(dashboard)
|
||||
}
|
||||
|
||||
private handleExportDashboard = (dashboard: Dashboard) => async (): Promise<
|
||||
void
|
||||
> => {
|
||||
const dashboardForDownload = await this.modifyDashboardForDownload(
|
||||
dashboard
|
||||
)
|
||||
try {
|
||||
download(
|
||||
JSON.stringify(dashboardForDownload, null, '\t'),
|
||||
`${dashboard.name}.json`,
|
||||
'text/plain'
|
||||
)
|
||||
this.props.notify(notifyDashboardExported(dashboard.name))
|
||||
} catch (error) {
|
||||
this.props.notify(notifyDashboardExportFailed(dashboard.name, error))
|
||||
}
|
||||
}
|
||||
|
||||
private modifyDashboardForDownload = async (
|
||||
dashboard: Dashboard
|
||||
): Promise<DashboardFile> => {
|
||||
const version = await this.props.handleGetChronografVersion()
|
||||
return {meta: {chronografVersion: version}, dashboard}
|
||||
}
|
||||
|
||||
private handleImportDashboard = async (
|
||||
dashboard: Dashboard
|
||||
): Promise<void> => {
|
||||
const name = _.get(dashboard, 'name', DEFAULT_DASHBOARD_NAME)
|
||||
await this.props.handleImportDashboard({
|
||||
...dashboard,
|
||||
name,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = ({dashboardUI: {dashboards, dashboard}}) => ({
|
||||
dashboards,
|
||||
dashboard,
|
||||
})
|
||||
|
||||
const mapDispatchToProps = {
|
||||
handleGetDashboards: getDashboardsAsync,
|
||||
handleDeleteDashboard: deleteDashboardAsync,
|
||||
handleGetChronografVersion: getChronografVersion,
|
||||
handleImportDashboard: importDashboardAsync,
|
||||
notify: notifyAction,
|
||||
}
|
||||
|
||||
export default withRouter(
|
||||
connect(mapStateToProps, mapDispatchToProps)(DashboardsPage)
|
||||
)
|
|
@ -46,6 +46,14 @@ export default function ui(state = initialState, action) {
|
|||
return {...state, ...newState}
|
||||
}
|
||||
|
||||
case 'CREATE_DASHBOARD': {
|
||||
const {dashboard} = action.payload
|
||||
const newState = {
|
||||
dashboards: [...state.dashboards, dashboard],
|
||||
}
|
||||
return {...state, ...newState}
|
||||
}
|
||||
|
||||
case 'DELETE_DASHBOARD': {
|
||||
const {dashboard} = action.payload
|
||||
const newState = {
|
||||
|
|
|
@ -1,4 +1,10 @@
|
|||
import {TEMPLATE_VARIABLE_QUERIES} from 'src/dashboards/constants'
|
||||
import {Template, TemplateQuery} from 'src/types/dashboard'
|
||||
|
||||
interface PartialTemplateWithQuery {
|
||||
query: string
|
||||
tempVars: Array<Partial<Template>>
|
||||
}
|
||||
|
||||
const generateTemplateVariableQuery = ({
|
||||
type,
|
||||
|
@ -8,7 +14,7 @@ const generateTemplateVariableQuery = ({
|
|||
measurement,
|
||||
tagKey,
|
||||
},
|
||||
}) => {
|
||||
}: Partial<Template>): PartialTemplateWithQuery => {
|
||||
const tempVars = []
|
||||
|
||||
if (database) {
|
||||
|
@ -45,7 +51,7 @@ const generateTemplateVariableQuery = ({
|
|||
})
|
||||
}
|
||||
|
||||
const query = TEMPLATE_VARIABLE_QUERIES[type]
|
||||
const query: string = TEMPLATE_VARIABLE_QUERIES[type]
|
||||
|
||||
return {
|
||||
query,
|
||||
|
@ -53,7 +59,12 @@ const generateTemplateVariableQuery = ({
|
|||
}
|
||||
}
|
||||
|
||||
export const makeQueryForTemplate = ({influxql, db, measurement, tagKey}) =>
|
||||
export const makeQueryForTemplate = ({
|
||||
influxql,
|
||||
db,
|
||||
measurement,
|
||||
tagKey,
|
||||
}: TemplateQuery): string =>
|
||||
influxql
|
||||
.replace(':database:', `"${db}"`)
|
||||
.replace(':measurement:', `"${measurement}"`)
|
|
@ -1,4 +1,4 @@
|
|||
import {modeIFQL, modeTickscript} from 'src/shared/constants/codeMirrorModes'
|
||||
import {modeFlux, modeTickscript} from 'src/shared/constants/codeMirrorModes'
|
||||
|
||||
/* eslint-disable */
|
||||
const CodeMirror = require('codemirror')
|
||||
|
@ -312,7 +312,7 @@ function indentFunction(states, meta) {
|
|||
}
|
||||
|
||||
// Modes
|
||||
CodeMirror.defineSimpleMode('ifql', modeIFQL)
|
||||
CodeMirror.defineSimpleMode('flux', modeFlux)
|
||||
CodeMirror.defineSimpleMode('tickscript', modeTickscript)
|
||||
|
||||
// CodeMirror Hints
|
||||
|
|
|
@ -3,7 +3,7 @@ import _ from 'lodash'
|
|||
import AJAX from 'src/utils/ajax'
|
||||
import {Service, FluxTable} from 'src/types'
|
||||
import {updateService} from 'src/shared/apis'
|
||||
import {parseResponse} from 'src/shared/parsing/v2/results'
|
||||
import {parseResponse} from 'src/shared/parsing/flux/response'
|
||||
|
||||
export const getSuggestions = async (url: string) => {
|
||||
try {
|
||||
|
@ -64,7 +64,7 @@ export const getTimeSeries = async (
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: replace with actual requests to IFQL daemon
|
||||
// TODO: replace with actual requests to Flux daemon
|
||||
export const getDatabases = async () => {
|
||||
try {
|
||||
const response = {data: {dbs: ['telegraf', 'chronograf', '_internal']}}
|
|
@ -5,7 +5,7 @@ import {
|
|||
FlatBody,
|
||||
BinaryExpressionNode,
|
||||
MemberExpressionNode,
|
||||
} from 'src/types/ifql'
|
||||
} from 'src/types/flux'
|
||||
|
||||
interface Expression {
|
||||
argument: object
|
|
@ -1,13 +1,13 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
import _ from 'lodash'
|
||||
|
||||
import ExpressionNode from 'src/ifql/components/ExpressionNode'
|
||||
import VariableName from 'src/ifql/components/VariableName'
|
||||
import FuncSelector from 'src/ifql/components/FuncSelector'
|
||||
import {funcNames} from 'src/ifql/constants'
|
||||
import ExpressionNode from 'src/flux/components/ExpressionNode'
|
||||
import VariableName from 'src/flux/components/VariableName'
|
||||
import FuncSelector from 'src/flux/components/FuncSelector'
|
||||
import {funcNames} from 'src/flux/constants'
|
||||
|
||||
import {Service} from 'src/types'
|
||||
import {FlatBody, Suggestion} from 'src/types/ifql'
|
||||
import {FlatBody, Suggestion} from 'src/types/flux'
|
||||
|
||||
interface Props {
|
||||
service: Service
|
|
@ -1,6 +1,6 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
|
||||
import DatabaseListItem from 'src/ifql/components/DatabaseListItem'
|
||||
import DatabaseListItem from 'src/flux/components/DatabaseListItem'
|
||||
|
||||
import {showDatabases} from 'src/shared/apis/metaQuery'
|
||||
import showDatabasesParser from 'src/shared/parsing/showDatabases'
|
|
@ -1,9 +1,9 @@
|
|||
import React, {PureComponent, ChangeEvent, MouseEvent} from 'react'
|
||||
import classnames from 'classnames'
|
||||
|
||||
import {tagKeys as fetchTagKeys} from 'src/shared/apis/v2/metaQueries'
|
||||
import parseValuesColumn from 'src/shared/parsing/v2/tags'
|
||||
import TagList from 'src/ifql/components/TagList'
|
||||
import {tagKeys as fetchTagKeys} from 'src/shared/apis/flux/metaQueries'
|
||||
import parseValuesColumn from 'src/shared/parsing/flux/values'
|
||||
import TagList from 'src/flux/components/TagList'
|
||||
import {Service} from 'src/types'
|
||||
|
||||
interface Props {
|
||||
|
@ -45,14 +45,14 @@ class DatabaseListItem extends PureComponent<Props, State> {
|
|||
|
||||
return (
|
||||
<div className={this.className} onClick={this.handleClick}>
|
||||
<div className="ifql-schema-item">
|
||||
<div className="ifql-schema-item-toggle" />
|
||||
<div className="flux-schema-item">
|
||||
<div className="flux-schema-item-toggle" />
|
||||
{db}
|
||||
<span className="ifql-schema-type">Bucket</span>
|
||||
<span className="flux-schema-type">Bucket</span>
|
||||
</div>
|
||||
{this.state.isOpen && (
|
||||
<>
|
||||
<div className="ifql-schema--filter">
|
||||
<div className="flux-schema--filter">
|
||||
<input
|
||||
className="form-control input-sm"
|
||||
placeholder={`Filter within ${db}`}
|
||||
|
@ -78,7 +78,7 @@ class DatabaseListItem extends PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
private get className(): string {
|
||||
return classnames('ifql-schema-tree', {
|
||||
return classnames('flux-schema-tree', {
|
||||
expanded: this.state.isOpen,
|
||||
})
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
|
||||
import {IFQLContext} from 'src/ifql/containers/IFQLPage'
|
||||
import FuncSelector from 'src/ifql/components/FuncSelector'
|
||||
import FuncNode from 'src/ifql/components/FuncNode'
|
||||
import {FluxContext} from 'src/flux/containers/FluxPage'
|
||||
import FuncSelector from 'src/flux/components/FuncSelector'
|
||||
import FuncNode from 'src/flux/components/FuncNode'
|
||||
|
||||
import {Func} from 'src/types/ifql'
|
||||
import {Func} from 'src/types/flux'
|
||||
|
||||
interface Props {
|
||||
funcNames: any[]
|
||||
|
@ -25,7 +25,7 @@ class ExpressionNode extends PureComponent<Props> {
|
|||
declarationsFromBody,
|
||||
} = this.props
|
||||
return (
|
||||
<IFQLContext.Consumer>
|
||||
<FluxContext.Consumer>
|
||||
{({
|
||||
onDeleteFuncNode,
|
||||
onAddNode,
|
||||
|
@ -57,7 +57,7 @@ class ExpressionNode extends PureComponent<Props> {
|
|||
</>
|
||||
)
|
||||
}}
|
||||
</IFQLContext.Consumer>
|
||||
</FluxContext.Consumer>
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
import {PureComponent, ReactNode} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
import {getAST} from 'src/ifql/apis'
|
||||
import {Links, BinaryExpressionNode, MemberExpressionNode} from 'src/types/ifql'
|
||||
import Walker from 'src/ifql/ast/walker'
|
||||
import {getAST} from 'src/flux/apis'
|
||||
import {Links, BinaryExpressionNode, MemberExpressionNode} from 'src/types/flux'
|
||||
import Walker from 'src/flux/ast/walker'
|
||||
|
||||
interface Props {
|
||||
value: string
|
||||
|
@ -41,7 +41,7 @@ export class Filter extends PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
const mapStateToProps = ({links}) => {
|
||||
return {links: links.ifql}
|
||||
return {links: links.flux}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, null)(Filter)
|
|
@ -1,5 +1,5 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
import {MemberExpressionNode} from 'src/types/ifql'
|
||||
import {MemberExpressionNode} from 'src/types/flux'
|
||||
|
||||
type FilterNode = MemberExpressionNode
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
import {BinaryExpressionNode, MemberExpressionNode} from 'src/types/ifql'
|
||||
import {BinaryExpressionNode, MemberExpressionNode} from 'src/types/flux'
|
||||
|
||||
type FilterNode = BinaryExpressionNode & MemberExpressionNode
|
||||
|
||||
|
@ -32,29 +32,29 @@ class FilterPreviewNode extends PureComponent<FilterPreviewNodeProps> {
|
|||
|
||||
switch (node.type) {
|
||||
case 'ObjectExpression': {
|
||||
return <div className="ifql-filter--key">{node.source}</div>
|
||||
return <div className="flux-filter--key">{node.source}</div>
|
||||
}
|
||||
case 'MemberExpression': {
|
||||
return <div className="ifql-filter--key">{node.property.name}</div>
|
||||
return <div className="flux-filter--key">{node.property.name}</div>
|
||||
}
|
||||
case 'OpenParen': {
|
||||
return <div className="ifql-filter--paren-open" />
|
||||
return <div className="flux-filter--paren-open" />
|
||||
}
|
||||
case 'CloseParen': {
|
||||
return <div className="ifql-filter--paren-close" />
|
||||
return <div className="flux-filter--paren-close" />
|
||||
}
|
||||
case 'NumberLiteral':
|
||||
case 'IntegerLiteral': {
|
||||
return <div className="ifql-filter--value number">{node.source}</div>
|
||||
return <div className="flux-filter--value number">{node.source}</div>
|
||||
}
|
||||
case 'BooleanLiteral': {
|
||||
return <div className="ifql-filter--value boolean">{node.source}</div>
|
||||
return <div className="flux-filter--value boolean">{node.source}</div>
|
||||
}
|
||||
case 'StringLiteral': {
|
||||
return <div className="ifql-filter--value string">{node.source}</div>
|
||||
return <div className="flux-filter--value string">{node.source}</div>
|
||||
}
|
||||
case 'Operator': {
|
||||
return <div className="ifql-filter--operator">{node.source}</div>
|
||||
return <div className="flux-filter--operator">{node.source}</div>
|
||||
}
|
||||
default: {
|
||||
return <div />
|
|
@ -1,9 +1,9 @@
|
|||
import React, {PureComponent, ChangeEvent, FormEvent} from 'react'
|
||||
|
||||
import IFQLForm from 'src/ifql/components/IFQLForm'
|
||||
import FluxForm from 'src/flux/components/FluxForm'
|
||||
|
||||
import {Service, Notification} from 'src/types'
|
||||
import {ifqlUpdated, ifqlNotUpdated} from 'src/shared/copy/notifications'
|
||||
import {fluxUpdated, fluxNotUpdated} from 'src/shared/copy/notifications'
|
||||
import {UpdateServiceAsync} from 'src/shared/actions/services'
|
||||
|
||||
interface Props {
|
||||
|
@ -17,7 +17,7 @@ interface State {
|
|||
service: Service
|
||||
}
|
||||
|
||||
class IFQLEdit extends PureComponent<Props, State> {
|
||||
class FluxEdit extends PureComponent<Props, State> {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
|
@ -27,7 +27,7 @@ class IFQLEdit extends PureComponent<Props, State> {
|
|||
|
||||
public render() {
|
||||
return (
|
||||
<IFQLForm
|
||||
<FluxForm
|
||||
service={this.state.service}
|
||||
onSubmit={this.handleSubmit}
|
||||
onInputChange={this.handleInputChange}
|
||||
|
@ -53,13 +53,13 @@ class IFQLEdit extends PureComponent<Props, State> {
|
|||
try {
|
||||
await updateService(service)
|
||||
} catch (error) {
|
||||
notify(ifqlNotUpdated(error.message))
|
||||
notify(fluxNotUpdated(error.message))
|
||||
return
|
||||
}
|
||||
|
||||
notify(ifqlUpdated)
|
||||
notify(fluxUpdated)
|
||||
onDismiss()
|
||||
}
|
||||
}
|
||||
|
||||
export default IFQLEdit
|
||||
export default FluxEdit
|
|
@ -11,7 +11,7 @@ interface Props {
|
|||
onInputChange: (e: ChangeEvent<HTMLInputElement>) => void
|
||||
}
|
||||
|
||||
class IFQLForm extends PureComponent<Props> {
|
||||
class FluxForm extends PureComponent<Props> {
|
||||
public render() {
|
||||
const {service, onSubmit, onInputChange} = this.props
|
||||
|
||||
|
@ -20,7 +20,7 @@ class IFQLForm extends PureComponent<Props> {
|
|||
<form onSubmit={onSubmit} style={{display: 'inline-block'}}>
|
||||
<Input
|
||||
name="url"
|
||||
label="IFQL URL"
|
||||
label="Flux URL"
|
||||
value={this.url}
|
||||
placeholder={this.url}
|
||||
onChange={onInputChange}
|
||||
|
@ -69,4 +69,4 @@ class IFQLForm extends PureComponent<Props> {
|
|||
}
|
||||
}
|
||||
|
||||
export default IFQLForm
|
||||
export default FluxForm
|
|
@ -1,7 +1,7 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
import {fluxTablesToDygraph} from 'src/shared/parsing/v2/dygraph'
|
||||
import {fluxTablesToDygraph} from 'src/shared/parsing/flux/dygraph'
|
||||
|
||||
import Dygraph from 'src/shared/components/Dygraph'
|
||||
import {FluxTable} from 'src/types'
|
|
@ -1,7 +1,7 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
import IFQLOverlay from 'src/ifql/components/IFQLOverlay'
|
||||
import FluxOverlay from 'src/flux/components/FluxOverlay'
|
||||
import {OverlayContext} from 'src/shared/components/OverlayTechnology'
|
||||
import {
|
||||
showOverlay as showOverlayAction,
|
||||
|
@ -16,7 +16,7 @@ interface Props {
|
|||
onGetTimeSeries: () => void
|
||||
}
|
||||
|
||||
class IFQLHeader extends PureComponent<Props> {
|
||||
class FluxHeader extends PureComponent<Props> {
|
||||
public render() {
|
||||
const {onGetTimeSeries} = this.props
|
||||
|
||||
|
@ -48,7 +48,7 @@ class IFQLHeader extends PureComponent<Props> {
|
|||
showOverlay(
|
||||
<OverlayContext.Consumer>
|
||||
{({onDismissOverlay}) => (
|
||||
<IFQLOverlay
|
||||
<FluxOverlay
|
||||
mode="edit"
|
||||
service={service}
|
||||
onDismiss={onDismissOverlay}
|
||||
|
@ -64,4 +64,4 @@ const mdtp = {
|
|||
showOverlay: showOverlayAction,
|
||||
}
|
||||
|
||||
export default connect(null, mdtp)(IFQLHeader)
|
||||
export default connect(null, mdtp)(FluxHeader)
|
|
@ -1,9 +1,9 @@
|
|||
import React, {PureComponent, ChangeEvent, FormEvent} from 'react'
|
||||
|
||||
import IFQLForm from 'src/ifql/components/IFQLForm'
|
||||
import FluxForm from 'src/flux/components/FluxForm'
|
||||
|
||||
import {NewService, Source, Notification} from 'src/types'
|
||||
import {ifqlCreated, ifqlNotCreated} from 'src/shared/copy/notifications'
|
||||
import {fluxCreated, fluxNotCreated} from 'src/shared/copy/notifications'
|
||||
import {CreateServiceAsync} from 'src/shared/actions/services'
|
||||
|
||||
interface Props {
|
||||
|
@ -19,7 +19,7 @@ interface State {
|
|||
|
||||
const port = 8093
|
||||
|
||||
class IFQLNew extends PureComponent<Props, State> {
|
||||
class FluxNew extends PureComponent<Props, State> {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
|
@ -29,7 +29,7 @@ class IFQLNew extends PureComponent<Props, State> {
|
|||
|
||||
public render() {
|
||||
return (
|
||||
<IFQLForm
|
||||
<FluxForm
|
||||
service={this.state.service}
|
||||
onSubmit={this.handleSubmit}
|
||||
onInputChange={this.handleInputChange}
|
||||
|
@ -56,21 +56,21 @@ class IFQLNew extends PureComponent<Props, State> {
|
|||
try {
|
||||
await createService(source, service)
|
||||
} catch (error) {
|
||||
notify(ifqlNotCreated(error.message))
|
||||
notify(fluxNotCreated(error.message))
|
||||
return
|
||||
}
|
||||
|
||||
notify(ifqlCreated)
|
||||
notify(fluxCreated)
|
||||
onDismiss()
|
||||
}
|
||||
|
||||
private get defaultService(): NewService {
|
||||
return {
|
||||
name: 'IFQL',
|
||||
name: 'Flux',
|
||||
url: this.url,
|
||||
username: '',
|
||||
insecureSkipVerify: false,
|
||||
type: 'ifql',
|
||||
type: 'flux',
|
||||
active: true,
|
||||
}
|
||||
}
|
||||
|
@ -83,4 +83,4 @@ class IFQLNew extends PureComponent<Props, State> {
|
|||
}
|
||||
}
|
||||
|
||||
export default IFQLNew
|
||||
export default FluxNew
|
|
@ -1,8 +1,8 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
|
||||
import IFQLNew from 'src/ifql/components/IFQLNew'
|
||||
import IFQLEdit from 'src/ifql/components/IFQLEdit'
|
||||
import FluxNew from 'src/flux/components/FluxNew'
|
||||
import FluxEdit from 'src/flux/components/FluxEdit'
|
||||
|
||||
import {Service, Source, Notification} from 'src/types'
|
||||
|
||||
|
@ -24,13 +24,13 @@ interface Props {
|
|||
updateService: UpdateServiceAsync
|
||||
}
|
||||
|
||||
class IFQLOverlay extends PureComponent<Props> {
|
||||
class FluxOverlay extends PureComponent<Props> {
|
||||
public render() {
|
||||
return (
|
||||
<div className="ifql-overlay">
|
||||
<div className="flux-overlay">
|
||||
<div className="template-variable-manager--header">
|
||||
<div className="page-header__left">
|
||||
<h1 className="page-header__title">Connect to IFQL</h1>
|
||||
<h1 className="page-header__title">Connect to Flux</h1>
|
||||
</div>
|
||||
<div className="page-header__right">
|
||||
<span
|
||||
|
@ -57,7 +57,7 @@ class IFQLOverlay extends PureComponent<Props> {
|
|||
|
||||
if (mode === 'new') {
|
||||
return (
|
||||
<IFQLNew
|
||||
<FluxNew
|
||||
source={source}
|
||||
notify={notify}
|
||||
onDismiss={onDismiss}
|
||||
|
@ -67,7 +67,7 @@ class IFQLOverlay extends PureComponent<Props> {
|
|||
}
|
||||
|
||||
return (
|
||||
<IFQLEdit
|
||||
<FluxEdit
|
||||
notify={notify}
|
||||
service={service}
|
||||
onDismiss={onDismiss}
|
||||
|
@ -83,4 +83,4 @@ const mdtp = {
|
|||
updateService: updateServiceAsync,
|
||||
}
|
||||
|
||||
export default connect(null, mdtp)(IFQLOverlay)
|
||||
export default connect(null, mdtp)(FluxOverlay)
|
|
@ -4,7 +4,7 @@ import {showDatabases} from 'src/shared/apis/metaQuery'
|
|||
import showDatabasesParser from 'src/shared/parsing/showDatabases'
|
||||
|
||||
import Dropdown from 'src/shared/components/Dropdown'
|
||||
import {OnChangeArg} from 'src/types/ifql'
|
||||
import {OnChangeArg} from 'src/types/flux'
|
||||
import {Service} from 'src/types'
|
||||
|
||||
interface Props {
|
|
@ -1,13 +1,13 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
|
||||
import FuncArgInput from 'src/ifql/components/FuncArgInput'
|
||||
import FuncArgTextArea from 'src/ifql/components/FuncArgTextArea'
|
||||
import FuncArgBool from 'src/ifql/components/FuncArgBool'
|
||||
import FuncArgInput from 'src/flux/components/FuncArgInput'
|
||||
import FuncArgTextArea from 'src/flux/components/FuncArgTextArea'
|
||||
import FuncArgBool from 'src/flux/components/FuncArgBool'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import From from 'src/ifql/components/From'
|
||||
import From from 'src/flux/components/From'
|
||||
|
||||
import {funcNames, argTypes} from 'src/ifql/constants'
|
||||
import {OnChangeArg} from 'src/types/ifql'
|
||||
import {funcNames, argTypes} from 'src/flux/constants'
|
||||
import {OnChangeArg} from 'src/types/flux'
|
||||
import {Service} from 'src/types'
|
||||
|
||||
interface Props {
|
|
@ -1,7 +1,7 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
import SlideToggle from 'src/shared/components/SlideToggle'
|
||||
|
||||
import {OnChangeArg} from 'src/types/ifql'
|
||||
import {OnChangeArg} from 'src/types/flux'
|
||||
|
||||
interface Props {
|
||||
argKey: string
|
|
@ -1,6 +1,6 @@
|
|||
import React, {PureComponent, ChangeEvent, KeyboardEvent} from 'react'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import {OnChangeArg} from 'src/types/ifql'
|
||||
import {OnChangeArg} from 'src/types/flux'
|
||||
|
||||
interface Props {
|
||||
funcID: string
|
|
@ -1,6 +1,6 @@
|
|||
import React, {PureComponent, ChangeEvent, KeyboardEvent} from 'react'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import {OnChangeArg} from 'src/types/ifql'
|
||||
import {OnChangeArg} from 'src/types/flux'
|
||||
|
||||
interface Props {
|
||||
funcID: string
|
|
@ -1,10 +1,10 @@
|
|||
import React, {PureComponent, ReactElement} from 'react'
|
||||
import FuncArg from 'src/ifql/components/FuncArg'
|
||||
import {OnChangeArg} from 'src/types/ifql'
|
||||
import FuncArg from 'src/flux/components/FuncArg'
|
||||
import {OnChangeArg} from 'src/types/flux'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import {Func} from 'src/types/ifql'
|
||||
import {funcNames} from 'src/ifql/constants'
|
||||
import Join from 'src/ifql/components/Join'
|
||||
import {Func} from 'src/types/flux'
|
||||
import {funcNames} from 'src/flux/constants'
|
||||
import Join from 'src/flux/components/Join'
|
||||
import {Service} from 'src/types'
|
||||
|
||||
interface Props {
|
|
@ -2,10 +2,10 @@ import React, {PureComponent} from 'react'
|
|||
import uuid from 'uuid'
|
||||
import _ from 'lodash'
|
||||
|
||||
import {Func} from 'src/types/ifql'
|
||||
import {funcNames} from 'src/ifql/constants'
|
||||
import Filter from 'src/ifql/components/Filter'
|
||||
import FilterPreview from 'src/ifql/components/FilterPreview'
|
||||
import {Func} from 'src/types/flux'
|
||||
import {funcNames} from 'src/flux/constants'
|
||||
import Filter from 'src/flux/components/Filter'
|
||||
import FilterPreview from 'src/flux/components/FilterPreview'
|
||||
|
||||
import {getDeep} from 'src/utils/wrappers'
|
||||
|
|
@ -2,7 +2,7 @@ import React, {SFC, ChangeEvent, KeyboardEvent} from 'react'
|
|||
|
||||
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
|
||||
import FuncSelectorInput from 'src/shared/components/FuncSelectorInput'
|
||||
import FuncListItem from 'src/ifql/components/FuncListItem'
|
||||
import FuncListItem from 'src/flux/components/FuncListItem'
|
||||
|
||||
interface Props {
|
||||
inputText: string
|
||||
|
@ -24,13 +24,13 @@ const FuncList: SFC<Props> = ({
|
|||
onSetSelectedFunc,
|
||||
}) => {
|
||||
return (
|
||||
<div className="ifql-func--autocomplete">
|
||||
<div className="flux-func--autocomplete">
|
||||
<FuncSelectorInput
|
||||
onFilterChange={onInputChange}
|
||||
onFilterKeyPress={onKeyDown}
|
||||
searchTerm={inputText}
|
||||
/>
|
||||
<ul className="ifql-func--list">
|
||||
<ul className="flux-func--list">
|
||||
<FancyScrollbar
|
||||
autoHide={false}
|
||||
autoHeight={true}
|
||||
|
@ -48,7 +48,7 @@ const FuncList: SFC<Props> = ({
|
|||
/>
|
||||
))
|
||||
) : (
|
||||
<div className="ifql-func--item empty">No matches</div>
|
||||
<div className="flux-func--item empty">No matches</div>
|
||||
)}
|
||||
</FancyScrollbar>
|
||||
</ul>
|
|
@ -15,7 +15,7 @@ export default class FuncListItem extends PureComponent<Props> {
|
|||
<li
|
||||
onClick={this.handleClick}
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
className={`ifql-func--item ${this.activeClass}`}
|
||||
className={`flux-func--item ${this.activeClass}`}
|
||||
>
|
||||
{this.props.name}
|
||||
</li>
|
|
@ -1,8 +1,8 @@
|
|||
import React, {PureComponent, MouseEvent} from 'react'
|
||||
|
||||
import FuncArgs from 'src/ifql/components/FuncArgs'
|
||||
import FuncArgsPreview from 'src/ifql/components/FuncArgsPreview'
|
||||
import {OnDeleteFuncNode, OnChangeArg, Func} from 'src/types/ifql'
|
||||
import FuncArgs from 'src/flux/components/FuncArgs'
|
||||
import FuncArgsPreview from 'src/flux/components/FuncArgsPreview'
|
||||
import {OnDeleteFuncNode, OnChangeArg, Func} from 'src/types/flux'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import {Service} from 'src/types'
|
||||
|
|
@ -3,8 +3,8 @@ import _ from 'lodash'
|
|||
import classnames from 'classnames'
|
||||
|
||||
import {ClickOutside} from 'src/shared/components/ClickOutside'
|
||||
import FuncList from 'src/ifql/components/FuncList'
|
||||
import {OnAddNode} from 'src/types/ifql'
|
||||
import FuncList from 'src/flux/components/FuncList'
|
||||
import {OnAddNode} from 'src/types/flux'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
interface State {
|
||||
|
@ -57,7 +57,7 @@ export class FuncSelector extends PureComponent<Props, State> {
|
|||
/>
|
||||
) : (
|
||||
<button
|
||||
className="btn btn-square btn-primary btn-sm ifql-func--button"
|
||||
className="btn btn-square btn-primary btn-sm flux-func--button"
|
||||
onClick={this.handleOpenList}
|
||||
tabIndex={0}
|
||||
>
|
||||
|
@ -72,7 +72,7 @@ export class FuncSelector extends PureComponent<Props, State> {
|
|||
private get className(): string {
|
||||
const {isOpen} = this.state
|
||||
|
||||
return classnames('ifql-func--selector', {open: isOpen})
|
||||
return classnames('flux-func--selector', {open: isOpen})
|
||||
}
|
||||
|
||||
private handleCloseList = () => {
|
|
@ -2,12 +2,12 @@ import React, {PureComponent} from 'react'
|
|||
import _ from 'lodash'
|
||||
|
||||
import Dropdown from 'src/shared/components/Dropdown'
|
||||
import FuncArgInput from 'src/ifql/components/FuncArgInput'
|
||||
import FuncArgTextArea from 'src/ifql/components/FuncArgTextArea'
|
||||
import FuncArgInput from 'src/flux/components/FuncArgInput'
|
||||
import FuncArgTextArea from 'src/flux/components/FuncArgTextArea'
|
||||
import {getDeep} from 'src/utils/wrappers'
|
||||
|
||||
import {OnChangeArg, Func, Arg} from 'src/types/ifql'
|
||||
import {argTypes} from 'src/ifql/constants'
|
||||
import {OnChangeArg, Func, Arg} from 'src/types/flux'
|
||||
import {argTypes} from 'src/flux/constants'
|
||||
|
||||
interface Props {
|
||||
func: Func
|
|
@ -0,0 +1,32 @@
|
|||
import React, {SFC, MouseEvent} from 'react'
|
||||
|
||||
const handleClick = (e: MouseEvent<HTMLDivElement>): void => {
|
||||
e.stopPropagation()
|
||||
}
|
||||
|
||||
const LoaderSkeleton: SFC = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="flux-schema-tree flux-tree-node" onClick={handleClick}>
|
||||
<div className="flux-schema-item no-hover">
|
||||
<div className="flux-schema-item-toggle" />
|
||||
<div className="flux-schema-item-skeleton" style={{width: '160px'}} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flux-schema-tree flux-tree-node">
|
||||
<div className="flux-schema-item no-hover">
|
||||
<div className="flux-schema-item-toggle" />
|
||||
<div className="flux-schema-item-skeleton" style={{width: '200px'}} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flux-schema-tree flux-tree-node">
|
||||
<div className="flux-schema-item no-hover">
|
||||
<div className="flux-schema-item-toggle" />
|
||||
<div className="flux-schema-item-skeleton" style={{width: '120px'}} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default LoaderSkeleton
|
|
@ -1,6 +1,6 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
|
||||
import DatabaseList from 'src/ifql/components/DatabaseList'
|
||||
import DatabaseList from 'src/flux/components/DatabaseList'
|
||||
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
|
||||
import {Service} from 'src/types'
|
||||
|
||||
|
@ -12,7 +12,7 @@ class SchemaExplorer extends PureComponent<Props> {
|
|||
public render() {
|
||||
const {service} = this.props
|
||||
return (
|
||||
<div className="ifql-schema-explorer">
|
||||
<div className="flux-schema-explorer">
|
||||
<FancyScrollbar>
|
||||
<DatabaseList service={service} />
|
||||
</FancyScrollbar>
|
|
@ -14,10 +14,10 @@ export default class SchemaItem extends PureComponent<Props, State> {
|
|||
const {schemaType} = this.props
|
||||
return (
|
||||
<div className={this.className}>
|
||||
<div className="ifql-schema-item" onClick={this.handleClick}>
|
||||
<div className="ifql-schema-item-toggle" />
|
||||
<div className="flux-schema-item" onClick={this.handleClick}>
|
||||
<div className="flux-schema-item-toggle" />
|
||||
{name}
|
||||
<span className="ifql-schema-type">{schemaType}</span>
|
||||
<span className="flux-schema-type">{schemaType}</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
@ -32,6 +32,6 @@ export default class SchemaItem extends PureComponent<Props, State> {
|
|||
const {isOpen} = this.state
|
||||
const openClass = isOpen ? 'expanded' : ''
|
||||
|
||||
return `ifql-schema-tree ifql-tree-node ${openClass}`
|
||||
return `flux-schema-tree flux-tree-node ${openClass}`
|
||||
}
|
||||
}
|
|
@ -4,8 +4,8 @@ import _ from 'lodash'
|
|||
import {FluxTable} from 'src/types'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
|
||||
import TableSidebarItem from 'src/ifql/components/TableSidebarItem'
|
||||
import {vis} from 'src/ifql/constants'
|
||||
import TableSidebarItem from 'src/flux/components/TableSidebarItem'
|
||||
import {vis} from 'src/flux/constants'
|
||||
|
||||
interface Props {
|
||||
data: FluxTable[]
|
|
@ -1,7 +1,7 @@
|
|||
import React, {PureComponent, MouseEvent} from 'react'
|
||||
|
||||
import {SchemaFilter, Service} from 'src/types'
|
||||
import TagListItem from 'src/ifql/components/TagListItem'
|
||||
import TagListItem from 'src/flux/components/TagListItem'
|
||||
|
||||
interface Props {
|
||||
db: string
|
||||
|
@ -41,8 +41,8 @@ export default class TagList extends PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
return (
|
||||
<div className="ifql-schema-tree ifql-tree-node">
|
||||
<div className="ifql-schema-item no-hover" onClick={this.handleClick}>
|
||||
<div className="flux-schema-tree flux-tree-node">
|
||||
<div className="flux-schema-item no-hover" onClick={this.handleClick}>
|
||||
<div className="no-results">No more tag keys.</div>
|
||||
</div>
|
||||
</div>
|
|
@ -8,12 +8,12 @@ import React, {
|
|||
import _ from 'lodash'
|
||||
|
||||
import {Service, SchemaFilter, RemoteDataState} from 'src/types'
|
||||
import {tagValues as fetchTagValues} from 'src/shared/apis/v2/metaQueries'
|
||||
import {explorer} from 'src/ifql/constants'
|
||||
import parseValuesColumn from 'src/shared/parsing/v2/tags'
|
||||
import TagValueList from 'src/ifql/components/TagValueList'
|
||||
import LoaderSkeleton from 'src/ifql/components/LoaderSkeleton'
|
||||
import LoadingSpinner from 'src/ifql/components/LoadingSpinner'
|
||||
import {tagValues as fetchTagValues} from 'src/shared/apis/flux/metaQueries'
|
||||
import {explorer} from 'src/flux/constants'
|
||||
import parseValuesColumn from 'src/shared/parsing/flux/values'
|
||||
import TagValueList from 'src/flux/components/TagValueList'
|
||||
import LoaderSkeleton from 'src/flux/components/LoaderSkeleton'
|
||||
import LoadingSpinner from 'src/flux/components/LoadingSpinner'
|
||||
|
||||
interface Props {
|
||||
tagKey: string
|
||||
|
@ -60,10 +60,10 @@ export default class TagListItem extends PureComponent<Props, State> {
|
|||
|
||||
return (
|
||||
<div className={this.className}>
|
||||
<div className="ifql-schema-item" onClick={this.handleClick}>
|
||||
<div className="ifql-schema-item-toggle" />
|
||||
<div className="flux-schema-item" onClick={this.handleClick}>
|
||||
<div className="flux-schema-item-toggle" />
|
||||
{tagKey}
|
||||
<span className="ifql-schema-type">Tag Key</span>
|
||||
<span className="flux-schema-type">Tag Key</span>
|
||||
</div>
|
||||
{this.state.isOpen && (
|
||||
<>
|
||||
|
@ -71,7 +71,7 @@ export default class TagListItem extends PureComponent<Props, State> {
|
|||
className="tag-value-list--header"
|
||||
onClick={this.handleInputClick}
|
||||
>
|
||||
<div className="ifql-schema--filter">
|
||||
<div className="flux-schema--filter">
|
||||
<input
|
||||
className="form-control input-sm"
|
||||
placeholder={`Filter within ${tagKey}`}
|
||||
|
@ -237,9 +237,9 @@ export default class TagListItem extends PureComponent<Props, State> {
|
|||
const parsed = parseValuesColumn(response)
|
||||
|
||||
if (parsed.length !== 1) {
|
||||
// We expect to never reach this state; instead, the IFQL server should
|
||||
// We expect to never reach this state; instead, the Flux server should
|
||||
// return a non-200 status code is handled earlier (after fetching).
|
||||
// This return guards against some unexpected behavior---the IFQL server
|
||||
// This return guards against some unexpected behavior---the Flux server
|
||||
// returning a 200 status code but ALSO having an error in the CSV
|
||||
// response body
|
||||
return
|
||||
|
@ -273,6 +273,6 @@ export default class TagListItem extends PureComponent<Props, State> {
|
|||
const {isOpen} = this.state
|
||||
const openClass = isOpen ? 'expanded' : ''
|
||||
|
||||
return `ifql-schema-tree ifql-tree-node ${openClass}`
|
||||
return `flux-schema-tree flux-tree-node ${openClass}`
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import React, {PureComponent, MouseEvent} from 'react'
|
||||
|
||||
import TagValueListItem from 'src/ifql/components/TagValueListItem'
|
||||
import LoadingSpinner from 'src/ifql/components/LoadingSpinner'
|
||||
import TagValueListItem from 'src/flux/components/TagValueListItem'
|
||||
import LoadingSpinner from 'src/flux/components/LoadingSpinner'
|
||||
import {Service, SchemaFilter} from 'src/types'
|
||||
|
||||
interface Props {
|
||||
|
@ -40,8 +40,8 @@ export default class TagValueList extends PureComponent<Props> {
|
|||
/>
|
||||
))}
|
||||
{shouldShowMoreValues && (
|
||||
<div className="ifql-schema-tree ifql-tree-node">
|
||||
<div className="ifql-schema-item no-hover">
|
||||
<div className="flux-schema-tree flux-tree-node">
|
||||
<div className="flux-schema-item no-hover">
|
||||
<button
|
||||
className="btn btn-xs btn-default increase-values-limit"
|
||||
onClick={this.handleClick}
|
|
@ -1,9 +1,9 @@
|
|||
import React, {PureComponent, MouseEvent, ChangeEvent} from 'react'
|
||||
|
||||
import {tagKeys as fetchTagKeys} from 'src/shared/apis/v2/metaQueries'
|
||||
import parseValuesColumn from 'src/shared/parsing/v2/tags'
|
||||
import TagList from 'src/ifql/components/TagList'
|
||||
import LoaderSkeleton from 'src/ifql/components/LoaderSkeleton'
|
||||
import {tagKeys as fetchTagKeys} from 'src/shared/apis/flux/metaQueries'
|
||||
import parseValuesColumn from 'src/shared/parsing/flux/values'
|
||||
import TagList from 'src/flux/components/TagList'
|
||||
import LoaderSkeleton from 'src/flux/components/LoaderSkeleton'
|
||||
import {Service, SchemaFilter, RemoteDataState} from 'src/types'
|
||||
|
||||
interface Props {
|
||||
|
@ -38,10 +38,10 @@ class TagValueListItem extends PureComponent<Props, State> {
|
|||
|
||||
return (
|
||||
<div className={this.className} onClick={this.handleClick}>
|
||||
<div className="ifql-schema-item">
|
||||
<div className="ifql-schema-item-toggle" />
|
||||
<div className="flux-schema-item">
|
||||
<div className="flux-schema-item-toggle" />
|
||||
{value}
|
||||
<span className="ifql-schema-type">Tag Value</span>
|
||||
<span className="flux-schema-type">Tag Value</span>
|
||||
</div>
|
||||
{this.state.isOpen && (
|
||||
<>
|
||||
|
@ -49,7 +49,7 @@ class TagValueListItem extends PureComponent<Props, State> {
|
|||
{!this.isLoading && (
|
||||
<>
|
||||
{!!this.tags.length && (
|
||||
<div className="ifql-schema--filter">
|
||||
<div className="flux-schema--filter">
|
||||
<input
|
||||
className="form-control input-sm"
|
||||
placeholder={`Filter within ${value}`}
|
||||
|
@ -110,7 +110,7 @@ class TagValueListItem extends PureComponent<Props, State> {
|
|||
const {isOpen} = this.state
|
||||
const openClass = isOpen ? 'expanded' : ''
|
||||
|
||||
return `ifql-schema-tree ifql-tree-node ${openClass}`
|
||||
return `flux-schema-tree flux-tree-node ${openClass}`
|
||||
}
|
||||
|
||||
private handleInputClick = (e: MouseEvent<HTMLInputElement>) => {
|
|
@ -1,8 +1,8 @@
|
|||
import React, {PureComponent, CSSProperties} from 'react'
|
||||
import SchemaExplorer from 'src/ifql/components/SchemaExplorer'
|
||||
import BodyBuilder from 'src/ifql/components/BodyBuilder'
|
||||
import TimeMachineEditor from 'src/ifql/components/TimeMachineEditor'
|
||||
import TimeMachineVis from 'src/ifql/components/TimeMachineVis'
|
||||
import SchemaExplorer from 'src/flux/components/SchemaExplorer'
|
||||
import BodyBuilder from 'src/flux/components/BodyBuilder'
|
||||
import TimeMachineEditor from 'src/flux/components/TimeMachineEditor'
|
||||
import TimeMachineVis from 'src/flux/components/TimeMachineVis'
|
||||
import Threesizer from 'src/shared/components/threesizer/Threesizer'
|
||||
import {
|
||||
Suggestion,
|
||||
|
@ -11,7 +11,7 @@ import {
|
|||
FlatBody,
|
||||
ScriptStatus,
|
||||
FluxTable,
|
||||
} from 'src/types/ifql'
|
||||
} from 'src/types/flux'
|
||||
|
||||
import {Service} from 'src/types'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
|
@ -3,8 +3,8 @@ import {Controlled as CodeMirror, IInstance} from 'react-codemirror2'
|
|||
import {EditorChange} from 'codemirror'
|
||||
import 'src/external/codemirror'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import {OnChangeScript, OnSubmitScript} from 'src/types/ifql'
|
||||
import {editor} from 'src/ifql/constants'
|
||||
import {OnChangeScript, OnSubmitScript} from 'src/types/flux'
|
||||
import {editor} from 'src/flux/constants'
|
||||
|
||||
interface Gutter {
|
||||
line: number
|
||||
|
@ -66,7 +66,7 @@ class TimeMachineEditor extends PureComponent<Props> {
|
|||
extraKeys: {'Ctrl-Space': 'autocomplete'},
|
||||
completeSingle: false,
|
||||
autoRefresh: true,
|
||||
mode: 'ifql',
|
||||
mode: 'flux',
|
||||
gutters: ['error-gutter'],
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ import {Grid, GridCellProps, AutoSizer, ColumnSizer} from 'react-virtualized'
|
|||
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import {FluxTable} from 'src/types'
|
||||
import {vis} from 'src/ifql/constants'
|
||||
import {vis} from 'src/flux/constants'
|
||||
|
||||
const NUM_FIXED_ROWS = 1
|
||||
|
|
@ -4,10 +4,10 @@ import _ from 'lodash'
|
|||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import {FluxTable} from 'src/types'
|
||||
import VisHeaderTabs from 'src/data_explorer/components/VisHeaderTabs'
|
||||
import TableSidebar from 'src/ifql/components/TableSidebar'
|
||||
import TimeMachineTable from 'src/ifql/components/TimeMachineTable'
|
||||
import FluxGraph from 'src/ifql/components/FluxGraph'
|
||||
import NoResults from 'src/ifql/components/NoResults'
|
||||
import TableSidebar from 'src/flux/components/TableSidebar'
|
||||
import TimeMachineTable from 'src/flux/components/TimeMachineTable'
|
||||
import FluxGraph from 'src/flux/components/FluxGraph'
|
||||
import NoResults from 'src/flux/components/NoResults'
|
||||
|
||||
interface Props {
|
||||
data: FluxTable[]
|
|
@ -0,0 +1,9 @@
|
|||
import {ast} from 'src/flux/constants/ast'
|
||||
import * as editor from 'src/flux/constants/editor'
|
||||
import * as argTypes from 'src/flux/constants/argumentTypes'
|
||||
import * as funcNames from 'src/flux/constants/funcNames'
|
||||
import * as builder from 'src/flux/constants/builder'
|
||||
import * as vis from 'src/flux/constants/vis'
|
||||
import * as explorer from 'src/flux/constants/explorer'
|
||||
|
||||
export {ast, funcNames, argTypes, editor, builder, vis, explorer}
|
|
@ -2,16 +2,16 @@ import React, {PureComponent, ReactChildren} from 'react'
|
|||
import {connect} from 'react-redux'
|
||||
import {WithRouterProps} from 'react-router'
|
||||
|
||||
import {IFQLPage} from 'src/ifql'
|
||||
import IFQLOverlay from 'src/ifql/components/IFQLOverlay'
|
||||
import {FluxPage} from 'src/flux'
|
||||
import FluxOverlay from 'src/flux/components/FluxOverlay'
|
||||
import {OverlayContext} from 'src/shared/components/OverlayTechnology'
|
||||
import {Source, Service, Notification} from 'src/types'
|
||||
import {Links} from 'src/types/ifql'
|
||||
import {Links} from 'src/types/flux'
|
||||
import {notify as notifyAction} from 'src/shared/actions/notifications'
|
||||
import {
|
||||
updateScript as updateScriptAction,
|
||||
UpdateScript,
|
||||
} from 'src/ifql/actions'
|
||||
} from 'src/flux/actions'
|
||||
import * as a from 'src/shared/actions/overlayTechnology'
|
||||
import * as b from 'src/shared/actions/services'
|
||||
|
||||
|
@ -54,7 +54,7 @@ export class CheckServices extends PureComponent<Props & WithRouterProps> {
|
|||
}
|
||||
|
||||
return (
|
||||
<IFQLPage
|
||||
<FluxPage
|
||||
source={this.source}
|
||||
services={services}
|
||||
links={links}
|
||||
|
@ -81,7 +81,7 @@ export class CheckServices extends PureComponent<Props & WithRouterProps> {
|
|||
showOverlay(
|
||||
<OverlayContext.Consumer>
|
||||
{({onDismissOverlay}) => (
|
||||
<IFQLOverlay
|
||||
<FluxOverlay
|
||||
mode="new"
|
||||
source={this.source}
|
||||
onDismiss={onDismissOverlay}
|
||||
|
@ -102,7 +102,7 @@ const mdtp = {
|
|||
|
||||
const mstp = ({sources, services, links, script}) => {
|
||||
return {
|
||||
links: links.ifql,
|
||||
links: links.flux,
|
||||
script,
|
||||
sources,
|
||||
services,
|
|
@ -1,20 +1,20 @@
|
|||
import React, {PureComponent} from 'react'
|
||||
import _ from 'lodash'
|
||||
|
||||
import TimeMachine from 'src/ifql/components/TimeMachine'
|
||||
import IFQLHeader from 'src/ifql/components/IFQLHeader'
|
||||
import TimeMachine from 'src/flux/components/TimeMachine'
|
||||
import FluxHeader from 'src/flux/components/FluxHeader'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import KeyboardShortcuts from 'src/shared/components/KeyboardShortcuts'
|
||||
|
||||
import {
|
||||
analyzeSuccess,
|
||||
ifqlTimeSeriesError,
|
||||
fluxTimeSeriesError,
|
||||
} from 'src/shared/copy/notifications'
|
||||
import {UpdateScript} from 'src/ifql/actions'
|
||||
import {UpdateScript} from 'src/flux/actions'
|
||||
|
||||
import {bodyNodes} from 'src/ifql/helpers'
|
||||
import {getSuggestions, getAST, getTimeSeries} from 'src/ifql/apis'
|
||||
import {builder, argTypes} from 'src/ifql/constants'
|
||||
import {bodyNodes} from 'src/flux/helpers'
|
||||
import {getSuggestions, getAST, getTimeSeries} from 'src/flux/apis'
|
||||
import {builder, argTypes} from 'src/flux/constants'
|
||||
|
||||
import {Source, Service, Notification, FluxTable} from 'src/types'
|
||||
import {
|
||||
|
@ -26,7 +26,7 @@ import {
|
|||
DeleteFuncNodeArgs,
|
||||
Func,
|
||||
ScriptStatus,
|
||||
} from 'src/types/ifql'
|
||||
} from 'src/types/flux'
|
||||
|
||||
interface Status {
|
||||
type: string
|
||||
|
@ -54,10 +54,10 @@ interface State {
|
|||
suggestions: Suggestion[]
|
||||
}
|
||||
|
||||
export const IFQLContext = React.createContext()
|
||||
export const FluxContext = React.createContext()
|
||||
|
||||
@ErrorHandling
|
||||
export class IFQLPage extends PureComponent<Props, State> {
|
||||
export class FluxPage extends PureComponent<Props, State> {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
|
@ -90,7 +90,7 @@ export class IFQLPage extends PureComponent<Props, State> {
|
|||
const {script} = this.props
|
||||
|
||||
return (
|
||||
<IFQLContext.Provider value={this.getContext}>
|
||||
<FluxContext.Provider value={this.getContext}>
|
||||
<KeyboardShortcuts onControlEnter={this.getTimeSeries}>
|
||||
<div className="page hosts-list-page">
|
||||
{this.header}
|
||||
|
@ -109,7 +109,7 @@ export class IFQLPage extends PureComponent<Props, State> {
|
|||
/>
|
||||
</div>
|
||||
</KeyboardShortcuts>
|
||||
</IFQLContext.Provider>
|
||||
</FluxContext.Provider>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -121,7 +121,7 @@ export class IFQLPage extends PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
return (
|
||||
<IFQLHeader service={this.service} onGetTimeSeries={this.getTimeSeries} />
|
||||
<FluxHeader service={this.service} onGetTimeSeries={this.getTimeSeries} />
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -371,7 +371,7 @@ export class IFQLPage extends PureComponent<Props, State> {
|
|||
return `${source}\n\n`
|
||||
}
|
||||
|
||||
// funcsToSource takes a list of funtion nodes and returns an ifql script
|
||||
// funcsToSource takes a list of funtion nodes and returns an flux script
|
||||
private funcsToSource = (funcs): string => {
|
||||
return funcs.reduce((acc, f, i) => {
|
||||
if (i === 0) {
|
||||
|
@ -437,7 +437,7 @@ export class IFQLPage extends PureComponent<Props, State> {
|
|||
} catch (error) {
|
||||
this.setState({data: []})
|
||||
|
||||
notify(ifqlTimeSeriesError(error))
|
||||
notify(fluxTimeSeriesError(error))
|
||||
console.error('Could not get timeSeries', error)
|
||||
}
|
||||
|
||||
|
@ -451,4 +451,4 @@ export class IFQLPage extends PureComponent<Props, State> {
|
|||
}
|
||||
}
|
||||
|
||||
export default IFQLPage
|
||||
export default FluxPage
|
|
@ -1,9 +1,9 @@
|
|||
import uuid from 'uuid'
|
||||
import _ from 'lodash'
|
||||
|
||||
import Walker from 'src/ifql/ast/walker'
|
||||
import Walker from 'src/flux/ast/walker'
|
||||
|
||||
import {FlatBody, Func, Suggestion} from 'src/types/ifql'
|
||||
import {FlatBody, Func, Suggestion} from 'src/types/flux'
|
||||
|
||||
interface Body extends FlatBody {
|
||||
id: string
|
|
@ -0,0 +1,4 @@
|
|||
import FluxPage from 'src/flux/containers/FluxPage'
|
||||
import CheckServices from 'src/flux/containers/CheckServices'
|
||||
|
||||
export {FluxPage, CheckServices}
|
|
@ -1,5 +1,5 @@
|
|||
import {Action, ActionTypes} from 'src/ifql/actions'
|
||||
import {editor} from 'src/ifql/constants'
|
||||
import {Action, ActionTypes} from 'src/flux/actions'
|
||||
import {editor} from 'src/flux/constants'
|
||||
|
||||
const scriptReducer = (
|
||||
state: string = editor.DEFAULT_SCRIPT,
|
|
@ -1,32 +0,0 @@
|
|||
import React, {SFC, MouseEvent} from 'react'
|
||||
|
||||
const handleClick = (e: MouseEvent<HTMLDivElement>): void => {
|
||||
e.stopPropagation()
|
||||
}
|
||||
|
||||
const LoaderSkeleton: SFC = () => {
|
||||
return (
|
||||
<>
|
||||
<div className="ifql-schema-tree ifql-tree-node" onClick={handleClick}>
|
||||
<div className="ifql-schema-item no-hover">
|
||||
<div className="ifql-schema-item-toggle" />
|
||||
<div className="ifql-schema-item-skeleton" style={{width: '160px'}} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="ifql-schema-tree ifql-tree-node">
|
||||
<div className="ifql-schema-item no-hover">
|
||||
<div className="ifql-schema-item-toggle" />
|
||||
<div className="ifql-schema-item-skeleton" style={{width: '200px'}} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="ifql-schema-tree ifql-tree-node">
|
||||
<div className="ifql-schema-item no-hover">
|
||||
<div className="ifql-schema-item-toggle" />
|
||||
<div className="ifql-schema-item-skeleton" style={{width: '120px'}} />
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default LoaderSkeleton
|
|
@ -1,9 +0,0 @@
|
|||
import {ast} from 'src/ifql/constants/ast'
|
||||
import * as editor from 'src/ifql/constants/editor'
|
||||
import * as argTypes from 'src/ifql/constants/argumentTypes'
|
||||
import * as funcNames from 'src/ifql/constants/funcNames'
|
||||
import * as builder from 'src/ifql/constants/builder'
|
||||
import * as vis from 'src/ifql/constants/vis'
|
||||
import * as explorer from 'src/ifql/constants/explorer'
|
||||
|
||||
export {ast, funcNames, argTypes, editor, builder, vis, explorer}
|
|
@ -1,4 +0,0 @@
|
|||
import IFQLPage from 'src/ifql/containers/IFQLPage'
|
||||
import CheckServices from 'src/ifql/containers/CheckServices'
|
||||
|
||||
export {IFQLPage, CheckServices}
|
|
@ -36,7 +36,7 @@ import {
|
|||
} from 'src/kapacitor'
|
||||
import {AdminChronografPage, AdminInfluxDBPage} from 'src/admin'
|
||||
import {SourcePage, ManageSources} from 'src/sources'
|
||||
import {CheckServices} from 'src/ifql'
|
||||
import {CheckServices} from 'src/flux'
|
||||
import NotFound from 'src/shared/components/NotFound'
|
||||
|
||||
import {getLinksAsync} from 'src/shared/actions/links'
|
||||
|
|
|
@ -322,7 +322,7 @@ export const createService = async (
|
|||
source: Source,
|
||||
{
|
||||
url,
|
||||
name = 'My IFQLD',
|
||||
name = 'My FluxD',
|
||||
type,
|
||||
username,
|
||||
password,
|
||||
|
|
|
@ -0,0 +1,216 @@
|
|||
import React, {PureComponent, ReactElement, DragEvent} from 'react'
|
||||
import classnames from 'classnames'
|
||||
// import {notifyDashboardUploadFailed} from 'src/shared/copy/notifications'
|
||||
|
||||
interface Props {
|
||||
fileTypesToAccept?: string
|
||||
containerClass?: string
|
||||
handleSubmit: (uploadContent: string, fileName: string) => void
|
||||
submitText?: string
|
||||
}
|
||||
|
||||
interface State {
|
||||
inputContent: string | null
|
||||
uploadContent: string
|
||||
fileName: string
|
||||
progress: string
|
||||
dragClass: string
|
||||
}
|
||||
|
||||
let dragCounter = 0
|
||||
class DragAndDrop extends PureComponent<Props, State> {
|
||||
public static defaultProps: Partial<Props> = {
|
||||
submitText: 'Write this File',
|
||||
}
|
||||
|
||||
private fileInput: HTMLInputElement
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
|
||||
this.state = {
|
||||
inputContent: null,
|
||||
uploadContent: '',
|
||||
fileName: '',
|
||||
progress: '',
|
||||
dragClass: 'drag-none',
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
return (
|
||||
<div className={this.containerClass}>
|
||||
{/* (Invisible, covers entire screen)
|
||||
This div handles drag only*/}
|
||||
<div
|
||||
onDrop={this.handleFile(true)}
|
||||
onDragOver={this.handleDragOver}
|
||||
onDragEnter={this.handleDragEnter}
|
||||
onDragExit={this.handleDragLeave}
|
||||
onDragLeave={this.handleDragLeave}
|
||||
className="drag-and-drop--dropzone"
|
||||
/>
|
||||
{/* visible form, handles drag & click */}
|
||||
{this.dragArea}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private get dragArea(): ReactElement<HTMLDivElement> {
|
||||
return (
|
||||
<div
|
||||
className={this.dragAreaClass}
|
||||
onClick={this.handleFileOpen}
|
||||
onDrop={this.handleFile(true)}
|
||||
onDragOver={this.handleDragOver}
|
||||
onDragEnter={this.handleDragEnter}
|
||||
onDragExit={this.handleDragLeave}
|
||||
onDragLeave={this.handleDragLeave}
|
||||
>
|
||||
{this.dragAreaHeader}
|
||||
<div className={this.infoClass} />
|
||||
<input
|
||||
type="file"
|
||||
ref={r => (this.fileInput = r)}
|
||||
className="drag-and-drop--input"
|
||||
accept={this.fileTypesToAccept}
|
||||
onChange={this.handleFile(false)}
|
||||
/>
|
||||
{this.buttons}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private get fileTypesToAccept(): string {
|
||||
const {fileTypesToAccept} = this.props
|
||||
|
||||
if (!fileTypesToAccept) {
|
||||
return '*'
|
||||
}
|
||||
|
||||
return fileTypesToAccept
|
||||
}
|
||||
|
||||
private get containerClass(): string {
|
||||
const {dragClass} = this.state
|
||||
|
||||
return `drag-and-drop ${dragClass}`
|
||||
}
|
||||
|
||||
private get infoClass(): string {
|
||||
const {uploadContent} = this.state
|
||||
|
||||
return classnames('drag-and-drop--graphic', {success: uploadContent})
|
||||
}
|
||||
|
||||
private get dragAreaClass(): string {
|
||||
const {uploadContent} = this.state
|
||||
|
||||
return classnames('drag-and-drop--form', {active: !uploadContent})
|
||||
}
|
||||
|
||||
private get dragAreaHeader(): ReactElement<HTMLHeadElement> {
|
||||
const {uploadContent, fileName} = this.state
|
||||
|
||||
if (uploadContent) {
|
||||
return <div className="drag-and-drop--header selected">{fileName}</div>
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="drag-and-drop--header empty">
|
||||
Drop a file here or click to upload
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private get buttons(): ReactElement<HTMLSpanElement> | null {
|
||||
const {uploadContent} = this.state
|
||||
const {submitText} = this.props
|
||||
|
||||
if (!uploadContent) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<span className="drag-and-drop--buttons">
|
||||
<button className="btn btn-sm btn-success" onClick={this.handleSubmit}>
|
||||
{submitText}
|
||||
</button>
|
||||
<button
|
||||
className="btn btn-sm btn-default"
|
||||
onClick={this.handleCancelFile}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
private handleSubmit = () => {
|
||||
const {handleSubmit} = this.props
|
||||
const {uploadContent, fileName} = this.state
|
||||
|
||||
handleSubmit(uploadContent, fileName)
|
||||
}
|
||||
|
||||
private handleFile = (drop: boolean) => (e: any): void => {
|
||||
let file
|
||||
if (drop) {
|
||||
file = e.dataTransfer.files[0]
|
||||
this.setState({
|
||||
dragClass: 'drag-none',
|
||||
})
|
||||
} else {
|
||||
file = e.currentTarget.files[0]
|
||||
}
|
||||
|
||||
if (!file) {
|
||||
return
|
||||
}
|
||||
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
const reader = new FileReader()
|
||||
reader.readAsText(file)
|
||||
reader.onload = loadEvent => {
|
||||
this.setState({
|
||||
uploadContent: loadEvent.target.result,
|
||||
fileName: file.name,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private handleFileOpen = (): void => {
|
||||
const {uploadContent} = this.state
|
||||
if (uploadContent === '') {
|
||||
this.fileInput.click()
|
||||
}
|
||||
}
|
||||
|
||||
private handleCancelFile = (): void => {
|
||||
this.setState({uploadContent: ''})
|
||||
this.fileInput.value = ''
|
||||
}
|
||||
|
||||
private handleDragOver = (e: DragEvent<HTMLDivElement>) => {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
}
|
||||
|
||||
private handleDragEnter = (e: DragEvent<HTMLDivElement>): void => {
|
||||
dragCounter += 1
|
||||
e.preventDefault()
|
||||
this.setState({dragClass: 'drag-over'})
|
||||
}
|
||||
|
||||
private handleDragLeave = (e: DragEvent<HTMLDivElement>): void => {
|
||||
dragCounter -= 1
|
||||
e.preventDefault()
|
||||
if (dragCounter === 0) {
|
||||
this.setState({dragClass: 'drag-none'})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default DragAndDrop
|
|
@ -15,7 +15,7 @@ const FuncSelectorInput: SFC<Props> = ({
|
|||
onFilterKeyPress,
|
||||
}) => (
|
||||
<input
|
||||
className="form-control input-sm ifql-func--input"
|
||||
className="form-control input-sm flux-func--input"
|
||||
type="text"
|
||||
autoFocus={true}
|
||||
placeholder="Add a Function..."
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import React, {PureComponent, ComponentClass} from 'react'
|
||||
import React, {PureComponent, Component} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
import {bindActionCreators} from 'redux'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
|
||||
import {dismissOverlay} from 'src/shared/actions/overlayTechnology'
|
||||
|
||||
interface Props {
|
||||
OverlayNode?: ComponentClass<any>
|
||||
OverlayNode?: Component<any>
|
||||
dismissOnClickOutside?: boolean
|
||||
dismissOnEscape?: boolean
|
||||
transitionTime?: number
|
||||
|
@ -98,8 +97,8 @@ const mapStateToProps = ({
|
|||
transitionTime,
|
||||
})
|
||||
|
||||
const mapDispatchToProps = dispatch => ({
|
||||
handleDismissOverlay: bindActionCreators(dismissOverlay, dispatch),
|
||||
})
|
||||
const mapDispatchToProps = {
|
||||
handleDismissOverlay: dismissOverlay,
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Overlay)
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import React, {SFC, ReactNode} from 'react'
|
||||
|
||||
interface Props {
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
const OverlayBody: SFC<Props> = ({children}) => (
|
||||
<div className="overlay--body">{children}</div>
|
||||
)
|
||||
|
||||
export default OverlayBody
|
|
@ -0,0 +1,30 @@
|
|||
import React, {Component, ReactNode, CSSProperties} from 'react'
|
||||
|
||||
interface Props {
|
||||
children: ReactNode
|
||||
maxWidth?: number
|
||||
}
|
||||
|
||||
class OverlayContainer extends Component<Props> {
|
||||
public static defaultProps: Partial<Props> = {
|
||||
maxWidth: 600,
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {children} = this.props
|
||||
|
||||
return (
|
||||
<div className="overlay--container" style={this.style}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
private get style(): CSSProperties {
|
||||
const {maxWidth} = this.props
|
||||
|
||||
return {maxWidth: `${maxWidth}px`}
|
||||
}
|
||||
}
|
||||
|
||||
export default OverlayContainer
|
|
@ -0,0 +1,28 @@
|
|||
import React, {PureComponent, ReactChildren} from 'react'
|
||||
|
||||
interface Props {
|
||||
children?: ReactChildren
|
||||
title: string
|
||||
onDismiss?: () => void
|
||||
}
|
||||
|
||||
class OverlayHeading extends PureComponent<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {title, onDismiss, children} = this.props
|
||||
|
||||
return (
|
||||
<div className="overlay--heading">
|
||||
<div className="overlay--title">{title}</div>
|
||||
{onDismiss && (
|
||||
<button className="overlay--dismiss" onClick={onDismiss} />
|
||||
)}
|
||||
{children && children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
export default OverlayHeading
|
|
@ -1,7 +1,7 @@
|
|||
export const modeIFQL = {
|
||||
export const modeFlux = {
|
||||
// The start state contains the rules that are intially used
|
||||
start: [
|
||||
// IFQL Syntax
|
||||
// Flux Syntax
|
||||
{
|
||||
regex: /[|][>]/,
|
||||
token: 'pipe-forward',
|
||||
|
|
|
@ -414,6 +414,30 @@ export const notifyDashboardDeleted = name => ({
|
|||
message: `Dashboard ${name} deleted successfully.`,
|
||||
})
|
||||
|
||||
export const notifyDashboardExported = name => ({
|
||||
...defaultSuccessNotification,
|
||||
icon: 'dash-h',
|
||||
message: `Dashboard ${name} exported successfully.`,
|
||||
})
|
||||
|
||||
export const notifyDashboardExportFailed = (name, errorMessage) => ({
|
||||
...defaultErrorNotification,
|
||||
duration: INFINITE,
|
||||
message: `Failed to export Dashboard ${name}: ${errorMessage}.`,
|
||||
})
|
||||
|
||||
export const notifyDashboardImported = name => ({
|
||||
...defaultSuccessNotification,
|
||||
icon: 'dash-h',
|
||||
message: `Dashboard ${name} imported successfully.`,
|
||||
})
|
||||
|
||||
export const notifyDashboardImportFailed = (fileName, errorMessage) => ({
|
||||
...defaultErrorNotification,
|
||||
duration: INFINITE,
|
||||
message: `Failed to import Dashboard from file ${fileName}: ${errorMessage}.`,
|
||||
})
|
||||
|
||||
export const notifyDashboardDeleteFailed = (name, errorMessage) =>
|
||||
`Failed to delete Dashboard ${name}: ${errorMessage}.`
|
||||
|
||||
|
@ -609,7 +633,7 @@ export const notifyKapacitorNotFound = () => ({
|
|||
message: 'We could not find a Kapacitor configuration for this source.',
|
||||
})
|
||||
|
||||
// IFQL notifications
|
||||
// Flux notifications
|
||||
export const analyzeSuccess = {
|
||||
...defaultSuccessNotification,
|
||||
message: 'No errors found. Happy Happy Joy Joy!',
|
||||
|
@ -621,27 +645,27 @@ export const couldNotGetServices = {
|
|||
message: 'We could not get services',
|
||||
}
|
||||
|
||||
export const ifqlCreated = {
|
||||
export const fluxCreated = {
|
||||
...defaultSuccessNotification,
|
||||
message: 'IFQL Connection Created. Script your heart out!',
|
||||
message: 'Flux Connection Created. Script your heart out!',
|
||||
}
|
||||
|
||||
export const ifqlNotCreated = (message: string) => ({
|
||||
export const fluxNotCreated = (message: string) => ({
|
||||
...defaultErrorNotification,
|
||||
message,
|
||||
})
|
||||
|
||||
export const ifqlNotUpdated = (message: string) => ({
|
||||
export const fluxNotUpdated = (message: string) => ({
|
||||
...defaultErrorNotification,
|
||||
message,
|
||||
})
|
||||
|
||||
export const ifqlUpdated = {
|
||||
export const fluxUpdated = {
|
||||
...defaultSuccessNotification,
|
||||
message: 'Connection Updated. Rejoice!',
|
||||
}
|
||||
|
||||
export const ifqlTimeSeriesError = (message: string) => ({
|
||||
export const fluxTimeSeriesError = (message: string) => ({
|
||||
...defaultErrorNotification,
|
||||
message: `Could not get data: ${message}`,
|
||||
})
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import _ from 'lodash'
|
||||
|
||||
import {FluxTable} from 'src/types'
|
||||
import {parseResponse} from 'src/shared/parsing/v2/results'
|
||||
import {parseResponse} from 'src/shared/parsing/flux/response'
|
||||
|
||||
const parseValuesColumn = (resp: string): string[] => {
|
||||
const results = parseResponse(resp)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue