Skip to content

Commit 624355a

Browse files
authored
Merge pull request #233 from anthonykhoa/arango
[DNM] closes #232 - Arango functions to stop/start server, and create/delete account
2 parents 642b583 + fe193b8 commit 624355a

File tree

10 files changed

+453
-82
lines changed

10 files changed

+453
-82
lines changed

database/arango/arango.js

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
require("dotenv").config();
2+
const { Database } = require("arangojs");
3+
const { sendFetch } = require("../../lib/sendFetch");
4+
const logger = require("./../../lib/log")(__filename);
5+
6+
const arangoModule = {};
7+
let db;
8+
9+
arangoModule.checkIfDatabaseExists = async (name) => {
10+
// returns array of objects containing a '_name' key.
11+
// Ex [{ _name: 'lol' }, { _name: 'cakes' }]
12+
const names = await db.databases();
13+
return names.map((db) => db._name).includes(name);
14+
};
15+
16+
arangoModule.startArangoDB = async () => {
17+
try {
18+
db = new Database({
19+
url: process.env.ARANGO_URL,
20+
databaseName: "_system",
21+
auth: {
22+
username: process.env.ARANGO_USER,
23+
password: process.env.ARANGO_PW,
24+
},
25+
});
26+
logger.info("Connected to Arango Database");
27+
} catch (err) {
28+
logger.error(err);
29+
throw new Error("Error in connecting to Arango Database", err);
30+
}
31+
};
32+
33+
arangoModule.closeArangoDB = () => {
34+
try {
35+
db.close();
36+
logger.info("Successful closing of connection to Arango database");
37+
} catch (err) {
38+
logger.error(err);
39+
throw new Error("Error closing Arango database", err);
40+
}
41+
};
42+
43+
arangoModule.createAccount = async (account) => {
44+
if (!account) return;
45+
const { username, dbPassword } = account;
46+
if (!username || !dbPassword) return;
47+
if (await arangoModule.checkIfDatabaseExists(username)) return;
48+
49+
try {
50+
await db.createDatabase(username, {
51+
users: [{ username, password: dbPassword }],
52+
});
53+
logger.info(`Successfully created Arango user and database ${username}`);
54+
} catch (err) {
55+
logger.error(err);
56+
throw new Error("Error in creating arango database :(", err);
57+
}
58+
};
59+
60+
arangoModule.deleteAccount = async (username) => {
61+
if (!username) return;
62+
if (!(await arangoModule.checkIfDatabaseExists(username))) return;
63+
64+
try {
65+
// grabs JWT token for use in second fetch
66+
const {
67+
jwt,
68+
} = await sendFetch(
69+
`${process.env.ARANGO_URL}_db/_system/_open/auth`,
70+
"post",
71+
{ username: process.env.ARANGO_USER, password: process.env.ARANGO_PW }
72+
);
73+
74+
// uses jwt token to authenticate request to delete user from arango database
75+
await sendFetch(
76+
`${process.env.ARANGO_URL}_db/_system/_api/user/${username}`,
77+
"delete",
78+
null,
79+
`bearer ${jwt}`
80+
);
81+
82+
logger.info(`Successfully deleted Arango user ${username}`);
83+
// deletes database(username and database names are the same for each user)
84+
await db.dropDatabase(username);
85+
logger.info(`Successfully deleted Arango database ${username}`);
86+
} catch (err) {
87+
logger.error(err);
88+
throw new Error(
89+
"Sum ting wong... deletion of arango user has failed.",
90+
err
91+
);
92+
}
93+
};
94+
95+
module.exports = arangoModule;

database/arango/arango.test.js

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
jest.mock("arangojs");
2+
jest.mock("../../lib/sendFetch");
3+
const { sendFetch } = require("../../lib/sendFetch");
4+
const Arango = require("arangojs");
5+
jest.mock("../../lib/log");
6+
const logGen = require("../../lib/log");
7+
const logger = {
8+
info: jest.fn(),
9+
error: jest.fn(),
10+
};
11+
logGen.mockReturnValue(logger);
12+
jest.mock("dotenv");
13+
Arango.Database = jest.fn().mockReturnValue({
14+
close: jest.fn(),
15+
createDatabase: jest.fn(),
16+
});
17+
18+
const {
19+
startArangoDB,
20+
closeArangoDB,
21+
createAccount,
22+
deleteAccount,
23+
checkIfDatabaseExists,
24+
} = require("./arango");
25+
26+
describe("ArangoDB functions", () => {
27+
beforeEach(() => {
28+
jest.clearAllMocks();
29+
});
30+
31+
test("should call Database function", () => {
32+
startArangoDB();
33+
expect(Arango.Database).toHaveBeenCalledTimes(1);
34+
});
35+
36+
test("should retun false", async () => {
37+
const db = new Arango.Database();
38+
db.databases = jest.fn().mockReturnValue([{ _name: "lol" }]);
39+
const res = await checkIfDatabaseExists("hi");
40+
expect(res).toEqual(false);
41+
});
42+
43+
test("should retun true", async () => {
44+
const db = new Arango.Database();
45+
db.databases = jest.fn().mockReturnValue([{ _name: "lol" }]);
46+
const res = await checkIfDatabaseExists("lol");
47+
expect(res).toEqual(true);
48+
});
49+
50+
test("should call db.close function", () => {
51+
const db = new Arango.Database();
52+
closeArangoDB();
53+
expect(db.close).toHaveBeenCalledTimes(1);
54+
});
55+
56+
test("should call db.close function and log error", () => {
57+
const db = new Arango.Database();
58+
db.close = jest.fn().mockImplementation(() => {
59+
throw new Error();
60+
});
61+
try {
62+
closeArangoDB();
63+
} catch (err) {}
64+
expect(db.close).toHaveBeenCalledTimes(1);
65+
expect(logger.error).toHaveBeenCalledTimes(1);
66+
});
67+
68+
test("should call db.createDatabase function, logger.error when theres an \
69+
error, and do nothing if argument is invalid", async () => {
70+
const db = new Arango.Database();
71+
await createAccount();
72+
expect(db.createDatabase).toHaveBeenCalledTimes(0);
73+
expect(logger.error).toHaveBeenCalledTimes(0);
74+
75+
db.databases = jest.fn().mockReturnValue([{ _name: "lol" }]);
76+
await createAccount({ username: "lol", dbPassword: "hi" });
77+
expect(db.createDatabase).toHaveBeenCalledTimes(0);
78+
79+
db.databases = jest.fn().mockReturnValue([{ _name: "lol" }]);
80+
await createAccount({ username: "", dbPassword: "" });
81+
expect(db.createDatabase).toHaveBeenCalledTimes(0);
82+
83+
try {
84+
db.databases = jest.fn().mockReturnValue([{ _name: "lol" }]);
85+
await createAccount({ username: "hi", dbPassword: "hi" });
86+
expect(db.createDatabase).toHaveBeenCalledTimes(1);
87+
88+
db.createDatabase = jest.fn().mockImplementation(() => {
89+
throw new Error();
90+
});
91+
await createAccount({ username: "hi", dbPassword: "hi" });
92+
} catch (err) {}
93+
expect(logger.error).toHaveBeenCalledTimes(1);
94+
});
95+
96+
test("should send two fetch requests if successful, logger.error when theres \
97+
an error, and do nothing if argument is invalid", async () => {
98+
const db = new Arango.Database();
99+
await deleteAccount();
100+
expect(sendFetch).toHaveBeenCalledTimes(0);
101+
expect(logger.error).toHaveBeenCalledTimes(0);
102+
103+
db.databases = jest.fn().mockReturnValue([{ _name: "lol" }]);
104+
sendFetch.mockReturnValue({ jwt: "lol" });
105+
await deleteAccount("hi");
106+
expect(sendFetch).toHaveBeenCalledTimes(0);
107+
108+
db.dropDatabase = jest.fn().mockReturnValue("lol");
109+
await deleteAccount("lol");
110+
expect(sendFetch).toHaveBeenCalledTimes(2);
111+
expect(db.dropDatabase).toHaveBeenCalledTimes(1);
112+
113+
try {
114+
sendFetch.mockImplementation(() => {
115+
throw new Error();
116+
});
117+
await deleteAccount("lol");
118+
} catch (err) {}
119+
expect(logger.error).toHaveBeenCalledTimes(1);
120+
});
121+
122+
test("should call Database function and FAIL", () => {
123+
Arango.Database.mockImplementation(() => {
124+
throw new Error();
125+
});
126+
startArangoDB();
127+
expect(logger.error).toHaveBeenCalledTimes(1);
128+
});
129+
});

database/elasticsearch/elastic.js

Lines changed: 31 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,20 @@
1-
const fetch = require("node-fetch");
21
const logger = require("./../../lib/log")(__filename);
32
require("dotenv").config();
4-
3+
const { sendFetch } = require("../../lib/sendFetch");
54
const es = {};
65

76
const ES_HOST = process.env.ES_HOST || "http://127.0.0.1:9200";
87
const authorization =
98
"Basic " +
109
Buffer.from(`elastic:${process.env.ES_PASSWORD}`).toString("base64");
1110

12-
const sendESRequest = (path, method, body) => {
13-
const options = {
14-
method,
15-
headers: {
16-
Authorization: authorization,
17-
"content-type": "application/json",
18-
},
19-
};
20-
if (body) {
21-
options.body = JSON.stringify(body);
22-
}
23-
return fetch(`${ES_HOST}${path}`, options).then((r) => r.json());
24-
};
25-
2611
es.createAccount = async (account) => {
2712
if (!account.username || !account.dbPassword) {
2813
logger.error("Account data is invalid");
2914
throw new Error("Account data is invalid");
3015
}
31-
const r1 = await sendESRequest(
32-
`/_security/role/${account.username}`,
16+
const r1 = await sendFetch(
17+
`${ES_HOST}/_security/role/${account.username}`,
3318
"POST",
3419
{
3520
indices: [
@@ -38,21 +23,28 @@ es.createAccount = async (account) => {
3823
privileges: ["all"],
3924
},
4025
],
41-
}
26+
},
27+
authorization
4228
);
43-
const r2 = await sendESRequest(
44-
`/_security/user/${account.username}`,
29+
const r2 = await sendFetch(
30+
`${ES_HOST}/_security/user/${account.username}`,
4531
"POST",
4632
{
4733
email: account.email,
4834
password: account.dbPassword,
4935
roles: [account.username],
50-
}
36+
},
37+
authorization
38+
);
39+
const r3 = await sendFetch(
40+
`${ES_HOST}/${account.username}-example/_doc`,
41+
"POST",
42+
{
43+
message:
44+
"Congratulations! You have created your first index at Elasticsearch!",
45+
},
46+
authorization
5147
);
52-
const r3 = await sendESRequest(`/${account.username}-example/_doc`, "POST", {
53-
message:
54-
"Congratulations! You have created your first index at Elasticsearch!",
55-
});
5648
const err = r1.error || r2.error || r3.error;
5749
if (err) {
5850
logger.error(err);
@@ -75,15 +67,19 @@ es.deleteAccount = async (account) => {
7567
logger.error("Account data is invalid");
7668
throw new Error("Account data is invalid");
7769
}
78-
const r1 = await sendESRequest(
79-
`/_security/user/${account.username}`,
80-
"DELETE"
70+
const r1 = await sendFetch(
71+
`${ES_HOST}/_security/user/${account.username}`,
72+
"DELETE",
73+
null,
74+
authorization
8175
);
82-
const r2 = await sendESRequest(
83-
`/_security/role/${account.username}`,
84-
"DELETE"
76+
const r2 = await sendFetch(
77+
`${ES_HOST}/_security/role/${account.username}`,
78+
"DELETE",
79+
null,
80+
authorization
8581
);
86-
const r3 = await sendESRequest(`/${account.username}-*`, "DELETE");
82+
const r3 = await sendFetch(`/${account.username}-*`, "DELETE", null, authorization);
8783
const err = r1.error || r2.error;
8884
if (err || !r1.found || !r2.found) {
8985
logger.error("Deleting Elasticsearch user failed");
@@ -100,7 +96,8 @@ es.checkAccount = async (account) => {
10096
logger.error("Account data is invalid");
10197
throw new Error("Account data is invalid");
10298
}
103-
const r1 = await sendESRequest(`/_security/user/${username}`, "GET");
99+
100+
const r1 = await sendFetch(`${ES_HOST}/_security/user/${username}`, "GET", null, authorization);
104101
logger.info(
105102
`Checking Elasticsearch account for ${username} result:`,
106103
!!r1[username]

database/neo4j/neo4j.js

Lines changed: 0 additions & 20 deletions
This file was deleted.

database/neo4j/neo4j.test.js

Lines changed: 0 additions & 24 deletions
This file was deleted.

0 commit comments

Comments
 (0)