Skip to content

Commit

Permalink
Refactor repository layer
Browse files Browse the repository at this point in the history
- Consistent use of arrow functions
- Simpler naming
- Extract query creation
  • Loading branch information
textbook committed Aug 3, 2023
1 parent 3ca380e commit 4d63db3
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 71 deletions.
93 changes: 64 additions & 29 deletions server/db.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
import pgSession from "connect-pg-simple";
import session, { MemoryStore } from "express-session";
import { Pool } from "pg";
import format from "pg-format";

import config from "./utils/config";
import logger from "./utils/logger";

const pool = new Pool({
connectionString: config.dbUrl,
connectionTimeoutMillis: 5_000,
ssl: config.dbUrl.includes("localhost")
? false
: { rejectUnauthorized: false },
});

/**
* Access this with `import db from "path/to/db";` then use it with
* `await db.query("<SQL>", [...<variables>])`.
Expand All @@ -20,34 +29,6 @@ export default {
},
};

/**
* {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates Tagged template}
* to turn a mutliline query in backticks into a single line.
* @example
* const myQuery = singleLine`
* SELECT *
* FROM some_table
* WHERE some_field = $1;
* `;
* @param {string} query
* @returns {string}
*/
export function singleLine([query]) {
return query
.trim()
.split("\n")
.map((line) => line.trim())
.join(" ");
}

const pool = new Pool({
connectionString: config.dbUrl,
connectionTimeoutMillis: 5_000,
ssl: config.dbUrl.includes("localhost")
? false
: { rejectUnauthorized: false },
});

export const connectDb = async () => {
let client;
try {
Expand All @@ -62,7 +43,11 @@ export const connectDb = async () => {

export const disconnectDb = () => pool.end();

export function getSessionStore() {
export const ErrorCodes = {
UNIQUE_VIOLATION: "23505",
};

export const getSessionStore = () => {
const store = config.sessionStore;
switch (store) {
case "memory":
Expand All @@ -72,4 +57,54 @@ export function getSessionStore() {
default:
throw new Error(`unknown store type: ${store}`);
}
};

/**
* Create an insert query for the given table name and columns.
* @param {string} tableName
* @param {string[]} columns
* @returns {string}
*/
export function insertQuery(tableName, columns) {
return format(
"INSERT INTO %I (%s) VALUES (%s) RETURNING *;",
tableName,
columns.map((column) => format.ident(column)).join(", "),
columns.map((_, index) => `$${index + 1}`).join(", ")
);
}

/**
* {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#tagged_templates Tagged template}
* to turn a mutliline query in backticks into a single line.
* @example
* const myQuery = singleLine`
* SELECT *
* FROM some_table
* WHERE some_field = $1;
* `;
* @param {string} query
* @returns {string}
*/
export function singleLine([query]) {
return query
.trim()
.split("\n")
.map((line) => line.trim())
.join(" ");
}
/**
* Create an update query for the given table name and columns.
* @param {string} tableName
* @param {string[]} columns
* @returns {string}
*/
export function updateQuery(tableName, columns) {
return format(
"UPDATE %I SET %s WHERE id = $1 RETURNING *;",
tableName,
columns
.map((column, index) => `${format.ident(column)} = $${index + 2}`)
.join(", ")
);
}
40 changes: 22 additions & 18 deletions server/resources/resourcesRepository.js
Original file line number Diff line number Diff line change
@@ -1,51 +1,55 @@
import db, { singleLine } from "../db";
import db, { ErrorCodes, insertQuery, singleLine, updateQuery } from "../db";

import { DuplicateResource } from "./resourcesService";

const resourceQuery = singleLine`
SELECT r.*, t.name as topic_name
FROM resources as r
LEFT JOIN topics as t
ON r.topic = t.id
`;

export const add = async ({ description, source, title, topic, url }) => {
try {
const {
rows: [created],
} = await db.query(
"INSERT INTO resources (description, source, title, topic, url) VALUES ($1, $2, $3, $4, $5) RETURNING *;",
insertQuery("resources", [
"description",
"source",
"title",
"topic",
"url",
]),
[description, source, title, topic, url]
);
return created;
} catch (err) {
if (err.code === "23505" /** unique_violation */) {
if (err.code === ErrorCodes.UNIQUE_VIOLATION) {
throw new DuplicateResource();
}
throw err;
}
};

export const findAll = async () => {
const { rows } = await db.query(resourceQuery);
return rows;
};

export const findOne = async (id) => {
const {
rows: [resource],
} = await db.query(`${resourceQuery} WHERE r.id = $1;`, [id]);
return resource;
};

export const getAll = async ({ draft }) => {
const { rows } = await db.query(
draft ? `${resourceQuery};` : `${resourceQuery} WHERE r.draft = false;`
);
return rows;
};

export const update = async (id, { draft, publication, publisher }) => {
const {
rows: [updated],
} = await db.query(
"UPDATE resources SET draft = $2, publication = $3, publisher = $4 WHERE id = $1 RETURNING *;",
updateQuery("resources", ["draft", "publication", "publisher"]),
[id, draft, publication, publisher]
);
return updated;
};

const resourceQuery = singleLine`
SELECT r.*, t.name as topic_name
FROM resources as r
LEFT JOIN topics as t
ON r.topic = t.id
`;
6 changes: 5 additions & 1 deletion server/resources/resourcesService.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ export const create = async (resource) => {
};

export async function getAll(includeDrafts) {
return await repository.getAll({ draft: includeDrafts });
const resources = await repository.findAll();
if (includeDrafts) {
return resources;
}
return resources.filter(({ draft }) => draft === false);
}

export async function publish(resourceId, publisherId) {
Expand Down
14 changes: 7 additions & 7 deletions server/topics/topicsRepository.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import db from "../db";

export async function findOne(id) {
export const findAll = async () => {
const { rows } = await db.query("SELECT * FROM topics;");
return rows;
};

export const findOne = async (id) => {
const {
rows: [topic],
} = await db.query("SELECT * FROM topics WHERE id = $1;", [id]);
return topic;
}

export async function getAll() {
const { rows } = await db.query("SELECT * FROM topics;");
return rows;
}
};
2 changes: 1 addition & 1 deletion server/topics/topicsService.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export class MissingTopic extends Error {
}

export async function getAll() {
return await repository.getAll();
return await repository.findAll();
}

export async function getById(id) {
Expand Down
29 changes: 15 additions & 14 deletions server/users/usersRepository.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
import db from "../db";
import db, { insertQuery } from "../db";

export const add = async ({ email, gitHubId, name }) => {
const {
rows: [created],
} = await db.query(
"INSERT INTO users (email, github_id, name) VALUES ($1, $2, $3) RETURNING *;",
[email, gitHubId, name]
);
} = await db.query(insertQuery("users", ["email", "github_id", "name"]), [
email,
gitHubId,
name,
]);
return created;
};

export async function findOne(id) {
export const findAll = async () => {
const { rows } = await db.query("SELECT * FROM users;");
return rows;
};

export const findOne = async (id) => {
const {
rows: [found],
} = await db.query("SELECT * FROM users WHERE id = $1;", [id]);
return found;
}
};

export const findOneByGitHubId = async (id) => {
const {
Expand All @@ -24,17 +30,12 @@ export const findOneByGitHubId = async (id) => {
return found;
};

export async function getAll() {
const { rows } = await db.query("SELECT * FROM users;");
return rows;
}

export async function update(id, { is_admin }) {
export const update = async (id, { is_admin }) => {
const {
rows: [updated],
} = await db.query(
"UPDATE users SET is_admin = $2 WHERE id = $1 RETURNING *;",
[id, is_admin]
);
return updated;
}
};
2 changes: 1 addition & 1 deletion server/users/usersService.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export async function findByGitHubId(id) {
}

export async function getAll() {
return await repository.getAll();
return await repository.findAll();
}

export async function getById(id) {
Expand Down

0 comments on commit 4d63db3

Please sign in to comment.