@transitive-sdk/clickhouse 0.3.2 → 0.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  const _ = require('lodash');
2
+ const waitPort = require('wait-port');
2
3
  const { createClient } = require('@clickhouse/client');
4
+
3
5
  const { topicToPath, topicMatch } = require('@transitive-sdk/datacache');
4
6
 
5
7
  // Default TTL in days for mqtt_history table
@@ -43,11 +45,21 @@ class ClickHouse {
43
45
  topics = {}; // list of topics registered for storage, as object for de-duplication
44
46
 
45
47
  /** Create the client, connecting to Clickhouse */
46
- init({ url, dbName, user, password } = {}) {
48
+ async init({ url, dbName, user, password } = {}) {
49
+
47
50
  const _url = url || process.env.CLICKHOUSE_URL || 'http://clickhouse:8123';
48
51
  const _dbName = dbName || process.env.CLICKHOUSE_DB || 'default';
49
52
  const _user = user || process.env.CLICKHOUSE_USER || 'default';
50
53
 
54
+ const {hostname, port} = URL.parse(_url);
55
+ const interval = 200;
56
+ await waitPort({
57
+ host: hostname,
58
+ port: port || 80,
59
+ interval
60
+ }, 10000);
61
+ await new Promise(done => setTimeout(done, 200));
62
+
51
63
  // console.debug(`Creating ClickHouse client for URL: ${_url}, DB: ${_dbName}, User: ${_user}`);
52
64
 
53
65
  this._client = createClient({
@@ -72,6 +84,8 @@ class ClickHouse {
72
84
  // asterisk_include_materialized_columns: 1
73
85
  },
74
86
  });
87
+
88
+ await this._client.query({ query: 'SELECT 1' });
75
89
  }
76
90
 
77
91
  /** Get the Clickhouse client (from @clickhouse/client) */
@@ -83,6 +97,15 @@ class ClickHouse {
83
97
  return this._client;
84
98
  }
85
99
 
100
+ /* sets up default row level security policies in ClickHouse */
101
+ async ensureDefaultPermissions() {
102
+ const cmd = 'CREATE ROW POLICY IF NOT EXISTS';
103
+ for (let query of [
104
+ `${cmd} default_users ON default.* USING OrgId = splitByString('_', currentUser())[2] TO ALL`,
105
+ `${cmd} default_admin ON default.* USING 1 TO ${process.env.CLICKHOUSE_USER || 'default'}`
106
+ ]) await this.client.command({ query });
107
+ }
108
+
86
109
  /** Create a table if it does not already exist adding OrgId and DeviceId
87
110
  * columns to the schema for multi-tenancy support.
88
111
  * @param {string} tableName - name of the table to create
@@ -260,35 +283,26 @@ class ClickHouse {
260
283
  * @param {string} topic - MQTT topic to register
261
284
  */
262
285
  async registerMqttTopicForStorage(selector, ttlDays = DEFAULT_TTL_DAYS) {
263
- this.topics[selector] = true;
264
-
265
- // ---------------------------------------------------------------
266
- // Set/update TTL for this capability and sub-topic
267
286
 
268
287
  const path = topicToPath(selector);
269
288
 
270
289
  if (path.length < 4) {
271
290
  // underspecified, don't set TTL
291
+ console.warn('Not registering topic as it is too short', selector);
272
292
  return;
273
293
  }
274
294
 
275
- // list of TopicParts indices and selected value to use in WHERE statement
276
- // const topicPartSelectors = [
277
- // [2, path[2]],
278
- // [3, path[3]]
279
- // ];
295
+ this.topics[selector] = true;
280
296
 
281
- // path.slice(5).forEach((value, i) => topicPartSelectors.push([i + 5, value]));
297
+ // ---------------------------------------------------------------
298
+ // Set/update TTL for this capability and sub-topic
282
299
 
283
- // const where = topicPartSelectors
284
- // // filter out wildcards
285
- // .filter(([i, value]) => !['+','#'].includes(value[0]))
286
- // // map to WHERE conditions
287
- // .map(([i, value]) => `((TopicParts[${i + 1}]) = '${value}')`);
300
+ // Derive WHERE conditions for TTL expression from non-wildcards
288
301
  const where = path2where(path);
289
302
 
290
303
  if (where.length == 0) {
291
304
  // underspecified, don't set TTL
305
+ console.warn('Not setting TTL as topic is under specified', selector);
292
306
  return;
293
307
  }
294
308
 
@@ -307,17 +321,14 @@ class ClickHouse {
307
321
  const newTTLStatement =
308
322
  `toDateTime(Timestamp) + toIntervalDay(${ttlDays}) ${whereStatement}`;
309
323
 
310
- let present = false;
311
- // check if TTL statement already present on table definiton
312
- ttls.forEach((ttl, i) => {
313
- if (ttl.replace(/[()]/g, '').endsWith(whereStatement)) {
314
- // condition already present, just replace it to update time
315
- ttls[i] = newTTLStatement;
316
- present = true;
317
- }
318
- });
324
+ const currentIndex =
325
+ ttls.findIndex(ttl => ttl.replace(/[()]/g, '').endsWith(whereStatement));
319
326
 
320
- if (!present) {
327
+ if (currentIndex >= 0) {
328
+ // replace existing
329
+ ttls[currentIndex] = newTTLStatement;
330
+ } else {
331
+ // add new
321
332
  ttls.push(newTTLStatement);
322
333
  }
323
334
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@transitive-sdk/clickhouse",
3
- "version": "0.3.2",
3
+ "version": "0.3.4",
4
4
  "description": "A tiny ClickHouse utility class for use in the Transitive framework.",
5
5
  "homepage": "https://transitiverobotics.com",
6
6
  "repository": {
@@ -25,7 +25,8 @@
25
25
  },
26
26
  "dependencies": {
27
27
  "@clickhouse/client": "^1.12.1",
28
- "@transitive-sdk/datacache": "^0.14.1"
28
+ "@transitive-sdk/datacache": "^0.14.1",
29
+ "wait-port": "^1.1.0"
29
30
  },
30
31
  "devDependencies": {
31
32
  "dotenv": "^17.2.3"
@@ -47,7 +47,7 @@ describe('ClickHouse', function() {
47
47
  const dataCache = new DataCache({});
48
48
 
49
49
  before(async () => {
50
- clickhouse.init({ url: CLICKHOUSE_URL });
50
+ await clickhouse.init({ url: CLICKHOUSE_URL });
51
51
  /* Register for `insert` events on ClickHouse client */
52
52
  emitter = interceptInserts();
53
53