spice-couchbase 2.0.13 → 2.0.16

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.
Files changed (3) hide show
  1. package/build/index.js +348 -231
  2. package/package.json +5 -3
  3. package/src/index.js +374 -199
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spice-couchbase",
3
- "version": "2.0.13",
3
+ "version": "2.0.16",
4
4
  "description": "",
5
5
  "main": "build/index.js",
6
6
  "scripts": {
@@ -9,8 +9,10 @@
9
9
  "author": "Chad Fraser <chad@sonover.com> (http://sonover.com)",
10
10
  "license": "ISC",
11
11
  "dependencies": {
12
- "couchbase": "^3.0.6",
13
- "lodash": "^4.17.15"
12
+ "axios": "^0.26.1",
13
+ "couchbase": "^4.0.0",
14
+ "lodash": "^4.17.15",
15
+ "qs": "^6.10.3"
14
16
  },
15
17
  "devDependencies": {
16
18
  "@babel/cli": "^7.7.7",
package/src/index.js CHANGED
@@ -1,136 +1,285 @@
1
1
  "use strict";
2
2
 
3
- let _ = require('lodash');
4
- let couchbase = require('couchbase');
3
+ let _ = require("lodash");
4
+ let couchbase = require("couchbase");
5
+ const { default: axios } = require("axios");
6
+ var qs = require("qs");
7
+
8
+ if (!Promise.allSettled) {
9
+ Promise.allSettled = (promises) =>
10
+ Promise.all(
11
+ promises.map((promise, i) =>
12
+ promise
13
+ .then((value) => ({
14
+ status: "fulfilled",
15
+ value,
16
+ }))
17
+ .catch((reason) => ({
18
+ status: "rejected",
19
+ reason,
20
+ }))
21
+ )
22
+ );
23
+ }
5
24
  module.exports = class Database {
6
- constructor(connection, full_text_index) {
25
+ constructor(
26
+ connection,
27
+ {
28
+ full_text_index,
29
+ collection,
30
+ scope,
31
+ query_scope = "cluster",
32
+ auto_create_collection,
33
+ } = {}
34
+ ) {
7
35
  if (connection) {
8
36
  this.connection_name = connection;
9
37
  this.connection = spice.database.connections[connection];
10
- this.cluster = spice.database.clusters[connection]
38
+ this.query_scope = query_scope;
39
+ this.cluster = spice.database.clusters[connection];
11
40
  this.bucket = this.connection._scope._bucket._name;
12
- this.full_text_index = spice.config.database.connections[connection].full_text_index || full_text_index;
13
- this.N1qlQuery = couchbase.N1qlQuery
41
+ this.db_config = spice.config.database.connections[connection];
42
+ this.collection_manager =
43
+ spice.database.buckets[connection].collections();
44
+ this.auto_create_collection =
45
+ auto_create_collection ||
46
+ ((collection ||
47
+ spice.config.database.connections[connection].collection) !=
48
+ undefined &&
49
+ (collection ||
50
+ spice.config.database.connections[connection].collection) !=
51
+ "default" &&
52
+ (scope || spice.config.database.connections[connection].scope) !=
53
+ "_default");
54
+ this.collection_name =
55
+ collection ||
56
+ spice.config.database.connections[connection].collection ||
57
+ "_default";
58
+ this.scope_name =
59
+ scope ||
60
+ spice.config.database.connections[connection].scope ||
61
+ "_default";
62
+ this.scope = spice.database.scopes[connection];
63
+ this.full_text_index =
64
+ spice.config.database.connections[connection].full_text_index ||
65
+ full_text_index;
66
+ this.N1qlQuery = couchbase.N1qlQuery;
14
67
  this.SearchQuery = couchbase.SearchQuery;
15
- }
16
- }
17
- connect(key, value) {
18
- return new Promise((resolve, reject) => {
19
- try {
20
- let cluster = new couchbase.Cluster(value.nodes, {
21
- username: value.username,
22
- password: value.password,
23
- });
24
- let bucket = cluster.bucket(value.bucket);
25
- let collection = bucket.defaultCollection();
26
- resolve({
27
- connection: key,
28
- bucket,
29
- cluster,
30
- collection,
31
- });
32
- } catch (e) {
33
- spice.logger.debug(e, e.stack);
34
- reject(e);
68
+ this.connection = this.scope.collection(this.collection_name);
69
+ this.userManager = this.getUserManager(connection);
70
+ if (this.auto_create_collection) {
71
+ this.upsertCollection();
35
72
  }
36
- })
73
+
74
+ this.use_bucket_name = !(
75
+ spice.config.database.connections[this.connection_name].scope &&
76
+ spice.config.database.connections[this.connection_name].collection
77
+ );
78
+ }
37
79
  }
38
80
 
39
- get(doc_id) {
40
- let that = this;
41
- return new Promise(async function (resolve, reject) {
42
- that.connection.get(doc_id, function (err, res) {
43
- var return_value = {};
44
- if (err) {
45
- reject(err);
46
- } else {
47
- _.defaults(return_value, res.value);
48
- return_value.id = doc_id;
49
- resolve(return_value);
50
- }
51
- });
81
+ async connect(connection_name, config) {
82
+ let cluster = await couchbase.connect(config.nodes, {
83
+ username: config.username,
84
+ password: config.password,
52
85
  });
86
+ let bucket = cluster.bucket(config.bucket);
87
+ let collection;
88
+ let scope_name = this.scope_name || config.scope || "_default";
89
+ let collection_name =
90
+ this.collection_name || config.collection || "_default";
91
+ let scope = bucket.scope(scope_name);
92
+
93
+ collection = bucket
94
+ .scope(scope_name)
95
+ .collection(collection_name || "_default");
96
+
97
+ return {
98
+ connection: connection_name,
99
+ bucket,
100
+ cluster,
101
+ scope,
102
+ collection,
103
+ };
53
104
  }
54
105
 
55
- insert(doc_id, document, expiry) {
56
- var that = this;
57
- return new Promise(async function (resolve, reject) {
58
- that.connection.insert(doc_id, document, {
59
- expiry: expiry,
60
- }, async function (err, res) {
61
- if (err) {
62
- reject(err);
63
- } else {
64
- try {
65
- let created = await that.get(doc_id)
66
- resolve(created)
67
- } catch (e) {
68
- reject(e)
69
- }
70
- }
71
- });
72
- });
106
+ getUserManager(connection) {
107
+ return spice.database.clusters[connection].users();
73
108
  }
74
109
 
75
- touch(doc_id, expiry = 0) {
76
- var that = this;
77
- return new Promise(async function (resolve, reject) {
78
- that.connection.touch(doc_id, expiry, async function (err, res) {
79
- if (err) {
80
- reject(err);
81
- } else {
82
- try {
83
- let created = await that.get(doc_id)
84
- resolve(created)
85
- } catch (e) {
86
- reject(e)
87
- }
110
+ async assignUserPermission() {
111
+ try {
112
+ let allUsers = await this.userManager.getAllUsers({ timeout: 6000 });
113
+ let update_user_response = await this.userManager.upsertUser(
114
+ this.db_config.username,
115
+ {
116
+ roles: [
117
+ { name: "scope_admin", bucket: this.db_config.bucket },
118
+ { name: "data_reader", bucket: this.db_config.bucket },
119
+ ],
88
120
  }
89
- });
90
- });
121
+ );
122
+ } catch (e) {
123
+ console.log(e);
124
+ }
91
125
  }
92
126
 
127
+ getCouchbaseHost(default_port = 8091) {
128
+ return (
129
+ (this.db_config.api_base ||
130
+ `http://${_.last(
131
+ _.first(this.db_config.nodes.split(",")).split("://")
132
+ )}`) +
133
+ ":" +
134
+ default_port
135
+ );
136
+ }
93
137
 
94
- count(doc_id, adjustment, initial_value) {
95
- var that = this;
96
- return new Promise(async function (resolve, reject) {
97
- that.connection.counter(doc_id, adjustment, {
98
- initial: initial_value,
99
- }, function (err, res) {
100
- if (err) {
101
- reject(err);
102
- } else {
103
- resolve(res.value)
104
- }
105
- });
106
- });
138
+ //create wait function
139
+ async wait(ms) {
140
+ return new Promise((resolve) => setTimeout(resolve, ms));
107
141
  }
108
142
 
143
+ async upsertCollection() {
144
+ try {
145
+ let collection_exists = await this.collectionExists(
146
+ this.collection_name,
147
+ this.scope_name
148
+ );
149
+ if (!collection_exists) {
150
+ await this.createCollection(this.collection_name, this.scope_name);
151
+ await this.wait(2000);
152
+ await this.createPrimaryIndex(this.scope_name, this.collection_name);
153
+ }
154
+ } catch (e) {
155
+ console.log(e);
156
+ }
157
+ }
109
158
 
110
- update(doc_id, document) {
111
- var that = this;
112
- return new Promise(async function (resolve, reject) {
113
- that.connection.replace(doc_id, document, function (err, res) {
114
- if (err) {
115
- reject(err);
116
- } else {
117
- resolve(res.value)
159
+ async collectionExists(collection_name, scope_name) {
160
+ try {
161
+ let all_scopes = await axios.get(
162
+ `${this.getCouchbaseHost()}/pools/default/buckets/${
163
+ this.db_config.bucket
164
+ }/scopes`,
165
+ {
166
+ auth: {
167
+ username: this.db_config.username,
168
+ password: this.db_config.password,
169
+ },
118
170
  }
119
- });
120
- });
171
+ );
172
+ let collection_e =
173
+ _.find(
174
+ _.find(all_scopes.data.scopes, { name: scope_name })?.collections ||
175
+ [],
176
+ { name: collection_name }
177
+ ) != undefined;
178
+ return collection_e;
179
+ } catch (e) {
180
+ console.log("Exists error happened", e);
181
+ }
121
182
  }
122
183
 
123
- delete(doc_id) {
124
- var that = this;
125
- return new Promise(async function (resolve, reject) {
126
- that.connection.remove(doc_id, function (err, res) {
127
- if (err) {
128
- reject(err);
129
- } else {
130
- resolve(res.value)
131
- }
132
- });
184
+ async createCollection(connection_name, scope_name) {
185
+ try {
186
+ const token = `${this.db_config.username}:${this.db_config.password}`;
187
+ const encodedToken = Buffer.from(token).toString("base64");
188
+ const headers = {
189
+ Authorization: "Basic " + encodedToken,
190
+ "Content-Type": "application/x-www-form-urlencoded",
191
+ };
192
+ let response = await axios({
193
+ method: "post",
194
+ url: `${this.getCouchbaseHost()}/pools/default/buckets/${
195
+ this.db_config.bucket
196
+ }/scopes/${scope_name}/collections`,
197
+ headers,
198
+ data: qs.stringify({ name: connection_name }),
199
+ }).data;
200
+ return response;
201
+ } catch (e) {}
202
+ }
203
+
204
+ async createPrimaryIndex(scope_name, col_name) {
205
+ try {
206
+ let response = await this.query(
207
+ `CREATE PRIMARY INDEX ON default:\`${this.db_config.bucket}\`.\`${scope_name}\`.\`${col_name}\``
208
+ );
209
+ return response;
210
+ } catch (e) {
211
+ console.log("CREATE Index :::", e);
212
+ }
213
+ }
214
+
215
+ async createScope(scope_name) {
216
+ try {
217
+ await this.collection_manager.createScope(scope_name);
218
+ } catch (e) {
219
+ if (e instanceof couchbase.ScopeExistsError) {
220
+ console.log("The scope already exists");
221
+ } else {
222
+ throw e;
223
+ }
224
+ }
225
+ }
226
+
227
+ async get(doc_id) {
228
+ try {
229
+ let response = await (this.use_bucket_name
230
+ ? this.connection
231
+ : this.collection
232
+ ).get(doc_id);
233
+
234
+ return { id: doc_id, ...response.content };
235
+ } catch (e) {
236
+ console.error(e);
237
+ }
238
+ }
239
+
240
+ async insert(doc_id, document, expiry) {
241
+ await (this.use_bucket_name ? this.connection : this.collection).insert(
242
+ doc_id,
243
+ document,
244
+ {
245
+ expiry: expiry,
246
+ }
247
+ );
248
+ return await this.get(doc_id);
249
+ }
250
+
251
+ async touch(doc_id, expiry = 0) {
252
+ await (this.use_bucket_name ? this.connection : this.collection).touch(
253
+ doc_id,
254
+ expiry
255
+ );
256
+ return await this.get(doc_id);
257
+ }
258
+
259
+ async count(doc_id, adjustment, initial_value) {
260
+ let res = await (this.use_bucket_name
261
+ ? this.connection
262
+ : this.collection
263
+ ).counter(doc_id, adjustment, {
264
+ initial: initial_value,
133
265
  });
266
+ return res.value;
267
+ }
268
+
269
+ async update(doc_id, document) {
270
+ let res = await (this.use_bucket_name
271
+ ? this.connection
272
+ : this.collection
273
+ ).replace(doc_id, document);
274
+ return res.value;
275
+ }
276
+
277
+ async delete(doc_id) {
278
+ let res = await (this.use_bucket_name
279
+ ? this.connection
280
+ : this.collection
281
+ ).remove(doc_id);
282
+ return res.value;
134
283
  }
135
284
 
136
285
  async multi_get(keys, keep_type) {
@@ -141,8 +290,18 @@ module.exports = class Database {
141
290
  }
142
291
  // console.log("on Multiget", keys);
143
292
  if (_.isArray(keys) && keys.length > 0) {
144
- let functions = _.map(keys, key => this.connection.get(key))
145
- let responses = await Promise.all(functions);
293
+ let functions = _.map(keys, (key) =>
294
+ (this.use_bucket_name ? this.connection : this.collection).get(key)
295
+ );
296
+ let returned_all = await Promise.allSettled(functions);
297
+
298
+ let responses = _.flatten(
299
+ _.compact(
300
+ _.map(returned_all, (returned_obj) => {
301
+ if (returned_obj.status == "fulfilled") return returned_obj.value;
302
+ })
303
+ )
304
+ );
146
305
 
147
306
  return _.map(responses, (res, index) => {
148
307
  if (!keep_type) {
@@ -151,24 +310,6 @@ module.exports = class Database {
151
310
  res.value.id = keys[index];
152
311
  return res.value;
153
312
  });
154
-
155
- /* that.connection.getMulti(keys, function (err, res) {
156
- if (res) {
157
- let return_value = [];
158
- for (var i in res) {
159
- if (res[i].value) {
160
- if (!keep_type) {
161
- delete res[i].value['type']
162
- }
163
- res[i].value.id = i;
164
- return_value.push(res[i].value);
165
- }
166
- }
167
- resolve(return_value);
168
- } else if (err) {
169
- reject(err);
170
- }
171
- }); */
172
313
  } else {
173
314
  return [];
174
315
  }
@@ -203,7 +344,7 @@ module.exports = class Database {
203
344
  that.connection.query(query, async function (err, results, matrix) {
204
345
  var return_value = [];
205
346
  if (err) {
206
- reject(err)
347
+ reject(err);
207
348
  } else {
208
349
  for (var i = 0; i < results.length; i++) {
209
350
  if (results[i].value) {
@@ -214,88 +355,114 @@ module.exports = class Database {
214
355
  }
215
356
 
216
357
  try {
217
- let created = await that.multi_get(return_value)
218
- resolve(created)
358
+ let created = await that.multi_get(return_value);
359
+ resolve(created);
219
360
  } catch (e) {
220
- reject(e)
361
+ reject(e);
221
362
  }
222
363
  }
223
364
  });
224
365
  });
225
366
  }
226
367
 
368
+ async search(
369
+ type,
370
+ columns,
371
+ query,
372
+ limit,
373
+ offset,
374
+ order_by,
375
+ do_count,
376
+ query_scope
377
+ ) {
378
+ let is_legacy =
379
+ this.scope_name !== "_default" && this.collection_name !== "_default";
380
+ let alias = "_reference";
381
+ let bucket = `\`${
382
+ this.use_bucket_name ? this.bucket : this.collection_name
383
+ }\``;
384
+
385
+ try {
386
+ //let uses_scopes = this.scope this.scope_name !== "_default";
387
+
388
+ if (do_count == undefined) {
389
+ do_count = "false";
390
+ }
391
+ var fix = "";
392
+ var count_query = "";
393
+ if (!limit) {
394
+ limit = 0;
395
+ }
227
396
 
228
- search(type, columns, query, limit, offset, order_by, do_count, statement_consistent) {
229
- let that = this;
230
- return new Promise(async (resolve, reject) => {
231
- try {
232
- if (do_count == undefined) {
233
- do_count = "false";
234
- }
235
- var fix = "";
236
- var count_query = "";
237
- if (!limit) {
238
- limit = 0;
239
- }
397
+ if (!offset) {
398
+ offset = 0;
399
+ }
240
400
 
241
- if (!offset) {
242
- offset = 0;
243
- }
401
+ if (columns == "*") {
402
+ columns = "(" + bucket + ").*";
403
+ fix = ", META(" + bucket + ").id AS id";
404
+ }
244
405
 
245
- if (columns == "*") {
246
- columns = "(" + that.bucket + ").*";
247
- fix = ", META(" + that.bucket + ").id AS id";
248
- }
406
+ if (!columns) {
407
+ columns = "(" + bucket + ").*";
408
+ fix = ", META(" + bucket + ").id AS id";
409
+ }
249
410
 
250
- if (!columns) {
251
- columns = "(" + that.bucket + ").*";
252
- fix = ", META(" + that.bucket + ").id AS id";
253
- }
411
+ if (columns == "id") {
412
+ fix = ", META(" + bucket + ").id AS id";
413
+ columns = "";
414
+ }
254
415
 
255
- if (columns == "id") {
256
- fix = ", META(" + that.bucket + ").id AS id";
257
- columns = "";
258
- }
416
+ var front_query = `SELECT ${columns} ${fix} FROM ${bucket}`;
259
417
 
260
- var front_query = "SELECT " + columns + fix + " FROM " + that.bucket + " WHERE type='" + type + "'";
261
- count_query = "SELECT count(meta().id) as count FROM " + that.bucket + " WHERE type='" + type + "'";
262
- if (query) {
263
- count_query = count_query + " AND " + query;
264
- query = front_query + " AND " + query;
265
- } else {
266
- query = front_query;
267
- }
418
+ if (is_legacy || query) {
419
+ front_query += " WHERE ";
420
+ }
268
421
 
269
- if (order_by) {
270
- query = `${query} ORDER BY ${order_by}`;
271
- }
422
+ let conditions = [];
423
+ if (this.use_bucket_name) {
424
+ conditions.push(`type = '${type}'`);
425
+ }
426
+ if (query) {
427
+ conditions.push(query);
428
+ }
272
429
 
273
- if (limit > 0) {
274
- query = query + " LIMIT " + limit;
275
- }
430
+ query = front_query + conditions.join(" AND ");
276
431
 
277
- if (offset > 0) {
278
- query = query + " OFFSET " + offset;
279
- }
432
+ count_query = `SELECT count(meta().id) as count FROM ${bucket}`;
433
+ if (this.use_bucket_name || query) {
434
+ count_query += " WHERE ";
435
+ }
436
+ query = front_query + conditions.join(" AND ");
280
437
 
281
- var query_element = query;
282
- var count_query_element = count_query;
438
+ if (order_by) {
439
+ query = `${query} ORDER BY ${order_by}`;
440
+ }
283
441
 
284
- let queries = [that.getData(query_element)]
285
- if (do_count === "true") {
286
- queries.push(that.getCount(count_query_element));
287
- }
288
- let responses = await Promise.all(queries);
442
+ if (limit > 0) {
443
+ query = query + " LIMIT " + limit;
444
+ }
289
445
 
290
- resolve({
291
- data: responses[0],
292
- total: responses.length > 1 ? responses[1] : null,
293
- });
294
- } catch (error) {
295
- console.log(error.stack);
296
- reject(error);
446
+ if (offset > 0) {
447
+ query = query + " OFFSET " + offset;
297
448
  }
298
- });
449
+
450
+ let active_query_space =
451
+ query_scope || !this.use_bucket_name ? "scope" : "cluster";
452
+
453
+ let queries = [this.getData(query, active_query_space)];
454
+ if (do_count === "true") {
455
+ queries.push(this.getCount(count_query, active_query_space));
456
+ }
457
+ let responses = await Promise.all(queries);
458
+
459
+ return {
460
+ data: responses[0],
461
+ total: responses.length > 1 ? responses[1] : null,
462
+ };
463
+ } catch (error) {
464
+ throw error;
465
+ }
299
466
  }
300
467
 
301
468
  full_text_search(type, search_query, limit, offset = 0, full_text_index) {
@@ -303,7 +470,10 @@ module.exports = class Database {
303
470
  return new Promise(async function (resolve, reject) {
304
471
  try {
305
472
  let full_query = search_query + " AND type:" + type;
306
- var query = that.SearchQuery.new(that.full_text_index || full_text_index, that.SearchQuery.queryString(full_query));
473
+ var query = that.SearchQuery.new(
474
+ that.full_text_index || full_text_index,
475
+ that.SearchQuery.queryString(full_query)
476
+ );
307
477
 
308
478
  if (!limit) {
309
479
  limit = 0;
@@ -322,7 +492,7 @@ module.exports = class Database {
322
492
  that.cluster.query(query, async function (err, results, matrix) {
323
493
  var return_value = [];
324
494
  if (err) {
325
- reject(err)
495
+ reject(err);
326
496
  } else {
327
497
  for (var i = 0; i < results.length; i++) {
328
498
  if (results[i].value) {
@@ -333,7 +503,7 @@ module.exports = class Database {
333
503
  }
334
504
 
335
505
  try {
336
- let getted = await that.multi_get(return_value)
506
+ let getted = await that.multi_get(return_value);
337
507
  resolve({
338
508
  data: getted,
339
509
  total: null,
@@ -349,16 +519,21 @@ module.exports = class Database {
349
519
  });
350
520
  }
351
521
 
352
- async getCount(count_query_element) {
353
- let response = await this.cluster.query(count_query_element);
522
+ async getCount(count_query_element, query_scope = "cluster") {
523
+ let response = await this.query(count_query_element, query_scope);
354
524
  return response.rows[0].count;
355
525
  }
356
- async getData(query_element) {
357
- let response = await this.cluster.query(query_element)
526
+ async getData(query_element, query_scope = "cluster") {
527
+ let response = await this.query(query_element, query_scope);
358
528
  return response.rows;
359
529
  }
360
530
 
361
- async query(query) {
362
- return this.cluster.query(query)
531
+ async query(query, query_scope = "cluster") {
532
+ let scope = query_scope || this.query_scope;
533
+ if (scope == "cluster") {
534
+ return await this.cluster.query(query);
535
+ } else {
536
+ return await this.scope.query(query);
537
+ }
363
538
  }
364
- }
539
+ };