wingbot-mongodb 4.1.2 → 4.2.0

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.
@@ -0,0 +1 @@
1
+ {"parent":null,"pid":92445,"argv":["/Users/david/.nvm/versions/node/v22.3.0/bin/node","/Users/david/Development/wingbot-mongodb/node_modules/.bin/mocha","./test"],"execArgv":[],"cwd":"/Users/david/Development/wingbot-mongodb","time":1728402100297,"ppid":92444,"coverageFilename":"/Users/david/Development/wingbot-mongodb/.nyc_output/dcd83c20-b25a-4e44-9809-1951738231ac.json","externalId":"","uuid":"dcd83c20-b25a-4e44-9809-1951738231ac","files":["/Users/david/Development/wingbot-mongodb/src/BaseStorage.js","/Users/david/Development/wingbot-mongodb/src/defaultLogger.js","/Users/david/Development/wingbot-mongodb/src/getNestedObjects.js","/Users/david/Development/wingbot-mongodb/src/AttachmentCache.js","/Users/david/Development/wingbot-mongodb/src/AuditLogStorage.js","/Users/david/Development/wingbot-mongodb/src/BotConfigStorage.js","/Users/david/Development/wingbot-mongodb/src/BotTokenStorage.js","/Users/david/Development/wingbot-mongodb/src/tokenFactory.js","/Users/david/Development/wingbot-mongodb/src/ChatLogStorage.js","/Users/david/Development/wingbot-mongodb/src/NotificationsStorage.js","/Users/david/Development/wingbot-mongodb/src/StateStorage.js"]}
@@ -1 +1 @@
1
- {"processes":{"596e9b84-51c8-44b2-b5a0-e9ba553853e7":{"parent":null,"children":[]}},"files":{"/Users/david/Development/wingbot-mongodb/src/BaseStorage.js":["596e9b84-51c8-44b2-b5a0-e9ba553853e7"],"/Users/david/Development/wingbot-mongodb/src/defaultLogger.js":["596e9b84-51c8-44b2-b5a0-e9ba553853e7"],"/Users/david/Development/wingbot-mongodb/src/AttachmentCache.js":["596e9b84-51c8-44b2-b5a0-e9ba553853e7"],"/Users/david/Development/wingbot-mongodb/src/AuditLogStorage.js":["596e9b84-51c8-44b2-b5a0-e9ba553853e7"],"/Users/david/Development/wingbot-mongodb/src/BotConfigStorage.js":["596e9b84-51c8-44b2-b5a0-e9ba553853e7"],"/Users/david/Development/wingbot-mongodb/src/BotTokenStorage.js":["596e9b84-51c8-44b2-b5a0-e9ba553853e7"],"/Users/david/Development/wingbot-mongodb/src/tokenFactory.js":["596e9b84-51c8-44b2-b5a0-e9ba553853e7"],"/Users/david/Development/wingbot-mongodb/src/ChatLogStorage.js":["596e9b84-51c8-44b2-b5a0-e9ba553853e7"],"/Users/david/Development/wingbot-mongodb/src/NotificationsStorage.js":["596e9b84-51c8-44b2-b5a0-e9ba553853e7"],"/Users/david/Development/wingbot-mongodb/src/StateStorage.js":["596e9b84-51c8-44b2-b5a0-e9ba553853e7"]},"externalIds":{}}
1
+ {"processes":{"dcd83c20-b25a-4e44-9809-1951738231ac":{"parent":null,"children":[]}},"files":{"/Users/david/Development/wingbot-mongodb/src/BaseStorage.js":["dcd83c20-b25a-4e44-9809-1951738231ac"],"/Users/david/Development/wingbot-mongodb/src/defaultLogger.js":["dcd83c20-b25a-4e44-9809-1951738231ac"],"/Users/david/Development/wingbot-mongodb/src/getNestedObjects.js":["dcd83c20-b25a-4e44-9809-1951738231ac"],"/Users/david/Development/wingbot-mongodb/src/AttachmentCache.js":["dcd83c20-b25a-4e44-9809-1951738231ac"],"/Users/david/Development/wingbot-mongodb/src/AuditLogStorage.js":["dcd83c20-b25a-4e44-9809-1951738231ac"],"/Users/david/Development/wingbot-mongodb/src/BotConfigStorage.js":["dcd83c20-b25a-4e44-9809-1951738231ac"],"/Users/david/Development/wingbot-mongodb/src/BotTokenStorage.js":["dcd83c20-b25a-4e44-9809-1951738231ac"],"/Users/david/Development/wingbot-mongodb/src/tokenFactory.js":["dcd83c20-b25a-4e44-9809-1951738231ac"],"/Users/david/Development/wingbot-mongodb/src/ChatLogStorage.js":["dcd83c20-b25a-4e44-9809-1951738231ac"],"/Users/david/Development/wingbot-mongodb/src/NotificationsStorage.js":["dcd83c20-b25a-4e44-9809-1951738231ac"],"/Users/david/Development/wingbot-mongodb/src/StateStorage.js":["dcd83c20-b25a-4e44-9809-1951738231ac"]},"externalIds":{}}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wingbot-mongodb",
3
- "version": "4.1.2",
3
+ "version": "4.2.0",
4
4
  "description": "MongoDB storage for wingbot.ai",
5
5
  "main": "src/main.js",
6
6
  "scripts": {
@@ -7,31 +7,12 @@ const { ObjectId } = require('mongodb');
7
7
  const { strict: assert } = require('assert');
8
8
  const crypto = require('crypto');
9
9
  const defaultLogger = require('./defaultLogger');
10
+ const getNestedObjects = require('./getNestedObjects');
10
11
 
11
12
  /** @typedef {import('mongodb').Db} Db */
12
13
  /** @typedef {import('mongodb').Document} Document */
13
14
  /** @typedef {import('mongodb').CreateIndexesOptions} CreateIndexesOptions */
14
15
 
15
- /**
16
- *
17
- * @param {any} obj
18
- * @param {boolean} nested
19
- * @param {string} [attr]
20
- * @param {object} [ret]
21
- * @returns {object}
22
- */
23
- function getNestedObjects (obj, nested, attr = null, ret = {}) {
24
- if (typeof obj !== 'object' || !obj || nested === null || Array.isArray(obj)) {
25
- Object.assign(ret, { [attr]: obj === undefined ? null : obj });
26
- } else {
27
- Object.entries(obj)
28
- .forEach(([key, val]) => {
29
- getNestedObjects(val, nested || null, attr ? `${attr}.${key}` : key, ret);
30
- });
31
- }
32
- return ret;
33
- }
34
-
35
16
  /**
36
17
  *
37
18
  * @template T
@@ -9,20 +9,21 @@ const defaultLogger = require('./defaultLogger');
9
9
  const { ObjectId } = mongodb;
10
10
 
11
11
  /**
12
- * @typedef Target {Object}
12
+ * @typedef {object} Target
13
13
  * @prop {string} senderId
14
14
  * @prop {string} pageId
15
+ * @prop {{ [key: string]: object }} [meta]
15
16
  */
16
17
 
17
18
  /**
18
- * @typedef Subscribtion {Object}
19
+ * @typedef {object} Subscribtion
19
20
  * @prop {string} senderId
20
21
  * @prop {string} pageId
21
22
  * @prop {string[]} subs
22
23
  */
23
24
 
24
25
  /**
25
- * @typedef Campaign {object}
26
+ * @typedef {object} Campaign
26
27
  * @prop {string} id
27
28
  * @prop {string} name
28
29
  *
@@ -59,7 +60,7 @@ const { ObjectId } = mongodb;
59
60
  */
60
61
 
61
62
  /**
62
- * @typedef Task {Object}
63
+ * @typedef {object} Task
63
64
  * @prop {string} id
64
65
  * @prop {string} pageId
65
66
  * @prop {string} senderId
@@ -73,6 +74,21 @@ const { ObjectId } = mongodb;
73
74
  * @prop {number} [leaved] - time the event was not sent because user left
74
75
  */
75
76
 
77
+ /**
78
+ * @typedef {object} Subscription
79
+ * @prop {string} tag
80
+ * @prop {object} meta
81
+ */
82
+
83
+ /**
84
+ * @typedef {object} SubscriptionData
85
+ * @prop {string} pageId
86
+ * @prop {string} senderId
87
+ * @prop {string[]} tags
88
+ * @prop {boolean} [remove]
89
+ * @prop {{ [key: string]: object }} [meta]
90
+ */
91
+
76
92
  const MAX_TS = 9999999999999;
77
93
  const COSMO_LIMIT = 999;
78
94
 
@@ -472,6 +488,7 @@ class NotificationsStorage {
472
488
  const c = await this._getCollection(this.taksCollection);
473
489
 
474
490
  const res = await c.findOne({
491
+ // @ts-ignore
475
492
  _id: ObjectId.isValid(taskId)
476
493
  ? ObjectId.createFromHexString(taskId)
477
494
  : taskId
@@ -489,6 +506,7 @@ class NotificationsStorage {
489
506
  const c = await this._getCollection(this.taksCollection);
490
507
 
491
508
  const res = await c.findOneAndUpdate({
509
+ // @ts-ignore
492
510
  _id: ObjectId.isValid(taskId)
493
511
  ? ObjectId.createFromHexString(taskId)
494
512
  : taskId
@@ -789,6 +807,66 @@ class NotificationsStorage {
789
807
  };
790
808
  }
791
809
 
810
+ /**
811
+ *
812
+ * @param {SubscriptionData[]} subscriptionData
813
+ * @param {boolean} [onlyToKnown=false]
814
+ * @returns {Promise}
815
+ */
816
+ async batchSubscribe (subscriptionData, onlyToKnown = false) {
817
+ const c = await this._getCollection(this.subscribtionsCollection);
818
+
819
+ const toSend = subscriptionData.filter((t) => t.tags.length !== 0);
820
+
821
+ while (toSend.length) {
822
+ const up = toSend.splice(0, 999);
823
+
824
+ await c.bulkWrite(
825
+ // @ts-ignore
826
+ up.map(({
827
+ pageId, senderId, tags, meta = {}, remove
828
+ }) => {
829
+ const set = Object.entries(meta);
830
+
831
+ let addSet = {};
832
+
833
+ if (remove) {
834
+ addSet = {
835
+ $set: Object.fromEntries(
836
+ tags.map((t) => [`meta.${t}`, {}])
837
+ )
838
+ };
839
+ } else if (set.length) {
840
+ addSet = {
841
+ $set: Object.fromEntries(
842
+ set.map(([k, v]) => [`meta.${k}`, v])
843
+ )
844
+ };
845
+ }
846
+
847
+ return {
848
+ updateOne: {
849
+ filter: { senderId, pageId },
850
+ update: {
851
+ ...(remove
852
+ ? { $pullAll: { subs: tags } }
853
+ : { $addToSet: { subs: { $each: tags } } }),
854
+ ...addSet
855
+ },
856
+ upsert: !remove && !onlyToKnown
857
+ }
858
+ };
859
+ }),
860
+ {
861
+ ordered: false,
862
+ writeConcern: {
863
+ w: onlyToKnown ? 0 : 1
864
+ }
865
+ }
866
+ );
867
+ }
868
+ }
869
+
792
870
  /**
793
871
  *
794
872
  * @param {string|string[]} senderId
@@ -801,25 +879,13 @@ class NotificationsStorage {
801
879
  // !IMPORTANT: do not add a default value to the fourth parameter!
802
880
  const senderIds = Array.isArray(senderId) ? senderId : [senderId];
803
881
 
804
- if (senderIds.length === 0) {
805
- return;
806
- }
807
- const c = await this._getCollection(this.subscribtionsCollection);
808
-
809
- await c.bulkWrite(
882
+ return this.batchSubscribe(
810
883
  senderIds.map((sid) => ({
811
- updateOne: {
812
- filter: { senderId: sid, pageId },
813
- update: { $addToSet: { subs: tag } },
814
- upsert: !onlyToKnown
815
- }
884
+ senderId: sid,
885
+ pageId,
886
+ tags: [tag]
816
887
  })),
817
- {
818
- ordered: false,
819
- writeConcern: {
820
- w: onlyToKnown ? 0 : 1
821
- }
822
- }
888
+ onlyToKnown
823
889
  );
824
890
  }
825
891
 
@@ -840,6 +906,7 @@ class NotificationsStorage {
840
906
  const res = await c.findOneAndUpdate({
841
907
  pageId, senderId, subs: tag
842
908
  }, {
909
+ // @ts-ignore
843
910
  $pull: { subs: tag }
844
911
  }, {
845
912
  returnDocument: 'after'
@@ -933,7 +1000,17 @@ class NotificationsStorage {
933
1000
  while (hasNext) {
934
1001
 
935
1002
  const cursor = c.find(condition)
936
- .project({ _id: 1, pageId: 1, senderId: 1 })
1003
+ .project({
1004
+ _id: 1,
1005
+ pageId: 1,
1006
+ senderId: 1,
1007
+ ...(include.length === 0
1008
+ ? { meta: 1 }
1009
+ : Object.fromEntries(
1010
+ include.map((k) => [`meta.${k}`, 1])
1011
+ )
1012
+ )
1013
+ })
937
1014
  .sort({ _id: 1 })
938
1015
  .skip(skip)
939
1016
  .limit(useLimit);
@@ -959,29 +1036,48 @@ class NotificationsStorage {
959
1036
  }
960
1037
 
961
1038
  return Promise.resolve({
962
- data: data.map(({ senderId, pageId: p }) => ({ senderId, pageId: p })),
1039
+ data: data.map(({
1040
+ senderId, pageId: p, meta
1041
+ }) => ({
1042
+ senderId, pageId: p, ...(meta ? { meta } : {})
1043
+ })),
963
1044
  lastKey: nextLastKey
964
1045
  });
965
1046
  }
966
1047
 
967
1048
  /**
968
- *
969
1049
  * @param {string} senderId
970
1050
  * @param {string} pageId
971
- * @returns {Promise<string[]>}
1051
+ * @returns {Promise<Subscription[]>}
972
1052
  */
973
- async getSenderSubscribtions (senderId, pageId) {
1053
+ async getSenderSubscriptions (senderId, pageId) {
974
1054
  const c = await this._getCollection(this.subscribtionsCollection);
975
1055
 
976
- const sub = await c.findOne({ senderId, pageId }, { projection: { _id: 0, subs: 1 } });
1056
+ const sub = await c.findOne({
1057
+ senderId, pageId
1058
+ }, { projection: { _id: 0, subs: 1, meta: 1 } });
977
1059
 
978
1060
  if (sub) {
979
- return sub.subs;
1061
+ return sub.subs.map((tag) => ({
1062
+ tag,
1063
+ meta: (sub.meta && sub.meta[tag]) || {}
1064
+ }));
980
1065
  }
981
1066
 
982
1067
  return [];
983
1068
  }
984
1069
 
1070
+ /**
1071
+ *
1072
+ * @param {string} senderId
1073
+ * @param {string} pageId
1074
+ * @returns {Promise<string[]>}
1075
+ */
1076
+ async getSenderSubscribtions (senderId, pageId) {
1077
+ const subs = await this.getSenderSubscriptions(senderId, pageId);
1078
+ return subs.map((s) => s.tag);
1079
+ }
1080
+
985
1081
  async getTags (pageId = null) {
986
1082
  const c = await this._getCollection(this.subscribtionsCollection);
987
1083
 
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @author David Menger
3
+ */
4
+ 'use strict';
5
+
6
+ /**
7
+ *
8
+ * @param {any} obj
9
+ * @param {boolean} nested
10
+ * @param {string} [attr]
11
+ * @param {object} [ret]
12
+ * @returns {object}
13
+ */
14
+ function getNestedObjects (obj, nested, attr = null, ret = {}) {
15
+ if (typeof obj !== 'object' || !obj || nested === null || Array.isArray(obj)) {
16
+ Object.assign(ret, { [attr]: obj === undefined ? null : obj });
17
+ } else {
18
+ Object.entries(obj)
19
+ .forEach(([key, val]) => {
20
+ getNestedObjects(val, nested || null, attr ? `${attr}.${key}` : key, ret);
21
+ });
22
+ }
23
+ return ret;
24
+ }
25
+
26
+ module.exports = getNestedObjects;