count);
- const sum = counts.reduce((a,b)=>parseInt(a)+parseInt(b),0);
+ const sum = counts.reduce((a, b) => parseInt(a) + parseInt(b), 0);
if (sum === 0) {
- return [];
+ return [];
}
- const otherPrcs = counts
- .map(c => c/sum * 100)
- .filter(hidePredicate);
- const otherPrcsSum = otherPrcs.reduce((a,b)=>a+b,0);
- const showLength = partitions.length - otherPrcs.length;
- const show = partitions
- .sort((a, b) => b.count - a.count)
- .slice(0, showLength)
- .map(p => ({
- label: mapCountry
- ? (countries[p.name] || "Unknown")
- : p.name,
- prc: p.count/sum * 100,
- }))
+ const otherPrcs = counts.map((c) => (c / sum) * 100).filter(hidePredicate);
+ const otherPrcsSum = otherPrcs.reduce((a, b) => a + b, 0);
+ const showLength = partitions.length - otherPrcs.length;
+ const show = partitions
+ .sort((a, b) => b.count - a.count)
+ .slice(0, showLength)
+ .map((p) => ({
+ label: mapCountry ? countries[p.name] || 'Unknown' : p.name,
+ prc: (p.count / sum) * 100,
+ }));
if (otherPrcsSum > 0) {
show.push({
- label: "Other",
+ label: 'Other',
prc: otherPrcsSum,
other: true,
- })
+ });
}
return show;
}
function tagsWrapper(tags = []) {
- return tags.map(({ name, partitions }) => ({
- name,
- partitions: partitionsWrapper(partitions, name === "country")
- }))
+ return tags.map(({ name, partitions }) => ({
+ name,
+ partitions: partitionsWrapper(partitions, name === 'country'),
+ }));
}
function dataWrapper(data = {}) {
- return {
- chart24: data.chart24 || [],
- chart30: data.chart30 || [],
- tags: tagsWrapper(data.tags),
- };
+ return {
+ chart24: data.chart24 || [],
+ chart30: data.chart30 || [],
+ tags: tagsWrapper(data.tags),
+ };
}
-@connect(state => ({
- error: state.getIn([ "errors", "instance" ])
-}))
-@withRequest({
- initialData: props => dataWrapper(props.error),
- endpoint: props => `/errors/${ props.error.errorId }/stats`,
- dataWrapper,
-})
-export default class SideSection extends React.PureComponent {
- onDateChange = ({ startDate, endDate }) => {
- this.props.request({ startDate, endDate });
- }
+function SideSection(props) {
+ const [data, setData] = React.useState({
+ chart24: [],
+ chart30: [],
+ tags: [],
+ });
+ const [loading, setLoading] = React.useState(false);
+ const { className } = props;
+ const { errorStore } = useStore();
+ const error = errorStore.instance;
- render() {
- const {
- className,
- error,
- data,
- loading,
- } = this.props;
- return (
-
-
Overview
-
-
-
-
-
-
- { data.tags.length > 0 &&
Summary
}
-
- { data.tags.map(({ name, partitions }) =>
-
- )}
-
-
- );
- }
+ const grabData = async () => {
+ setLoading(true);
+ errorService.fetchErrorStats(error.errorId)
+ .then(data => {
+ setData(dataWrapper(data))
+ })
+ .finally(() => setLoading(false));
+ }
+
+ React.useEffect(() => {
+ setData(dataWrapper(error))
+ }, [error.errorId])
+
+ return (
+
+
Overview
+
+
+
+
+
+
+ {data.tags.length > 0 &&
Summary
}
+
+ {data.tags.map(({ name, partitions }) => (
+
+ ))}
+
+
+ );
}
+
+export default observer(SideSection);
diff --git a/frontend/app/components/ui/ErrorDetails/ErrorDetails.tsx b/frontend/app/components/ui/ErrorDetails/ErrorDetails.tsx
index 2aca274ea..02ce0af1b 100644
--- a/frontend/app/components/ui/ErrorDetails/ErrorDetails.tsx
+++ b/frontend/app/components/ui/ErrorDetails/ErrorDetails.tsx
@@ -1,8 +1,9 @@
import React, { useEffect, useState } from 'react';
import ErrorFrame from '../ErrorFrame/ErrorFrame';
-import { fetchErrorStackList } from 'Duck/sessions';
import { Button, Icon } from 'UI';
import { connect } from 'react-redux';
+import { observer } from 'mobx-react-lite';
+import { useStore } from 'App/mstore';
const docLink = 'https://docs.openreplay.com/installation/upload-sourcemaps';
@@ -15,21 +16,16 @@ interface Props {
error: any;
}
function ErrorDetails(props: Props) {
- const { error, sessionId, message = '', errorStack = [], sourcemapUploaded = false } = props;
+ const { errorStore } = useStore();
+ const errorStack = errorStore.instanceTrace;
+ const { error, sessionId, message = '', sourcemapUploaded = false } = props;
const [showRaw, setShowRaw] = useState(false);
- const firstFunc = errorStack.first() && errorStack.first().function;
-
+ const firstFunc = errorStack[0] && errorStack[0].function;
const openDocs = () => {
window.open(docLink, '_blank');
};
- useEffect(() => {
- if (sessionId) {
- props.fetchErrorStackList(sessionId, error.errorId);
- }
- }, []);
-
return (
{!sourcemapUploaded && (
@@ -78,9 +74,6 @@ function ErrorDetails(props: Props) {
ErrorDetails.displayName = 'ErrorDetails';
export default connect(
(state: any) => ({
- // errorStack: state.getIn(['sessions', 'errorStack']),
- errorStack: state.getIn(['errors', 'instanceTrace']),
sessionId: state.getIn(['sessions', 'current']).sessionId,
- }),
- { fetchErrorStackList }
-)(ErrorDetails);
+ })
+)(observer(ErrorDetails));
diff --git a/frontend/app/mstore/errorStore.ts b/frontend/app/mstore/errorStore.ts
index 909dfcff6..6b0ce0c9e 100644
--- a/frontend/app/mstore/errorStore.ts
+++ b/frontend/app/mstore/errorStore.ts
@@ -1,13 +1,13 @@
import { makeAutoObservable } from 'mobx';
import apiClient from 'App/api_client';
-import { errorService } from 'App/services';
+import { errorService, sessionService } from 'App/services';
import { ErrorInfo } from './types/error';
export default class ErrorStore {
instance: ErrorInfo | null = null;
- instanceTrace: Record = [];
+ instanceTrace: Record[] = [];
stats: Record = {};
sourcemapUploaded = false;
isLoading = false;
@@ -65,8 +65,8 @@ export default class ErrorStore {
try {
const response = await errorService.fetchErrorTrace(id);
- this.setInstanceTrace(response.data.trace);
- this.setSourcemapUploaded(response.data.sourcemapUploaded);
+ this.setInstanceTrace(response.trace);
+ this.setSourcemapUploaded(response.sourcemapUploaded);
} catch (error) {
this.setErrorState(actionKey, error);
} finally {
diff --git a/frontend/app/mstore/metricStore.ts b/frontend/app/mstore/metricStore.ts
index 6119ddff5..950e79249 100644
--- a/frontend/app/mstore/metricStore.ts
+++ b/frontend/app/mstore/metricStore.ts
@@ -2,7 +2,7 @@ import { makeAutoObservable } from 'mobx';
import Widget from './types/widget';
import { metricService, errorService } from 'App/services';
import { toast } from 'react-toastify';
-import Error from './types/error';
+import { ErrorInfo } from './types/error';
import {
TIMESERIES,
TABLE,
@@ -318,7 +318,7 @@ export default class MetricStore {
errorService
.one(errorId)
.then((error: any) => {
- resolve(new Error().fromJSON(error));
+ resolve(new ErrorInfo(error));
})
.catch((error: any) => {
toast.error('Failed to fetch error details.');
diff --git a/frontend/app/mstore/types/widget.ts b/frontend/app/mstore/types/widget.ts
index 8495f1aed..99f1ad92d 100644
--- a/frontend/app/mstore/types/widget.ts
+++ b/frontend/app/mstore/types/widget.ts
@@ -9,7 +9,7 @@ import Period, {LAST_24_HOURS} from 'Types/app/period';
import Funnel from '../types/funnel';
import {metricService} from 'App/services';
import { FUNNEL, HEATMAP, INSIGHTS, TABLE, USER_PATH, WEB_VITALS } from "App/constants/card";
-import Error from '../types/error';
+import { ErrorInfo } from '../types/error';
import {getChartFormatter} from 'Types/dashboard/helper';
import FilterItem from './filterItem';
import {filtersMap} from 'Types/filter/newFilter';
@@ -299,7 +299,7 @@ export default class Widget {
}
if (this.metricOf === FilterKey.ERRORS) {
- _data['errors'] = data.errors.map((s: any) => new Error().fromJSON(s));
+ _data['errors'] = data.errors.map((s: any) => new ErrorInfo(s));
} else if (this.metricType === INSIGHTS) {
_data['issues'] = data
.filter((i: any) => i.change > 0 || i.change < 0)
diff --git a/frontend/app/services/ErrorService.ts b/frontend/app/services/ErrorService.ts
index dc8b3336e..70d51ccc3 100644
--- a/frontend/app/services/ErrorService.ts
+++ b/frontend/app/services/ErrorService.ts
@@ -1,4 +1,5 @@
import BaseService from './BaseService';
+import { IErrorStack } from 'Types/session/errorStack';
export default class ErrorService extends BaseService {
fetchError = async (id: string) => {
@@ -13,10 +14,11 @@ export default class ErrorService extends BaseService {
return await r.json();
};
- fetchErrorTrace = async (id: string) => {
+ fetchErrorTrace = async (id: string): Promise<{ trace: IErrorStack[], sourcemapUploaded: boolean }> => {
const r = await this.client.get(`/errors/${id}/sourcemaps`);
+ const { data } = await r.json()
- return await r.json();
+ return data;
};
fetchNewErrorsCount = async (params: any) => {
@@ -24,4 +26,10 @@ export default class ErrorService extends BaseService {
return await r.json();
};
+
+ fetchErrorStats = async (errorId: string) => {
+ const r = await this.client.get(`/errors/${errorId}/stats`);
+
+ return await r.json();
+ }
}