Skip to content
Snippets Groups Projects
Unverified Commit 48b684fd authored by Kyle Doherty's avatar Kyle Doherty
Browse files

error handling + refactor

parent 197cc1de
No related branches found
No related tags found
No related merge requests found
......@@ -4,8 +4,15 @@ import { connect } from 'react-redux'
import { saturated } from 'metabase/lib/colors'
import { fetchCardXray } from 'metabase/xray/xray'
import { getLoadingStatus } from 'metabase/xray/selectors'
import { fetchXray } from 'metabase/xray/xray'
import {
getLoadingStatus,
getError,
getXray
} from 'metabase/xray/selectors'
import { hasXray, xrayLoadingMessages } from 'metabase/xray/utils'
import Icon from 'metabase/components/Icon'
import Tooltip from 'metabase/components/Tooltip'
import LoadingAndErrorWrapper from 'metabase/components/LoadingAndErrorWrapper'
......@@ -13,12 +20,11 @@ import Visualization from 'metabase/visualizations/components/Visualization'
import { XRayPageWrapper, Heading } from 'metabase/xray/components/XRayLayout'
import Periodicity from 'metabase/xray/components/Periodicity'
import { hasXray, xrayLoadingMessages } from 'metabase/xray/utils'
import LoadingAnimation from 'metabase/xray/components/LoadingAnimation'
type Props = {
fetchCardXray: () => void,
fetchXray: () => void,
isLoading: boolean,
xray: {}
}
......@@ -45,25 +51,17 @@ const GrowthRateDisplay = ({ period }) =>
</div>
class CardXRay extends Component {
props: Props
state = {
error: null
}
props: Props
async componentWillMount () {
componentWillMount () {
const { cardId, cost } = this.props.params
try {
await this.props.fetchCardXray(cardId, cost)
} catch (error) {
this.setState({ error })
}
this.props.fetchXray('card', cardId, cost)
}
render () {
const { xray, isLoading } = this.props
const { error } = this.state
const { xray, isLoading, error } = this.props
return (
<LoadingAndErrorWrapper
loading={isLoading || !hasXray(xray)}
......@@ -191,12 +189,13 @@ class CardXRay extends Component {
}
const mapStateToProps = state => ({
xray: state.xray.xray,
isLoading: getLoadingStatus(state)
xray: getXray(state),
isLoading: getLoadingStatus(state),
error: getError(state)
})
const mapDispatchToProps = {
fetchCardXray
fetchXray
}
export default connect(mapStateToProps, mapDispatchToProps)(CardXRay)
......@@ -6,10 +6,12 @@ import title from 'metabase/hoc/Title'
import { Link } from 'react-router'
import { isDate } from 'metabase/lib/schema_metadata'
import { fetchFieldXray } from 'metabase/xray/xray'
import { getFieldXray, getLoadingStatus } from 'metabase/xray/selectors'
import COSTS from 'metabase/xray/costs'
import { fetchXray } from 'metabase/xray/xray'
import {
getLoadingStatus,
getError,
getFeatures
} from 'metabase/xray/selectors'
import {
ROBOTS,
......@@ -33,7 +35,7 @@ import type { Field } from 'metabase/meta/types/Field'
import type { Table } from 'metabase/meta/types/Table'
type Props = {
fetchFieldXray: () => void,
fetchXray: () => void,
isLoading: boolean,
xray: {
table: Table,
......@@ -46,15 +48,17 @@ type Props = {
cost: string,
fieldId: number
},
error: {}
}
const mapStateToProps = state => ({
xray: getFieldXray(state),
isLoading: getLoadingStatus(state)
xray: getFeatures(state),
isLoading: getLoadingStatus(state),
error: getError(state)
})
const mapDispatchToProps = {
fetchFieldXray
fetchXray
}
@connect(mapStateToProps, mapDispatchToProps)
......@@ -62,34 +66,23 @@ const mapDispatchToProps = {
class FieldXRay extends Component {
props: Props
state = {
error: null
}
componentDidMount () {
this.fetchFieldXray()
componentWillMount () {
this.fetch()
}
async fetchFieldXray() {
const { params } = this.props
const cost = COSTS[params.cost]
try {
await this.props.fetchFieldXray(params.fieldId, cost)
} catch (error) {
this.setState({ error })
}
fetch() {
const { params, fetchXray } = this.props
fetchXray('field', params.fieldId, params.cost)
}
componentDidUpdate (prevProps: Props) {
if(prevProps.params.cost !== this.props.params.cost) {
this.fetchFieldXray()
this.fetch()
}
}
render () {
const { xray, params, isLoading } = this.props
const { error } = this.state
const { xray, params, isLoading, error } = this.props
return (
<LoadingAndErrorWrapper
......
......@@ -11,7 +11,8 @@ import {
getComparisonContributors,
getSegmentItem,
getTitle,
getLoadingStatus
getLoadingStatus,
getError
} from 'metabase/xray/selectors'
import LoadingAndErrorWrapper from 'metabase/components/LoadingAndErrorWrapper'
......@@ -21,12 +22,13 @@ import LoadingAnimation from 'metabase/xray/components/LoadingAnimation'
import { hasComparison, comparisonLoadingMessages } from 'metabase/xray/utils'
const mapStateToProps = (state) => ({
comparison: getComparison(state),
fields: getComparisonFields(state),
contributors: getComparisonContributors(state),
itemA: getSegmentItem(state, 0),
itemB: getSegmentItem(state, 1),
isLoading: getLoadingStatus(state)
comparison: getComparison(state),
fields: getComparisonFields(state),
contributors: getComparisonContributors(state),
itemA: getSegmentItem(state, 0),
itemB: getSegmentItem(state, 1),
isLoading: getLoadingStatus(state),
error: getError(state)
})
const mapDispatchToProps = {
......@@ -37,33 +39,23 @@ const mapDispatchToProps = {
@title(props => getTitle(props))
class SegmentComparison extends Component {
state = {
error: null
}
async componentWillMount () {
componentWillMount () {
const { cost, segmentId1, segmentId2 } = this.props.params
try {
await this.props.fetchSegmentComparison(segmentId1, segmentId2, cost)
} catch (error) {
console.log('error', error)
this.setState({ error })
}
this.props.fetchSegmentComparison(segmentId1, segmentId2, cost)
}
render () {
const {
contributors,
params,
comparison,
contributors,
error,
fields,
isLoading,
itemA,
itemB,
isLoading
params,
} = this.props
const { error } = this.state
return (
<LoadingAndErrorWrapper
loading={isLoading || !hasComparison(comparison)}
......
......@@ -8,6 +8,7 @@ import { fetchSegmentTableComparison } from 'metabase/xray/xray'
import {
getComparison,
getComparisonFields,
getError,
getSegmentItem,
getTableItem,
getTitle,
......@@ -24,7 +25,8 @@ const mapStateToProps = state => ({
fields: getComparisonFields(state),
itemA: getSegmentItem(state),
itemB: getTableItem(state),
isLoading: getLoadingStatus(state)
isLoading: getLoadingStatus(state),
error: getError(state)
})
const mapDispatchToProps = {
......@@ -39,18 +41,13 @@ class SegmentTableComparison extends Component {
error: null
}
async componentWillMount () {
componentWillMount () {
const { cost, segmentId, tableId } = this.props.params
try {
await this.props.fetchSegmentTableComparison(segmentId, tableId, cost)
} catch (error) {
this.setState({ error })
}
this.props.fetchSegmentTableComparison(segmentId, tableId, cost)
}
render () {
const { params, fields, comparison, itemA, itemB, isLoading } = this.props
const { error } = this.state
const { params, fields, comparison, itemA, itemB, isLoading, error } = this.props
return (
<LoadingAndErrorWrapper
loading={isLoading || !hasComparison(comparison)}
......
......@@ -7,16 +7,16 @@ import { Link } from 'react-router'
import LoadingAndErrorWrapper from 'metabase/components/LoadingAndErrorWrapper'
import { XRayPageWrapper, Heading } from 'metabase/xray/components/XRayLayout'
import { fetchSegmentXray } from 'metabase/xray/xray'
import { fetchXray } from 'metabase/xray/xray'
import Icon from 'metabase/components/Icon'
import COSTS from 'metabase/xray/costs'
import CostSelect from 'metabase/xray/components/CostSelect'
import {
getSegmentConstituents,
getSegmentXray,
getLoadingStatus
getConstituents,
getLoadingStatus,
getError,
getFeatures
} from 'metabase/xray/selectors'
import Constituent from 'metabase/xray/components/Constituent'
......@@ -25,10 +25,10 @@ import LoadingAnimation from 'metabase/xray/components/LoadingAnimation'
import type { Table } from 'metabase/meta/types/Table'
import type { Segment } from 'metabase/meta/types/Segment'
import { hasXray } from 'metabase/xray/utils'
import { hasXray, xrayLoadingMessages } from 'metabase/xray/utils'
type Props = {
fetchSegmentXray: () => void,
fetchXray: () => void,
constituents: [],
xray: {
table: Table,
......@@ -38,60 +38,49 @@ type Props = {
segmentId: number,
cost: string,
},
isLoading: boolean
isLoading: boolean,
error: {}
}
const mapStateToProps = state => ({
xray: getSegmentXray(state),
constituents: getSegmentConstituents(state),
isLoading: getLoadingStatus(state)
xray: getFeatures(state),
constituents: getConstituents(state),
isLoading: getLoadingStatus(state),
error: getError(state)
})
const mapDispatchToProps = {
fetchSegmentXray
fetchXray
}
@connect(mapStateToProps, mapDispatchToProps)
@title(({ xray }) => xray && xray.segment.name || "Segment" )
class SegmentXRay extends Component {
props: Props
state = {
error: null
componentWillMount () {
this.fetch()
}
componentDidMount () {
this.fetchSegmentXray()
}
async fetchSegmentXray () {
const { params } = this.props
// TODO - this should happen in the action
const cost = COSTS[params.cost]
try {
await this.props.fetchSegmentXray(params.segmentId, cost)
} catch (error) {
this.setState({ error })
}
fetch () {
const { params, fetchXray } = this.props
fetchXray('segment', params.segmentId, params.cost)
}
componentDidUpdate (prevProps: Props) {
if(prevProps.params.cost !== this.props.params.cost) {
this.fetchSegmentXray()
this.fetch()
}
}
render () {
const { constituents, xray, params, isLoading } = this.props
const { error } = this.state
const { constituents, xray, params, isLoading, error } = this.props
return (
<LoadingAndErrorWrapper
loading={isLoading || !hasXray(xray)}
error={error}
loadingMessages={[
'Generating x-ray',
'Still working...'
]}
loadingMessages={xrayLoadingMessages}
loadingScenes={[
<LoadingAnimation />
]}
......
......@@ -4,18 +4,17 @@ import React, { Component } from 'react'
import { connect } from 'react-redux'
import title from 'metabase/hoc/Title'
import { fetchTableXray } from 'metabase/xray/xray'
import { fetchXray } from 'metabase/xray/xray'
import { XRayPageWrapper } from 'metabase/xray/components/XRayLayout'
import COSTS from 'metabase/xray/costs'
import CostSelect from 'metabase/xray/components/CostSelect'
import Constituent from 'metabase/xray/components/Constituent'
import {
getTableConstituents,
getTableXray,
getLoadingStatus
getFeatures,
getLoadingStatus,
getError
} from 'metabase/xray/selectors'
import Icon from 'metabase/components/Icon'
......@@ -27,8 +26,8 @@ import type { Table } from 'metabase/meta/types/Table'
import { hasXray, xrayLoadingMessages } from 'metabase/xray/utils'
type Props = {
fetchXray: () => void,
constituents: [],
fetchTableXray: () => void,
isLoading: boolean,
xray: {
table: Table
......@@ -36,52 +35,45 @@ type Props = {
params: {
tableId: number,
cost: string
}
},
error: {}
}
const mapStateToProps = state => ({
xray: getTableXray(state),
xray: getFeatures(state),
constituents: getTableConstituents(state),
isLoading: getLoadingStatus(state)
isLoading: getLoadingStatus(state),
error: getError(state)
})
const mapDispatchToProps = {
fetchTableXray
fetchXray
}
@connect(mapStateToProps, mapDispatchToProps)
@title(({ xray }) => xray && xray.table.display_name || "Table")
class TableXRay extends Component {
props: Props
state = {
error: null
}
props: Props
componentDidMount () {
this.fetchTableXray()
componentWillMount () {
this.fetch()
}
async fetchTableXray () {
const { params } = this.props
fetch () {
const { params, fetchXray } = this.props
// TODO this should happen at the action level
const cost = COSTS[params.cost]
try {
await this.props.fetchTableXray(params.tableId, cost)
} catch (error) {
this.setState({ error })
}
fetchXray('table', params.tableId, params.cost)
}
componentDidUpdate (prevProps: Props) {
if(prevProps.params.cost !== this.props.params.cost) {
this.fetchTableXray()
this.fetch()
}
}
render () {
const { constituents, xray, params, isLoading } = this.props
const { error } = this.state
const { constituents, xray, params, isLoading, error } = this.props
return (
<LoadingAndErrorWrapper
......
......@@ -4,14 +4,13 @@ import { normal } from 'metabase/lib/colors'
export const getLoadingStatus = (state) =>
state.xray.loading
/* TODO - these can be collapsed into getXray */
export const getFieldXray = (state) =>
state.xray.xray && state.xray.xray.features
export const getError = (state) =>
state.xray.error
export const getTableXray = (state) =>
state.xray.xray && state.xray.xray.features
export const getXray = (state) =>
state.xray.xray
export const getSegmentXray = (state) =>
export const getFeatures = (state) =>
state.xray.xray && state.xray.xray.features
/* TODO - these can be collapsed into getConstituents */
......@@ -29,6 +28,13 @@ export const getSegmentConstituents = (state) =>
)
)
export const getConstituents = (state) =>
state.xray.xray && (
Object.keys(state.xray.xray.constituents).map(key =>
state.xray.xray.constituents[key]
)
)
export const getComparison = (state) => state.xray.comparison
export const getComparisonFields = createSelector(
......
......@@ -10,57 +10,26 @@ import {
import { XRayApi } from 'metabase/services'
export const FETCH_FIELD_XRAY = 'metabase/xray/FETCH_FIELD_XRAY'
export const fetchFieldXray = createThunkAction(FETCH_FIELD_XRAY, (fieldId, cost) =>
export const FETCH_XRAY = 'metabase/xray/FETCH_XRAY'
export const fetchXray = createThunkAction(
FETCH_XRAY,
(
type: string,
id: number,
cost: string
) =>
async (dispatch) => {
dispatch(startLoad())
try {
const xray = await XRayApi.field_xray({ fieldId, ...cost.method })
return dispatch(loadXray(xray))
} catch (error) {
console.error(error)
}
}
)
export const FETCH_TABLE_XRAY = 'metabase/xray/FETCH_TABLE_XRAY'
export const fetchTableXray = createThunkAction(FETCH_TABLE_XRAY, (tableId, cost) =>
async (dispatch) => {
dispatch(startLoad())
try {
const xray = await XRayApi.table_xray({ tableId, ...cost.method })
return dispatch(loadXray(xray))
} catch (error) {
console.error(error)
}
}
)
export const FETCH_SEGMENT_XRAY = 'metabase/xray/FETCH_SEGMENT_XRAY'
export const fetchSegmentXray = createThunkAction(FETCH_SEGMENT_XRAY, (segmentId, cost) =>
async (dispatch) => {
dispatch(startLoad())
try {
const xray = await XRayApi.segment_xray({ segmentId, ...cost.method })
return dispatch(loadXray(xray))
const c = COSTS[cost]
const xray = await XRayApi[`${type}_xray`]({
[`${type}Id`]: id,
...c.method
})
dispatch(loadXray(xray))
} catch (error) {
console.error(error)
}
}
)
export const FETCH_CARD_XRAY = 'metabase/xray/FETCH_CARD_XRAY';
export const fetchCardXray = createThunkAction(FETCH_CARD_XRAY, (cardId, cost) =>
async (dispatch) => {
const c = COSTS[cost]
dispatch(startLoad())
try {
const xray = await XRayApi.card_xray({ cardId, ...c.method });
dispatch(loadXray(xray));
return false
} catch (error) {
console.error(error);
dispatch(xrayError(error))
}
}
)
......@@ -77,6 +46,7 @@ export const fetchSegmentComparison = createThunkAction(
return dispatch(loadComparison(comparison))
} catch (error) {
console.error(error)
return dispatch(xrayError(error))
}
}
)
......@@ -93,99 +63,7 @@ export const fetchSegmentTableComparison = createThunkAction(
return dispatch(loadComparison(comparison))
} catch (error) {
console.error(error)
}
}
)
const FETCH_TABLE_COMPARISON = 'metabase/xray/FETCH_TABLE_COMPARISON';
export const fetchTableComparison = createThunkAction(
FETCH_TABLE_COMPARISON,
(tableId1, tableId2) =>
async () => {
try {
const comparison = await XRayApi.table_compare({ tableId1, tableId2 })
return comparison
} catch (error) {
console.error(error)
}
}
)
export const FETCH_CARD_COMPARISON = 'metabase/xray/FETCH_CARD_COMPARISON';
export const fetchCardComparison = createThunkAction(FETCH_CARD_COMPARISON, (cardId1, cardId2) =>
async () => {
try {
const comparison = await XRayApi.card_compare({ cardId1, cardId2 })
return comparison
} catch (error) {
console.error(error)
}
}
)
export const FETCH_FIELD_COMPARISON = 'metabase/xray/FETCH_FIELD_COMPARISON';
export const fetchFieldComparison = createThunkAction(
FETCH_FIELD_COMPARISON,
(fieldId1, fieldId2) =>
async (dispatch) => {
try {
const comparison = await XRayApi.field_compare({ fieldId1, fieldId2 })
dispatch(loadComparison(comparison))
return false
} catch (error) {
console.error(error)
}
}
)
/*
* NOTE Kyle Doherty 9/8/17 - future comparisons
export const FETCH_METRIC_COMPARISON = 'metabase/xray/FETCH_METRIC_COMPARISON';
export const fetchMetricComparison = createThunkAction(FETCH_METRIC_COMPARISON, function(metricId1, metricId2) {
async () => {
try {
const comparison = await XRayApi.metric_compare({ metricId1, metricId2 })
return comparison
} catch (error) {
console.error(error)
}
}
})
*/
export const FETCH_SEGMENT_TABLE_FIELD_COMPARISON = 'metabase/xray/FETCH_SEGMENT_TABLE_FIELD_COMPARISON';
export const fetchSegmentTableFieldComparison = createThunkAction(
FETCH_SEGMENT_TABLE_FIELD_COMPARISON,
(requestParams) =>
async (dispatch) => {
requestParams.cost = COSTS[requestParams.cost].method
dispatch(startLoad())
try {
const comparison = await XRayApi.segment_table_field_compare(requestParams)
return dispatch(loadComparison(comparison))
} catch (error) {
console.error(error)
}
}
)
export const FETCH_SEGMENT_FIELD_COMPARISON = 'metabase/xray/FETCH_SEGMENT_FIELD_COMPARISON';
export const fetchSegmentFieldComparison = createThunkAction(
FETCH_SEGMENT_FIELD_COMPARISON,
(requestParams) =>
async (dispatch) => {
requestParams.cost = COSTS[requestParams.cost].method
dispatch(startLoad())
try {
const comparison = await XRayApi.segment_field_compare(requestParams)
return dispatch(loadComparison(comparison))
} catch (error) {
console.error(error)
return dispatch(xrayError(error))
}
}
)
......@@ -199,6 +77,9 @@ export const loadXray = createAction(LOAD_XRAY)
export const LOAD_COMPARISON = 'metabase/xray/LOAD_COMPARISON'
export const loadComparison = createAction(LOAD_COMPARISON)
export const XRAY_ERROR = 'metabase/xray/XRAY_ERROR'
export const xrayError = createAction(XRAY_ERROR)
export default handleActions({
[START_LOAD]: {
next: (state, { payload }) => assoc(state, 'loading', true)
......@@ -216,8 +97,16 @@ export default handleActions({
.assoc('comparison', payload)
.assoc('loading', false)
.value()
},
[XRAY_ERROR]: {
next: (state, { payload }) =>
chain(state)
.assoc('loading', false)
.assoc('error', payload)
.value()
}
}, {
loading: false
loading: false,
error: null,
})
......@@ -11,7 +11,7 @@ import { mount } from "enzyme";
import { CardApi, SegmentApi, SettingsApi } from "metabase/services";
import { delay } from "metabase/lib/promise";
import { FETCH_CARD_XRAY, FETCH_SEGMENT_XRAY, FETCH_TABLE_XRAY, LOAD_XRAY } from "metabase/xray/xray";
import { FETCH_XRAY, LOAD_XRAY } from "metabase/xray/xray";
import TableXRay from "metabase/xray/containers/TableXRay";
import CostSelect from "metabase/xray/components/CostSelect";
import Constituent from "metabase/xray/components/Constituent";
......@@ -78,7 +78,7 @@ describe("xray integration tests", () => {
store.pushPath(`/xray/table/1/approximate`);
const app = mount(store.getAppContainer());
await store.waitForActions(FETCH_TABLE_XRAY, LOAD_XRAY, { timeout: 20000 })
await store.waitForActions(FETCH_XRAY, LOAD_XRAY, { timeout: 20000 })
const tableXRay = app.find(TableXRay)
expect(tableXRay.length).toBe(1)
......@@ -110,7 +110,7 @@ describe("xray integration tests", () => {
click(xrayOptionIcon);
await store.waitForActions(FETCH_CARD_XRAY, LOAD_XRAY, {timeout: 5000})
await store.waitForActions(FETCH_XRAY, LOAD_XRAY, {timeout: 5000})
expect(store.getPath()).toBe(`/xray/card/${timeBreakoutQuestion.id()}/extended`)
const cardXRay = app.find(CardXRay)
......@@ -131,7 +131,7 @@ describe("xray integration tests", () => {
const xrayOptionIcon = actionsWidget.find('.Icon.Icon-beaker')
click(xrayOptionIcon);
await store.waitForActions(FETCH_SEGMENT_XRAY, LOAD_XRAY, { timeout: 5000 })
await store.waitForActions(FETCH_XRAY, LOAD_XRAY, { timeout: 5000 })
expect(store.getPath()).toBe(`/xray/segment/${segmentId}/approximate`)
const segmentXRay = app.find(SegmentXRay)
......@@ -227,7 +227,7 @@ describe("xray integration tests", () => {
store.pushPath(`/xray/table/1/approximate`);
await store.waitForActions(FETCH_TABLE_XRAY, { timeout: 20000 })
await store.waitForActions(FETCH_XRAY, { timeout: 20000 })
await delay(200)
const tableXRay = app.find(TableXRay)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment