UNPKG

8.32 kBJavaScriptView Raw
1(function () {
2 "use strict";
3 var FS = require('fs');
4 var events = require('events');
5 var JsonUtils = require("./lib/utils");
6 var DBParentData = require("./lib/DBParentData");
7 var DatabaseError = require("./lib/Errors").DatabaseError;
8 var DataError = require("./lib/Errors").DataError;
9 var mkdirp = require('mkdirp');
10 var path = require('path');
11
12 /**
13 * Create the JSON database
14 * @param filename where to save the data base
15 * @param saveOnPush saving on modification of the data
16 * @param humanReadable is the json file humand readable
17 * @returns {JsonDB}
18 * @constructor
19 */
20 var JsonDB = function (filename, saveOnPush, humanReadable) {
21
22 this.filename = filename;
23
24 if (!JsonUtils.strEndWith(filename, ".json")) {
25 this.filename += ".json";
26 }
27 var self = this;
28 this.loaded = false;
29 this.data = {};
30 if (!FS.existsSync(this.filename)) {
31 var dirname = path.dirname(this.filename);
32 mkdirp.sync(dirname);
33 self.save(true);
34 self.loaded = true;
35 }
36 this.saveOnPush = ( typeof( saveOnPush ) == "boolean" ) ? saveOnPush : true;
37 if (humanReadable) {
38 this.humanReadable = humanReadable;
39 }
40 else {
41 this.humanReadable = false;
42 }
43
44 return this;
45 };
46 JsonDB.prototype._processDataPath = function (dataPath) {
47 if (dataPath === undefined || !dataPath.trim()) {
48 throw new DataError("The Data Path can't be empty", 6);
49 }
50 if (dataPath == "/") {
51 return [];
52 }
53 var path = dataPath.split("/");
54 path.shift();
55 return path;
56 };
57
58 JsonDB.prototype._getParentData = function (dataPath, create) {
59 var path = this._processDataPath(dataPath);
60 var last = path.pop();
61 return new DBParentData(last, this._getData(path, create), this, dataPath);
62 };
63 /**
64 * Get the deta stored in the data base
65 * @param dataPath path leading to the data
66 * @returns {*}
67 */
68 JsonDB.prototype.getData = function (dataPath) {
69 var path = this._processDataPath(dataPath);
70 return this._getData(path);
71 };
72
73 JsonDB.prototype._getData = function (dataPath, create) {
74
75 this.load();
76
77 create = create || false;
78 dataPath = JsonUtils.removeTrailingSlash(dataPath);
79
80 function recursiveProcessDataPath(data, index) {
81
82 var property = dataPath[index];
83
84
85 /**
86 * Find the wanted Data or create it.
87 */
88 function findData(isArray) {
89 isArray = isArray || false;
90 if (data.hasOwnProperty(property)) {
91 data = data[property];
92 } else if (create) {
93 if (isArray) {
94 data[property] = [];
95 } else {
96 data[property] = {};
97 }
98 data = data[property];
99 } else {
100 throw new DataError("Can't find dataPath: /" + dataPath.join("/") + ". Stopped at " + property, 5);
101 }
102 }
103
104 var arrayInfo = JsonUtils.processArray(property);
105 if (arrayInfo) {
106 property = arrayInfo.property;
107 findData(true);
108 if (!Array.isArray(data)) {
109 throw new DataError("DataPath: /" + dataPath.join("/") + ". " + property + " is not an array.", 11);
110 }
111 var arrayIndex = arrayInfo.getIndex(data, true);
112 if (data.hasOwnProperty(arrayIndex)) {
113 data = data[arrayIndex];
114 } else if (create) {
115 if (arrayInfo.append) {
116 data.push({});
117 data = data[data.length - 1];
118 }
119 else {
120 data[arrayIndex] = {};
121 data = data[arrayIndex];
122 }
123 } else {
124 throw new DataError("DataPath: /" + dataPath.join("/") + ". Can't find index " + arrayInfo.index + " in array " + property, 10);
125 }
126 } else {
127 findData();
128 }
129
130 if (dataPath.length == ++index) {
131 return data;
132 }
133 return recursiveProcessDataPath(data, index);
134 }
135
136 if (dataPath.length === 0) {
137 return this.data;
138 }
139
140 return recursiveProcessDataPath(this.data, 0);
141
142 };
143
144 /**
145 * Pushing data into the database
146 * @param dataPath path leading to the data
147 * @param data data to push
148 * @param override overriding or not the data, if not, it will merge them
149 */
150 JsonDB.prototype.push = function (dataPath, data, override) {
151 override = override === undefined ? true : override;
152
153 dataPath = JsonUtils.removeTrailingSlash(dataPath);
154 var dbData = this._getParentData(dataPath, true);
155 if (!dbData) {
156 throw new Error("Data not found");
157 }
158 var toSet = data;
159 if (!override) {
160 if (Array.isArray(data)) {
161 var storedData = dbData.getData();
162 if (storedData === undefined) {
163 storedData = [];
164 } else if (!Array.isArray(storedData)) {
165 throw new DataError("Can't merge another type of data with an Array", 3);
166 }
167 toSet = storedData.concat(data);
168 } else if (data === Object(data)) {
169 if (Array.isArray(dbData.getData())) {
170 throw new DataError("Can't merge an Array with an Object", 4);
171 }
172 toSet = JsonUtils.mergeObject(dbData.getData(), data);
173 }
174 }
175 dbData.setData(toSet);
176
177 if (this.saveOnPush) {
178 this.save();
179 }
180 };
181 /**
182 * Delete the data
183 * @param dataPath path leading to the data
184 */
185 JsonDB.prototype.delete = function (dataPath) {
186 dataPath = JsonUtils.removeTrailingSlash(dataPath);
187 var dbData = this._getParentData(dataPath, true);
188 if (!dbData) {
189 return;
190 }
191 dbData.delete();
192
193 if (this.saveOnPush) {
194 this.save();
195 }
196 };
197 /**
198 * Reload the database from the file
199 * @returns {*}
200 */
201 JsonDB.prototype.reload = function () {
202 this.loaded = false;
203 return this.load();
204 };
205 /**
206 * Manually load the database
207 * It is automatically called when the first getData is done
208 */
209 JsonDB.prototype.load = function () {
210 if (this.loaded) {
211 return;
212 }
213 try {
214 var data = FS.readFileSync(this.filename, 'utf8');
215 this.data = JSON.parse(data);
216 this.loaded = true;
217 } catch (err) {
218 var error = new DatabaseError("Can't Load Database", 1, err);
219 error.inner = err;
220 throw error;
221 }
222 };
223 /**
224 * Manually save the database
225 * By default you can't save the database if it's not loaded
226 * @param force force the save of the database
227 */
228 JsonDB.prototype.save = function (force) {
229 force = force || false;
230 if (!force && !this.loaded) {
231 throw new DatabaseError("DataBase not loaded. Can't write", 7);
232 }
233 var data = "";
234 try {
235 if (this.humanReadable) {
236 data = JSON.stringify(this.data, null, 4);
237 }
238 else {
239 data = JSON.stringify(this.data);
240 }
241 FS.writeFileSync(this.filename, data, 'utf8');
242 } catch (err) {
243 var error = new DatabaseError("Can't save the database", 2, err);
244 error.inner = err;
245 throw error;
246 }
247
248 };
249
250 module.exports = JsonDB;
251})();