Repository: node-influx/node-influx
Pull request #513 (2020-07-22 20:11:28)

feat: Placeholder support
## Proposed Changes

- #500 but rebased and commit message in semantic format

## Checklist

- [x] A test has been added if appropriate
- [x] `npm test` completes successfully
- [x] Commit messages are in [semantic format](https://seesparkbox.com/foundry/semantic_commit_messages)



Patch:
diff --git a/package.json b/package.json
index 6da8717..62ffac1 100644
--- a/package.json
+++ b/package.json
@@ -36,7 +36,8 @@
   "contributors": [
     "Ben Evans <ben@bensbit.co.uk> (http://bensbit.co.uk)",
     "Connor Peet <connor@peet.io>",
-    "Steffen Konerow <steffen@nrg-media.de> (http://www.nrg-media.de)"
+    "Steffen Konerow <steffen@nrg-media.de> (http://www.nrg-media.de)",
+    "Timm Murray <tmurray@wumpus-cave.net> (http://wumpus-cave.net)"
   ],
   "license": "MIT",
   "devDependencies": {
diff --git a/src/index.ts b/src/index.ts
index 73bf061..7342517 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -184,6 +184,12 @@ export interface IQueryOptions {
    * database is not provided in Influx.
    */
   database?: string;
+
+  /**
+   * Any placeholders used by the query. Using these is strongly recommended
+   * to avoid injection attacks.
+   */
+  placeholders?: Record<string, string>;
 }
 
 /**
@@ -296,10 +302,14 @@ function defaults<T>(target: T, ...srcs: T[]): T {
  * ]).then(() => {
  *   return influx.query(`
  *     select * from response_times
- *     where host = ${Influx.escape.stringLit(os.hostname())}
+ *     where host = $<host>
  *     order by time desc
  *     limit 10
- *   `)
+ *   `, {
+ *      placeholders: {
+ *        host: os.hostname()
+ *      }
+ *   })
  * }).then(rows => {
  *   rows.forEach(row => console.log(`A request to ${row.path} took ${row.duration}ms`))
  * })
@@ -1402,7 +1412,11 @@ export class InfluxDB {
     query: string | string[],
     options: IQueryOptions = {}
   ): Promise<any> {
-    const { database = this._defaultDB(), retentionPolicy } = options;
+    const {
+      database = this._defaultDB(),
+      retentionPolicy,
+      placeholders = {},
+    } = options;
 
     if (query instanceof Array) {
       query = query.join(";");
@@ -1414,6 +1428,7 @@ export class InfluxDB {
         epoch: options.precision,
         q: query,
         rp: retentionPolicy,
+        params: placeholders,
       })
     );
   }


Test patch:
diff --git a/test/unit/influx.test.ts b/test/unit/influx.test.ts
index 1727957..02a0f2b 100644
--- a/test/unit/influx.test.ts
+++ b/test/unit/influx.test.ts
@@ -1,6 +1,3 @@
-/* eslint-env node, mocha */
-/* eslint-disable no-unused-expressions */
-
 import { expect } from "chai";
 import * as sinon from "sinon";
 
@@ -795,6 +792,7 @@ describe("influxdb", () => {
             epoch: undefined,
             rp: undefined,
             db: "my_db",
+            params: {},
           },
           "GET",
           dbFixture("selectFromOne")
@@ -813,6 +811,7 @@ describe("influxdb", () => {
             epoch: undefined,
             rp: undefined,
             db: "my_db",
+            params: {},
           },
           "GET",
           dbFixture("selectFromOne")
@@ -842,6 +841,7 @@ describe("influxdb", () => {
             epoch: undefined,
             rp: undefined,
             db: "my_db",
+            params: {},
           },
           "GET",
           dbFixture("selectFromOne")
@@ -861,6 +861,7 @@ describe("influxdb", () => {
             epoch: "ms",
             rp: "asdf",
             db: "my_db",
+            params: {},
           },
           "GET",
           dbFixture("selectFromOne")
@@ -880,6 +881,7 @@ describe("influxdb", () => {
             epoch: undefined,
             rp: "asdf",
             db: "my_db",
+            params: {},
           },
           "GET",
           dbFixture("selectFromOne")
@@ -890,6 +892,32 @@ describe("influxdb", () => {
           retentionPolicy: "asdf",
         });
       });
+
+      it("uses placeholders", () => {
+        expectQuery(
+          "json",
+          {
+            q: "select * from series_0 WHERE time > now() - $<since>",
+            epoch: undefined,
+            rp: "asdf",
+            db: "my_db",
+            params: { since: "10s" },
+          },
+          "GET",
+          dbFixture("selectFromOne")
+        );
+
+        return influx.query(
+          ["select * from series_0 WHERE time > now() - $<since>"],
+          {
+            precision: "n",
+            retentionPolicy: "asdf",
+            placeholders: {
+              since: "10s",
+            },
+          }
+        );
+      });
     });
 
     describe(".createRetentionPolicy", () => {