Merge pull request #4244 from influxdata/enhancements/web-workers
Initial implementation of background workerspull/4269/head
commit
442888b9cc
|
@ -10,7 +10,7 @@
|
|||
},
|
||||
"scripts": {
|
||||
"start": "node parcel.js",
|
||||
"build": "parcel build -d build --no-source-maps --public-url '' src/index.html",
|
||||
"build": "parcel build -d build --no-source-maps --public-url '' src/index.html src/worker/worker.ts",
|
||||
"clean": "rm -rf ./build/* && rm -rf ./.cache",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
|
@ -33,6 +33,7 @@
|
|||
"@types/dygraphs": "^1.1.6",
|
||||
"@types/enzyme": "^3.1.9",
|
||||
"@types/jest": "^22.1.4",
|
||||
"@types/levelup": "^0.0.30",
|
||||
"@types/lodash": "^4.14.104",
|
||||
"@types/node": "^9.4.6",
|
||||
"@types/papaparse": "^4.1.34",
|
||||
|
@ -71,6 +72,7 @@
|
|||
"eslint-plugin-react": "6.6.0",
|
||||
"eslint-watch": "^3.1.2",
|
||||
"express": "^4.14.0",
|
||||
"fake-indexeddb": "^2.0.4",
|
||||
"http-proxy-middleware": "^0.18.0",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "^23.1.0",
|
||||
|
@ -98,11 +100,14 @@
|
|||
"d3-color": "^1.2.0",
|
||||
"d3-scale": "^2.1.0",
|
||||
"dygraphs": "2.1.0",
|
||||
"encoding-down": "^5.0.4",
|
||||
"enzyme-adapter-react-16": "^1.1.1",
|
||||
"eslint-plugin-babel": "^4.1.2",
|
||||
"fast.js": "^0.1.1",
|
||||
"fixed-data-table-2": "^0.8.13",
|
||||
"he": "^1.1.1",
|
||||
"level-js": "^3.0.0",
|
||||
"levelup": "^3.1.1",
|
||||
"lodash": "^4.3.0",
|
||||
"moment": "^2.13.0",
|
||||
"nano-date": "^2.0.1",
|
||||
|
|
|
@ -7,7 +7,7 @@ const port = Number(process.env.PORT || 8080)
|
|||
console.log(`Serving on http://localhost:${port}`) // eslint-disable-line no-console
|
||||
|
||||
const app = express()
|
||||
const bundler = new Bundler('src/index.html', {
|
||||
const bundler = new Bundler(['src/index.html', 'src/worker/worker.ts'], {
|
||||
outDir: './build/',
|
||||
})
|
||||
|
||||
|
|
|
@ -660,8 +660,7 @@ export const getDashboardWithTemplatesAsync = (
|
|||
let dashboard: Dashboard
|
||||
|
||||
try {
|
||||
const resp = await getDashboardAJAX(dashboardId)
|
||||
dashboard = resp.data
|
||||
dashboard = await getDashboardAJAX(dashboardId)
|
||||
} catch {
|
||||
dispatch(replace(`/sources/${source.id}/dashboards`))
|
||||
dispatch(notify(notifyDashboardNotFound(dashboardId)))
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import AJAX from 'src/utils/ajax'
|
||||
|
||||
import {manager} from 'src/worker/JobManager'
|
||||
|
||||
import {
|
||||
linksFromDashboards,
|
||||
updateDashboardLinks,
|
||||
|
@ -37,10 +39,8 @@ export const loadDashboardLinks = async (
|
|||
|
||||
export const getDashboard = async dashboardID => {
|
||||
try {
|
||||
return await AJAX({
|
||||
method: 'GET',
|
||||
url: `/chronograf/v1/dashboards/${dashboardID}`,
|
||||
})
|
||||
const url = `/chronograf/v1/dashboards/${dashboardID}`
|
||||
return manager.get(url)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
throw error
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import calculateSize from 'calculate-size'
|
||||
import _ from 'lodash'
|
||||
import {fastMap, fastReduce, fastFilter} from 'src/utils/fast'
|
||||
|
||||
|
@ -16,6 +15,10 @@ import {
|
|||
} from 'src/types/dashboards'
|
||||
import {TimeSeriesValue} from 'src/types/series'
|
||||
|
||||
const calculateSize = (message: string): number => {
|
||||
return message.length * 7
|
||||
}
|
||||
|
||||
interface ColumnWidths {
|
||||
totalWidths: number
|
||||
widths: {[x: string]: number}
|
||||
|
@ -41,11 +44,7 @@ const calculateTimeColumnWidth = (timeFormat: string): number => {
|
|||
timeFormat = _.replace(timeFormat, 'h', '00')
|
||||
timeFormat = _.replace(timeFormat, 'X', '1522286058')
|
||||
|
||||
const {width} = calculateSize(timeFormat, {
|
||||
font: '"RobotoMono", monospace',
|
||||
fontSize: '13px',
|
||||
fontWeight: 'bold',
|
||||
})
|
||||
const width = calculateSize(timeFormat)
|
||||
|
||||
return width + CELL_HORIZONTAL_PADDING
|
||||
}
|
||||
|
@ -91,11 +90,7 @@ const updateMaxWidths = (
|
|||
|
||||
const currentWidth = useTimeWidth
|
||||
? timeFormatWidth
|
||||
: calculateSize(colValue.toString().trim(), {
|
||||
font: isLabel ? '"Roboto"' : '"RobotoMono", monospace',
|
||||
fontSize: '12px',
|
||||
fontWeight: '500',
|
||||
}).width + CELL_HORIZONTAL_PADDING
|
||||
: calculateSize(colValue.toString().trim()) + CELL_HORIZONTAL_PADDING
|
||||
|
||||
const {widths: Widths} = maxColumnWidths
|
||||
const maxWidth = _.get(Widths, `${columnLabel}`, 0)
|
||||
|
|
|
@ -47,9 +47,9 @@ export const getDataForCSV = (
|
|||
query: queryString,
|
||||
})
|
||||
|
||||
const {data} = timeSeriesToTableGraph([{response}])
|
||||
const {data} = await timeSeriesToTableGraph([{response}])
|
||||
const name = csvName(query.queryConfig)
|
||||
download(dataToCSV(data), `${name}.csv`, 'text/plain')
|
||||
download(dataToCSV(data as any), `${name}.csv`, 'text/plain')
|
||||
} catch (error) {
|
||||
errorThrown(error, 'Unable to download .csv file')
|
||||
console.error(error)
|
||||
|
|
|
@ -4,17 +4,12 @@ import {connect} from 'react-redux'
|
|||
import TimeSeries from 'src/shared/components/time_series/TimeSeries'
|
||||
|
||||
// Components
|
||||
import Dygraph from 'src/shared/components/Dygraph'
|
||||
import TimeRangeDropdown from 'src/shared/components/TimeRangeDropdown'
|
||||
import RuleGraphDygraph from 'src/kapacitor/components/RuleGraphDygraph'
|
||||
|
||||
// Utils
|
||||
import buildInfluxQLQuery from 'src/utils/influxql'
|
||||
import buildQueries from 'src/utils/buildQueriesForGraphs'
|
||||
import underlayCallback from 'src/kapacitor/helpers/ruleGraphUnderlay'
|
||||
import {timeSeriesToDygraph} from 'src/utils/timeSeriesTransformers'
|
||||
|
||||
// Constants
|
||||
import {LINE_COLORS_RULE_GRAPH} from 'src/shared/constants/graphColorPalettes'
|
||||
|
||||
// Types
|
||||
import {Source, AlertRule, QueryConfig, Query, TimeRange} from 'src/types'
|
||||
|
@ -63,25 +58,14 @@ class RuleGraph extends PureComponent<Props> {
|
|||
queries={this.queries}
|
||||
>
|
||||
{data => {
|
||||
const {labels, timeSeries, dygraphSeries} = timeSeriesToDygraph(
|
||||
data.timeSeries,
|
||||
'rule-builder'
|
||||
)
|
||||
return (
|
||||
<Dygraph
|
||||
labels={labels}
|
||||
staticLegend={false}
|
||||
isGraphFilled={false}
|
||||
ruleValues={rule.values}
|
||||
options={this.options}
|
||||
<RuleGraphDygraph
|
||||
loading={data.loading}
|
||||
query={this.props.query}
|
||||
rule={rule}
|
||||
timeRange={timeRange}
|
||||
queries={this.queries}
|
||||
timeSeries={timeSeries}
|
||||
dygraphSeries={dygraphSeries}
|
||||
colors={LINE_COLORS_RULE_GRAPH}
|
||||
containerStyle={this.containerStyle}
|
||||
underlayCallback={underlayCallback(rule)}
|
||||
handleSetHoverTime={this.props.setHoverTime}
|
||||
timeSeries={data.timeSeries}
|
||||
setHoverTime={this.props.setHoverTime}
|
||||
/>
|
||||
)
|
||||
}}
|
||||
|
@ -91,30 +75,6 @@ class RuleGraph extends PureComponent<Props> {
|
|||
)
|
||||
}
|
||||
|
||||
private get options() {
|
||||
return {
|
||||
rightGap: 0,
|
||||
yRangePad: 10,
|
||||
labelsKMB: true,
|
||||
fillGraph: true,
|
||||
axisLabelWidth: 60,
|
||||
animatedZooms: true,
|
||||
drawAxesAtZero: true,
|
||||
axisLineColor: '#383846',
|
||||
gridLineColor: '#383846',
|
||||
connectSeparatedPoints: true,
|
||||
}
|
||||
}
|
||||
|
||||
private get containerStyle(): CSSProperties {
|
||||
return {
|
||||
width: 'calc(100% - 32px)',
|
||||
height: 'calc(100% - 16px)',
|
||||
position: 'absolute',
|
||||
top: '8px',
|
||||
}
|
||||
}
|
||||
|
||||
private get style(): CSSProperties {
|
||||
return {height: '100%'}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
import React, {Component, CSSProperties} from 'react'
|
||||
import Dygraph from 'src/shared/components/Dygraph'
|
||||
|
||||
// Constants
|
||||
import {LINE_COLORS_RULE_GRAPH} from 'src/shared/constants/graphColorPalettes'
|
||||
|
||||
// Utils
|
||||
import buildQueries from 'src/utils/buildQueriesForGraphs'
|
||||
import underlayCallback from 'src/kapacitor/helpers/ruleGraphUnderlay'
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import {setHoverTime as setHoverTimeAction} from 'src/dashboards/actions'
|
||||
import {
|
||||
TimeSeriesToDyGraphReturnType,
|
||||
timeSeriesToDygraph,
|
||||
} from 'src/utils/timeSeriesTransformers'
|
||||
|
||||
// Types
|
||||
import {
|
||||
AlertRule,
|
||||
QueryConfig,
|
||||
Query,
|
||||
TimeRange,
|
||||
RemoteDataState,
|
||||
} from 'src/types'
|
||||
import {TimeSeriesServerResponse} from 'src/types/series'
|
||||
|
||||
interface Props {
|
||||
query: QueryConfig
|
||||
rule: AlertRule
|
||||
timeRange: TimeRange
|
||||
setHoverTime: typeof setHoverTimeAction
|
||||
loading: RemoteDataState
|
||||
timeSeries: TimeSeriesServerResponse[]
|
||||
}
|
||||
|
||||
interface State {
|
||||
timeSeriesToDygraphResult?: TimeSeriesToDyGraphReturnType
|
||||
}
|
||||
|
||||
@ErrorHandling
|
||||
class RuleGraphDygraph extends Component<Props, State> {
|
||||
public async componentWillReceiveProps(nextProps: Props) {
|
||||
const {loading, timeSeries} = this.props
|
||||
if (
|
||||
loading !== nextProps.loading &&
|
||||
nextProps.loading === RemoteDataState.Done
|
||||
) {
|
||||
const result = await timeSeriesToDygraph(timeSeries, 'rule-builder')
|
||||
this.setState({timeSeriesToDygraphResult: result})
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {timeRange, rule} = this.props
|
||||
const {timeSeriesToDygraphResult} = this.state
|
||||
|
||||
return (
|
||||
<Dygraph
|
||||
labels={timeSeriesToDygraphResult.labels}
|
||||
staticLegend={false}
|
||||
isGraphFilled={false}
|
||||
ruleValues={rule.values}
|
||||
options={this.options}
|
||||
timeRange={timeRange}
|
||||
queries={this.queries}
|
||||
timeSeries={timeSeriesToDygraphResult.timeSeries}
|
||||
dygraphSeries={timeSeriesToDygraphResult.dygraphSeries}
|
||||
colors={LINE_COLORS_RULE_GRAPH}
|
||||
containerStyle={this.containerStyle}
|
||||
underlayCallback={underlayCallback(rule)}
|
||||
handleSetHoverTime={this.props.setHoverTime}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
private get containerStyle(): CSSProperties {
|
||||
return {
|
||||
width: 'calc(100% - 32px)',
|
||||
height: 'calc(100% - 16px)',
|
||||
position: 'absolute',
|
||||
top: '8px',
|
||||
}
|
||||
}
|
||||
|
||||
private get options() {
|
||||
return {
|
||||
rightGap: 0,
|
||||
yRangePad: 10,
|
||||
labelsKMB: true,
|
||||
fillGraph: true,
|
||||
axisLabelWidth: 60,
|
||||
animatedZooms: true,
|
||||
drawAxesAtZero: true,
|
||||
axisLineColor: '#383846',
|
||||
gridLineColor: '#383846',
|
||||
connectSeparatedPoints: true,
|
||||
}
|
||||
}
|
||||
|
||||
private get queries(): Query[] {
|
||||
const {query, timeRange} = this.props
|
||||
return buildQueries([query], timeRange)
|
||||
}
|
||||
}
|
||||
|
||||
export default RuleGraphDygraph
|
|
@ -126,13 +126,13 @@ export default class LayoutCell extends Component<Props> {
|
|||
onSummonOverlayTechnologies(cell)
|
||||
}
|
||||
|
||||
private handleCSVDownload = (): void => {
|
||||
private handleCSVDownload = async (): Promise<void> => {
|
||||
const {cellData, cell} = this.props
|
||||
const joinedName = cell.name.split(' ').join('_')
|
||||
const {data} = timeSeriesToTableGraph(cellData)
|
||||
const {data} = await timeSeriesToTableGraph(cellData)
|
||||
|
||||
try {
|
||||
download(dataToCSV(data), `${joinedName}.csv`, 'text/plain')
|
||||
download(dataToCSV(data as any), `${joinedName}.csv`, 'text/plain')
|
||||
} catch (error) {
|
||||
notify(notifyCSVDownloadFailed())
|
||||
console.error(error)
|
||||
|
|
|
@ -14,12 +14,13 @@ import {
|
|||
timeSeriesToDygraph,
|
||||
TimeSeriesToDyGraphReturnType,
|
||||
} from 'src/utils/timeSeriesTransformers'
|
||||
import {manager} from 'src/worker/JobManager'
|
||||
|
||||
// Types
|
||||
import {ColorString} from 'src/types/colors'
|
||||
import {DecimalPlaces} from 'src/types/dashboards'
|
||||
import {TimeSeriesServerResponse} from 'src/types/series'
|
||||
import {DygraphValue} from 'src/types/dygraphs'
|
||||
// import {DygraphValue} from 'src/types/dygraphs'
|
||||
import {Query, Axes, TimeRange, RemoteDataState, CellType} from 'src/types'
|
||||
|
||||
interface Props {
|
||||
|
@ -41,34 +42,50 @@ interface Props {
|
|||
|
||||
type LineGraphProps = Props & RouteComponentProps<any, any>
|
||||
|
||||
interface State {
|
||||
timeSeries?: TimeSeriesToDyGraphReturnType
|
||||
}
|
||||
|
||||
@ErrorHandlingWith(InvalidData)
|
||||
class LineGraph extends PureComponent<LineGraphProps> {
|
||||
class LineGraph extends PureComponent<LineGraphProps, State> {
|
||||
public static defaultProps: Partial<LineGraphProps> = {
|
||||
staticLegend: false,
|
||||
}
|
||||
|
||||
private isComponentMounted: boolean = false
|
||||
private isValidData: boolean = true
|
||||
private timeSeries: TimeSeriesToDyGraphReturnType
|
||||
|
||||
public componentWillMount() {
|
||||
const {data} = this.props
|
||||
this.parseTimeSeries(data)
|
||||
constructor(props: LineGraphProps) {
|
||||
super(props)
|
||||
|
||||
this.state = {}
|
||||
}
|
||||
|
||||
public parseTimeSeries(data) {
|
||||
public async componentDidMount() {
|
||||
this.isComponentMounted = true
|
||||
const {data} = this.props
|
||||
await this.parseTimeSeries(data)
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
this.isComponentMounted = false
|
||||
}
|
||||
|
||||
public async parseTimeSeries(data) {
|
||||
const {location} = this.props
|
||||
|
||||
this.timeSeries = timeSeriesToDygraph(data, location.pathname)
|
||||
const timeSeries = _.get(this.timeSeries, 'timeSeries', [])
|
||||
this.isValidData = this.validateTimeSeries(timeSeries)
|
||||
const timeSeries = await timeSeriesToDygraph(data, location.pathname)
|
||||
const innerTimeSeries = _.get(timeSeries, 'timeSeries', [])
|
||||
this.isValidData = await manager.validateDygraphData(innerTimeSeries)
|
||||
if (!this.isComponentMounted) {
|
||||
return
|
||||
}
|
||||
|
||||
this.setState({timeSeries})
|
||||
}
|
||||
|
||||
public componentWillUpdate(nextProps) {
|
||||
const {data, activeQueryIndex} = this.props
|
||||
if (
|
||||
data !== nextProps.data ||
|
||||
activeQueryIndex !== nextProps.activeQueryIndex
|
||||
) {
|
||||
public componentWillReceiveProps(nextProps: LineGraphProps) {
|
||||
if (nextProps.loading === RemoteDataState.Done) {
|
||||
this.parseTimeSeries(nextProps.data)
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +111,11 @@ class LineGraph extends PureComponent<LineGraphProps> {
|
|||
handleSetHoverTime,
|
||||
} = this.props
|
||||
|
||||
const {labels, timeSeries, dygraphSeries} = this.timeSeries
|
||||
if (!this.state.timeSeries) {
|
||||
return <h3 className="graph-spinner" />
|
||||
}
|
||||
|
||||
const {labels, timeSeries, dygraphSeries} = this.state.timeSeries
|
||||
|
||||
const options = {
|
||||
rightGap: 0,
|
||||
|
@ -147,16 +168,6 @@ class LineGraph extends PureComponent<LineGraphProps> {
|
|||
)
|
||||
}
|
||||
|
||||
private validateTimeSeries = (ts: DygraphValue[][]) => {
|
||||
return _.every(ts, r =>
|
||||
_.every(
|
||||
r,
|
||||
(v, i: number) =>
|
||||
(i === 0 && Date.parse(v as string)) || _.isNumber(v) || _.isNull(v)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private get isGraphFilled(): boolean {
|
||||
const {type} = this.props
|
||||
|
||||
|
|
|
@ -85,6 +85,7 @@ class RefreshingGraph extends PureComponent<Props> {
|
|||
return (
|
||||
<TimeSeries
|
||||
source={source}
|
||||
cellType={type}
|
||||
inView={inView}
|
||||
queries={this.queries}
|
||||
timeRange={timeRange}
|
||||
|
|
|
@ -35,6 +35,8 @@ import {
|
|||
Sort,
|
||||
} from 'src/types/dashboards'
|
||||
|
||||
import {manager} from 'src/worker/JobManager'
|
||||
|
||||
const COLUMN_MIN_WIDTH = 100
|
||||
const ROW_HEIGHT = 30
|
||||
|
||||
|
@ -84,6 +86,7 @@ interface State {
|
|||
class TableGraph extends Component<Props, State> {
|
||||
private gridContainer: HTMLDivElement
|
||||
private multiGrid?: MultiGrid
|
||||
private isComponentMounted: boolean = false
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
|
@ -167,6 +170,7 @@ class TableGraph extends Component<Props, State> {
|
|||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
this.isComponentMounted = false
|
||||
window.removeEventListener('resize', this.handleResize)
|
||||
}
|
||||
|
||||
|
@ -179,7 +183,8 @@ class TableGraph extends Component<Props, State> {
|
|||
)
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
public async componentDidMount() {
|
||||
this.isComponentMounted = true
|
||||
window.addEventListener('resize', this.handleResize)
|
||||
|
||||
const sortField: string = _.get(
|
||||
|
@ -196,13 +201,17 @@ class TableGraph extends Component<Props, State> {
|
|||
fieldOptions,
|
||||
decimalPlaces,
|
||||
} = this.props
|
||||
const result = timeSeriesToTableGraph(data)
|
||||
const result = await timeSeriesToTableGraph(data)
|
||||
const sortedLabels = result.sortedLabels
|
||||
const computedFieldOptions = computeFieldOptions(fieldOptions, sortedLabels)
|
||||
|
||||
this.handleUpdateFieldOptions(computedFieldOptions)
|
||||
|
||||
const {transformedData, sortedTimeVals, columnWidths} = transformTableData(
|
||||
const {
|
||||
transformedData,
|
||||
sortedTimeVals,
|
||||
columnWidths,
|
||||
} = await manager.tableTransform(
|
||||
result.data,
|
||||
sort,
|
||||
computedFieldOptions,
|
||||
|
@ -234,12 +243,12 @@ class TableGraph extends Component<Props, State> {
|
|||
)
|
||||
}
|
||||
|
||||
public componentWillReceiveProps(nextProps: Props) {
|
||||
public async componentWillReceiveProps(nextProps: Props) {
|
||||
const {sort} = this.state
|
||||
|
||||
let result = {}
|
||||
if (this.hasDataChanged(nextProps.data)) {
|
||||
result = timeSeriesToTableGraph(nextProps.data)
|
||||
result = await timeSeriesToTableGraph(nextProps.data)
|
||||
}
|
||||
const data = _.get(result, 'data', this.state.data)
|
||||
|
||||
|
@ -278,7 +287,7 @@ class TableGraph extends Component<Props, State> {
|
|||
transformedData,
|
||||
sortedTimeVals,
|
||||
columnWidths,
|
||||
} = transformTableData(
|
||||
} = await manager.tableTransform(
|
||||
data,
|
||||
sort,
|
||||
computedFieldOptions,
|
||||
|
@ -296,6 +305,10 @@ class TableGraph extends Component<Props, State> {
|
|||
isTimeVisible = _.get(timeField, 'visible', this.state.isTimeVisible)
|
||||
}
|
||||
|
||||
if (!this.isComponentMounted) {
|
||||
return
|
||||
}
|
||||
|
||||
this.setState({
|
||||
data,
|
||||
sortedLabels,
|
||||
|
|
|
@ -7,7 +7,14 @@ import uuid from 'uuid'
|
|||
import {fetchTimeSeries} from 'src/shared/apis/query'
|
||||
|
||||
// Types
|
||||
import {Template, Source, Query, RemoteDataState, TimeRange} from 'src/types'
|
||||
import {
|
||||
Template,
|
||||
Source,
|
||||
Query,
|
||||
RemoteDataState,
|
||||
TimeRange,
|
||||
CellType,
|
||||
} from 'src/types'
|
||||
import {TimeSeriesServerResponse, TimeSeriesResponse} from 'src/types/series'
|
||||
import {GrabDataForDownloadHandler} from 'src/types/layout'
|
||||
|
||||
|
@ -23,6 +30,7 @@ interface RenderProps {
|
|||
|
||||
interface Props {
|
||||
source: Source
|
||||
cellType?: CellType
|
||||
queries: Query[]
|
||||
timeRange: TimeRange
|
||||
children: (r: RenderProps) => JSX.Element
|
||||
|
@ -46,6 +54,7 @@ const GraphLoadingDots = () => (
|
|||
<div />
|
||||
</div>
|
||||
)
|
||||
|
||||
class TimeSeries extends Component<Props, State> {
|
||||
public static defaultProps = {
|
||||
inView: true,
|
||||
|
@ -53,25 +62,23 @@ class TimeSeries extends Component<Props, State> {
|
|||
}
|
||||
|
||||
public static getDerivedStateFromProps(props: Props, state: State) {
|
||||
let {isFirstFetch, timeRange} = state
|
||||
|
||||
const oldUpper = _.get(state, 'timeRange.upper', null)
|
||||
const oldLower = _.get(state, 'timeRange.lower', null)
|
||||
const newUpper = _.get(props, 'timeRange.upper', null)
|
||||
const newLower = _.get(props, 'timeRange.lower', null)
|
||||
|
||||
if (oldUpper !== newUpper || oldLower !== newLower) {
|
||||
isFirstFetch = true
|
||||
timeRange = props.timeRange
|
||||
return {
|
||||
isFirstFetch: true,
|
||||
timeRange: props.timeRange,
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
isFirstFetch,
|
||||
timeRange,
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private latestUUID: string = uuid.v1()
|
||||
private isComponentMounted: boolean = false
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
|
@ -83,12 +90,32 @@ class TimeSeries extends Component<Props, State> {
|
|||
}
|
||||
}
|
||||
|
||||
public shouldComponentUpdate(prevProps: Props, prevState: State) {
|
||||
const list = [
|
||||
'source',
|
||||
'queries',
|
||||
'timeRange',
|
||||
'inView',
|
||||
'templates',
|
||||
'cellType',
|
||||
]
|
||||
|
||||
return (
|
||||
this.state.loading !== prevState.loading ||
|
||||
_.some(list, key => {
|
||||
return !_.isEqual(this.props[key], prevProps[key])
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
public async componentDidMount() {
|
||||
this.isComponentMounted = true
|
||||
this.executeQueries()
|
||||
AutoRefresh.subscribe(this.executeQueries)
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
this.isComponentMounted = false
|
||||
AutoRefresh.unsubscribe(this.executeQueries)
|
||||
}
|
||||
|
||||
|
@ -105,7 +132,7 @@ class TimeSeries extends Component<Props, State> {
|
|||
this.setState({loading: RemoteDataState.Loading}, () => {
|
||||
window.setTimeout(() => {
|
||||
resolve()
|
||||
}, 10)
|
||||
}, 0)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
@ -153,21 +180,28 @@ class TimeSeries extends Component<Props, State> {
|
|||
response,
|
||||
}))
|
||||
|
||||
if (!this.isComponentMounted) {
|
||||
return
|
||||
}
|
||||
|
||||
this.setState({
|
||||
timeSeries: newSeries,
|
||||
loading: RemoteDataState.Done,
|
||||
isFirstFetch: false,
|
||||
})
|
||||
|
||||
if (grabDataForDownload) {
|
||||
grabDataForDownload(newSeries)
|
||||
}
|
||||
} catch (err) {
|
||||
if (!this.isComponentMounted) {
|
||||
return
|
||||
}
|
||||
|
||||
this.setState({
|
||||
timeSeries: [],
|
||||
loading: RemoteDataState.Error,
|
||||
})
|
||||
} finally {
|
||||
this.setState({isFirstFetch: false})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -94,15 +94,9 @@ async function AJAX<T = any>(
|
|||
excludeBasepath = false
|
||||
): Promise<(T | T & {links: object}) | AxiosResponse<T>> {
|
||||
try {
|
||||
if (!links) {
|
||||
console.error(
|
||||
`AJAX function has no links. Trying to reach url ${url}, resource ${resource}, id ${id}, method ${method}`
|
||||
)
|
||||
}
|
||||
|
||||
url = addBasepath(url, excludeBasepath)
|
||||
|
||||
if (resource) {
|
||||
if (resource && links) {
|
||||
url = id
|
||||
? addBasepath(`${links[resource]}/${id}`, excludeBasepath)
|
||||
: addBasepath(`${links[resource]}`, excludeBasepath)
|
||||
|
|
|
@ -2,6 +2,10 @@ import {getRootNode} from 'src/utils/nodes'
|
|||
|
||||
export const getBasepath = () => {
|
||||
const rootNode = getRootNode()
|
||||
if (!rootNode) {
|
||||
return ''
|
||||
}
|
||||
|
||||
return rootNode.getAttribute('data-basepath') || ''
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import AJAX from 'src/utils/ajax'
|
||||
import {manager} from 'src/worker/JobManager'
|
||||
|
||||
interface ProxyQuery {
|
||||
source: string
|
||||
|
@ -16,16 +16,8 @@ export async function proxy<T = any>({
|
|||
uuid,
|
||||
}: ProxyQuery) {
|
||||
try {
|
||||
return await AJAX<T>({
|
||||
method: 'POST',
|
||||
url: source,
|
||||
data: {
|
||||
query,
|
||||
db,
|
||||
rp,
|
||||
uuid,
|
||||
},
|
||||
})
|
||||
const result = await manager.proxy(source, query, db, rp, uuid)
|
||||
return result as T
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
throw error
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
import {fastMap, fastReduce} from 'src/utils/fast'
|
||||
import {groupByTimeSeriesTransform} from 'src/utils/groupByTimeSeriesTransform'
|
||||
import {fastMap} from 'src/utils/fast'
|
||||
import {manager} from 'src/worker/JobManager'
|
||||
|
||||
import {
|
||||
TimeSeriesServerResponse,
|
||||
TimeSeries,
|
||||
TimeSeriesValue,
|
||||
} from 'src/types/series'
|
||||
import {TimeSeriesServerResponse, TimeSeriesValue} from 'src/types/series'
|
||||
import {DygraphSeries, DygraphValue} from 'src/types'
|
||||
|
||||
interface Label {
|
||||
|
@ -25,64 +21,22 @@ interface TimeSeriesToTableGraphReturnType {
|
|||
sortedLabels: Label[]
|
||||
}
|
||||
|
||||
export const timeSeriesToDygraph = (
|
||||
export const timeSeriesToDygraph = async (
|
||||
raw: TimeSeriesServerResponse[],
|
||||
pathname: string = ''
|
||||
): TimeSeriesToDyGraphReturnType => {
|
||||
const isTable = false
|
||||
const isInDataExplorer = pathname.includes('data-explorer')
|
||||
const {sortedLabels, sortedTimeSeries} = groupByTimeSeriesTransform(
|
||||
raw,
|
||||
isTable
|
||||
): Promise<TimeSeriesToDyGraphReturnType> => {
|
||||
const result = await manager.timeSeriesToDygraph(raw, pathname)
|
||||
const {timeSeries} = result
|
||||
const newTimeSeries = fastMap<DygraphValue[], DygraphValue[]>(
|
||||
timeSeries,
|
||||
([time, ...values]) => [new Date(time), ...values]
|
||||
)
|
||||
|
||||
const labels = [
|
||||
'time',
|
||||
...fastMap<Label, string>(sortedLabels, ({label}) => label),
|
||||
]
|
||||
|
||||
const timeSeries = fastMap<TimeSeries, DygraphValue[]>(
|
||||
sortedTimeSeries,
|
||||
({time, values}) => [new Date(time), ...values]
|
||||
)
|
||||
|
||||
const dygraphSeries = fastReduce<Label, DygraphSeries>(
|
||||
sortedLabels,
|
||||
(acc, {label, responseIndex}) => {
|
||||
if (!isInDataExplorer) {
|
||||
acc[label] = {
|
||||
axis: responseIndex === 0 ? 'y' : 'y2',
|
||||
}
|
||||
}
|
||||
return acc
|
||||
},
|
||||
{}
|
||||
)
|
||||
|
||||
return {labels, timeSeries, dygraphSeries}
|
||||
return {...result, timeSeries: newTimeSeries}
|
||||
}
|
||||
|
||||
export const timeSeriesToTableGraph = (
|
||||
export const timeSeriesToTableGraph = async (
|
||||
raw: TimeSeriesServerResponse[]
|
||||
): TimeSeriesToTableGraphReturnType => {
|
||||
const isTable = true
|
||||
const {sortedLabels, sortedTimeSeries} = groupByTimeSeriesTransform(
|
||||
raw,
|
||||
isTable
|
||||
)
|
||||
|
||||
const labels = [
|
||||
'time',
|
||||
...fastMap<Label, string>(sortedLabels, ({label}) => label),
|
||||
]
|
||||
|
||||
const tableData = fastMap<TimeSeries, TimeSeriesValue[]>(
|
||||
sortedTimeSeries,
|
||||
({time, values}) => [time, ...values]
|
||||
)
|
||||
const data = tableData.length ? [labels, ...tableData] : [[]]
|
||||
return {
|
||||
data,
|
||||
sortedLabels,
|
||||
}
|
||||
): Promise<TimeSeriesToTableGraphReturnType> => {
|
||||
return await manager.timeSeriesToTableGraph(raw)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
import levelup from 'levelup'
|
||||
import encoding from 'encoding-down'
|
||||
import level from 'level-js'
|
||||
|
||||
export default levelup(encoding(level('worker'), {valueEncoding: 'json'}))
|
|
@ -0,0 +1,14 @@
|
|||
class Deferred {
|
||||
public promise: Promise<any>
|
||||
public resolve: (...rest: any[]) => void
|
||||
public reject: (error: Error) => void
|
||||
|
||||
constructor() {
|
||||
this.promise = new Promise((resolve, reject) => {
|
||||
this.resolve = resolve
|
||||
this.reject = reject
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export default Deferred
|
|
@ -0,0 +1,154 @@
|
|||
import _ from 'lodash'
|
||||
import idGenerator from 'uuid'
|
||||
import Deferred from 'src/worker/Deferred'
|
||||
import DB from './Database'
|
||||
import {TimeSeriesServerResponse} from 'src/types/series'
|
||||
import {DygraphValue} from 'src/types'
|
||||
import {getBasepath} from 'src/utils/basepath'
|
||||
import {TimeSeriesToTableGraphReturnType} from 'src/worker/jobs/timeSeriesToTableGraph'
|
||||
import {TimeSeriesToDyGraphReturnType} from 'src/worker/jobs/timeSeriesToDygraph'
|
||||
|
||||
const workerCount = navigator.hardwareConcurrency - 1
|
||||
|
||||
// HACK: work around parcel picking up workers and trying to inline them.
|
||||
// This is need to allow for basepaths
|
||||
const WorkerClass = Worker
|
||||
|
||||
class JobManager {
|
||||
private currentIndex: number = 0
|
||||
private workers: Worker[] = []
|
||||
private jobs: {[key: string]: Deferred} = {}
|
||||
|
||||
constructor() {
|
||||
_.times(workerCount, () => {
|
||||
const worker = new WorkerClass(
|
||||
[getBasepath(), 'worker', 'worker.js'].join('/')
|
||||
)
|
||||
|
||||
worker.onmessage = this.handleMessage
|
||||
worker.onerror = this.handleError
|
||||
|
||||
this.workers.push(worker)
|
||||
})
|
||||
}
|
||||
|
||||
public async tableTransform(
|
||||
data,
|
||||
sort,
|
||||
fieldOptions,
|
||||
tableOptions,
|
||||
timeFormat,
|
||||
decimalPlaces
|
||||
): Promise<any> {
|
||||
const payload = {
|
||||
data,
|
||||
sort,
|
||||
fieldOptions,
|
||||
tableOptions,
|
||||
timeFormat,
|
||||
decimalPlaces,
|
||||
}
|
||||
|
||||
return this.publishDBJob('TABLETRANSFORM', payload)
|
||||
}
|
||||
|
||||
public proxy(url, query, db, rp, uuid): Promise<any> {
|
||||
if (getBasepath() !== '') {
|
||||
url = `${getBasepath()}${url}`
|
||||
}
|
||||
return this.publishJob('PROXY', {url, query, db, rp, uuid})
|
||||
}
|
||||
|
||||
public get(url: string): Promise<any> {
|
||||
if (getBasepath() !== '') {
|
||||
url = `${getBasepath()}${url}`
|
||||
}
|
||||
return this.publishJob('GET', {url})
|
||||
}
|
||||
|
||||
public timeSeriesToTableGraph = (
|
||||
raw: TimeSeriesServerResponse[]
|
||||
): Promise<TimeSeriesToTableGraphReturnType> => {
|
||||
return this.publishDBJob('TSTOTABLEGRAPH', {raw})
|
||||
}
|
||||
|
||||
public timeSeriesToDygraph = (
|
||||
raw: TimeSeriesServerResponse[],
|
||||
pathname: string = ''
|
||||
): Promise<TimeSeriesToDyGraphReturnType> => {
|
||||
return this.publishDBJob('TSTODYGRAPH', {raw, pathname})
|
||||
}
|
||||
|
||||
public validateDygraphData = (ts: DygraphValue[][]) => {
|
||||
return this.publishDBJob('VALIDATEDYGRAPHDATA', ts)
|
||||
}
|
||||
|
||||
private handleMessage = async msg => {
|
||||
const {data} = msg
|
||||
const deferred = this.jobs[data.origin]
|
||||
if (deferred) {
|
||||
if (data.result === 'success') {
|
||||
this.fetchPayload(deferred, data.id)
|
||||
} else {
|
||||
deferred.reject(data.error)
|
||||
}
|
||||
delete this.jobs[data.origin]
|
||||
}
|
||||
}
|
||||
|
||||
private fetchPayload = async (deferred, id) => {
|
||||
try {
|
||||
const payload = await DB.get(id)
|
||||
await DB.del(id)
|
||||
deferred.resolve(payload)
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
deferred.reject(e)
|
||||
}
|
||||
}
|
||||
|
||||
private handleError = err => {
|
||||
console.error(err)
|
||||
}
|
||||
|
||||
private get worker(): Worker {
|
||||
return this.workers[this.currentIndex]
|
||||
}
|
||||
|
||||
private postMessage(msg: any): void {
|
||||
this.worker.postMessage(msg)
|
||||
this.incrementWorker()
|
||||
}
|
||||
|
||||
private incrementWorker(): void {
|
||||
this.currentIndex += 1
|
||||
this.currentIndex %= workerCount
|
||||
}
|
||||
|
||||
private publishJob = async (type, payload) => {
|
||||
const id = idGenerator.v1()
|
||||
const deferred = new Deferred()
|
||||
|
||||
this.jobs[id] = deferred
|
||||
|
||||
this.postMessage({id, type, payload})
|
||||
|
||||
return deferred.promise
|
||||
}
|
||||
|
||||
private publishDBJob = async (type, payload) => {
|
||||
const id = idGenerator.v1()
|
||||
const deferred = new Deferred()
|
||||
|
||||
this.jobs[id] = deferred
|
||||
|
||||
await DB.put(id, payload)
|
||||
|
||||
this.postMessage({id, type})
|
||||
|
||||
return deferred.promise
|
||||
}
|
||||
}
|
||||
|
||||
export default JobManager
|
||||
export const manager = new JobManager()
|
|
@ -0,0 +1,12 @@
|
|||
import {Message} from 'src/worker/types'
|
||||
|
||||
const get = async (msg: Message) => {
|
||||
const {
|
||||
payload: {url},
|
||||
} = msg
|
||||
|
||||
const response = await fetch(url)
|
||||
return await response.json()
|
||||
}
|
||||
|
||||
export default get
|
|
@ -0,0 +1,15 @@
|
|||
const proxy = async msg => {
|
||||
const {
|
||||
payload: {url, query, rp, db, uuid},
|
||||
} = msg
|
||||
|
||||
const response = await fetch(url, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({query, rp, db, uuid}),
|
||||
})
|
||||
const data = await response.json()
|
||||
|
||||
return {data}
|
||||
}
|
||||
|
||||
export default proxy
|
|
@ -0,0 +1,26 @@
|
|||
import {fetchData} from 'src/worker/utils'
|
||||
import {transformTableData} from 'src/dashboards/utils/tableGraph'
|
||||
|
||||
const tableTransform = async msg => {
|
||||
const dbResult = await fetchData(msg)
|
||||
|
||||
const {
|
||||
data,
|
||||
sort,
|
||||
fieldOptions,
|
||||
tableOptions,
|
||||
timeFormat,
|
||||
decimalPlaces,
|
||||
} = dbResult
|
||||
|
||||
return transformTableData(
|
||||
data,
|
||||
sort,
|
||||
fieldOptions,
|
||||
tableOptions,
|
||||
timeFormat,
|
||||
decimalPlaces
|
||||
)
|
||||
}
|
||||
|
||||
export default tableTransform
|
|
@ -0,0 +1,62 @@
|
|||
import {fastMap, fastReduce} from 'src/utils/fast'
|
||||
import {groupByTimeSeriesTransform} from 'src/utils/groupByTimeSeriesTransform'
|
||||
import {TimeSeriesServerResponse, TimeSeries} from 'src/types/series'
|
||||
import {DygraphSeries, DygraphValue} from 'src/types'
|
||||
import {fetchData} from 'src/worker/utils'
|
||||
|
||||
interface Label {
|
||||
label: string
|
||||
seriesIndex: number
|
||||
responseIndex: number
|
||||
}
|
||||
|
||||
export interface TimeSeriesToDyGraphReturnType {
|
||||
labels: string[]
|
||||
timeSeries: DygraphValue[][]
|
||||
dygraphSeries: DygraphSeries
|
||||
}
|
||||
|
||||
export const timeSeriesToDygraphWork = (
|
||||
raw: TimeSeriesServerResponse[],
|
||||
pathname: string = ''
|
||||
): TimeSeriesToDyGraphReturnType => {
|
||||
const isTable = false
|
||||
const isInDataExplorer = pathname.includes('data-explorer')
|
||||
const {sortedLabels, sortedTimeSeries} = groupByTimeSeriesTransform(
|
||||
raw,
|
||||
isTable
|
||||
)
|
||||
|
||||
const labels = [
|
||||
'time',
|
||||
...fastMap<Label, string>(sortedLabels, ({label}) => label),
|
||||
]
|
||||
|
||||
const timeSeries = fastMap<TimeSeries, DygraphValue[]>(
|
||||
sortedTimeSeries,
|
||||
({time, values}) => [new Date(time), ...values]
|
||||
)
|
||||
|
||||
const dygraphSeries = fastReduce<Label, DygraphSeries>(
|
||||
sortedLabels,
|
||||
(acc, {label, responseIndex}) => {
|
||||
if (!isInDataExplorer) {
|
||||
acc[label] = {
|
||||
axis: responseIndex === 0 ? 'y' : 'y2',
|
||||
}
|
||||
}
|
||||
return acc
|
||||
},
|
||||
{}
|
||||
)
|
||||
|
||||
return {labels, timeSeries, dygraphSeries}
|
||||
}
|
||||
|
||||
const timeSeriesToDygraph = async msg => {
|
||||
const {raw, pathname} = await fetchData(msg)
|
||||
|
||||
return timeSeriesToDygraphWork(raw, pathname)
|
||||
}
|
||||
|
||||
export default timeSeriesToDygraph
|
|
@ -0,0 +1,51 @@
|
|||
import {fastMap} from 'src/utils/fast'
|
||||
import {groupByTimeSeriesTransform} from 'src/utils/groupByTimeSeriesTransform'
|
||||
import {
|
||||
TimeSeriesServerResponse,
|
||||
TimeSeries,
|
||||
TimeSeriesValue,
|
||||
} from 'src/types/series'
|
||||
import {fetchData} from 'src/worker/utils'
|
||||
|
||||
interface Label {
|
||||
label: string
|
||||
seriesIndex: number
|
||||
responseIndex: number
|
||||
}
|
||||
|
||||
export interface TimeSeriesToTableGraphReturnType {
|
||||
data: TimeSeriesValue[][]
|
||||
sortedLabels: Label[]
|
||||
}
|
||||
|
||||
export const timeSeriesToTableGraphWork = (
|
||||
raw: TimeSeriesServerResponse[]
|
||||
): TimeSeriesToTableGraphReturnType => {
|
||||
const isTable = true
|
||||
const {sortedLabels, sortedTimeSeries} = groupByTimeSeriesTransform(
|
||||
raw,
|
||||
isTable
|
||||
)
|
||||
|
||||
const labels = [
|
||||
'time',
|
||||
...fastMap<Label, string>(sortedLabels, ({label}) => label),
|
||||
]
|
||||
|
||||
const tableData = fastMap<TimeSeries, TimeSeriesValue[]>(
|
||||
sortedTimeSeries,
|
||||
({time, values}) => [time, ...values]
|
||||
)
|
||||
const data = tableData.length ? [labels, ...tableData] : [[]]
|
||||
return {
|
||||
data,
|
||||
sortedLabels,
|
||||
}
|
||||
}
|
||||
|
||||
const timeSeriesToTableGraph = async msg => {
|
||||
const {raw} = await fetchData(msg)
|
||||
return timeSeriesToTableGraphWork(raw)
|
||||
}
|
||||
|
||||
export default timeSeriesToTableGraph
|
|
@ -0,0 +1,19 @@
|
|||
import _ from 'lodash'
|
||||
import {DygraphValue} from 'src/types/dygraphs'
|
||||
import {fetchData} from 'src/worker/utils'
|
||||
|
||||
import {Message} from 'src/worker/types'
|
||||
|
||||
const validateDygraphData = async (msg: Message): Promise<boolean> => {
|
||||
const ts: DygraphValue[][] = await fetchData(msg)
|
||||
|
||||
return _.every(ts, r =>
|
||||
_.every(
|
||||
r,
|
||||
(v: any, i: number) =>
|
||||
(i === 0 && Date.parse(v as string)) || _.isNumber(v) || _.isNull(v)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
export default validateDygraphData
|
|
@ -0,0 +1,5 @@
|
|||
export interface Message {
|
||||
id: string
|
||||
type: string
|
||||
payload: any
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
import DB from 'src/worker/Database'
|
||||
import uuid from 'uuid'
|
||||
|
||||
import {Message} from 'src/worker/types'
|
||||
|
||||
export const removeData = async (msg: Message): Promise<void> => {
|
||||
await DB.del(msg.id)
|
||||
}
|
||||
|
||||
export const fetchData = async (msg: Message): Promise<any> => {
|
||||
const result = await DB.get(msg.id)
|
||||
|
||||
await removeData(msg)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export const error = (msg: Message, err: Error) => {
|
||||
const id = uuid.v1()
|
||||
|
||||
postMessage({
|
||||
id,
|
||||
origin: msg.id,
|
||||
result: 'error',
|
||||
error: err.toString(),
|
||||
})
|
||||
}
|
||||
|
||||
export const success = async (msg: Message, payload: any) => {
|
||||
const id = uuid.v1()
|
||||
|
||||
await DB.put(id, payload)
|
||||
|
||||
postMessage({
|
||||
id,
|
||||
origin: msg.id,
|
||||
result: 'success',
|
||||
})
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
import _ from 'lodash'
|
||||
import {error, success} from 'src/worker/utils'
|
||||
|
||||
import {Message} from 'src/worker/types'
|
||||
|
||||
interface WorkerMessage {
|
||||
data: Message
|
||||
}
|
||||
|
||||
import timeSeriesToTableGraph from 'src/worker/jobs/timeSeriesToTableGraph'
|
||||
import timeSeriesToDygraph from 'src/worker/jobs/timeSeriesToDygraph'
|
||||
import proxy from 'src/worker/jobs/proxy'
|
||||
import get from 'src/worker/jobs/get'
|
||||
import tableTransform from 'src/worker/jobs/tableTransform'
|
||||
import validateDygraphData from 'src/worker/jobs/validateDygraphData'
|
||||
|
||||
type Job = (msg: Message) => Promise<any>
|
||||
|
||||
const jobMapping: {[key: string]: Job} = {
|
||||
GET: get,
|
||||
PROXY: proxy,
|
||||
TABLETRANSFORM: tableTransform,
|
||||
TSTOTABLEGRAPH: timeSeriesToTableGraph,
|
||||
TSTODYGRAPH: timeSeriesToDygraph,
|
||||
VALIDATEDYGRAPHDATA: validateDygraphData,
|
||||
}
|
||||
|
||||
const errorJob = async (data: Message) => {
|
||||
error(data, new Error('UNKNOWN JOB TYPE'))
|
||||
}
|
||||
|
||||
onmessage = async (workerMessage: WorkerMessage) => {
|
||||
const {data} = workerMessage
|
||||
const job: Job = _.get(jobMapping, data.type, errorJob)
|
||||
|
||||
try {
|
||||
const result = await job(data)
|
||||
success(data, result)
|
||||
} catch (e) {
|
||||
error(data, e)
|
||||
}
|
||||
}
|
|
@ -3,6 +3,9 @@ import React from 'react'
|
|||
|
||||
import Adapter from 'enzyme-adapter-react-16'
|
||||
|
||||
window.indexedDB = require('fake-indexeddb')
|
||||
window.Worker = function() {}
|
||||
|
||||
configure({
|
||||
adapter: new Adapter(),
|
||||
})
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import {
|
||||
timeSeriesToDygraph,
|
||||
timeSeriesToTableGraph,
|
||||
} from 'src/utils/timeSeriesTransformers'
|
||||
import {timeSeriesToDygraphWork as timeSeriesToDygraph} from 'src/worker/jobs/timeSeriesToDygraph'
|
||||
import {timeSeriesToTableGraphWork as timeSeriesToTableGraph} from 'src/worker/jobs/timeSeriesToTableGraph'
|
||||
|
||||
import {
|
||||
filterTableColumns,
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
"target": "es6",
|
||||
"module": "es2015",
|
||||
"moduleResolution": "node",
|
||||
"lib": ["es6", "es2017", "dom"],
|
||||
"lib": ["es6", "es2017", "dom", "webworker"],
|
||||
"skipLibCheck": true,
|
||||
"isolatedModules": false,
|
||||
"jsx": "react",
|
||||
|
|
150
ui/yarn.lock
150
ui/yarn.lock
|
@ -78,6 +78,12 @@
|
|||
version "22.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-22.2.3.tgz#0157c0316dc3722c43a7b71de3fdf3acbccef10d"
|
||||
|
||||
"@types/levelup@^0.0.30":
|
||||
version "0.0.30"
|
||||
resolved "https://registry.yarnpkg.com/@types/levelup/-/levelup-0.0.30.tgz#869dd7a82cdbe5983e737006a1ff4e42fac97ca7"
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/lodash@^4.14.104":
|
||||
version "4.14.111"
|
||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.111.tgz#d926250baa9526c0ffe85914dd10363068e7893a"
|
||||
|
@ -159,6 +165,12 @@ abbrev@1:
|
|||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
|
||||
|
||||
abstract-leveldown@^5.0.0, abstract-leveldown@~5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/abstract-leveldown/-/abstract-leveldown-5.0.0.tgz#f7128e1f86ccabf7d2893077ce5d06d798e386c6"
|
||||
dependencies:
|
||||
xtend "~4.0.0"
|
||||
|
||||
accepts@~1.3.5:
|
||||
version "1.3.5"
|
||||
resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.5.tgz#eb777df6011723a3b14e8a72c0805c8e86746bd2"
|
||||
|
@ -1258,6 +1270,10 @@ balanced-match@^1.0.0:
|
|||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||
|
||||
base64-arraybuffer-es6@0.3.1:
|
||||
version "0.3.1"
|
||||
resolved "https://registry.yarnpkg.com/base64-arraybuffer-es6/-/base64-arraybuffer-es6-0.3.1.tgz#fdf0e382f4e2f56caf881f48ee0ce01ae79afe48"
|
||||
|
||||
base64-js@^1.0.2:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3"
|
||||
|
@ -1974,7 +1990,7 @@ core-js@^1.0.0:
|
|||
version "1.2.7"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-1.2.7.tgz#652294c14651db28fa93bd2d5ff2983a4f08c636"
|
||||
|
||||
core-js@^2.4.0, core-js@^2.5.0:
|
||||
core-js@^2.4.0, core-js@^2.4.1, core-js@^2.5.0, core-js@^2.5.3:
|
||||
version "2.5.7"
|
||||
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.5.7.tgz#f972608ff0cead68b841a16a932d0b183791814e"
|
||||
|
||||
|
@ -2452,6 +2468,13 @@ defaults@^1.0.3:
|
|||
dependencies:
|
||||
clone "^1.0.2"
|
||||
|
||||
deferred-leveldown@~4.0.0:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/deferred-leveldown/-/deferred-leveldown-4.0.2.tgz#0b0570087827bf480a23494b398f04c128c19a20"
|
||||
dependencies:
|
||||
abstract-leveldown "~5.0.0"
|
||||
inherits "^2.0.3"
|
||||
|
||||
define-properties@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.2.tgz#83a73f2fea569898fb737193c8f873caf6d45c94"
|
||||
|
@ -2608,7 +2631,7 @@ domelementtype@~1.1.1:
|
|||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b"
|
||||
|
||||
domexception@^1.0.0:
|
||||
domexception@^1.0.0, domexception@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90"
|
||||
dependencies:
|
||||
|
@ -2702,6 +2725,16 @@ encodeurl@~1.0.2:
|
|||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
|
||||
|
||||
encoding-down@^5.0.4:
|
||||
version "5.0.4"
|
||||
resolved "https://registry.yarnpkg.com/encoding-down/-/encoding-down-5.0.4.tgz#1e477da8e9e9d0f7c8293d320044f8b2cd8e9614"
|
||||
dependencies:
|
||||
abstract-leveldown "^5.0.0"
|
||||
inherits "^2.0.3"
|
||||
level-codec "^9.0.0"
|
||||
level-errors "^2.0.0"
|
||||
xtend "^4.0.1"
|
||||
|
||||
encoding@^0.1.11:
|
||||
version "0.1.12"
|
||||
resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.12.tgz#538b66f3ee62cd1ab51ec323829d1f9480c74beb"
|
||||
|
@ -2758,6 +2791,12 @@ enzyme@^3.3.0:
|
|||
raf "^3.4.0"
|
||||
rst-selector-parser "^2.2.3"
|
||||
|
||||
errno@~0.1.1:
|
||||
version "0.1.7"
|
||||
resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618"
|
||||
dependencies:
|
||||
prr "~1.0.1"
|
||||
|
||||
error-ex@^1.2.0, error-ex@^1.3.1:
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf"
|
||||
|
@ -3237,6 +3276,14 @@ extsprintf@^1.2.0:
|
|||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
|
||||
|
||||
fake-indexeddb@^2.0.4:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/fake-indexeddb/-/fake-indexeddb-2.0.4.tgz#401715deb7fc9501866c9f329bde7742599e2de8"
|
||||
dependencies:
|
||||
core-js "^2.4.1"
|
||||
realistic-structured-clone "^2.0.1"
|
||||
setimmediate "^1.0.5"
|
||||
|
||||
falafel@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/falafel/-/falafel-2.1.0.tgz#96bb17761daba94f46d001738b3cedf3a67fe06c"
|
||||
|
@ -3954,6 +4001,10 @@ ignore@^3.2.0, ignore@^3.3.3:
|
|||
version "3.3.10"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043"
|
||||
|
||||
immediate@~3.2.3:
|
||||
version "3.2.3"
|
||||
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.2.3.tgz#d140fa8f614659bd6541233097ddaac25cdd991c"
|
||||
|
||||
import-local@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/import-local/-/import-local-1.0.0.tgz#5e4ffdc03f4fe6c009c6729beb29631c2f8227bc"
|
||||
|
@ -4342,7 +4393,7 @@ is-symbol@^1.0.1:
|
|||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572"
|
||||
|
||||
is-typedarray@~1.0.0:
|
||||
is-typedarray@^1.0.0, is-typedarray@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
|
||||
|
||||
|
@ -5106,6 +5157,43 @@ left-pad@^1.2.0:
|
|||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e"
|
||||
|
||||
level-codec@^9.0.0:
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/level-codec/-/level-codec-9.0.0.tgz#2d3a0e835c4aa8339ec63de3f5a37480b74a5f87"
|
||||
|
||||
level-errors@^2.0.0, level-errors@~2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/level-errors/-/level-errors-2.0.0.tgz#2de5b566b62eef92f99e19be74397fbc512563fa"
|
||||
dependencies:
|
||||
errno "~0.1.1"
|
||||
|
||||
level-iterator-stream@~3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/level-iterator-stream/-/level-iterator-stream-3.0.0.tgz#2f780b524b8e7fa479c195e5b1180cd409f85219"
|
||||
dependencies:
|
||||
inherits "^2.0.1"
|
||||
readable-stream "^2.0.5"
|
||||
xtend "^4.0.0"
|
||||
|
||||
level-js@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/level-js/-/level-js-3.0.0.tgz#e6c066fb529b23eec230849c0751e4f6d548c865"
|
||||
dependencies:
|
||||
abstract-leveldown "~5.0.0"
|
||||
immediate "~3.2.3"
|
||||
inherits "^2.0.3"
|
||||
ltgt "^2.1.2"
|
||||
typedarray-to-buffer "~3.1.5"
|
||||
|
||||
levelup@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/levelup/-/levelup-3.1.1.tgz#c2c0b3be2b4dc316647c53b42e2f559e232d2189"
|
||||
dependencies:
|
||||
deferred-leveldown "~4.0.0"
|
||||
level-errors "~2.0.0"
|
||||
level-iterator-stream "~3.0.0"
|
||||
xtend "~4.0.0"
|
||||
|
||||
leven@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580"
|
||||
|
@ -5230,6 +5318,10 @@ lru-cache@^4.0.1:
|
|||
pseudomap "^1.0.2"
|
||||
yallist "^2.1.2"
|
||||
|
||||
ltgt@^2.1.2:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ltgt/-/ltgt-2.2.1.tgz#f35ca91c493f7b73da0e07495304f17b31f87ee5"
|
||||
|
||||
magic-string@^0.22.4:
|
||||
version "0.22.5"
|
||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.22.5.tgz#8e9cf5afddf44385c1da5bc2a6a0dbd10b03657e"
|
||||
|
@ -6740,6 +6832,10 @@ proxy-addr@~2.0.3:
|
|||
forwarded "~0.1.2"
|
||||
ipaddr.js "1.6.0"
|
||||
|
||||
prr@~1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476"
|
||||
|
||||
pseudomap@^1.0.1, pseudomap@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
|
||||
|
@ -7061,7 +7157,7 @@ read-pkg@^1.0.0:
|
|||
normalize-package-data "^2.3.2"
|
||||
path-type "^1.0.0"
|
||||
|
||||
readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.3:
|
||||
readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.3:
|
||||
version "2.3.6"
|
||||
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.6.tgz#b11c27d88b8ff1fbe070643cf94b0c79ae1b0aaf"
|
||||
dependencies:
|
||||
|
@ -7090,6 +7186,15 @@ readline2@^1.0.1:
|
|||
is-fullwidth-code-point "^1.0.0"
|
||||
mute-stream "0.0.5"
|
||||
|
||||
realistic-structured-clone@^2.0.1:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/realistic-structured-clone/-/realistic-structured-clone-2.0.2.tgz#2f8ec225b1f9af20efc79ac96a09043704414959"
|
||||
dependencies:
|
||||
core-js "^2.5.3"
|
||||
domexception "^1.0.1"
|
||||
typeson "^5.8.2"
|
||||
typeson-registry "^1.0.0-alpha.20"
|
||||
|
||||
realpath-native@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/realpath-native/-/realpath-native-1.0.1.tgz#07f40a0cce8f8261e2e8b7ebebf5c95965d7b633"
|
||||
|
@ -8182,7 +8287,7 @@ tough-cookie@~2.3.0, tough-cookie@~2.3.3:
|
|||
dependencies:
|
||||
punycode "^1.4.1"
|
||||
|
||||
tr46@^1.0.1:
|
||||
tr46@^1.0.0, tr46@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09"
|
||||
dependencies:
|
||||
|
@ -8293,6 +8398,12 @@ type-is@~1.6.15, type-is@~1.6.16:
|
|||
media-typer "0.3.0"
|
||||
mime-types "~2.1.18"
|
||||
|
||||
typedarray-to-buffer@~3.1.5:
|
||||
version "3.1.5"
|
||||
resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080"
|
||||
dependencies:
|
||||
is-typedarray "^1.0.0"
|
||||
|
||||
typedarray@^0.0.6:
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||
|
@ -8301,6 +8412,19 @@ typescript@^2.7.2:
|
|||
version "2.9.2"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.9.2.tgz#1cbf61d05d6b96269244eb6a3bce4bd914e0f00c"
|
||||
|
||||
typeson-registry@^1.0.0-alpha.20:
|
||||
version "1.0.0-alpha.21"
|
||||
resolved "https://registry.yarnpkg.com/typeson-registry/-/typeson-registry-1.0.0-alpha.21.tgz#8a4e31abb471d482aa0306ad56d35af7633a166a"
|
||||
dependencies:
|
||||
base64-arraybuffer-es6 "0.3.1"
|
||||
typeson "5.8.2"
|
||||
uuid "3.2.1"
|
||||
whatwg-url "6.4.0"
|
||||
|
||||
typeson@5.8.2, typeson@^5.8.2:
|
||||
version "5.8.2"
|
||||
resolved "https://registry.yarnpkg.com/typeson/-/typeson-5.8.2.tgz#cc26f45b705760a8777fba5d3c910cc3f0e8d7dd"
|
||||
|
||||
ua-parser-js@^0.7.18:
|
||||
version "0.7.18"
|
||||
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.18.tgz#a7bfd92f56edfb117083b69e31d2aa8882d4b1ed"
|
||||
|
@ -8430,6 +8554,10 @@ utils-merge@1.0.1:
|
|||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
|
||||
|
||||
uuid@3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.2.1.tgz#12c528bb9d58d0b9265d9a2f6f0fe8be17ff1f14"
|
||||
|
||||
uuid@^3.0.0, uuid@^3.1.0, uuid@^3.2.1:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
|
||||
|
@ -8506,7 +8634,7 @@ webidl-conversions@^3.0.0:
|
|||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
|
||||
|
||||
webidl-conversions@^4.0.0, webidl-conversions@^4.0.2:
|
||||
webidl-conversions@^4.0.0, webidl-conversions@^4.0.1, webidl-conversions@^4.0.2:
|
||||
version "4.0.2"
|
||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
|
||||
|
||||
|
@ -8524,6 +8652,14 @@ whatwg-mimetype@^2.0.0, whatwg-mimetype@^2.1.0:
|
|||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.1.0.tgz#f0f21d76cbba72362eb609dbed2a30cd17fcc7d4"
|
||||
|
||||
whatwg-url@6.4.0:
|
||||
version "6.4.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-6.4.0.tgz#08fdf2b9e872783a7a1f6216260a1d66cc722e08"
|
||||
dependencies:
|
||||
lodash.sortby "^4.7.0"
|
||||
tr46 "^1.0.0"
|
||||
webidl-conversions "^4.0.1"
|
||||
|
||||
whatwg-url@^4.3.0:
|
||||
version "4.8.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-4.8.0.tgz#d2981aa9148c1e00a41c5a6131166ab4683bbcc0"
|
||||
|
@ -8625,7 +8761,7 @@ xml-name-validator@^3.0.0:
|
|||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz#6ae73e06de4d8c6e47f9fb181f78d648ad457c6a"
|
||||
|
||||
xtend@^4.0.0, xtend@~4.0.1:
|
||||
xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0, xtend@~4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af"
|
||||
|
||||
|
|
Loading…
Reference in New Issue