Software zum Installieren eines Smart-Mirror Frameworks , zum Nutzen von hochschulrelevanten Informationen, auf einem Raspberry-Pi.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

JsonDB.js 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.JsonDB = void 0;
  4. const Utils_1 = require("./lib/Utils");
  5. const FS = require("fs");
  6. const path = require("path");
  7. const mkdirp = require("mkdirp");
  8. const Errors_1 = require("./lib/Errors");
  9. const DBParentData_1 = require("./lib/DBParentData");
  10. const ArrayInfo_1 = require("./lib/ArrayInfo");
  11. const JsonDBConfig_1 = require("./lib/JsonDBConfig");
  12. class JsonDB {
  13. loaded = false;
  14. data = {};
  15. config;
  16. /**
  17. * JSONDB Constructor
  18. * @param filename where to save the "DB". Can also be used to give the whole configuration
  19. * @param saveOnPush save the database at each push command into the json file
  20. * @param humanReadable the JSON file will be readable easily by a human
  21. * @param separator what to use as separator
  22. */
  23. constructor(filename, saveOnPush = true, humanReadable = false, separator = '/') {
  24. if (filename instanceof JsonDBConfig_1.Config) {
  25. this.config = filename;
  26. }
  27. else {
  28. this.config = new JsonDBConfig_1.Config(filename, saveOnPush, humanReadable, separator);
  29. }
  30. if (!FS.existsSync(this.config.filename)) {
  31. const dirname = path.dirname(this.config.filename);
  32. mkdirp.sync(dirname);
  33. this.save(true);
  34. this.loaded = true;
  35. }
  36. }
  37. /**
  38. * Process datapath into different parts
  39. * @param dataPath
  40. */
  41. processDataPath(dataPath) {
  42. if (dataPath === undefined || !dataPath.trim()) {
  43. throw new Errors_1.DataError("The Data Path can't be empty", 6);
  44. }
  45. if (dataPath == this.config.separator) {
  46. return [];
  47. }
  48. dataPath = (0, Utils_1.removeTrailingChar)(dataPath, this.config.separator);
  49. const path = dataPath.split(this.config.separator);
  50. path.shift();
  51. return path;
  52. }
  53. retrieveData(dataPath, create = false) {
  54. this.load();
  55. const thisDb = this;
  56. const recursiveProcessDataPath = (data, index) => {
  57. let property = dataPath[index];
  58. /**
  59. * Find the wanted Data or create it.
  60. */
  61. function findData(isArray = false) {
  62. if (data.hasOwnProperty(property)) {
  63. data = data[property];
  64. }
  65. else if (create) {
  66. if (isArray) {
  67. data[property] = [];
  68. }
  69. else {
  70. data[property] = {};
  71. }
  72. data = data[property];
  73. }
  74. else {
  75. throw new Errors_1.DataError(`Can't find dataPath: ${thisDb.config.separator}${dataPath.join(thisDb.config.separator)}. Stopped at ${property}`, 5);
  76. }
  77. }
  78. const arrayInfo = ArrayInfo_1.ArrayInfo.processArray(property);
  79. if (arrayInfo) {
  80. property = arrayInfo.property;
  81. findData(true);
  82. if (!Array.isArray(data)) {
  83. throw new Errors_1.DataError(`DataPath: ${thisDb.config.separator}${dataPath.join(thisDb.config.separator)}. ${property} is not an array.`, 11);
  84. }
  85. const arrayIndex = arrayInfo.getIndex(data, true);
  86. if (!arrayInfo.append && data.hasOwnProperty(arrayIndex)) {
  87. data = data[arrayIndex];
  88. }
  89. else if (create) {
  90. if (arrayInfo.append) {
  91. data.push({});
  92. data = data[data.length - 1];
  93. }
  94. else {
  95. data[arrayIndex] = {};
  96. data = data[arrayIndex];
  97. }
  98. }
  99. else {
  100. throw new Errors_1.DataError(`DataPath: ${thisDb.config.separator}${dataPath.join(thisDb.config.separator)}. . Can't find index ${arrayInfo.index} in array ${property}`, 10);
  101. }
  102. }
  103. else {
  104. findData();
  105. }
  106. if (dataPath.length == ++index) {
  107. return data;
  108. }
  109. return recursiveProcessDataPath(data, index);
  110. };
  111. if (dataPath.length === 0) {
  112. return this.data;
  113. }
  114. return recursiveProcessDataPath(this.data, 0);
  115. }
  116. getParentData(dataPath, create) {
  117. const path = this.processDataPath(dataPath);
  118. const last = path.pop();
  119. return new DBParentData_1.DBParentData(this.retrieveData(path, create), this, dataPath, last);
  120. }
  121. /**
  122. * Get the wanted data
  123. * @param dataPath path of the data to retrieve
  124. */
  125. getData(dataPath) {
  126. const path = this.processDataPath(dataPath);
  127. return this.retrieveData(path, false);
  128. }
  129. /**
  130. * Same as getData only here it's directly typed to your object
  131. * @param dataPath path of the data to retrieve
  132. */
  133. getObject(dataPath) {
  134. return this.getData(dataPath);
  135. }
  136. /**
  137. * Check for existing datapath
  138. * @param dataPath
  139. */
  140. exists(dataPath) {
  141. try {
  142. this.getData(dataPath);
  143. return true;
  144. }
  145. catch (e) {
  146. if (e instanceof Errors_1.DataError) {
  147. return false;
  148. }
  149. throw e;
  150. }
  151. }
  152. /**
  153. * Returns the number of element which constitutes the array
  154. * @param dataPath
  155. */
  156. count(dataPath) {
  157. const result = this.getData(dataPath);
  158. if (!Array.isArray(result)) {
  159. throw new Errors_1.DataError(`DataPath: ${dataPath} is not an array.`, 11);
  160. }
  161. const path = this.processDataPath(dataPath);
  162. const data = this.retrieveData(path, false);
  163. return data.length;
  164. }
  165. /**
  166. * Returns the index of the object that meets the criteria submitted. Returns -1, if no match is found.
  167. * @param dataPath base dataPath from where to start searching
  168. * @param searchValue value to look for in the dataPath
  169. * @param propertyName name of the property to look for searchValue
  170. */
  171. getIndex(dataPath, searchValue, propertyName = 'id') {
  172. const data = this.getArrayData(dataPath);
  173. return data.map(function (element) { return element[propertyName]; }).indexOf(searchValue);
  174. }
  175. /**
  176. * Return the index of the value inside the array. Returns -1, if no match is found.
  177. * @param dataPath base dataPath from where to start searching
  178. * @param searchValue value to look for in the dataPath
  179. */
  180. getIndexValue(dataPath, searchValue) {
  181. return this.getArrayData(dataPath).indexOf(searchValue);
  182. }
  183. getArrayData(dataPath) {
  184. const result = this.getData(dataPath);
  185. if (!Array.isArray(result)) {
  186. throw new Errors_1.DataError(`DataPath: ${dataPath} is not an array.`, 11);
  187. }
  188. const path = this.processDataPath(dataPath);
  189. return this.retrieveData(path, false);
  190. }
  191. /**
  192. * Find all specific entry in an array/object
  193. * @param rootPath base dataPath from where to start searching
  194. * @param callback method to filter the result and find the wanted entry. Receive the entry and it's index.
  195. */
  196. filter(rootPath, callback) {
  197. const result = this.getData(rootPath);
  198. if (Array.isArray(result)) {
  199. return result.filter(callback);
  200. }
  201. if (result instanceof Object) {
  202. const entries = Object.entries(result);
  203. const found = entries.filter((entry) => {
  204. return callback(entry[1], entry[0]);
  205. });
  206. if (!found || found.length < 1) {
  207. return undefined;
  208. }
  209. return found.map((entry) => {
  210. return entry[1];
  211. });
  212. }
  213. throw new Errors_1.DataError("The entry at the path (" + rootPath + ") needs to be either an Object or an Array", 12);
  214. }
  215. /**
  216. * Find a specific entry in an array/object
  217. * @param rootPath base dataPath from where to start searching
  218. * @param callback method to filter the result and find the wanted entry. Receive the entry and it's index.
  219. */
  220. find(rootPath, callback) {
  221. const result = this.getData(rootPath);
  222. if (Array.isArray(result)) {
  223. return result.find(callback);
  224. }
  225. if (result instanceof Object) {
  226. const entries = Object.entries(result);
  227. const found = entries.find((entry) => {
  228. return callback(entry[1], entry[0]);
  229. });
  230. if (!found || found.length < 2) {
  231. return undefined;
  232. }
  233. return found[1];
  234. }
  235. throw new Errors_1.DataError("The entry at the path (" + rootPath + ") needs to be either an Object or an Array", 12);
  236. }
  237. /**
  238. * Pushing data into the database
  239. * @param dataPath path leading to the data
  240. * @param data data to push
  241. * @param override overriding or not the data, if not, it will merge them
  242. */
  243. push(dataPath, data, override = true) {
  244. const dbData = this.getParentData(dataPath, true);
  245. if (!dbData) {
  246. throw new Error("Data not found");
  247. }
  248. let toSet = data;
  249. if (!override) {
  250. if (Array.isArray(data)) {
  251. let storedData = dbData.getData();
  252. if (storedData === undefined) {
  253. storedData = [];
  254. }
  255. else if (!Array.isArray(storedData)) {
  256. throw new Errors_1.DataError("Can't merge another type of data with an Array", 3);
  257. }
  258. toSet = storedData.concat(data);
  259. }
  260. else if (data === Object(data)) {
  261. if (Array.isArray(dbData.getData())) {
  262. throw new Errors_1.DataError("Can't merge an Array with an Object", 4);
  263. }
  264. toSet = (0, Utils_1.merge)(dbData.getData(), data);
  265. }
  266. }
  267. dbData.setData(toSet);
  268. if (this.config.saveOnPush) {
  269. this.save();
  270. }
  271. }
  272. /**
  273. * Delete the data
  274. * @param dataPath path leading to the data
  275. */
  276. delete(dataPath) {
  277. const dbData = this.getParentData(dataPath, true);
  278. if (!dbData) {
  279. return;
  280. }
  281. dbData.delete();
  282. if (this.config.saveOnPush) {
  283. this.save();
  284. }
  285. }
  286. /**
  287. * Only use this if you know what you're doing.
  288. * It reset the full data of the database.
  289. * @param data
  290. */
  291. resetData(data) {
  292. this.data = data;
  293. }
  294. /**
  295. * Reload the database from the file
  296. */
  297. reload() {
  298. this.loaded = false;
  299. this.load();
  300. }
  301. ;
  302. /**
  303. * Manually load the database
  304. * It is automatically called when the first getData is done
  305. */
  306. load() {
  307. if (this.loaded) {
  308. return;
  309. }
  310. try {
  311. const data = FS.readFileSync(this.config.filename, 'utf8');
  312. this.data = JSON.parse(data);
  313. this.loaded = true;
  314. }
  315. catch (err) {
  316. const error = new Errors_1.DatabaseError("Can't Load Database", 1, err);
  317. throw error;
  318. }
  319. }
  320. /**
  321. * Manually save the database
  322. * By default you can't save the database if it's not loaded
  323. * @param force force the save of the database
  324. */
  325. save(force) {
  326. force = force || false;
  327. if (!force && !this.loaded) {
  328. throw new Errors_1.DatabaseError("DataBase not loaded. Can't write", 7);
  329. }
  330. let data = "";
  331. try {
  332. if (this.config.humanReadable) {
  333. data = JSON.stringify(this.data, null, 4);
  334. }
  335. else {
  336. data = JSON.stringify(this.data);
  337. }
  338. FS.writeFileSync(this.config.filename, data, 'utf8');
  339. }
  340. catch (err) {
  341. const error = new Errors_1.DatabaseError("Can't save the database", 2, err);
  342. throw error;
  343. }
  344. }
  345. }
  346. exports.JsonDB = JsonDB;
  347. //# sourceMappingURL=JsonDB.js.map