diff --git a/app/src/components/features/rules/RulePairs/Pairs/Rows/RowsMarkup/RequestSourceRow/index.js b/app/src/components/features/rules/RulePairs/Pairs/Rows/RowsMarkup/RequestSourceRow/index.js index ef77047b7e..f11c9aff4e 100644 --- a/app/src/components/features/rules/RulePairs/Pairs/Rows/RowsMarkup/RequestSourceRow/index.js +++ b/app/src/components/features/rules/RulePairs/Pairs/Rows/RowsMarkup/RequestSourceRow/index.js @@ -22,6 +22,7 @@ import { RQButton } from "lib/design-system-v2/components"; import { sampleRegex } from "./sampleRegex"; import { useLocation } from "react-router-dom"; import PATHS from "config/constants/sub/paths"; +import { getAdvancedFiltersCount } from "./utils"; import "./RequestSourceRow.css"; const { Text } = Typography; @@ -70,14 +71,11 @@ const RequestSourceRow = ({ rowIndex, pair, pairIndex, ruleDetails, isInputDisab const getFilterCount = useCallback( (pairIndex) => { - const copyOfCurrentlySelectedRule = JSON.parse(JSON.stringify(currentlySelectedRuleData)); - return isSourceFilterFormatUpgraded(pairIndex, copyOfCurrentlySelectedRule) - ? Object.keys(currentlySelectedRuleData.pairs[pairIndex].source.filters[0] || {}).filter( - (key) => key !== GLOBAL_CONSTANTS.RULE_SOURCE_FILTER_TYPES.PAGE_URL - ).length - : Object.keys(currentlySelectedRuleData.pairs[pairIndex].source.filters || {}).filter( - (key) => key !== GLOBAL_CONSTANTS.RULE_SOURCE_FILTER_TYPES.PAGE_URL - ).length; + const sourceFilters = currentlySelectedRuleData.pairs[pairIndex].source.filters; + + return isSourceFilterFormatUpgraded(pairIndex, currentlySelectedRuleData) + ? getAdvancedFiltersCount(sourceFilters[0]) + : getAdvancedFiltersCount(sourceFilters); }, [currentlySelectedRuleData, isSourceFilterFormatUpgraded] ); diff --git a/app/src/components/features/rules/RulePairs/Pairs/Rows/RowsMarkup/RequestSourceRow/utils.js b/app/src/components/features/rules/RulePairs/Pairs/Rows/RowsMarkup/RequestSourceRow/utils.js new file mode 100644 index 0000000000..505341f9be --- /dev/null +++ b/app/src/components/features/rules/RulePairs/Pairs/Rows/RowsMarkup/RequestSourceRow/utils.js @@ -0,0 +1,43 @@ +import { CONSTANTS as GLOBAL_CONSTANTS } from "@requestly/requestly-core"; + +const hasFilterValue = (value) => { + if (Array.isArray(value)) { + return value.some(hasFilterValue); + } + + if (value === null || value === undefined) { + return false; + } + + if (typeof value === "string") { + return value.trim().length > 0; + } + + if (typeof value === "object") { + return Object.values(value).some(hasFilterValue); + } + + return true; +}; + +const isCompleteRequestPayloadFilter = (filterValue) => { + if (!filterValue || typeof filterValue !== "object" || Array.isArray(filterValue)) { + return false; + } + + return hasFilterValue(filterValue.key) && hasFilterValue(filterValue.value); +}; + +export const getAdvancedFiltersCount = (filters = {}) => { + return Object.entries(filters).filter(([filterType, filterValue]) => { + if (filterType === GLOBAL_CONSTANTS.RULE_SOURCE_FILTER_TYPES.PAGE_URL) { + return false; + } + + if (filterType === GLOBAL_CONSTANTS.RULE_SOURCE_FILTER_TYPES.REQUEST_DATA) { + return isCompleteRequestPayloadFilter(filterValue); + } + + return hasFilterValue(filterValue); + }).length; +}; diff --git a/app/src/components/features/rules/RulePairs/Pairs/Rows/RowsMarkup/RequestSourceRow/utils.test.js b/app/src/components/features/rules/RulePairs/Pairs/Rows/RowsMarkup/RequestSourceRow/utils.test.js new file mode 100644 index 0000000000..31d3c4c873 --- /dev/null +++ b/app/src/components/features/rules/RulePairs/Pairs/Rows/RowsMarkup/RequestSourceRow/utils.test.js @@ -0,0 +1,64 @@ +import { describe, expect, it } from "vitest"; +import { CONSTANTS as GLOBAL_CONSTANTS } from "@requestly/requestly-core"; +import { getAdvancedFiltersCount } from "./utils"; + +const { PAGE_DOMAINS, PAGE_URL, REQUEST_DATA, REQUEST_METHOD, RESOURCE_TYPE } = + GLOBAL_CONSTANTS.RULE_SOURCE_FILTER_TYPES; + +describe("getAdvancedFiltersCount", () => { + it("does not count empty default filters", () => { + expect( + getAdvancedFiltersCount({ + [PAGE_DOMAINS]: [], + [REQUEST_METHOD]: "", + [RESOURCE_TYPE]: {}, + [REQUEST_DATA]: { key: "", operator: "Equals", value: "" }, + }) + ).toBe(0); + }); + + it("does not count arrays that only contain empty entries", () => { + expect( + getAdvancedFiltersCount({ + [PAGE_DOMAINS]: ["", {}, null], + }) + ).toBe(0); + }); + + it("requires request payload filters to have both key and value", () => { + expect( + getAdvancedFiltersCount({ + [REQUEST_DATA]: { key: "id", operator: "Equals", value: "" }, + }) + ).toBe(0); + expect( + getAdvancedFiltersCount({ + [REQUEST_DATA]: { key: "", operator: "Equals", value: "123" }, + }) + ).toBe(0); + expect( + getAdvancedFiltersCount({ + [REQUEST_DATA]: { key: "id", operator: "Equals", value: "123" }, + }) + ).toBe(1); + }); + + it("excludes page URL from the advanced filter count", () => { + expect( + getAdvancedFiltersCount({ + [PAGE_URL]: "https://example.com", + [REQUEST_METHOD]: "GET", + }) + ).toBe(1); + }); + + it("counts meaningful filter values", () => { + expect( + getAdvancedFiltersCount({ + [PAGE_DOMAINS]: ["example.com"], + [REQUEST_METHOD]: "GET", + [RESOURCE_TYPE]: { script: true }, + }) + ).toBe(3); + }); +});