Skip to content

Commit

Permalink
Merge pull request #700 from maxwroc/FixFilter
Browse files Browse the repository at this point in the history
Fixed filtering by the exact value type
  • Loading branch information
maxwroc authored Feb 16, 2024
2 parents 86638f5 + 27791b5 commit 35344fc
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 13 deletions.
13 changes: 8 additions & 5 deletions src/filter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,17 @@ import { getRegexFromString, getValueFromObject, isNumber, log, toNumber } from
/**
* Functions to check if filter condition is met
*/
const operatorHandlers: { [key in FilterOperator]: (val: string | number | undefined, expectedVal: string | number | undefined) => boolean } = {
const operatorHandlers: { [key in FilterOperator]: (val: FilterValueType, expectedVal: FilterValueType) => boolean } = {
"exists": val => val !== undefined,
"not_exists": val => val === undefined,
"contains": (val, searchString) => val !== undefined && val.toString().indexOf(searchString!.toString()) != -1,
"contains": (val, searchString) => val !== undefined && val !== null && val.toString().indexOf(searchString!.toString()) != -1,
"=": (val, expectedVal) => isNumber(val) || isNumber(expectedVal) ? toNumber(val) == toNumber(expectedVal) : val == expectedVal,
">": (val, expectedVal) => toNumber(val) > toNumber(expectedVal),
"<": (val, expectedVal) => toNumber(val) < toNumber(expectedVal),
">=": (val, expectedVal) => toNumber(val) >= toNumber(expectedVal),
"<=": (val, expectedVal) => toNumber(val) <= toNumber(expectedVal),
"matches": (val, pattern) => {
if (val === undefined) {
if (val === undefined || val === null) {
return false;
}

Expand Down Expand Up @@ -62,7 +62,7 @@ export class Filter {
* @param entityData Hass entity data
* @param state State override - battery state/level
*/
private getValue(entityData: any, state?: string): string | undefined {
private getValue(entityData: any, state?: string): FilterValueType {
if (!this.config.name) {
log("Missing filter 'name' property");
return;
Expand All @@ -79,13 +79,16 @@ export class Filter {
* Checks whether value meets the filter conditions.
* @param val Value to validate
*/
private meetsExpectations(val: string | number | undefined): boolean {
private meetsExpectations(val: FilterValueType): boolean {

let operator = this.config.operator;
if (!operator) {
if (this.config.value === undefined) {
operator = "exists";
}
else if (this.config.value === null) {
operator = "=";
}
else {
const expectedVal = this.config.value.toString();
const regex = getRegexFromString(expectedVal);
Expand Down
7 changes: 6 additions & 1 deletion src/typings.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ type FilterGroups = "exclude" | "include";
*/
type FilterOperator = "exists" | "not_exists" | "=" | ">" | "<" | ">=" | "<=" | "contains" | "matches";

/**
* Allowed filter value types
*/
type FilterValueType = string | number | boolean | null | undefined;

/**
* Filter object
*/
Expand All @@ -170,7 +175,7 @@ interface IFilter {
/**
* Value to compare with the extracted one
*/
value?: string | number;
value?: FilterValueType;
}

interface IBatteryEntityConfig {
Expand Down
14 changes: 7 additions & 7 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ export const log = (message: string, level: "warn" | "error" = "warn") => {
* Checks whether given value is a number
* @param val String value to check
*/
export const isNumber = (value: string | number | undefined): boolean => {
if (value === undefined) {
export const isNumber = (value: string | number | boolean | null | undefined): boolean => {
if (value === undefined || value === null || typeof value === "boolean") {
return false;
}

Expand All @@ -27,15 +27,15 @@ export const isNumber = (value: string | number | undefined): boolean => {
value = value.replace(",", ".");
}

return (value!== undefined && value !== null && value !== '' && !isNaN(Number(value)))
return value !== '' && !isNaN(Number(value));
}

/**
* Converts string representation of the number to the actual JS number
* @param value String value to convert
* @returns Result number
*/
export const toNumber = (value: string | number | undefined): number => {
export const toNumber = (value: string | number | boolean | null | undefined): number => {
if (typeof(value) == "string") {
// trying to solve decimal number formatting in some langs
value = value.replace(",", ".");
Expand Down Expand Up @@ -153,7 +153,7 @@ export const getRegexFromString = (ruleVal: string): RegExp | null => {
* @param path Path to the value
* @returns Value from the path
*/
export const getValueFromObject = (dataObject: any, path: string): string | undefined => {
export const getValueFromObject = (dataObject: any, path: string): string | number | boolean | null | undefined => {
const chunks = path.split(".");

for (let i = 0; i < chunks.length; i++) {
Expand All @@ -163,9 +163,9 @@ export const getValueFromObject = (dataObject: any, path: string): string | unde
}
}

if (typeof dataObject == "object") {
if (dataObject !== null && typeof dataObject == "object") {
dataObject = JSON.stringify(dataObject);
}

return dataObject === undefined ? undefined : dataObject.toString();
return dataObject;
}
21 changes: 21 additions & 0 deletions test/other/filter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,27 @@ describe("Filter", () => {

expect(isValid).toBe(expectedIsVlid);
})
test.each([
[44, <FilterOperator>"<", "44,1", true],
[44, <FilterOperator>">", "44.1", false],
[true, <FilterOperator>"=", "false", false],
[true, <FilterOperator>"=", "true", false],
[true, <FilterOperator>"=", true, true],
[true, undefined, true, true],
[false, undefined, true, false],
[true, undefined, false, false],
[true, undefined, null, false],
[null, undefined, null, true],
])("non mixed types of values", (attributeValue: FilterValueType, operator: FilterOperator | undefined, value: FilterValueType, expectedIsVlid: boolean) => {
const hassMock = new HomeAssistantMock();

const entity = hassMock.addEntity("Entity name", "ok", { entity_attrib: attributeValue });

const filter = new Filter({ name: "attributes.entity_attrib", operator, value });
const isValid = filter.isValid(entity);

expect(isValid).toBe(expectedIsVlid);
})

test.each([
[{ state: "45", device: { name: "Device name" } }, "path.missing", "Device name", false],
Expand Down

0 comments on commit 35344fc

Please sign in to comment.