Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions database/influx/influx.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
const Influx = require("influx");
const logger = require("./../../lib/log")(__filename);
require("dotenv").config();

const influxModule = {};
let client;
const HOST = process.env.INF_HOST||"localhost";
const PORT = process.env.INF_PORT||8086;
influxModule.startInflux = () => {
client = new Influx.InfluxDB({
host: HOST,
port: PORT,
});
logger.info("Sucessfully started InfluxDB");
};

influxModule.createAccount = async (account) => {
const { username, dbPassword } = account;
if (!username || !dbPassword) return;
try {
await client.createUser(username, dbPassword);
await client.createDatabase(username);
await client.grantPrivilege(username, "WRITE", username);
logger.info(
`Sucessfully created new user and database with ${username} name`
);
} catch (err) {
logger.error(err);
throw new Error(`failed to create new influx user with ${username} name`);
}
};
influxModule.deleteAccount = async (account) => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function only uses username, so why is the entire account object needed?

const { username } = account;
if (!username) return;
try {
await client.dropUser(username);
await client.dropDatabase(username);
logger.info(`Sucessfully deleted account with ${username} name`);
} catch (err) {
logger.error(err);
throw new Error(`failed to delete influx ${username} account`);
}
};
influxModule.checkAccount = async (username) => {
if (!username) return;
try {
const users = await client.getUsers();
const user = users.filter((u) => u === username)[0];
logger.info(
user
? `Found ${user} account with ${username}`
: `No account with ${username} were found`
);
return Boolean(user);
} catch (err) {
logger.error(err);
throw new Error(`failed to check for influx ${username} account`);
}
};

module.exports = influxModule;
107 changes: 107 additions & 0 deletions database/influx/influx.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
jest.mock("../../lib/log");
const logGen = require("../../lib/log");
const logger = { error: jest.fn(), info: jest.fn() };
logGen.mockReturnValue(logger);
jest.mock("dotenv");
require("dotenv").config();
jest.mock("influx");
const { InfluxDB } = require("influx");
const influx = require("influx");
const {
startInflux,
createAccount,
deleteAccount,
checkAccount,
} = require("./influx");
describe("test InfluxDB", () => {
beforeEach(() => {
jest.clearAllMocks();
});
const mockClient = {
InfluxDB: jest.fn(),
createUser: jest.fn(),
createDatabase: jest.fn(),
grantPrivilege: jest.fn(),
dropUser: jest.fn(),
dropDatabase: jest.fn(),
getUsers: jest.fn(),
};
const user = { username: "username", dbPassword: "password" };
InfluxDB.mockImplementation(function () {
return mockClient;
});
it("should start influx client", () => {
startInflux();
expect(influx.InfluxDB).toHaveBeenCalledTimes(1);
});
it("should use global variables", () => {
startInflux();
expect(influx.InfluxDB.mock.calls[0][0].host).toEqual("localhost");
expect(influx.InfluxDB.mock.calls[0][0].port).toEqual(8086);
});
describe("createAccount", () => {
it("should sucessfully create new account", async () => {
await createAccount(user);
expect(mockClient.createUser).toHaveBeenCalledTimes(1);
expect(mockClient.createDatabase).toHaveBeenCalledTimes(1);
expect(mockClient.grantPrivilege).toHaveBeenCalledTimes(1);
});
it("should call logger in case of an error", async () => {
try {
mockClient.createUser.mockReturnValue(Promise.reject());
expect(await createAccount(user)).rejects.toThrow();
} catch (err) {
expect(logger.error).toHaveBeenCalledTimes(1);
}
});
it("should return if no account argument was provided", async () => {
const res = await createAccount({});
expect(res).toEqual(undefined);
});
});
describe("deleteAccount", () => {
it("should sucessfully delete account", async () => {
await deleteAccount(user);
expect(mockClient.dropUser).toHaveBeenCalledTimes(1);
expect(mockClient.dropDatabase).toHaveBeenCalledTimes(1);
});
it("should throw error to invalid credentials", async () => {
try {
mockClient.dropUser.mockReturnValue(Promise.reject());
expect(await deleteAccount(user)).rejects.toThrow();
} catch (err) {
expect(logger.error).toHaveBeenCalledTimes(1);
}
});
it("should return if no username was provided", async () => {
const res = await deleteAccount({});
expect(res).toEqual(undefined);
});
});
describe("checkAccount", () => {
it("should return true if account exists", async () => {
mockClient.getUsers.mockReturnValue(["name", "username"]);
const res = await checkAccount("username");
expect(mockClient.getUsers).toHaveBeenCalledTimes(1);
expect(res).toEqual(true);
});
it("should return false if account doesnt exitsts", async () => {
mockClient.getUsers.mockReturnValue([]);
const res = await checkAccount(user);
expect(mockClient.getUsers).toHaveBeenCalledTimes(1);
expect(res).toEqual(false);
});
it("should throw an error", async () => {
try {
mockClient.getUsers.mockReturnValue(Promise.reject());
expect(await checkAccount(user)).rejects.toThrow();
} catch (err) {
expect(logger.error).toHaveBeenCalledTimes(1);
}
});
it("should return if no username was provided", async () => {
const res = await checkAccount();
expect(res).toEqual(undefined);
});
});
});
16 changes: 10 additions & 6 deletions env.example
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,20 @@ PASSWORD=***
ALTER_DB=true
#password for session-express
SECRET=***
#Neo4j graph database url
NEO4J_URL=neo4j://104.168.169.204
#Neo4j username
NEO4J_USER=***
#Neo4j password
NEO4J_PASSWORD=***
#production/development flag, in jest tests this varible is "test" by default
NODE_ENV = development
#local port, default value 3052
PORT = 3052
#hostname adress, default is https://learndatabases.dev,
#if you want to use localhost you need to specify port, for example http://localhost:4000
HOSTNAME = https://learndatabases.dev
#ArangoDB hostname
ARANGO_URL = http://localhost:8529
#ArangoDB username
ARANGO_USER = root
#ArangoDB password
ARANGO_PW = ***
#InfluxDB hostname
INF_HOST = localhost
#InfluxDB port
INF_PORT = 8086
4 changes: 4 additions & 0 deletions lib/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const db = require("../sequelize/db");
const pg = require("../database/postgres/pg");
const arango = require("../database/arango/arango");
const es = require("../database/elasticsearch/elastic");
const influx = require("../database/influx/influx");
const logger = require("./log")(__filename);

const util = {};
Expand Down Expand Up @@ -37,6 +38,9 @@ util.cleanAnonymous = async () => {
const arangoDbExists = await arango.checkIfDatabaseExists(username);
if (arangoDbExists) await arango.deleteAccount(username);

const influxDbExists = await influx.checkAccount(username);
if (influxDbExists) await influx.deleteAccount(user);
Copy link
Collaborator

@anthonykhoa anthonykhoa Oct 28, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have the choice to pass in username, so you should do that instead of user. I don't think it makes sense to pass in an object to a function that only uses one string data-type variable as an input. That is confusing -- it makes me think that the function needs something else from user other than just user.username

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Basically, this code threw me off. PG and Elastic use username for checks and user for deletions while Arango needs only username in both cases. I was trying to be consistent so I added user as argument for Influx.

    const pgDbExists = await pg.userHasPgAccount(username);
    if (pgDbExists) await pg.deletePgAccount(user);

    const esDbExists = await es.checkAccount(username);
    if (esDbExists) await es.deleteAccount(user);

    const arangoDbExists = await arango.checkIfDatabaseExists(username);
    if (arangoDbExists) await arango.deleteAccount(username);

I changed it, so deleteAccount now needs only username as argument.


return await user.destroy();
})
).then(() => {
Expand Down
7 changes: 7 additions & 0 deletions lib/util.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@ jest.mock("../sequelize/db");
jest.mock("../database/postgres/pg");
jest.mock("../database/arango/arango");
jest.mock("../database/elasticsearch/elastic");
jest.mock("../database/influx/influx");
jest.mock("./log");

const sequelize = require("sequelize");
const db = require("../sequelize/db");
const pg = require("../database/postgres/pg");
const es = require("../database/elasticsearch/elastic");
const arango = require("../database/arango/arango");
const influx = require("../database/influx/influx");

sequelize.Op = { and: "and", lt: "lt" };
const Accounts = {
Expand All @@ -21,6 +23,7 @@ db.getModels = () => {
pg.deletePgAccount = jest.fn();
es.deleteAccount = jest.fn();
arango.deleteAccount = jest.fn();
influx.deleteAccount = jest.fn();
const logGen = require("./log");
const logger = {
info: jest.fn(),
Expand Down Expand Up @@ -54,20 +57,24 @@ describe("Testing cleanAnonymous function", () => {
pg.userHasPgAccount = () => false;
es.checkAccount = () => false;
arango.checkIfDatabaseExists = () => false;
influx.checkAccount = () => false;
await util.cleanAnonymous();
expect(pg.deletePgAccount).not.toHaveBeenCalled();
expect(es.deleteAccount).not.toHaveBeenCalled();
expect(arango.deleteAccount).not.toHaveBeenCalled();
expect(influx.deleteAccount).not.toHaveBeenCalled();
});
it("should call database functions if expired accounts are found", async () => {
Accounts.findAll.mockReturnValue([{ destroy: () => {} }]);
pg.userHasPgAccount = () => true;
es.checkAccount = () => true;
arango.checkIfDatabaseExists = () => true;
influx.checkAccount = () => true;
await util.cleanAnonymous();
expect(pg.deletePgAccount).toHaveBeenCalled();
expect(es.deleteAccount).toHaveBeenCalled();
expect(arango.deleteAccount).toHaveBeenCalled();
expect(influx.deleteAccount).toHaveBeenCalled();
});
it("should call logger.error if cleaning fails", async () => {
Accounts.findAll.mockImplementation(() => {
Expand Down
5 changes: 5 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"ejs": "^3.1.3",
"express": "^4.17.1",
"express-session": "^1.17.1",
"influx": "^5.6.3",
"mailgun-js": "^0.22.0",
"mongodb": "^3.6.1",
"neo4j-driver": "^4.1.1",
Expand Down
Binary file added public/TICK.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading