• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

suculent / thinx-device-api / #252646724

23 Apr 2022 01:29PM UTC coverage: 4.357% (-63.3%) from 67.616%
#252646724

push

web-flow
Merge pull request #360 from suculent/thinx-class

staging latest APIv2 and coverage improvements

2 of 243 branches covered (0.82%)

Branch coverage included in aggregate %.

0 of 2 new or added lines in 1 file covered. (0.0%)

411 existing lines in 7 files now uncovered.

39 of 698 relevant lines covered (5.59%)

0.06 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.75
/lib/thinx/database.js
1
// Database Manager
2

3
const Globals = require("./globals.js");
1✔
UNCOV
4
const app_config = Globals.app_config();
×
UNCOV
5
const fs = require("fs-extra");
×
UNCOV
6
module.exports = class Database {
×
7

8
        constructor() {
9

10
                let db_uri;
UNCOV
11
                let user = process.env.COUCHDB_USER;
×
UNCOV
12
                let pass = process.env.COUCHDB_PASS;
×
13

UNCOV
14
                if ((typeof (user) !== "undefined") && (typeof (pass) !== "undefined")) {
×
UNCOV
15
                        db_uri = `http://${user}:${pass}@couchdb:5984`;
×
16
                } else {
17
                        db_uri = app_config.database_uri; // fallback to old config.json; deprecated
×
18
                        console.log("⛔️ [deprecated] Using database credentials from configuration...");
×
19
                }
20

UNCOV
21
                this.db_uri = db_uri;
×
UNCOV
22
                this.nano = require("nano")(db_uri);
×
23

24
        }
25

26
        nano() {
27
                return this.nano;
×
28
        }
29

30
        uri() {
31

32
                /* duplicate code, happens in constructor and that's enough
33
                let db_uri;
34
                let user = process.env.COUCHDB_USER;
35
                let pass = process.env.COUCHDB_PASS;
36

37
                if ((typeof (user) !== "undefined") && (typeof (pass) !== "undefined")) {
38
                        db_uri = `http://${user}:${pass}@couchdb:5984`;
39
                } else {
40
                        db_uri = app_config.database_uri; // fallback to old config.json; deprecated
41
                        console.log("⛔️ [deprecated] Using database credentials from configuration...");
42
                }
43

44
                this.db_uri = db_uri;
45
                */
46

UNCOV
47
                return this.db_uri;
×
48
        }
49
        
50

51
        null_cb(err, body, header) {
52
                // only unexpected errors should be logged
53
                if (process.env.ENVIRONMENT === "test") {
×
54
                        // The database may already exist.
55
                        if (err.ststusCode !== 412) {
×
56
                                console.log(err, body, header);
×
57
                        }
58
                }
59
        }
60

61
        // Designated initalizer
62
        init(callback) {
63

UNCOV
64
                console.log("ℹ️ [info] Initializing databases...");
×
65

UNCOV
66
                let db_names = [
×
67
                        "devices", "builds", "users", "logs"
68
                ];
69

UNCOV
70
                this.nano.db.list((_err, existing_dbs) => {
×
71

UNCOV
72
                        if ((typeof(existing_dbs) === "undefined") || (existing_dbs === null)) existing_dbs = [];
×
73

UNCOV
74
                        db_names.forEach((name) => {
×
75

UNCOV
76
                                if (existing_dbs.includes(name)) {
×
77
                                        console.log(`ℹ️ [info] DB ${name} already exists.`);
×
78
                                        return;
×
79
                                }
80

UNCOV
81
                                const dbprefix = Globals.prefix();
×
82

UNCOV
83
                                this.nano.db.create(dbprefix + "managed_" + name).then((/* cerr, data */) => {
×
UNCOV
84
                                        var couch_db = this.nano.db.use(dbprefix + "managed_" + name);
×
UNCOV
85
                                        this.injectDesign(couch_db, name, "/opt/thinx/thinx-device-api/design/design_" + name + ".json");
×
UNCOV
86
                                        this.injectReplFilter(couch_db, "/opt/thinx/thinx-device-api/design/filters_" + name + ".json");
×
UNCOV
87
                                        console.log(`ℹ️ [info] Database managed_${name} initialized.`);
×
88
                                }).catch((err2) => {
89
                                        // returns error normally if DB already exists
UNCOV
90
                                        this.handleDatabaseErrors(err2, "managed_" + name, dbprefix);
×
91
                                });
92
                        });
93

UNCOV
94
                        this.nano.db.list((err2, new_dbs) => {
×
UNCOV
95
                                if (typeof (callback) !== "undefined") {
×
UNCOV
96
                                        callback(err2, new_dbs);
×
97
                                } else {
98
                                        return new_dbs;
×
99
                                }
100
                        });
101

UNCOV
102
                        setTimeout(() => {
×
UNCOV
103
                                setInterval(this.compactDatabases, 3600 * 1000); // Compact databases once an hour        
×
104
                        }, 30000);
105
                        
106
                });
107
        }
108

109
        compactDatabases(opt_callback) {
UNCOV
110
                const prefix = Globals.prefix();
×
UNCOV
111
                let db_uri = new Database().uri();
×
UNCOV
112
                this.nano = require("nano")(db_uri);
×
UNCOV
113
                this.nano.db.compact(prefix + "managed_logs")
×
114
                .then(() => {
UNCOV
115
                        this.nano.db.compact(prefix + "managed_builds");
×
116
                }).then(() => {
UNCOV
117
                        this.nano.db.compact(prefix + "managed_devices");
×
118
                }).then(() => {
UNCOV
119
                        this.nano.db.compact(prefix + "managed_users");
×
120
                }).then(() => {
UNCOV
121
                        if (typeof (opt_callback) !== "undefined") opt_callback(true);
×
122
                }).catch(e => {
123
                        console.log("InitDB error", e);
×
124
                        if (typeof (opt_callback) !== "undefined") opt_callback(e);
×
125
                });
126
        }
127

128
        // Database preparation on first run
129
        getDocument(file) {
UNCOV
130
                if (!fs.existsSync(file)) {
×
131
                        console.log("☣️ [error] Initializing replication filter failed, file does not exist", file);
×
132
                        return false;
×
133
                }
UNCOV
134
                const data = fs.readFileSync(file);
×
UNCOV
135
                if (typeof (data) === "undefined") {
×
136
                        console.log("☣️ [error] [getDocument] no data read.");
×
137
                        return false;
×
138
                }
139
                // Parser may fail
UNCOV
140
                try {
×
UNCOV
141
                        return JSON.parse(data);
×
142
                } catch (e) {
143
                        console.log("☣️ [error] Document File may not exist: " + e);
×
144
                        return false;
×
145
                }
146
        }
147

148
        logCouchError(err, body, header, tag) {
UNCOV
149
                if (err !== null) {
×
150
                        if (err.toString().indexOf("conflict") === -1) {
×
151
                                console.log("☣️ [error] Couch Init error: ", err, body, header, tag);
×
152
                        }
153
                        if (err.toString().indexOf("ENOTFOUND") !== -1) {
×
154
                                console.log("Critical DB integration error, exiting.");
×
155
                                process.exit(1);
×
156
                        }
157
                } else {
UNCOV
158
                        return;
×
159
                }
160
                if (typeof (body) !== "undefined") {
×
161
                        console.log("☣️ [error] Log Couch Insert body: " + body + " " + tag);
×
162
                }
163
                if (typeof (header) !== "undefined") {
×
164
                        console.log("☣️ [error] Log Couchd Insert header: " + header + " " + tag);
×
165
                }
166
        }
167

168
        injectDesign(db, design, file) {
UNCOV
169
                if (typeof (design) === "undefined") return;
×
UNCOV
170
                let design_doc = this.getDocument(file);
×
UNCOV
171
                if (design_doc != null) {
×
UNCOV
172
                        db.insert(design_doc, "_design/" + design, (err, body, header) => {
×
UNCOV
173
                                this.logCouchError(err, body, header, "init:design:" + design); // returns if no err
×
174
                        });
175
                } else {
176
                        console.log("☣️ [error] Design doc injection issue at " + file);
×
177
                }
178
        }
179

180
        injectReplFilter(db, file) {
UNCOV
181
                let filter_doc = this.getDocument(file);
×
UNCOV
182
                if (filter_doc !== false) {
×
UNCOV
183
                        db.insert(filter_doc, "_design/repl_filters", (err, body, header) => {
×
UNCOV
184
                                this.logCouchError(err, body, header, "init:repl:" + JSON.stringify(filter_doc)); // returns if no err
×
185
                        });
186
                } else {
187
                        console.log("☣️ [error] Filter doc injection issue (no doc) at " + file);
×
188
                }
189
        }
190

191
        handleDatabaseErrors(err, name, info) {
UNCOV
192
                if (err.toString().indexOf("the file already exists") !== -1) {
×
193
                        // silently fail, this is ok
194
                } else if (err.toString().indexOf("error happened") !== -1) {
×
195
                        console.log("🚫 [critical] Database connectivity issue. " + err.toString());
×
196
                        // give some time for DB to wake up until next try, also prevents too fast restarts...
197
                        setTimeout(() => {
×
198
                                process.exit(1);
×
199
                        }, 1000);
200
                } else {
201
                        console.log("🚫 [critical] Database " + name + " creation failed. " + err, " info:", info);
×
202
                        setTimeout(() => {
×
203
                                process.exit(2);
×
204
                        }, 1000);
205
                }
206
        }
207
};
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc