All files / src/services/filtering index.ts

93.93% Statements 31/33
100% Branches 6/6
85.71% Functions 12/14
96.87% Lines 31/32

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111                                  7616x 32640x 32640x 3264x   29376x 12512x   16864x   21216x     29376x           7616x     544x   7616x                 221x 221x 221x 221x   221x                   544x 720x 1108x 164x       720x                       720x 720x 4182x 1108x     720x               544x     240x   240x 720x            
import { QueryType } from "@redux/slices/fundraisers/types";
import {
  AND_OPERATOR,
  filteringConst,
  OR_OPERATOR,
  resources,
} from "./filtering.constants";
import { replaceSemicolonWithURLEncoded } from "./filtering.string";
import { InternalObject, KeyAttribute, ResourcesType } from "./filtering.types";
import { MerchantsQueryType } from "@redux/types/enterprise/merchants";
import { CustomersQueryType } from "@redux/types/customers";
 
function appendKey(
  obj: InternalObject,
  keyValueMap: KeyAttribute,
  operator: typeof AND_OPERATOR | typeof OR_OPERATOR,
) {
  const attrs = Object.entries(obj).reduce((acc, [key, value]) => {
    let nn = "";
    if (["typeID", "statusID"].includes(keyValueMap)) {
      return acc;
    }
    if (["key", "operator"].includes(key)) {
      nn = value;
    } else {
      nn = value
        .split(operator)
        .map((x: string) => `${keyValueMap}:${x}`)
        .join(operator);
    }
    return {
      ...acc,
      [key]: nn,
    };
  }, {});
 
  return attrs;
}
 
export const withAppendedKey = Object.entries(filteringConst).reduce(
  (acc, [key, value]) => {
    return {
      ...acc,
      [key]: appendKey(value, value.key, (value as any).operator),
    };
  },
  {} as typeof filteringConst,
);
 
export function replaceValues(originalString: string, ...values: any[]) {
  let replacedString = originalString;
  values.forEach((value, index) => {
    const placeholder = `{$${index + 1}}`;
    replacedString = replacedString.replace(placeholder, value);
  });
  return replacedString;
}
 
export function replaceArrayTypeValues(
  keyValueMap: KeyAttribute,
  ...values: any[]
) {
  return values.map((x) => `${keyValueMap}:${x}`).join(OR_OPERATOR);
}
 
const encodedQueryFilter = (queryFilters: any) => {
  const str = Object.entries(queryFilters)
    .filter((x) => x[1])
    .map(([, v]) => `${v}`)
    .join("%3B")
    .trim();
 
  return replaceSemicolonWithURLEncoded(`${str.trim()}`);
};
 
type QueryInputFiltersType =
  | QueryType
  | MerchantsQueryType
  | CustomersQueryType;
 
function filterByResource(
  inputObj: QueryInputFiltersType,
  resourceKey: keyof typeof resources,
) {
  const outputObj = {} as any;
  for (const key in inputObj) {
    if (resources[resourceKey].has(key)) {
      outputObj[key] = inputObj[key as keyof QueryInputFiltersType];
    }
  }
  return outputObj;
}
 
/*
    As our application currently stores all filter types within a single Redux object, 
    it's crucial to separate these filters based on their respective resources. 
    Not all filters should be applied to a single resource, so we've implemented a solution to split the filters according to the resources defined in filtering.constants.
*/
export const encodedQueryFilterMap = (
  queryFilters: QueryInputFiltersType,
): ResourcesType => {
  const resourcesKeys: any[] = Object.keys(resources);
 
  return resourcesKeys.reduce((acc, key) => {
    return {
      ...acc,
      [key]: encodedQueryFilter(filterByResource(queryFilters, key)),
    };
  }, {});
};