From f0d5238b650368631ff9b599b846acc0437352c8 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 17 Aug 2022 14:22:54 +0200 Subject: [PATCH 1/7] feat(ui) - url search - read query params --- .../shared/SessionSearch/SessionSearch.tsx | 91 +++++++-- frontend/app/types/filter/filterType.ts | 185 ++++++++++-------- 2 files changed, 171 insertions(+), 105 deletions(-) diff --git a/frontend/app/components/shared/SessionSearch/SessionSearch.tsx b/frontend/app/components/shared/SessionSearch/SessionSearch.tsx index 66bd28a1b..156c42312 100644 --- a/frontend/app/components/shared/SessionSearch/SessionSearch.tsx +++ b/frontend/app/components/shared/SessionSearch/SessionSearch.tsx @@ -1,25 +1,71 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import FilterList from 'Shared/Filters/FilterList'; import FilterSelection from 'Shared/Filters/FilterSelection'; import SaveFilterButton from 'Shared/SaveFilterButton'; import { connect } from 'react-redux'; import { Button } from 'UI'; import { edit, addFilter } from 'Duck/search'; +import { withRouter } from 'react-router'; +import { clearSearch, fetchSessions, addFilterByKeyAndValue } from 'Duck/search'; +import { FilterKey, getFilterKeyTypeByKey } from 'Types/filter/filterType'; + +const allowedQueryKeys = [ + 'userOs', + 'userId', + 'userBrowser', + 'userDevice', + 'userCountry', + 'startDate', + 'endDate', + 'minDuration', + 'maxDuration', + 'referrer', + 'sort', + 'order', +]; interface Props { appliedFilter: any; edit: typeof edit; addFilter: typeof addFilter; saveRequestPayloads: boolean; + location: any; + addFilterByKeyAndValue: typeof addFilterByKeyAndValue; } function SessionSearch(props: Props) { - const { appliedFilter, saveRequestPayloads = false } = props; + const { + appliedFilter, + saveRequestPayloads = false, + location: { search }, + } = props; const hasEvents = appliedFilter.filters.filter((i: any) => i.isEvent).size > 0; const hasFilters = appliedFilter.filters.filter((i: any) => !i.isEvent).size > 0; + useEffect(() => { + const queryParams = Object.fromEntries( + Object.entries(Object.fromEntries(new URLSearchParams(search))).filter(([key]) => + allowedQueryKeys.includes(key) + ) + ); + const entires = Object.entries(queryParams); + if (entires.length > 0) { + entires.forEach(([key, value]) => { + if (value !== '') { + const filterKey = getFilterKeyTypeByKey(key); + const valueArr = value.split('|'); + const operator = valueArr.shift(); + console.log('operator', operator, valueArr); + if (filterKey) { + props.addFilterByKeyAndValue(filterKey, valueArr, operator); + } + } + }); + } + }, []); + const onAddFilter = (filter: any) => { props.addFilter(filter); - } + }; const onUpdateFilter = (filterIndex: any, filter: any) => { const newFilters = appliedFilter.filters.map((_filter: any, i: any) => { @@ -31,10 +77,10 @@ function SessionSearch(props: Props) { }); props.edit({ - ...appliedFilter, - filters: newFilters, + ...appliedFilter, + filters: newFilters, }); - } + }; const onRemoveFilter = (filterIndex: any) => { const newFilters = appliedFilter.filters.filter((_filter: any, i: any) => { @@ -44,15 +90,15 @@ function SessionSearch(props: Props) { props.edit({ filters: newFilters, }); - } + }; const onChangeEventsOrder = (e: any, { value }: any) => { props.edit({ eventsOrder: value, }); - } + }; - return (hasEvents || hasFilters) ? ( + return hasEvents || hasFilters ? (
- + {/* */}
@@ -85,10 +129,17 @@ function SessionSearch(props: Props) {
- ) : <>; + ) : ( + <> + ); } -export default connect((state: any) => ({ - saveRequestPayloads: state.getIn(['site', 'active', 'saveRequestPayloads']), - appliedFilter: state.getIn([ 'search', 'instance' ]), -}), { edit, addFilter })(SessionSearch); +export default withRouter( + connect( + (state: any) => ({ + saveRequestPayloads: state.getIn(['site', 'active', 'saveRequestPayloads']), + appliedFilter: state.getIn(['search', 'instance']), + }), + { edit, addFilter, addFilterByKeyAndValue } + )(SessionSearch) +); diff --git a/frontend/app/types/filter/filterType.ts b/frontend/app/types/filter/filterType.ts index 772bea55e..17c84ece3 100644 --- a/frontend/app/types/filter/filterType.ts +++ b/frontend/app/types/filter/filterType.ts @@ -1,97 +1,112 @@ export enum FilterCategory { - INTERACTIONS = "Interactions", - GEAR = "Gear", - RECORDING_ATTRIBUTES = "Recording Attributes", - JAVASCRIPT = "Javascript", - USER = "User Identification", - METADATA = "Session & User Metadata", - PERFORMANCE = "Performance", + INTERACTIONS = 'Interactions', + GEAR = 'Gear', + RECORDING_ATTRIBUTES = 'Recording Attributes', + JAVASCRIPT = 'Javascript', + USER = 'User Identification', + METADATA = 'Session & User Metadata', + PERFORMANCE = 'Performance', +} + +export const getFilterKeyTypeByKey = (key: string) => { + switch (key) { + case 'userId': + return FilterKey.USERID; + case 'userOs': + return FilterKey.USER_OS; + case 'userBrowser': + return FilterKey.USER_BROWSER; + case 'userDevice': + return FilterKey.USER_DEVICE; + case 'userCountry': + return FilterKey.USER_COUNTRY; + } }; export enum IssueType { - CLICK_RAGE = "click_rage", - DEAD_CLICK = "dead_click", - EXCESSIVE_SCROLLING = "excessive_scrolling", - BAD_REQUEST = "bad_request", - MISSING_RESOURCE = "missing_resource", - MEMORY = "memory", - CPU = "cpu", - SLOW_RESOURCE = "slow_resource", - SLOW_PAGE_LOAD = "slow_page_load", - CRASH = "crash", - CUSTOM = "custom", - JS_EXCEPTION = "js_exception", + CLICK_RAGE = 'click_rage', + DEAD_CLICK = 'dead_click', + EXCESSIVE_SCROLLING = 'excessive_scrolling', + BAD_REQUEST = 'bad_request', + MISSING_RESOURCE = 'missing_resource', + MEMORY = 'memory', + CPU = 'cpu', + SLOW_RESOURCE = 'slow_resource', + SLOW_PAGE_LOAD = 'slow_page_load', + CRASH = 'crash', + CUSTOM = 'custom', + JS_EXCEPTION = 'js_exception', } export enum FilterType { - STRING = "STRING", - ISSUE = "ISSUE", - BOOLEAN = "BOOLEAN", - NUMBER = "NUMBER", - NUMBER_MULTIPLE = "NUMBER_MULTIPLE", - DURATION = "DURATION", - MULTIPLE = "MULTIPLE", - SUB_FILTERS = "SUB_FILTERS", - COUNTRY = "COUNTRY", - DROPDOWN = "DROPDOWN", - MULTIPLE_DROPDOWN = "MULTIPLE_DROPDOWN", - AUTOCOMPLETE_LOCAL = "AUTOCOMPLETE_LOCAL", -}; + STRING = 'STRING', + ISSUE = 'ISSUE', + BOOLEAN = 'BOOLEAN', + NUMBER = 'NUMBER', + NUMBER_MULTIPLE = 'NUMBER_MULTIPLE', + DURATION = 'DURATION', + MULTIPLE = 'MULTIPLE', + SUB_FILTERS = 'SUB_FILTERS', + COUNTRY = 'COUNTRY', + DROPDOWN = 'DROPDOWN', + MULTIPLE_DROPDOWN = 'MULTIPLE_DROPDOWN', + AUTOCOMPLETE_LOCAL = 'AUTOCOMPLETE_LOCAL', +} export enum FilterKey { - ERROR = "ERROR", - MISSING_RESOURCE = "MISSING_RESOURCE", - SLOW_SESSION = "SLOW_SESSION", - CLICK_RAGE = "CLICK_RAGE", - CLICK = "CLICK", - INPUT = "INPUT", - LOCATION = "LOCATION", - VIEW = "VIEW", - CONSOLE = "CONSOLE", - METADATA = "METADATA", - CUSTOM = "CUSTOM", - URL = "URL", - USER_BROWSER = "USERBROWSER", - USER_OS = "USEROS", - USER_DEVICE = "USERDEVICE", - PLATFORM = "PLATFORM", - DURATION = "DURATION", - REFERRER = "REFERRER", - USER_COUNTRY = "USERCOUNTRY", - JOURNEY = "JOURNEY", - REQUEST = "REQUEST", - GRAPHQL = "GRAPHQL", - STATEACTION = "STATEACTION", - REVID = "REVID", - USERANONYMOUSID = "USERANONYMOUSID", - USERID = "USERID", - ISSUE = "ISSUE", - EVENTS_COUNT = "EVENTS_COUNT", - UTM_SOURCE = "UTM_SOURCE", - UTM_MEDIUM = "UTM_MEDIUM", - UTM_CAMPAIGN = "UTM_CAMPAIGN", - - DOM_COMPLETE = "DOM_COMPLETE", - LARGEST_CONTENTFUL_PAINT_TIME = "LARGEST_CONTENTFUL_PAINT_TIME", - TIME_BETWEEN_EVENTS = "TIME_BETWEEN_EVENTS", - TTFB = "TTFB", - AVG_CPU_LOAD = "AVG_CPU_LOAD", - AVG_MEMORY_USAGE = "AVG_MEMORY_USAGE", - FETCH_FAILED = "FETCH_FAILED", - - FETCH = "FETCH", - FETCH_URL = "FETCH_URL", - FETCH_STATUS_CODE = "FETCH_STATUS_CODE", - FETCH_METHOD = "FETCH_METHOD", - FETCH_DURATION = "FETCH_DURATION", - FETCH_REQUEST_BODY = "FETCH_REQUEST_BODY", - FETCH_RESPONSE_BODY = "FETCH_RESPONSE_BODY", + ERROR = 'ERROR', + MISSING_RESOURCE = 'MISSING_RESOURCE', + SLOW_SESSION = 'SLOW_SESSION', + CLICK_RAGE = 'CLICK_RAGE', + CLICK = 'CLICK', + INPUT = 'INPUT', + LOCATION = 'LOCATION', + VIEW = 'VIEW', + CONSOLE = 'CONSOLE', + METADATA = 'METADATA', + CUSTOM = 'CUSTOM', + URL = 'URL', + USER_BROWSER = 'USERBROWSER', + USER_OS = 'USEROS', + USER_DEVICE = 'USERDEVICE', + PLATFORM = 'PLATFORM', + DURATION = 'DURATION', + REFERRER = 'REFERRER', + USER_COUNTRY = 'USERCOUNTRY', + JOURNEY = 'JOURNEY', + REQUEST = 'REQUEST', + GRAPHQL = 'GRAPHQL', + STATEACTION = 'STATEACTION', + REVID = 'REVID', + USERANONYMOUSID = 'USERANONYMOUSID', + USERID = 'USERID', + ISSUE = 'ISSUE', + EVENTS_COUNT = 'EVENTS_COUNT', + UTM_SOURCE = 'UTM_SOURCE', + UTM_MEDIUM = 'UTM_MEDIUM', + UTM_CAMPAIGN = 'UTM_CAMPAIGN', - GRAPHQL_NAME = "GRAPHQL_NAME", - GRAPHQL_METHOD = "GRAPHQL_METHOD", - GRAPHQL_REQUEST_BODY = "GRAPHQL_REQUEST_BODY", - GRAPHQL_RESPONSE_BODY = "GRAPHQL_RESPONSE_BODY", + DOM_COMPLETE = 'DOM_COMPLETE', + LARGEST_CONTENTFUL_PAINT_TIME = 'LARGEST_CONTENTFUL_PAINT_TIME', + TIME_BETWEEN_EVENTS = 'TIME_BETWEEN_EVENTS', + TTFB = 'TTFB', + AVG_CPU_LOAD = 'AVG_CPU_LOAD', + AVG_MEMORY_USAGE = 'AVG_MEMORY_USAGE', + FETCH_FAILED = 'FETCH_FAILED', + + FETCH = 'FETCH', + FETCH_URL = 'FETCH_URL', + FETCH_STATUS_CODE = 'FETCH_STATUS_CODE', + FETCH_METHOD = 'FETCH_METHOD', + FETCH_DURATION = 'FETCH_DURATION', + FETCH_REQUEST_BODY = 'FETCH_REQUEST_BODY', + FETCH_RESPONSE_BODY = 'FETCH_RESPONSE_BODY', + + GRAPHQL_NAME = 'GRAPHQL_NAME', + GRAPHQL_METHOD = 'GRAPHQL_METHOD', + GRAPHQL_REQUEST_BODY = 'GRAPHQL_REQUEST_BODY', + GRAPHQL_RESPONSE_BODY = 'GRAPHQL_RESPONSE_BODY', SESSIONS = 'SESSIONS', - ERRORS = 'js_exception' -} \ No newline at end of file + ERRORS = 'js_exception', +} From a8760279b949d7dd762d7941b313043a76901b84 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 17 Aug 2022 18:08:24 +0200 Subject: [PATCH 2/7] feat(ui) - url search - read query params --- .../shared/SessionSearch/SessionSearch.tsx | 134 ++++++------------ .../SessionSearchQueryParamHandler.tsx | 111 +++++++++++++++ .../SessionSearchQueryParamHandler/index.ts | 1 + frontend/app/types/filter/filterType.ts | 97 ++++++++++++- frontend/app/types/filter/newFilter.js | 2 +- 5 files changed, 252 insertions(+), 93 deletions(-) create mode 100644 frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx create mode 100644 frontend/app/components/shared/SessionSearchQueryParamHandler/index.ts diff --git a/frontend/app/components/shared/SessionSearch/SessionSearch.tsx b/frontend/app/components/shared/SessionSearch/SessionSearch.tsx index 156c42312..8fc9f16c5 100644 --- a/frontend/app/components/shared/SessionSearch/SessionSearch.tsx +++ b/frontend/app/components/shared/SessionSearch/SessionSearch.tsx @@ -1,68 +1,23 @@ -import React, { useEffect } from 'react'; +import React from 'react'; import FilterList from 'Shared/Filters/FilterList'; import FilterSelection from 'Shared/Filters/FilterSelection'; import SaveFilterButton from 'Shared/SaveFilterButton'; import { connect } from 'react-redux'; import { Button } from 'UI'; import { edit, addFilter } from 'Duck/search'; -import { withRouter } from 'react-router'; -import { clearSearch, fetchSessions, addFilterByKeyAndValue } from 'Duck/search'; -import { FilterKey, getFilterKeyTypeByKey } from 'Types/filter/filterType'; - -const allowedQueryKeys = [ - 'userOs', - 'userId', - 'userBrowser', - 'userDevice', - 'userCountry', - 'startDate', - 'endDate', - 'minDuration', - 'maxDuration', - 'referrer', - 'sort', - 'order', -]; +import SessionSearchQueryParamHandler from 'Shared/SessionSearchQueryParamHandler'; interface Props { appliedFilter: any; edit: typeof edit; addFilter: typeof addFilter; saveRequestPayloads: boolean; - location: any; - addFilterByKeyAndValue: typeof addFilterByKeyAndValue; } function SessionSearch(props: Props) { - const { - appliedFilter, - saveRequestPayloads = false, - location: { search }, - } = props; + const { appliedFilter, saveRequestPayloads = false } = props; const hasEvents = appliedFilter.filters.filter((i: any) => i.isEvent).size > 0; const hasFilters = appliedFilter.filters.filter((i: any) => !i.isEvent).size > 0; - useEffect(() => { - const queryParams = Object.fromEntries( - Object.entries(Object.fromEntries(new URLSearchParams(search))).filter(([key]) => - allowedQueryKeys.includes(key) - ) - ); - const entires = Object.entries(queryParams); - if (entires.length > 0) { - entires.forEach(([key, value]) => { - if (value !== '') { - const filterKey = getFilterKeyTypeByKey(key); - const valueArr = value.split('|'); - const operator = valueArr.shift(); - console.log('operator', operator, valueArr); - if (filterKey) { - props.addFilterByKeyAndValue(filterKey, valueArr, operator); - } - } - }); - } - }, []); - const onAddFilter = (filter: any) => { props.addFilter(filter); }; @@ -98,48 +53,51 @@ function SessionSearch(props: Props) { }); }; - return hasEvents || hasFilters ? ( -
-
- -
+ return ( + <> + + {hasEvents || hasFilters ? ( +
+
+ +
-
-
- - {/* */} - - +
+
+ + {/* */} + + +
+
+ +
+
-
- -
-
-
- ) : ( - <> + ) : ( + <> + )} + ); } -export default withRouter( - connect( - (state: any) => ({ - saveRequestPayloads: state.getIn(['site', 'active', 'saveRequestPayloads']), - appliedFilter: state.getIn(['search', 'instance']), - }), - { edit, addFilter, addFilterByKeyAndValue } - )(SessionSearch) -); +export default connect( + (state: any) => ({ + saveRequestPayloads: state.getIn(['site', 'active', 'saveRequestPayloads']), + appliedFilter: state.getIn(['search', 'instance']), + }), + { edit, addFilter } +)(SessionSearch); diff --git a/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx b/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx new file mode 100644 index 000000000..bc75e0195 --- /dev/null +++ b/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx @@ -0,0 +1,111 @@ +import React, { useEffect } from 'react'; +import { useHistory } from 'react-router'; +import { connect } from 'react-redux'; +import { addFilterByKeyAndValue } from 'Duck/search'; +import { getFilterKeyTypeByKey, setQueryParamKeyFromFilterkey } from 'Types/filter/filterType'; + +const allowedQueryKeys = [ + 'userId', + 'userid', + 'uid', + 'usera', + + 'clk', + 'inp', + 'loc', + + 'os', + 'browser', + 'device', + 'platform', + 'revid', + + 'country', + + // 'startDate', + // 'endDate', + // 'minDuration', + // 'maxDuration', + 'ref', + 'sort', + 'order', + 'ce', + 'sa', + 'err', + 'iss', + + // PERFORMANCE + 'domc', + 'lcp', + 'ttfb', + 'acpu', + 'amem', + 'ff', +]; + +interface Props { + appliedFilter: any; + addFilterByKeyAndValue: typeof addFilterByKeyAndValue; +} +function SessionSearchQueryParamHandler(props: Props) { + const { appliedFilter } = props; + const history = useHistory(); + + const createUrlQuery = (filters: any) => { + const query: any = {}; + filters.forEach((filter: any) => { + if (filter.value.length > 0) { + const _key = setQueryParamKeyFromFilterkey(filter.key); + query[_key] = `${filter.operator}|${filter.value.join('|')}`; + } + }); + return query; + }; + + const addFilter = ([key, value]: [string, string]): void => { + if (value !== '') { + const filterKey = getFilterKeyTypeByKey(key); + const valueArr = value.split('|'); + const operator = valueArr.shift(); + // TODO validate operator + if (filterKey) { + props.addFilterByKeyAndValue(filterKey, valueArr, operator); + } + } + }; + + const applyFilterFromQuery = () => { + const entires = getQueryObject(history.location.search); + console.log('entires', entires); + if (entires.length > 0) { + entires.forEach(addFilter); + } + }; + + useEffect(() => { + console.log('rerender'); + applyFilterFromQuery(); + }, []); + + useEffect(() => { + const query: any = createUrlQuery(appliedFilter.filters); + history.replace({ search: new URLSearchParams(query).toString() }); + }, [appliedFilter]); + return <>; +} + +export default connect( + (state: any) => ({ + appliedFilter: state.getIn(['search', 'instance']), + }), + { addFilterByKeyAndValue } +)(SessionSearchQueryParamHandler); + +function getQueryObject(search: any) { + const queryParams = Object.fromEntries( + Object.entries(Object.fromEntries(new URLSearchParams(search))).filter(([key]) => + allowedQueryKeys.includes(key) + ) + ); + return Object.entries(queryParams); +} diff --git a/frontend/app/components/shared/SessionSearchQueryParamHandler/index.ts b/frontend/app/components/shared/SessionSearchQueryParamHandler/index.ts new file mode 100644 index 000000000..c13bb493d --- /dev/null +++ b/frontend/app/components/shared/SessionSearchQueryParamHandler/index.ts @@ -0,0 +1 @@ +export { default } from './SessionSearchQueryParamHandler'; \ No newline at end of file diff --git a/frontend/app/types/filter/filterType.ts b/frontend/app/types/filter/filterType.ts index 17c84ece3..9915a3aa5 100644 --- a/frontend/app/types/filter/filterType.ts +++ b/frontend/app/types/filter/filterType.ts @@ -8,18 +8,107 @@ export enum FilterCategory { PERFORMANCE = 'Performance', } +export const setQueryParamKeyFromFilterkey = (filterKey: string) => { + switch (filterKey) { + case FilterKey.USERID: + return 'uid'; + case FilterKey.USERANONYMOUSID: + return 'usera'; + case FilterKey.CLICK: + return 'clk'; + case FilterKey.INPUT: + return 'inp'; + case FilterKey.LOCATION: + return 'loc'; + case FilterKey.USER_OS: + return 'os'; + case FilterKey.USER_BROWSER: + return 'browser'; + case FilterKey.USER_DEVICE: + return 'device'; + case FilterKey.PLATFORM: + return 'platform'; + case FilterKey.REVID: + return 'revid'; + case FilterKey.USER_COUNTRY: + return 'country'; + case FilterKey.REFERRER: + return 'ref'; + case FilterKey.CUSTOM: + return 'ce'; + case FilterKey.STATEACTION: + return 'sa'; + case FilterKey.ERROR: + return 'err'; + case FilterKey.ISSUE: + return 'iss'; + + // PERFORMANCE + case FilterKey.DOM_COMPLETE: + return 'domc'; + case FilterKey.LARGEST_CONTENTFUL_PAINT_TIME: + return 'lcp'; + case FilterKey.TTFB: + return 'ttfb'; + case FilterKey.AVG_CPU_LOAD: + return 'acpu'; + case FilterKey.AVG_MEMORY_USAGE: + return 'amem'; + case FilterKey.FETCH_FAILED: + return 'ff'; + } +}; + export const getFilterKeyTypeByKey = (key: string) => { switch (key) { case 'userId': + case 'uid': + case 'userid': return FilterKey.USERID; - case 'userOs': + case 'usera': + return FilterKey.USERANONYMOUSID; + case 'clk': + return FilterKey.CLICK; + case 'inp': + return FilterKey.INPUT; + case 'loc': + return FilterKey.LOCATION; + case 'os': return FilterKey.USER_OS; - case 'userBrowser': + case 'browser': return FilterKey.USER_BROWSER; - case 'userDevice': + case 'device': return FilterKey.USER_DEVICE; - case 'userCountry': + case 'platform': + return FilterKey.PLATFORM; + case 'revid': + return FilterKey.REVID; + case 'country': return FilterKey.USER_COUNTRY; + case 'ref': + return FilterKey.REFERRER; + case 'ce': + return FilterKey.CUSTOM; + case 'sa': + return FilterKey.STATEACTION; + case 'err': + return FilterKey.ERROR; + case 'iss': + return FilterKey.ISSUE; + + // PERFORMANCE + case 'domc': + return FilterKey.DOM_COMPLETE; + case 'lcp': + return FilterKey.LARGEST_CONTENTFUL_PAINT_TIME; + case 'ttfb': + return FilterKey.TTFB; + case 'acpu': + return FilterKey.AVG_CPU_LOAD; + case 'amem': + return FilterKey.AVG_MEMORY_USAGE; + case 'ff': + return FilterKey.FETCH_FAILED; } }; diff --git a/frontend/app/types/filter/newFilter.js b/frontend/app/types/filter/newFilter.js index 9a87da2c6..452c4f1c9 100644 --- a/frontend/app/types/filter/newFilter.js +++ b/frontend/app/types/filter/newFilter.js @@ -11,7 +11,7 @@ export const filters = [ { key: FilterKey.INPUT, type: FilterType.MULTIPLE, category: FilterCategory.INTERACTIONS, label: 'Input', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/input', isEvent: true }, { key: FilterKey.LOCATION, type: FilterType.MULTIPLE, category: FilterCategory.INTERACTIONS, label: 'Path', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/location', isEvent: true }, { key: FilterKey.CUSTOM, type: FilterType.MULTIPLE, category: FilterCategory.JAVASCRIPT, label: 'Custom Events', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/custom', isEvent: true }, - { key: FilterKey.REQUEST, type: FilterType.MULTIPLE, category: FilterCategory.JAVASCRIPT, label: 'Fetch', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/fetch', isEvent: true }, + // { key: FilterKey.REQUEST, type: FilterType.MULTIPLE, category: FilterCategory.JAVASCRIPT, label: 'Fetch', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/fetch', isEvent: true }, { key: FilterKey.FETCH, type: FilterType.SUB_FILTERS, category: FilterCategory.JAVASCRIPT, operator: 'is', label: 'Network Request', filters: [ { key: FilterKey.FETCH_URL, type: FilterType.MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'with URL', operator: 'is', operatorOptions: filterOptions.stringOperators, icon: 'filters/fetch' }, { key: FilterKey.FETCH_STATUS_CODE, type: FilterType.NUMBER_MULTIPLE, category: FilterCategory.PERFORMANCE, label: 'with status code', operator: '=', operatorOptions: filterOptions.customOperators, icon: 'filters/fetch' }, From 719a29dad8f014b07c9f4ddafc8b3158ad2e54e2 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 17 Aug 2022 18:09:09 +0200 Subject: [PATCH 3/7] feat(ui) - url search - removed logs --- .../SessionSearchQueryParamHandler.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx b/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx index bc75e0195..6428de66a 100644 --- a/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx +++ b/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx @@ -76,14 +76,12 @@ function SessionSearchQueryParamHandler(props: Props) { const applyFilterFromQuery = () => { const entires = getQueryObject(history.location.search); - console.log('entires', entires); if (entires.length > 0) { entires.forEach(addFilter); } }; useEffect(() => { - console.log('rerender'); applyFilterFromQuery(); }, []); From 9d0ce368a335dab1042f954061c21b9497109069 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 17 Aug 2022 18:11:41 +0200 Subject: [PATCH 4/7] feat(ui) - url search - read query params --- .../SessionSearchQueryParamHandler.tsx | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx b/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx index 6428de66a..d8d67a14d 100644 --- a/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx +++ b/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx @@ -21,11 +21,6 @@ const allowedQueryKeys = [ 'revid', 'country', - - // 'startDate', - // 'endDate', - // 'minDuration', - // 'maxDuration', 'ref', 'sort', 'order', @@ -47,7 +42,7 @@ interface Props { appliedFilter: any; addFilterByKeyAndValue: typeof addFilterByKeyAndValue; } -function SessionSearchQueryParamHandler(props: Props) { +const SessionSearchQueryParamHandler = React.memo((props: Props) => { const { appliedFilter } = props; const history = useHistory(); @@ -81,16 +76,15 @@ function SessionSearchQueryParamHandler(props: Props) { } }; - useEffect(() => { - applyFilterFromQuery(); - }, []); - - useEffect(() => { + const generateUrlQuery = () => { const query: any = createUrlQuery(appliedFilter.filters); history.replace({ search: new URLSearchParams(query).toString() }); - }, [appliedFilter]); + }; + + useEffect(applyFilterFromQuery, []); + useEffect(generateUrlQuery, [appliedFilter]); return <>; -} +}); export default connect( (state: any) => ({ From 2709c96e90bf2910caaa447660ab331197c811f9 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 17 Aug 2022 18:39:10 +0200 Subject: [PATCH 5/7] feat(ui) - url search - handle source --- .../SessionSearchQueryParamHandler.tsx | 17 +++++++++++++---- frontend/app/duck/search.js | 6 +++++- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx b/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx index d8d67a14d..bfca8c763 100644 --- a/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx +++ b/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx @@ -51,7 +51,11 @@ const SessionSearchQueryParamHandler = React.memo((props: Props) => { filters.forEach((filter: any) => { if (filter.value.length > 0) { const _key = setQueryParamKeyFromFilterkey(filter.key); - query[_key] = `${filter.operator}|${filter.value.join('|')}`; + let str = `${filter.operator}|${filter.value.join('|')}`; + if (filter.hasSource) { + str = `${str}^${filter.sourceOperator}|${filter.source.join('|')}`; + } + query[_key] = str; } }); return query; @@ -60,11 +64,15 @@ const SessionSearchQueryParamHandler = React.memo((props: Props) => { const addFilter = ([key, value]: [string, string]): void => { if (value !== '') { const filterKey = getFilterKeyTypeByKey(key); - const valueArr = value.split('|'); + const tmp = value.split('^'); + const valueArr = tmp[0].split('|'); const operator = valueArr.shift(); + const sourceArr = tmp[1] ? tmp[1].split('|') : []; + const sourceOperator = sourceArr.shift(); + const source = sourceArr; // TODO validate operator if (filterKey) { - props.addFilterByKeyAndValue(filterKey, valueArr, operator); + props.addFilterByKeyAndValue(filterKey, valueArr, operator, sourceOperator, source); } } }; @@ -78,7 +86,8 @@ const SessionSearchQueryParamHandler = React.memo((props: Props) => { const generateUrlQuery = () => { const query: any = createUrlQuery(appliedFilter.filters); - history.replace({ search: new URLSearchParams(query).toString() }); + const queryString = new URLSearchParams(query).toString(); + history.replace({ search: queryString }); }; useEffect(applyFilterFromQuery, []); diff --git a/frontend/app/duck/search.js b/frontend/app/duck/search.js index 3abeb408c..ff1795b74 100644 --- a/frontend/app/duck/search.js +++ b/frontend/app/duck/search.js @@ -315,13 +315,17 @@ export const addFilter = (filter) => (dispatch, getState) => { }; export const addFilterByKeyAndValue = - (key, value, operator = undefined) => + (key, value, operator = undefined, sourceOperator = undefined, source = undefined) => (dispatch, getState) => { let defaultFilter = filtersMap[key]; defaultFilter.value = value; if (operator) { defaultFilter.operator = operator; } + if (defaultFilter.hasSource && source && sourceOperator) { + defaultFilter.sourceOperator = sourceOperator; + defaultFilter.source = source; + } dispatch(addFilter(defaultFilter)); }; From 0ecdd7e9d0e5c9447965af5fb35950058c4abb47 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 17 Aug 2022 18:43:57 +0200 Subject: [PATCH 6/7] feat(ui) - url search - handle source --- .../SessionSearchQueryParamHandler.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx b/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx index bfca8c763..0d6b735a1 100644 --- a/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx +++ b/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx @@ -67,12 +67,12 @@ const SessionSearchQueryParamHandler = React.memo((props: Props) => { const tmp = value.split('^'); const valueArr = tmp[0].split('|'); const operator = valueArr.shift(); + const sourceArr = tmp[1] ? tmp[1].split('|') : []; const sourceOperator = sourceArr.shift(); - const source = sourceArr; // TODO validate operator if (filterKey) { - props.addFilterByKeyAndValue(filterKey, valueArr, operator, sourceOperator, source); + props.addFilterByKeyAndValue(filterKey, valueArr, operator, sourceOperator, sourceArr); } } }; @@ -86,6 +86,7 @@ const SessionSearchQueryParamHandler = React.memo((props: Props) => { const generateUrlQuery = () => { const query: any = createUrlQuery(appliedFilter.filters); + // const queryString = Object.entries(query).map(([key, value]) => `${key}=${value}`).join('&'); const queryString = new URLSearchParams(query).toString(); history.replace({ search: queryString }); }; From 45da0aee4f9c088fbf2967aa07e16b5562ea7576 Mon Sep 17 00:00:00 2001 From: Shekar Siri Date: Wed, 17 Aug 2022 19:49:54 +0200 Subject: [PATCH 7/7] feat(ui) - url search - handle source --- .../SessionSearchQueryParamHandler.tsx | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx b/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx index 0d6b735a1..4cc1e3be6 100644 --- a/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx +++ b/frontend/app/components/shared/SessionSearchQueryParamHandler/SessionSearchQueryParamHandler.tsx @@ -1,8 +1,10 @@ import React, { useEffect } from 'react'; import { useHistory } from 'react-router'; import { connect } from 'react-redux'; -import { addFilterByKeyAndValue } from 'Duck/search'; +import { addFilterByKeyAndValue, addFilter } from 'Duck/search'; import { getFilterKeyTypeByKey, setQueryParamKeyFromFilterkey } from 'Types/filter/filterType'; +import { FilterCategory, FilterKey } from 'Types/filter/filterType'; +import { filtersMap } from 'Types/filter/newFilter'; const allowedQueryKeys = [ 'userId', @@ -41,6 +43,7 @@ const allowedQueryKeys = [ interface Props { appliedFilter: any; addFilterByKeyAndValue: typeof addFilterByKeyAndValue; + addFilter: typeof addFilter; } const SessionSearchQueryParamHandler = React.memo((props: Props) => { const { appliedFilter } = props; @@ -51,17 +54,22 @@ const SessionSearchQueryParamHandler = React.memo((props: Props) => { filters.forEach((filter: any) => { if (filter.value.length > 0) { const _key = setQueryParamKeyFromFilterkey(filter.key); - let str = `${filter.operator}|${filter.value.join('|')}`; - if (filter.hasSource) { - str = `${str}^${filter.sourceOperator}|${filter.source.join('|')}`; + if (_key) { + let str = `${filter.operator}|${filter.value.join('|')}`; + if (filter.hasSource) { + str = `${str}^${filter.sourceOperator}|${filter.source.join('|')}`; + } + query[_key] = str; + } else { + let str = `${filter.operator}|${filter.value.join('|')}`; + query[filter.key] = str; } - query[_key] = str; } }); return query; }; - const addFilter = ([key, value]: [string, string]): void => { + const addFilter = ([key, value]: [any, any]): void => { if (value !== '') { const filterKey = getFilterKeyTypeByKey(key); const tmp = value.split('^'); @@ -73,6 +81,8 @@ const SessionSearchQueryParamHandler = React.memo((props: Props) => { // TODO validate operator if (filterKey) { props.addFilterByKeyAndValue(filterKey, valueArr, operator, sourceOperator, sourceArr); + } else { + console.warn(`Filter key ${key} not found`); } } }; @@ -100,14 +110,12 @@ export default connect( (state: any) => ({ appliedFilter: state.getIn(['search', 'instance']), }), - { addFilterByKeyAndValue } + { addFilterByKeyAndValue, addFilter } )(SessionSearchQueryParamHandler); function getQueryObject(search: any) { const queryParams = Object.fromEntries( - Object.entries(Object.fromEntries(new URLSearchParams(search))).filter(([key]) => - allowedQueryKeys.includes(key) - ) + Object.entries(Object.fromEntries(new URLSearchParams(search))) ); return Object.entries(queryParams); }