fix(ui): some issue/assignment refactoring

This commit is contained in:
sylenien 2023-01-02 17:59:16 +01:00
parent df12385e5f
commit f6a62d835a
17 changed files with 188 additions and 179 deletions

View file

@ -36,7 +36,7 @@ function SessionList(props: Props) {
</div> </div>
<Loader loading={props.loading}> <Loader loading={props.loading}>
<NoContent <NoContent
show={!props.loading && props.list.size === 0} show={!props.loading && props.list.length === 0}
title={ title={
<div className="flex items-center justify-center flex-col"> <div className="flex items-center justify-center flex-col">
<AnimatedSVG name={ICONS.NO_LIVE_SESSIONS} size={170} /> <AnimatedSVG name={ICONS.NO_LIVE_SESSIONS} size={170} />

View file

@ -53,5 +53,5 @@ export default connect(state => ({
users: state.getIn(['assignments', 'users']), users: state.getIn(['assignments', 'users']),
loading: state.getIn(['assignments', 'fetchAssignment', 'loading']), loading: state.getIn(['assignments', 'fetchAssignment', 'loading']),
issueTypeIcons: state.getIn(['assignments', 'issueTypeIcons']), issueTypeIcons: state.getIn(['assignments', 'issueTypeIcons']),
issuesIntegration: state.getIn([ 'issues', 'list']).first() || {}, issuesIntegration: state.getIn([ 'issues', 'list'])[0] || {},
}))(IssueDetails); }))(IssueDetails);

View file

@ -1,7 +1,6 @@
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Form, Input, Button, CircularLoader, Loader } from 'UI'; import { Form, Input, Button, CircularLoader, Loader } from 'UI';
//import { } from 'Duck/issues';
import { addActivity, init, edit, fetchAssignments, fetchMeta } from 'Duck/assignments'; import { addActivity, init, edit, fetchAssignments, fetchMeta } from 'Duck/assignments';
import Select from 'Shared/Select'; import Select from 'Shared/Select';
@ -119,7 +118,6 @@ class IssueForm extends React.PureComponent {
selection selection
name="assignee" name="assignee"
options={userOptions} options={userOptions}
// value={ instance.assignee }
fluid fluid
onChange={this.writeOption} onChange={this.writeOption}
placeholder="Select a user" placeholder="Select a user"
@ -153,7 +151,7 @@ class IssueForm extends React.PureComponent {
<Button <Button
loading={creating} loading={creating}
variant="primary" variant="primary"
disabled={!instance.validate()} disabled={!instance.isValid}
className="float-left mr-2" className="float-left mr-2"
type="submit" type="submit"
> >

View file

@ -1,9 +1,8 @@
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Icon, Popover, Button } from 'UI'; import { Popover, Button } from 'UI';
import IssuesModal from './IssuesModal'; import IssuesModal from './IssuesModal';
import { fetchProjects, fetchMeta } from 'Duck/assignments'; import { fetchProjects, fetchMeta } from 'Duck/assignments';
import stl from './issues.module.css';
@connect( @connect(
(state) => ({ (state) => ({
@ -15,9 +14,7 @@ import stl from './issues.module.css';
fetchIssueLoading: state.getIn(['assignments', 'fetchAssignment', 'loading']), fetchIssueLoading: state.getIn(['assignments', 'fetchAssignment', 'loading']),
fetchIssuesLoading: state.getIn(['assignments', 'fetchAssignments', 'loading']), fetchIssuesLoading: state.getIn(['assignments', 'fetchAssignments', 'loading']),
projectsLoading: state.getIn(['assignments', 'fetchProjects', 'loading']), projectsLoading: state.getIn(['assignments', 'fetchProjects', 'loading']),
issuesIntegration: state.getIn(['issues', 'list']).first() || {}, issuesIntegration: state.getIn(['issues', 'list']) || {},
jiraConfig: state.getIn(['issues', 'list']).first(),
issuesFetched: state.getIn(['issues', 'issuesFetched']), issuesFetched: state.getIn(['issues', 'issuesFetched']),
}), }),
{ fetchMeta, fetchProjects } { fetchMeta, fetchProjects }
@ -58,13 +55,9 @@ class Issues extends React.Component {
render() { render() {
const { const {
sessionId, sessionId,
isModalDisplayed,
projectsLoading,
metaLoading,
fetchIssuesLoading,
issuesIntegration, issuesIntegration,
} = this.props; } = this.props;
const provider = issuesIntegration.provider; const provider = issuesIntegration.first()?.provider || '';
return ( return (
<Popover <Popover
@ -80,13 +73,6 @@ class Issues extends React.Component {
Create Issue Create Issue
</Button> </Button>
</div> </div>
{/* <div
className="flex items-center cursor-pointer"
disabled={!isModalDisplayed && (metaLoading || fetchIssuesLoading || projectsLoading)}
>
<Icon name={`integrations/${provider === 'jira' ? 'jira' : 'github'}`} size="16" />
<span className="ml-2 whitespace-nowrap">Create Issue</span>
</div> */}
</Popover> </Popover>
); );
} }

View file

@ -10,7 +10,7 @@ import styles from './playerBlock.module.css';
fullscreen: state.getIn(['components', 'player', 'fullscreen']), fullscreen: state.getIn(['components', 'player', 'fullscreen']),
sessionId: state.getIn(['sessions', 'current']).sessionId, sessionId: state.getIn(['sessions', 'current']).sessionId,
disabled: state.getIn(['components', 'targetDefiner', 'inspectorMode']), disabled: state.getIn(['components', 'targetDefiner', 'inspectorMode']),
jiraConfig: state.getIn(['issues', 'list']).first(), jiraConfig: state.getIn(['issues', 'list'])[0],
})) }))
export default class PlayerBlock extends React.PureComponent { export default class PlayerBlock extends React.PureComponent {
render() { render() {

View file

@ -169,7 +169,7 @@ function SessionList(props: Props) {
</Button> </Button>
</div> </div>
} }
show={!loading && list.size === 0} show={!loading && list.length === 0}
> >
{list.map((session: any) => ( {list.map((session: any) => (
<div key={session.sessionId} className="border-b"> <div key={session.sessionId} className="border-b">
@ -188,7 +188,7 @@ function SessionList(props: Props) {
<div className="flex items-center justify-between p-5"> <div className="flex items-center justify-between p-5">
<div> <div>
Showing <span className="font-medium">{(currentPage - 1) * pageSize + 1}</span> to{' '} Showing <span className="font-medium">{(currentPage - 1) * pageSize + 1}</span> to{' '}
<span className="font-medium">{(currentPage - 1) * pageSize + list.size}</span> of{' '} <span className="font-medium">{(currentPage - 1) * pageSize + list.length}</span> of{' '}
<span className="font-medium">{numberWithCommas(total)}</span> sessions. <span className="font-medium">{numberWithCommas(total)}</span> sessions.
</div> </div>
<Pagination <Pagination

View file

@ -2,10 +2,9 @@ import { List, Map, Set } from 'immutable';
import Assignment from 'Types/session/assignment'; import Assignment from 'Types/session/assignment';
import Activity from 'Types/session/activity'; import Activity from 'Types/session/activity';
import withRequestState, { RequestTypes } from './requestStateCreator'; import withRequestState, { RequestTypes } from './requestStateCreator';
import { createListUpdater, createItemInListUpdater } from './funcTools/tools'; import { createListUpdater } from './funcTools/tools';
import { editType, initType } from './funcTools/crud/types'; import { editType, initType } from './funcTools/crud/types';
import { createInit, createEdit } from './funcTools/crud'; import { createInit, createEdit } from './funcTools/crud';
import IssuesType from 'Types/issue/issuesType'
const idKey = 'id'; const idKey = 'id';
const name = 'assignment'; const name = 'assignment';
@ -22,8 +21,8 @@ const INIT = initType(name);
const initialState = Map({ const initialState = Map({
list: List(), list: List(),
instance: Assignment(), instance: new Assignment(),
activeIssue: Assignment(), activeIssue: new Assignment(),
issueTypes: List(), issueTypes: List(),
issueTypeIcons: Set(), issueTypeIcons: Set(),
users: List(), users: List(),
@ -33,22 +32,23 @@ const initialState = Map({
const reducer = (state = initialState, action = {}) => { const reducer = (state = initialState, action = {}) => {
const users = state.get('users'); const users = state.get('users');
var issueTypes = [] let issueTypes = []
switch (action.type) { switch (action.type) {
case INIT: case INIT:
action.instance.issueType = issueTypes.length > 0 ? issueTypes[0].id : ''; action.instance.issueType = issueTypes.length > 0 ? issueTypes[0].id : '';
return state.set('instance', Assignment(action.instance)); return state.set('instance', new Assignment(action.instance));
case EDIT: case EDIT:
return state.mergeIn([ 'instance' ], action.instance); const inst = state.get('instance')
return state.set('instance', new Assignment({ ...inst, ...action.instance }));
case FETCH_PROJECTS.SUCCESS: case FETCH_PROJECTS.SUCCESS:
return state.set('projects', List(action.data)).set('projectsFetched', true); return state.set('projects', List(action.data)).set('projectsFetched', true);
case FETCH_ASSIGNMENTS.SUCCESS: case FETCH_ASSIGNMENTS.SUCCESS:
return state.set('list', List(action.data).map(Assignment)); return state.set('list', List(action.data).map(as => new Assignment(as)));
case FETCH_ASSIGNMENT.SUCCESS: case FETCH_ASSIGNMENT.SUCCESS:
return state.set('activeIssue', Assignment({ ...action.data, users})); return state.set('activeIssue', new Assignment({ ...action.data, users}));
case FETCH_META.SUCCESS: case FETCH_META.SUCCESS:
issueTypes = action.data.issueTypes issueTypes = action.data.issueTypes
var issueTypeIcons = {} const issueTypeIcons = {}
issueTypes.forEach(iss => { issueTypes.forEach(iss => {
issueTypeIcons[iss.id] = iss.iconUrl issueTypeIcons[iss.id] = iss.iconUrl
}) })
@ -56,12 +56,12 @@ const reducer = (state = initialState, action = {}) => {
.set('users', List(action.data.users)) .set('users', List(action.data.users))
.set('issueTypeIcons', issueTypeIcons) .set('issueTypeIcons', issueTypeIcons)
case ADD_ACTIVITY.SUCCESS: case ADD_ACTIVITY.SUCCESS:
const instance = Assignment(action.data); const instance = new Assignment(action.data);
return listUpdater(state, instance); return listUpdater(state, instance);
case ADD_MESSAGE.SUCCESS: case ADD_MESSAGE.SUCCESS:
const user = users.filter(user => user.id === action.data.author).first(); const user = users.filter(user => user.id === action.data.author).first();
const activity = new Activity({ type: 'message', user, ...action.data,}); const activity = new Activity({ type: 'message', user, ...action.data,});
return state.updateIn([ 'activeIssue', 'activities' ], list => list.push(activity)); return state.update([ 'activeIssue' ], issue => issue.activities.push(activity));
default: default:
return state; return state;
} }
@ -79,7 +79,7 @@ export default withRequestState({
export const init = createInit(name); export const init = createInit(name);
export const edit = createEdit(name); export const edit = createEdit(name);
export function fetchProjects(sessionId) { export function fetchProjects() {
return { return {
types: FETCH_PROJECTS.toArray(), types: FETCH_PROJECTS.toArray(),
call: client => client.get(`/integrations/issues/list_projects`) call: client => client.get(`/integrations/issues/list_projects`)
@ -100,13 +100,6 @@ export function fetchAssignments(sessionId) {
} }
} }
export function fetchAssigment(sessionId, id) {
return {
types: FETCH_ASSIGNMENT.toArray(),
call: client => client.get(`/sessions/${ sessionId }/assign/${ id }`)
}
}
export function addActivity(sessionId, params) { export function addActivity(sessionId, params) {
const data = { ...params, assignee: params.assignee, issueType: params.issueType } const data = { ...params, assignee: params.assignee, issueType: params.issueType }
return { return {

View file

@ -9,7 +9,6 @@ import { createInit, createEdit } from './funcTools/crud';
const idKey = 'id'; const idKey = 'id';
const name = 'assignment'; const name = 'assignment';
const listUpdater = createListUpdater(idKey); const listUpdater = createListUpdater(idKey);
const itemInListUpdater = createItemInListUpdater(idKey);
const FETCH_ASSIGNMENTS = new RequestTypes('asignment/FETCH_ASSIGNMENTS'); const FETCH_ASSIGNMENTS = new RequestTypes('asignment/FETCH_ASSIGNMENTS');
const FETCH_ISSUE = new RequestTypes('asignment/FETCH_ISSUE'); const FETCH_ISSUE = new RequestTypes('asignment/FETCH_ISSUE');
@ -23,8 +22,8 @@ const RESET_ACTIVE_ISSUE = 'assignment/RESET_ACTIVE_ISSUE';
const initialState = Map({ const initialState = Map({
list: List(), list: List(),
instance: Assignment(), instance: new Assignment(),
activeIssue: Assignment(), activeIssue: new Assignment(),
issueTypes: List(), issueTypes: List(),
issueTypeIcons: Set(), issueTypeIcons: Set(),
users: List(), users: List(),
@ -39,9 +38,9 @@ const reducer = (state = initialState, action = {}) => {
case FETCH_PROJECTS.SUCCESS: case FETCH_PROJECTS.SUCCESS:
return state.set('projects', List(action.data)); return state.set('projects', List(action.data));
case FETCH_ASSIGNMENTS.SUCCESS: case FETCH_ASSIGNMENTS.SUCCESS:
return state.set('list', List(action.data).map(Assignment)); return state.set('list', action.data.map(as => new Assignment(as)));
case ADD_ACTIVITY.SUCCESS: case ADD_ACTIVITY.SUCCESS:
const instance = Assignment(action.data); const instance = new Assignment(action.data);
return listUpdater(state, instance); return listUpdater(state, instance);
case FETCH_META.SUCCESS: case FETCH_META.SUCCESS:
issueTypes = action.data.issueTypes; issueTypes = action.data.issueTypes;
@ -53,16 +52,16 @@ const reducer = (state = initialState, action = {}) => {
.set('users', List(action.data.users)) .set('users', List(action.data.users))
.set('issueTypeIcons', issueTypeIcons) .set('issueTypeIcons', issueTypeIcons)
case FETCH_ISSUE.SUCCESS: case FETCH_ISSUE.SUCCESS:
return state.set('activeIssue', Assignment({ ...action.data, users})); return state.set('activeIssue', new Assignment({ ...action.data, users}));
case RESET_ACTIVE_ISSUE: case RESET_ACTIVE_ISSUE:
return state.set('activeIssue', Assignment()); return state.set('activeIssue', new Assignment());
case ADD_MESSAGE.SUCCESS: case ADD_MESSAGE.SUCCESS:
const user = users.filter(user => user.id === action.data.author).first(); const user = users.filter(user => user.id === action.data.author).first();
const activity = new Activity({ type: 'message', user, ...action.data,}); const activity = new Activity({ type: 'message', user, ...action.data,});
return state.updateIn([ 'activeIssue', 'activities' ], list => list.push(activity)); return state.updateIn([ 'activeIssue', 'activities' ], list => list.push(activity));
case INIT: case INIT:
action.instance.issueType = issueTypes.length > 0 ? issueTypes[0].id : ''; action.instance.issueType = issueTypes.length > 0 ? issueTypes[0].id : '';
return state.set('instance', Assignment(action.instance)); return state.set('instance', new Assignment(action.instance));
case EDIT: case EDIT:
return state.mergeIn([ 'instance' ], action.instance); return state.mergeIn([ 'instance' ], action.instance);
default: default:
@ -101,13 +100,6 @@ export function fetchProjects() {
} }
} }
export function fetchIssue(sessionId, id) {
return {
types: FETCH_ISSUE.toArray(),
call: client => client.get(`/sessions/${ sessionId }/assign/jira/${ id }`)
}
}
export function fetchMeta(projectId) { export function fetchMeta(projectId) {
return { return {
types: FETCH_META.toArray(), types: FETCH_META.toArray(),

View file

@ -1,6 +1,7 @@
import { List, Map } from 'immutable'; import { List, Map } from 'immutable';
import Session from 'Types/session'; import Session from 'Types/session';
import ErrorStack from 'Types/session/errorStack'; import ErrorStack from 'Types/session/errorStack';
import { Location, InjectedEvent } from 'Types/session/event'
import Watchdog from 'Types/watchdog'; import Watchdog from 'Types/watchdog';
import { clean as cleanParams } from 'App/api_client'; import { clean as cleanParams } from 'App/api_client';
import withRequestState, { RequestTypes } from './requestStateCreator'; import withRequestState, { RequestTypes } from './requestStateCreator';
@ -46,7 +47,7 @@ const defaultDateFilters = {
}; };
const initObj = { const initObj = {
list: List(), list: [],
sessionIds: [], sessionIds: [],
current: new Session(), current: new Session(),
total: 0, total: 0,
@ -61,7 +62,7 @@ const initObj = {
filteredEvents: null, filteredEvents: null,
eventsQuery: '', eventsQuery: '',
showChatWindow: false, showChatWindow: false,
liveSessions: List(), liveSessions: [],
visitedEvents: List(), visitedEvents: List(),
insights: List(), insights: List(),
insightFilters: defaultDateFilters, insightFilters: defaultDateFilters,
@ -76,7 +77,12 @@ const initObj = {
const initialState = Map(initObj); const initialState = Map(initObj);
const reducer = (state = initialState, action = {}) => { interface IAction extends Record<string, any>{
type: string;
data: any;
}
const reducer = (state = initialState, action: IAction) => {
switch (action.type) { switch (action.type) {
case FETCH_ERROR_STACK.SUCCESS: case FETCH_ERROR_STACK.SUCCESS:
return state.set('errorStack', List(action.data.trace).map(es => new ErrorStack(es))).set('sourcemapUploaded', action.data.sourcemapUploaded); return state.set('errorStack', List(action.data.trace).map(es => new ErrorStack(es))).set('sourcemapUploaded', action.data.sourcemapUploaded);
@ -87,13 +93,11 @@ const reducer = (state = initialState, action = {}) => {
const { sessions, total } = action.data; const { sessions, total } = action.data;
const list = sessions.map(s => new Session(s)); const list = sessions.map(s => new Session(s));
console.log(sessions, list, action)
return state return state
.set('list', list) .set('list', list)
.set('sessionIds', list.map(({ sessionId }) => sessionId).toJS()) .set('sessionIds', list.map(({ sessionId }) => sessionId))
.set( .set('favoriteList', list.filter(({ favorite }) => favorite))
'favoriteList',
list.filter(({ favorite }) => favorite)
)
.set('total', total); .set('total', total);
case FETCH_AUTOPLAY_LIST.SUCCESS: case FETCH_AUTOPLAY_LIST.SUCCESS:
let sessionIds = state.get('sessionIds'); let sessionIds = state.get('sessionIds');
@ -125,13 +129,13 @@ const reducer = (state = initialState, action = {}) => {
const events = action.filter.events; const events = action.filter.events;
const session = new Session(action.data); const session = new Session(action.data);
const matching = []; const matching: number[] = [];
const visitedEvents = []; const visitedEvents: Location[] = [];
const tmpMap = {}; const tmpMap = new Set();
session.events.forEach((event) => { session.events.forEach((event) => {
if (event.type === 'LOCATION' && !tmpMap.hasOwnProperty(event.url)) { if (event.type === 'LOCATION' && !tmpMap.has(event.url)) {
tmpMap[event.url] = event.url; tmpMap.add(event.url);
visitedEvents.push(event); visitedEvents.push(event);
} }
}); });
@ -156,7 +160,7 @@ const reducer = (state = initialState, action = {}) => {
.set('host', visitedEvents[0] && visitedEvents[0].host); .set('host', visitedEvents[0] && visitedEvents[0].host);
} }
case FETCH_FAVORITE_LIST.SUCCESS: case FETCH_FAVORITE_LIST.SUCCESS:
return state.set('favoriteList', List(action.data).map(s => new Session(s))); return state.set('favoriteList', action.data.map(s => new Session(s)));
case TOGGLE_FAVORITE.SUCCESS: { case TOGGLE_FAVORITE.SUCCESS: {
const id = action.sessionId; const id = action.sessionId;
let mutableState = state let mutableState = state
@ -186,7 +190,7 @@ const reducer = (state = initialState, action = {}) => {
diff = diff === 0 ? s1.startedAt - s2.startedAt : diff; diff = diff === 0 ? s1.startedAt - s2.startedAt : diff;
return action.sign * diff; return action.sign * diff;
}; };
return state.update('list', (list) => list.sort(comparator)).update('favoriteList', (list) => list.sort(comparator)); return state.update('list', (list: Session[]) => list.sort(comparator)).update('favoriteList', (list: Session[]) => list.sort(comparator));
} }
case REDEFINE_TARGET: { case REDEFINE_TARGET: {
// TODO: update for list // TODO: update for list

View file

@ -1,47 +0,0 @@
import Record from 'Types/Record';
import Activity from './activity';
import { List } from 'immutable';
import { DateTime } from 'luxon';
import { validateName, notEmptyString } from 'App/validate';
export default Record({
id: undefined,
title: '',
timestamp: undefined,
creatorId: undefined,
sessionId: undefined,
projectId: '',
siteId: undefined,
activities: List(),
closed: false,
assignee: '',
commentsCount: undefined,
issueType: '',
description: '',
iconUrl: ''
}, {
fromJS: (assignment) => ({
...assignment,
timestamp: assignment.createdAt ? DateTime.fromISO(assignment.createdAt) : undefined,
activities: assignment.comments ? List(assignment.comments).map(activity => {
if (assignment.users) {
activity.user = assignment.users.filter(user => user.id === activity.author).first();
}
return new Activity(activity)
}) : List()
}),
methods: {
validate: function() {
return !!this.projectId && !!this.issueType &&
notEmptyString(this.title) && notEmptyString(this.description)
},
toCreate: function() {
return {
title: this.title,
description: this.description,
assignee: this.assignee,
issueType: this.issueType
}
}
}
})

View file

@ -0,0 +1,77 @@
import Activity, { IActivity } from './activity';
import { DateTime } from 'luxon';
import { notEmptyString } from 'App/validate';
interface IAssignment {
id: string;
title: string;
timestamp: number;
creatorId: string;
sessionId: string;
projectId: string;
siteId: string;
activities: [];
closed: boolean;
assignee: string;
commentsCount: number;
issueType: string;
description: string;
iconUrl: string;
createdAt?: string;
comments: IActivity[]
users: { id: string }[]
}
export default class Assignment {
id: IAssignment["id"];
title: IAssignment["title"] = '';
timestamp: IAssignment["timestamp"];
creatorId: IAssignment["creatorId"];
sessionId: IAssignment["sessionId"];
projectId: IAssignment["projectId"] = '';
siteId: IAssignment["siteId"];
activities: IAssignment["activities"];
closed: IAssignment["closed"];
assignee: IAssignment["assignee"] = '';
commentsCount: IAssignment["commentsCount"];
issueType: IAssignment["issueType"] = '';
description: IAssignment["description"] = '';
iconUrl: IAssignment["iconUrl"] = '';
constructor(assignment?: IAssignment) {
if (assignment) {
Object.assign(this, {
...assignment,
timestamp: assignment.createdAt ? DateTime.fromISO(assignment.createdAt) : undefined,
activities: assignment.comments ? assignment.comments.map(activity => {
if (assignment.users) {
// @ts-ignore ???
activity.user = assignment.users.filter(user => user.id === activity.author)[0];
}
return new Activity(activity)
}) : []
})
}
}
toJS() {
return this
}
validate() {
return !!this.projectId && !!this.issueType && notEmptyString(this.title) && notEmptyString(this.description)
}
get isValid() {
return !!this.projectId && !!this.issueType && notEmptyString(this.title) && notEmptyString(this.description)
}
toCreate() {
return {
title: this.title,
description: this.description,
assignee: this.assignee,
issueType: this.issueType
}
}
}

View file

@ -10,7 +10,7 @@ function getStck0InfoString(stack: Stack) {
return s; return s;
} }
type Stack = { function: string; url: string}[] type Stack = { function: string; url: string }[]
export interface IError { export interface IError {
sessionId: string sessionId: string

View file

@ -15,7 +15,7 @@ export default class ErrorStack {
lineNo: IErrorStack["lineNo"] lineNo: IErrorStack["lineNo"]
colNo: IErrorStack["colNo"] colNo: IErrorStack["colNo"]
offset: IErrorStack["offset"] offset: IErrorStack["offset"]
context:IErrorStack["context"] context: IErrorStack["context"]
constructor(es: IErrorStack) { constructor(es: IErrorStack) {
Object.assign(this, { Object.assign(this, {

View file

@ -111,7 +111,7 @@ class Input extends Event {
} }
class Location extends Event { export class Location extends Event {
readonly name = 'Location'; readonly name = 'Location';
readonly type = LOCATION; readonly type = LOCATION;
url: LocationEvent["url"] url: LocationEvent["url"]

View file

@ -12,7 +12,7 @@ const OTHER = 'other' as const;
function getResourceStatus(status: number, success: boolean) { function getResourceStatus(status: number, success: boolean) {
if (status != null) return String(status); if (status != null) return String(status);
if (typeof success === 'boolean' || typeof success === 'number') { if (typeof success === 'boolean' || typeof success === 'number') {
return !!success return !!success
? '2xx-3xx' ? '2xx-3xx'
: '4xx-5xx'; : '4xx-5xx';
} }
@ -20,8 +20,12 @@ function getResourceStatus(status: number, success: boolean) {
} }
function getResourceSuccess(success: boolean, status: number) { function getResourceSuccess(success: boolean, status: number) {
if (success != null) { return !!success } if (success != null) {
if (status != null) { return status < 400 } return !!success
}
if (status != null) {
return status < 400
}
return true return true
} }
@ -56,7 +60,7 @@ interface IResource {
success: boolean, success: boolean,
score: number, score: number,
method: string, method: string,
request:string, request: string,
response: string, response: string,
headerSize: number, headerSize: number,
encodedBodySize: number, encodedBodySize: number,

View file

@ -8,6 +8,7 @@ import { Note } from 'App/services/NotesService'
const HASH_MOD = 1610612741; const HASH_MOD = 1610612741;
const HASH_P = 53; const HASH_P = 53;
function hashString(s: string): number { function hashString(s: string): number {
let mul = 1; let mul = 1;
let hash = 0; let hash = 0;
@ -192,7 +193,7 @@ export default class Session {
const isMobile = ['console', 'mobile', 'tablet'].includes(userDeviceType); const isMobile = ['console', 'mobile', 'tablet'].includes(userDeviceType);
const events: InjectedEvent[] = [] const events: InjectedEvent[] = []
const rawEvents: (EventData & { key: number})[] = [] const rawEvents: (EventData & { key: number })[] = []
if (session.events?.length) { if (session.events?.length) {
(session.events as EventData[]).forEach((event: EventData, k) => { (session.events as EventData[]).forEach((event: EventData, k) => {
@ -224,12 +225,13 @@ export default class Session {
const exceptions = (errors as IError[]).map(e => new SessionError(e)) || []; const exceptions = (errors as IError[]).map(e => new SessionError(e)) || [];
const issuesList = (issues as IIssue[]).map((i, k) => new Issue({ ...i, time: i.timestamp - startedAt, key: k })) || []; const issuesList = (issues as IIssue[]).map(
(i, k) => new Issue({ ...i, time: i.timestamp - startedAt, key: k })) || [];
const rawNotes = notes; const rawNotes = notes;
const notesWithEvents = [...rawEvents, ...rawNotes].sort((a, b) => { const notesWithEvents = [...rawEvents, ...rawNotes].sort((a, b) => {
// @ts-ignore just in case // @ts-ignore just in case
const aTs = a.timestamp || a.time; const aTs = a.timestamp || a.time;
// @ts-ignore // @ts-ignore
const bTs = b.timestamp || b.time; const bTs = b.timestamp || b.time;

View file

@ -9,62 +9,62 @@ export const CLOUDWATCH = 'cloudwatch';
export const ELASTICSEARCH = 'elasticsearch'; export const ELASTICSEARCH = 'elasticsearch';
export const SUMOLOGIC = 'sumologic'; export const SUMOLOGIC = 'sumologic';
export const typeList = [ OPENREPLAY, SENTRY, DATADOG, STACKDRIVER, ROLLBAR, BUGSNAG, CLOUDWATCH, ELASTICSEARCH, SUMOLOGIC ]; export const typeList = [OPENREPLAY, SENTRY, DATADOG, STACKDRIVER, ROLLBAR, BUGSNAG, CLOUDWATCH, ELASTICSEARCH, SUMOLOGIC];
export function isRed(event: StackEvent) { export function isRed(event: StackEvent) {
if (!event.payload) return false; if (!event.payload) return false;
switch(event.source) { switch (event.source) {
case SENTRY: case SENTRY:
return event.payload['event.type'] === 'error'; return event.payload['event.type'] === 'error';
case DATADOG: case DATADOG:
return true; return true;
case STACKDRIVER: case STACKDRIVER:
return false; return false;
case ROLLBAR: case ROLLBAR:
return true; return true;
case NEWRELIC: case NEWRELIC:
return true; return true;
case BUGSNAG: case BUGSNAG:
return true; return true;
case CLOUDWATCH: case CLOUDWATCH:
return true; return true;
case SUMOLOGIC: case SUMOLOGIC:
return false; return false;
default: default:
return event.level === 'error'; return event.level === 'error';
} }
} }
export interface IStackEvent { export interface IStackEvent {
time: number; time: number;
timestamp: number; timestamp: number;
index: number; index: number;
name: string; name: string;
message: string; message: string;
payload: any; payload: any;
source: any; source: any;
level: string; level: string;
isRed: () => boolean; isRed: () => boolean;
} }
export default class StackEvent { export default class StackEvent {
time: IStackEvent["time"] time: IStackEvent["time"]
index: IStackEvent["index"]; index: IStackEvent["index"];
name: IStackEvent["name"]; name: IStackEvent["name"];
message: IStackEvent["message"]; message: IStackEvent["message"];
payload: IStackEvent["payload"]; payload: IStackEvent["payload"];
source: IStackEvent["source"]; source: IStackEvent["source"];
level: IStackEvent["level"]; level: IStackEvent["level"];
constructor(evt: IStackEvent) { constructor(evt: IStackEvent) {
Object.assign(this, { Object.assign(this, {
...evt, ...evt,
source: evt.source || OPENREPLAY source: evt.source || OPENREPLAY
}); });
} }
isRed() { isRed() {
return isRed(this); return isRed(this);
} }
} }