Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
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
7 changes: 1 addition & 6 deletions src/bots/slackbot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,12 +218,7 @@ export namespace SlackBot {

export function notifyOfNewLock(projectName: string) {
DB.allNotifiableEvaluators((user) => {
SlackBot.sendMessage(
user,
`A new Peer++ evaluation for the project \`${projectName}\` is ready.` +
`Use the command \`/book\` to book it.` +
`Use the command \`/notify-off\` to stop receiving these notifications.`
);
SlackBot.sendMessage(user, `A new Peer++ evaluation for \`${projectName}\` is ready to be booked`);
});
}

Expand Down
73 changes: 29 additions & 44 deletions src/bots/webhook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { getFullUser } from "../utils/user";
import { IntraWebhook } from "../utils/types";
import Logger, { LogType } from "../utils/logger";
import { Request, Response, NextFunction } from "express";
import * as Checks from "../checks/index";
import * as Checks from "../checks/evaluators";
import db from "../db";

/*============================================================================*/
Expand Down Expand Up @@ -44,28 +44,32 @@ async function filterAlreadyDeliveredWebhook(req: Request) {
}

/**
* Filters for requests and sends back corresponding error.
* @param req The incoming request.
* Filters for requests and, if required sends corresponding error.
* @param secret The Webhook secret.
* @returns Status code and error message.
*/
async function filterHook(req: Request, secret: string) {
if (!req.is("application/json")) {
return { code: 400, msg: "Content-Type is not application/json" };
}
if (!req.headers["x-delivery"]) {
return { code: 400, msg: "X-Delivery header missing" };
}
if (!req.headers["x-secret"]) {
return { code: 400, msg: "X-Secret header missing" };
}
if (req.headers["x-secret"] !== secret) {
return { code: 412, msg: "X-Secret header incorrect" };
}
if (await filterAlreadyDeliveredWebhook(req)) {
return { code: 200, msg: "Webhook already received" };
}
return null;
function filterHook(secret: string): (req: Request, res: Response, next: NextFunction) => Promise<void> {
return async (req: Request, res: Response, next: NextFunction) => {
let filter: { code: number; msg: string } | undefined = undefined;

if (!req.is("application/json")) {
filter = { code: 400, msg: "Content-Type is not application/json" };
} else if (!req.headers["x-delivery"]) {
filter = { code: 400, msg: "X-Delivery header missing" };
} else if (!req.headers["x-secret"]) {
filter = { code: 400, msg: "X-Secret header missing" };
} else if (req.headers["x-secret"] !== secret) {
filter = { code: 412, msg: "X-Secret header incorrect" };
} else if (await filterAlreadyDeliveredWebhook(req)) {
filter = { code: 200, msg: "Webhook already delivered" };
}

if (filter) {
Logger.log(`Webhook filtered: ${JSON.stringify(filter)}`);
res.status(filter.code).send(filter.msg);
} else {
next();
}
};
}

/**
Expand Down Expand Up @@ -127,7 +131,7 @@ export namespace Webhook {

// NOTE (W2): Completely fucked up and weird endpoint btw.
const teamUsers = await Intra.getTeamUsers(hook.team.id);
return (await Checks.Evaluators(hook, evaluations, teamUsers)) || (await Checks.Random());
return (await Checks.evaluators(hook, evaluations, teamUsers)) || Checks.random();
}

/**
Expand Down Expand Up @@ -168,15 +172,8 @@ webhookApp.use((err: any, req: Request, res: Response, next: NextFunction) => {
// TODO: Figure out how evaluation points should be handled.

// Runs whenever a ScaleTeam / Evaluation is created.
webhookApp.post("/create", async (req: Request, res: Response) => {
Logger.log(JSON.stringify(req.headers));
webhookApp.post("/create", filterHook(Env.WEBHOOK_CREATE_SECRET), async (req: Request, res: Response) => {
const hook: IntraWebhook.Root = req.body;
const filter = await filterHook(req, Env.WEBHOOK_CREATE_SECRET);

if (filter) {
res.status(filter.code).send(filter.msg);
return Logger.log(`Webhook: ${filter}`);
}

Logger.log(`Evaluation created: ${hook.team.name} -> ${hook.project.name}`);
if (await blockPotentialEvaluation(hook)) {
Expand Down Expand Up @@ -222,14 +219,8 @@ webhookApp.post("/create", async (req: Request, res: Response) => {
/*============================================================================*/

// Runs whenever a ScaleTeam / Evaluation is destroyed.
webhookApp.post("/delete", async (req: Request, res: Response) => {
Logger.log(JSON.stringify(req.headers));
webhookApp.post("/delete", filterHook(Env.WEBHOOK_DELETE_SECRET), async (req: Request, res: Response) => {
const hook: IntraWebhook.Root = req.body;
const filter = await filterHook(req, Env.WEBHOOK_DELETE_SECRET);
if (filter) {
res.status(filter.code).send(filter.msg);
return Logger.log(`Webhook: ${filter}`);
}

Logger.log(`Evaluation destroyed: ${hook.team.name} -> ${hook.project.name} -> ID: ${req.headers["x-delivery"]}`);
if (hook.user && hook.user.id != Config.botID) {
Expand Down Expand Up @@ -262,14 +253,8 @@ webhookApp.post("/delete", async (req: Request, res: Response) => {
/*============================================================================*/

// Runs whenever a ScaleTeam / Evaluation is changed in some way.
webhookApp.post("/update", async (req: Request, res: Response) => {
Logger.log(JSON.stringify(req.headers));
webhookApp.post("/update", filterHook(Env.WEBHOOK_UPDATE_SECRET), async (req: Request, res: Response) => {
const hook: IntraWebhook.Root = req.body;
const filter = await filterHook(req, Env.WEBHOOK_UPDATE_SECRET);
if (filter) {
res.status(filter.code).send(filter.msg);
return Logger.log(`Webhook: ${filter}`);
}

Logger.log(`Evaluation update: ${hook.team.name} -> ${hook.project.name} -> ID: ${req.headers["x-delivery"]}`);

Expand Down
6 changes: 5 additions & 1 deletion src/checks/evaluators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { getFullUser, User } from "../utils/user";
* @param evaluations The evaluations of the project.
* @return True if an evaluation is required, else false.
*/
export async function Evaluators(hook: IntraWebhook.Root, evaluations: Intra.ScaleTeam[], teamUsers: IntraResponse.TeamUser[]) {
export async function evaluators(hook: IntraWebhook.Root, evaluations: Intra.ScaleTeam[], teamUsers: IntraResponse.TeamUser[]) {
const leaderData = teamUsers.find((value) => value.leader == true)!;

let levels: number[] = [];
Expand Down Expand Up @@ -49,3 +49,7 @@ export async function Evaluators(hook: IntraWebhook.Root, evaluations: Intra.Sca
}
return true;
}

export function random() {
return Math.random() < Config.randomEvalChance / 100;
}
Comment on lines +52 to +55
Copy link
Contributor

Choose a reason for hiding this comment

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

Why ? The whole idea is to separate all potential checks into their own files to avoid cluttering them into a giant file like this.

Copy link
Contributor

Choose a reason for hiding this comment

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

Also please start using docs.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll add comments
And I moved it to a single file because they're all part of the same Checks namespace
Otherwise you have 2 files with < 5 LOC in it
Now we have one file with 55 lines
Much cleaner

9 changes: 0 additions & 9 deletions src/checks/index.ts

This file was deleted.

18 changes: 0 additions & 18 deletions src/checks/random.ts

This file was deleted.

50 changes: 23 additions & 27 deletions src/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,55 +11,51 @@ import { User } from "./utils/user";
/*============================================================================*/

async function dbRun(query: string): Promise<void> {
return new Promise((resolve, reject) => {
db.run(query, (err) => (err ? reject(err) : resolve()));
return await new Promise((resolve, reject) => {
db.run(query, (err) => {
if (err) {
err.message = `Query failed: "${query}"\n${err.message}`;
return reject(err);
}
resolve();
});
});
}

async function dbGet<T>(query: string): Promise<Partial<T>> {
async function dbGet<T>(query: string): Promise<T> {
return new Promise((resolve, reject) => {
db.get(query, (err, t) => (err ? reject(err) : resolve(t)));
db.get(query, (err, t) => {
if (err) {
err.message = `Query failed: "${query}"\n${err.message}`;
return reject(err);
}
resolve(t);
});
});
}

/** SQLlite3 database wrapper functions */
namespace DB {
/** Deletes all expiredTeam rows which are older than the lock days. */
export function emptyOldLocks() {
return new Promise<void>((resolve, reject) => {
db.run(`DELETE FROM expiredTeam WHERE datetime(created_at) < datetime('now', '-${Config.lockExpirationDays} days')`, (err) => {
if (err != null) return reject(`Failed to clear database: ${err}`);
return resolve();
});
});
export async function emptyOldLocks() {
await dbRun(`DELETE FROM expiredTeam WHERE datetime(created_at) < datetime('now', '-${Config.lockExpirationDays} days')`);
}

/**
* Insert the given team into the database and mark them as expired.
* @param teamID The TeamID.
*/
export function insert(teamID: number) {
return new Promise<void>((resolve, reject) => {
db.run(`INSERT INTO expiredTeam(teamID) VALUES(${teamID})`, (err) => {
if (err != null) return reject(`Failed to insert value ${teamID}: ${err}`);
return resolve();
});
});
export async function insert(teamID: number) {
await dbRun(`INSERT INTO expiredTeam(teamID) VALUES(${teamID})`);
}

/**
* Checks wether the given teamID exists in the db.
* @param teamID The TeamID.
*/
export function exists(teamID: number) {
return new Promise<boolean>((resolve, reject) => {
db.get(`SELECT COUNT(*) AS amount FROM expiredTeam WHERE teamID = ${teamID}`, (err, row) => {
if (err != null) {
return reject(`Failed to check if ${teamID} exists: ${err}`);
}
return resolve(row["amount"] > 0);
});
});
export async function exists(teamID: number) {
const team = await dbGet<{ amount: number }>(`SELECT COUNT(*) AS amount FROM expiredTeam WHERE teamID = ${teamID}`);
return team.amount > 0;
}

export async function hasWebhookDelivery(id: string): Promise<boolean> {
Expand Down