UNPKG

6.43 kBJavaScriptView Raw
1/**
2* vim:set sw=2 ts=2 sts=2 ft=javascript expandtab:
3*
4* # Database Module
5*
6* ## License
7*
8* Licensed to the Apache Software Foundation (ASF) under one
9* or more contributor license agreements. See the NOTICE file
10* distributed with this work for additional information
11* regarding copyright ownership. The ASF licenses this file
12* to you under the Apache License, Version 2.0 (the
13* "License"); you may not use this file except in compliance
14* with the License. You may obtain a copy of the License at
15*
16* http://www.apache.org/licenses/LICENSE-2.0
17*
18* Unless required by applicable law or agreed to in writing,
19* software distributed under the License is distributed on an
20* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21* KIND, either express or implied. See the License for the
22* specific language governing permissions and limitations
23* under the License.
24*
25* ## Description
26*
27* This module consists only on a wrapper around etherpad database.
28*/
29
30module.exports = (function () {
31 'use strict';
32 // Dependencies
33 var ld = require('lodash');
34
35 var storage = {};
36
37 // Database PREFIX CONSTANTS
38 storage.DBPREFIX = { GLOBAL: 'mypads:' };
39 var DBPG = storage.DBPREFIX.GLOBAL;
40 storage.DBPREFIX.CONF = DBPG + 'conf:';
41 storage.DBPREFIX.USER = DBPG + 'user:';
42 storage.DBPREFIX.GROUP = DBPG + 'group:';
43 storage.DBPREFIX.PAD = DBPG + 'pad:';
44 storage.DBPREFIX.JOBQ = DBPG + 'jobqueue:';
45
46 try {
47 // Normal case : when installed as a plugin
48 storage.db = require('ep_etherpad-lite/node/db/DB').db;
49 }
50 catch (e) {
51 // Testing case : we need to mock the database connection, using ueberDB and
52 // coherent default configuration with eptherpad-lite one.
53 var ueberDB = require('ueberdb2');
54 storage.db = new ueberDB.database('dirty', { filename: './test.db' });
55 storage.db.init(function (err) { if (err) { console.error(err); } });
56 }
57
58 /**
59 * `init` function for initial configuration cache init and in memory
60 * secondary indexes. At the moment only user / logins / firstname / lastname.
61 */
62
63 storage.init = function (callback) {
64 var configuration = require('./configuration.js');
65 configuration.init(function (err) {
66 if (err) { return callback(err); }
67 var userCache = require('./model/user-cache.js');
68 userCache.init(callback);
69 });
70 };
71
72 /**
73 * ## Internal functions `fn`
74 *
75 * These functions are not private like with closures, for testing purposes,
76 * but they are expected be used only internally by other MyPads functions.
77 */
78
79 storage.fn = {};
80
81 /** ### getDelKeys
82 *
83 * `getDelKeys` is a function for multiple asynchronous gets and removes,
84 * taking :
85 *
86 * - a `del` boolean, for removals to *true*
87 * - a `keys` array, wich contains a list of keys to retrieve
88 * - a `callback` function, called if error or when finished with *null* and
89 * the `results` object, which is composed of keys and values for gets,
90 * *true* for removals
91 * FIXME: TCO ?
92 */
93
94 storage.fn.getDelKeys = function (del, keys, callback) {
95 var done;
96 var results = del ? true : {};
97 var action = del ? 'remove' : 'get';
98 var getDel = function (k) {
99 storage.db[action](k, function (err, res) {
100 if (err) { return callback(err); }
101 if (!del && !ld.isNull(res)) { results[k] = res; }
102 done();
103 });
104 };
105 done = function () {
106 if (keys.length) {
107 getDel(keys.pop());
108 } else {
109 return callback(null, results);
110 }
111 };
112 done();
113 };
114
115 /**
116 * ### getKeysUncached
117 *
118 * Same than `getKeys` but tries to get values in bulk, bypassing UeberDB cache
119 */
120 storage.fn.getKeysUncached = function (keys, callback) {
121 if (storage.db && (storage.db.type === 'postgres' || storage.db.type === 'postgrespool')) {
122 var query = 'SELECT key, value FROM store WHERE key = ANY ($1)';
123 return storage.db.db.wrappedDB.db.query(query, [keys], function (err, queryResult) {
124 if (err) { return callback(err); }
125
126 var rows = queryResult.rows;
127
128 var results = {};
129 rows.forEach(function (row) {
130 try {
131 if (!ld.isNull(row)) { results[row.key] = JSON.parse(row.value); }
132 } catch (e) {
133 console.error('JSON-PROBLEM:' + row.value);
134 }
135 });
136
137 return callback(null, results);
138 });
139 }
140
141 return storage.fn.getKeys(keys, callback);
142 };
143
144 /**
145 * ### getKeys
146 *
147 * `getKeys` is an helper around `storage.fn.getDelKeys` with `del` argument
148 * to *false*.
149 */
150 storage.fn.getKeys = ld.partial(storage.fn.getDelKeys, false);
151
152 /**
153 * ### delKeys
154 *
155 * `delKeys` is an helper around `storage.fn.getDelKeys` with `del` argument
156 * to *true*.
157 */
158
159 storage.fn.delKeys = ld.partial(storage.fn.getDelKeys, true);
160
161 /**
162 * `setKeys` is a function for multiple asynchronous sets, taking :
163 s
164 * - a `kv` object, wich contains a list a keys and values to set
165 * - a `callback` function, called if error or when finished with null
166 * FIXME: TCO ?
167 */
168
169 storage.fn.setKeys = function (kv, callback) {
170 var done;
171 var pairs = ld.pairs(kv);
172 var setK = function (k, v) { storage.db.set(k, v, done); };
173 done = function (err) {
174 if (err) { return callback(err); }
175 if (pairs.length) {
176 var pair = pairs.pop();
177 setK(pair[0], pair[1]);
178 } else {
179 return callback(null);
180 }
181 };
182 done();
183 };
184
185 /**
186 * `setKeysIfNotExists` is a function for multiple asynchronous sets if the
187 * key does not exists in database, taking :
188 s
189 * - a `kv` object, wich contains a list a keys and values to set
190 * - a `callback` function, called if error or when finished with null
191 * FIXME: TCO ?
192 */
193
194 storage.fn.setKeysIfNotExists = function (kv, callback) {
195 var done;
196 var pairs = ld.pairs(kv);
197 var setK = function (k, v) { storage.db.set(k, v, done); };
198 done = function (err) {
199 if (err) { return callback(err); }
200 if (pairs.length) {
201 var pair = pairs.pop();
202 storage.db.get(pair[0], function(err, res) {
203 if (err) { return callback(err); }
204 if (ld.isUndefined(res) || ld.isNull(res)) {
205 setK(pair[0], pair[1]);
206 } else {
207 done();
208 }
209 });
210 } else {
211 return callback(null);
212 }
213 };
214 done();
215 };
216
217 return storage;
218}).call(this);