Allow read processing + move write processing to items service

This commit is contained in:
rijkvanzanten
2020-07-07 16:19:54 -04:00
parent a034e44775
commit f3264859aa
6 changed files with 74 additions and 65 deletions

View File

@@ -5,7 +5,6 @@ import validateQuery from '../middleware/validate-query';
import useCollection from '../middleware/use-collection';
import * as FoldersService from '../services/folders';
import * as ActivityService from '../services/activity';
import * as PayloadService from '../services/payload';
const router = express.Router();
@@ -13,8 +12,7 @@ router.post(
'/',
useCollection('directus_folders'),
asyncHandler(async (req, res) => {
const payload = await PayloadService.processValues('create', req.collection, req.body);
const record = await FoldersService.createFolder(payload, req.sanitizedQuery);
const record = await FoldersService.createFolder(req.body, req.sanitizedQuery);
ActivityService.createActivity({
action: ActivityService.Action.CREATE,
@@ -55,11 +53,9 @@ router.patch(
'/:pk',
useCollection('directus_folders'),
asyncHandler(async (req, res) => {
const payload = await PayloadService.processValues('create', req.collection, req.body);
const record = await FoldersService.updateFolder(
req.params.pk,
payload,
req.body,
req.sanitizedQuery
);

View File

@@ -6,7 +6,6 @@ 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 * as PayloadService from '../services/payload';
import * as ActivityService from '../services/activity';
const router = express.Router();
@@ -16,8 +15,7 @@ router.post(
validateCollection,
validateSingleton,
asyncHandler(async (req, res) => {
const payload = await PayloadService.processValues('create', req.collection, req.body);
const item = await createItem(req.params.collection, payload);
const item = await createItem(req.params.collection, req.body);
ActivityService.createActivity({
action: ActivityService.Action.CREATE,
@@ -67,8 +65,7 @@ router.patch(
'/:collection/:pk',
validateCollection,
asyncHandler(async (req, res) => {
const payload = await PayloadService.processValues('update', req.collection, req.body);
const item = await updateItem(req.params.collection, req.params.pk, payload);
const item = await updateItem(req.params.collection, req.params.pk, req.body);
ActivityService.createActivity({
action: ActivityService.Action.UPDATE,

View File

@@ -1,7 +1,6 @@
import { Query } from '../types/query';
import * as ItemsService from './items';
import storage from '../storage';
import * as PayloadService from './payload';
import database from '../database';
import logger from '../logger';
import sharp from 'sharp';
@@ -9,14 +8,19 @@ import { parse as parseICC } from 'icc';
import parseEXIF from 'exif-reader';
import parseIPTC from '../utils/parse-iptc';
import path from 'path';
import { SYSTEM_ASSET_WHITELIST } from '../constants';
import { v4 as uuidv4 } from 'uuid';
export const createFile = async (
data: Record<string, any>,
stream: NodeJS.ReadableStream,
query?: Query
) => {
const payload = await PayloadService.processValues('create', 'directus_files', data);
const id = uuidv4();
const payload: Record<string, any> = {
...data,
id,
};
payload.filename_disk = payload.id + path.extname(payload.filename_disk);
@@ -55,29 +59,7 @@ export const readFiles = async (query: Query) => {
};
export const readFile = async (pk: string | number, query: Query) => {
const file = await ItemsService.readItem('directus_files', pk, query);
const { asset_allowlist: assetAllowlist } =
(await database.select('asset_allowlist').from('directus_settings').first()) || {};
const assetSizes = [...SYSTEM_ASSET_WHITELIST, ...(assetAllowlist || [])];
file.links = {
asset_url: new URL(`/assets/${file.id}`, process.env.PUBLIC_URL),
/** @TODO confirm is public url is set before returning */
original_url: new URL(
file.filename_disk,
process.env[`STORAGE_${file.storage.toUpperCase()}_PUBLIC_URL`]
),
thumbnails: assetSizes.map((size) => {
return {
...size,
url: new URL(`/assets/${file.id}?key=${size.key}`, process.env.PUBLIC_URL),
};
}),
};
return file;
return await ItemsService.readItem('directus_files', pk, query);
};
// @todo Add query support
@@ -87,8 +69,6 @@ export const updateFile = async (
stream?: NodeJS.ReadableStream,
query?: Query
) => {
const payload = await PayloadService.processValues('update', 'directus_files', data);
/**
* @TODO
* Handle changes in storage adapter -> going from local to S3 needs to delete from one, upload to the other
@@ -110,7 +90,7 @@ export const updateFile = async (
await storage.disk(file.storage).put(file.filename_disk, stream as any);
}
return await ItemsService.updateItem('directus_files', pk, payload, query);
return await ItemsService.updateItem('directus_files', pk, data, query);
};
export const deleteFile = async (pk: string | number) => {

View File

@@ -2,14 +2,16 @@ import database, { schemaInspector } from '../database';
import { Query } from '../types/query';
import runAST from '../database/run-ast';
import getAST from '../utils/get-ast';
import * as PayloadService from './payload';
export const createItem = async (
collection: string,
data: Record<string, any>,
query: Query = {}
) => {
const payload = await PayloadService.processValues('create', collection, data);
const primaryKeyField = await schemaInspector.primary(collection);
const result = await database(collection).insert(data).returning(primaryKeyField);
const result = await database(collection).insert(payload).returning(primaryKeyField);
return readItem(collection, result[0], query);
};
@@ -19,7 +21,7 @@ export const readItems = async <T = Record<string, any>>(
): Promise<T[]> => {
const ast = await getAST(collection, query);
const records = await runAST(ast);
return records;
return await PayloadService.processValues('read', collection, records);
};
export const readItem = async <T = any>(
@@ -43,7 +45,7 @@ export const readItem = async <T = any>(
const ast = await getAST(collection, query);
const records = await runAST(ast);
return records[0];
return await PayloadService.processValues('read', collection, records[0]);
};
export const updateItem = async (
@@ -52,9 +54,10 @@ export const updateItem = async (
data: Record<string, any>,
query: Query = {}
) => {
const payload = await PayloadService.processValues('create', collection, data);
const primaryKeyField = await schemaInspector.primary(collection);
const result = await database(collection)
.update(data)
.update(payload)
.where({ [primaryKeyField]: pk })
.returning('id');
return readItem(collection, result[0], query);

View File

@@ -9,6 +9,8 @@ import { v4 as uuidv4 } from 'uuid';
import database from '../database';
import { clone } from 'lodash';
type Operation = 'create' | 'read' | 'update';
/**
* Process and update all the special fields in the given payload
*
@@ -18,11 +20,15 @@ import { clone } from 'lodash';
* @returns The updated payload
*/
export const processValues = async (
operation: 'create' | 'update',
operation: Operation,
collection: string,
payload: Record<string, any>
payload: Record<string, any> | Record<string, any>[]
) => {
const processedPayload = clone(payload);
let processedPayload = clone(payload);
if (Array.isArray(payload) === false) {
processedPayload = [processedPayload];
}
const specialFieldsInCollection = await database
.select('field', 'special')
@@ -30,8 +36,19 @@ export const processValues = async (
.where({ collection: collection })
.whereNotNull('special');
for (const field of specialFieldsInCollection) {
processedPayload[field.field] = await processField(field, processedPayload, operation);
await Promise.all(
processedPayload.map(async (record: any) => {
await Promise.all(
specialFieldsInCollection.map(async (field) => {
record[field.field] = await processField(field, processedPayload, operation);
})
);
})
);
// Return the payload in it's original format
if (Array.isArray(payload) === false) {
return processedPayload[0];
}
return processedPayload;
@@ -40,24 +57,47 @@ export const processValues = async (
async function processField(
field: Pick<System, 'field' | 'special'>,
payload: Record<string, any>,
operation: 'create' | 'update'
operation: Operation
) {
switch (field.special) {
case 'hash':
return await genHash(payload[field.field]);
return await genHash(operation, payload[field.field]);
case 'uuid':
return await genUUID(operation);
return await genUUID(operation, payload[field.field]);
case 'file-links':
return await genFileLinks(operation, payload[field.field]);
default:
return payload[field.field];
}
}
async function genHash(value?: string | number) {
/**
* @note The following functions can be called _a lot_. Make sure to utilize some form of caching
* if you have to rely on heavy operations
*/
async function genHash(operation: Operation, value?: any) {
if (!value) return;
return await argon2.hash(String(value));
if (operation === 'create' || operation === 'update') {
return await argon2.hash(String(value));
}
return value;
}
async function genUUID(operation: 'create' | 'update') {
if (operation === 'create') {
async function genUUID(operation: Operation, value?: any) {
if (operation === 'create' && !value) {
return uuidv4();
}
return value;
}
async function genFileLinks(operation: Operation, value?: any) {
if (operation === 'read') {
return 'test';
}
return value;
}

View File

@@ -4,12 +4,10 @@ import jwt from 'jsonwebtoken';
import { sendInviteMail } from '../mail';
import database from '../database';
import argon2 from 'argon2';
import * as PayloadService from '../services/payload';
import { InvalidPayloadException } from '../exceptions';
export const createUser = async (data: Record<string, any>, query?: Query) => {
const payload = await PayloadService.processValues('create', 'directus_users', data);
return await ItemsService.createItem('directus_users', payload, query);
return await ItemsService.createItem('directus_users', data, query);
};
export const readUsers = async (query?: Query) => {
@@ -35,12 +33,7 @@ export const deleteUser = async (pk: string | number) => {
};
export const inviteUser = async (email: string, role: string) => {
const userPayload = await PayloadService.processValues('create', 'directus_users', {
email,
role,
status: 'invited',
});
await createUser(userPayload);
await createUser({ email, role, status: 'invited' });
const payload = { email };
const token = jwt.sign(payload, process.env.SECRET, { expiresIn: '7d' });