-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1bd3e26
commit 0dad49e
Showing
4 changed files
with
234 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
{ | ||
"extends": [ | ||
"eslint:recommended", | ||
"plugin:node/recommended" | ||
], | ||
"env": {}, | ||
"globals": { | ||
"window": true | ||
}, | ||
"plugins": [ | ||
"node" | ||
], | ||
"rules": { | ||
"no-undef": 2, | ||
"no-unused-vars": 2, | ||
"strict": [2, "global"], | ||
"new-cap": 0, | ||
"indent": ["error", "tab", {"MemberExpression": 1, "ArrayExpression": 1, "ObjectExpression": 1 }], | ||
"no-console": 0, | ||
"no-invalid-this": 0, | ||
"node/no-missing-require": 1, | ||
"no-trailing-spaces": 2, | ||
"semi": ["error", "always"], | ||
"key-spacing": ["warn", { "beforeColon": false, "afterColon": true }], | ||
"space-infix-ops": ["warn"], | ||
"object-curly-newline": ["warn", {"minProperties": 1}], | ||
"no-shadow": ["error"], | ||
"no-undef-init": ["error"] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,71 @@ | ||
# mqtt-pattern | ||
Fast library for matching MQTT patterns with named wildcards | ||
Fast library for matching MQTT patterns with named wildcards to extract data from topics | ||
|
||
Successor to [mqtt-regex](./mqtt-regex) | ||
|
||
## Example: | ||
|
||
``` javascript | ||
var MQTTPattern = require("mqtt-pattern"); | ||
|
||
// Wildcards in patterns don't need names | ||
var pattern = "device/+id/+/#data"; | ||
|
||
var topic = "device/fitbit/heartrate/rate/bpm"; | ||
|
||
var params = MQTTPattern.exec(pattern, topic); | ||
|
||
// params will be | ||
{ | ||
id: "fitbit", | ||
data: ["rate", "bmp"] | ||
} | ||
|
||
``` | ||
|
||
## Installing | ||
|
||
With NPM: | ||
|
||
```bash | ||
npm install --save mqtt-pattern | ||
``` | ||
|
||
## API | ||
|
||
### `exec(pattern : String, topic : String) : Object | null` | ||
Validates that `topic` fits the `pattern` and parses out any parameters. | ||
If the topic doesn't match, it returns `null` | ||
|
||
### `matches(pattern : String, topic : String) : Boolean` | ||
Validates whether `topic` fits the `pattern`. Ignores parameters. | ||
|
||
### `extract(pattern : String, topic : String) : Object` | ||
Traverses the `pattern` and attempts to fetch parameters from the `topic`. | ||
Useful if you know in advance that your `topic` will be valid and want to extract data. | ||
If the `topic` doesn't match, or the `pattern` doesn't contain named wildcards, returns an empty object. | ||
Do not use this for validation. | ||
|
||
## How params work | ||
|
||
MQTT defines two types of "wildcards", one for matching a single section of the path (`+`), and one for zero or more sections of the path (`#`). | ||
Note that the `#` wildcard must only be used if it's at the end of the topic. | ||
This library was inspired by the syntax in the routers for web frameworks. | ||
|
||
### Examples of topic patterns: | ||
|
||
#### user/+id/#path | ||
This would match paths that start with `user/`, and then extract the next section as the user `id`. | ||
Then it would get the following paths and turn them into an array for the `path` param. | ||
Here is some input/output that you can expect: | ||
|
||
user/bob/status/mood: {id: "bob", path:["status","mood"] | ||
user/bob: {id:"bob", path: []} | ||
user/bob/ishungry: {id: "bob", path: ["ishungry"] | ||
|
||
#### device/+/+/component/+type/#path | ||
Not all wildcards need to be associated with a parameter, and it could be useful to use plain MQTT topics. | ||
In this example you might only care about the status of some part of a device, and are willing to ignore a part of the path. | ||
Here are some examples of what this might be used with: | ||
|
||
device/deviceversion/deviceidhere/component/infrared/status/active: {type:"infrared",path: ["status","active"]} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
{ | ||
"name": "mqtt-pattern", | ||
"version": "1.0.0", | ||
"description": "Fast library for matching MQTT patterns with named wildcards", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "node test" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/RangerMauve/mqtt-pattern.git" | ||
}, | ||
"keywords": [ | ||
"mqtt", | ||
"pattern", | ||
"match", | ||
"topic" | ||
], | ||
"author": "rangermauve", | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/RangerMauve/mqtt-pattern/issues" | ||
}, | ||
"homepage": "https://github.com/RangerMauve/mqtt-pattern#readme", | ||
"devDependencies": { | ||
"eslint": "^3.19.0", | ||
"eslint-plugin-node": "^4.2.2", | ||
"tape": "^4.6.3" | ||
}, | ||
"dependencies": { | ||
"mqtt-match": "^1.0.2" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
"use strict"; | ||
var test = require("tape"); | ||
|
||
var MQTTPattern = require("./"); | ||
|
||
test("matches() supports patterns with no wildcards", function (t) { | ||
t.plan(1); | ||
t.ok(MQTTPattern.matches("foo/bar/baz", "foo/bar/baz"), "Matched topic"); | ||
}); | ||
|
||
test("matches() doesn't match different topics", function (t) { | ||
t.plan(1); | ||
t.notOk(MQTTPattern.matches("foo/bar/baz", "baz/bar/foo"), "Didn't match topic"); | ||
}); | ||
|
||
test("matches() supports patterns with # at the beginning", function (t) { | ||
t.plan(1); | ||
t.ok(MQTTPattern.matches("#", "foo/bar/baz"), "Matched topic"); | ||
}); | ||
|
||
test("matches() supports patterns with # at the end", function (t) { | ||
t.plan(1); | ||
t.ok(MQTTPattern.matches("foo/#", "foo/bar/baz"), "Matched topic"); | ||
}); | ||
|
||
test("matches() doesn't support # wildcards with more after them", function (t) { | ||
t.plan(1); | ||
t.notOk(MQTTPattern.matches("#/bar/baz", "foo/bar/baz"), "Didn't match topic"); | ||
}); | ||
|
||
test("matches() supports patterns with + at the beginning", function (t) { | ||
t.plan(1); | ||
t.ok(MQTTPattern.matches("+/bar/baz", "foo/bar/baz"), "Matched topic"); | ||
}); | ||
|
||
test("matches() supports patterns with + at the end", function (t) { | ||
t.plan(1); | ||
t.ok(MQTTPattern.matches("foo/bar/+", "foo/bar/baz"), "Matched topic"); | ||
}); | ||
|
||
test("matches() supports patterns with + in the middle", function (t) { | ||
t.plan(1); | ||
t.ok(MQTTPattern.matches("foo/+/baz", "foo/bar/baz"), "Matched topic"); | ||
}); | ||
|
||
test("matches() supports patterns multiple wildcards", function (t) { | ||
t.plan(1); | ||
t.ok(MQTTPattern.matches("foo/+/#", "foo/bar/baz"), "Matched topic"); | ||
}); | ||
|
||
test("matches() supports named wildcards", function (t) { | ||
t.plan(1); | ||
t.ok(MQTTPattern.matches("foo/+something/#else", "foo/bar/baz"), "Matched topic"); | ||
}); | ||
|
||
test("extract() returns empty object of there's nothing to extract", function (t) { | ||
t.plan(1); | ||
t.deepEqual(MQTTPattern.extract("foo/bar/baz", "foo/bar/baz"), {}, "Extracted empty object"); | ||
}); | ||
|
||
test("extract() returns empty object if wildcards don't have label", function (t) { | ||
t.plan(1); | ||
t.deepEqual(MQTTPattern.extract("foo/+/#", "foo/bar/baz"), {}, "Extracted empty object"); | ||
}); | ||
|
||
test("extract() returns object with an array for # wildcard", function (t) { | ||
t.plan(1); | ||
t.deepEqual(MQTTPattern.extract("foo/#something", "foo/bar/baz"), { | ||
something: ["bar", "baz"] | ||
}, "Extracted param"); | ||
}); | ||
|
||
test("extract() returns object with a string for + wildcard", function (t) { | ||
t.plan(1); | ||
t.deepEqual(MQTTPattern.extract("foo/+hello/+world", "foo/bar/baz"), { | ||
hello: "bar", | ||
world: "baz" | ||
}, "Extracted params"); | ||
}); | ||
|
||
test("extract() parses params from all wildcards", function (t) { | ||
t.plan(1); | ||
t.deepEqual(MQTTPattern.extract("+hello/+world/#wow", "foo/bar/baz/fizz"), { | ||
hello: "foo", | ||
world: "bar", | ||
wow: ["baz", "fizz"] | ||
}, "Extracted params"); | ||
}); | ||
|
||
test("exec() returns null if it doesn't match", function (t) { | ||
t.plan(1); | ||
t.equal(MQTTPattern.exec("hello/world", "foo/bar/baz"), null, "Got null"); | ||
}); | ||
|
||
test("exec() returns params if they can be parsed", function(t){ | ||
t.plan(1); | ||
t.deepEqual(MQTTPattern.exec("foo/+hello/#world", "foo/bar/baz"), { | ||
hello: "bar", | ||
world: ["baz"] | ||
}, "Extracted params"); | ||
}); |