removes autopopulate, adds manual population
This commit is contained in:
@@ -5,7 +5,8 @@ module.exports = {
|
||||
label: 'Global with Strict Access',
|
||||
access: {
|
||||
update: ({ req: { user } }) => checkRole(['admin'], user),
|
||||
read: ({ req: { user } }) => checkRole(['admin'], user),
|
||||
// read: ({ req: { user } }) => checkRole(['admin'], user),
|
||||
read: () => true,
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
|
||||
@@ -66,7 +66,6 @@
|
||||
"mkdirp": "^0.5.1",
|
||||
"mongodb-memory-server": "^6.5.2",
|
||||
"mongoose": "^5.8.9",
|
||||
"mongoose-autopopulate": "^0.11.0",
|
||||
"mongoose-hidden": "^1.8.1",
|
||||
"mongoose-paginate-v2": "^1.3.6",
|
||||
"node-sass": "^4.13.1",
|
||||
|
||||
@@ -5,7 +5,7 @@ const executeAccess = async (operation, access) => {
|
||||
const result = await access(operation);
|
||||
|
||||
if (!result) {
|
||||
throw new Forbidden();
|
||||
if (!operation.disableErrors) throw new Forbidden();
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -15,7 +15,8 @@ const executeAccess = async (operation, access) => {
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new Forbidden();
|
||||
if (!operation.disableErrors) throw new Forbidden();
|
||||
return false;
|
||||
};
|
||||
|
||||
module.exports = executeAccess;
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
const allOperations = ['create', 'read', 'update', 'delete'];
|
||||
|
||||
const accessOperation = async (args) => {
|
||||
async function accessOperation(args) {
|
||||
const { config } = this;
|
||||
|
||||
const {
|
||||
config,
|
||||
req,
|
||||
req: { user },
|
||||
} = args;
|
||||
@@ -101,6 +102,6 @@ const accessOperation = async (args) => {
|
||||
await Promise.all(promises);
|
||||
|
||||
return results;
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = accessOperation;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
const crypto = require('crypto');
|
||||
const { APIError } = require('../../errors');
|
||||
|
||||
const forgotPassword = async (args) => {
|
||||
async function forgotPassword(args) {
|
||||
const { config, email } = this;
|
||||
|
||||
if (!Object.prototype.hasOwnProperty.call(args.data, 'email')) {
|
||||
throw new APIError('Missing email.');
|
||||
}
|
||||
@@ -26,9 +28,7 @@ const forgotPassword = async (args) => {
|
||||
collection: {
|
||||
Model,
|
||||
},
|
||||
config,
|
||||
data,
|
||||
email,
|
||||
} = options;
|
||||
|
||||
let token = await crypto.randomBytes(20);
|
||||
@@ -66,6 +66,6 @@ const forgotPassword = async (args) => {
|
||||
if (typeof afterForgotPassword === 'function') {
|
||||
await afterForgotPassword(options);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = forgotPassword;
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
const login = require('./login');
|
||||
const refresh = require('./refresh');
|
||||
const register = require('./register');
|
||||
const init = require('./init');
|
||||
const forgotPassword = require('./forgotPassword');
|
||||
const resetPassword = require('./resetPassword');
|
||||
const registerFirstUser = require('./registerFirstUser');
|
||||
const update = require('./update');
|
||||
const access = require('./access');
|
||||
const me = require('./me');
|
||||
const logout = require('./logout');
|
||||
|
||||
module.exports = {
|
||||
login,
|
||||
refresh,
|
||||
init,
|
||||
register,
|
||||
forgotPassword,
|
||||
update,
|
||||
resetPassword,
|
||||
registerFirstUser,
|
||||
access,
|
||||
me,
|
||||
logout,
|
||||
};
|
||||
@@ -1,4 +1,4 @@
|
||||
const init = async (args) => {
|
||||
async function init(args) {
|
||||
const {
|
||||
Model,
|
||||
} = args;
|
||||
@@ -8,6 +8,6 @@ const init = async (args) => {
|
||||
if (count >= 1) return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = init;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
const { AuthenticationError } = require('../../errors');
|
||||
|
||||
const login = async (args) => {
|
||||
// Await validation here
|
||||
async function login(args) {
|
||||
const { config } = this;
|
||||
|
||||
const options = { ...args };
|
||||
|
||||
@@ -21,7 +21,6 @@ const login = async (args) => {
|
||||
Model,
|
||||
config: collectionConfig,
|
||||
},
|
||||
config,
|
||||
data,
|
||||
} = options;
|
||||
|
||||
@@ -90,6 +89,6 @@ const login = async (args) => {
|
||||
// /////////////////////////////////////
|
||||
|
||||
return token;
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = login;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
const logout = async (args) => {
|
||||
async function logout(args) {
|
||||
const { config } = this;
|
||||
|
||||
const {
|
||||
config,
|
||||
collection: {
|
||||
config: collectionConfig,
|
||||
},
|
||||
@@ -28,6 +29,6 @@ const logout = async (args) => {
|
||||
res.cookie(`${config.cookiePrefix}-token`, '', cookieOptions);
|
||||
|
||||
return 'Logged out successfully.';
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = logout;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
const getExtractJWT = require('../getExtractJWT');
|
||||
|
||||
const me = async ({ req, config }) => {
|
||||
const extractJWT = getExtractJWT(config);
|
||||
async function me({ req }) {
|
||||
const extractJWT = getExtractJWT(this.config);
|
||||
|
||||
if (req.user) {
|
||||
const response = {
|
||||
@@ -26,6 +26,6 @@ const me = async ({ req, config }) => {
|
||||
return {
|
||||
user: null,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = me;
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
const { Forbidden } = require('../../errors');
|
||||
|
||||
const refresh = async (args) => {
|
||||
async function refresh(args) {
|
||||
// Await validation here
|
||||
|
||||
const { secret, cookiePrefix } = this.config;
|
||||
let options = { ...args };
|
||||
|
||||
// /////////////////////////////////////
|
||||
@@ -20,7 +21,6 @@ const refresh = async (args) => {
|
||||
// 2. Perform refresh
|
||||
// /////////////////////////////////////
|
||||
|
||||
const { secret, cookiePrefix } = options.config;
|
||||
const opts = {};
|
||||
opts.expiresIn = options.collection.config.auth.tokenExpiration;
|
||||
|
||||
@@ -71,6 +71,6 @@ const refresh = async (args) => {
|
||||
refreshedToken,
|
||||
user: payload,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = refresh;
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
const passport = require('passport');
|
||||
const executeAccess = require('../executeAccess');
|
||||
const performFieldOperations = require('../../fields/performFieldOperations');
|
||||
|
||||
const register = async (args) => {
|
||||
async function register(args) {
|
||||
const { config } = this;
|
||||
|
||||
const {
|
||||
depth,
|
||||
overrideAccess,
|
||||
config,
|
||||
collection: {
|
||||
Model,
|
||||
config: collectionConfig,
|
||||
@@ -45,7 +45,7 @@ const register = async (args) => {
|
||||
// 3. Execute field-level hooks, access, and validation
|
||||
// /////////////////////////////////////
|
||||
|
||||
data = await performFieldOperations(config, collectionConfig, {
|
||||
data = await this.performFieldOperations(collectionConfig, {
|
||||
data,
|
||||
hook: 'beforeCreate',
|
||||
operationName: 'create',
|
||||
@@ -77,7 +77,7 @@ const register = async (args) => {
|
||||
// 7. Execute field-level hooks and access
|
||||
// /////////////////////////////////////
|
||||
|
||||
result = await performFieldOperations(config, collectionConfig, {
|
||||
result = await this.performFieldOperations(collectionConfig, {
|
||||
data: result,
|
||||
hook: 'afterRead',
|
||||
operationName: 'read',
|
||||
@@ -103,6 +103,6 @@ const register = async (args) => {
|
||||
// /////////////////////////////////////
|
||||
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = register;
|
||||
|
||||
@@ -2,7 +2,7 @@ const register = require('./register');
|
||||
const login = require('./login');
|
||||
const { Forbidden } = require('../../errors');
|
||||
|
||||
const registerFirstUser = async (args) => {
|
||||
async function registerFirstUser(args) {
|
||||
const {
|
||||
collection: {
|
||||
Model,
|
||||
@@ -40,6 +40,6 @@ const registerFirstUser = async (args) => {
|
||||
message: 'Registered successfully. Welcome to Payload!',
|
||||
user: result,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = registerFirstUser;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
const jwt = require('jsonwebtoken');
|
||||
const { APIError } = require('../../errors');
|
||||
|
||||
const resetPassword = async (args) => {
|
||||
async function resetPassword(args) {
|
||||
const { config } = this;
|
||||
|
||||
if (!Object.prototype.hasOwnProperty.call(args.data, 'token')
|
||||
|| !Object.prototype.hasOwnProperty.call(args.data, 'password')) {
|
||||
throw new APIError('Missing required data.');
|
||||
@@ -28,7 +30,6 @@ const resetPassword = async (args) => {
|
||||
Model,
|
||||
config: collectionConfig,
|
||||
},
|
||||
config,
|
||||
data,
|
||||
} = options;
|
||||
|
||||
@@ -85,6 +86,6 @@ const resetPassword = async (args) => {
|
||||
// /////////////////////////////////////
|
||||
|
||||
return token;
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = resetPassword;
|
||||
|
||||
@@ -2,12 +2,12 @@ const deepmerge = require('deepmerge');
|
||||
const overwriteMerge = require('../../utilities/overwriteMerge');
|
||||
const { NotFound, Forbidden } = require('../../errors');
|
||||
const executeAccess = require('../executeAccess');
|
||||
const performFieldOperations = require('../../fields/performFieldOperations');
|
||||
|
||||
const update = async (args) => {
|
||||
async function update(args) {
|
||||
const { config } = this;
|
||||
|
||||
const {
|
||||
depth,
|
||||
config,
|
||||
collection: {
|
||||
Model,
|
||||
config: collectionConfig,
|
||||
@@ -77,7 +77,7 @@ const update = async (args) => {
|
||||
// 4. Execute field-level hooks, access, and validation
|
||||
// /////////////////////////////////////
|
||||
|
||||
data = await performFieldOperations(config, collectionConfig, {
|
||||
data = await this.performFieldOperations(collectionConfig, {
|
||||
data,
|
||||
req,
|
||||
hook: 'beforeUpdate',
|
||||
@@ -111,7 +111,7 @@ const update = async (args) => {
|
||||
// 7. Execute field-level hooks and access
|
||||
// /////////////////////////////////////
|
||||
|
||||
user = performFieldOperations(config, collectionConfig, {
|
||||
user = this.performFieldOperations(collectionConfig, {
|
||||
data: user,
|
||||
hook: 'afterRead',
|
||||
operationName: 'read',
|
||||
@@ -137,6 +137,6 @@ const update = async (args) => {
|
||||
// /////////////////////////////////////
|
||||
|
||||
return user;
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = update;
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
const httpStatus = require('http-status');
|
||||
const { access } = require('../operations');
|
||||
|
||||
const policiesHandler = (config) => async (req, res, next) => {
|
||||
async function policiesHandler(req, res, next) {
|
||||
try {
|
||||
const accessResults = await access({
|
||||
const accessResults = await this.operations.collections.auth.access({
|
||||
req,
|
||||
config,
|
||||
});
|
||||
|
||||
return res.status(httpStatus.OK)
|
||||
@@ -13,6 +11,6 @@ const policiesHandler = (config) => async (req, res, next) => {
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = policiesHandler;
|
||||
|
||||
@@ -1,14 +1,11 @@
|
||||
const httpStatus = require('http-status');
|
||||
const { forgotPassword } = require('../operations');
|
||||
|
||||
const forgotPasswordHandler = (config, email) => async (req, res, next) => {
|
||||
async function forgotPasswordHandler(req, res, next) {
|
||||
try {
|
||||
await forgotPassword({
|
||||
await this.operations.collections.auth.forgotPassword({
|
||||
req,
|
||||
collection: req.collection,
|
||||
config,
|
||||
data: req.body,
|
||||
email,
|
||||
});
|
||||
|
||||
return res.status(httpStatus.OK)
|
||||
@@ -18,6 +15,6 @@ const forgotPasswordHandler = (config, email) => async (req, res, next) => {
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = forgotPasswordHandler;
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
const login = require('./login');
|
||||
const me = require('./me');
|
||||
const refresh = require('./refresh');
|
||||
const register = require('./register');
|
||||
const init = require('./init');
|
||||
const forgotPassword = require('./forgotPassword');
|
||||
const resetPassword = require('./resetPassword');
|
||||
const registerFirstUser = require('./registerFirstUser');
|
||||
const update = require('./update');
|
||||
const access = require('./access');
|
||||
const logout = require('./logout');
|
||||
|
||||
module.exports = {
|
||||
login,
|
||||
logout,
|
||||
me,
|
||||
refresh,
|
||||
init,
|
||||
register,
|
||||
forgotPassword,
|
||||
registerFirstUser,
|
||||
resetPassword,
|
||||
update,
|
||||
access,
|
||||
};
|
||||
@@ -1,12 +1,10 @@
|
||||
const { init } = require('../operations');
|
||||
|
||||
const initHandler = async (req, res, next) => {
|
||||
async function initHandler(req, res, next) {
|
||||
try {
|
||||
const initialized = await init({ Model: req.collection.Model });
|
||||
const initialized = await this.operations.collections.auth.init({ Model: req.collection.Model });
|
||||
return res.status(200).json({ initialized });
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = initHandler;
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
const httpStatus = require('http-status');
|
||||
const { login } = require('../operations');
|
||||
|
||||
const loginHandler = (config) => async (req, res, next) => {
|
||||
async function loginHandler(req, res, next) {
|
||||
try {
|
||||
const token = await login({
|
||||
const token = await this.operations.collections.auth.login({
|
||||
req,
|
||||
res,
|
||||
collection: req.collection,
|
||||
config,
|
||||
data: req.body,
|
||||
});
|
||||
|
||||
@@ -19,6 +17,6 @@ const loginHandler = (config) => async (req, res, next) => {
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = loginHandler;
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
const { logout } = require('../operations');
|
||||
|
||||
const logoutHandler = (config) => async (req, res, next) => {
|
||||
async function logoutHandler(req, res, next) {
|
||||
try {
|
||||
const message = await logout({
|
||||
config,
|
||||
const message = await this.operations.collections.auth.logout({
|
||||
collection: req.collection,
|
||||
res,
|
||||
req,
|
||||
@@ -13,6 +10,6 @@ const logoutHandler = (config) => async (req, res, next) => {
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = logoutHandler;
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
const { me } = require('../operations');
|
||||
|
||||
const meHandler = config => async (req, res, next) => {
|
||||
async function me(req, res, next) {
|
||||
try {
|
||||
const response = await me({ req, config });
|
||||
const response = await this.operations.collections.auth.me({ req });
|
||||
return res.status(200).json(response);
|
||||
} catch (err) {
|
||||
return next(err);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = meHandler;
|
||||
module.exports = me;
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
const { refresh } = require('../operations');
|
||||
const getExtractJWT = require('../getExtractJWT');
|
||||
|
||||
const refreshHandler = config => async (req, res, next) => {
|
||||
async function refreshHandler(req, res, next) {
|
||||
try {
|
||||
const extractJWT = getExtractJWT(config);
|
||||
const extractJWT = getExtractJWT(this.config);
|
||||
const token = extractJWT(req);
|
||||
|
||||
const result = await refresh({
|
||||
const result = await this.operations.collections.auth.refresh({
|
||||
req,
|
||||
res,
|
||||
collection: req.collection,
|
||||
config,
|
||||
token,
|
||||
});
|
||||
|
||||
@@ -21,6 +19,6 @@ const refreshHandler = config => async (req, res, next) => {
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = refreshHandler;
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
const httpStatus = require('http-status');
|
||||
const formatSuccessResponse = require('../../express/responses/formatSuccess');
|
||||
const { register } = require('../operations');
|
||||
|
||||
const registerHandler = (config) => async (req, res, next) => {
|
||||
async function register(req, res, next) {
|
||||
try {
|
||||
const user = await register({
|
||||
config,
|
||||
const user = await this.operations.collections.auth.register({
|
||||
collection: req.collection,
|
||||
req,
|
||||
data: req.body,
|
||||
@@ -18,6 +16,6 @@ const registerHandler = (config) => async (req, res, next) => {
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = registerHandler;
|
||||
module.exports = register;
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
const { registerFirstUser } = require('../operations');
|
||||
|
||||
const registerFirstUserHandler = (config) => async (req, res, next) => {
|
||||
async function registerFirstUser(req, res, next) {
|
||||
try {
|
||||
const firstUser = await registerFirstUser({
|
||||
const firstUser = await this.operations.collections.auth.registerFirstUser({
|
||||
req,
|
||||
res,
|
||||
config,
|
||||
collection: req.collection,
|
||||
data: req.body,
|
||||
});
|
||||
@@ -14,6 +11,6 @@ const registerFirstUserHandler = (config) => async (req, res, next) => {
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = registerFirstUserHandler;
|
||||
module.exports = registerFirstUser;
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
const httpStatus = require('http-status');
|
||||
const { resetPassword } = require('../operations');
|
||||
|
||||
const resetPasswordHandler = config => async (req, res, next) => {
|
||||
async function resetPassword(req, res, next) {
|
||||
try {
|
||||
const token = await resetPassword({
|
||||
const token = await this.operations.collections.auth.resetPassword({
|
||||
req,
|
||||
collection: req.collection,
|
||||
config,
|
||||
data: req.body,
|
||||
});
|
||||
|
||||
@@ -18,6 +16,6 @@ const resetPasswordHandler = config => async (req, res, next) => {
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = resetPasswordHandler;
|
||||
module.exports = resetPassword;
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
const httpStatus = require('http-status');
|
||||
const formatSuccessResponse = require('../../express/responses/formatSuccess');
|
||||
const { update } = require('../operations');
|
||||
|
||||
const updateHandler = (config) => async (req, res, next) => {
|
||||
async function update(req, res, next) {
|
||||
try {
|
||||
const user = await update({
|
||||
const user = await this.operations.collections.auth.update({
|
||||
req,
|
||||
data: req.body,
|
||||
collection: req.collection,
|
||||
config,
|
||||
id: req.params.id,
|
||||
});
|
||||
|
||||
@@ -19,6 +17,6 @@ const updateHandler = (config) => async (req, res, next) => {
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = updateHandler;
|
||||
module.exports = update;
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
const express = require('express');
|
||||
const bindCollectionMiddleware = require('../collections/bindCollection');
|
||||
|
||||
const {
|
||||
init,
|
||||
login,
|
||||
logout,
|
||||
refresh,
|
||||
me,
|
||||
register,
|
||||
registerFirstUser,
|
||||
forgotPassword,
|
||||
resetPassword,
|
||||
update,
|
||||
} = require('./requestHandlers');
|
||||
|
||||
const {
|
||||
find,
|
||||
findByID,
|
||||
deleteHandler,
|
||||
} = require('../collections/requestHandlers');
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const authRoutes = (collection, config, sendEmail) => {
|
||||
const { slug } = collection.config;
|
||||
|
||||
router.all('*',
|
||||
bindCollectionMiddleware(collection));
|
||||
|
||||
router
|
||||
.route(`/${slug}/init`)
|
||||
.get(init);
|
||||
|
||||
router
|
||||
.route(`/${slug}/login`)
|
||||
.post(login(config));
|
||||
|
||||
router
|
||||
.route(`/${slug}/logout`)
|
||||
.get(logout(config));
|
||||
|
||||
router
|
||||
.route(`/${slug}/refresh-token`)
|
||||
.post(refresh(config));
|
||||
|
||||
router
|
||||
.route(`/${slug}/me`)
|
||||
.get(me(config));
|
||||
|
||||
router
|
||||
.route(`/${slug}/first-register`)
|
||||
.post(registerFirstUser(config));
|
||||
|
||||
router
|
||||
.route(`/${slug}/forgot-password`)
|
||||
.post(forgotPassword(config, sendEmail));
|
||||
|
||||
router
|
||||
.route(`${slug}/reset-password`)
|
||||
.post(resetPassword);
|
||||
|
||||
router
|
||||
.route(`/${slug}/register`)
|
||||
.post(register(config));
|
||||
|
||||
router
|
||||
.route(`/${slug}`)
|
||||
.get(find(config));
|
||||
|
||||
router.route(`/${slug}/:id`)
|
||||
.get(findByID(config))
|
||||
.put(update(config))
|
||||
.delete(deleteHandler(config));
|
||||
|
||||
return router;
|
||||
};
|
||||
|
||||
module.exports = authRoutes;
|
||||
@@ -1,7 +1,5 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
const { findByID } = require('../../operations');
|
||||
|
||||
const findByIDResolver = (config, collection) => async (_, args, context) => {
|
||||
const findByIDResolver = (collection) => async (_, args, context) => {
|
||||
if (args.locale) context.req.locale = args.locale;
|
||||
if (args.fallbackLocale) context.req.fallbackLocale = args.fallbackLocale;
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const mongoose = require('mongoose');
|
||||
const express = require('express');
|
||||
const mongooseHidden = require('mongoose-hidden')({
|
||||
hidden: {
|
||||
salt: true, hash: true, _id: true, __v: true,
|
||||
@@ -9,9 +10,8 @@ const passport = require('passport');
|
||||
const passportLocalMongoose = require('passport-local-mongoose');
|
||||
const LocalStrategy = require('passport-local').Strategy;
|
||||
const apiKeyStrategy = require('../auth/strategies/apiKey');
|
||||
const collectionRoutes = require('./routes');
|
||||
const buildSchema = require('./buildSchema');
|
||||
const authRoutes = require('../auth/routes');
|
||||
const bindCollectionMiddleware = require('./bindCollection');
|
||||
|
||||
function registerCollections() {
|
||||
this.config.collections = this.config.collections.map((collection) => {
|
||||
@@ -30,6 +30,27 @@ function registerCollections() {
|
||||
config: formattedCollection,
|
||||
};
|
||||
|
||||
const router = express.Router();
|
||||
const { slug } = collection;
|
||||
|
||||
router.all(`/${slug}*`, bindCollectionMiddleware(this.collections[formattedCollection.slug]));
|
||||
|
||||
const {
|
||||
create,
|
||||
find,
|
||||
update,
|
||||
findByID,
|
||||
delete: deleteHandler,
|
||||
} = this.requestHandlers.collections;
|
||||
|
||||
router.route(`/${slug}`)
|
||||
.get(find)
|
||||
.put(update);
|
||||
|
||||
router.route(`/${slug}/:id`)
|
||||
.get(findByID)
|
||||
.delete(deleteHandler);
|
||||
|
||||
if (collection.auth) {
|
||||
const AuthCollection = this.collections[formattedCollection.slug];
|
||||
passport.use(new LocalStrategy(AuthCollection.Model.authenticate()));
|
||||
@@ -38,11 +59,65 @@ function registerCollections() {
|
||||
passport.use(`${AuthCollection.config.slug}-api-key`, apiKeyStrategy(AuthCollection));
|
||||
}
|
||||
|
||||
this.router.use(authRoutes(AuthCollection, this.config, this.sendEmail));
|
||||
const {
|
||||
init,
|
||||
login,
|
||||
logout,
|
||||
refresh,
|
||||
me,
|
||||
register,
|
||||
registerFirstUser,
|
||||
forgotPassword,
|
||||
resetPassword,
|
||||
update: authUpdate,
|
||||
} = this.requestHandlers.collections.auth;
|
||||
|
||||
router
|
||||
.route(`/${slug}/init`)
|
||||
.get(init);
|
||||
|
||||
router
|
||||
.route(`/${slug}/login`)
|
||||
.post(login);
|
||||
|
||||
router
|
||||
.route(`/${slug}/logout`)
|
||||
.get(logout);
|
||||
|
||||
router
|
||||
.route(`/${slug}/refresh-token`)
|
||||
.post(refresh);
|
||||
|
||||
router
|
||||
.route(`/${slug}/me`)
|
||||
.get(me);
|
||||
|
||||
router
|
||||
.route(`/${slug}/first-register`)
|
||||
.post(registerFirstUser);
|
||||
|
||||
router
|
||||
.route(`/${slug}/forgot-password`)
|
||||
.post(forgotPassword);
|
||||
|
||||
router
|
||||
.route(`${slug}/reset-password`)
|
||||
.post(resetPassword);
|
||||
|
||||
router
|
||||
.route(`/${slug}/register`)
|
||||
.post(register);
|
||||
|
||||
router.route(`/${slug}/:id`)
|
||||
.put(authUpdate);
|
||||
} else {
|
||||
this.router.use(collectionRoutes(this.collections[formattedCollection.slug], this.config));
|
||||
router.route(`/${slug}`)
|
||||
.get(find)
|
||||
.post(create);
|
||||
}
|
||||
|
||||
this.router.use(router);
|
||||
|
||||
return formattedCollection;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@ const getSafeFilename = require('../../uploads/getSafeFilename');
|
||||
const getImageSize = require('../../uploads/getImageSize');
|
||||
const imageMIMETypes = require('../../uploads/imageMIMETypes');
|
||||
|
||||
const performFieldOperations = require('../../fields/performFieldOperations');
|
||||
async function create(args) {
|
||||
const { performFieldOperations } = this;
|
||||
|
||||
const create = async (args) => {
|
||||
const {
|
||||
collection: {
|
||||
Model,
|
||||
@@ -21,7 +21,7 @@ const create = async (args) => {
|
||||
locale,
|
||||
fallbackLocale,
|
||||
},
|
||||
config,
|
||||
depth,
|
||||
} = args;
|
||||
|
||||
let { data } = args;
|
||||
@@ -88,7 +88,7 @@ const create = async (args) => {
|
||||
// 4. Execute field-level access, hooks, and validation
|
||||
// /////////////////////////////////////
|
||||
|
||||
data = await performFieldOperations(config, collectionConfig, {
|
||||
data = await performFieldOperations(collectionConfig, {
|
||||
data,
|
||||
hook: 'beforeCreate',
|
||||
operationName: 'create',
|
||||
@@ -114,11 +114,12 @@ const create = async (args) => {
|
||||
// 6. Execute field-level hooks and access
|
||||
// /////////////////////////////////////
|
||||
|
||||
result = await performFieldOperations(config, collectionConfig, {
|
||||
result = await performFieldOperations(collectionConfig, {
|
||||
data: result,
|
||||
hook: 'afterRead',
|
||||
operationName: 'read',
|
||||
req,
|
||||
depth,
|
||||
});
|
||||
|
||||
// /////////////////////////////////////
|
||||
@@ -139,6 +140,6 @@ const create = async (args) => {
|
||||
// /////////////////////////////////////
|
||||
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = create;
|
||||
|
||||
@@ -2,9 +2,7 @@ const fs = require('fs');
|
||||
const { NotFound, Forbidden, ErrorDeletingFile } = require('../../errors');
|
||||
const executeAccess = require('../../auth/executeAccess');
|
||||
|
||||
const performFieldOperations = require('../../fields/performFieldOperations');
|
||||
|
||||
const deleteQuery = async (args) => {
|
||||
async function deleteQuery(args) {
|
||||
const {
|
||||
depth,
|
||||
collection: {
|
||||
@@ -17,7 +15,6 @@ const deleteQuery = async (args) => {
|
||||
locale,
|
||||
fallbackLocale,
|
||||
},
|
||||
config,
|
||||
} = args;
|
||||
|
||||
// /////////////////////////////////////
|
||||
@@ -97,7 +94,7 @@ const deleteQuery = async (args) => {
|
||||
// 6. Execute field-level hooks and access
|
||||
// /////////////////////////////////////
|
||||
|
||||
result = await performFieldOperations(config, collectionConfig, {
|
||||
result = await this.performFieldOperations(collectionConfig, {
|
||||
data: result,
|
||||
hook: 'afterRead',
|
||||
operationName: 'read',
|
||||
@@ -120,6 +117,6 @@ const deleteQuery = async (args) => {
|
||||
// /////////////////////////////////////
|
||||
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = deleteQuery;
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
const executeAccess = require('../../auth/executeAccess');
|
||||
const performFieldOperations = require('../../fields/performFieldOperations');
|
||||
|
||||
const find = async (args) => {
|
||||
async function find(args) {
|
||||
const {
|
||||
where,
|
||||
page,
|
||||
limit,
|
||||
config,
|
||||
depth,
|
||||
collection: {
|
||||
Model,
|
||||
@@ -90,13 +88,17 @@ const find = async (args) => {
|
||||
|
||||
const data = doc.toJSON({ virtuals: true });
|
||||
|
||||
return performFieldOperations(config, collectionConfig, {
|
||||
depth,
|
||||
data,
|
||||
req,
|
||||
hook: 'afterRead',
|
||||
operationName: 'read',
|
||||
});
|
||||
return this.performFieldOperations(
|
||||
collectionConfig,
|
||||
{
|
||||
depth,
|
||||
data,
|
||||
req,
|
||||
hook: 'afterRead',
|
||||
operationName: 'read',
|
||||
},
|
||||
find,
|
||||
);
|
||||
})),
|
||||
};
|
||||
|
||||
@@ -128,6 +130,6 @@ const find = async (args) => {
|
||||
// /////////////////////////////////////
|
||||
|
||||
return afterReadResult;
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = find;
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
const { Forbidden, NotFound } = require('../../errors');
|
||||
const executeAccess = require('../../auth/executeAccess');
|
||||
const performFieldOperations = require('../../fields/performFieldOperations');
|
||||
|
||||
const findByID = async (args) => {
|
||||
async function findByID(args) {
|
||||
const {
|
||||
config,
|
||||
depth,
|
||||
collection: {
|
||||
Model,
|
||||
@@ -16,13 +14,15 @@ const findByID = async (args) => {
|
||||
locale,
|
||||
fallbackLocale,
|
||||
},
|
||||
disableErrors,
|
||||
currentDepth,
|
||||
} = args;
|
||||
|
||||
// /////////////////////////////////////
|
||||
// 1. Retrieve and execute access
|
||||
// /////////////////////////////////////
|
||||
|
||||
const accessResults = await executeAccess({ req }, collectionConfig.access.read);
|
||||
const accessResults = await executeAccess({ req, disableErrors }, collectionConfig.access.read);
|
||||
const hasWhereAccess = typeof accessResults === 'object';
|
||||
|
||||
const queryToBuild = {
|
||||
@@ -55,8 +55,14 @@ const findByID = async (args) => {
|
||||
|
||||
let result = await Model.findOne(query, {});
|
||||
|
||||
if (!result && !hasWhereAccess) throw new NotFound();
|
||||
if (!result && hasWhereAccess) throw new Forbidden();
|
||||
if (!result) {
|
||||
if (!disableErrors) {
|
||||
if (!hasWhereAccess) throw new NotFound();
|
||||
if (hasWhereAccess) throw new Forbidden();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (locale && result.setLocale) {
|
||||
result.setLocale(locale, fallbackLocale);
|
||||
@@ -68,12 +74,13 @@ const findByID = async (args) => {
|
||||
// 4. Execute field-level hooks and access
|
||||
// /////////////////////////////////////
|
||||
|
||||
result = await performFieldOperations(config, collectionConfig, {
|
||||
result = await this.performFieldOperations(collectionConfig, {
|
||||
depth,
|
||||
req,
|
||||
data: result,
|
||||
hook: 'afterRead',
|
||||
operationName: 'read',
|
||||
currentDepth,
|
||||
});
|
||||
|
||||
|
||||
@@ -96,6 +103,6 @@ const findByID = async (args) => {
|
||||
// /////////////////////////////////////
|
||||
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = findByID;
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
const findByID = require('./findByID');
|
||||
const find = require('./find');
|
||||
const create = require('./create');
|
||||
const update = require('./update');
|
||||
const deleteQuery = require('./delete');
|
||||
|
||||
module.exports = {
|
||||
findByID,
|
||||
find,
|
||||
create,
|
||||
update,
|
||||
deleteQuery,
|
||||
};
|
||||
@@ -2,16 +2,14 @@ const deepmerge = require('deepmerge');
|
||||
const overwriteMerge = require('../../utilities/overwriteMerge');
|
||||
const executeAccess = require('../../auth/executeAccess');
|
||||
const { NotFound, Forbidden } = require('../../errors');
|
||||
const performFieldOperations = require('../../fields/performFieldOperations');
|
||||
const imageMIMETypes = require('../../uploads/imageMIMETypes');
|
||||
const getImageSize = require('../../uploads/getImageSize');
|
||||
const getSafeFilename = require('../../uploads/getSafeFilename');
|
||||
|
||||
const resizeAndSave = require('../../uploads/imageResizer');
|
||||
|
||||
const update = async (args) => {
|
||||
async function update(args) {
|
||||
const {
|
||||
config,
|
||||
depth,
|
||||
collection: {
|
||||
Model,
|
||||
@@ -91,7 +89,7 @@ const update = async (args) => {
|
||||
// 4. Execute field-level hooks, access, and validation
|
||||
// /////////////////////////////////////
|
||||
|
||||
data = await performFieldOperations(config, collectionConfig, {
|
||||
data = await this.performFieldOperations(collectionConfig, {
|
||||
data,
|
||||
req,
|
||||
originalDoc,
|
||||
@@ -154,7 +152,7 @@ const update = async (args) => {
|
||||
// 7. Execute field-level hooks and access
|
||||
// /////////////////////////////////////
|
||||
|
||||
doc = await performFieldOperations(config, collectionConfig, {
|
||||
doc = await this.performFieldOperations(collectionConfig, {
|
||||
data: doc,
|
||||
hook: 'afterRead',
|
||||
operationName: 'read',
|
||||
@@ -180,6 +178,6 @@ const update = async (args) => {
|
||||
// /////////////////////////////////////
|
||||
|
||||
return doc;
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = update;
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
const httpStatus = require('http-status');
|
||||
const formatSuccessResponse = require('../../express/responses/formatSuccess');
|
||||
const { create } = require('../operations');
|
||||
|
||||
const createHandler = (config) => async (req, res, next) => {
|
||||
async function create(req, res, next) {
|
||||
try {
|
||||
const doc = await create({
|
||||
const doc = await this.operations.collections.create({
|
||||
req,
|
||||
collection: req.collection,
|
||||
config,
|
||||
data: req.body,
|
||||
depth: req.query.depth,
|
||||
});
|
||||
@@ -19,6 +17,6 @@ const createHandler = (config) => async (req, res, next) => {
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = createHandler;
|
||||
module.exports = create;
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
const httpStatus = require('http-status');
|
||||
const { NotFound } = require('../../errors');
|
||||
const { deleteQuery } = require('../operations');
|
||||
|
||||
const deleteHandler = (config) => async (req, res, next) => {
|
||||
async function deleteHandler(req, res, next) {
|
||||
try {
|
||||
const doc = await deleteQuery({
|
||||
const doc = await this.operations.collections.deleteQuery({
|
||||
req,
|
||||
collection: req.collection,
|
||||
config,
|
||||
id: req.params.id,
|
||||
depth: req.query.depth,
|
||||
});
|
||||
@@ -20,6 +18,6 @@ const deleteHandler = (config) => async (req, res, next) => {
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = deleteHandler;
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
const httpStatus = require('http-status');
|
||||
const { find } = require('../operations');
|
||||
|
||||
const findHandler = (config) => async (req, res, next) => {
|
||||
async function find(req, res, next) {
|
||||
try {
|
||||
const options = {
|
||||
req,
|
||||
collection: req.collection,
|
||||
config,
|
||||
where: req.query.where,
|
||||
page: req.query.page,
|
||||
limit: req.query.limit,
|
||||
@@ -14,12 +12,12 @@ const findHandler = (config) => async (req, res, next) => {
|
||||
depth: req.query.depth,
|
||||
};
|
||||
|
||||
const result = await find(options);
|
||||
const result = await this.operations.collections.find(options);
|
||||
|
||||
return res.status(httpStatus.OK).json(result);
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = findHandler;
|
||||
module.exports = find;
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
const { findByID } = require('../operations');
|
||||
|
||||
const findByIDHandler = (config) => async (req, res, next) => {
|
||||
async function findByID(req, res, next) {
|
||||
const options = {
|
||||
req,
|
||||
collection: req.collection,
|
||||
config,
|
||||
id: req.params.id,
|
||||
depth: req.query.depth,
|
||||
};
|
||||
|
||||
try {
|
||||
const doc = await findByID(options);
|
||||
const doc = await this.operations.collections.findByID(options);
|
||||
return res.json(doc);
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = findByIDHandler;
|
||||
module.exports = findByID;
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
const create = require('./create');
|
||||
const deleteHandler = require('./delete');
|
||||
const findByID = require('./findByID');
|
||||
const find = require('./find');
|
||||
const update = require('./update');
|
||||
|
||||
module.exports = {
|
||||
create,
|
||||
deleteHandler,
|
||||
findByID,
|
||||
find,
|
||||
update,
|
||||
};
|
||||
@@ -1,13 +1,11 @@
|
||||
const httpStatus = require('http-status');
|
||||
const formatSuccessResponse = require('../../express/responses/formatSuccess');
|
||||
const { update } = require('../operations');
|
||||
|
||||
const updateHandler = (config) => async (req, res, next) => {
|
||||
async function update(req, res, next) {
|
||||
try {
|
||||
const doc = await update({
|
||||
const doc = await this.operations.collections.update({
|
||||
req,
|
||||
collection: req.collection,
|
||||
config,
|
||||
id: req.params.id,
|
||||
data: req.body,
|
||||
depth: req.query.depth,
|
||||
@@ -20,6 +18,6 @@ const updateHandler = (config) => async (req, res, next) => {
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = updateHandler;
|
||||
module.exports = update;
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
const express = require('express');
|
||||
|
||||
const requestHandlers = require('./requestHandlers');
|
||||
const bindCollectionMiddleware = require('./bindCollection');
|
||||
|
||||
const {
|
||||
find, create, findByID, deleteHandler, update,
|
||||
} = requestHandlers;
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const registerRoutes = (collection, config) => {
|
||||
router.all(`/${collection.config.slug}*`, bindCollectionMiddleware(collection));
|
||||
|
||||
router.route(`/${collection.config.slug}`)
|
||||
.get(find(config))
|
||||
.post(create(config));
|
||||
|
||||
router.route(`/${collection.config.slug}/:id`)
|
||||
.get(findByID(config))
|
||||
.put(update(config))
|
||||
.delete(deleteHandler(config));
|
||||
|
||||
return router;
|
||||
};
|
||||
|
||||
module.exports = registerRoutes;
|
||||
@@ -1,6 +1,7 @@
|
||||
const { ValidationError } = require('../errors');
|
||||
const executeAccess = require('../auth/executeAccess');
|
||||
|
||||
const performFieldOperations = async (config, entityConfig, operation) => {
|
||||
async function performFieldOperations(entityConfig, operation) {
|
||||
const {
|
||||
data: fullData,
|
||||
originalDoc: fullOriginalDoc,
|
||||
@@ -9,12 +10,54 @@ const performFieldOperations = async (config, entityConfig, operation) => {
|
||||
req,
|
||||
} = operation;
|
||||
|
||||
const recursivePerformFieldOperations = performFieldOperations.bind(this);
|
||||
|
||||
const depth = (operation.depth || operation.depth === 0) ? parseInt(operation.depth, 10) : this.config.defaultDepth;
|
||||
const currentDepth = operation.currentDepth || 0;
|
||||
|
||||
const populateRelationship = async (dataReference, data, field, i) => {
|
||||
let dataToUpdate = dataReference;
|
||||
|
||||
const relation = Array.isArray(field.relationTo) ? data.relationTo : field.relationTo;
|
||||
const relatedCollection = this.collections[relation];
|
||||
|
||||
const accessResult = await executeAccess({ req, disableErrors: true }, relatedCollection.config.access.read);
|
||||
|
||||
let populatedRelationship = null;
|
||||
|
||||
if (accessResult && (depth && currentDepth <= depth)) {
|
||||
populatedRelationship = await this.operations.collections.findByID({
|
||||
req,
|
||||
collection: relatedCollection,
|
||||
id: Array.isArray(field.relationTo) ? data.value : data,
|
||||
currentDepth: currentDepth + 1,
|
||||
disableErrors: true,
|
||||
});
|
||||
}
|
||||
|
||||
// If access control fails, update value to null
|
||||
// If populatedRelationship comes back, update value
|
||||
if (!accessResult || populatedRelationship) {
|
||||
if (typeof i === 'number') {
|
||||
if (Array.isArray(field.relationTo)) {
|
||||
dataToUpdate[field.name][i].value = populatedRelationship;
|
||||
} else {
|
||||
dataToUpdate[field.name][i] = populatedRelationship;
|
||||
}
|
||||
} else if (Array.isArray(field.relationTo)) {
|
||||
dataToUpdate.value = populatedRelationship;
|
||||
} else {
|
||||
dataToUpdate = populatedRelationship;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Maintain a top-level list of promises
|
||||
// so that all async field access / validations / hooks
|
||||
// can run in parallel
|
||||
const validationPromises = [];
|
||||
const accessPromises = [];
|
||||
const relationshipAccessPromises = [];
|
||||
const relationshipPopulationPromises = [];
|
||||
const hookPromises = [];
|
||||
const errors = [];
|
||||
|
||||
@@ -36,13 +79,25 @@ const performFieldOperations = async (config, entityConfig, operation) => {
|
||||
}
|
||||
};
|
||||
|
||||
const createRelationshipAccessPromise = async (data, field, access) => {
|
||||
const createRelationshipPopulationPromise = async (data, field) => {
|
||||
const resultingData = data;
|
||||
|
||||
const result = await access({ req });
|
||||
if (field.hasMany && Array.isArray(data[field.name])) {
|
||||
const rowPromises = [];
|
||||
|
||||
if (result === false) {
|
||||
delete resultingData[field.name];
|
||||
data[field.name].forEach((relatedDoc, i) => {
|
||||
const rowPromise = async () => {
|
||||
if (relatedDoc) {
|
||||
await populateRelationship(resultingData, relatedDoc, field, i);
|
||||
}
|
||||
};
|
||||
|
||||
rowPromises.push(rowPromise());
|
||||
});
|
||||
|
||||
await Promise.all(rowPromises);
|
||||
} else if (data[field.name]) {
|
||||
await populateRelationship(resultingData, data[field.name], field);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -59,30 +114,13 @@ const performFieldOperations = async (config, entityConfig, operation) => {
|
||||
}
|
||||
}
|
||||
|
||||
if (field.type === 'relationship' && operationName === 'read') {
|
||||
const relatedCollections = Array.isArray(field.relationTo) ? field.relationTo : [field.relationTo];
|
||||
|
||||
relatedCollections.forEach((slug) => {
|
||||
const collection = config.collections.find((coll) => coll.slug === slug);
|
||||
|
||||
if (collection && collection.access && collection.access.read) {
|
||||
relationshipAccessPromises.push(createRelationshipAccessPromise(data, field, collection.access.read));
|
||||
}
|
||||
});
|
||||
if ((field.type === 'relationship' || field.type === 'upload') && hook === 'afterRead') {
|
||||
relationshipPopulationPromises.push(createRelationshipPopulationPromise(data, field));
|
||||
}
|
||||
};
|
||||
|
||||
const createHookPromise = async (data, field) => {
|
||||
const resultingData = data;
|
||||
const findRelatedCollection = (relation) => config.collections.find((collection) => collection.slug === relation);
|
||||
// Todo:
|
||||
// Check for afterRead operation and if found,
|
||||
// Run relationship and upload-based hooks here
|
||||
// Handle following scenarios:
|
||||
//
|
||||
// hasMany
|
||||
// relationTo hasMany
|
||||
// single
|
||||
|
||||
if (hook === 'afterRead') {
|
||||
if ((field.type === 'relationship' || field.type === 'upload')) {
|
||||
@@ -90,51 +128,58 @@ const performFieldOperations = async (config, entityConfig, operation) => {
|
||||
|
||||
// If there are many related documents
|
||||
if (field.hasMany && Array.isArray(data[field.name])) {
|
||||
const relationshipDocPromises = [];
|
||||
// Loop through relations
|
||||
data[field.name].forEach(async (value, i) => {
|
||||
let relation = field.relationTo;
|
||||
data[field.name].forEach((value, i) => {
|
||||
const generateRelationshipDocPromise = async () => {
|
||||
let relation = field.relationTo;
|
||||
|
||||
// If this field can be related to many collections,
|
||||
// Set relationTo based on value
|
||||
if (hasManyRelations && value && value.relationTo) {
|
||||
relation = value.relationTo;
|
||||
}
|
||||
// If this field can be related to many collections,
|
||||
// Set relationTo based on value
|
||||
if (hasManyRelations && value && value.relationTo) {
|
||||
relation = value.relationTo;
|
||||
}
|
||||
|
||||
if (relation) {
|
||||
const relatedCollection = findRelatedCollection(relation);
|
||||
if (relation) {
|
||||
const relatedCollection = this.collections[relation].config;
|
||||
|
||||
if (relatedCollection) {
|
||||
let relatedDocumentData = data[field.name][i];
|
||||
let dataToHook = resultingData[field.name][i];
|
||||
if (relatedCollection) {
|
||||
let relatedDocumentData = data[field.name][i];
|
||||
let dataToHook = resultingData[field.name][i];
|
||||
|
||||
if (hasManyRelations) {
|
||||
relatedDocumentData = data[field.name][i].value;
|
||||
dataToHook = resultingData[field.name][i].value;
|
||||
}
|
||||
if (hasManyRelations) {
|
||||
relatedDocumentData = data[field.name][i].value;
|
||||
dataToHook = resultingData[field.name][i].value;
|
||||
}
|
||||
|
||||
// Only run hooks for populated sub documents - NOT IDs
|
||||
if (relatedDocumentData && typeof relatedDocumentData !== 'string') {
|
||||
// Perform field hooks on related collection
|
||||
dataToHook = await performFieldOperations(config, relatedCollection, {
|
||||
req,
|
||||
data: relatedDocumentData,
|
||||
hook: 'afterRead',
|
||||
operationName: 'read',
|
||||
});
|
||||
|
||||
await relatedCollection.hooks.afterRead.reduce(async (priorHook, currentHook) => {
|
||||
await priorHook;
|
||||
|
||||
dataToHook = await currentHook({
|
||||
// Only run hooks for populated sub documents - NOT IDs
|
||||
if (relatedDocumentData && typeof relatedDocumentData !== 'string') {
|
||||
// Perform field hooks on related collection
|
||||
dataToHook = await recursivePerformFieldOperations(relatedCollection, {
|
||||
req,
|
||||
doc: relatedDocumentData,
|
||||
}) || dataToHook;
|
||||
}, Promise.resolve());
|
||||
data: relatedDocumentData,
|
||||
hook: 'afterRead',
|
||||
operationName: 'read',
|
||||
});
|
||||
|
||||
await relatedCollection.hooks.afterRead.reduce(async (priorHook, currentHook) => {
|
||||
await priorHook;
|
||||
|
||||
dataToHook = await currentHook({
|
||||
req,
|
||||
doc: relatedDocumentData,
|
||||
}) || dataToHook;
|
||||
}, Promise.resolve());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
relationshipDocPromises.push(generateRelationshipDocPromise());
|
||||
});
|
||||
|
||||
await Promise.all(relationshipDocPromises);
|
||||
|
||||
// Otherwise, there is only one related document
|
||||
} else {
|
||||
let relation = field.relationTo;
|
||||
@@ -143,7 +188,7 @@ const performFieldOperations = async (config, entityConfig, operation) => {
|
||||
relation = data[field.name].relationTo;
|
||||
}
|
||||
|
||||
const relatedCollection = findRelatedCollection(relation);
|
||||
const relatedCollection = this.collections[relation].config;
|
||||
|
||||
if (relatedCollection) {
|
||||
let relatedDocumentData = data[field.name];
|
||||
@@ -157,7 +202,7 @@ const performFieldOperations = async (config, entityConfig, operation) => {
|
||||
// Only run hooks for populated sub documents - NOT IDs
|
||||
if (relatedDocumentData && typeof relatedDocumentData !== 'string') {
|
||||
// Perform field hooks on related collection
|
||||
dataToHook = await performFieldOperations(config, relatedCollection, {
|
||||
dataToHook = await recursivePerformFieldOperations(relatedCollection, {
|
||||
req,
|
||||
data: relatedDocumentData,
|
||||
hook: 'afterRead',
|
||||
@@ -255,10 +300,11 @@ const performFieldOperations = async (config, entityConfig, operation) => {
|
||||
}
|
||||
|
||||
await Promise.all(accessPromises);
|
||||
await Promise.all(relationshipPopulationPromises);
|
||||
await Promise.all(hookPromises);
|
||||
|
||||
return fullData;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
module.exports = performFieldOperations;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const express = require('express');
|
||||
const buildModel = require('./buildModel');
|
||||
const routes = require('./routes');
|
||||
|
||||
function initGlobals() {
|
||||
if (this.config.globals) {
|
||||
@@ -8,7 +8,16 @@ function initGlobals() {
|
||||
config: this.config.globals,
|
||||
};
|
||||
|
||||
this.router.use(routes(this.config, this.globals.Model));
|
||||
const router = express.Router();
|
||||
|
||||
this.config.globals.forEach((global) => {
|
||||
router
|
||||
.route(`/globals/${global.slug}`)
|
||||
.get(this.requestHandlers.globals.findOne(global))
|
||||
.post(this.requestHandlers.globals.update(global));
|
||||
});
|
||||
|
||||
this.router.use(router);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
const executeAccess = require('../../auth/executeAccess');
|
||||
const performFieldOperations = require('../../fields/performFieldOperations');
|
||||
|
||||
const findOne = async (args) => {
|
||||
async function findOne(args) {
|
||||
const { globals: { Model } } = this;
|
||||
|
||||
const {
|
||||
config,
|
||||
globalConfig,
|
||||
Model,
|
||||
req,
|
||||
req: {
|
||||
locale,
|
||||
@@ -48,7 +47,7 @@ const findOne = async (args) => {
|
||||
// 4. Execute field-level hooks and access
|
||||
// /////////////////////////////////////
|
||||
|
||||
doc = performFieldOperations(config, globalConfig, {
|
||||
doc = this.performFieldOperations(globalConfig, {
|
||||
data: doc,
|
||||
hook: 'afterRead',
|
||||
operationName: 'read',
|
||||
@@ -74,6 +73,6 @@ const findOne = async (args) => {
|
||||
// /////////////////////////////////////
|
||||
|
||||
return doc;
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = findOne;
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
const update = require('./update');
|
||||
const findOne = require('./findOne');
|
||||
|
||||
module.exports = {
|
||||
findOne,
|
||||
update,
|
||||
};
|
||||
@@ -1,13 +1,12 @@
|
||||
const deepmerge = require('deepmerge');
|
||||
const overwriteMerge = require('../../utilities/overwriteMerge');
|
||||
const executeAccess = require('../../auth/executeAccess');
|
||||
const performFieldOperations = require('../../fields/performFieldOperations');
|
||||
|
||||
const update = async (args) => {
|
||||
async function update(args) {
|
||||
const { config, globals: { Model } } = this;
|
||||
|
||||
const {
|
||||
config,
|
||||
globalConfig,
|
||||
Model,
|
||||
slug,
|
||||
req,
|
||||
req: {
|
||||
@@ -65,7 +64,7 @@ const update = async (args) => {
|
||||
// 5. Execute field-level hooks, access, and validation
|
||||
// /////////////////////////////////////
|
||||
|
||||
data = await performFieldOperations(config, globalConfig, {
|
||||
data = await this.performFieldOperations(globalConfig, {
|
||||
data,
|
||||
req,
|
||||
hook: 'beforeUpdate',
|
||||
@@ -87,7 +86,7 @@ const update = async (args) => {
|
||||
// 7. Execute field-level hooks and access
|
||||
// /////////////////////////////////////
|
||||
|
||||
global = await performFieldOperations(config, globalConfig, {
|
||||
global = await this.performFieldOperations(globalConfig, {
|
||||
data: global,
|
||||
hook: 'afterRead',
|
||||
operationName: 'read',
|
||||
@@ -113,6 +112,6 @@ const update = async (args) => {
|
||||
// /////////////////////////////////////
|
||||
|
||||
return global;
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = update;
|
||||
|
||||
@@ -1,23 +1,26 @@
|
||||
const httpStatus = require('http-status');
|
||||
const { findOne } = require('../operations');
|
||||
|
||||
const findOneHandler = (config, Model, globalConfig) => async (req, res, next) => {
|
||||
try {
|
||||
const { slug } = globalConfig;
|
||||
function findOne(globalConfig) {
|
||||
async function handler(req, res, next) {
|
||||
try {
|
||||
const { slug } = globalConfig;
|
||||
|
||||
const result = await findOne({
|
||||
req,
|
||||
Model,
|
||||
config,
|
||||
globalConfig,
|
||||
slug,
|
||||
depth: req.query.depth,
|
||||
});
|
||||
const result = await this.operations.globals.findOne({
|
||||
req,
|
||||
globalConfig,
|
||||
slug,
|
||||
depth: req.query.depth,
|
||||
});
|
||||
|
||||
return res.status(httpStatus.OK).json(result);
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
return res.status(httpStatus.OK).json(result);
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = findOneHandler;
|
||||
const findOneHandler = handler.bind(this);
|
||||
|
||||
return findOneHandler;
|
||||
}
|
||||
|
||||
module.exports = findOne;
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
const findOne = require('./findOne');
|
||||
const update = require('./update');
|
||||
|
||||
module.exports = {
|
||||
findOne,
|
||||
update,
|
||||
};
|
||||
@@ -1,24 +1,26 @@
|
||||
const httpStatus = require('http-status');
|
||||
const { update } = require('../operations');
|
||||
|
||||
const updateHandler = (config, Model, globalConfig) => async (req, res, next) => {
|
||||
try {
|
||||
const { slug } = globalConfig;
|
||||
function update(globalConfig) {
|
||||
async function handler(req, res, next) {
|
||||
try {
|
||||
const { slug } = globalConfig;
|
||||
|
||||
const result = await update({
|
||||
req,
|
||||
Model,
|
||||
config,
|
||||
globalConfig,
|
||||
slug,
|
||||
depth: req.query.depth,
|
||||
data: req.body,
|
||||
});
|
||||
const result = await this.operations.globals.update({
|
||||
req,
|
||||
globalConfig,
|
||||
slug,
|
||||
depth: req.query.depth,
|
||||
data: req.body,
|
||||
});
|
||||
|
||||
return res.status(httpStatus.OK).json({ message: 'Global saved successfully.', result });
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
return res.status(httpStatus.OK).json({ message: 'Global saved successfully.', result });
|
||||
} catch (error) {
|
||||
return next(error);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = updateHandler;
|
||||
const updateHandler = handler.bind(this);
|
||||
return updateHandler;
|
||||
}
|
||||
|
||||
module.exports = update;
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
const express = require('express');
|
||||
const requestHandlers = require('./requestHandlers');
|
||||
|
||||
const { update, findOne } = requestHandlers;
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
const registerGlobals = (config, Globals) => {
|
||||
config.globals.forEach((global) => {
|
||||
router
|
||||
.route(`/globals/${global.slug}`)
|
||||
.get(findOne(config, Globals, global))
|
||||
.post(update(config, Globals, global));
|
||||
});
|
||||
|
||||
return router;
|
||||
};
|
||||
|
||||
module.exports = registerGlobals;
|
||||
@@ -16,7 +16,6 @@ const { GraphQLJSON } = require('graphql-type-json');
|
||||
const formatName = require('../utilities/formatName');
|
||||
const combineParentName = require('../utilities/combineParentName');
|
||||
const withNullableType = require('./withNullableType');
|
||||
const { find } = require('../../collections/operations');
|
||||
|
||||
function buildObjectType(name, fields, parentName, baseFields = {}) {
|
||||
const recursiveBuildObjectType = buildObjectType.bind(this);
|
||||
@@ -122,7 +121,7 @@ function buildObjectType(name, fields, parentName, baseFields = {}) {
|
||||
id = relatedDoc.value;
|
||||
}
|
||||
|
||||
const result = await find({
|
||||
const result = await this.operations.collections.find({
|
||||
Model: this.collections[relatedCollectionSlug].Model,
|
||||
query: {
|
||||
where: {
|
||||
@@ -169,7 +168,7 @@ function buildObjectType(name, fields, parentName, baseFields = {}) {
|
||||
if (args.page) relatedDocumentQuery.paginate.page = args.page;
|
||||
if (args.limit) relatedDocumentQuery.paginate.limit = args.limit;
|
||||
|
||||
const relatedDocument = await find();
|
||||
const relatedDocument = await this.operations.collections.find();
|
||||
|
||||
if (relatedDocument.docs[0]) return relatedDocument.docs[0];
|
||||
|
||||
|
||||
40
src/index.js
40
src/index.js
@@ -2,7 +2,9 @@ require('es6-promise').polyfill();
|
||||
require('isomorphic-fetch');
|
||||
|
||||
const express = require('express');
|
||||
const graphQLPlayground = require('graphql-playground-middleware-express').default;
|
||||
// const graphQLPlayground = require('graphql-playground-middleware-express').default;
|
||||
const bindOperations = require('./init/bindOperations');
|
||||
const bindRequestHandlers = require('./init/bindRequestHandlers');
|
||||
const getConfig = require('./utilities/getConfig');
|
||||
const authenticate = require('./express/middleware/authenticate');
|
||||
const connectMongoose = require('./mongoose/connect');
|
||||
@@ -12,12 +14,12 @@ const initAuth = require('./auth/init');
|
||||
const initCollections = require('./collections/init');
|
||||
const initGlobals = require('./globals/init');
|
||||
const initStatic = require('./express/static');
|
||||
const GraphQL = require('./graphql');
|
||||
// const GraphQL = require('./graphql');
|
||||
const sanitizeConfig = require('./utilities/sanitizeConfig');
|
||||
const buildEmail = require('./email/build');
|
||||
const identifyAPI = require('./express/middleware/identifyAPI');
|
||||
// const identifyAPI = require('./express/middleware/identifyAPI');
|
||||
const errorHandler = require('./express/middleware/errorHandler');
|
||||
const { access } = require('./auth/requestHandlers');
|
||||
const performFieldOperations = require('./fields/performFieldOperations');
|
||||
|
||||
class Payload {
|
||||
constructor(options) {
|
||||
@@ -31,6 +33,9 @@ class Payload {
|
||||
this.router = express.Router();
|
||||
this.collections = {};
|
||||
|
||||
bindOperations(this);
|
||||
bindRequestHandlers(this);
|
||||
|
||||
this.initAuth = initAuth.bind(this);
|
||||
this.initCollections = initCollections.bind(this);
|
||||
this.initGlobals = initGlobals.bind(this);
|
||||
@@ -39,6 +44,7 @@ class Payload {
|
||||
this.getMockEmailCredentials = this.getMockEmailCredentials.bind(this);
|
||||
this.initStatic = initStatic.bind(this);
|
||||
this.initAdmin = initAdmin.bind(this);
|
||||
this.performFieldOperations = performFieldOperations.bind(this);
|
||||
|
||||
// Configure email service
|
||||
this.email = this.buildEmail();
|
||||
@@ -53,22 +59,22 @@ class Payload {
|
||||
this.initGlobals();
|
||||
this.initAdmin();
|
||||
|
||||
this.router.get('/access', access(this.config));
|
||||
this.router.get('/access', this.requestHandlers.collections.auth.access);
|
||||
|
||||
const graphQLHandler = new GraphQL(this);
|
||||
// const graphQLHandler = new GraphQL(this);
|
||||
|
||||
this.router.use(
|
||||
this.config.routes.graphQL,
|
||||
identifyAPI('GraphQL'),
|
||||
(req, res) => graphQLHandler.init(req, res)(req, res),
|
||||
);
|
||||
// this.router.use(
|
||||
// this.config.routes.graphQL,
|
||||
// identifyAPI('GraphQL'),
|
||||
// (req, res) => graphQLHandler.init(req, res)(req, res),
|
||||
// );
|
||||
|
||||
this.router.get(this.config.routes.graphQLPlayground, graphQLPlayground({
|
||||
endpoint: `${this.config.routes.api}${this.config.routes.graphQL}`,
|
||||
settings: {
|
||||
'request.credentials': 'include',
|
||||
},
|
||||
}));
|
||||
// this.router.get(this.config.routes.graphQLPlayground, graphQLPlayground({
|
||||
// endpoint: `${this.config.routes.api}${this.config.routes.graphQL}`,
|
||||
// settings: {
|
||||
// 'request.credentials': 'include',
|
||||
// },
|
||||
// }));
|
||||
|
||||
// Bind router to API
|
||||
this.express.use(this.config.routes.api, this.router);
|
||||
|
||||
53
src/init/bindOperations.js
Normal file
53
src/init/bindOperations.js
Normal file
@@ -0,0 +1,53 @@
|
||||
const access = require('../auth/operations/access');
|
||||
const forgotPassword = require('../auth/operations/forgotPassword');
|
||||
const init = require('../auth/operations/init');
|
||||
const login = require('../auth/operations/login');
|
||||
const logout = require('../auth/operations/logout');
|
||||
const me = require('../auth/operations/me');
|
||||
const refresh = require('../auth/operations/refresh');
|
||||
const register = require('../auth/operations/register');
|
||||
const registerFirstUser = require('../auth/operations/registerFirstUser');
|
||||
const resetPassword = require('../auth/operations/resetPassword');
|
||||
const authUpdate = require('../auth/operations/update');
|
||||
|
||||
const create = require('../collections/operations/create');
|
||||
const find = require('../collections/operations/find');
|
||||
const findByID = require('../collections/operations/findByID');
|
||||
const update = require('../collections/operations/update');
|
||||
const deleteHandler = require('../collections/operations/delete');
|
||||
|
||||
const findOne = require('../globals/operations/findOne');
|
||||
const globalUpdate = require('../globals/operations/update');
|
||||
|
||||
function bindOperations(ctx) {
|
||||
const payload = ctx;
|
||||
|
||||
payload.operations = {
|
||||
collections: {
|
||||
create: create.bind(ctx),
|
||||
find: find.bind(ctx),
|
||||
findByID: findByID.bind(ctx),
|
||||
update: update.bind(ctx),
|
||||
delete: deleteHandler.bind(ctx),
|
||||
auth: {
|
||||
access: access.bind(ctx),
|
||||
forgotPassword: forgotPassword.bind(ctx),
|
||||
init: init.bind(ctx),
|
||||
login: login.bind(ctx),
|
||||
logout: logout.bind(ctx),
|
||||
me: me.bind(ctx),
|
||||
refresh: refresh.bind(ctx),
|
||||
register: register.bind(ctx),
|
||||
registerFirstUser: registerFirstUser.bind(ctx),
|
||||
resetPassword: resetPassword.bind(ctx),
|
||||
update: authUpdate.bind(ctx),
|
||||
},
|
||||
},
|
||||
globals: {
|
||||
findOne: findOne.bind(ctx),
|
||||
update: globalUpdate.bind(ctx),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = bindOperations;
|
||||
53
src/init/bindRequestHandlers.js
Normal file
53
src/init/bindRequestHandlers.js
Normal file
@@ -0,0 +1,53 @@
|
||||
const access = require('../auth/requestHandlers/access');
|
||||
const forgotPassword = require('../auth/requestHandlers/forgotPassword');
|
||||
const init = require('../auth/requestHandlers/init');
|
||||
const login = require('../auth/requestHandlers/login');
|
||||
const logout = require('../auth/requestHandlers/logout');
|
||||
const me = require('../auth/requestHandlers/me');
|
||||
const refresh = require('../auth/requestHandlers/refresh');
|
||||
const register = require('../auth/requestHandlers/register');
|
||||
const registerFirstUser = require('../auth/requestHandlers/registerFirstUser');
|
||||
const resetPassword = require('../auth/requestHandlers/resetPassword');
|
||||
const authUpdate = require('../auth/requestHandlers/update');
|
||||
|
||||
const create = require('../collections/requestHandlers/create');
|
||||
const find = require('../collections/requestHandlers/find');
|
||||
const findByID = require('../collections/requestHandlers/findByID');
|
||||
const update = require('../collections/requestHandlers/update');
|
||||
const deleteHandler = require('../collections/requestHandlers/delete');
|
||||
|
||||
const findOne = require('../globals/requestHandlers/findOne');
|
||||
const globalUpdate = require('../globals/requestHandlers/update');
|
||||
|
||||
function bindRequestHandlers(ctx) {
|
||||
const payload = ctx;
|
||||
|
||||
payload.requestHandlers = {
|
||||
collections: {
|
||||
create: create.bind(ctx),
|
||||
find: find.bind(ctx),
|
||||
findByID: findByID.bind(ctx),
|
||||
update: update.bind(ctx),
|
||||
delete: deleteHandler.bind(ctx),
|
||||
auth: {
|
||||
access: access.bind(ctx),
|
||||
forgotPassword: forgotPassword.bind(ctx),
|
||||
init: init.bind(ctx),
|
||||
login: login.bind(ctx),
|
||||
logout: logout.bind(ctx),
|
||||
me: me.bind(ctx),
|
||||
refresh: refresh.bind(ctx),
|
||||
register: register.bind(ctx),
|
||||
registerFirstUser: registerFirstUser.bind(ctx),
|
||||
resetPassword: resetPassword.bind(ctx),
|
||||
update: authUpdate.bind(ctx),
|
||||
},
|
||||
},
|
||||
globals: {
|
||||
findOne: findOne.bind(ctx),
|
||||
update: globalUpdate.bind(ctx),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = bindRequestHandlers;
|
||||
Reference in New Issue
Block a user