Support Unicode characters in tags

This commit is contained in:
Zaid Abdulla committed 2020-10-20 15:44:11 -07:00
1 parent dfab724515
commit c52ff6f6ec
7 files changed
+48 -40

No files matched your search

+14
View File
@@ -47,3 +47,17 @@ export function generateDeviceId(
export function escapeRegExp(str: string): string {
return str.replace(/[-[\]/{}()*+?.\\^$|]/g, "\\$&");
}
export function encodeTag(tag: string): string {
return encodeURIComponent(tag)
.replace(
/[!~*'()]/g,
(c) => "%" + c.charCodeAt(0).toString(16).toUpperCase()
)
.replace(/0x(?=[0-9A-Z]{2})/g, "0%78")
.replace(/%/g, "0x");
}
export function decodeTag(tag: string): string {
return decodeURIComponent(tag.replace(/0x(?=[0-9A-Z]{2})/g, "%"));
}
+3 -4
View File
@@ -19,7 +19,7 @@
import { MongoClient, ObjectID, Collection } from "mongodb";
import { get } from "./config";
import { escapeRegExp } from "./common";
import { encodeTag, escapeRegExp } from "./common";
import { parse } from "./common/expression-parser";
import {
DeviceData,
@@ -234,10 +234,9 @@ export async function fetchDevice(
]);
}
for (let t of v as string[]) {
t = t.replace(/[^a-zA-Z0-9-]+/g, "_");
for (const t of v as string[]) {
res.push([
Path.parse("Tags." + t),
Path.parse("Tags." + encodeTag(t)),
timestamp,
{
object: [timestamp, 0],
+9 -5
View File
@@ -21,6 +21,7 @@ import { ObjectID } from "mongodb";
import { map, parse, stringify, parseList } from "./common/expression-parser";
import { likePatternToRegExp } from "./common/expression";
import { Expression, Fault, Task } from "./types";
import { decodeTag, encodeTag } from "./common";
const isArray = Array.isArray;
@@ -43,7 +44,7 @@ export function processDeviceFilter(filter: Expression): Expression {
exp[1][0] === "PARAM" &&
exp[1][1].startsWith("Tags.")
) {
const t = exp[1][1].slice(5);
const t = decodeTag(exp[1][1].slice(5));
if (exp[0] === "IS NULL") return ["_tags", t, false];
else if (exp[0] === "IS NOT NULL") return ["_tags", t, true];
else if (exp[0] === "=" && exp[2] === true) return ["_tags", t, true];
@@ -406,7 +407,7 @@ export function flattenDevice(device: Record<string, unknown>): FlatDevice {
};
for (const t of tree as string[]) {
output[`Tags.${t}`] = {
output[`Tags.${encodeTag(t)}`] = {
value: [true, "xsd:boolean"],
valueTimestamp: timestamp,
writable: true,
@@ -510,11 +511,11 @@ export function convertOldPrecondition(
if (op === "$ne") {
if (typeof v["$ne"] !== "string")
throw new Error("Only string values are allowed for _tags");
conjs.push(["IS NULL", ["PARAM", `Tags.${val}`]]);
conjs.push(["IS NULL", ["PARAM", `Tags.${encodeTag(val)}`]]);
} else if (op === "$eq") {
if (typeof v["$eq"] !== "string")
throw new Error("Only string values are allowed for _tags");
conjs.push(["IS NOT NULL", ["PARAM", `Tags.${val}`]]);
conjs.push(["IS NOT NULL", ["PARAM", `Tags.${encodeTag(val)}`]]);
} else {
throw new Error(`Invalid tag query`);
}
@@ -522,7 +523,10 @@ export function convertOldPrecondition(
if (conjs.length === 1) expressions.push(conjs[0]);
else if (conjs.length > 1) expressions.push(["AND", ...conjs]);
} else {
expressions.push(["IS NOT NULL", ["PARAM", `Tags.${v}`]]);
expressions.push([
"IS NOT NULL",
["PARAM", `Tags.${encodeTag(v as string)}`],
]);
}
} else if (k.startsWith("Tags.")) {
let exists: boolean;
+2 -1
View File
@@ -31,6 +31,7 @@ import { del } from "../cache";
import Authorizer from "../common/authorizer";
import { ping } from "../ping";
import * as url from "url";
import { decodeTag } from "../common";
const router = new Router();
export default router;
@@ -280,7 +281,7 @@ for (const [resource, flags] of Object.entries(resources)) {
if (e[1] === "Tags") {
const tags = [];
for (const p in obj)
if (p.startsWith("Tags.")) tags.push(p.slice(5));
if (p.startsWith("Tags.")) tags.push(decodeTag(p.slice(5)));
return tags.join(", ");
}
+16 -28
View File
@@ -237,39 +237,27 @@ export function count(resource: string, filter: Expression): Promise<number> {
});
}
export function updateDeviceTags(
export async function updateDeviceTags(
deviceId: string,
tags: Record<string, boolean>
): Promise<void> {
return new Promise((resolve, reject) => {
const add = [];
const pull = [];
const add = [];
const pull = [];
const regex = /^[0-9a-zA-Z_]+$/;
for (let [tag, onOff] of Object.entries(tags)) {
tag = tag.trim();
if (onOff) {
if (!tag.match(regex))
return void reject(new Error(`Invalid tag '${tag}'`));
add.push(tag);
} else {
pull.push(tag);
}
}
getClient()
.then((client) => {
const collection = client.db().collection("devices");
for (let [tag, onOff] of Object.entries(tags)) {
tag = tag.trim();
if (onOff) add.push(tag);
else pull.push(tag);
}
const object = {};
if (add && add.length) object["$addToSet"] = { _tags: { $each: add } };
if (pull && pull.length) object["$pullAll"] = { _tags: pull };
collection.updateOne({ _id: deviceId }, object, (err) => {
if (err) return void reject(err);
resolve();
});
})
.catch(reject);
});
const client = await getClient();
const collection = client.db().collection("devices");
const object = {};
if (add && add.length) object["$addToSet"] = { _tags: { $each: add } };
if (pull && pull.length) object["$pullAll"] = { _tags: pull };
await collection.updateOne({ _id: deviceId }, object);
}
function putResource(resource, id, object): Promise<void> {