diff --git a/CHANGELOG.md b/CHANGELOG.md index 20b730e..7426ac1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +## Version 1.6.0 + +- watchObjects is now case insensitive when comparing against the matched objects ([issue 134](https://github.com/danecreekphotography/node-deepstackai-trigger/issues/134)) - Address a warning during config file validation ([issue 123](https://github.com/danecreekphotography/node-deepstackai-trigger/issues/123)). - Document the available Docker image tags ([issue 128](https://github.com/danecreekphotography/node-deepstackai-trigger/issues/128)). - Addresses code cleanup [issue 136](https://github.com/danecreekphotography/node-deepstackai-trigger/issues/136). diff --git a/package-lock.json b/package-lock.json index 70daaac..457a87f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "node-deepstackai-trigger", - "version": "1.5.0", + "version": "1.6.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -1167,6 +1167,15 @@ "@sinonjs/commons": "^1.7.0" } }, + "@types/ajv-keywords": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/@types/ajv-keywords/-/ajv-keywords-3.4.0.tgz", + "integrity": "sha512-b+hs8g76ADmdv9KuAkra5Wvtq1G1UfZfCZEZMQqSMiXIkFoRTsjh6p4WtyGPtBhjXNVY/a4AfDeJVoHIRgrHng==", + "dev": true, + "requires": { + "ajv": "^6.9.1" + } + }, "@types/babel__core": { "version": "7.1.7", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.7.tgz", @@ -1737,8 +1746,7 @@ "ajv-keywords": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.4.1.tgz", - "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==", - "dev": true + "integrity": "sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==" }, "ansi-escapes": { "version": "4.3.1", diff --git a/package.json b/package.json index f9d1046..9d026db 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "node-deepstackai-trigger", - "version": "1.5.0", + "version": "1.6.0", "description": "Detects motion using DeepStack AI and calls registered triggers based on trigger rules.", "main": "dist/src/main.js", "files": [ @@ -32,6 +32,7 @@ "homepage": "https://github.com/danecreekphotography/node-deepstackai-trigger#readme", "dependencies": { "ajv": "^6.12.2", + "ajv-keywords": "^3.4.1", "async-mqtt": "^2.6.0", "bufferutil": "^4.0.1", "chalk": "^3.0.0", @@ -47,6 +48,7 @@ "utf-8-validate": "^5.0.2" }, "devDependencies": { + "@types/ajv-keywords": "^3.4.0", "@types/jest": "^25.2.3", "@types/node": "^13.1.2", "@types/node-telegram-bot-api": "^0.40.3", diff --git a/src/Trigger.ts b/src/Trigger.ts index 3a69da7..260d86a 100644 --- a/src/Trigger.ts +++ b/src/Trigger.ts @@ -210,7 +210,10 @@ export default class Trigger { * @returns True if the trigger is activated by the label */ public isRegisteredForObject(fileName: string, label: string): boolean { - const isRegistered = this.watchObjects?.includes(label); + const isRegistered = this.watchObjects?.some(watchLabel => { + return watchLabel.toLowerCase() === label?.toLowerCase(); + }); + if (!isRegistered) { log.info( `Trigger ${this.name}`, diff --git a/src/schemaValidator.ts b/src/schemaValidator.ts index 9299c1f..d7dbd4e 100644 --- a/src/schemaValidator.ts +++ b/src/schemaValidator.ts @@ -10,6 +10,7 @@ import telegramHandlerConfiguration from "./schemas/telegramHandlerConfiguration import triggerSchema from "./schemas/triggerConfiguration.schema.json"; import webRequestHandlerConfig from "./schemas/webRequestHandlerConfig.schema.json"; import maskConfiguration from "./schemas/maskConfiguration.schema.json"; +import ajvkeywords from "ajv-keywords"; /** * Validates an object against a schema file @@ -23,6 +24,8 @@ export default async function validateJsonAgainstSchema( ): Promise { const validator = new Ajv(); + ajvkeywords(validator, "transform"); + // Register all the schemas that get used with this app. It doesn't matter // if they are for different schema files/uses, ajv only loads them when // actually required by the file being processed. diff --git a/src/schemas/triggerConfiguration.schema.json b/src/schemas/triggerConfiguration.schema.json index 038fa4e..4ca23d5 100644 --- a/src/schemas/triggerConfiguration.schema.json +++ b/src/schemas/triggerConfiguration.schema.json @@ -99,6 +99,7 @@ "type": "array", "items": { "type": "string", + "transform": ["toLowerCase"], "enum": [ "person", "bicycle", diff --git a/tests/Trigger.test.ts b/tests/Trigger.test.ts index 8fd0423..0c05593 100644 --- a/tests/Trigger.test.ts +++ b/tests/Trigger.test.ts @@ -2,18 +2,45 @@ * Copyright (c) Neil Enns. All rights reserved. * Licensed under the MIT License. See LICENSE in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import trigger from "../src/Trigger"; +import Trigger from "../src/Trigger"; +import validateJsonAgainstSchema from "../src/schemaValidator"; +import triggerConfigJson from "../src/schemas/triggerConfiguration.schema.json"; +import triggerJson from "./triggers.json"; -test("Verify watchObjects", () => { - const testTrigger = new trigger(); - testTrigger.watchObjects = ["cat", "elephant", "bike"]; +test("Verify isRegisteredForObject()", () => { + // Empty constructor should default to enabled true + const trigger = new Trigger(); + trigger.name = "Trigger.test.ts"; - expect(testTrigger.isRegisteredForObject("test", "dog")).toBe(false); - expect(testTrigger.isRegisteredForObject("test", "cat")).toBe(true); + trigger.watchObjects = ["dog"]; + expect(trigger.isRegisteredForObject("unit test", "dog")).toBe(true); - // testTrigger.watchObjects = undefined; - // expect(testTrigger.isRegisteredForObject("test", "cat")).toBe(false); + trigger.watchObjects = []; + expect(trigger.isRegisteredForObject("unit test", "dog")).toBe(false); - testTrigger.watchObjects = []; - expect(testTrigger.isRegisteredForObject("test", "cat")).toBe(false); + trigger.watchObjects = undefined; + expect(trigger.isRegisteredForObject("unit test", "dog")).toBe(false); + + trigger.watchObjects = null; + expect(trigger.isRegisteredForObject("unit test", "dog")).toBe(false); + + trigger.watchObjects = ["DoG"]; + expect(trigger.isRegisteredForObject("unit test", "dog")).toBe(true); + + trigger.watchObjects = ["dog"]; + expect(trigger.isRegisteredForObject("unit test", "doG")).toBe(true); + + trigger.watchObjects = ["cat", "elephant"]; + expect(trigger.isRegisteredForObject("unit test", "dog")).toBe(false); + + trigger.watchObjects = ["cat", "elephant"]; + expect(trigger.isRegisteredForObject("unit test", undefined)).toBe(false); +}); + +test("Verify isRegisteredForObject()", async () => { + await expect(validateJsonAgainstSchema(triggerJson, triggerConfigJson)).resolves.toEqual(true); + + // Check that case doesn't matter for string arrays that take an enum + triggerJson.triggers[0].watchObjects = ["dOg"]; + await expect(validateJsonAgainstSchema(triggerJson, triggerConfigJson)).resolves.toEqual(true); }); diff --git a/tests/handlers/Trigger.test.ts b/tests/handlers/Trigger.test.ts deleted file mode 100644 index c477634..0000000 --- a/tests/handlers/Trigger.test.ts +++ /dev/null @@ -1,32 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Neil Enns. All rights reserved. - * Licensed under the MIT License. See LICENSE in the project root for license information. - *--------------------------------------------------------------------------------------------*/ -import Trigger from "../../src/Trigger"; - -test("Verify isRegisteredForObject()", () => { - // Empty constructor should default to enabled true - const trigger = new Trigger(); - trigger.name = "Trigger.test.ts"; - - trigger.watchObjects = ["dog"]; - expect(trigger.isRegisteredForObject("unit test", "dog")).toBe(true); - - trigger.watchObjects = []; - expect(trigger.isRegisteredForObject("unit test", "dog")).toBe(false); - - trigger.watchObjects = undefined; - expect(trigger.isRegisteredForObject("unit test", "dog")).toBe(false); - - trigger.watchObjects = null; - expect(trigger.isRegisteredForObject("unit test", "dog")).toBe(false); - - trigger.watchObjects = ["DoG"]; - expect(trigger.isRegisteredForObject("unit test", "dog")).toBe(false); - - trigger.watchObjects = ["dog"]; - expect(trigger.isRegisteredForObject("unit test", "doG")).toBe(false); - - trigger.watchObjects = ["cat", "elephant"]; - expect(trigger.isRegisteredForObject("unit test", "dog")).toBe(false); -}); diff --git a/tests/triggers.json b/tests/triggers.json new file mode 100644 index 0000000..1c8866e --- /dev/null +++ b/tests/triggers.json @@ -0,0 +1,27 @@ +{ + "$schema": "https://raw.githubusercontent.com/danecreekphotography/node-deepstackai-trigger/master/src/schemas/triggerConfiguration.schema.json", + "triggers": [ + { + "name": "Dog detector", + "watchPattern": "/aiinput/Dog*.jpg", + "enabled": true, + "threshold": { + "minimum": 50, + "maximum": 100 + }, + "handlers": { + "webRequest": { + "triggerUris": ["http://localhost:81/admin?trigger&camera=Dog"] + }, + "mqtt": { + "topic": "aimotion/triggers/dog" + }, + "telegram": { + "chatIds": [1], + "cooldownTime": 60 + } + }, + "watchObjects": ["dog"] + } + ] +}