shack/src/shelves.js

142 lines
3.1 KiB
JavaScript

import knex from './knex';
import { HttpError } from './errors';
function curateDatabaseShelf(shelf) {
if (!shelf) {
return null;
}
return {
id: shelf.id,
slug: shelf.slug,
name: shelf.slug,
subscribed: !!shelf.subscribed,
};
}
function identityQuery(builder, shelfId) {
const id = Number(shelfId);
if (Number.isNaN(id)) {
builder.where('slug', shelfId);
return;
}
builder.where('id', shelfId);
}
function identitiesQuery(builder, shelfIds) {
const ids = Array.from(new Set(shelfIds.filter((shelfId) => !Number.isNaN(Number(shelfId)))));
const slugs = Array.from(new Set(shelfIds.filter((shelfId) => Number.isNaN(Number(shelfId)))));
builder
.whereIn('id', ids)
.orWhereIn('slug', slugs);
}
function isMemberQuery(builder, user) {
if (user) {
builder.select(knex.raw('shelves_subscriptions.id IS NOT NULL as subscribed'));
builder.leftJoin('shelves_subscriptions', (joinBuilder) => {
joinBuilder.on('shelf_id', 'shelves.id');
joinBuilder.andOnVal('user_id', user.id);
});
}
}
async function fetchShelf(shelfId, { user } = {}) {
const shelfEntry = await knex('shelves')
.select('shelves.*')
.modify((builder) => isMemberQuery(builder, user))
.where((builder) => identityQuery(builder, shelfId))
.first();
return curateDatabaseShelf(shelfEntry);
}
async function fetchAllShelves({ user, limit = 100 } = {}) {
const shelfEntries = await knex('shelves')
.select('shelves.*')
.modify((builder) => isMemberQuery(builder, user))
.orderBy('slug', 'asc')
.limit(limit);
return shelfEntries.map((shelfEntry) => curateDatabaseShelf(shelfEntry));
}
async function fetchShelves(shelfIds, { user } = {}) {
const shelfEntries = await knex('shelves')
.select('shelves.*')
.where((builder) => identitiesQuery(builder, shelfIds))
.modify((builder) => isMemberQuery(builder, user));
return Object.fromEntries(shelfEntries.map((shelfEntry) => [shelfEntry.id, curateDatabaseShelf(shelfEntry)]));
}
async function createShelf(shelf, user) {
const [shelfEntry] = await knex('shelves')
.insert({
slug: shelf.slug,
founder_id: user.id,
})
.returning('*');
return curateDatabaseShelf(shelfEntry);
}
async function subscribe(shelfId, user) {
const shelf = await fetchShelf(shelfId);
if (!shelf) {
throw new HttpError({
statusMessage: `Shelf ${shelfId} does not exist`,
statusCode: 404,
});
}
try {
await knex('shelves_subscriptions').insert({
shelf_id: shelf.id,
user_id: user.id,
});
} catch (error) {
if (error.code === '23505') {
throw new HttpError({
statusMessage: `You are already subscribed to s/${shelf.slug}`,
statusCode: 409,
});
}
throw error;
}
}
async function unsubscribe(shelfId, user) {
const shelf = await fetchShelf(shelfId);
if (!shelf) {
throw new HttpError({
statusMessage: `Shelf ${shelfId} does not exist`,
statusCode: 404,
});
}
await knex('shelves_subscriptions')
.where({
shelf_id: shelf.id,
user_id: user.id,
})
.delete();
}
export {
fetchShelf,
fetchShelves,
fetchAllShelves,
createShelf,
curateDatabaseShelf,
subscribe,
unsubscribe,
};