Merge branch 'dev' into insights_fix

This commit is contained in:
MauricioGarciaS 2023-01-12 15:19:47 +01:00
commit 104356bbe2
29 changed files with 294 additions and 67 deletions

View file

@ -27,7 +27,7 @@ RUN adduser -u 1001 openreplay -D
ENV TZ=UTC \
GIT_SHA=$GIT_SHA \
FS_ULIMIT=1000 \
FS_ULIMIT=10000 \
FS_DIR=/mnt/efs \
MAXMINDDB_FILE=/home/openreplay/geoip.mmdb \
UAPARSER_FILE=/home/openreplay/regexes.yaml \
@ -76,8 +76,8 @@ ENV TZ=UTC \
USE_FAILOVER=false \
GROUP_STORAGE_FAILOVER=failover \
TOPIC_STORAGE_FAILOVER=storage-failover \
PROFILER_ENABLED=false
PROFILER_ENABLED=false \
COMPRESSION_TYPE=zstd
ARG SERVICE_NAME

View file

@ -90,8 +90,8 @@ func (c *cacher) cacheURL(t *Task) {
defer res.Body.Close()
if res.StatusCode >= 400 {
printErr := true
// Retry 403 error
if res.StatusCode == 403 && t.retries > 0 {
// Retry 403/503 errors
if (res.StatusCode == 403 || res.StatusCode == 503) && t.retries > 0 {
c.workers.AddTask(t)
printErr = false
}

View file

@ -21,6 +21,7 @@ type Config struct {
ProducerCloseTimeout int `env:"PRODUCER_CLOSE_TIMEOUT,default=15000"`
UseFailover bool `env:"USE_FAILOVER,default=false"`
MaxFileSize int64 `env:"MAX_FILE_SIZE,default=524288000"`
UseSort bool `env:"USE_SESSION_SORT,default=true"`
UseProfiler bool `env:"PROFILER_ENABLED,default=false"`
}

View file

@ -42,6 +42,8 @@ type Storage struct {
sessionDEVSize syncfloat64.Histogram
readingDOMTime syncfloat64.Histogram
readingDEVTime syncfloat64.Histogram
sortingDOMTime syncfloat64.Histogram
sortingDEVTime syncfloat64.Histogram
archivingDOMTime syncfloat64.Histogram
archivingDEVTime syncfloat64.Histogram
uploadingDOMTime syncfloat64.Histogram
@ -79,6 +81,14 @@ func New(cfg *config.Config, s3 *storage.S3, metrics *monitoring.Metrics) (*Stor
if err != nil {
log.Printf("can't create reading_duration metric: %s", err)
}
sortingDOMTime, err := metrics.RegisterHistogram("sorting_duration")
if err != nil {
log.Printf("can't create reading_duration metric: %s", err)
}
sortingDEVTime, err := metrics.RegisterHistogram("sorting_dt_duration")
if err != nil {
log.Printf("can't create reading_duration metric: %s", err)
}
archivingDOMTime, err := metrics.RegisterHistogram("archiving_duration")
if err != nil {
log.Printf("can't create archiving_duration metric: %s", err)
@ -104,6 +114,8 @@ func New(cfg *config.Config, s3 *storage.S3, metrics *monitoring.Metrics) (*Stor
sessionDEVSize: sessionDevtoolsSize,
readingDOMTime: readingDOMTime,
readingDEVTime: readingDEVTime,
sortingDOMTime: sortingDOMTime,
sortingDEVTime: sortingDEVTime,
archivingDOMTime: archivingDOMTime,
archivingDEVTime: archivingDEVTime,
uploadingDOMTime: uploadingDOMTime,
@ -156,14 +168,41 @@ func (s *Storage) Upload(msg *messages.SessionEnd) (err error) {
return nil
}
func (s *Storage) openSession(filePath string) ([]byte, error) {
func (s *Storage) openSession(filePath string, tp FileType) ([]byte, error) {
// Check file size before download into memory
info, err := os.Stat(filePath)
if err == nil && info.Size() > s.cfg.MaxFileSize {
return nil, fmt.Errorf("big file, size: %d", info.Size())
}
// Read file into memory
return os.ReadFile(filePath)
raw, err := os.ReadFile(filePath)
if err != nil {
return nil, err
}
if !s.cfg.UseSort {
return raw, nil
}
start := time.Now()
res, err := s.sortSessionMessages(raw)
if err != nil {
return nil, fmt.Errorf("can't sort session, err: %s", err)
}
if tp == DOM {
s.sortingDOMTime.Record(context.Background(), float64(time.Now().Sub(start).Milliseconds()))
} else {
s.sortingDEVTime.Record(context.Background(), float64(time.Now().Sub(start).Milliseconds()))
}
return res, nil
}
func (s *Storage) sortSessionMessages(raw []byte) ([]byte, error) {
// Parse messages, sort by index and save result into slice of bytes
unsortedMessages, err := messages.SplitMessages(raw)
if err != nil {
log.Printf("can't sort session, err: %s", err)
return raw, nil
}
return messages.MergeMessages(raw, messages.SortMessages(unsortedMessages)), nil
}
func (s *Storage) prepareSession(path string, tp FileType, task *Task) error {
@ -172,7 +211,7 @@ func (s *Storage) prepareSession(path string, tp FileType, task *Task) error {
path += "devtools"
}
startRead := time.Now()
mob, err := s.openSession(path)
mob, err := s.openSession(path, tp)
if err != nil {
return err
}

View file

@ -1,6 +1,7 @@
package messages
import (
"encoding/binary"
"errors"
"fmt"
"io"
@ -13,6 +14,7 @@ type BytesReader interface {
ReadInt() (int64, error)
ReadBoolean() (bool, error)
ReadString() (string, error)
ReadIndex() (uint64, error)
Data() []byte
Pointer() int64
SetPointer(p int64)
@ -106,6 +108,15 @@ func (m *bytesReaderImpl) ReadString() (string, error) {
return str, nil
}
func (m *bytesReaderImpl) ReadIndex() (uint64, error) {
if len(m.data)-int(m.curr) < 8 {
return 0, fmt.Errorf("out of range")
}
size := binary.LittleEndian.Uint64(m.data[m.curr : m.curr+8])
m.curr += 8
return size, nil
}
func (m *bytesReaderImpl) Data() []byte {
return m.data
}

View file

@ -0,0 +1,71 @@
package messages
import (
"bytes"
"fmt"
"io"
"log"
"sort"
)
type msgInfo struct {
index uint64
start int64
end int64
}
func SplitMessages(data []byte) ([]*msgInfo, error) {
messages := make([]*msgInfo, 0)
reader := NewBytesReader(data)
for {
// Get message start
msgStart := reader.Pointer()
if int(msgStart) >= len(data) {
return messages, nil
}
// Read message index
msgIndex, err := reader.ReadIndex()
if err != nil {
if err != io.EOF {
log.Println(reader.Pointer(), msgStart)
return nil, fmt.Errorf("read message index err: %s", err)
}
return messages, nil
}
// Read message type
msgType, err := reader.ReadUint()
if err != nil {
return nil, fmt.Errorf("read message type err: %s", err)
}
// Read message body
_, err = ReadMessage(msgType, reader)
if err != nil {
return nil, fmt.Errorf("read message body err: %s", err)
}
// Add new message info to messages slice
messages = append(messages, &msgInfo{
index: msgIndex,
start: msgStart,
end: reader.Pointer(),
})
}
}
func SortMessages(messages []*msgInfo) []*msgInfo {
sort.SliceStable(messages, func(i, j int) bool {
return messages[i].index < messages[j].index
})
return messages
}
func MergeMessages(data []byte, messages []*msgInfo) []byte {
sortedSession := bytes.NewBuffer(make([]byte, 0, len(data)))
for _, info := range messages {
sortedSession.Write(data[info.start:info.end])
}
return sortedSession.Bytes()
}

View file

@ -15,13 +15,20 @@ type Producer struct {
func NewProducer(messageSizeLimit int, useBatch bool) *Producer {
kafkaConfig := &kafka.ConfigMap{
"enable.idempotence": true,
"bootstrap.servers": env.String("KAFKA_SERVERS"),
"go.delivery.reports": true,
"security.protocol": "plaintext",
"go.batch.producer": useBatch,
"queue.buffering.max.ms": 100,
"message.max.bytes": messageSizeLimit,
"enable.idempotence": true,
"bootstrap.servers": env.String("KAFKA_SERVERS"),
"go.delivery.reports": true,
"security.protocol": "plaintext",
"go.batch.producer": useBatch,
"message.max.bytes": messageSizeLimit, // should be synced with broker config
"linger.ms": 1000,
"queue.buffering.max.ms": 1000,
"batch.num.messages": 1000,
"queue.buffering.max.messages": 1000,
"retries": 3,
"retry.backoff.ms": 100,
"max.in.flight.requests.per.connection": 1,
"compression.type": env.String("COMPRESSION_TYPE"),
}
// Apply ssl configuration
if env.Bool("KAFKA_USE_SSL") {

View file

@ -1,6 +1,6 @@
import logger from 'App/logger';
import APIClient from './api_client';
import { UPDATE_JWT } from './duck/user';
import { LOGIN, UPDATE_JWT } from './duck/user';
export default () => (next) => (action) => {
const { types, call, ...rest } = action;
@ -14,7 +14,7 @@ export default () => (next) => (action) => {
return call(client)
.then(async (response) => {
if (response.status === 403) {
next({ type: UPDATE_JWT, data: null });
next({ type: LOGIN.FAILURE });
}
if (!response.ok) {
const text = await response.text();

View file

@ -11,10 +11,11 @@ interface Props {
series: any;
onRemoveSeries: (seriesIndex: any) => void;
canDelete?: boolean;
supportsEmpty?: boolean;
hideHeader?: boolean;
emptyMessage?: any;
observeChanges?: () => void;
excludeFilterKeys?: Array<string>
}
function FilterSeries(props: Props) {
@ -23,7 +24,9 @@ function FilterSeries(props: Props) {
},
canDelete,
hideHeader = false,
emptyMessage = 'Add user event or filter to define the series by clicking Add Step.'
emptyMessage = 'Add user event or filter to define the series by clicking Add Step.',
supportsEmpty = true,
excludeFilterKeys = []
} = props;
const [expanded, setExpanded] = useState(true)
const { series, seriesIndex } = props;
@ -74,6 +77,8 @@ function FilterSeries(props: Props) {
onUpdateFilter={onUpdateFilter}
onRemoveFilter={onRemoveFilter}
onChangeEventsOrder={onChangeEventsOrder}
supportsEmpty={supportsEmpty}
excludeFilterKeys={excludeFilterKeys}
/>
) : (
<div className="color-gray-medium">{emptyMessage}</div>
@ -84,6 +89,7 @@ function FilterSeries(props: Props) {
<FilterSelection
filter={undefined}
onFilterClick={onAddFilter}
excludeFilterKeys={excludeFilterKeys}
>
<Button variant="text-primary" icon="plus">ADD STEP</Button>
</FilterSelection>

View file

@ -19,9 +19,8 @@ import {
PERFORMANCE,
WEB_VITALS,
} from 'App/constants/card';
import { clickmapFilter } from 'App/types/filter/newFilter';
import { clickmapFilter, eventKeys } from 'App/types/filter/newFilter';
import { renderClickmapThumbnail } from './renderMap';
interface Props {
history: any;
match: any;
@ -51,6 +50,8 @@ function WidgetForm(props: Props) {
metric.metricType
);
const excludeFilterKeys = isClickmap ? eventKeys : []
const writeOption = ({ value, name }: { value: any; name: any }) => {
value = Array.isArray(value) ? value : value.value;
const obj: any = { [name]: value };
@ -201,6 +202,8 @@ function WidgetForm(props: Props) {
.map((series: any, index: number) => (
<div className="mb-2" key={series.name}>
<FilterSeries
supportsEmpty={!isClickmap}
excludeFilterKeys={excludeFilterKeys}
observeChanges={() => metric.updateKey('hasChanged', true)}
hideHeader={isTable || isClickmap}
seriesIndex={index}

View file

@ -122,15 +122,15 @@ export default class Login extends React.Component {
</div>
</div>
</Loader>
{ errors &&
<div className={ stl.errors }>
{ errors.length ?
(<div className={ stl.errors }>
{ errors.map(error => (
<div className={stl.errorItem}>
<Icon name="info" color="red" size="20"/>
<span className="color-red ml-2">{ error }<br /></span>
</div>
)) }
</div>
</div>) : null
}
{/* <div className={ stl.formFooter }> */}
<Button className="mt-2" type="submit" variant="primary" >{ 'Login' }</Button>

View file

@ -108,5 +108,5 @@ $offset: 10px;
}
.inactiveRow {
opacity: 0.5;
opacity: 0.4;
}

View file

@ -31,7 +31,7 @@ function AlertTriggersModal(props: Props) {
}, [])
return useObserver(() => (
<div className="bg-white box-shadow h-screen overflow-y-auto" style={{ width: '450px'}}>
<div className="bg-white box-shadow h-screen overflow-y-auto" style={{ width: '350px'}}>
<div className="flex items-center justify-between p-5 text-2xl">
<div>Alerts</div>
{ count > 0 && (

View file

@ -214,7 +214,7 @@ export default class TimeTable extends React.PureComponent<Props, State> {
'error color-red': !!row.isRed && row.isRed(),
'cursor-pointer': typeof onRowClick === 'function',
[stl.activeRow]: activeIndex === index,
// [stl.inactiveRow]: !activeIndex || index > activeIndex,
[stl.inactiveRow]: !activeIndex || index > activeIndex,
}
)}
onClick={typeof onRowClick === 'function' ? () => onRowClick(row, index) : undefined}

View file

@ -2,7 +2,7 @@ import React from 'react';
import FilterOperator from '../FilterOperator';
import FilterSelection from '../FilterSelection';
import FilterValue from '../FilterValue';
import { Icon } from 'UI';
import { Icon, Button } from 'UI';
import FilterSource from '../FilterSource';
import { FilterKey, FilterType } from 'App/types/filter/filterType';
import SubFilterItem from '../SubFilterItem';
@ -14,9 +14,11 @@ interface Props {
onRemoveFilter: () => void;
isFilter?: boolean;
saveRequestPayloads?: boolean;
disableDelete?: boolean;
excludeFilterKeys?: Array<string>;
}
function FilterItem(props: Props) {
const { isFilter = false, filterIndex, filter, saveRequestPayloads } = props;
const { isFilter = false, filterIndex, filter, saveRequestPayloads, disableDelete = false, excludeFilterKeys = [] } = props;
const canShowValues = !(filter.operator === 'isAny' || filter.operator === 'onAny' || filter.operator === 'isUndefined');
const isSubFilter = filter.type === FilterType.SUB_FILTERS;
@ -49,14 +51,14 @@ function FilterItem(props: Props) {
};
return (
<div className="flex items-center hover:bg-active-blue -mx-5 px-5 py-2">
<div className="flex items-center hover:bg-active-blue -mx-5 px-5">
<div className="flex items-start w-full">
{!isFilter && (
<div className="mt-1 flex-shrink-0 border w-6 h-6 text-xs flex items-center justify-center rounded-full bg-gray-light-shade mr-2">
<span>{filterIndex + 1}</span>
</div>
)}
<FilterSelection filter={filter} onFilterClick={replaceFilter} />
<FilterSelection filter={filter} onFilterClick={replaceFilter} excludeFilterKeys={excludeFilterKeys} disabled={disableDelete} />
{/* Filter with Source */}
{filter.hasSource && (
@ -102,10 +104,8 @@ function FilterItem(props: Props) {
</div>
)}
</div>
<div className="flex flex-shrink-0 self-start mt-1 ml-auto px-2">
<div className="cursor-pointer p-1" onClick={props.onRemoveFilter}>
<Icon name="trash" size="14" />
</div>
<div className="flex flex-shrink-0 self-start ml-auto">
<Button disabled={disableDelete} variant="text" icon="trash" onClick={props.onRemoveFilter} size="small" iconSize={14} />
</div>
</div>
);

View file

@ -12,13 +12,16 @@ interface Props {
hideEventsOrder?: boolean;
observeChanges?: () => void;
saveRequestPayloads?: boolean;
supportsEmpty?: boolean
excludeFilterKeys?: Array<string>
}
function FilterList(props: Props) {
const { observeChanges = () => {}, filter, hideEventsOrder = false, saveRequestPayloads } = props;
const { observeChanges = () => {}, filter, hideEventsOrder = false, saveRequestPayloads, supportsEmpty = true, excludeFilterKeys = [] } = props;
const filters = List(filter.filters);
const hasEvents = filters.filter((i: any) => i.isEvent).size > 0;
const hasFilters = filters.filter((i: any) => !i.isEvent).size > 0;
let rowIndex = 0;
const cannotDeleteFilter = hasEvents && !supportsEmpty;
useEffect(observeChanges, [filters]);
@ -69,6 +72,8 @@ function FilterList(props: Props) {
onUpdate={(filter) => props.onUpdateFilter(filterIndex, filter)}
onRemoveFilter={() => onRemoveFilter(filterIndex)}
saveRequestPayloads={saveRequestPayloads}
disableDelete={cannotDeleteFilter}
excludeFilterKeys={excludeFilterKeys}
/>
) : null
)}
@ -89,6 +94,7 @@ function FilterList(props: Props) {
filter={filter}
onUpdate={(filter) => props.onUpdateFilter(filterIndex, filter)}
onRemoveFilter={() => onRemoveFilter(filterIndex)}
excludeFilterKeys={excludeFilterKeys}
/>
) : null
)}

View file

@ -1,4 +1,4 @@
import React from 'react';
import React, { useEffect, useState } from 'react';
import { Icon, Loader } from 'UI';
import { connect } from 'react-redux';
import cn from 'classnames';
@ -6,10 +6,27 @@ import stl from './FilterModal.module.css';
import { filtersMap } from 'Types/filter/newFilter';
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
function filterJson(
jsonObj: Record<string, any>,
excludeKeys: string[] = []
): Record<string, any> {
let filtered: Record<string, any> = {};
for (const key in jsonObj) {
const arr = jsonObj[key].filter((i: any) => !excludeKeys.includes(i.key));
if (arr.length) {
filtered[key] = arr;
}
}
return filtered;
}
export const getMatchingEntries = (searchQuery: string, filters: Record<string, any>) => {
const matchingCategories: string[] = [];
const matchingFilters: Record<string, any> = {};
const lowerCaseQuery = searchQuery.toLowerCase();
if (lowerCaseQuery.length === 0) return {
matchingCategories: Object.keys(filters),
matchingFilters: filters,
@ -33,12 +50,13 @@ export const getMatchingEntries = (searchQuery: string, filters: Record<string,
interface Props {
filters: any,
onFilterClick?: (filter) => void,
onFilterClick?: (filter: any) => void,
filterSearchList: any,
// metaOptions: any,
isMainSearch?: boolean,
fetchingFilterSearchList: boolean,
searchQuery?: string,
excludeFilterKeys?: Array<string>
}
function FilterModal(props: Props) {
const {
@ -48,6 +66,7 @@ function FilterModal(props: Props) {
isMainSearch = false,
fetchingFilterSearchList,
searchQuery = '',
excludeFilterKeys = []
} = props;
const showSearchList = isMainSearch && searchQuery.length > 0;
@ -57,7 +76,7 @@ function FilterModal(props: Props) {
onFilterClick(_filter);
}
const { matchingCategories, matchingFilters } = getMatchingEntries(searchQuery, filters);
const { matchingCategories, matchingFilters } = getMatchingEntries(searchQuery, filterJson(filters, excludeFilterKeys));
const isResultEmpty = (!filterSearchList || Object.keys(filterSearchList).length === 0)
&& matchingCategories.length === 0 && Object.keys(matchingFilters).length === 0

View file

@ -3,7 +3,8 @@ import FilterModal from '../FilterModal';
import OutsideClickDetectingDiv from 'Shared/OutsideClickDetectingDiv';
import { Icon } from 'UI';
import { connect } from 'react-redux';
import { assist as assistRoute, isRoute } from "App/routes";
import { assist as assistRoute, isRoute } from 'App/routes';
import cn from 'classnames';
const ASSIST_ROUTE = assistRoute();
@ -14,40 +15,54 @@ interface Props {
onFilterClick: (filter: any) => void;
children?: any;
isLive?: boolean;
excludeFilterKeys?: Array<string>
disabled?: boolean
}
function FilterSelection(props: Props) {
const { filter, onFilterClick, children } = props;
const { filter, onFilterClick, children, excludeFilterKeys = [], disabled = false } = props;
const [showModal, setShowModal] = useState(false);
return (
<div className="relative flex-shrink-0">
<OutsideClickDetectingDiv
className="relative"
onClickOutside={ () => setTimeout(function() {
setShowModal(false)
}, 200)}
onClickOutside={() =>
setTimeout(function () {
setShowModal(false);
}, 200)
}
>
{ children ? React.cloneElement(children, { onClick: (e) => {
e.stopPropagation();
e.preventDefault();
setShowModal(true);
}}) : (
{children ? (
React.cloneElement(children, {
onClick: (e) => {
e.stopPropagation();
e.preventDefault();
setShowModal(true);
},
disabled: disabled
})
) : (
<div
className="rounded py-1 px-3 flex items-center cursor-pointer bg-gray-lightest text-ellipsis hover:bg-gray-light-shade"
className={cn("rounded py-1 px-3 flex items-center cursor-pointer bg-gray-lightest text-ellipsis hover:bg-gray-light-shade", { 'opacity-50 pointer-events-none' : disabled })}
style={{ width: '150px', height: '26px', border: 'solid thin #e9e9e9' }}
onClick={() => setShowModal(true)}
>
<div className="overflow-hidden whitespace-nowrap text-ellipsis mr-auto truncate" style={{ textOverflow: 'ellipsis'}}>{filter.label}</div>
<div
className="overflow-hidden whitespace-nowrap text-ellipsis mr-auto truncate"
style={{ textOverflow: 'ellipsis' }}
>
{filter.label}
</div>
<Icon name="chevron-down" size="14" />
</div>
) }
)}
</OutsideClickDetectingDiv>
{showModal && (
<div className="absolute left-0 border shadow rounded bg-white z-50">
<FilterModal
isLive={isRoute(ASSIST_ROUTE, window.location.pathname)}
onFilterClick={onFilterClick}
// filters={isRoute(ASSIST_ROUTE, window.location.pathname) ? props.filterListLive : props.filterList }
excludeFilterKeys={excludeFilterKeys}
/>
</div>
)}
@ -55,8 +70,11 @@ function FilterSelection(props: Props) {
);
}
export default connect((state: any) => ({
filterList: state.getIn([ 'search', 'filterList' ]),
filterListLive: state.getIn([ 'search', 'filterListLive' ]),
isLive: state.getIn([ 'sessions', 'activeTab' ]).type === 'live',
}), { })(FilterSelection);
export default connect(
(state: any) => ({
filterList: state.getIn(['search', 'filterList']),
filterListLive: state.getIn(['search', 'filterListLive']),
isLive: state.getIn(['sessions', 'activeTab']).type === 'live',
}),
{}
)(FilterSelection);

View file

@ -28,7 +28,11 @@ export const initialState = Map({
authDetails: {},
onboarding: false,
sites: List(),
jwt: null
jwt: null,
loginRequest: {
loading: false,
errors: []
},
});
const setClient = (state, data) => {
@ -50,10 +54,12 @@ const reducer = (state = initialState, action = {}) => {
switch (action.type) {
case UPDATE_JWT:
return state.set('jwt', action.data);
case LOGIN.REQUEST:
return state.set('loginRequest', { loading: true, errors: [] })
case RESET_PASSWORD.SUCCESS:
case UPDATE_PASSWORD.SUCCESS:
case LOGIN.SUCCESS:
state.set('account', Account({...action.data.user }))
state.set('account', Account({...action.data.user })).set('loginRequest', { loading: false, errors: [] })
case SIGNUP.SUCCESS:
state.set('account', Account(action.data.user)).set('onboarding', true);
case REQUEST_RESET_PASSWORD.SUCCESS:
@ -65,8 +71,10 @@ const reducer = (state = initialState, action = {}) => {
return state.set('authDetails', action.data);
case UPDATE_PASSWORD.FAILURE:
return state.set('passwordErrors', List(action.errors))
case FETCH_ACCOUNT.FAILURE:
case LOGIN.FAILURE:
deleteCookie('jwt', '/', 'openreplay.com')
return state.set('loginRequest', { loading: false, errors: ['Invalid username or password'] });
case FETCH_ACCOUNT.FAILURE:
case DELETE.SUCCESS:
case DELETE.FAILURE:
deleteCookie('jwt', '/', 'openreplay.com')
@ -86,7 +94,6 @@ const reducer = (state = initialState, action = {}) => {
export default withRequestState({
loginRequest: LOGIN,
signupRequest: SIGNUP,
updatePasswordRequest: UPDATE_PASSWORD,
requestResetPassowrd: REQUEST_RESET_PASSWORD,
@ -96,7 +103,7 @@ export default withRequestState({
updateAccountRequest: UPDATE_ACCOUNT,
}, reducer);
export const login = params => dispatch => dispatch({
export const login = params => ({
types: LOGIN.toArray(),
call: client => client.post('/login', params),
});

View file

@ -26,19 +26,18 @@ export default class WebPlayer extends Player {
private targetMarker: TargetMarker
constructor(protected wpState: Store<typeof WebPlayer.INITIAL_STATE>, session: any, live: boolean) {
console.log(session.events, session.stackEvents, session.resources, session.errors)
let initialLists = live ? {} : {
event: session.events,
event: session.events || [],
stack: session.stackEvents || [],
resource: session.resources || [], // MBTODO: put ResourceTiming in file
exceptions: session.errors.map(({ time, errorId, name }: any) =>
exceptions: session.errors?.map(({ time, errorId, name }: any) =>
Log({
level: LogLevel.ERROR,
value: name,
time,
errorId,
})
),
) || [],
}
const screen = new Screen(session.isMobile)

View file

@ -6,6 +6,9 @@ import apiMiddleware from './api_middleware';
import LocalStorage from './local_storage';
import { initialState as initUserState, UPDATE_JWT } from './duck/user'
// TODO @remove after few days
localStorage.removeItem('jwt')
const storage = new LocalStorage({
user: Object,
});

View file

@ -53,6 +53,8 @@ export const filters = [
{ key: FilterKey.ISSUE, type: FilterType.ISSUE, category: FilterCategory.JAVASCRIPT, label: 'Issue', placeholder: 'Select an issue', operator: 'is', operatorOptions: filterOptions.getOperatorsByKeys(['is', 'isAny', 'isNot']), icon: 'filters/click', options: filterOptions.issueOptions },
];
export const eventKeys = filters.filter((i) => i.isEvent).map(i => i.key);
export const clickmapFilter = {
key: FilterKey.LOCATION,
type: FilterType.MULTIPLE,

View file

@ -11,6 +11,11 @@ spec:
spec:
backoffLimit: 0 # Don't restart the failed jobs
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 12 }}
{{- end }}
spec:
restartPolicy: Never
containers:

View file

@ -12,6 +12,11 @@ spec:
spec:
backoffLimit: 0 # Don't restart the failed jobs
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 12 }}
{{- end }}
spec:
restartPolicy: Never
containers:

View file

@ -12,6 +12,11 @@ spec:
spec:
backoffLimit: 0 # Don't restart the failed jobs
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 12 }}
{{- end }}
spec:
restartPolicy: Never
containers:

View file

@ -12,6 +12,11 @@ spec:
spec:
backoffLimit: 0 # Don't restart the failed jobs
template:
metadata:
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 12 }}
{{- end }}
spec:
restartPolicy: Never
containers:

View file

@ -85,6 +85,9 @@ cron: "5 3 */3 * *"
# Pod configurations
podAnnotations:
linkerd.io/inject: disabled
securityContext:
runAsUser: 1001
runAsGroup: 1001

View file

@ -26,6 +26,10 @@ spec:
template:
metadata:
name: postgresqlMigrate
{{- with .Values.migrationJob.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
initContainers:
- name: git

View file

@ -1,3 +1,7 @@
migrationJob:
podAnnotations:
linkerd.io/inject: disabled
redis: &redis
tls:
enabled: false
@ -5,6 +9,10 @@ redis: &redis
ingress-nginx:
enabled: true
controller:
admissionWebhooks:
patch:
podAnnotations:
linkerd.io/inject: disabled
name: controller
image:
registry: k8s.gcr.io