Merge branch 'master' into ifql/join

ifql/join
ebb-tide 2018-05-23 14:28:33 -07:00
commit 9902cf295d
72 changed files with 1579 additions and 629 deletions

View File

@ -61,6 +61,7 @@
1. [#3166](https://github.com/influxdata/chronograf/pull/3166): Fixes naming of new TICKScripts
1. [#3449](https://github.com/influxdata/chronograf/pull/3449): Fixes data explorer query error reporting regression
1. [#3453](https://github.com/influxdata/chronograf/pull/3453): Fix Kapacitor Logs fetch regression
1. [#3500](https://github.com/influxdata/chronograf/pull/3500): Fix switching sources for a dashboard cell
## v1.4.4.1 [2018-04-16]

View File

@ -140,7 +140,7 @@ type Talk struct{}
// Kafka sends alerts to any Kafka brokers specified in the handler config
type Kafka struct {
Cluster string `json:"cluster"`
Topic string `json:"topic"`
Topic string `json:"kafka-topic"`
Template string `json:"template"`
}

View File

@ -128,9 +128,12 @@ export class CheckSources extends Component<Props, State> {
const source = sources.find(s => s.id === params.sourceID)
const defaultSource = sources.find(s => s.default === true)
const role = _.get(this.props, 'auth.me.role', '')
const nextRole = _.get(nextProps, 'auth.me.role', '')
if (
isUserAuthorized(this.props.auth.me.role, ADMIN_ROLE) &&
!isUserAuthorized(nextProps.auth.me.role, ADMIN_ROLE)
isUserAuthorized(role, ADMIN_ROLE) &&
!isUserAuthorized(nextRole, ADMIN_ROLE)
) {
return router.push('/')
}

View File

@ -2,6 +2,7 @@ import React, {Component} from 'react'
import _ from 'lodash'
import uuid from 'uuid'
import {getDeep} from 'src/utils/wrappers'
import ResizeContainer from 'src/shared/components/ResizeContainer'
import QueryMaker from 'src/dashboards/components/QueryMaker'
@ -16,7 +17,6 @@ import defaultQueryConfig from 'src/utils/defaultQueryConfig'
import {buildQuery} from 'src/utils/influxql'
import {getQueryConfigAndStatus} from 'src/shared/apis'
import {IS_STATIC_LEGEND} from 'src/shared/constants'
import {ColorString, ColorNumber} from 'src/types/colors'
import {nextSource} from 'src/dashboards/utils/sources'
import {
@ -43,6 +43,9 @@ import {
Legend,
Status,
} from 'src/types'
import {ColorString, ColorNumber} from 'src/types/colors'
import {SourceOption} from 'src/dashboards/components/OverlayControls'
type QueryTransitions = typeof queryTransitions
type EditRawTextAsyncFunc = (
url: string,
@ -96,37 +99,29 @@ interface State {
isStaticLegend: boolean
}
const createWorkingDraft = (
sourceLink: string,
query: CellQuery
): QueryConfig => {
const createWorkingDraft = (source: Source, query: CellQuery): QueryConfig => {
const {queryConfig} = query
const draft: QueryConfig = {
...queryConfig,
id: uuid.v4(),
sourceLink,
source,
}
return draft
}
const createWorkingDrafts = (
sourceLink: string,
source: Source,
queries: CellQuery[]
): QueryConfig[] =>
_.cloneDeep(
queries.map((query: CellQuery) => createWorkingDraft(sourceLink, query))
queries.map((query: CellQuery) => createWorkingDraft(source, query))
)
@ErrorHandling
class CellEditorOverlay extends Component<Props, State> {
private overlayRef: HTMLDivElement
private formattedSources = this.props.sources.map(s => ({
...s,
text: `${s.name} @ ${s.url}`,
}))
constructor(props) {
super(props)
@ -142,7 +137,7 @@ class CellEditorOverlay extends Component<Props, State> {
queries = [{id: uuid.v4()}]
}
const queriesWorkingDraft = createWorkingDrafts(this.sourceLink, queries)
const queriesWorkingDraft = createWorkingDrafts(this.initialSource, queries)
this.state = {
queriesWorkingDraft,
@ -254,6 +249,14 @@ class CellEditorOverlay extends Component<Props, State> {
)
}
private get formattedSources(): SourceOption[] {
const {sources} = this.props
return sources.map(s => ({
...s,
text: `${s.name} @ ${s.url}`,
}))
}
private onRef = (r: HTMLDivElement) => {
this.overlayRef = r
}
@ -284,7 +287,7 @@ class CellEditorOverlay extends Component<Props, State> {
this.setState({
queriesWorkingDraft: [
...queriesWorkingDraft,
{...defaultQueryConfig({id: uuid.v4()}), sourceLink: null},
{...defaultQueryConfig({id: uuid.v4()}), source: this.initialSource},
],
})
this.handleSetActiveQueryIndex(newIndex)
@ -301,13 +304,13 @@ class CellEditorOverlay extends Component<Props, State> {
const {queriesWorkingDraft, isStaticLegend} = this.state
const {cell, thresholdsListColors, gaugeColors, lineColors} = this.props
const queries = queriesWorkingDraft.map(q => {
const queries: CellQuery[] = queriesWorkingDraft.map(q => {
const timeRange = q.range || {upper: null, lower: TEMP_VAR_DASHBOARD_TIME}
const source = getDeep<string | null>(q.source, 'links.self', null)
return {
queryConfig: q,
query: q.rawText || buildQuery(TYPE_QUERY_CONFIG, timeRange, q),
sourceLink: q.sourceLink,
source,
}
})
@ -318,12 +321,14 @@ class CellEditorOverlay extends Component<Props, State> {
lineColors,
})
this.props.onSave({
const newCell: Cell = {
...cell,
queries,
colors,
legend: isStaticLegend ? staticLegend : {},
})
}
this.props.onSave(newCell)
}
private handleClickDisplayOptionsTab = isDisplayOptionsTabActive => () => {
@ -342,10 +347,9 @@ class CellEditorOverlay extends Component<Props, State> {
const queriesWorkingDraft: QueryConfig[] = this.state.queriesWorkingDraft.map(
q => ({
..._.cloneDeep(q),
sourceLink: source.links.self,
source,
})
)
this.setState({queriesWorkingDraft})
}
@ -427,7 +431,7 @@ class CellEditorOverlay extends Component<Props, State> {
return {
...config.queryConfig,
sourceLink: q.sourceLink,
source: q.source,
isQuerySupportedByExplorer,
}
}
@ -445,14 +449,20 @@ class CellEditorOverlay extends Component<Props, State> {
private findSelectedSource = (): string => {
const {source} = this.props
const sources = this.formattedSources
const currentSource = _.get(this.state.queriesWorkingDraft, '0.sourceLink')
const currentSource = getDeep<Source | null>(
this.state.queriesWorkingDraft,
'0.source',
null
)
if (!currentSource) {
const defaultSource = sources.find(s => s.id === source.id)
const defaultSource: Source = sources.find(s => s.id === source.id)
return (defaultSource && defaultSource.text) || 'No sources'
}
const selected = sources.find(s => s.links.self === currentSource)
const selected: Source = sources.find(
s => s.links.self === currentSource.links.self
)
return (selected && selected.text) || 'No sources'
}
@ -514,23 +524,42 @@ class CellEditorOverlay extends Component<Props, State> {
return result
}
private get sourceLink(): string {
private get initialSource(): Source {
const {
cell: {queries},
source: {links},
source,
sources,
} = this.props
return _.get(queries, '0.source.links.self', links.self)
const initialSourceLink: string = getDeep<string>(queries, '0.source', null)
if (initialSourceLink) {
const initialSource = sources.find(
s => s.links.self === initialSourceLink
)
return initialSource
}
return source
}
private get source(): Source {
const {source, sources} = this.props
const query = _.get(this.state.queriesWorkingDraft, 0, {sourceLink: null})
const query = _.get(this.state.queriesWorkingDraft, 0, {source: null})
if (!query.sourceLink) {
if (!query.source) {
return source
}
return sources.find(s => s.links.self === query.sourceLink) || source
const foundSource = sources.find(
s =>
s.links.self ===
getDeep<string | null>(query, 'source.links.self', null)
)
if (foundSource) {
return foundSource
}
return source
}
}

View File

@ -1,135 +0,0 @@
import React from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import RefreshingGraph from 'src/shared/components/RefreshingGraph'
import buildQueries from 'utils/buildQueriesForGraphs'
import VisualizationName from 'src/dashboards/components/VisualizationName'
import {getCellTypeColors} from 'src/dashboards/constants/cellEditor'
import {colorsStringSchema, colorsNumberSchema} from 'shared/schemas'
const DashVisualization = (
{
axes,
type,
templates,
timeRange,
lineColors,
autoRefresh,
gaugeColors,
queryConfigs,
editQueryStatus,
resizerTopHeight,
staticLegend,
thresholdsListColors,
tableOptions,
timeFormat,
decimalPlaces,
fieldOptions,
isInCEO,
},
{
source: {
links: {proxy},
},
}
) => {
const colors = getCellTypeColors({
cellType: type,
gaugeColors,
thresholdsListColors,
lineColors,
})
return (
<div className="graph">
<VisualizationName />
<div className="graph-container">
<RefreshingGraph
colors={colors}
axes={axes}
type={type}
tableOptions={tableOptions}
queries={buildQueries(proxy, queryConfigs, timeRange)}
templates={templates}
autoRefresh={autoRefresh}
editQueryStatus={editQueryStatus}
resizerTopHeight={resizerTopHeight}
staticLegend={staticLegend}
timeFormat={timeFormat}
decimalPlaces={decimalPlaces}
fieldOptions={fieldOptions}
isInCEO={isInCEO}
/>
</div>
</div>
)
}
const {arrayOf, bool, func, number, shape, string} = PropTypes
DashVisualization.propTypes = {
type: string.isRequired,
autoRefresh: number.isRequired,
templates: arrayOf(shape()),
timeRange: shape({
upper: string,
lower: string,
}).isRequired,
queryConfigs: arrayOf(shape({})).isRequired,
editQueryStatus: func.isRequired,
axes: shape({
y: shape({
bounds: arrayOf(string),
}),
}),
tableOptions: shape({}),
timeFormat: string.isRequired,
decimalPlaces: shape({
isEnforced: bool,
digits: number,
}),
fieldOptions: arrayOf(
shape({
internalName: string.isRequired,
displayName: string.isRequired,
visible: bool.isRequired,
})
),
resizerTopHeight: number,
thresholdsListColors: colorsNumberSchema,
gaugeColors: colorsNumberSchema,
lineColors: colorsStringSchema,
staticLegend: bool,
isInCEO: bool,
}
DashVisualization.contextTypes = {
source: PropTypes.shape({
links: PropTypes.shape({
proxy: PropTypes.string.isRequired,
}).isRequired,
}).isRequired,
}
const mapStateToProps = ({
cellEditorOverlay: {
thresholdsListColors,
gaugeColors,
lineColors,
cell: {type, axes, tableOptions, fieldOptions, timeFormat, decimalPlaces},
},
}) => ({
gaugeColors,
thresholdsListColors,
lineColors,
type,
axes,
tableOptions,
fieldOptions,
timeFormat,
decimalPlaces,
})
export default connect(mapStateToProps, null)(DashVisualization)

View File

@ -0,0 +1,119 @@
import React, {SFC} from 'react'
import {connect} from 'react-redux'
import _ from 'lodash'
import RefreshingGraph from 'src/shared/components/RefreshingGraph'
import buildQueries from 'src/utils/buildQueriesForGraphs'
import VisualizationName from 'src/dashboards/components/VisualizationName'
import {SourceContext} from 'src/CheckSources'
import {getCellTypeColors} from 'src/dashboards/constants/cellEditor'
import {TimeRange, QueryConfig, Axes, Template, Source} from 'src/types'
import {
TableOptions,
DecimalPlaces,
FieldName,
CellType,
} from 'src/types/dashboard'
import {ColorString, ColorNumber} from 'src/types/colors'
interface Props {
type: CellType
autoRefresh: number
templates: Template[]
timeRange: TimeRange
queryConfigs: QueryConfig[]
editQueryStatus: () => void
axes: Axes
tableOptions: TableOptions
timeFormat: string
decimalPlaces: DecimalPlaces
fieldOptions: FieldName[]
resizerTopHeight: number
thresholdsListColors: ColorNumber[]
gaugeColors: ColorNumber[]
lineColors: ColorString[]
staticLegend: boolean
isInCEO: boolean
}
const DashVisualization: SFC<Props> = ({
axes,
type,
templates,
timeRange,
lineColors,
autoRefresh,
gaugeColors,
queryConfigs,
editQueryStatus,
resizerTopHeight,
staticLegend,
thresholdsListColors,
tableOptions,
timeFormat,
decimalPlaces,
fieldOptions,
isInCEO,
}) => {
const colors: ColorString[] = getCellTypeColors({
cellType: type,
gaugeColors,
thresholdsListColors,
lineColors,
})
return (
<div className="graph">
<VisualizationName />
<div className="graph-container">
<SourceContext.Consumer>
{(source: Source) => (
<RefreshingGraph
colors={colors}
axes={axes}
type={type}
tableOptions={tableOptions}
queries={buildQueries(
_.get(source, 'links.proxy'),
queryConfigs,
timeRange
)}
templates={templates}
autoRefresh={autoRefresh}
editQueryStatus={editQueryStatus}
resizerTopHeight={resizerTopHeight}
staticLegend={staticLegend}
timeFormat={timeFormat}
decimalPlaces={decimalPlaces}
fieldOptions={fieldOptions}
isInCEO={isInCEO}
/>
)}
</SourceContext.Consumer>
</div>
</div>
)
}
const mapStateToProps = ({
cellEditorOverlay: {
thresholdsListColors,
gaugeColors,
lineColors,
cell: {type, axes, tableOptions, fieldOptions, timeFormat, decimalPlaces},
},
}) => ({
gaugeColors,
thresholdsListColors,
lineColors,
type,
axes,
tableOptions,
fieldOptions,
timeFormat,
decimalPlaces,
})
export default connect(mapStateToProps, null)(DashVisualization)

View File

@ -1,6 +1,7 @@
import {DEFAULT_TABLE_OPTIONS} from 'src/dashboards/constants'
import {stringifyColorValues} from 'src/shared/constants/colorOperations'
import {CellType} from 'src/types/dashboard'
import {ColorString, ColorNumber} from 'src/types/colors'
export const initializeOptions = (cellType: CellType) => {
switch (cellType) {
@ -21,18 +22,20 @@ export const AXES_SCALE_OPTIONS = {
export const TOOLTIP_Y_VALUE_FORMAT =
'<p><strong>K/M/B</strong> = Thousand / Million / Billion<br/><strong>K/M/G</strong> = Kilo / Mega / Giga </p>'
interface Color {
cellType: CellType
thresholdsListColors: ColorNumber[]
gaugeColors: ColorNumber[]
lineColors: ColorString[]
}
export const getCellTypeColors = ({
cellType,
gaugeColors,
thresholdsListColors,
lineColors,
}: {
cellType: CellType
gaugeColors
thresholdsListColors
lineColors
}) => {
let colors = []
}: Color): ColorString[] => {
let colors: ColorString[] = []
switch (cellType) {
case CellType.Gauge: {

View File

@ -1,12 +1,12 @@
import {QueryConfig} from 'src/types'
import {QueryConfig, Source} from 'src/types'
export const nextSource = (
prevQuery: QueryConfig,
nextQuery: QueryConfig
): string => {
if (nextQuery.sourceLink) {
return nextQuery.sourceLink
): Source => {
if (nextQuery.source) {
return nextQuery.source
}
return prevQuery.sourceLink
return prevQuery.source
}

View File

@ -4,12 +4,12 @@ import Table from './Table'
import RefreshingGraph from 'src/shared/components/RefreshingGraph'
import {DEFAULT_LINE_COLORS} from 'src/shared/constants/graphColorPalettes'
import {QueryConfig, Template} from 'src/types'
import {Query, Template} from 'src/types'
interface Props {
view: string
query?: QueryConfig
queries: QueryConfig[]
query?: Query
queries: Query[]
templates: Template[]
autoRefresh: number
editQueryStatus: () => void

View File

@ -9,7 +9,7 @@ import {GRAPH, TABLE} from 'src/shared/constants'
import buildQueries from 'src/utils/buildQueriesForGraphs'
import {ErrorHandling} from 'src/shared/decorators/errors'
import {Source, QueryConfig, Template, TimeRange} from 'src/types'
import {Source, Query, QueryConfig, Template, TimeRange} from 'src/types'
const META_QUERY_REGEX = /^(show|create|drop)/i
@ -101,12 +101,12 @@ class DataExplorerVisualization extends PureComponent<Props, State> {
})
}
private get queries(): QueryConfig[] {
private get queries(): Query[] {
const {source, queryConfigs, timeRange} = this.props
return buildQueries(source.links.proxy, queryConfigs, timeRange)
}
private get query(): QueryConfig {
private get query(): Query {
const {activeQueryIndex} = this.props
const activeQuery = this.queries[activeQueryIndex]
const defaultQuery = this.queries[0]

View File

@ -1,3 +1,5 @@
import {modeIFQL, modeTickscript} from 'src/shared/constants/codeMirrorModes'
/* eslint-disable */
const CodeMirror = require('codemirror')
@ -309,79 +311,9 @@ function indentFunction(states, meta) {
}
}
CodeMirror.defineSimpleMode('tickscript', {
// The start state contains the rules that are intially used
start: [
// The regex matches the token, the token property contains the type
{
regex: /"(?:[^\\]|\\.)*?(?:"|$)/,
token: 'string.double'
},
{
regex: /'(?:[^\\]|\\.)*?(?:'|$)/,
token: 'string.single'
},
{
regex: /(function)(\s+)([a-z$][\w$]*)/,
token: ['keyword', null, 'variable-2'],
},
// Rules are matched in the order in which they appear, so there is
// no ambiguity between this one and the one above
{
regex: /(?:var|return|if|for|while|else|do|this|stream|batch|influxql|lambda)/,
token: 'keyword',
},
{
regex: /true|false|null|undefined|TRUE|FALSE/,
token: 'atom'
},
{
regex: /0x[a-f\d]+|[-+]?(?:\.\d+|\d+\.?\d*)(?:e[-+]?\d+)?/i,
token: 'number',
},
{
regex: /\/\/.*/,
token: 'comment'
},
{
regex: /\/(?:[^\\]|\\.)*?\//,
token: 'variable-3'
},
// A next property will cause the mode to move to a different state
{
regex: /\/\*/,
token: 'comment',
next: 'comment'
},
{
regex: /[-+\/*=<>!]+/,
token: 'operator'
},
{
regex: /[a-z$][\w$]*/,
token: 'variable'
},
],
// The multi-line comment state.
comment: [{
regex: /.*?\*\//,
token: 'comment',
next: 'start'
},
{
regex: /.*/,
token: 'comment'
},
],
// The meta property contains global information about the mode. It
// can contain properties like lineComment, which are supported by
// all modes, and also directives like dontIndentStates, which are
// specific to simple modes.
meta: {
dontIndentStates: ['comment'],
lineComment: '//',
},
})
// Modes
CodeMirror.defineSimpleMode('ifql', modeIFQL)
CodeMirror.defineSimpleMode('tickscript', modeTickscript)
// CodeMirror Hints

View File

@ -0,0 +1,21 @@
export type Action = ActionUpdateScript
export enum ActionTypes {
UpdateScript = 'UPDATE_SCRIPT',
}
export interface ActionUpdateScript {
type: ActionTypes.UpdateScript
payload: {
script: string
}
}
export type UpdateScript = (script: string) => ActionUpdateScript
export const updateScript = (script: string): ActionUpdateScript => {
return {
type: ActionTypes.UpdateScript,
payload: {script},
}
}

View File

@ -1,4 +1,6 @@
import AJAX from 'src/utils/ajax'
import {Service} from 'src/types'
import {updateService} from 'src/shared/apis'
export const getSuggestions = async (url: string) => {
try {
@ -34,17 +36,24 @@ export const getAST = async (request: ASTRequest) => {
}
}
export const getTimeSeries = async (script: string) => {
export const getTimeSeries = async (service: Service, script: string) => {
const and = encodeURIComponent('&')
const mark = encodeURIComponent('?')
const garbage = script.replace(/\s/g, '') // server cannot handle whitespace
try {
const data = await AJAX({
method: 'POST',
url: `http://localhost:8093/query?q=${script}`,
url: `${
service.links.proxy
}?path=/v1/query${mark}orgName=defaulorgname${and}q=${garbage}`,
headers: {'Content-Type': 'text/plain'},
})
return data
} catch (error) {
console.error('Problem fetching data', error)
throw error
throw error.data.message
}
}
@ -82,3 +91,19 @@ export const getTagValues = async () => {
throw error
}
}
export const updateScript = async (service: Service, script: string) => {
const updates = {...service, metadata: {script}}
try {
const response = await updateService(updates)
return response
} catch (error) {
if (error.data) {
console.error('Could not update script', error.data)
throw error.data
}
throw error
}
}

View File

@ -91,7 +91,7 @@ class TimeMachine extends PureComponent<Props> {
headerButtons: [
<div
key="analyze"
className="btn btn-default btn-sm analyze--button"
className="btn btn-default btn-xs analyze--button"
onClick={onAnalyze}
>
Analyze

View File

@ -60,12 +60,13 @@ class TimeMachineEditor extends PureComponent<Props> {
const options = {
lineNumbers: true,
theme: 'material',
theme: 'time-machine',
tabIndex: 1,
readonly: false,
extraKeys: {'Ctrl-Space': 'autocomplete'},
completeSingle: false,
autoRefresh: true,
mode: 'ifql',
gutters: ['error-gutter'],
}

View File

@ -58,3 +58,6 @@ export const EXCLUDED_KEYS = [
'Meta',
' ',
]
export const DEFAULT_SCRIPT =
'fil = (r) => r._measurement == "cpu"\ntele = from(db: "telegraf") \n\t\t|> filter(fn: fil)\n |> range(start: -1m)\n |> sum()\n\n'

View File

@ -10,11 +10,14 @@ import KeyboardShortcuts from 'src/shared/components/KeyboardShortcuts'
import {notify as notifyAction} from 'src/shared/actions/notifications'
import {analyzeSuccess} from 'src/shared/copy/notifications'
import {
updateScript as updateScriptAction,
UpdateScript,
} from 'src/ifql/actions'
import {bodyNodes} from 'src/ifql/helpers'
import {getSuggestions, getAST, getTimeSeries} from 'src/ifql/apis'
import {builder, argTypes} from 'src/ifql/constants'
import {funcNames} from 'src/ifql/constants'
import {funcNames, builder, argTypes} from 'src/ifql/constants'
import {Source, Service, Notification} from 'src/types'
import {
@ -37,6 +40,11 @@ interface Props {
services: Service[]
sources: Source[]
notify: (message: Notification) => void
script: string
updateScript: UpdateScript
params: {
sourceID: string
}
}
interface Body extends FlatBody {
@ -46,7 +54,6 @@ interface Body extends FlatBody {
interface State {
body: Body[]
ast: object
script: string
data: string
suggestions: Suggestion[]
status: Status
@ -63,7 +70,6 @@ export class IFQLPage extends PureComponent<Props, State> {
ast: null,
data: 'Hit "Get Data!" or Ctrl + Enter to run your script',
suggestions: [],
script: `fil = (r) => r._measurement == \"cpu\"\ntele = from(db: \"telegraf\") \n\t\t|> filter(fn: fil)\n |> range(start: -1m)\n |> sum()\n\n`,
status: {
type: 'none',
text: '',
@ -72,7 +78,7 @@ export class IFQLPage extends PureComponent<Props, State> {
}
public async componentDidMount() {
const {links} = this.props
const {links, script} = this.props
try {
const suggestions = await getSuggestions(links.suggestions)
@ -81,11 +87,12 @@ export class IFQLPage extends PureComponent<Props, State> {
console.error('Could not get function suggestions: ', error)
}
this.getASTResponse(this.state.script)
this.getASTResponse(script)
}
public render() {
const {suggestions, script, data, body, status} = this.state
const {suggestions, data, body, status} = this.state
const {script} = this.props
return (
<CheckServices>
@ -140,7 +147,7 @@ export class IFQLPage extends PureComponent<Props, State> {
}
private handleSubmitScript = () => {
this.getASTResponse(this.state.script)
this.getASTResponse(this.props.script)
}
private handleGenerateScript = (): void => {
@ -260,21 +267,21 @@ export class IFQLPage extends PureComponent<Props, State> {
}
private handleAppendFrom = (): void => {
const {script} = this.state
const {script} = this.props
const newScript = `${script.trim()}\n\n${builder.NEW_FROM}\n\n`
this.getASTResponse(newScript)
}
private handleAppendJoin = (): void => {
const {script} = this.state
const {script} = this.props
const newScript = `${script.trim()}\n\n${builder.NEW_JOIN}\n\n`
this.getASTResponse(newScript)
}
private handleChangeScript = (script: string): void => {
this.setState({script})
this.props.updateScript(script)
}
private handleAddNode = (
@ -376,10 +383,10 @@ export class IFQLPage extends PureComponent<Props, State> {
}
private handleAnalyze = async () => {
const {links, notify} = this.props
const {links, notify, script} = this.props
try {
const ast = await getAST({url: links.ast, body: this.state.script})
const ast = await getAST({url: links.ast, body: script})
const body = bodyNodes(ast, this.state.suggestions)
const status = {type: 'success', text: ''}
notify(analyzeSuccess)
@ -411,7 +418,8 @@ export class IFQLPage extends PureComponent<Props, State> {
})
const body = bodyNodes(ast, suggestions)
const status = {type: 'success', text: ''}
this.setState({ast, script, body, status})
this.setState({ast, body, status})
this.props.updateScript(script)
} catch (error) {
this.setState({status: this.parseError(error)})
return console.error('Could not parse AST', error)
@ -419,14 +427,14 @@ export class IFQLPage extends PureComponent<Props, State> {
}
private getTimeSeries = async () => {
const {script} = this.state
const {script} = this.props
this.setState({data: 'fetching data...'})
try {
const {data} = await getTimeSeries(script)
const {data} = await getTimeSeries(this.service, script)
this.setState({data})
} catch (error) {
this.setState({data: 'Error fetching data'})
this.setState({data: error})
console.error('Could not get timeSeries', error)
}
@ -440,12 +448,13 @@ export class IFQLPage extends PureComponent<Props, State> {
}
}
const mapStateToProps = ({links, services, sources}) => {
return {links: links.ifql, services, sources}
const mapStateToProps = ({links, services, sources, script}) => {
return {links: links.ifql, services, sources, script}
}
const mapDispatchToProps = {
notify: notifyAction,
updateScript: updateScriptAction,
}
export default connect(mapStateToProps, mapDispatchToProps)(IFQLPage)

View File

@ -0,0 +1,17 @@
import {Action, ActionTypes} from 'src/ifql/actions'
import {editor} from 'src/ifql/constants'
const scriptReducer = (
state: string = editor.DEFAULT_SCRIPT,
action: Action
): string => {
switch (action.type) {
case ActionTypes.UpdateScript: {
return action.payload.script
}
}
return state
}
export default scriptReducer

View File

@ -26,6 +26,7 @@ import {StatusPage} from 'src/status'
import {HostsPage, HostPage} from 'src/hosts'
import DataExplorerPage from 'src/data_explorer'
import {DashboardsPage, DashboardPage} from 'src/dashboards'
import {LogsPage} from 'src/logs'
import AlertsApp from 'src/alerts'
import {
KapacitorPage,
@ -119,6 +120,9 @@ class Root extends PureComponent<{}, State> {
<Route path="/" component={UserIsAuthenticated(CheckSources)} />
<Route path="/login" component={UserIsNotAuthenticated(Login)} />
<Route path="/purgatory" component={UserIsAuthenticated(Purgatory)} />
<Route component={UserIsAuthenticated(App)}>
<Route path="/logs" component={LogsPage} />
</Route>
<Route
path="/sources/new"
component={UserIsAuthenticated(SourcePage)}

View File

@ -1,7 +1,7 @@
import React, {PureComponent, MouseEvent} from 'react'
import _ from 'lodash'
import {get} from 'src/utils/wrappers'
import {getDeep} from 'src/utils/wrappers'
import {
Tab,
@ -431,12 +431,12 @@ class AlertTabs extends PureComponent<Props, State> {
private get isMultipleConfigsSupported(): boolean {
const {configSections} = this.state
const hasPagerDuty2: Section = get(
const hasPagerDuty2 = getDeep<Section>(
configSections,
AlertTypes.pagerduty2,
undefined
)
const hasOpsGenie2: Section = get(
const hasOpsGenie2 = getDeep<Section>(
configSections,
AlertTypes.opsgenie2,
undefined
@ -454,9 +454,13 @@ class AlertTabs extends PureComponent<Props, State> {
private getConfigEnabled = (sections: Sections, section: string): boolean => {
if (section === AlertTypes.slack || section === AlertTypes.kafka) {
const configElements: Section[] = get(sections, `${section}.elements`, [])
const configElements = getDeep<Section[]>(
sections,
`${section}.elements`,
[]
)
const enabledConfigElements = configElements.filter(e => {
const enabled: boolean = get(e, 'options.enabled', false)
const enabled = getDeep<boolean>(e, 'options.enabled', false)
return enabled
})
return enabledConfigElements.length > 0

View File

@ -19,9 +19,10 @@ class TickscriptEditor extends Component {
const options = {
lineNumbers: true,
theme: 'material',
theme: 'tickscript',
tabIndex: 1,
readonly: false,
mode: 'tickscript',
}
return (

View File

@ -8,7 +8,7 @@ import {Notification, NotificationFunc} from 'src/types'
import {KafkaProperties} from 'src/types/kapacitor'
import {notifyInvalidBatchSizeValue} from 'src/shared/copy/notifications'
import {get} from 'src/utils/wrappers'
import {getDeep} from 'src/utils/wrappers'
interface Config {
options: KafkaProperties
@ -62,7 +62,7 @@ class KafkaConfig extends PureComponent<Props, State> {
this.state = {
currentBrokers: brokers || [],
testEnabled: this.props.enabled,
enabled: get(this.props, 'config.options.enabled', false),
enabled: getDeep<boolean>(this.props, 'config.options.enabled', false),
}
}
@ -244,7 +244,7 @@ class KafkaConfig extends PureComponent<Props, State> {
}
private get isNewConfig(): boolean {
return get(this.props, 'config.isNewConfig', false)
return getDeep<boolean>(this.props, 'config.isNewConfig', false)
}
private get isDefaultConfig(): boolean {

View File

@ -7,7 +7,7 @@ import {ErrorHandling} from 'src/shared/decorators/errors'
import {KafkaProperties} from 'src/types/kapacitor'
import {Notification, NotificationFunc} from 'src/types'
import {get} from 'src/utils/wrappers'
import {getDeep} from 'src/utils/wrappers'
const DEFAULT_CONFIG = {
options: {
@ -63,8 +63,8 @@ class KafkaConfigs extends Component<Props, State> {
return (
<div>
{this.configs.map(c => {
const enabled = get(c, 'options.enabled', false)
const id = get(c, 'options.id', '')
const enabled = getDeep<boolean>(c, 'options.enabled', false)
const id = getDeep<string>(c, 'options.id', '')
return (
<KafkaConfig
config={c}
@ -93,7 +93,7 @@ class KafkaConfigs extends Component<Props, State> {
}
private get configs(): Config[] {
return _.sortBy(this.state.configs, c => {
const id = get<string>(c, 'options.id', '')
const id = getDeep<string>(c, 'options.id', '')
const {isNewConfig} = c
if (id === 'default') {
return ''

View File

@ -1,6 +1,6 @@
import React, {PureComponent, MouseEvent} from 'react'
import _ from 'lodash'
import {get} from 'src/utils/wrappers'
import {getDeep} from 'src/utils/wrappers'
import {ErrorHandling} from 'src/shared/decorators/errors'
import SlackConfig from 'src/kapacitor/components/config/SlackConfig'
@ -98,7 +98,7 @@ class SlackConfigs extends PureComponent<Props, State> {
}
private isNewConfig = (config: Config): boolean => {
return get(config, 'isNewConfig', false)
return getDeep(config, 'isNewConfig', false)
}
private isDefaultConfig = (config: Config): boolean => {

View File

@ -61,7 +61,7 @@ const KafkaHandler: SFC<Props> = ({
<HandlerInput
selectedHandler={handler}
handleModifyHandler={handleModifyHandler}
fieldName="topic"
fieldName="kafka-topic"
fieldDisplay="Topic"
placeholder=""
/>

View File

@ -242,7 +242,7 @@ export const HANDLERS_TO_RULE_THEM_ALL: FieldsFromAllAlerts = {
'service',
],
hipChat: ['room'],
kafka: ['id', 'cluster', 'topic', 'template'],
kafka: ['id', 'cluster', 'kafka-topic', 'template'],
opsGenie: ['teams', 'recipients'],
opsGenie2: ['teams', 'recipients'],
pagerDuty: [],

View File

@ -56,6 +56,7 @@ export const saveToLocalStorage = ({
timeRange,
dataExplorer,
dashTimeV1: {ranges},
script,
}: LocalStorage): void => {
try {
const appPersisted = {app: {persisted}}
@ -70,6 +71,7 @@ export const saveToLocalStorage = ({
dashTimeV1,
dataExplorer,
dataExplorerQueryConfigs,
script,
})
)
} catch (err) {

View File

@ -0,0 +1,93 @@
import {Source, Namespace, TimeRange} from 'src/types'
import {getSource} from 'src/shared/apis'
import {getDatabasesWithRetentionPolicies} from 'src/shared/apis/databases'
import {getDeep} from 'src/utils/wrappers'
export enum ActionTypes {
SetSource = 'LOGS_SET_SOURCE',
SetNamespaces = 'LOGS_SET_NAMESPACES',
SetTimeRange = 'LOGS_SET_TIMERANGE',
SetNamespace = 'LOGS_SET_NAMESPACE',
}
interface SetSourceAction {
type: ActionTypes.SetSource
payload: {
source: Source
}
}
interface SetNamespacesAction {
type: ActionTypes.SetNamespaces
payload: {
namespaces: Namespace[]
}
}
interface SetNamespaceAction {
type: ActionTypes.SetNamespace
payload: {
namespace: Namespace
}
}
interface SetTimeRangeAction {
type: ActionTypes.SetTimeRange
payload: {
timeRange: TimeRange
}
}
export type Action =
| SetSourceAction
| SetNamespacesAction
| SetTimeRangeAction
| SetNamespaceAction
export const setSource = (source: Source): SetSourceAction => ({
type: ActionTypes.SetSource,
payload: {
source,
},
})
export const setNamespace = (namespace: Namespace): SetNamespaceAction => ({
type: ActionTypes.SetNamespace,
payload: {
namespace,
},
})
export const setNamespaces = (
namespaces: Namespace[]
): SetNamespacesAction => ({
type: ActionTypes.SetNamespaces,
payload: {
namespaces,
},
})
export const setTimeRange = (timeRange: TimeRange): SetTimeRangeAction => ({
type: ActionTypes.SetTimeRange,
payload: {
timeRange,
},
})
export const getSourceAsync = (sourceID: string) => async dispatch => {
const response = await getSource(sourceID)
const source = response.data
const proxyLink = getDeep<string | null>(source, 'links.proxy', null)
if (proxyLink) {
const namespaces = await getDatabasesWithRetentionPolicies(proxyLink)
if (namespaces && namespaces.length > 0) {
dispatch(setNamespaces(namespaces))
dispatch(setNamespace(namespaces[0]))
}
dispatch(setSource(source))
}
}

View File

@ -0,0 +1,103 @@
import _ from 'lodash'
import React, {PureComponent} from 'react'
import {Source, Namespace} from 'src/types'
import Dropdown from 'src/shared/components/Dropdown'
import TimeRangeDropdown from 'src/logs/components/TimeRangeDropdown'
import {TimeRange} from 'src/types'
interface SourceItem {
id: string
text: string
}
interface Props {
currentNamespace: Namespace
availableSources: Source[]
currentSource: Source | null
currentNamespaces: Namespace[]
timeRange: TimeRange
onChooseSource: (sourceID: string) => void
onChooseNamespace: (namespace: Namespace) => void
onChooseTimerange: (timeRange: TimeRange) => void
}
class LogViewerHeader extends PureComponent<Props> {
public render(): JSX.Element {
const {timeRange} = this.props
return (
<>
<Dropdown
className="dropdown-300"
items={this.sourceDropDownItems}
selected={this.selectedSource}
onChoose={this.handleChooseSource}
/>
<Dropdown
className="dropdown-300"
items={this.namespaceDropDownItems}
selected={this.selectedNamespace}
onChoose={this.handleChooseNamespace}
/>
<TimeRangeDropdown
onChooseTimeRange={this.handleChooseTimeRange}
selected={timeRange}
/>
</>
)
}
private handleChooseTimeRange = (timerange: TimeRange) => {
this.props.onChooseTimerange(timerange)
}
private handleChooseSource = (item: SourceItem) => {
this.props.onChooseSource(item.id)
}
private handleChooseNamespace = (namespace: Namespace) => {
this.props.onChooseNamespace(namespace)
}
private get selectedSource(): string {
if (_.isEmpty(this.sourceDropDownItems)) {
return ''
}
return this.sourceDropDownItems[0].text
}
private get selectedNamespace(): string {
const {currentNamespace} = this.props
if (!currentNamespace) {
return ''
}
return `${currentNamespace.database}.${currentNamespace.retentionPolicy}`
}
private get namespaceDropDownItems() {
const {currentNamespaces} = this.props
return currentNamespaces.map(namespace => {
return {
text: `${namespace.database}.${namespace.retentionPolicy}`,
...namespace,
}
})
}
private get sourceDropDownItems(): SourceItem[] {
const {availableSources} = this.props
return availableSources.map(source => {
return {
text: `${source.name} @ ${source.url}`,
id: source.id,
}
})
}
}
export default LogViewerHeader

View File

@ -0,0 +1,174 @@
import React, {Component} from 'react'
import classnames from 'classnames'
import moment from 'moment'
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
import timeRanges from 'src/logs/data/timeRanges'
import {DROPDOWN_MENU_MAX_HEIGHT} from 'src/shared/constants/index'
import {ErrorHandling} from 'src/shared/decorators/errors'
import {ClickOutside} from 'src/shared/components/ClickOutside'
import CustomTimeRange from 'src/shared/components/CustomTimeRange'
import {TimeRange} from 'src/types'
const dateFormat = 'YYYY-MM-DD HH:mm'
const emptyTime = {lower: '', upper: ''}
const format = t => moment(t.replace(/\'/g, '')).format(dateFormat)
interface Props {
selected: {
lower: string
upper?: string
}
onChooseTimeRange: (timeRange: TimeRange) => void
preventCustomTimeRange?: boolean
page?: string
}
interface State {
autobind: boolean
isOpen: boolean
isCustomTimeRangeOpen: boolean
customTimeRange: TimeRange
}
@ErrorHandling
class TimeRangeDropdown extends Component<Props, State> {
public static defaultProps = {
page: 'default',
}
constructor(props) {
super(props)
const {lower, upper} = props.selected
const isTimeValid = moment(upper).isValid() && moment(lower).isValid()
const customTimeRange = isTimeValid ? {lower, upper} : emptyTime
this.state = {
autobind: false,
isOpen: false,
isCustomTimeRangeOpen: false,
customTimeRange,
}
}
public render() {
const {selected, preventCustomTimeRange, page} = this.props
const {customTimeRange, isCustomTimeRangeOpen, isOpen} = this.state
return (
<ClickOutside onClickOutside={this.handleClickOutside}>
<div className="time-range-dropdown">
<div
className={classnames('dropdown dropdown-290', {
open: isOpen,
})}
>
<div
className="btn btn-sm btn-default dropdown-toggle"
onClick={this.toggleMenu}
>
<span className="icon clock" />
<span className="dropdown-selected">
{this.findTimeRangeInputValue(selected)}
</span>
<span className="caret" />
</div>
<ul className="dropdown-menu">
<FancyScrollbar
autoHide={false}
autoHeight={true}
maxHeight={DROPDOWN_MENU_MAX_HEIGHT}
>
{preventCustomTimeRange ? null : (
<div>
<li className="dropdown-header">Absolute Time</li>
<li
className={
isCustomTimeRangeOpen
? 'active dropdown-item custom-timerange'
: 'dropdown-item custom-timerange'
}
>
<a href="#" onClick={this.showCustomTimeRange}>
Date Picker
</a>
</li>
</div>
)}
<li className="dropdown-header">
{preventCustomTimeRange ? '' : 'Relative '}Time
</li>
{timeRanges.map(item => {
return (
<li className="dropdown-item" key={item.menuOption}>
<a href="#" onClick={this.handleSelection(item)}>
{item.menuOption}
</a>
</li>
)
})}
</FancyScrollbar>
</ul>
</div>
{isCustomTimeRangeOpen ? (
<ClickOutside onClickOutside={this.handleCloseCustomTimeRange}>
<div className="custom-time--overlay">
<CustomTimeRange
onApplyTimeRange={this.handleApplyCustomTimeRange}
timeRange={customTimeRange}
onClose={this.handleCloseCustomTimeRange}
isVisible={isCustomTimeRangeOpen}
timeInterval={60}
page={page}
/>
</div>
</ClickOutside>
) : null}
</div>
</ClickOutside>
)
}
private findTimeRangeInputValue = ({upper, lower}: TimeRange) => {
if (upper && lower) {
if (upper === 'now()') {
return `${format(lower)} - Now`
}
return `${format(lower)} - ${format(upper)}`
}
const selected = timeRanges.find(range => range.lower === lower)
return selected ? selected.inputValue : 'Custom'
}
private handleClickOutside = () => {
this.setState({isOpen: false})
}
private handleSelection = timeRange => () => {
this.props.onChooseTimeRange(timeRange)
this.setState({customTimeRange: emptyTime, isOpen: false})
}
private toggleMenu = () => {
this.setState({isOpen: !this.state.isOpen})
}
private showCustomTimeRange = () => {
this.setState({isCustomTimeRangeOpen: true})
}
private handleApplyCustomTimeRange = customTimeRange => {
this.props.onChooseTimeRange({...customTimeRange})
this.setState({customTimeRange, isOpen: false})
}
private handleCloseCustomTimeRange = () => {
this.setState({isCustomTimeRangeOpen: false})
}
}
export default TimeRangeDropdown

View File

@ -0,0 +1,101 @@
import React, {PureComponent} from 'react'
import {connect} from 'react-redux'
import {getSourceAsync, setTimeRange, setNamespace} from 'src/logs/actions'
import {getSourcesAsync} from 'src/shared/actions/sources'
import {Source, Namespace, TimeRange} from 'src/types'
import LogViewerHeader from 'src/logs/components/LogViewerHeader'
interface Props {
sources: Source[]
currentSource: Source | null
currentNamespaces: Namespace[]
currentNamespace: Namespace
getSource: (sourceID: string) => void
getSources: () => void
setTimeRange: (timeRange: TimeRange) => void
setNamespace: (namespace: Namespace) => void
timeRange: TimeRange
}
class LogsPage extends PureComponent<Props> {
public componentDidUpdate() {
if (!this.props.currentSource) {
this.props.getSource(this.props.sources[0].id)
}
}
public componentDidMount() {
this.props.getSources()
}
public render() {
return (
<div className="page hosts-list-page">
<div className="page-header full-width">
<div className="page-header__container">
<div className="page-header__left">
<h1 className="page-header__title">Log Viewer</h1>
</div>
<div className="page-header__right">{this.header}</div>
</div>
</div>
</div>
)
}
private get header(): JSX.Element {
const {
sources,
currentSource,
currentNamespaces,
timeRange,
currentNamespace,
} = this.props
return (
<LogViewerHeader
availableSources={sources}
timeRange={timeRange}
onChooseSource={this.handleChooseSource}
onChooseNamespace={this.handleChooseNamespace}
onChooseTimerange={this.handleChooseTimerange}
currentSource={currentSource}
currentNamespaces={currentNamespaces}
currentNamespace={currentNamespace}
/>
)
}
private handleChooseTimerange = (timeRange: TimeRange) => {
this.props.setTimeRange(timeRange)
}
private handleChooseSource = (sourceID: string) => {
this.props.getSource(sourceID)
}
private handleChooseNamespace = (namespace: Namespace) => {
// Do flip
this.props.setNamespace(namespace)
}
}
const mapStateToProps = ({
sources,
logs: {currentSource, currentNamespaces, timeRange, currentNamespace},
}) => ({
sources,
currentSource,
currentNamespaces,
timeRange,
currentNamespace,
})
const mapDispatchToProps = {
getSource: getSourceAsync,
getSources: getSourcesAsync,
setTimeRange,
setNamespace,
}
export default connect(mapStateToProps, mapDispatchToProps)(LogsPage)

View File

@ -0,0 +1,74 @@
export default [
{
defaultGroupBy: '10s',
seconds: 60,
inputValue: 'Past 1m',
lower: 'now() - 1m',
upper: null,
menuOption: 'Past 1m',
},
{
defaultGroupBy: '10s',
seconds: 300,
inputValue: 'Past 5m',
lower: 'now() - 5m',
upper: null,
menuOption: 'Past 5m',
},
{
defaultGroupBy: '1m',
seconds: 900,
inputValue: 'Past 15m',
lower: 'now() - 15m',
upper: null,
menuOption: 'Past 15m',
},
{
defaultGroupBy: '1m',
seconds: 1800,
inputValue: 'Past 30m',
lower: 'now() - 30m',
upper: null,
menuOption: 'Past 30m',
},
{
defaultGroupBy: '1m',
seconds: 3600,
inputValue: 'Past 1h',
lower: 'now() - 1h',
upper: null,
menuOption: 'Past 1h',
},
{
defaultGroupBy: '1m',
seconds: 5200,
inputValue: 'Past 2h',
lower: 'now() - 2h',
upper: null,
menuOption: 'Past 2h',
},
{
defaultGroupBy: '1m',
seconds: 21600,
inputValue: 'Past 6h',
lower: 'now() - 6h',
upper: null,
menuOption: 'Past 6h',
},
{
defaultGroupBy: '5m',
seconds: 43200,
inputValue: 'Past 12h',
lower: 'now() - 12h',
upper: null,
menuOption: 'Past 12h',
},
{
defaultGroupBy: '10m',
seconds: 86400,
inputValue: 'Past 24h',
lower: 'now() - 24h',
upper: null,
menuOption: 'Past 24h',
},
]

3
ui/src/logs/index.ts Normal file
View File

@ -0,0 +1,3 @@
import LogsPage from 'src/logs/containers/LogsPage'
export {LogsPage}

View File

@ -0,0 +1,31 @@
import {Source, Namespace, TimeRange} from 'src/types'
import {ActionTypes, Action} from 'src/logs/actions'
interface LogsState {
currentSource: Source | null
currentNamespaces: Namespace[]
currentNamespace: Namespace | null
timeRange: TimeRange
}
const defaultState = {
currentSource: null,
currentNamespaces: [],
timeRange: {lower: 'now() - 1m', upper: null},
currentNamespace: null,
}
export default (state: LogsState = defaultState, action: Action) => {
switch (action.type) {
case ActionTypes.SetSource:
return {...state, currentSource: action.payload.source}
case ActionTypes.SetNamespaces:
return {...state, currentNamespaces: action.payload.namespaces}
case ActionTypes.SetTimeRange:
return {...state, timeRange: action.payload.timeRange}
case ActionTypes.SetNamespace:
return {...state, currentNamespace: action.payload.namespace}
default:
return state
}
}

View File

@ -0,0 +1,35 @@
import _ from 'lodash'
import {showDatabases, showRetentionPolicies} from 'src/shared/apis/metaQuery'
import showDatabasesParser from 'src/shared/parsing/showDatabases'
import showRetentionPoliciesParser from 'src/shared/parsing/showRetentionPolicies'
import {Namespace} from 'src/types/query'
export const getDatabasesWithRetentionPolicies = async (
proxy: string
): Promise<Namespace[]> => {
try {
const {data} = await showDatabases(proxy)
const {databases} = showDatabasesParser(data)
const rps = await showRetentionPolicies(proxy, databases)
const namespaces = rps.data.results.reduce((acc, result, index) => {
const {retentionPolicies} = showRetentionPoliciesParser(result)
const dbrp = retentionPolicies.map(rp => ({
database: databases[index],
retentionPolicy: rp.name,
}))
return [...acc, ...dbrp]
}, [])
const sorted = _.sortBy(namespaces, ({database}: Namespace) =>
database.toLowerCase()
)
return sorted
} catch (err) {
console.error(err)
}
}

View File

@ -53,7 +53,6 @@ export const fetchTimeSeries = async (
// it may be used, but this slight modification is intended to allow for the use of
// `database` while moving over to `db` for consistency over time
const db = _.get(query, 'db', database)
const templatesWithIntervalVals = templates.map(temp => {
if (temp.tempVar === ':interval:') {
if (resolution) {

View File

@ -17,7 +17,7 @@ class CustomTimeRange extends Component {
}
componentDidMount() {
const {timeRange} = this.props
const {timeRange, timeInterval} = this.props
const lower = rome(this.lower, {
dateValidator: rome.val.beforeEq(this.upper),
@ -26,6 +26,7 @@ class CustomTimeRange extends Component {
autoClose: false,
autoHideOnBlur: false,
autoHideOnClick: false,
timeInterval,
})
const upper = rome(this.upper, {
@ -35,6 +36,7 @@ class CustomTimeRange extends Component {
initialValue: this.getInitialDate(timeRange.upper),
autoHideOnBlur: false,
autoHideOnClick: false,
timeInterval,
})
this.lowerCal = lower
@ -239,7 +241,11 @@ class CustomTimeRange extends Component {
}
}
const {func, shape, string} = PropTypes
CustomTimeRange.defaultProps = {
timeInterval: 1800,
}
const {func, shape, string, number} = PropTypes
CustomTimeRange.propTypes = {
onApplyTimeRange: func.isRequired,
@ -247,6 +253,7 @@ CustomTimeRange.propTypes = {
lower: string.isRequired,
upper: string,
}).isRequired,
timeInterval: number,
onClose: func,
page: string,
}

View File

@ -6,14 +6,12 @@ import _ from 'lodash'
import {QueryConfig, Source} from 'src/types'
import {Namespace} from 'src/types/query'
import {showDatabases, showRetentionPolicies} from 'src/shared/apis/metaQuery'
import showDatabasesParser from 'src/shared/parsing/showDatabases'
import showRetentionPoliciesParser from 'src/shared/parsing/showRetentionPolicies'
import DatabaseListItem from 'src/shared/components/DatabaseListItem'
import FancyScrollbar from 'src/shared/components/FancyScrollbar'
import {ErrorHandling} from 'src/shared/decorators/errors'
import {getDatabasesWithRetentionPolicies} from 'src/shared/apis/databases'
interface DatabaseListProps {
query: QueryConfig
querySource?: Source
@ -80,24 +78,7 @@ class DatabaseList extends Component<DatabaseListProps, DatabaseListState> {
const proxy = _.get(querySource, ['links', 'proxy'], source.links.proxy)
try {
const {data} = await showDatabases(proxy)
const {databases} = showDatabasesParser(data)
const rps = await showRetentionPolicies(proxy, databases)
const namespaces = rps.data.results.reduce((acc, result, index) => {
const {retentionPolicies} = showRetentionPoliciesParser(result)
const dbrp = retentionPolicies.map(rp => ({
database: databases[index],
retentionPolicy: rp.name,
}))
return [...acc, ...dbrp]
}, [])
const sorted = _.sortBy(namespaces, ({database}: Namespace) =>
database.toLowerCase()
)
const sorted = await getDatabasesWithRetentionPolicies(proxy)
this.setState({namespaces: sorted})
} catch (err) {
console.error(err)

View File

@ -59,7 +59,7 @@ class Division extends PureComponent<Props> {
fontSize: '16px',
fontWeight: '500',
})
const NAME_OFFSET = 66
const NAME_OFFSET = 96
this.collapseThreshold = width + NAME_OFFSET
}

View File

@ -62,7 +62,7 @@ class DivisionMenu extends PureComponent<Props, State> {
private get buttonClass(): string {
const {expanded} = this.state
return classnames('btn btn-sm btn-square btn-default', {
return classnames('btn btn-xs btn-square btn-default', {
active: expanded,
})
}

View File

@ -0,0 +1,172 @@
export const modeIFQL = {
// The start state contains the rules that are intially used
start: [
// IFQL Syntax
{
regex: /[|][>]/,
token: 'pipe-forward',
},
{
regex: /\w+(?=[(])/,
token: 'function',
},
{
regex: /[\w\d]+(?=\s[=]\s)/,
token: 'variable',
},
{
regex: /[=][>]/,
token: 'arrow-function',
},
{
regex: /\w+(?=[)]\s[=][>])(?![(])/,
token: 'function-arg',
},
{
regex: /\w+(?=[[]["]|[.])/,
token: 'function-arg-ref',
},
{
regex: /AND|OR|[=][=]|[!][=]|[<][=]|[>][=]/,
token: 'operator',
},
{
regex: /\w+[:]/,
token: 'argument',
},
// The regex matches the token, the token property contains the type
{
regex: /"(?:[^\\]|\\.)*?(?:"|$)/,
token: 'string-double',
},
{
regex: /'(?:[^\\]|\\.)*?(?:'|$)/,
token: 'string-single',
},
{
regex: /(function)(\s+)([a-z$][\w$]*)/,
token: ['keyword', null, 'variable-2'],
},
{
regex: /true|false|TRUE|FALSE/,
token: 'boolean',
},
{
regex: /null|undefined/,
token: 'null',
},
{
regex: /0x[a-f\d]+|[-+]?(?:\.\d+|\d+\.?\d*)(?:e[-+]?\d+)?/i,
token: 'number',
},
{
regex: /\/\/.*/,
token: 'comment',
},
// A next property will cause the mode to move to a different state
{
regex: /\/\*/,
token: 'comment',
next: 'comment',
},
{
regex: /[-+\/*=<>!]+/,
token: 'operator',
},
],
// The multi-line comment state.
comment: [
{
regex: /.*?\*\//,
token: 'comment',
next: 'start',
},
{
regex: /.*/,
token: 'comment',
},
],
// The meta property contains global information about the mode. It
// can contain properties like lineComment, which are supported by
// all modes, and also directives like dontIndentStates, which are
// specific to simple modes.
meta: {
dontIndentStates: ['comment'],
lineComment: '//',
},
}
export const modeTickscript = {
// The start state contains the rules that are intially used
start: [
// The regex matches the token, the token property contains the type
{
regex: /"(?:[^\\]|\\.)*?(?:"|$)/,
token: 'string.double',
},
{
regex: /'(?:[^\\]|\\.)*?(?:'|$)/,
token: 'string.single',
},
{
regex: /(function)(\s+)([a-z$][\w$]*)/,
token: ['keyword', null, 'variable-2'],
},
// Rules are matched in the order in which they appear, so there is
// no ambiguity between this one and the one above
{
regex: /(?:var|return|if|for|while|else|do|this|stream|batch|influxql|lambda)/,
token: 'keyword',
},
{
regex: /true|false|null|undefined|TRUE|FALSE/,
token: 'atom',
},
{
regex: /0x[a-f\d]+|[-+]?(?:\.\d+|\d+\.?\d*)(?:e[-+]?\d+)?/i,
token: 'number',
},
{
regex: /\/\/.*/,
token: 'comment',
},
{
regex: /\/(?:[^\\]|\\.)*?\//,
token: 'variable-3',
},
// A next property will cause the mode to move to a different state
{
regex: /\/\*/,
token: 'comment',
next: 'comment',
},
{
regex: /[-+\/*=<>!]+/,
token: 'operator',
},
{
regex: /[a-z$][\w$]*/,
token: 'variable',
},
],
// The multi-line comment state.
comment: [
{
regex: /.*?\*\//,
token: 'comment',
next: 'start',
},
{
regex: /.*/,
token: 'comment',
},
],
// The meta property contains global information about the mode. It
// can contain properties like lineComment, which are supported by
// all modes, and also directives like dontIndentStates, which are
// specific to simple modes.
meta: {
dontIndentStates: ['comment'],
lineComment: '//',
},
}

View File

@ -479,6 +479,6 @@ export const REQUIRED_HALVES = 2
export const HANDLE_VERTICAL = 'vertical'
export const HANDLE_HORIZONTAL = 'horizontal'
export const HANDLE_NONE = 'none'
export const HANDLE_PIXELS = 30
export const HANDLE_PIXELS = 20
export const MAX_SIZE = 1
export const MIN_SIZE = 0

View File

@ -1,3 +1,4 @@
import _ from 'lodash'
import React, {PureComponent} from 'react'
import {withRouter, Link} from 'react-router'
import {connect} from 'react-redux'
@ -14,10 +15,13 @@ import {
} from 'src/side_nav/components/NavItems'
import {DEFAULT_HOME_PAGE} from 'src/shared/constants'
import {Params, Location, Links, Me} from 'src/types/sideNav'
import {ErrorHandling} from 'src/shared/decorators/errors'
import {Params, Location, Links, Me} from 'src/types/sideNav'
import {Source} from 'src/types'
interface Props {
sources: Source[]
params: Params
location: Location
isHidden: boolean
@ -42,9 +46,13 @@ class SideNav extends PureComponent<Props> {
logoutLink,
links,
me,
sources = [],
} = this.props
const sourcePrefix = `/sources/${sourceID}`
const defaultSource = sources.find(s => s.default)
const id = sourceID || _.get(defaultSource, 'id', 0)
const sourcePrefix = `/sources/${id}`
const dataExplorerLink = `${sourcePrefix}/chronograf/data-explorer`
const isDefaultPage = location.split('/').includes(DEFAULT_HOME_PAGE)
@ -69,6 +77,16 @@ class SideNav extends PureComponent<Props> {
>
<NavHeader link={`${sourcePrefix}/hosts`} title="Host List" />
</NavBlock>
<FeatureFlag name="log-viewer">
<NavBlock
highlightWhen={['logs']}
icon="cubo-node"
link={'/logs'}
location={location}
>
<NavHeader link={'/logs'} title="Log Viewer" />
</NavBlock>
</FeatureFlag>
<NavBlock
highlightWhen={['data-explorer', 'delorean']}
icon="graphline"
@ -164,12 +182,14 @@ class SideNav extends PureComponent<Props> {
}
const mapStateToProps = ({
sources,
auth: {isUsingAuth, logoutLink, me},
app: {
ephemeral: {inPresentationMode},
},
links,
}) => ({
sources,
isHidden: inPresentationMode,
isUsingAuth,
logoutLink,

View File

@ -7,6 +7,7 @@ import errorsMiddleware from 'shared/middleware/errors'
import {resizeLayout} from 'shared/middleware/resizeLayout'
import {queryStringConfig} from 'shared/middleware/queryStringConfig'
import statusReducers from 'src/status/reducers'
import logsReducer from 'src/logs/reducers'
import sharedReducers from 'shared/reducers'
import dataExplorerReducers from 'src/data_explorer/reducers'
import adminReducers from 'src/admin/reducers'
@ -17,6 +18,7 @@ import overlayTechnology from 'src/shared/reducers/overlayTechnology'
import dashTimeV1 from 'src/dashboards/reducers/dashTimeV1'
import persistStateEnhancer from './persistStateEnhancer'
import servicesReducer from 'src/shared/reducers/services'
import scriptReducer from 'src/ifql/reducers/script'
const rootReducer = combineReducers({
...statusReducers,
@ -28,8 +30,10 @@ const rootReducer = combineReducers({
cellEditorOverlay,
overlayTechnology,
dashTimeV1,
logs: logsReducer,
routing: routerReducer,
services: servicesReducer,
script: scriptReducer,
})
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose

View File

@ -37,7 +37,7 @@
@import 'components/ceo-display-options';
@import 'components/confirm-button';
@import 'components/confirm-or-cancel';
@import 'components/code-mirror-theme';
@import 'components/code-mirror/theme';
@import 'components/color-dropdown';
@import 'components/custom-time-range';
@import 'components/customize-fields';

View File

@ -1,209 +0,0 @@
/*
Name: CHRONOGRAF YO
Author: Michael Kaminsky (http://github.com/mkaminsky11)
Original material color scheme by Mattia Astorino (https://github.com/equinusocio/material-theme)
*/
.react-codemirror2 {
position: relative;
width: 100%;
height: 100%;
}
.cm-s-material.CodeMirror {
border-radius: 0 0 $radius $radius;
font-family: $code-font;
background-color: transparent;
color: $g13-mist;
font-weight: 600;
height: 100%;
}
.CodeMirror-vscrollbar {
@include custom-scrollbar-round($g2-kevlar, $g6-smoke);
}
.CodeMirror-hscrollbar {
@include custom-scrollbar-round($g0-obsidian, $g6-smoke);
}
.cm-s-material .CodeMirror-gutters {
@include gradient-v($g2-kevlar, $g0-obsidian) border: none;
}
.cm-s-material .CodeMirror-gutters .CodeMirror-gutter {
background-color: fade-out($g4-onyx, 0.75);
height: calc(100% + 30px);
}
.CodeMirror-gutter.CodeMirror-linenumbers {
width: 60px;
}
.cm-s-material.CodeMirror .CodeMirror-sizer {
margin-left: 60px;
}
.cm-s-material.CodeMirror .CodeMirror-linenumber.CodeMirror-gutter-elt {
padding-right: 9px;
width: 46px;
color: $g8-storm;
}
.cm-s-material .CodeMirror-guttermarker,
.cm-s-material .CodeMirror-guttermarker-subtle,
.cm-s-material .CodeMirror-linenumber {
color: rgb(83, 127, 126);
}
.cm-s-material .CodeMirror-cursor {
width: 2px;
border: 0;
background-color: $g20-white;
box-shadow: 0 0 3px $c-laser, 0 0 6px $c-ocean, 0 0 11px $c-amethyst;
}
.cm-s-material div.CodeMirror-selected,
.cm-s-material.CodeMirror-focused div.CodeMirror-selected {
background-color: fade-out($g8-storm, 0.7);
}
.cm-s-material .CodeMirror-line::selection,
.cm-s-material .CodeMirror-line>span::selection,
.cm-s-material .CodeMirror-line>span>span::selection {
background: rgba(255, 255, 255, 0.10);
}
.cm-s-material .CodeMirror-line::-moz-selection,
.cm-s-material .CodeMirror-line>span::-moz-selection,
.cm-s-material .CodeMirror-line>span>span::-moz-selection {
background: rgba(255, 255, 255, 0.10);
}
.cm-s-material .CodeMirror-activeline-background {
background: rgba(0, 0, 0, 0);
}
.cm-s-material .cm-keyword {
color: $c-comet;
}
.cm-s-material .cm-operator {
color: $c-dreamsicle;
}
.cm-s-material .cm-variable-2 {
color: #80CBC4;
}
.cm-s-material .cm-variable-3,
.cm-s-material .cm-type {
color: $c-laser;
}
.cm-s-material .cm-builtin {
color: #DECB6B;
}
.cm-s-material .cm-atom {
color: $c-viridian;
}
.cm-s-material .cm-number {
color: $c-daisy;
}
.cm-s-material .cm-def {
color: rgba(233, 237, 237, 1);
}
.cm-s-material .cm-string {
color: $c-krypton;
}
.cm-s-material .cm-string-2 {
color: #80CBC4;
}
.cm-s-material .cm-comment {
color: $g10-wolf;
}
.cm-s-material .cm-variable {
color: $c-laser;
}
.cm-s-material .cm-tag {
color: #80CBC4;
}
.cm-s-material .cm-meta {
color: #80CBC4;
}
.cm-s-material .cm-attribute {
color: #FFCB6B;
}
.cm-s-material .cm-property {
color: #80CBAE;
}
.cm-s-material .cm-qualifier {
color: #DECB6B;
}
.cm-s-material .cm-variable-3,
.cm-s-material .cm-type {
color: #DECB6B;
}
.cm-s-material .cm-tag {
color: rgba(255, 83, 112, 1);
}
.cm-s-material .cm-error {
color: rgba(255, 255, 255, 1.0);
background-color: #EC5F67;
}
.cm-s-material .CodeMirror-matchingbracket {
text-decoration: underline;
color: white !important;
}
// CodeMirror hints
.CodeMirror-hints {
position: absolute;
z-index: 10;
overflow: hidden;
list-style: none;
margin: 0;
padding: 2px;
-webkit-box-shadow: 2px 3px 5px rgba(0, 0, 0, .2);
-moz-box-shadow: 2px 3px 5px rgba(0, 0, 0, .2);
box-shadow: 2px 3px 5px rgba(0, 0, 0, .2);
border-radius: 3px;
border: 1px solid silver;
background: white;
font-size: 90%;
font-family: monospace;
max-height: 20em;
overflow-y: auto;
}
.CodeMirror-hint {
margin: 0;
padding: 0 4px;
border-radius: 2px;
white-space: pre;
color: black;
cursor: pointer;
}
li.CodeMirror-hint-active {
background: #08f;
color: white;
}

View File

@ -0,0 +1,35 @@
/*
CodeMirror Hints Styles
------------------------------------------------------------------------------
*/
.CodeMirror-hints {
position: absolute;
z-index: 10;
overflow: hidden;
list-style: none;
margin: 0;
padding: 0;
box-shadow: 0 2px 5px 0.6px fade-out($g0-obsidian, 0.8);
border-radius: $radius;
font-size: 12px;
font-family: $code-font;
font-weight: 600;
max-height: 20em;
overflow-y: auto;
@include gradient-h($c-star, $c-pool);
}
li.CodeMirror-hint {
margin: 0;
padding: 6px 7px;
line-height: 12px;
white-space: pre;
color: $c-neutrino;
cursor: pointer;
}
li.CodeMirror-hint-active {
@include gradient-h($c-comet, $c-laser);
color: $g20-white;
}

View File

@ -0,0 +1,95 @@
/*
CodeMirror "TICKscript" Theme
------------------------------------------------------------------------------
Intended for use with the TICKscript CodeMirror Mode
*/
.cm-s-tickscript {
.cm-keyword {
color: $c-comet;
}
.cm-operator {
color: $c-dreamsicle;
}
.cm-variable-2 {
color: #80CBC4;
}
.cm-variable-3,
.cm-type {
color: $c-laser;
}
.cm-builtin {
color: #DECB6B;
}
.cm-atom {
color: $c-viridian;
}
.cm-number {
color: $c-daisy;
}
.cm-def {
color: rgba(233, 237, 237, 1);
}
.cm-string {
color: $c-krypton;
}
.cm-string-2 {
color: #80CBC4;
}
.cm-comment {
color: $g10-wolf;
}
.cm-variable {
color: $c-laser;
}
.cm-tag {
color: #80CBC4;
}
.cm-meta {
color: #80CBC4;
}
.cm-attribute {
color: #FFCB6B;
}
.cm-property {
color: #80CBAE;
}
.cm-qualifier {
color: #DECB6B;
}
.cm-variable-3,
.cm-type {
color: #DECB6B;
}
.cm-tag {
color: rgba(255, 83, 112, 1);
}
.cm-error {
color: rgba(255, 255, 255, 1.0);
background-color: #EC5F67;
}
.CodeMirror-matchingbracket {
text-decoration: underline;
color: white !important;
}
}

View File

@ -0,0 +1,46 @@
/*
CodeMirror "Time Machine" Theme
------------------------------------------------------------------------------
Intended for use with the IFQL CodeMirror Mode
*/
.cm-s-time-machine {
color: $g11-sidewalk;
.cm-variable {
color: $c-pool;
font-weight: 700;
}
.cm-function,
.cm-pipe-forward {
color: $c-comet;
font-weight: 700;
}
.cm-arrow-function,
.cm-function-arg,
.cm-function-arg-ref {
color: #ff4d96;
}
.cm-operator {
color: $g15-platinum;
}
.cm-argument {
color: $g11-sidewalk;
}
.cm-string-single,
.cm-string-double {
color: $c-honeydew;
}
.cm-boolean {
color: $c-viridian;
}
.cm-null {
color: $c-dreamsicle;
}
.cm-number {
color: $c-hydrogen;
}
.cm-comment {
color: $g8-storm;
}
}

View File

@ -0,0 +1,93 @@
/*
CodeMirror Styles
------------------------------------------------------------------------------
*/
.react-codemirror2 {
position: relative;
width: 100%;
height: 100%;
}
.CodeMirror {
border-radius: 0 0 $radius $radius;
font-family: $code-font;
background-color: transparent;
color: $g13-mist;
font-weight: 500;
font-size: 13px;
height: 100%;
}
.CodeMirror-vscrollbar {
@include custom-scrollbar-round($g2-kevlar, $g6-smoke);
}
.CodeMirror-hscrollbar {
@include custom-scrollbar-round($g0-obsidian, $g6-smoke);
}
.CodeMirror-gutters {
@include gradient-v($g2-kevlar, $g0-obsidian);
border: none;
}
.CodeMirror-gutters .CodeMirror-gutter {
background-color: fade-out($g4-onyx, 0.75);
height: calc(100% + 30px);
}
.CodeMirror-gutter.CodeMirror-linenumbers {
width: 60px;
}
.CodeMirror .CodeMirror-sizer {
margin-left: 60px;
}
.CodeMirror .CodeMirror-linenumber.CodeMirror-gutter-elt {
padding-right: 9px;
width: 46px;
color: $g8-storm;
}
.CodeMirror-guttermarker,
.CodeMirror-guttermarker-subtle,
.CodeMirror-linenumber {
color: rgb(83, 127, 126);
}
.CodeMirror-cursor {
width: 2px;
border: 0;
background-color: $g20-white;
box-shadow: 0 0 3px $c-laser, 0 0 6px $c-ocean, 0 0 11px $c-amethyst;
}
div.CodeMirror-selected,
.CodeMirror-focused div.CodeMirror-selected {
background-color: fade-out($g8-storm, 0.7);
}
.CodeMirror-line::selection,
.CodeMirror-line>span::selection,
.CodeMirror-line>span>span::selection {
background: rgba(255, 255, 255, 0.10);
}
.CodeMirror-line::-moz-selection,
.CodeMirror-line>span::-moz-selection,
.CodeMirror-line>span>span::-moz-selection {
background: rgba(255, 255, 255, 0.10);
}
.CodeMirror-activeline-background {
background: rgba(0, 0, 0, 0);
}
/*
Themes & Sub-Components
------------------------------------------------------------------------------
*/
@import 'time-machine';
@import 'tickscript';
@import 'hints';

View File

@ -3,7 +3,6 @@
------------------------------------------------------------------------------
*/
$threesizer-handle: 30px;
.threesizer {
width: 100%;
height: 100%;
@ -73,16 +72,19 @@ $threesizer-handle: 30px;
.threesizer--title {
padding-left: 14px;
position: relative;
font-size: 16px;
font-weight: 500;
font-size: 14px;
font-weight: 600;
white-space: nowrap;
text-transform: uppercase;
letter-spacing: 0.05em;
color: $g11-sidewalk;
z-index: 1;
transition: transform 0.25s ease;
transition: transform 0.25s ease, letter-spacing 0.25s ease;
&.vertical {
transform: translate(28px, 14px);
transform: translate(20px, 14px);
&.threesizer--collapsed {
transform: translate(0, 3px) rotate(90deg);
transform: translate(0, 5px) rotate(90deg) scale(0.75);
letter-spacing: 0.15em;
}
}
}
@ -138,11 +140,11 @@ $threesizer-shadow-stop: fade-out($g0-obsidian, 1);
padding: 0 11px;
background-color: $g2-kevlar;
.horizontal>& {
width: 50px;
width: 44px;
border-right: 2px solid $g4-onyx;
}
.vertical>& {
height: 50px;
height: 44px;
border-bottom: 2px solid $g4-onyx;
}
}

Binary file not shown.

View File

@ -19,36 +19,35 @@
<glyph unicode="&#xe908;" glyph-name="group" d="M742.4 327.68c58.88 28.16 99.84 89.6 99.84 161.28 0 99.84-79.36 179.2-179.2 179.2-64 0-120.32-33.28-153.6-84.48 46.080-40.96 76.8-102.4 76.8-171.52 0-56.32-20.48-110.080-56.32-151.040 71.68-51.2 133.12-140.8 133.12-243.2v-94.72h238.080c10.24 0 17.92 10.24 17.92 25.6v145.92c0 112.64-89.6 209.92-176.64 232.96zM435.2 250.88c58.88 28.16 99.84 89.6 99.84 161.28 0 99.84-79.36 179.2-179.2 179.2s-179.2-79.36-179.2-179.2c0-71.68 40.96-130.56 99.84-161.28-81.92-23.040-174.080-120.32-174.080-235.52v-143.36c0-12.8 7.68-25.6 17.92-25.6h476.16c10.24 0 17.92 10.24 17.92 25.6v145.92c-2.56 112.64-92.16 209.92-179.2 232.96z" />
<glyph unicode="&#xe909;" glyph-name="remove" d="M870.4 23.040l-215.040 215.040c-10.24 10.24-10.24 25.6 0 35.84l215.040 215.040c33.28 33.28 38.4 89.6 7.68 128-35.84 38.4-94.72 38.4-130.56 2.56l-217.6-217.6c-10.24-10.24-25.6-10.24-35.84 0l-215.040 212.48c-33.28 33.28-89.6 38.4-125.44 7.68-38.4-35.84-38.4-94.72-2.56-130.56l217.6-217.6c10.24-10.24 10.24-25.6 0-35.84l-215.040-215.040c-33.28-33.28-38.4-89.6-7.68-128 35.84-38.4 94.72-38.4 130.56-2.56l217.6 217.6c10.24 10.24 25.6 10.24 35.84 0l217.6-217.6c35.84-35.84 94.72-35.84 130.56 2.56 30.72 38.4 25.6 94.72-7.68 128z" />
<glyph unicode="&#xe90a;" glyph-name="plus" d="M929.28 345.6h-302.080c-12.8 0-25.6 10.24-25.6 25.6v302.080c0 48.64-35.84 92.16-84.48 94.72-51.2 2.56-94.72-38.4-94.72-89.6v-307.2c0-12.8-10.24-25.6-25.6-25.6h-302.080c-48.64 0-92.16-35.84-94.72-84.48-2.56-51.2 38.4-94.72 89.6-94.72h307.2c12.8 0 25.6-10.24 25.6-25.6v-302.080c0-48.64 35.84-92.16 84.48-94.72 51.2-2.56 94.72 38.4 94.72 89.6v307.2c0 12.8 10.24 25.6 25.6 25.6h307.2c51.2 0 92.16 43.52 89.6 94.72-2.56 48.64-46.080 84.48-94.72 84.48z" />
<glyph unicode="&#xe90b;" glyph-name="arrow-up" d="M934.4 89.6c0-7.68-2.56-17.92-10.24-25.6-12.8-15.36-38.4-17.92-53.76-5.12l-358.4 312.32-358.4-309.76c-15.36-12.8-40.96-12.8-53.76 5.12-12.8 15.36-12.8 40.96 5.12 53.76l384 332.8c15.36 12.8 35.84 12.8 51.2 0l384-332.8c5.12-10.24 10.24-20.48 10.24-30.72z" />
<glyph unicode="&#xe90c;" glyph-name="arrow-left" d="M678.4 678.4c7.68 0 17.92-2.56 25.6-10.24 15.36-12.8 17.92-38.4 5.12-53.76l-312.32-358.4 309.76-358.4c12.8-15.36 12.8-40.96-5.12-53.76-15.36-12.8-40.96-12.8-53.76 5.12l-332.8 384c-12.8 15.36-12.8 35.84 0 51.2l332.8 384c10.24 5.12 20.48 10.24 30.72 10.24z" />
<glyph unicode="&#xe90b;" glyph-name="cube" d="M985.6 496.64c0 0 0 2.56-2.56 2.56 0 2.56-2.56 5.12-2.56 5.12s-2.56 2.56-2.56 2.56c0 2.56-2.56 2.56-2.56 5.12 0 0-2.56 2.56-2.56 2.56s-2.56 2.56-2.56 2.56c0 0 0 0 0 0s0 0 0 0l-437.76 230.4c-10.24 5.12-25.6 5.12-35.84 0l-437.76-230.4c-5.12 2.56-7.68 0-10.24-2.56 0 0 0 0-2.56-2.56s-2.56-2.56-5.12-5.12c0 0 0-2.56-2.56-2.56 0 0 0 0 0 0 0-2.56-2.56-5.12-2.56-7.68 0 0 0 0 0 0 0-2.56 0-5.12 0-7.68 0 0 0 0 0 0v-460.8c0-15.36 7.68-28.16 20.48-33.28l437.76-230.4c2.56 0 2.56-2.56 5.12-2.56 0 0 2.56 0 2.56 0 2.56 0 7.68-2.56 10.24-2.56 5.12 0 12.8 2.56 17.92 5.12l437.76 230.4c12.8 7.68 20.48 20.48 20.48 33.28v458.24c0 2.56 0 7.68-2.56 10.24zM512 673.28l355.84-186.88-355.84-186.88-355.84 186.88 355.84 186.88zM112.64 422.4l360.96-189.44v-373.76l-360.96 189.44v373.76z" />
<glyph unicode="&#xe90c;" glyph-name="export" d="M204.8 729.6h230.4c20.48 0 38.4-17.92 38.4-38.4s-17.92-38.4-38.4-38.4h-230.4c-48.64 0-89.6-40.96-89.6-89.6v-614.4c0-48.64 40.96-89.6 89.6-89.6h614.4c48.64 0 89.6 40.96 89.6 89.6v230.4c0 20.48 17.92 38.4 38.4 38.4s38.4-17.92 38.4-38.4v-230.4c0-92.16-74.24-166.4-166.4-166.4h-614.4c-92.16 0-166.4 74.24-166.4 166.4v614.4c0 92.16 74.24 166.4 166.4 166.4zM678.4 568.32l-89.6 89.6c-25.6 25.6-7.68 71.68 30.72 71.68h368.64v-368.64c0-38.4-46.080-56.32-71.68-30.72l-89.6 89.6-250.88-250.88c-40.96-40.96-104.96-40.96-145.92 0s-40.96 104.96 0 145.92l248.32 253.44z" />
<glyph unicode="&#xe90d;" glyph-name="user" d="M588.8 289.28c58.88 28.16 99.84 89.6 99.84 161.28 0 99.84-79.36 179.2-179.2 179.2s-179.2-79.36-179.2-179.2c0-71.68 40.96-130.56 99.84-161.28-84.48-20.48-176.64-120.32-176.64-232.96v-145.92c0-12.8 7.68-25.6 17.92-25.6h476.16c10.24 0 17.92 10.24 17.92 25.6v145.92c0 112.64-89.6 209.92-176.64 232.96z" />
<glyph unicode="&#xe90e;" glyph-name="graphline" d="M888.32 465.92c-17.92 0-33.28-5.12-48.64-12.8l-76.8 79.36c10.24 15.36 15.36 33.28 15.36 53.76 0 56.32-46.080 104.96-104.96 104.96s-104.96-46.080-104.96-104.96c0-33.28 15.36-64 40.96-81.92l-192-476.16c-2.56 0-7.68 0-10.24 0-20.48 0-40.96-7.68-58.88-17.92l-117.76 99.84c5.12 12.8 10.24 28.16 10.24 43.52 0 56.32-46.080 104.96-104.96 104.96s-104.96-46.080-104.96-104.96 46.080-104.96 104.96-104.96c20.48 0 40.96 7.68 58.88 17.92l117.76-99.84c-5.12-12.8-10.24-28.16-10.24-43.52 0-56.32 46.080-104.96 104.96-104.96s104.96 46.080 104.96 104.96c0 33.28-15.36 64-40.96 81.92l192 476.16c2.56 0 7.68 0 10.24 0 17.92 0 33.28 5.12 48.64 12.8l76.8-79.36c-10.24-15.36-15.36-33.28-15.36-53.76 0-56.32 46.080-104.96 104.96-104.96s104.96 46.080 104.96 104.96-48.64 104.96-104.96 104.96z" />
<glyph unicode="&#xe90f;" glyph-name="collapse" d="M560.64 189.44c-2.56 2.56-5.12 5.12-7.68 7.68 0 0-2.56 2.56-2.56 2.56-2.56 0-2.56 2.56-5.12 2.56s-2.56 0-5.12 2.56c-2.56 0-2.56 0-5.12 2.56-2.56 0-7.68 0-10.24 0 0 0 0 0 0 0s0 0 0 0c-2.56 0-7.68 0-10.24 0s-2.56 0-5.12 0-2.56 0-5.12-2.56c-2.56 0-2.56-2.56-5.12-2.56s-2.56-2.56-5.12-2.56-5.12-5.12-7.68-7.68l-204.8-204.8c-20.48-20.48-20.48-51.2 0-71.68s51.2-20.48 71.68 0l117.76 117.76v-238.080c0-28.16 23.040-51.2 51.2-51.2s51.2 23.040 51.2 51.2v235.52l117.76-117.76c10.24-10.24 23.040-15.36 35.84-15.36s25.6 5.12 35.84 15.36c20.48 20.48 20.48 51.2 0 71.68l-202.24 204.8zM488.96 322.56c2.56-2.56 5.12-5.12 7.68-7.68 0 0 2.56-2.56 5.12-2.56s2.56-2.56 5.12-2.56c2.56 0 2.56 0 5.12-2.56 2.56 0 2.56 0 5.12-2.56 2.56 0 7.68 0 10.24 0s7.68 0 10.24 0c2.56 0 2.56 0 5.12 2.56 2.56 0 2.56 0 5.12 2.56 2.56 0 2.56 2.56 5.12 2.56 0 0 2.56 0 2.56 2.56 2.56 2.56 5.12 5.12 7.68 7.68l204.8 204.8c20.48 20.48 20.48 51.2 0 71.68s-51.2 20.48-71.68 0l-117.76-117.76v235.52c0 28.16-23.040 51.2-51.2 51.2s-51.2-23.040-51.2-51.2v-235.52l-117.76 117.76c-20.48 20.48-51.2 20.48-71.68 0s-20.48-51.2 0-71.68l202.24-204.8z" />
<glyph unicode="&#xe910;" glyph-name="arrow-down" d="M89.6 422.4c0 7.68 2.56 17.92 10.24 25.6 12.8 15.36 38.4 17.92 53.76 5.12l358.4-309.76 358.4 309.76c15.36 12.8 40.96 12.8 53.76-5.12 12.8-15.36 12.8-40.96-5.12-53.76l-384-332.8c-15.36-12.8-35.84-12.8-51.2 0l-384 332.8c-5.12 7.68-10.24 17.92-10.24 28.16z" />
<glyph unicode="&#xe911;" glyph-name="arrow-right" d="M345.6-166.4c-7.68 0-17.92 2.56-25.6 10.24-15.36 12.8-17.92 38.4-5.12 53.76l309.76 358.4-307.2 358.4c-12.8 15.36-12.8 40.96 5.12 53.76 15.36 12.8 40.96 12.8 53.76-5.12l332.8-384c12.8-15.36 12.8-35.84 0-51.2l-332.8-384c-10.24-5.12-20.48-10.24-30.72-10.24z" />
<glyph unicode="&#xe910;" glyph-name="import" d="M819.2 729.6h-358.4c-20.48 0-38.4-17.92-38.4-38.4s17.92-38.4 38.4-38.4h358.4c48.64 0 89.6-40.96 89.6-89.6v-614.4c0-48.64-40.96-89.6-89.6-89.6h-614.4c-48.64 0-89.6 40.96-89.6 89.6v358.4c0 20.48-17.92 38.4-38.4 38.4s-38.4-17.92-38.4-38.4v-358.4c0-92.16 74.24-166.4 166.4-166.4h614.4c92.16 0 166.4 74.24 166.4 166.4v614.4c0 92.16-74.24 166.4-166.4 166.4zM384 238.080l-89.6-89.6c-25.6-25.6-7.68-71.68 30.72-71.68h366.080v368.64c0 38.4-46.080 56.32-71.68 30.72l-89.6-92.16-304.64 302.080c-40.96 40.96-104.96 40.96-145.92 0s-40.96-104.96 0-145.92l304.64-302.080z" />
<glyph unicode="&#xe911;" glyph-name="text-block" d="M1024 614.4c0-15.36-12.8-25.6-25.6-25.6h-537.6c-12.8 0-25.6 10.24-25.6 25.6v128c0 15.36 12.8 25.6 25.6 25.6h537.6c12.8 0 25.6-10.24 25.6-25.6v-128zM1024 332.8c0-12.8-12.8-25.6-25.6-25.6h-972.8c-15.36 0-25.6 12.8-25.6 25.6v128c0 15.36 10.24 25.6 25.6 25.6h972.8c12.8 0 25.6-10.24 25.6-25.6v-128zM1024 51.2c0-12.8-12.8-25.6-25.6-25.6h-972.8c-15.36 0-25.6 12.8-25.6 25.6v128c0 12.8 10.24 25.6 25.6 25.6h972.8c12.8 0 25.6-12.8 25.6-25.6v-128zM588.8-230.4c0-12.8-12.8-25.6-25.6-25.6h-537.6c-15.36 0-25.6 12.8-25.6 25.6v128c0 12.8 10.24 25.6 25.6 25.6h537.6c12.8 0 25.6-12.8 25.6-25.6v-128z" />
<glyph unicode="&#xe912;" glyph-name="okta" d="M816.64 642.56v-286.72c69.12 10.24 125.44 71.68 125.44 143.36s-56.32 135.68-125.44 143.36zM650.24 499.2c0-71.68 53.76-133.12 125.44-143.36v286.72c-71.68-7.68-125.44-69.12-125.44-143.36zM1024 499.2c0 125.44-102.4 227.84-227.84 227.84-48.64 0-94.72-15.36-130.56-40.96-58.88 25.6-125.44 40.96-194.56 40.96-258.56 0-471.040-209.92-471.040-471.040s212.48-471.040 471.040-471.040c261.12 0 471.040 212.48 471.040 471.040 0 23.040-2.56 46.080-5.12 66.56 53.76 43.52 87.040 107.52 87.040 176.64zM471.040-133.12c-215.040 0-389.12 174.080-389.12 389.12s176.64 389.12 389.12 389.12c46.080 0 89.6-7.68 130.56-23.040-15.36-23.040-25.6-48.64-30.72-76.8-30.72 10.24-64 17.92-99.84 17.92-168.96 0-307.2-138.24-307.2-307.2s138.24-307.2 307.2-307.2c168.96 0 307.2 138.24 307.2 307.2 0 5.12 0 12.8 0 17.92 5.12 0 12.8 0 17.92 0 23.040 0 43.52 2.56 64 10.24 0-7.68 2.56-17.92 2.56-25.6 0-217.6-176.64-391.68-391.68-391.68zM796.16 314.88c-102.4 0-186.88 84.48-186.88 186.88s84.48 186.88 186.88 186.88 186.88-84.48 186.88-186.88-84.48-186.88-186.88-186.88z" />
<glyph unicode="&#xe913;" glyph-name="bar-chart" d="M179.2 179.2c0 12.8-10.24 25.6-25.6 25.6h-128c-15.36 0-25.6-12.8-25.6-25.6v-409.6c0-12.8 10.24-25.6 25.6-25.6h128c15.36 0 25.6 12.8 25.6 25.6v409.6zM460.8 742.4c0 15.36-12.8 25.6-25.6 25.6h-128c-15.36 0-25.6-10.24-25.6-25.6v-972.8c0-12.8 10.24-25.6 25.6-25.6h128c12.8 0 25.6 12.8 25.6 25.6v972.8zM742.4 460.8c0 15.36-12.8 25.6-25.6 25.6h-128c-12.8 0-25.6-10.24-25.6-25.6v-691.2c0-12.8 12.8-25.6 25.6-25.6h128c12.8 0 25.6 12.8 25.6 25.6v691.2zM1024-51.2c0 12.8-12.8 25.6-25.6 25.6h-128c-12.8 0-25.6-12.8-25.6-25.6v-179.2c0-12.8 12.8-25.6 25.6-25.6h128c12.8 0 25.6 12.8 25.6 25.6v179.2z" />
<glyph unicode="&#xe916;" glyph-name="search" d="M993.28-99.84l-202.24 202.24c40.96 64 64 135.68 64 217.6 0 225.28-181.76 407.040-407.040 407.040s-404.48-184.32-404.48-407.040 181.76-407.040 407.040-407.040c79.36 0 153.6 23.040 217.6 64l202.24-202.24c17.92-17.92 40.96-25.6 64-25.6s46.080 7.68 64 25.6c30.72 33.28 30.72 92.16-5.12 125.44zM145.92 320c0 166.4 135.68 302.080 302.080 302.080s304.64-135.68 304.64-304.64c0-79.36-30.72-151.040-79.36-204.8-2.56-2.56-7.68-5.12-10.24-7.68s-5.12-7.68-7.68-10.24c-53.76-48.64-125.44-79.36-204.8-79.36-168.96 0-304.64 135.68-304.64 304.64z" />
<glyph unicode="&#xe917;" glyph-name="duplicate" d="M921.6 768h-563.2c-56.32 0-102.4-46.080-102.4-102.4v-153.6h-153.6c-56.32 0-102.4-46.080-102.4-102.4v-563.2c0-56.32 46.080-102.4 102.4-102.4h563.2c56.32 0 102.4 46.080 102.4 102.4v153.6h153.6c56.32 0 102.4 46.080 102.4 102.4v563.2c0 56.32-46.080 102.4-102.4 102.4zM691.2-153.6c0-12.8-12.8-25.6-25.6-25.6h-563.2c-12.8 0-25.6 12.8-25.6 25.6v563.2c0 12.8 12.8 25.6 25.6 25.6h153.6v-332.8c0-56.32 46.080-102.4 102.4-102.4h332.8v-153.6z" />
<glyph unicode="&#xe918;" glyph-name="checkmark" d="M345.6-87.040l-271.36 271.36c-33.28 33.28-38.4 89.6-7.68 128 35.84 38.4 94.72 38.4 130.56 2.56l168.96-168.96c5.12-5.12 12.8-5.12 15.36 0l442.88 442.88c35.84 35.84 94.72 35.84 130.56-2.56 33.28-35.84 28.16-92.16-7.68-128l-542.72-542.72c-15.36-20.48-43.52-20.48-58.88-2.56z" />
<glyph unicode="&#xe919;" glyph-name="cubo-node" horiz-adv-x="1008" d="M917.78 487.468c-8.792 2.928-20.512 2.928-29.296 5.856l-23.44 108.408c-2.928 8.792-8.792 17.576-20.512 20.512l-451.208 137.704c-8.792 2.928-20.512 0-29.296-5.856l-82.040-76.176c-8.792 5.856-17.576 11.72-29.296 14.648-64.456 20.512-131.848-17.576-149.424-79.112-8.792-32.232-5.856-64.456 8.792-93.76l-93.76-87.896c-8.792-5.856-11.72-17.576-8.792-29.296l105.48-460c2.928-8.792 8.792-17.576 20.512-20.512l114.264-35.16c-2.928-52.736 29.296-105.48 84.968-120.128 52.736-17.576 108.408 5.856 137.704 52.736l114.264-35.16c2.928 0 5.856 0 8.792 0 5.856 0 14.648 2.928 20.512 8.792l345.736 322.296c8.792 5.856 11.72 17.576 8.792 29.296l-29.296 125.984c26.368 14.648 46.88 38.088 55.672 70.32 20.512 58.6-17.576 125.984-79.112 146.496zM698.028 45.052l143.568 32.232-184.584-172.864 41.016 140.64zM577.9-151.26l-87.896 26.368c2.928 52.736-32.232 105.48-84.968 120.128-52.736 17.576-108.408-5.856-137.704-52.736l-84.968 26.368 61.528 202.168 395.544-120.128-61.528-202.168zM182.364 461.1c64.456-20.512 131.848 17.576 149.424 79.112 8.792 32.232 5.856 64.456-8.792 93.76l58.6 55.672 143.568-155.288-301.784-281.272-143.568 155.288 73.248 67.392c8.792-5.856 20.512-11.72 29.296-14.648zM281.98 220.844l269.552 251.976 82.040-360.384-351.592 108.408zM577.9 563.652l-99.616 108.408 243.184-73.248-143.568-35.16zM91.532 308.74l99.616-105.48-41.016-137.704-58.6 243.184zM906.060 153.46l-208.024-49.808-93.76 401.4 208.024 49.808 17.576-76.176c-29.296-14.648-52.736-38.088-64.456-73.248-20.512-64.456 17.576-131.848 79.112-149.424 11.72-2.928 23.44-5.856 35.16-5.856l26.368-96.688z" />
<glyph unicode="&#xe91a;" glyph-name="cubo-uniform" d="M1004.192 120.646l-109.376 470.316c-2.736 10.936-10.936 19.14-21.876 21.876l-462.112 142.188c-10.936 2.736-21.876 0-30.080-5.468l-352.736-330.86c-8.204-8.204-10.936-19.14-8.204-30.080l109.376-470.316c2.736-10.936 10.936-19.14 19.14-21.876l462.112-142.188c2.736 0 5.468 0 8.204 0 8.204 0 13.672 2.736 19.14 8.204l352.736 330.86c10.936 5.468 16.408 16.408 13.672 27.344zM574.896 462.442l84.764-369.144-363.672 112.108 278.908 257.032zM602.24 552.678l-101.172 109.376 248.828-76.564-147.656-32.812zM550.284 522.598l-308.984-289.844-147.656 161.328 308.984 289.844 147.656-161.328zM257.708 153.458l404.688-123.048-62.892-207.812-404.688 123.048 62.892 207.812zM725.288 24.942l147.656 32.812-188.672-177.736 41.016 144.924zM938.568 137.050l-213.284-49.22-95.704 412.892 213.284 49.22 95.704-412.892zM104.58 292.91l101.172-109.376-43.752-142.188-57.424 251.564z" />
<glyph unicode="&#xe91b;" glyph-name="clock" d="M512 768c-281.6 0-512-230.4-512-512s230.4-512 512-512 512 230.4 512 512-230.4 512-512 512zM512-179.2c-240.64 0-435.2 194.56-435.2 435.2s194.56 435.2 435.2 435.2 435.2-194.56 435.2-435.2-194.56-435.2-435.2-435.2zM742.4 268.8h-217.6v217.6c0 20.48-17.92 38.4-38.4 38.4s-38.4-17.92-38.4-38.4v-256c0-20.48 17.92-38.4 38.4-38.4h256c20.48 0 38.4 17.92 38.4 38.4s-17.92 38.4-38.4 38.4z" />
<glyph unicode="&#xe91d;" glyph-name="download" d="M985.6-256h-947.2c-20.48 0-38.4 17.92-38.4 38.4v307.2c0 20.48 17.92 38.4 38.4 38.4s38.4-17.92 38.4-38.4v-268.8h870.4v268.8c0 20.48 17.92 38.4 38.4 38.4s38.4-17.92 38.4-38.4v-307.2c0-20.48-17.92-38.4-38.4-38.4zM512-71.68c-10.24 0-23.040 5.12-30.72 12.8l-337.92 412.16c-10.24 10.24-10.24 28.16-5.12 40.96s20.48 23.040 35.84 23.040h145.92v312.32c0 20.48 17.92 38.4 38.4 38.4h307.2c20.48 0 38.4-17.92 38.4-38.4v-312.32h145.92c15.36 0 28.16-7.68 35.84-23.040 5.12-12.8 5.12-28.16-5.12-40.96l-337.92-412.16c-7.68-7.68-20.48-12.8-30.72-12.8zM256 337.92l256-312.32 256 312.32h-102.4c-20.48 0-38.4 17.92-38.4 38.4v314.88h-230.4v-312.32c0-20.48-17.92-38.4-38.4-38.4h-102.4z" />
<glyph unicode="&#xe91b;" glyph-name="clock" d="M512 768c-281.6 0-512-230.4-512-512s230.4-512 512-512 512 230.4 512 512-230.4 512-512 512zM768 179.2h-281.6c-28.16 0-51.2 23.040-51.2 51.2v281.6c0 28.16 23.040 51.2 51.2 51.2s51.2-23.040 51.2-51.2v-230.4h230.4c28.16 0 51.2-23.040 51.2-51.2s-23.040-51.2-51.2-51.2z" />
<glyph unicode="&#xe91d;" glyph-name="download" d="M407.040 235.52h-125.44c-35.84 0-56.32-46.080-28.16-71.68l258.56-258.56 261.12 261.12c28.16 28.16 7.68 71.68-28.16 71.68l-128-2.56-5.12 430.080c0 58.88-46.080 102.4-102.4 102.4-58.88 0-102.4-46.080-102.4-102.4v-430.080zM908.8 0v-51.2c0-48.64-40.96-89.6-89.6-89.6h-614.4c-48.64 0-89.6 40.96-89.6 89.6v51.2c0 20.48-17.92 38.4-38.4 38.4s-38.4-17.92-38.4-38.4v-51.2c0-92.16 74.24-166.4 166.4-166.4h614.4c92.16 0 166.4 74.24 166.4 166.4v51.2c0 20.48-17.92 38.4-38.4 38.4v0c-20.48 0-38.4-17.92-38.4-38.4z" />
<glyph unicode="&#xe927;" glyph-name="dash-f" d="M320 345.6c0 28.16-23.040 51.2-51.2 51.2h-179.2c-28.16 0-51.2-23.040-51.2-51.2v-179.2c0-28.16 23.040-51.2 51.2-51.2h179.2c28.16 0 51.2 23.040 51.2 51.2v179.2zM320 678.4c0 28.16-23.040 51.2-51.2 51.2h-179.2c-28.16 0-51.2-23.040-51.2-51.2v-179.2c0-28.16 23.040-51.2 51.2-51.2h179.2c28.16 0 51.2 23.040 51.2 51.2v179.2zM601.6 448c28.16 0 51.2 23.040 51.2 51.2v179.2c0 28.16-23.040 51.2-51.2 51.2h-179.2c-28.16 0-51.2-23.040-51.2-51.2v-179.2c0-28.16 23.040-51.2 51.2-51.2h179.2zM652.8 345.6c0 28.16-23.040 51.2-51.2 51.2h-179.2c-28.16 0-51.2-23.040-51.2-51.2v-179.2c0-28.16 23.040-51.2 51.2-51.2h179.2c28.16 0 51.2 23.040 51.2 51.2v179.2zM89.6 64c-28.16 0-51.2-23.040-51.2-51.2v-179.2c0-28.16 23.040-51.2 51.2-51.2h179.2c28.16 0 51.2 23.040 51.2 51.2v179.2c0 28.16-23.040 51.2-51.2 51.2h-179.2zM422.4 64c-28.16 0-51.2-23.040-51.2-51.2v-179.2c0-28.16 23.040-51.2 51.2-51.2h179.2c28.16 0 51.2 23.040 51.2 51.2v179.2c0 28.16-23.040 51.2-51.2 51.2h-179.2zM755.2 729.6c-28.16 0-51.2-23.040-51.2-51.2v-179.2c0-28.16 23.040-51.2 51.2-51.2h179.2c28.16 0 51.2 23.040 51.2 51.2v179.2c0 28.16-23.040 51.2-51.2 51.2h-179.2zM704 166.4c0-28.16 23.040-51.2 51.2-51.2h179.2c28.16 0 51.2 23.040 51.2 51.2v179.2c0 28.16-23.040 51.2-51.2 51.2h-179.2c-28.16 0-51.2-23.040-51.2-51.2v-179.2zM704-166.4c0-28.16 23.040-51.2 51.2-51.2h179.2c28.16 0 51.2 23.040 51.2 51.2v179.2c0 28.16-23.040 51.2-51.2 51.2h-179.2c-28.16 0-51.2-23.040-51.2-51.2v-179.2z" />
<glyph unicode="&#xe929;" glyph-name="dash-h" d="M320 512c0 28.16-23.040 51.2-51.2 51.2h-179.2c-28.16 0-51.2-23.040-51.2-51.2v-179.2c0-28.16 23.040-51.2 51.2-51.2h179.2c28.16 0 51.2 23.040 51.2 51.2v179.2zM89.6 230.4c-28.16 0-51.2-23.040-51.2-51.2v-179.2c0-28.16 23.040-51.2 51.2-51.2h179.2c28.16 0 51.2 23.040 51.2 51.2v179.2c0 28.16-23.040 51.2-51.2 51.2h-179.2zM422.4 230.4c-28.16 0-51.2-23.040-51.2-51.2v-179.2c0-28.16 23.040-51.2 51.2-51.2h179.2c28.16 0 51.2 23.040 51.2 51.2v179.2c0 28.16-23.040 51.2-51.2 51.2h-179.2zM371.2 332.8c0-28.16 23.040-51.2 51.2-51.2h512c28.16 0 51.2 23.040 51.2 51.2v179.2c0 28.16-23.040 51.2-51.2 51.2h-512c-28.16 0-51.2-23.040-51.2-51.2v-179.2zM704 0c0-28.16 23.040-51.2 51.2-51.2h179.2c28.16 0 51.2 23.040 51.2 51.2v179.2c0 28.16-23.040 51.2-51.2 51.2h-179.2c-28.16 0-51.2-23.040-51.2-51.2v-179.2z" />
<glyph unicode="&#xe92c;" glyph-name="triangle" d="M832-153.6h-640c-53.76 0-97.28 20.48-120.32 58.88-20.48 35.84-17.92 84.48 10.24 133.12l320 552.96c28.16 46.080 66.56 74.24 110.080 74.24s81.92-28.16 110.080-74.24l320-552.96c28.16-46.080 30.72-94.72 10.24-133.12-23.040-35.84-66.56-58.88-120.32-58.88zM512 588.8c-15.36 0-30.72-12.8-43.52-35.84l-320-552.96c-12.8-23.040-15.36-43.52-10.24-56.32 7.68-12.8 25.6-20.48 53.76-20.48h640c25.6 0 46.080 7.68 53.76 20.48s5.12 33.28-10.24 56.32l-320 552.96c-12.8 23.040-28.16 35.84-43.52 35.84z" />
<glyph unicode="&#xe92d;" glyph-name="octagon" d="M686.080-204.8h-350.72c-10.24 0-20.48 5.12-28.16 10.24l-248.32 248.32c-7.68 7.68-10.24 17.92-10.24 28.16v350.72c0 10.24 5.12 20.48 10.24 28.16l248.32 248.32c7.68 7.68 17.92 10.24 28.16 10.24h350.72c10.24 0 20.48-5.12 28.16-10.24l248.32-248.32c7.68-7.68 10.24-17.92 10.24-28.16v-350.72c0-10.24-5.12-20.48-10.24-28.16l-248.32-248.32c-7.68-5.12-17.92-10.24-28.16-10.24zM353.28-128h317.44l225.28 225.28v317.44l-225.28 225.28h-317.44l-225.28-225.28v-317.44l225.28-225.28z" />
<glyph unicode="&#xe92e;" glyph-name="cube" horiz-adv-x="896" d="M417.28 295.68c17.92-10.24 40.96-10.24 58.88 0l348.16 202.24c7.68 5.12 7.68 15.36 0 20.48l-343.040 197.12c-23.040 12.8-51.2 12.8-74.24 0l-343.040-197.12c-7.68-5.12-7.68-15.36 0-20.48l353.28-202.24zM422.4 200.96c0 20.48-10.24 40.96-30.72 51.2l-348.16 202.24c-7.68 5.12-17.92 0-17.92-10.24v-394.24c0-25.6 12.8-51.2 35.84-64l343.040-197.12c7.68-5.12 17.92 0 17.92 10.24v401.92zM504.32 252.16c-17.92-10.24-30.72-30.72-30.72-51.2v-401.92c0-7.68 10.24-15.36 17.92-10.24l343.040 197.12c23.040 12.8 35.84 38.4 35.84 64v394.24c0 7.68-10.24 15.36-17.92 10.24l-348.16-202.24z" />
<glyph unicode="&#xe936;" glyph-name="pulse-c" d="M399.36 202.24v104.96c0 7.68 5.12 15.36 10.24 20.48l92.16 53.76c7.68 5.12 15.36 5.12 23.040 0l92.16-53.76c7.68-5.12 10.24-12.8 10.24-20.48v-104.96c0-7.68-5.12-15.36-10.24-20.48l-92.16-53.76c-7.68-5.12-15.36-5.12-23.040 0l-92.16 53.76c-7.68 5.12-10.24 12.8-10.24 20.48zM732.16 186.88v69.12c0 15.36-12.8 28.16-28.16 25.6v0c-12.8-2.56-23.040-12.8-23.040-25.6v-69.12c0-17.92-10.24-33.28-23.040-40.96l-58.88-33.28c-10.24-5.12-15.36-20.48-10.24-30.72v0c5.12-15.36 23.040-20.48 35.84-12.8l58.88 33.28c30.72 15.36 48.64 48.64 48.64 84.48zM460.8 614.4l-128-74.24c-12.8-7.68-17.92-25.6-7.68-38.4v0c7.68-10.24 23.040-12.8 33.28-7.68l128 74.24c7.68 5.12 17.92 7.68 25.6 7.68 10.24 0 17.92-2.56 25.6-7.68l128-74.24c10.24-5.12 25.6-2.56 33.28 7.68v0c10.24 12.8 5.12 30.72-7.68 38.4l-128 74.24c-30.72 17.92-71.68 17.92-102.4 0zM340.48 99.84l58.88-33.28c12.8-7.68 30.72-2.56 35.84 12.8v0c5.12 12.8 0 25.6-10.24 30.72l-58.88 33.28c-15.36 7.68-23.040 25.6-23.040 40.96v69.12c0 12.8-10.24 23.040-23.040 25.6v0c-15.36 2.56-28.16-10.24-28.16-25.6v-69.12c0-33.28 17.92-66.56 48.64-84.48zM463.36 481.28l-58.88-33.28c-12.8-7.68-17.92-25.6-7.68-38.4v0c7.68-10.24 23.040-12.8 33.28-7.68l71.68 40.96c2.56 2.56 7.68 2.56 12.8 2.56s7.68 0 12.8-2.56l71.68-40.96c10.24-5.12 25.6-2.56 33.28 7.68v0c10.24 12.8 5.12 30.72-7.68 38.4l-58.88 33.28c-35.84 17.92-71.68 17.92-102.4 0zM286.72 588.8l163.84 94.72c38.4 23.040 87.040 23.040 125.44 0l163.84-94.72c10.24-5.12 25.6-2.56 33.28 7.68v0c10.24 12.8 5.12 30.72-7.68 38.4l-176.64 102.4c-46.080 28.16-104.96 28.16-151.040 0l-176.64-102.4c-12.8-7.68-17.92-25.6-7.68-38.4v0c7.68-10.24 23.040-12.8 33.28-7.68zM913.92 284.16v-186.88c0-43.52-23.040-87.040-61.44-107.52l-163.84-94.72c-10.24-5.12-15.36-20.48-10.24-30.72v0c5.12-15.36 23.040-20.48 35.84-12.8l163.84 94.72c53.76 30.72 87.040 89.6 87.040 153.6v186.88c0 15.36-12.8 28.16-28.16 25.6v0c-12.8-5.12-23.040-15.36-23.040-28.16zM240.64 25.6l115.2-66.56c12.8-7.68 30.72-2.56 35.84 12.8v0c5.12 12.8 0 25.6-10.24 30.72l-115.2 66.56c-23.040 12.8-38.4 38.4-38.4 66.56v133.12c0 12.8-10.24 23.040-23.040 25.6v0c-15.36 2.56-28.16-10.24-28.16-25.6v-133.12c0-46.080 25.6-87.040 64-110.080zM335.36-104.96l-163.84 94.72c-38.4 23.040-61.44 64-61.44 107.52v186.88c0 12.8-10.24 23.040-23.040 25.6v0c-15.36 2.56-28.16-10.24-28.16-25.6v-186.88c0-64 33.28-120.32 87.040-153.6l163.84-94.72c12.8-7.68 30.72-2.56 35.84 12.8v0c5.12 12.8 2.56 25.6-10.24 33.28zM847.36 135.68v133.12c0 15.36-12.8 28.16-28.16 25.6v0c-12.8-2.56-23.040-12.8-23.040-25.6v-133.12c0-28.16-15.36-53.76-38.4-66.56l-115.2-66.56c-10.24-5.12-15.36-20.48-10.24-30.72v0c5.12-15.36 23.040-20.48 35.84-12.8l115.2 66.56c38.4 23.040 64 64 64 110.080z" />
<glyph unicode="&#xe939;" glyph-name="brush" d="M819.2 332.8v99.84c0 15.36-12.8 28.16-28.16 28.16h-133.12c-23.040 0-43.52 20.48-43.52 43.52v161.28c0 56.32-46.080 102.4-102.4 102.4s-102.4-46.080-102.4-102.4v-161.28c0-23.040-20.48-43.52-43.52-43.52h-133.12c-15.36 0-28.16-12.8-28.16-28.16v-99.84h614.4zM512 716.8c28.16 0 51.2-23.040 51.2-51.2s-23.040-51.2-51.2-51.2-51.2 23.040-51.2 51.2 23.040 51.2 51.2 51.2zM204.8 281.6v-537.6l76.8 76.8 76.8-76.8 76.8 76.8 76.8-76.8 76.8 76.8 76.8-76.8 76.8 76.8 76.8-76.8v537.6z" />
<glyph unicode="&#xe93e;" glyph-name="square" d="M819.2-166.4h-614.4c-64 0-115.2 51.2-115.2 115.2v614.4c0 64 51.2 115.2 115.2 115.2h614.4c64 0 115.2-51.2 115.2-115.2v-614.4c0-64-51.2-115.2-115.2-115.2zM204.8 601.6c-20.48 0-38.4-17.92-38.4-38.4v-614.4c0-20.48 17.92-38.4 38.4-38.4h614.4c20.48 0 38.4 17.92 38.4 38.4v614.4c0 20.48-17.92 38.4-38.4 38.4h-614.4z" />
<glyph unicode="&#xe940;" glyph-name="circle" d="M512-140.8c-217.6 0-396.8 179.2-396.8 396.8s179.2 396.8 396.8 396.8 396.8-179.2 396.8-396.8-179.2-396.8-396.8-396.8zM512 576c-176.64 0-320-143.36-320-320s143.36-320 320-320 320 143.36 320 320-143.36 320-320 320z" />
<glyph unicode="&#xe941;" glyph-name="export" d="M921.6-153.6c0-28.16-23.040-51.2-51.2-51.2h-768c-28.16 0-51.2 23.040-51.2 51.2v768c0 28.16 23.040 51.2 51.2 51.2h281.6c15.36 0 25.6 10.24 25.6 25.6v0c0 15.36-10.24 25.6-25.6 25.6h-281.6c-56.32 0-102.4-46.080-102.4-102.4v-768c0-56.32 46.080-102.4 102.4-102.4h768c56.32 0 102.4 46.080 102.4 102.4v281.6c0 15.36-10.24 25.6-25.6 25.6v0c-15.36 0-25.6-10.24-25.6-25.6v-281.6zM921.6 768h-384c-15.36 0-25.6-10.24-25.6-25.6v-102.4c0-15.36 10.24-25.6 25.6-25.6h225.28l-414.72-414.72c-10.24-10.24-10.24-25.6 0-35.84l71.68-71.68c10.24-10.24 25.6-10.24 35.84 0l414.72 414.72v-225.28c0-15.36 10.24-25.6 25.6-25.6h102.4c15.36 0 25.6 10.24 25.6 25.6v384c0 56.32-46.080 102.4-102.4 102.4z" />
<glyph unicode="&#xe942;" glyph-name="expand-b" d="M947.2 665.6h-870.4c-43.52 0-76.8-33.28-76.8-76.8v-665.6c0-43.52 33.28-76.8 76.8-76.8h870.4c43.52 0 76.8 33.28 76.8 76.8v665.6c0 43.52-33.28 76.8-76.8 76.8zM307.2-64c0-7.68-5.12-12.8-12.8-12.8h-192c-15.36 0-25.6 10.24-25.6 25.6v192c0 7.68 5.12 12.8 12.8 12.8h51.2c7.68 0 12.8-5.12 12.8-12.8v-140.8h140.8c7.68 0 12.8-5.12 12.8-12.8v-51.2zM307.2 524.8c0-7.68-5.12-12.8-12.8-12.8h-140.8v-140.8c0-7.68-5.12-12.8-12.8-12.8h-51.2c-7.68 0-12.8 5.12-12.8 12.8v192c0 15.36 10.24 25.6 25.6 25.6h192c7.68 0 12.8-5.12 12.8-12.8v-51.2zM947.2-51.2c0-15.36-10.24-25.6-25.6-25.6h-192c-7.68 0-12.8 5.12-12.8 12.8v51.2c0 7.68 5.12 12.8 12.8 12.8h140.8v140.8c0 7.68 5.12 12.8 12.8 12.8h51.2c7.68 0 12.8-5.12 12.8-12.8v-192zM947.2 371.2c0-7.68-5.12-12.8-12.8-12.8h-51.2c-7.68 0-12.8 5.12-12.8 12.8v140.8h-140.8c-7.68 0-12.8 5.12-12.8 12.8v51.2c0 7.68 5.12 12.8 12.8 12.8h192c15.36 0 25.6-10.24 25.6-25.6v-192z" />
<glyph unicode="&#xe944;" glyph-name="expand-a" d="M870.4 25.6c0-12.8-12.8-25.6-25.6-25.6h-665.6c-12.8 0-25.6 12.8-25.6 25.6v460.8c0 12.8 12.8 25.6 25.6 25.6h665.6c12.8 0 25.6-12.8 25.6-25.6v-460.8zM89.6 588.8h153.6c7.68 0 12.8 5.12 12.8 12.8v51.2c0 7.68-5.12 12.8-12.8 12.8h-166.4c-43.52 0-76.8-33.28-76.8-76.8v-166.4c0-7.68 5.12-12.8 12.8-12.8h51.2c7.68 0 12.8 5.12 12.8 12.8v153.6c0 7.68 5.12 12.8 12.8 12.8zM76.8-64v153.6c0 7.68-5.12 12.8-12.8 12.8h-51.2c-7.68 0-12.8-5.12-12.8-12.8v-166.4c0-43.52 33.28-76.8 76.8-76.8h166.4c7.68 0 12.8 5.12 12.8 12.8v51.2c0 7.68-5.12 12.8-12.8 12.8h-153.6c-7.68 0-12.8 5.12-12.8 12.8zM934.4-76.8h-153.6c-7.68 0-12.8-5.12-12.8-12.8v-51.2c0-7.68 5.12-12.8 12.8-12.8h166.4c43.52 0 76.8 33.28 76.8 76.8v166.4c0 7.68-5.12 12.8-12.8 12.8h-51.2c-7.68 0-12.8-5.12-12.8-12.8v-153.6c0-7.68-5.12-12.8-12.8-12.8zM947.2 576v-153.6c0-7.68 5.12-12.8 12.8-12.8h51.2c7.68 0 12.8 5.12 12.8 12.8v166.4c0 43.52-33.28 76.8-76.8 76.8h-166.4c-7.68 0-12.8-5.12-12.8-12.8v-51.2c0-7.68 5.12-12.8 12.8-12.8h153.6c7.68 0 12.8-5.12 12.8-12.8z" />
<glyph unicode="&#xe947;" glyph-name="heroku" d="M819.2 768h-614.4c-43.52 0-76.8-33.28-76.8-76.8v-870.4c0-43.52 33.28-76.8 76.8-76.8h614.4c43.52 0 76.8 33.28 76.8 76.8v870.4c0 43.52-33.28 76.8-76.8 76.8zM399.36-2.56l-115.2-92.16c0 0-2.56-2.56-5.12-2.56 0 0-2.56 0-2.56 0-2.56 0-2.56 2.56-2.56 5.12v194.56c0 2.56 2.56 5.12 2.56 5.12 2.56 0 5.12 0 7.68 0l115.2-102.4c2.56 0 2.56-2.56 2.56-5.12 0 0 0-2.56-2.56-2.56zM750.080-89.6c0-2.56-2.56-5.12-5.12-5.12h-115.2c-2.56 0-5.12 2.56-5.12 5.12v268.8c0 23.040 0 43.52-15.36 56.32-12.8 10.24-35.84 12.8-71.68 7.68-46.080-7.68-97.28-20.48-143.36-33.28-2.56 0-5.12-2.56-7.68-2.56s-7.68-2.56-10.24-2.56c-2.56 0-5.12-2.56-7.68-2.56s-7.68-2.56-10.24-2.56c-2.56 0-5.12-2.56-7.68-2.56s-5.12-2.56-7.68-2.56c-2.56 0-5.12-2.56-7.68-2.56s-5.12-2.56-7.68-2.56c-2.56 0-5.12-2.56-7.68-2.56s-2.56-2.56-5.12-2.56-5.12-2.56-7.68-5.12c0 0-2.56 0-2.56-2.56-2.56-2.56-5.12-2.56-7.68-5.12 0 0-2.56 0-2.56 0-2.56-2.56-5.12-5.12-7.68-5.12 0 0-2.56 0-2.56 0s0 0 0 0 0 0 0 0-2.56 0-2.56 0c0 0 0 0 0 0s0 0 0 0 0 0 0 0 0 2.56 0 2.56c0 0 0 0 0 2.56 0 0 0 0 0 0v432.64c0 2.56 2.56 5.12 5.12 5.12h115.2c2.56 0 5.12-2.56 5.12-5.12v-261.12c58.88 17.92 138.24 33.28 212.48 23.040 81.92-10.24 135.68-48.64 135.68-189.44v-266.24zM668.16 435.2c0-2.56-2.56-2.56-5.12-2.56h-115.2c-2.56 0-5.12 2.56-5.12 2.56s0 5.12 0 7.68c28.16 38.4 79.36 107.52 79.36 158.72 0 2.56 2.56 5.12 5.12 5.12h115.2c2.56 0 5.12-2.56 5.12-5.12 2.56-53.76-51.2-125.44-79.36-166.4z" />
<glyph unicode="&#xe948;" glyph-name="heroku-simple" d="M348.16-120.32l-166.4-133.12c-2.56-2.56-2.56-2.56-5.12-2.56s-2.56 0-5.12 0c-2.56 2.56-5.12 5.12-5.12 7.68v281.6c0 2.56 2.56 7.68 5.12 7.68 2.56 2.56 7.68 0 10.24-2.56l166.4-148.48c2.56-2.56 2.56-5.12 2.56-7.68 2.56 0 0-2.56-2.56-2.56zM857.6-245.76c0-5.12-5.12-10.24-10.24-10.24h-166.4c-5.12 0-10.24 5.12-10.24 10.24v391.68c0 30.72 0 64-20.48 81.92-17.92 15.36-53.76 20.48-102.4 12.8-66.56-10.24-140.8-28.16-207.36-48.64-5.12 0-7.68-2.56-10.24-2.56-5.12-2.56-10.24-2.56-15.36-5.12-2.56 0-7.68-2.56-10.24-2.56-5.12-2.56-10.24-2.56-12.8-5.12-2.56 0-7.68-2.56-10.24-2.56-5.12-2.56-7.68-2.56-12.8-5.12-2.56-2.56-7.68-2.56-10.24-5.12s-7.68-2.56-10.24-5.12c-5.12-2.56-7.68-2.56-12.8-5.12-2.56 0-5.12-2.56-7.68-2.56-5.12-2.56-7.68-5.12-12.8-7.68-2.56 0-2.56-2.56-5.12-2.56-5.12-2.56-7.68-5.12-12.8-7.68 0 0-2.56 0-2.56-2.56-5.12-2.56-7.68-5.12-12.8-7.68-2.56 0-2.56-2.56-5.12-2.56 0 0 0 0 0 0s0 0 0 0-2.56 0-2.56 0c0 0 0 0 0 0s0 0 0 0 0 0 0 0-2.56 2.56-2.56 2.56c0 0 0 0 0 2.56 0 0 0 0 0 0v629.76c0 5.12 5.12 10.24 10.24 10.24h166.4c5.12 0 10.24-5.12 10.24-10.24v-378.88c87.040 25.6 202.24 48.64 307.2 33.28 117.76-15.36 197.12-69.12 197.12-276.48v-378.88zM739.84 517.12c-2.56-2.56-5.12-5.12-7.68-5.12h-166.4c-2.56 0-7.68 2.56-7.68 5.12s-2.56 7.68 0 10.24c40.96 56.32 115.2 158.72 115.2 230.4 0 5.12 5.12 10.24 10.24 10.24h166.4c5.12 0 10.24-5.12 10.24-10.24-2.56-76.8-79.36-181.76-120.32-240.64z" />
<glyph unicode="&#xe949;" glyph-name="refresh" d="M417.28 442.88l-261.12 28.16c-15.36 2.56-25.6 15.36-25.6 30.72l28.16 261.12c0 7.68 10.24 10.24 17.92 5.12l248.32-307.2c5.12-10.24 0-20.48-7.68-17.92zM865.28-248.32l28.16 261.12c2.56 15.36-10.24 28.16-25.6 30.72l-261.12 28.16c-7.68 0-12.8-7.68-7.68-15.36l248.32-307.2c5.12-7.68 15.36-5.12 17.92 2.56zM834.56 578.56c-87.040 87.040-202.24 133.12-322.56 133.12-110.080 0-217.6-40.96-302.080-112.64l-10.24-10.24 58.88-58.88 10.24 7.68c84.48 74.24 199.68 104.96 314.88 81.92 51.2-10.24 99.84-30.72 143.36-61.44 130.56-94.72 186.88-253.44 145.92-404.48-5.12-20.48 5.12-43.52 23.040-51.2 5.12-2.56 10.24-2.56 17.92-2.56 5.12 0 12.8 2.56 17.92 5.12 10.24 5.12 20.48 15.36 23.040 28.16 43.52 161.28-2.56 327.68-120.32 445.44zM755.2-28.16c-84.48-74.24-199.68-104.96-314.88-81.92-51.2 10.24-99.84 30.72-143.36 61.44-130.56 94.72-186.88 250.88-145.92 404.48 5.12 20.48-5.12 43.52-23.040 51.2-10.24 5.12-23.040 5.12-35.84 0-10.24-5.12-17.92-15.36-20.48-25.6-46.080-161.28-2.56-327.68 117.76-445.44 87.040-87.040 202.24-133.12 322.56-133.12 110.080 0 217.6 40.96 302.080 112.64l10.24 10.24-58.88 58.88-10.24-12.8z" />
<glyph unicode="&#xe949;" glyph-name="refresh" d="M829.44 2.56l-225.28 23.040c-7.68 0-10.24-7.68-7.68-12.8l69.12-84.48c-69.12-33.28-145.92-43.52-222.72-25.6-51.2 10.24-97.28 30.72-138.24 58.88-128 92.16-181.76 240.64-140.8 391.68 7.68 25.6-7.68 56.32-30.72 66.56-12.8 7.68-30.72 7.68-46.080 0-12.8-7.68-23.040-20.48-28.16-33.28-46.080-168.96-2.56-337.92 120.32-460.8 87.040-87.040 204.8-135.68 332.8-135.68 76.8 0 153.6 20.48 222.72 56.32l79.36-97.28c5.12-7.68 12.8-5.12 15.36 2.56l23.040 225.28c2.56 12.8-7.68 23.040-23.040 25.6zM844.8 588.8c-87.040 87.040-204.8 135.68-332.8 135.68-76.8 0-153.6-20.48-222.72-56.32l-79.36 97.28c-5.12 5.12-15.36 2.56-15.36-5.12l-23.040-225.28c0-12.8 7.68-23.040 23.040-25.6l225.28-23.040c7.68-2.56 10.24 7.68 7.68 15.36l-66.56 81.92c66.56 30.72 143.36 40.96 220.16 25.6 51.2-10.24 97.28-30.72 138.24-58.88 125.44-92.16 181.76-243.2 140.8-391.68-7.68-25.6 7.68-56.32 30.72-66.56 7.68-5.12 15.36-5.12 23.040-5.12s17.92 2.56 23.040 7.68c12.8 5.12 25.6 17.92 30.72 35.84 43.52 163.84-2.56 335.36-122.88 458.24z" />
<glyph unicode="&#xe94a;" glyph-name="pause" d="M435.2-115.2c0-20.48-17.92-38.4-38.4-38.4h-153.6c-20.48 0-38.4 17.92-38.4 38.4v742.4c0 20.48 17.92 38.4 38.4 38.4h153.6c20.48 0 38.4-17.92 38.4-38.4v-742.4zM819.2-115.2c0-20.48-17.92-38.4-38.4-38.4h-153.6c-20.48 0-38.4 17.92-38.4 38.4v742.4c0 20.48 17.92 38.4 38.4 38.4h153.6c20.48 0 38.4-17.92 38.4-38.4v-742.4z" />
<glyph unicode="&#xe94b;" glyph-name="crown2" d="M882.4-64.4l135.2 523.4c2.2 8.4-7 15.2-14.4 10.4l-262.4-168.2c-10.4-6.6-24.2-3.8-31.2 6.4l-189.8 276.4c-3.8 5.6-12 5.6-15.8 0l-189.6-276.6c-7-10.2-20.8-13-31.2-6.4l-262.4 168.2c-7.4 4.6-16.6-2-14.4-10.4l135.2-523.4c2.8-11 12.8-18.8 24.2-18.8h692.6c11.2 0.2 21.2 8 24 19z" />
<glyph unicode="&#xe94c;" glyph-name="server2" d="M992.4 729.2h-960.8c-10.2 0-20.4-10.2-20.4-20.4v-203.8c0-10.2 10.2-20.4 20.4-20.4h960.6c10.2 0 20.4 10.2 20.4 20.4v203.8c0.2 10.2-10 20.4-20.2 20.4zM241.6 556.4c0-7.4-6-13.6-13.6-13.6h-101c-7.4 0-13.6 6-13.6 13.6v101c0 7.4 6 13.6 13.6 13.6h101c7.4 0 13.6-6 13.6-13.6v-101zM992.4 378.4h-960.8c-10.2 0-20.4-10.2-20.4-20.4v-204c0-10.2 10.2-20.4 20.4-20.4h960.6c10.2 0 20.4 10.2 20.4 20.4v204c0.2 10.2-10 20.4-20.2 20.4zM241.6 205.6c0-7.4-6-13.6-13.6-13.6h-101c-7.4 0-13.6 6-13.6 13.6v100.8c0 7.4 6 13.6 13.6 13.6h101c7.4 0 13.6-6 13.6-13.6v-100.8zM992.4 47.6h-960.8c-10.2 0-20.4-10.2-20.4-20.4v-203.8c0-10.2 10.2-20.4 20.4-20.4h960.6c10.2 0 20.4 10.2 20.4 20.4v203.8c0.2 10.2-10 20.4-20.2 20.4zM241.6-125.2c0-7.4-6-13.6-13.6-13.6h-101c-7.4 0-13.6 6-13.6 13.6v101c0 7.4 6 13.6 13.6 13.6h101c7.4 0 13.6-6 13.6-13.6v-101z" />

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -30,19 +30,8 @@
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
&.collapse:before {content: "\e90f";}
&.okta:before {content: "\e912";}
&.user-remove:before {content: "\e904";}
&.user-add:before {content: "\e907";}
&.group:before {content: "\e908";}
&.user:before {content: "\e90d";}
&.eye-closed:before {content: "\e956";}
&.eye-open:before {content: "\e957";}
&.arrow-down:before {content: "\e910";}
&.arrow-left:before {content: "\e90c";}
&.arrow-right:before {content: "\e911";}
&.arrow-up:before {content: "\e90b";}
&.authzero:before {content: "\e951";}
&.bar-chart:before {content: "\e913";}
&.brush:before {content: "\e939";}
&.caret-down:before {content: "\e902";}
&.caret-left:before {content: "\e900";}
@ -52,8 +41,9 @@
&.circle:before {content: "\e940";}
&.clock:before {content: "\e91b";}
&.cog-thick:before {content: "\e906";}
&.collapse:before {content: "\e90f";}
&.crown2:before {content: "\e94b";}
&.cube:before {content: "\e92e";}
&.cube:before {content: "\e90b";}
&.cubo-node:before {content: "\e919";}
&.cubo-uniform:before {content: "\e91a";}
&.dash-f:before {content: "\e927";}
@ -63,12 +53,17 @@
&.duplicate:before {content: "\e917";}
&.expand-a:before {content: "\e944";}
&.expand-b:before {content: "\e942";}
&.export:before {content: "\e941";}
&.export:before {content: "\e90c";}
&.eye-closed:before {content: "\e956";}
&.eye-open:before {content: "\e957";}
&.graphline:before {content: "\e90e";}
&.group:before {content: "\e908";}
&.heroku:before {content: "\e947";}
&.heroku-simple:before {content: "\e948";}
&.import:before {content: "\e910";}
&.oauth:before {content: "\e94f";}
&.octagon:before {content: "\e92d";}
&.okta:before {content: "\e912";}
&.pause:before {content: "\e94a";}
&.plus:before {content: "\e90a";}
&.pulse-c:before {content: "\e936";}
@ -78,8 +73,12 @@
&.server2:before {content: "\e94c";}
&.shuffle:before {content: "\e94e";}
&.square:before {content: "\e93e";}
&.text-block:before {content: "\e911";}
&.trash:before {content: "\e905";}
&.triangle:before {content: "\e92c";}
&.user:before {content: "\e90d";}
&.user-add:before {content: "\e907";}
&.user-remove:before {content: "\e904";}
&.alert-triangle:before {content: "\f02d";}
&.link:before {content: "\f05c";}
&.pencil:before {content: "\f058";}

View File

@ -30,7 +30,7 @@ $dash-ceo-z: $dygraph-legend-z + 10;
&:only-child {
top: 0;
height: 100%;
height: 100% !important;
}
}
.page-contents--split {

View File

@ -45,9 +45,12 @@ interface CellLinks {
self: string
}
// corresponds to DashboardQuery on the backend
export interface CellQuery {
query: string
queryConfig: QueryConfig
source: string
text?: string // doesn't come from server
}
export interface Legend {

View File

@ -3,6 +3,7 @@ import {AuthLinks, Organization, Role, User, Me} from './auth'
import {Template, Cell, CellQuery, Legend, Axes} from './dashboard'
import {
GroupBy,
Query,
QueryConfig,
Status,
TimeRange,
@ -32,6 +33,7 @@ export {
CellQuery,
Legend,
Status,
Query,
QueryConfig,
TimeShift,
ApplyFuncsToFieldArgs,

View File

@ -7,6 +7,7 @@ export interface LocalStorage {
dataExplorer: DataExplorer
dataExplorerQueryConfigs: DataExplorerQueryConfigs
timeRange: TimeRange
script: string
}
export type VERSION = string

View File

@ -1,3 +1,12 @@
import {Source} from 'src/types'
export interface Query {
host: string[] // doesn't come from server - is set in buildQueriesForGraphs
text: string
id: string
queryConfig: QueryConfig
}
export interface QueryConfig {
id?: string
database?: string
@ -9,7 +18,7 @@ export interface QueryConfig {
areTagsAccepted: boolean
rawText?: string
range?: DurationRange | null
sourceLink?: string
source?: Source | null // doesn't come from server -- is set in CellEditorOverlay
fill?: string
status?: Status
shifts?: TimeShift[]

View File

@ -10,6 +10,7 @@ export interface NewService {
export interface Service {
id?: string
sourceID: string
url: string
name: string
type: string
@ -17,6 +18,9 @@ export interface Service {
password?: string
active: boolean
insecureSkipVerify: boolean
metadata: {
[x: string]: any
}
links: {
source: string
self: string

View File

@ -17,6 +17,7 @@ export interface Source {
defaultRP: string
links: SourceLinks
kapacitors?: Kapacitor[] // this field does not exist on the server type for Source and is added in the client in the reducer for loading kapacitors.
text?: string // added client-side for dropdowns
services?: Service[]
}

View File

@ -1,45 +0,0 @@
import _ from 'lodash'
import {buildQuery} from 'utils/influxql'
import {TYPE_QUERY_CONFIG, TYPE_SHIFTED} from 'src/dashboards/constants'
const buildQueries = (proxy, queryConfigs, tR) => {
const statements = queryConfigs.map(query => {
const {rawText, range, id, shifts, database, measurement, fields} = query
const timeRange = range || tR
const text = rawText || buildQuery(TYPE_QUERY_CONFIG, timeRange, query)
const isParsable = database && measurement && fields.length
if (shifts && shifts.length && isParsable) {
const shiftedQueries = shifts
.filter(s => s.unit)
.map(s => buildQuery(TYPE_SHIFTED, timeRange, query, s))
return {
text: `${text};${shiftedQueries.join(';')}`,
id,
queryConfig: query,
}
}
return {text, id, queryConfig: query}
})
const queries = statements
.filter(s => s.text !== null)
.map(({queryConfig, text, id}) => {
const queryProxy = _.get(queryConfig, 'source.links.proxy', '')
const host = [queryProxy || proxy]
return {
host,
text,
id,
queryConfig,
}
})
return queries
}
export default buildQueries

View File

@ -0,0 +1,59 @@
import _ from 'lodash'
import {getDeep} from 'src/utils/wrappers'
import {buildQuery} from 'src/utils/influxql'
import {TYPE_QUERY_CONFIG, TYPE_SHIFTED} from 'src/dashboards/constants'
import {Query, QueryConfig, TimeRange} from 'src/types'
interface Statement {
queryConfig: QueryConfig
id: string
text: string
}
const buildQueries = (
proxy: string,
queryConfigs: QueryConfig[],
tR: TimeRange
): Query[] => {
const statements: Statement[] = queryConfigs.map((query: QueryConfig) => {
const {rawText, range, id, shifts, database, measurement, fields} = query
const timeRange: TimeRange = range || tR
const text: string =
rawText || buildQuery(TYPE_QUERY_CONFIG, timeRange, query)
const isParsable: boolean =
!_.isEmpty(database) && !_.isEmpty(measurement) && fields.length > 0
if (shifts && shifts.length && isParsable) {
const shiftedQueries: string[] = shifts
.filter(s => s.unit)
.map(s => buildQuery(TYPE_SHIFTED, timeRange, query, s))
return {
text: `${text};${shiftedQueries.join(';')}`,
id,
queryConfig: query,
}
}
return {text, id, queryConfig: query}
})
const queries: Query[] = statements
.filter(s => s.text !== null)
.map(({queryConfig, text, id}) => {
const queryProxy = getDeep<string>(queryConfig, 'source.links.proxy', '')
const host: string[] = [queryProxy || proxy]
return {
host,
text,
id,
queryConfig,
}
})
return queries
}
export default buildQueries

View File

@ -16,7 +16,7 @@ import {
TimeSeriesSuccessfulResult,
TimeSeries,
} from 'src/types/series'
import {get} from 'src/utils/wrappers'
import {getDeep} from 'src/utils/wrappers'
interface Result {
series: TimeSeriesSeries[]
@ -60,7 +60,7 @@ const flattenGroupBySeries = (
}
const tagsKeys = _.keys(tags)
const seriesArray = get<TimeSeriesSeries[]>(results, '[0].series', [])
const seriesArray = getDeep<TimeSeriesSeries[]>(results, '[0].series', [])
const accumulatedValues = fastReduce<TimeSeriesSeries, TimeSeriesValue[][]>(
seriesArray,
@ -76,7 +76,7 @@ const flattenGroupBySeries = (
},
[]
)
const firstColumns = get<string[]>(results, '[0].series[0]columns', [])
const firstColumns = getDeep<string[]>(results, '[0].series[0]columns', [])
const flattenedSeries: Result[] = [
{
@ -102,7 +102,11 @@ const constructResults = (
const MappedResponse = fastMap<TimeSeriesServerResponse, Result[]>(
raw,
(response, index) => {
const results = get<TimeSeriesResult[]>(response, 'response.results', [])
const results = getDeep<TimeSeriesResult[]>(
response,
'response.results',
[]
)
const successfulResults = results.filter(
r => 'series' in r && !('error' in r)

View File

@ -1,5 +1,5 @@
import _ from 'lodash'
export function get<T = any>(obj: any, path: string, fallack: T): T {
return _.get<T>(obj, path, fallack)
export function getDeep<T = any>(obj: any, path: string, fallback: T): T {
return _.get<T>(obj, path, fallback)
}

View File

@ -91,6 +91,7 @@ export const query: CellQuery = {
query:
'SELECT mean("usage_idle") AS "mean_usage_idle", mean("usage_user") AS "mean_usage_user" FROM "telegraf"."autogen"."cpu" WHERE time > :dashboardTime: GROUP BY time(:interval:) FILL(null)',
queryConfig,
source: '',
}
export const axes: Axes = {

View File

@ -3,6 +3,7 @@ import {shallow} from 'enzyme'
import {IFQLPage} from 'src/ifql/containers/IFQLPage'
import TimeMachine from 'src/ifql/components/TimeMachine'
import {ActionTypes} from 'src/ifql/actions'
jest.mock('src/ifql/apis', () => require('mocks/ifql/apis'))
@ -15,7 +16,19 @@ const setup = () => {
},
services: [],
sources: [],
script: '',
notify: () => {},
params: {
sourceID: '',
},
updateScript: (script: string) => {
return {
type: ActionTypes.UpdateScript,
payload: {
script,
},
}
},
}
const wrapper = shallow(<IFQLPage {...props} />)

View File

@ -96,6 +96,7 @@ export const kapacitor = {
export const service = {
id: '1',
sourceID: '1',
url: 'localhost:8082',
type: 'ifql',
name: 'IFQL',
@ -108,6 +109,7 @@ export const service = {
proxy: '/chronograf/v1/sources/1/services/2/proxy',
self: '/chronograf/v1/sources/1/services/2',
},
metadata: {},
}
export const kapacitorRules = [

View File

@ -1,17 +1,17 @@
import {get} from 'src/utils/wrappers'
import {getDeep} from 'src/utils/wrappers'
describe('utils.wrappers', () => {
describe('get', () => {
it('gets a nested value', () => {
const example = {a: {b: 'hello'}}
expect(get(example, 'a.b', 'default')).toEqual('hello')
expect(getDeep(example, 'a.b', 'default')).toEqual('hello')
})
it('gets the default value when key is empty', () => {
const example = {a: {b: 'hello'}}
expect(get(example, 'a.c', 'default')).toEqual('default')
expect(getDeep(example, 'a.c', 'default')).toEqual('default')
})
})
})