From 06d04fff50e029fa36d643aabb486a43994fda81 Mon Sep 17 00:00:00 2001 From: rijkvanzanten Date: Thu, 9 Jul 2020 17:55:10 -0400 Subject: [PATCH] Add item singleton support --- src/middleware/validate-singleton.ts | 32 ---------------------- src/routes/items.ts | 41 ++++++++++++++++++++++++++-- src/services/items.ts | 33 ++++++++++++++++++++++ src/services/settings.ts | 29 ++------------------ 4 files changed, 73 insertions(+), 62 deletions(-) delete mode 100644 src/middleware/validate-singleton.ts diff --git a/src/middleware/validate-singleton.ts b/src/middleware/validate-singleton.ts deleted file mode 100644 index 25d8b91bd5..0000000000 --- a/src/middleware/validate-singleton.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Check if you're allowed to add an additional item when POSTing to a singleton - */ - -import { RequestHandler } from 'express'; -import asyncHandler from 'express-async-handler'; -import database from '../database'; -import { ItemLimitException } from '../exceptions'; - -const validateCollection: RequestHandler = asyncHandler(async (req, res, next) => { - if (!req.collection) return next(); - - const collectionInfo = await database - .select('single') - .from('directus_collections') - .where({ collection: req.collection }) - .first(); - - if (!collectionInfo) return next(); - - if (collectionInfo.single === false) return next(); - - const { count } = await database.count('*').as('count').from(req.collection).first(); - - if (Number(count) === 0) return next(); - - throw new ItemLimitException( - `You can only create a single item in singleton "${req.collection}"` - ); -}); - -export default validateCollection; diff --git a/src/routes/items.ts b/src/routes/items.ts index b449cb176e..ca8c1ebff7 100644 --- a/src/routes/items.ts +++ b/src/routes/items.ts @@ -3,19 +3,22 @@ import asyncHandler from 'express-async-handler'; import * as ItemsService from '../services/items'; import sanitizeQuery from '../middleware/sanitize-query'; import validateCollection from '../middleware/validate-collection'; -import validateSingleton from '../middleware/validate-singleton'; import validateQuery from '../middleware/validate-query'; import * as MetaService from '../services/meta'; +import { RouteNotFoundException } from '../exceptions'; const router = express.Router(); router.post( '/:collection', validateCollection, - validateSingleton, sanitizeQuery, validateQuery, asyncHandler(async (req, res) => { + if (req.single) { + throw new RouteNotFoundException(req.path); + } + const primaryKey = await ItemsService.createItem(req.collection, req.body, { ip: req.ip, userAgent: req.get('user-agent'), @@ -35,7 +38,9 @@ router.get( validateQuery, asyncHandler(async (req, res) => { const [records, meta] = await Promise.all([ - ItemsService.readItems(req.collection, req.sanitizedQuery), + req.single + ? ItemsService.readSingleton(req.collection, req.sanitizedQuery) + : ItemsService.readItems(req.collection, req.sanitizedQuery), MetaService.getMetaForQuery(req.collection, req.sanitizedQuery), ]); @@ -52,6 +57,10 @@ router.get( sanitizeQuery, validateQuery, asyncHandler(async (req, res) => { + if (req.single) { + throw new RouteNotFoundException(req.path); + } + const record = await ItemsService.readItem( req.collection, req.params.pk, @@ -64,12 +73,38 @@ router.get( }) ); +router.patch( + '/:collection', + validateCollection, + sanitizeQuery, + validateQuery, + asyncHandler(async (req, res) => { + if (req.single === false) { + throw new RouteNotFoundException(req.path); + } + + await ItemsService.upsertSingleton(req.collection, req.body, { + ip: req.ip, + userAgent: req.get('user-agent'), + user: req.user, + }); + + const item = await ItemsService.readSingleton(req.collection, req.sanitizedQuery); + + return res.json({ data: item || null }); + }) +); + router.patch( '/:collection/:pk', validateCollection, sanitizeQuery, validateQuery, asyncHandler(async (req, res) => { + if (req.single) { + throw new RouteNotFoundException(req.path); + } + const primaryKey = await ItemsService.updateItem(req.collection, req.params.pk, req.body, { ip: req.ip, userAgent: req.get('user-agent'), diff --git a/src/services/items.ts b/src/services/items.ts index 74035360ff..74f6ea7f70 100644 --- a/src/services/items.ts +++ b/src/services/items.ts @@ -173,3 +173,36 @@ export const deleteItem = async ( .delete() .where({ [primaryKeyField]: pk }); }; + +export const readSingleton = async (collection: string, query: Query = {}) => { + const records = await readItems(collection, { ...query, limit: 1 }); + const record = records[0]; + + if (!record) { + const columns = await schemaInspector.columnInfo(collection); + const defaults = {}; + + for (const column of columns) { + defaults[column.name] = column.default_value; + } + + return defaults; + } + + return record; +}; + +export const upsertSingleton = async ( + collection: string, + data: Record, + accountability: Accountability +) => { + const primaryKeyField = await schemaInspector.primary(collection); + const record = await database.select(primaryKeyField).from(collection).limit(1).first(); + + if (record) { + return await updateItem(collection, record.id, data, accountability); + } + + return await createItem(collection, data, accountability); +}; diff --git a/src/services/settings.ts b/src/services/settings.ts index c416c98621..6d7fafd7ca 100644 --- a/src/services/settings.ts +++ b/src/services/settings.ts @@ -1,36 +1,11 @@ import { Query } from '../types/query'; import * as ItemsService from './items'; import { Accountability } from '../types'; -import database, { schemaInspector } from '../database'; export const readSettings = async (query: Query) => { - const settings = await ItemsService.readItems('directus_settings', { - ...query, - limit: 1, - }); - - const settingsObj = settings[0]; - - if (!settingsObj) { - const settingsColumns = await schemaInspector.columnInfo('directus_settings'); - const defaults = {}; - - for (const column of settingsColumns) { - defaults[column.name] = column.default_value; - } - - return defaults; - } - - return settingsObj; + return await ItemsService.readSingleton('directus_settings', query); }; export const updateSettings = async (data: Record, accountability: Accountability) => { - const record = await database.select('id').from('directus_settings').limit(1).first(); - - if (record) { - return await ItemsService.updateItem('directus_settings', record.id, data, accountability); - } - - return await ItemsService.createItem('directus_settings', data, accountability); + return await ItemsService.upsertSingleton('directus_settings', data, accountability); };