Source: components/user-bridge-store.js

  1. /*
  2. * User storage format:
  3. * {
  4. * type: "matrix|remote",
  5. * id: "user_id|remote_id",
  6. * data: {
  7. * .. matrix-specific info e.g. display name ..
  8. * .. remote specific info e.g. IRC username ..
  9. * }
  10. * }
  11. * Examples:
  12. * {
  13. * type: "matrix",
  14. * id: "@foo:bar",
  15. * data: {
  16. * localpart: "foo", // Required.
  17. * displayName: "Foo Bar" // Optional.
  18. * }
  19. * }
  20. *
  21. * {
  22. * type: "remote",
  23. * id: "foobar@irc.freenode.net",
  24. * data: {
  25. * nickChoices: ["foobar", "foobar_", "foobar__"]
  26. * }
  27. * }
  28. *
  29. * There is also a third type, the "union" type. This binds together a single
  30. * matrix <--> remote pairing. A single remote ID can have many matrix_id and
  31. * vice versa, via mutliple union entries.
  32. *
  33. * {
  34. * type: "union",
  35. * remote_id: "foobar@irc.freenode.net",
  36. * matrix_id: "@foo:bar"
  37. * }
  38. */
  39. "use strict";
  40. var BridgeStore = require("./bridge-store");
  41. var MatrixUser = require("../models/users/matrix");
  42. var RemoteUser = require("../models/users/remote");
  43. var util = require("util");
  44. /**
  45. * Construct a store suitable for user bridging information.
  46. * @constructor
  47. * @param {Datastore} db The connected NEDB database instance
  48. * @param {Object} opts Options for this store.
  49. */
  50. function UserBridgeStore(db, opts) {
  51. this.db = db;
  52. }
  53. util.inherits(UserBridgeStore, BridgeStore);
  54. /**
  55. * Retrieve a list of corresponding remote users for the given matrix user ID.
  56. * @param {string} userId The Matrix user ID
  57. * @return {Promise<RemoteUser[], Error>} Resolves to a list of Remote users.
  58. */
  59. UserBridgeStore.prototype.getRemoteUsersFromMatrixId = function(userId) {
  60. var self = this;
  61. return this.select({
  62. type: "union",
  63. matrix_id: userId
  64. }, self.convertTo(function(doc) {
  65. return doc.remote_id;
  66. })).then(function(remoteIds) {
  67. return self.select({
  68. type: "remote",
  69. id: { $in: remoteIds }
  70. }, self.convertTo(function(doc) {
  71. return new RemoteUser(doc.id, doc.data);
  72. }));
  73. });
  74. };
  75. /**
  76. * Retrieve a list of corresponding matrix users for the given remote ID.
  77. * @param {string} remoteId The Remote ID
  78. * @return {Promise<MatrixUser[], Error>} Resolves to a list of Matrix users.
  79. */
  80. UserBridgeStore.prototype.getMatrixUsersFromRemoteId = function(remoteId) {
  81. var self = this;
  82. return this.select({
  83. type: "union",
  84. remote_id: remoteId
  85. }, self.convertTo(function(doc) {
  86. return doc.matrix_id;
  87. })).then(function(matrixUserIds) {
  88. return self.select({
  89. type: "matrix",
  90. id: { $in: matrixUserIds }
  91. }, self.convertTo(function(doc) {
  92. return new MatrixUser(doc.id, doc.data);
  93. }));
  94. });
  95. };
  96. /**
  97. * Retrieve a MatrixUser based on their user ID localpart. If there is more than
  98. * one match (e.g. same localpart, different domains) then this will return an
  99. * arbitrary matching user.
  100. * @param {string} localpart The user localpart
  101. * @return {Promise<?MatrixUser, Error>} Resolves to a MatrixUser or null.
  102. */
  103. UserBridgeStore.prototype.getByMatrixLocalpart = function(localpart) {
  104. return this.selectOne({
  105. type: "matrix",
  106. "data.localpart": localpart
  107. }, this.convertTo(function(doc) {
  108. return new MatrixUser(doc.id, doc.data);
  109. }));
  110. };
  111. /**
  112. * Get a matrix user by their user ID.
  113. * @param {string} userId The user_id
  114. * @return {Promise<?MatrixUser, Error>} Resolves to the user or null if they
  115. * do not exist. Rejects with an error if there was a problem querying the store.
  116. */
  117. UserBridgeStore.prototype.getMatrixUser = function(userId) {
  118. return this.selectOne({
  119. type: "matrix",
  120. id: userId
  121. }, this.convertTo(function(doc) {
  122. return new MatrixUser(doc.id, doc.data);
  123. }));
  124. };
  125. /**
  126. * Store a Matrix user. If they already exist, they will be updated. Equivalence
  127. * is determined by their user ID.
  128. * @param {MatrixUser} matrixUser The matrix user
  129. * @return {Promise}
  130. */
  131. UserBridgeStore.prototype.setMatrixUser = function(matrixUser) {
  132. return this.upsert({
  133. type: "matrix",
  134. id: matrixUser.getId()
  135. }, {
  136. type: "matrix",
  137. id: matrixUser.getId(),
  138. data: matrixUser.serialize()
  139. });
  140. };
  141. /**
  142. * Get a remote user by their remote ID.
  143. * @param {string} id The remote ID
  144. * @return {Promise<?RemoteUser, Error>} Resolves to the user or null if they
  145. * do not exist. Rejects with an error if there was a problem querying the store.
  146. */
  147. UserBridgeStore.prototype.getRemoteUser = function(id) {
  148. return this.selectOne({
  149. type: "remote",
  150. id: id
  151. }, this.convertTo(function(doc) {
  152. return new RemoteUser(doc.id, doc.data);
  153. }));
  154. };
  155. /**
  156. * Get remote users by some data about them, previously stored via the set
  157. * method on the Remote user.
  158. * @param {Object} dataQuery The keys and matching values the remote users share.
  159. * This should use dot notation for nested types. For example:
  160. * <code> { "topLevel.midLevel.leaf": 42, "otherTopLevel": "foo" } </code>
  161. * @return {Promise<RemoteUser[], Error>} Resolves to a possibly empty list of
  162. * RemoteUsers. Rejects with an error if there was a problem querying the store.
  163. * @throws If dataQuery isn't an object.
  164. * @example
  165. * remoteUser.set({
  166. * toplevel: "foo",
  167. * nested: {
  168. * bar: {
  169. * baz: 43
  170. * }
  171. * }
  172. * });
  173. * store.setRemoteUser(remoteUser).then(function() {
  174. * store.getByRemoteData({
  175. * "toplevel": "foo",
  176. * "nested.bar.baz": 43
  177. * })
  178. * });
  179. */
  180. UserBridgeStore.prototype.getByRemoteData = function(dataQuery) {
  181. if (typeof dataQuery !== "object") {
  182. throw new Error("Data query must be an object.");
  183. }
  184. var query = {};
  185. Object.keys(dataQuery).forEach(function(key) {
  186. query["data." + key] = dataQuery[key];
  187. });
  188. query.type = "remote";
  189. return this.select(query, this.convertTo(function(doc) {
  190. return new RemoteUser(doc.id, doc.data);
  191. }));
  192. };
  193. /**
  194. * Get Matrix users by some data about them, previously stored via the set
  195. * method on the Matrix user.
  196. * @param {Object} dataQuery The keys and matching values the remote users share.
  197. * This should use dot notation for nested types. For example:
  198. * <code> { "topLevel.midLevel.leaf": 42, "otherTopLevel": "foo" } </code>
  199. * @return {Promise<MatrixUser[], Error>} Resolves to a possibly empty list of
  200. * MatrixUsers. Rejects with an error if there was a problem querying the store.
  201. * @throws If dataQuery isn't an object.
  202. * @example
  203. * matrixUser.set({
  204. * toplevel: "foo",
  205. * nested: {
  206. * bar: {
  207. * baz: 43
  208. * }
  209. * }
  210. * });
  211. * store.setMatrixUser(matrixUser).then(function() {
  212. * store.getByMatrixData({
  213. * "toplevel": "foo",
  214. * "nested.bar.baz": 43
  215. * })
  216. * });
  217. */
  218. UserBridgeStore.prototype.getByMatrixData = function(dataQuery) {
  219. if (typeof dataQuery !== "object") {
  220. throw new Error("Data query must be an object.");
  221. }
  222. var query = {};
  223. Object.keys(dataQuery).forEach(function(key) {
  224. query["data." + key] = dataQuery[key];
  225. });
  226. query.type = "matrix";
  227. return this.select(query, this.convertTo(function(doc) {
  228. return new MatrixUser(doc.id, doc.data);
  229. }));
  230. };
  231. /**
  232. * Store a Remote user. If they already exist, they will be updated. Equivalence
  233. * is determined by the Remote ID.
  234. * @param {RemoteUser} remoteUser The remote user
  235. * @return {Promise}
  236. */
  237. UserBridgeStore.prototype.setRemoteUser = function(remoteUser) {
  238. return this.upsert({
  239. type: "remote",
  240. id: remoteUser.getId()
  241. }, {
  242. type: "remote",
  243. id: remoteUser.getId(),
  244. data: remoteUser.serialize()
  245. });
  246. };
  247. /**
  248. * Create a link between a matrix and remote user. If either user does not exist,
  249. * they will be inserted prior to linking. This is done to ensure foreign key
  250. * constraints are satisfied (so you cannot have a mapping to a user ID which
  251. * does not exist).
  252. * @param {MatrixUser} matrixUser The matrix user
  253. * @param {RemoteUser} remoteUser The remote user
  254. * @return {Promise}
  255. */
  256. UserBridgeStore.prototype.linkUsers = function(matrixUser, remoteUser) {
  257. var self = this;
  258. return self.insertIfNotExists({
  259. type: "remote",
  260. id: remoteUser.getId()
  261. }, {
  262. type: "remote",
  263. id: remoteUser.getId(),
  264. data: remoteUser.serialize()
  265. }).then(function() {
  266. return self.insertIfNotExists({
  267. type: "matrix",
  268. id: matrixUser.getId()
  269. }, {
  270. type: "matrix",
  271. id: matrixUser.getId(),
  272. data: matrixUser.serialize()
  273. });
  274. }).then(function() {
  275. return self.upsert({
  276. type: "union",
  277. remote_id: remoteUser.getId(),
  278. matrix_id: matrixUser.getId()
  279. }, {
  280. type: "union",
  281. remote_id: remoteUser.getId(),
  282. matrix_id: matrixUser.getId()
  283. });
  284. });
  285. };
  286. /**
  287. * Delete a link between a matrix user and a remote user.
  288. * @param {MatrixUser} matrixUser The matrix user
  289. * @param {RemoteUser} remoteUser The remote user
  290. * @return {Promise<Number, Error>} Resolves to the number of entries removed.
  291. */
  292. UserBridgeStore.prototype.unlinkUsers = function(matrixUser, remoteUser) {
  293. return this.unlinkUserIds(matrixUser.getId(), remoteUser.getId());
  294. };
  295. /**
  296. * Delete a link between a matrix user ID and a remote user ID.
  297. * @param {string} matrixUserId The matrix user ID
  298. * @param {string} remoteUserId The remote user ID
  299. * @return {Promise<Number, Error>} Resolves to the number of entries removed.
  300. */
  301. UserBridgeStore.prototype.unlinkUserIds = function(matrixUserId, remoteUserId) {
  302. return this.delete({
  303. type: "union",
  304. remote_id: remoteUserId,
  305. matrix_id: matrixUserId
  306. });
  307. };
  308. /**
  309. * Retrieve a list of matrix user IDs linked to this remote ID.
  310. * @param {string} remoteId The remote ID
  311. * @return {Promise<String[], Error>} A list of user IDs.
  312. */
  313. UserBridgeStore.prototype.getMatrixLinks = function(remoteId) {
  314. return this.select({
  315. type: "union",
  316. remote_id: remoteId
  317. }, this.convertTo(function(doc) {
  318. return doc.matrix_id;
  319. }));
  320. };
  321. /**
  322. * Retrieve a list of remote IDs linked to this matrix user ID.
  323. * @param {string} matrixId The matrix user ID
  324. * @return {Promise<String[], Error>} A list of remote IDs.
  325. */
  326. UserBridgeStore.prototype.getRemoteLinks = function(matrixId) {
  327. return this.select({
  328. type: "union",
  329. matrix_id: matrixId
  330. }, this.convertTo(function(doc) {
  331. return doc.remote_id;
  332. }));
  333. };
  334. /** The UserBridgeStore class. */
  335. module.exports = UserBridgeStore;