Merge pull request #95 from keen-studio/src-directory-restructure
refactor directory structure
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import validate from 'express-validation';
|
||||
import Joi from 'joi';
|
||||
|
||||
// TODO: move field specific validations to the config
|
||||
export default {
|
||||
post: validate({
|
||||
body: {
|
||||
|
||||
@@ -1,54 +1,54 @@
|
||||
export default {
|
||||
port: 3000,
|
||||
serverUrl: "http://localhost:3000",
|
||||
cors: ['http://localhost:8080', 'http://localhost:8081'],
|
||||
adminURL: "/payload-login",
|
||||
serverUrl: 'http://localhost:3000',
|
||||
cors: ['http://localhost', 'http://localhost:8080', 'http://localhost:8081'],
|
||||
adminURL: '/payload-login',
|
||||
routes: {
|
||||
api: "/api",
|
||||
admin: "/admin"
|
||||
api: '/api',
|
||||
admin: '/admin'
|
||||
},
|
||||
mongoURL: "mongodb://localhost/payload",
|
||||
mongoURL: 'mongodb://localhost/payload',
|
||||
roles: [
|
||||
"admin",
|
||||
"editor",
|
||||
"moderator",
|
||||
"user",
|
||||
"viewer"
|
||||
'admin',
|
||||
'editor',
|
||||
'moderator',
|
||||
'user',
|
||||
'viewer'
|
||||
],
|
||||
localization: {
|
||||
locales: [
|
||||
"en",
|
||||
"es"
|
||||
'en',
|
||||
'es'
|
||||
],
|
||||
defaultLocale: "en",
|
||||
defaultLocale: 'en',
|
||||
fallback: true
|
||||
},
|
||||
staticUrl: "/media",
|
||||
staticDir: "demo/media",
|
||||
staticUrl: '/media',
|
||||
staticDir: 'demo/media',
|
||||
imageSizes: [
|
||||
{
|
||||
name: "tablet",
|
||||
name: 'tablet',
|
||||
width: 640,
|
||||
height: 480,
|
||||
crop: "left top"
|
||||
crop: 'left top'
|
||||
},
|
||||
{
|
||||
name: "mobile",
|
||||
name: 'mobile',
|
||||
width: 320,
|
||||
height: 240,
|
||||
crop: "left top"
|
||||
crop: 'left top'
|
||||
},
|
||||
{
|
||||
name: "icon",
|
||||
name: 'icon',
|
||||
width: 16,
|
||||
height: 16
|
||||
}
|
||||
],
|
||||
email: {
|
||||
provider: "mock"
|
||||
provider: 'mock'
|
||||
},
|
||||
graphQL: {
|
||||
path: "/graphql",
|
||||
path: '/graphql',
|
||||
graphiql: true
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* @param roles
|
||||
* @returns {Function}
|
||||
*/
|
||||
export default function checkRole(...roles) {
|
||||
export default function checkRoleMiddleware(...roles) {
|
||||
return function (req, res, next) {
|
||||
if (!roles.some(role => role === req.user.role)) res.status(401).send('Role not authorized.');
|
||||
else next();
|
||||
@@ -1,7 +1,7 @@
|
||||
import jwt from 'jsonwebtoken';
|
||||
import passport from 'passport';
|
||||
import httpStatus from 'http-status';
|
||||
import APIError from '../lib/helpers/APIError';
|
||||
import APIError from '../errors/APIError';
|
||||
|
||||
export default User => ({
|
||||
/**
|
||||
|
||||
@@ -1,34 +1,34 @@
|
||||
const httpStatus = require('http-status');
|
||||
|
||||
/**
|
||||
* @extends Error
|
||||
*/
|
||||
class ExtendableError extends Error {
|
||||
constructor(message, status, isPublic) {
|
||||
super(message);
|
||||
this.name = this.constructor.name;
|
||||
this.message = message;
|
||||
this.status = status;
|
||||
this.isPublic = isPublic;
|
||||
this.isOperational = true; // This is required since bluebird 4 doesn't append it anymore.
|
||||
Error.captureStackTrace(this, this.constructor.name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing an API error.
|
||||
* @extends ExtendableError
|
||||
*/
|
||||
class APIError extends ExtendableError {
|
||||
/**
|
||||
* Creates an API error.
|
||||
* @param {string} message - Error message.
|
||||
* @param {number} status - HTTP status code of error.
|
||||
* @param {boolean} isPublic - Whether the message should be visible to user or not.
|
||||
*/
|
||||
constructor(message, status = httpStatus.INTERNAL_SERVER_ERROR, isPublic = false) {
|
||||
super(message, status, isPublic);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = APIError;
|
||||
const httpStatus = require('http-status');
|
||||
|
||||
/**
|
||||
* @extends Error
|
||||
*/
|
||||
class ExtendableError extends Error {
|
||||
constructor(message, status, isPublic) {
|
||||
super(message);
|
||||
this.name = this.constructor.name;
|
||||
this.message = message;
|
||||
this.status = status;
|
||||
this.isPublic = isPublic;
|
||||
this.isOperational = true; // This is required since bluebird 4 doesn't append it anymore.
|
||||
Error.captureStackTrace(this, this.constructor.name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Class representing an API error.
|
||||
* @extends ExtendableError
|
||||
*/
|
||||
class APIError extends ExtendableError {
|
||||
/**
|
||||
* Creates an API error.
|
||||
* @param {string} message - Error message.
|
||||
* @param {number} status - HTTP status code of error.
|
||||
* @param {boolean} isPublic - Whether the message should be visible to user or not.
|
||||
*/
|
||||
constructor(message, status = httpStatus.INTERNAL_SERVER_ERROR, isPublic = false) {
|
||||
super(message, status, isPublic);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = APIError;
|
||||
32
src/index.js
32
src/index.js
@@ -5,20 +5,22 @@ import bodyParser from 'body-parser';
|
||||
import methodOverride from 'method-override';
|
||||
import jwtStrategy from './auth/jwt';
|
||||
import fileUpload from 'express-fileupload';
|
||||
import mediaRoutes from './routes/media.routes';
|
||||
import mediaRoutes from './media/media.routes';
|
||||
import emailRoutes from './auth/passwordResets/email.routes';
|
||||
import autopopulate from './plugins/autopopulate';
|
||||
import paginate from './plugins/paginate';
|
||||
import buildQuery from './plugins/buildQuery';
|
||||
import internationalization from './plugins/internationalization';
|
||||
import { bindModel, locale, checkRole } from './middleware';
|
||||
import { query, create, findOne, destroy, update } from './requestHandlers';
|
||||
import { schemaBaseFields } from './helpers/mongoose/schemaBaseFields';
|
||||
import fieldToSchemaMap from './helpers/mongoose/fieldToSchemaMap';
|
||||
import autopopulate from './mongoose/autopopulate.plugin';
|
||||
import paginate from './mongoose/paginate.plugin';
|
||||
import buildQueryPlugin from './mongoose/buildQuery.plugin';
|
||||
import localizationPlugin from './localization/localization.plugin';
|
||||
import bindModelMiddleware from './mongoose/bindModel.middleware';
|
||||
import localizationMiddleware from './localization/localization.middleware';
|
||||
import checkRoleMiddleware from './auth/checkRole.middleware';
|
||||
import { query, create, findOne, destroy, update } from './mongoose/requestHandlers';
|
||||
import { schemaBaseFields } from './mongoose/schemaBaseFields';
|
||||
import fieldToSchemaMap from './mongoose/fieldToSchemaMap';
|
||||
import authValidate from './auth/validate';
|
||||
import authRequestHandlers from './auth/requestHandlers';
|
||||
import passwordResetConfig from './auth/passwordResets/passwordReset.config';
|
||||
import validateConfig from './lib/validateConfig';
|
||||
import validateConfig from './utilities/validateConfig';
|
||||
|
||||
class Payload {
|
||||
|
||||
@@ -62,7 +64,7 @@ class Payload {
|
||||
options.app.use(methodOverride('X-HTTP-Method-Override'));
|
||||
options.app.use(express.urlencoded({ extended: true }));
|
||||
options.app.use(bodyParser.urlencoded({ extended: true }));
|
||||
options.app.use(locale(options.config.localization));
|
||||
options.app.use(localizationMiddleware(options.config.localization));
|
||||
options.app.use(options.router);
|
||||
|
||||
// TODO: Build safe config before initializing models and routes
|
||||
@@ -84,8 +86,8 @@ class Payload {
|
||||
const Schema = new mongoose.Schema(fields, { timestamps: config.timestamps });
|
||||
|
||||
Schema.plugin(paginate);
|
||||
Schema.plugin(buildQuery);
|
||||
Schema.plugin(internationalization, options.config.localization);
|
||||
Schema.plugin(buildQueryPlugin);
|
||||
Schema.plugin(localizationPlugin, options.config.localization);
|
||||
Schema.plugin(autopopulate);
|
||||
|
||||
if (config.plugins) {
|
||||
@@ -117,7 +119,7 @@ class Payload {
|
||||
options.config.roles.forEach((role) => {
|
||||
options.router
|
||||
.route(`/role/${role}`)
|
||||
.get(passport.authenticate(config.auth.strategy, { session: false }), checkRole(role), auth.me);
|
||||
.get(passport.authenticate(config.auth.strategy, { session: false }), checkRoleMiddleware(role), auth.me);
|
||||
});
|
||||
|
||||
// password resets
|
||||
@@ -133,7 +135,7 @@ class Payload {
|
||||
}
|
||||
|
||||
this.models[config.labels.singular] = model;
|
||||
options.router.all(`/${config.slug}*`, bindModel(model));
|
||||
options.router.all(`/${config.slug}*`, bindModelMiddleware(model));
|
||||
|
||||
options.router.route(`/${config.slug}`)
|
||||
.get(config.policies.read, query)
|
||||
|
||||
@@ -6,7 +6,7 @@ import languageParser from 'accept-language-parser';
|
||||
* @param localization
|
||||
* @returns {Function}
|
||||
*/
|
||||
export default function locale(localization) {
|
||||
export default function localizationMiddleware(localization) {
|
||||
return function (req, res, next) {
|
||||
let setLocale;
|
||||
if (req.query.locale) {
|
||||
@@ -1,6 +1,6 @@
|
||||
import mongoose from 'mongoose';
|
||||
|
||||
export default function internationalization(schema, options) {
|
||||
export default function localizationPlugin(schema, options) {
|
||||
if (!options || !options.locales || !Array.isArray(options.locales) || !options.locales.length) {
|
||||
throw new mongoose.Error('Required locales array is missing');
|
||||
}
|
||||
@@ -21,7 +21,7 @@ export default function internationalization(schema, options) {
|
||||
schema.eachPath((path, schemaType) => {
|
||||
|
||||
if (schemaType.schema) { // propagate plugin initialization for sub-documents schemas
|
||||
schemaType.schema.plugin(internationalization, pluginOptions);
|
||||
schemaType.schema.plugin(localizationPlugin, pluginOptions);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import mongoose from 'mongoose';
|
||||
import buildQuery from '../plugins/buildQuery';
|
||||
import paginate from '../plugins/paginate';
|
||||
import internationalization from '../plugins/internationalization';
|
||||
import buildQueryPlugin from '../mongoose/buildQuery.plugin';
|
||||
import paginate from '../mongoose/paginate.plugin';
|
||||
import localizationPlugin from '../localization/localization.plugin';
|
||||
|
||||
const mediaModelLoader = (config) => {
|
||||
const MediaSchema = new mongoose.Schema({
|
||||
@@ -19,8 +19,8 @@ const mediaModelLoader = (config) => {
|
||||
);
|
||||
|
||||
MediaSchema.plugin(paginate);
|
||||
MediaSchema.plugin(buildQuery);
|
||||
MediaSchema.plugin(internationalization, config.localization);
|
||||
MediaSchema.plugin(buildQueryPlugin);
|
||||
MediaSchema.plugin(localizationPlugin, config.localization);
|
||||
|
||||
return mongoose.model('Media', MediaSchema);
|
||||
};
|
||||
@@ -1,34 +1,34 @@
|
||||
import sharp from 'sharp';
|
||||
const { promisify } = require('util');
|
||||
const sizeOf = promisify(require('image-size'));
|
||||
|
||||
function getOutputImageName(sourceImage, size) {
|
||||
let extension = sourceImage.split('.').pop();
|
||||
let filenameWithoutExtension = sourceImage.substr(0, sourceImage.lastIndexOf('.')) || sourceImage;
|
||||
return `${filenameWithoutExtension}-${size.width}x${size.height}.${extension}`;
|
||||
}
|
||||
|
||||
export async function resizeAndSave(config, file) {
|
||||
let sourceImage = `${config.staticDir}/${file.name}`;
|
||||
|
||||
let outputSizes = [];
|
||||
try {
|
||||
const dimensions = await sizeOf(sourceImage);
|
||||
for (let desiredSize of config.imageSizes) {
|
||||
if (desiredSize.width > dimensions.width) {
|
||||
continue;
|
||||
}
|
||||
let outputImageName = getOutputImageName(sourceImage, desiredSize);
|
||||
await sharp(sourceImage)
|
||||
.resize(desiredSize.width, desiredSize.height, {
|
||||
position: desiredSize.crop || 'centre'
|
||||
})
|
||||
.toFile(outputImageName);
|
||||
outputSizes.push({ height: desiredSize.height, width: desiredSize.width });
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('error in resize and save', e.message);
|
||||
}
|
||||
|
||||
return outputSizes;
|
||||
}
|
||||
import sharp from 'sharp';
|
||||
const { promisify } = require('util');
|
||||
const sizeOf = promisify(require('image-size'));
|
||||
|
||||
function getOutputImageName(sourceImage, size) {
|
||||
let extension = sourceImage.split('.').pop();
|
||||
let filenameWithoutExtension = sourceImage.substr(0, sourceImage.lastIndexOf('.')) || sourceImage;
|
||||
return `${filenameWithoutExtension}-${size.width}x${size.height}.${extension}`;
|
||||
}
|
||||
|
||||
export async function resizeAndSave(config, file) {
|
||||
let sourceImage = `${config.staticDir}/${file.name}`;
|
||||
|
||||
let outputSizes = [];
|
||||
try {
|
||||
const dimensions = await sizeOf(sourceImage);
|
||||
for (let desiredSize of config.imageSizes) {
|
||||
if (desiredSize.width > dimensions.width) {
|
||||
continue;
|
||||
}
|
||||
let outputImageName = getOutputImageName(sourceImage, desiredSize);
|
||||
await sharp(sourceImage)
|
||||
.resize(desiredSize.width, desiredSize.height, {
|
||||
position: desiredSize.crop || 'centre'
|
||||
})
|
||||
.toFile(outputImageName);
|
||||
outputSizes.push({ height: desiredSize.height, width: desiredSize.width });
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('error in resize and save', e.message);
|
||||
}
|
||||
|
||||
return outputSizes;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import mkdirp from 'mkdirp';
|
||||
import { resizeAndSave } from '../utils/imageResizer';
|
||||
import { resizeAndSave } from './imageResizer';
|
||||
import httpStatus from 'http-status';
|
||||
import modelById from '../resolvers/modelById';
|
||||
import modelById from '../mongoose/resolvers/modelById';
|
||||
|
||||
export async function update(req, res, next, config) {
|
||||
req.model.setDefaultLocale(req.locale);
|
||||
@@ -1,15 +1,15 @@
|
||||
import express from 'express';
|
||||
import passport from 'passport';
|
||||
import { upload, update } from '../controllers/media.controller';
|
||||
import { query } from '../requestHandlers';
|
||||
import bindModel from '../middleware/bindModel';
|
||||
import mediaModelLoader from '../models/Media.model';
|
||||
import { upload, update } from './media.controller';
|
||||
import { query } from '../mongoose/requestHandlers';
|
||||
import bindModelMiddleware from '../mongoose/bindModel.middleware';
|
||||
import mediaModelLoader from './Media.model';
|
||||
|
||||
const router = express.Router();
|
||||
const mediaRoutes = config => {
|
||||
|
||||
const mediaModel = mediaModelLoader(config); // Needs config for intl
|
||||
router.all('*', bindModel(mediaModel));
|
||||
router.all('*', bindModelMiddleware(mediaModel));
|
||||
|
||||
router
|
||||
.route('')
|
||||
@@ -1,5 +0,0 @@
|
||||
import bindModel from './bindModel';
|
||||
import checkRole from './checkRole';
|
||||
import locale from './locale';
|
||||
|
||||
export { bindModel, checkRole, locale };
|
||||
@@ -1,153 +1,153 @@
|
||||
'use strict';
|
||||
|
||||
const mongoose = require('mongoose');
|
||||
|
||||
module.exports = function (schema) {
|
||||
const pathsToPopulate = [];
|
||||
|
||||
eachPathRecursive(schema, (pathname, schemaType) => {
|
||||
let option;
|
||||
if (schemaType.options && schemaType.options.autopopulate) {
|
||||
option = schemaType.options.autopopulate;
|
||||
pathsToPopulate.push({
|
||||
options: defaultOptions(pathname, schemaType.options),
|
||||
autopopulate: option
|
||||
});
|
||||
} else if (schemaType.options &&
|
||||
schemaType.options.type &&
|
||||
schemaType.options.type[0] &&
|
||||
schemaType.options.type[0].autopopulate) {
|
||||
option = schemaType.options.type[0].autopopulate;
|
||||
pathsToPopulate.push({
|
||||
options: defaultOptions(pathname, schemaType.options.type[0]),
|
||||
autopopulate: option
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (schema.virtuals) {
|
||||
Object.keys(schema.virtuals).forEach((pathname) => {
|
||||
if (schema.virtuals[pathname].options.autopopulate) {
|
||||
pathsToPopulate.push({
|
||||
options: defaultOptions(pathname, schema.virtuals[pathname].options),
|
||||
autopopulate: schema.virtuals[pathname].options.autopopulate,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var autopopulateHandler = function () {
|
||||
if (this._mongooseOptions &&
|
||||
this._mongooseOptions.lean &&
|
||||
// If lean and user didn't explicitly do `lean({ autopulate: true })`,
|
||||
// skip it. See gh-27, gh-14, gh-48
|
||||
!this._mongooseOptions.lean.autopopulate) {
|
||||
return;
|
||||
}
|
||||
|
||||
const options = this.options || {};
|
||||
if (options.autopopulate === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
let maxDepth = options.maxDepth;
|
||||
|
||||
if (options.autopopulate && options.autopopulate.maxDepth) {
|
||||
maxDepth = options.autopopulate.maxDepth;
|
||||
}
|
||||
|
||||
const depth = options._depth != null ? options._depth : 0;
|
||||
|
||||
if (options.maxDepth > 0 && depth >= maxDepth) {
|
||||
return;
|
||||
}
|
||||
|
||||
const numPaths = pathsToPopulate.length;
|
||||
for (let i = 0; i < numPaths; ++i) {
|
||||
pathsToPopulate[i].options = pathsToPopulate[i].options || {};
|
||||
pathsToPopulate[i].options.options = pathsToPopulate[i].options.options || {};
|
||||
Object.assign(pathsToPopulate[i].options.options, {
|
||||
...options,
|
||||
_depth: depth + 1
|
||||
});
|
||||
processOption.call(this,
|
||||
pathsToPopulate[i].autopopulate, pathsToPopulate[i].options);
|
||||
}
|
||||
};
|
||||
|
||||
schema.
|
||||
pre('find', autopopulateHandler).
|
||||
pre('findOne', autopopulateHandler).
|
||||
pre('findOneAndUpdate', autopopulateHandler);
|
||||
};
|
||||
|
||||
function defaultOptions(pathname, v) {
|
||||
const ret = { path: pathname, options: { maxDepth: 10 } };
|
||||
if (v.ref != null) {
|
||||
ret.model = v.ref;
|
||||
ret.ref = v.ref;
|
||||
}
|
||||
if (v.refPath != null) {
|
||||
ret.refPath = v.refPath;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
function processOption(value, options) {
|
||||
switch (typeof value) {
|
||||
case 'function':
|
||||
handleFunction.call(this, value, options);
|
||||
break;
|
||||
case 'object':
|
||||
handleObject.call(this, value, options);
|
||||
break;
|
||||
default:
|
||||
handlePrimitive.call(this, value, options);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function handlePrimitive(value, options) {
|
||||
if (value) {
|
||||
this.populate(options);
|
||||
}
|
||||
}
|
||||
|
||||
function handleObject(value, optionsToUse) {
|
||||
// Special case: support top-level `maxDepth`
|
||||
if (value.maxDepth != null) {
|
||||
optionsToUse.options = optionsToUse.options || {};
|
||||
optionsToUse.options.maxDepth = value.maxDepth;
|
||||
delete value.maxDepth;
|
||||
}
|
||||
optionsToUse = Object.assign({}, optionsToUse, value);
|
||||
this.populate(optionsToUse);
|
||||
}
|
||||
|
||||
function handleFunction(fn, options) {
|
||||
const val = fn.call(this, options);
|
||||
processOption.call(this, val, options);
|
||||
}
|
||||
|
||||
function mergeOptions(destination, source) {
|
||||
const keys = Object.keys(source);
|
||||
const numKeys = keys.length;
|
||||
for (let i = 0; i < numKeys; ++i) {
|
||||
destination[keys[i]] = source[keys[i]];
|
||||
}
|
||||
}
|
||||
|
||||
function eachPathRecursive(schema, handler, path) {
|
||||
if (!path) {
|
||||
path = [];
|
||||
}
|
||||
schema.eachPath((pathname, schemaType) => {
|
||||
path.push(pathname);
|
||||
if (schemaType.schema) {
|
||||
eachPathRecursive(schemaType.schema, handler, path);
|
||||
} else {
|
||||
handler(path.join('.'), schemaType);
|
||||
}
|
||||
path.pop();
|
||||
});
|
||||
}
|
||||
'use strict';
|
||||
|
||||
const mongoose = require('mongoose');
|
||||
|
||||
module.exports = function (schema) {
|
||||
const pathsToPopulate = [];
|
||||
|
||||
eachPathRecursive(schema, (pathname, schemaType) => {
|
||||
let option;
|
||||
if (schemaType.options && schemaType.options.autopopulate) {
|
||||
option = schemaType.options.autopopulate;
|
||||
pathsToPopulate.push({
|
||||
options: defaultOptions(pathname, schemaType.options),
|
||||
autopopulate: option
|
||||
});
|
||||
} else if (schemaType.options &&
|
||||
schemaType.options.type &&
|
||||
schemaType.options.type[0] &&
|
||||
schemaType.options.type[0].autopopulate) {
|
||||
option = schemaType.options.type[0].autopopulate;
|
||||
pathsToPopulate.push({
|
||||
options: defaultOptions(pathname, schemaType.options.type[0]),
|
||||
autopopulate: option
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (schema.virtuals) {
|
||||
Object.keys(schema.virtuals).forEach((pathname) => {
|
||||
if (schema.virtuals[pathname].options.autopopulate) {
|
||||
pathsToPopulate.push({
|
||||
options: defaultOptions(pathname, schema.virtuals[pathname].options),
|
||||
autopopulate: schema.virtuals[pathname].options.autopopulate,
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var autopopulateHandler = function () {
|
||||
if (this._mongooseOptions &&
|
||||
this._mongooseOptions.lean &&
|
||||
// If lean and user didn't explicitly do `lean({ autopulate: true })`,
|
||||
// skip it. See gh-27, gh-14, gh-48
|
||||
!this._mongooseOptions.lean.autopopulate) {
|
||||
return;
|
||||
}
|
||||
|
||||
const options = this.options || {};
|
||||
if (options.autopopulate === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
let maxDepth = options.maxDepth;
|
||||
|
||||
if (options.autopopulate && options.autopopulate.maxDepth) {
|
||||
maxDepth = options.autopopulate.maxDepth;
|
||||
}
|
||||
|
||||
const depth = options._depth != null ? options._depth : 0;
|
||||
|
||||
if (options.maxDepth > 0 && depth >= maxDepth) {
|
||||
return;
|
||||
}
|
||||
|
||||
const numPaths = pathsToPopulate.length;
|
||||
for (let i = 0; i < numPaths; ++i) {
|
||||
pathsToPopulate[i].options = pathsToPopulate[i].options || {};
|
||||
pathsToPopulate[i].options.options = pathsToPopulate[i].options.options || {};
|
||||
Object.assign(pathsToPopulate[i].options.options, {
|
||||
...options,
|
||||
_depth: depth + 1
|
||||
});
|
||||
processOption.call(this,
|
||||
pathsToPopulate[i].autopopulate, pathsToPopulate[i].options);
|
||||
}
|
||||
};
|
||||
|
||||
schema.
|
||||
pre('find', autopopulateHandler).
|
||||
pre('findOne', autopopulateHandler).
|
||||
pre('findOneAndUpdate', autopopulateHandler);
|
||||
};
|
||||
|
||||
function defaultOptions(pathname, v) {
|
||||
const ret = { path: pathname, options: { maxDepth: 10 } };
|
||||
if (v.ref != null) {
|
||||
ret.model = v.ref;
|
||||
ret.ref = v.ref;
|
||||
}
|
||||
if (v.refPath != null) {
|
||||
ret.refPath = v.refPath;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
function processOption(value, options) {
|
||||
switch (typeof value) {
|
||||
case 'function':
|
||||
handleFunction.call(this, value, options);
|
||||
break;
|
||||
case 'object':
|
||||
handleObject.call(this, value, options);
|
||||
break;
|
||||
default:
|
||||
handlePrimitive.call(this, value, options);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function handlePrimitive(value, options) {
|
||||
if (value) {
|
||||
this.populate(options);
|
||||
}
|
||||
}
|
||||
|
||||
function handleObject(value, optionsToUse) {
|
||||
// Special case: support top-level `maxDepth`
|
||||
if (value.maxDepth != null) {
|
||||
optionsToUse.options = optionsToUse.options || {};
|
||||
optionsToUse.options.maxDepth = value.maxDepth;
|
||||
delete value.maxDepth;
|
||||
}
|
||||
optionsToUse = Object.assign({}, optionsToUse, value);
|
||||
this.populate(optionsToUse);
|
||||
}
|
||||
|
||||
function handleFunction(fn, options) {
|
||||
const val = fn.call(this, options);
|
||||
processOption.call(this, val, options);
|
||||
}
|
||||
|
||||
function mergeOptions(destination, source) {
|
||||
const keys = Object.keys(source);
|
||||
const numKeys = keys.length;
|
||||
for (let i = 0; i < numKeys; ++i) {
|
||||
destination[keys[i]] = source[keys[i]];
|
||||
}
|
||||
}
|
||||
|
||||
function eachPathRecursive(schema, handler, path) {
|
||||
if (!path) {
|
||||
path = [];
|
||||
}
|
||||
schema.eachPath((pathname, schemaType) => {
|
||||
path.push(pathname);
|
||||
if (schemaType.schema) {
|
||||
eachPathRecursive(schemaType.schema, handler, path);
|
||||
} else {
|
||||
handler(path.join('.'), schemaType);
|
||||
}
|
||||
path.pop();
|
||||
});
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
const bindModel = model => {
|
||||
const bindModelMiddleware = model => {
|
||||
return (req, res, next) => {
|
||||
req.model = model;
|
||||
next();
|
||||
};
|
||||
};
|
||||
|
||||
export default bindModel;
|
||||
export default bindModelMiddleware;
|
||||
@@ -1,6 +1,6 @@
|
||||
/* eslint-disable no-use-before-define */
|
||||
|
||||
export default function buildQuery(schema) {
|
||||
export default function buildQueryPlugin(schema) {
|
||||
|
||||
schema.statics.apiQuery = function (rawParams, locale, cb) {
|
||||
const model = this;
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createAutopopulateOptions } from '../helpers/mongoose/createAutopopulateOptions';
|
||||
import { createAutopopulateOptions } from './createAutopopulateOptions';
|
||||
|
||||
/**
|
||||
* @param {Object} [dbQuery={}]
|
||||
@@ -19,9 +19,9 @@ import { createAutopopulateOptions } from '../helpers/mongoose/createAutopopulat
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
function paginate(dbQuery, options, callback) {
|
||||
function paginatePlugin(dbQuery, options, callback) {
|
||||
dbQuery = dbQuery || {};
|
||||
options = Object.assign({}, paginate.options, options);
|
||||
options = Object.assign({}, paginatePlugin.options, options);
|
||||
options.customLabels = options.customLabels ? options.customLabels : {};
|
||||
|
||||
let defaultLimit = 10;
|
||||
@@ -150,7 +150,7 @@ function paginate(dbQuery, options, callback) {
|
||||
* @param {Schema} schema
|
||||
*/
|
||||
module.exports = function (schema) {
|
||||
schema.statics.paginate = paginate;
|
||||
schema.statics.paginate = paginatePlugin;
|
||||
};
|
||||
|
||||
module.exports.paginate = paginate;
|
||||
module.exports.paginate = paginatePlugin;
|
||||
@@ -1,17 +1,17 @@
|
||||
import httpStatus from 'http-status';
|
||||
|
||||
const create = (req, res) => {
|
||||
req.model.setDefaultLocale(req.locale); // TODO - move to middleware
|
||||
req.model.create(req.body, (err, result) => {
|
||||
if (err)
|
||||
return res.status(httpStatus.INTERNAL_SERVER_ERROR).json({ error: err });
|
||||
|
||||
return res.status(httpStatus.CREATED)
|
||||
.json({
|
||||
message: 'success',
|
||||
result: result.toJSON({ virtuals: true })
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export default create;
|
||||
import httpStatus from 'http-status';
|
||||
|
||||
const create = (req, res) => {
|
||||
req.model.setDefaultLocale(req.locale); // TODO - move to middleware
|
||||
req.model.create(req.body, (err, result) => {
|
||||
if (err)
|
||||
return res.status(httpStatus.INTERNAL_SERVER_ERROR).json({ error: err });
|
||||
|
||||
return res.status(httpStatus.CREATED)
|
||||
.json({
|
||||
message: 'success',
|
||||
result: result.toJSON({ virtuals: true })
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export default create;
|
||||
@@ -1,6 +1,6 @@
|
||||
import httpStatus from 'http-status';
|
||||
import { modelById } from '../resolvers';
|
||||
import { createAutopopulateOptions } from '../helpers/mongoose/createAutopopulateOptions';
|
||||
import { createAutopopulateOptions } from '../createAutopopulateOptions';
|
||||
|
||||
const findOne = (req, res) => {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
const create = query => {
|
||||
return new Promise((resolve, reject) => {
|
||||
query.Model.create(query.input, (err, doc) => {
|
||||
console.log(err,doc);
|
||||
console.log(err, doc);
|
||||
if (err || !doc) {
|
||||
return reject({message: err})
|
||||
}
|
||||
@@ -1,23 +1,23 @@
|
||||
const find = query => {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
query.Model.find({}, (err, docs) => {
|
||||
|
||||
// if (err || !doc) {
|
||||
// return reject({ message: 'not found' })
|
||||
// }
|
||||
|
||||
let result = docs;
|
||||
|
||||
if (query.locale) {
|
||||
docs.setLocale(query.locale, query.fallback);
|
||||
const json = docs.toJSON({ virtuals: true });
|
||||
result = json;
|
||||
}
|
||||
|
||||
resolve(result);
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
export default find;
|
||||
const find = query => {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
query.Model.find({}, (err, docs) => {
|
||||
|
||||
// if (err || !doc) {
|
||||
// return reject({ message: 'not found' })
|
||||
// }
|
||||
|
||||
let result = docs;
|
||||
|
||||
if (query.locale) {
|
||||
docs.setLocale(query.locale, query.fallback);
|
||||
const json = docs.toJSON({ virtuals: true });
|
||||
result = json;
|
||||
}
|
||||
|
||||
resolve(result);
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
export default find;
|
||||
@@ -1,9 +1,9 @@
|
||||
import modelById from './modelById';
|
||||
import find from './find';
|
||||
import create from './create';
|
||||
|
||||
export {
|
||||
modelById,
|
||||
find,
|
||||
create
|
||||
}
|
||||
import modelById from './modelById';
|
||||
import find from './find';
|
||||
import create from './create';
|
||||
|
||||
export {
|
||||
modelById,
|
||||
find,
|
||||
create
|
||||
}
|
||||
@@ -1,24 +1,24 @@
|
||||
const modelById = (query, options) => {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
query.Model.findOne({ _id: query.id }, {}, options, (err, doc) => {
|
||||
|
||||
if (err || !doc) {
|
||||
return reject({ message: 'not found' })
|
||||
}
|
||||
|
||||
let result = doc;
|
||||
|
||||
if (query.locale) {
|
||||
doc.setLocale(query.locale, query.fallback);
|
||||
result = doc.toJSON({ virtuals: true });
|
||||
}
|
||||
|
||||
resolve(options.returnRawDoc
|
||||
? doc
|
||||
: result);
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
export default modelById;
|
||||
const modelById = (query, options) => {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
query.Model.findOne({ _id: query.id }, {}, options, (err, doc) => {
|
||||
|
||||
if (err || !doc) {
|
||||
return reject({ message: 'not found' })
|
||||
}
|
||||
|
||||
let result = doc;
|
||||
|
||||
if (query.locale) {
|
||||
doc.setLocale(query.locale, query.fallback);
|
||||
result = doc.toJSON({ virtuals: true });
|
||||
}
|
||||
|
||||
resolve(options.returnRawDoc
|
||||
? doc
|
||||
: result);
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
export default modelById;
|
||||
@@ -1,4 +1,5 @@
|
||||
import { checkRole, locale} from '../../middleware';
|
||||
import localizationMiddleware from '../../localization/localization.middleware';
|
||||
import checkRoleMiddleware from '../../auth/checkRole.middleware';
|
||||
import mockExpress from 'jest-mock-express';
|
||||
|
||||
let res = null;
|
||||
@@ -18,7 +19,7 @@ describe('Payload Middleware', () => {
|
||||
}
|
||||
};
|
||||
|
||||
checkRole('user')(req, res, next);
|
||||
checkRoleMiddleware('user')(req, res, next);
|
||||
|
||||
expect(next).toHaveBeenCalledTimes(1);
|
||||
expect(res.status).not.toHaveBeenCalled();
|
||||
@@ -31,7 +32,7 @@ describe('Payload Middleware', () => {
|
||||
}
|
||||
};
|
||||
|
||||
checkRole('admin')(req, res, next);
|
||||
checkRoleMiddleware('admin')(req, res, next);
|
||||
|
||||
expect(next).not.toHaveBeenCalled();
|
||||
expect(res.status).toHaveBeenCalled();
|
||||
@@ -45,7 +46,7 @@ describe('Payload Middleware', () => {
|
||||
}
|
||||
};
|
||||
|
||||
checkRole('admin', 'user')(req, res, next);
|
||||
checkRoleMiddleware('admin', 'user')(req, res, next);
|
||||
|
||||
expect(next).toHaveBeenCalledTimes(1);
|
||||
expect(res.status).not.toHaveBeenCalled();
|
||||
@@ -58,7 +59,7 @@ describe('Payload Middleware', () => {
|
||||
}
|
||||
};
|
||||
|
||||
checkRole('admin', 'owner')(req, res, next);
|
||||
checkRoleMiddleware('admin', 'owner')(req, res, next);
|
||||
|
||||
expect(next).not.toHaveBeenCalled();
|
||||
expect(res.status).toHaveBeenCalled();
|
||||
@@ -87,7 +88,7 @@ describe('Payload Middleware', () => {
|
||||
it('Supports query params', () => {
|
||||
req.query.locale = 'es';
|
||||
|
||||
locale(localization)(req, res, next);
|
||||
localizationMiddleware(localization)(req, res, next);
|
||||
|
||||
expect(next).toHaveBeenCalledTimes(1);
|
||||
expect(req.locale).toEqual(req.query.locale);
|
||||
@@ -96,7 +97,7 @@ describe('Payload Middleware', () => {
|
||||
it('Supports query param fallback to default', () => {
|
||||
req.query.locale = 'pt';
|
||||
|
||||
locale(localization)(req, res, next);
|
||||
localizationMiddleware(localization)(req, res, next);
|
||||
|
||||
expect(next).toHaveBeenCalledTimes(1);
|
||||
expect(req.locale).toEqual(localization.defaultLocale);
|
||||
@@ -105,7 +106,7 @@ describe('Payload Middleware', () => {
|
||||
it('Supports accept-language header', () => {
|
||||
req.headers['accept-language'] = 'es,fr;';
|
||||
|
||||
locale(localization)(req, res, next);
|
||||
localizationMiddleware(localization)(req, res, next);
|
||||
|
||||
expect(next).toHaveBeenCalledTimes(1);
|
||||
expect(req.locale).toEqual('es');
|
||||
@@ -115,7 +116,7 @@ describe('Payload Middleware', () => {
|
||||
req.query.locale = 'pt';
|
||||
req.headers['accept-language'] = 'fr;';
|
||||
|
||||
locale(localization)(req, res, next);
|
||||
localizationMiddleware(localization)(req, res, next);
|
||||
|
||||
expect(next).toHaveBeenCalledTimes(1);
|
||||
expect(req.locale).toEqual(localization.defaultLocale);
|
||||
@@ -125,14 +126,14 @@ describe('Payload Middleware', () => {
|
||||
req.query.locale = 'es';
|
||||
req.headers['accept-language'] = 'en;';
|
||||
|
||||
locale(localization)(req, res, next);
|
||||
localizationMiddleware(localization)(req, res, next);
|
||||
|
||||
expect(next).toHaveBeenCalledTimes(1);
|
||||
expect(req.locale).toEqual('es');
|
||||
});
|
||||
|
||||
it('Supports default locale', () => {
|
||||
locale(localization)(req, res, next);
|
||||
localizationMiddleware(localization)(req, res, next);
|
||||
|
||||
expect(next).toHaveBeenCalledTimes(1);
|
||||
expect(req.locale).toEqual(localization.defaultLocale);
|
||||
@@ -140,7 +141,7 @@ describe('Payload Middleware', () => {
|
||||
|
||||
it('Supports locale all', () => {
|
||||
req.query.locale = '*';
|
||||
locale(localization)(req, res, next);
|
||||
localizationMiddleware(localization)(req, res, next);
|
||||
|
||||
expect(next).toHaveBeenCalledTimes(1);
|
||||
expect(req.locale).toBeUndefined();
|
||||
@@ -149,7 +150,7 @@ describe('Payload Middleware', () => {
|
||||
it('Supports locale in body on post', () => {
|
||||
req.body = {locale: 'es'};
|
||||
req.method = 'post';
|
||||
locale(localization)(req, res, next);
|
||||
localizationMiddleware(localization)(req, res, next);
|
||||
|
||||
expect(next).toHaveBeenCalledTimes(1);
|
||||
expect(req.locale).toEqual('es');
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import mongoose from 'mongoose';
|
||||
const Schema = mongoose.Schema;
|
||||
import mongooseApiQuery from '../../plugins/buildQuery';
|
||||
import mongooseApiQuery from '../../mongoose/buildQuery.plugin';
|
||||
|
||||
const TestUserSchema = new Schema({
|
||||
name: {type: String},
|
||||
|
||||
@@ -3,7 +3,7 @@ import mongoose from 'mongoose';
|
||||
|
||||
const Schema = mongoose.Schema;
|
||||
import { intlModel } from './testModels/IntlModel';
|
||||
import { paramParser } from '../../plugins/buildQuery';
|
||||
import { paramParser } from '../../mongoose/buildQuery.plugin';
|
||||
|
||||
const AuthorSchema = new Schema({
|
||||
name: String,
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import mongoose from 'mongoose';
|
||||
import { schemaBaseFields } from '../../../helpers/mongoose/schemaBaseFields';
|
||||
import paginate from '../../../plugins/paginate';
|
||||
import buildQuery from '../../../plugins/buildQuery';
|
||||
import internationalization from '../../../plugins/internationalization';
|
||||
import { schemaBaseFields } from '../../../mongoose/schemaBaseFields';
|
||||
import paginate from '../../../mongoose/paginate.plugin';
|
||||
import buildQueryPlugin from '../../../mongoose/buildQuery.plugin';
|
||||
import localizationPlugin from '../../../localization/localization.plugin';
|
||||
|
||||
const IntlSchema = new mongoose.Schema({
|
||||
...schemaBaseFields,
|
||||
@@ -15,8 +15,8 @@ const IntlSchema = new mongoose.Schema({
|
||||
);
|
||||
|
||||
IntlSchema.plugin(paginate);
|
||||
IntlSchema.plugin(buildQuery);
|
||||
IntlSchema.plugin(internationalization, {
|
||||
IntlSchema.plugin(buildQueryPlugin);
|
||||
IntlSchema.plugin(localizationPlugin, {
|
||||
locales: [
|
||||
'en',
|
||||
'es'
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
export const convertArrayToObject = (arr, key) => {
|
||||
return arr.reduce((obj, item) => {
|
||||
if (key) {
|
||||
obj[item[key]] = item;
|
||||
return obj;
|
||||
}
|
||||
|
||||
obj[item] = {};
|
||||
return obj;
|
||||
}, {});
|
||||
}
|
||||
|
||||
export const convertObjectToArray = arr => {
|
||||
return Object.values(arr);
|
||||
}
|
||||
|
||||
export const convertArrayToHash = (arr, key) => {
|
||||
return arr.reduce((obj, item, i) => {
|
||||
obj[item[key]] = i;
|
||||
return obj;
|
||||
}, {});
|
||||
}
|
||||
export const convertArrayToObject = (arr, key) => {
|
||||
return arr.reduce((obj, item) => {
|
||||
if (key) {
|
||||
obj[item[key]] = item;
|
||||
return obj;
|
||||
}
|
||||
|
||||
obj[item] = {};
|
||||
return obj;
|
||||
}, {});
|
||||
}
|
||||
|
||||
export const convertObjectToArray = arr => {
|
||||
return Object.values(arr);
|
||||
}
|
||||
|
||||
export const convertArrayToHash = (arr, key) => {
|
||||
return arr.reduce((obj, item, i) => {
|
||||
obj[item[key]] = i;
|
||||
return obj;
|
||||
}, {});
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
export default (keys, obj) => keys.reduce((a, c) => ({ ...a, [c]: obj[c] }), {});
|
||||
export default (keys, obj) => keys.reduce((a, c) => ({ ...a, [c]: obj[c] }), {});
|
||||
@@ -1,3 +1,3 @@
|
||||
const toKebabCase = string => string.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/\s+/g, '-').toLowerCase();
|
||||
|
||||
export default toKebabCase;
|
||||
const toKebabCase = string => string.replace(/([a-z])([A-Z])/g, '$1-$2').replace(/\s+/g, '-').toLowerCase();
|
||||
|
||||
export default toKebabCase;
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DuplicateModelNameError } from './errors/DuplicateModelNameError';
|
||||
import { DuplicateModelNameError } from '../errors/DuplicateModelNameError';
|
||||
|
||||
export default function validateConfig(config, models) {
|
||||
if (models[config.labels.singular]) {
|
||||
@@ -1,3 +1,3 @@
|
||||
export { default as toKebabCase } from './lib/helpers/toKebabCase';
|
||||
export { default as getPropSubset } from './lib/helpers/getPropSubset';
|
||||
export { convertArrayToHash, convertArrayToObject, convertObjectToArray } from './lib/helpers/convertData';
|
||||
export { default as toKebabCase } from './helpers/toKebabCase';
|
||||
export { default as getPropSubset } from './helpers/getPropSubset';
|
||||
export { convertArrayToHash, convertArrayToObject, convertObjectToArray } from './helpers/convertData';
|
||||
|
||||
Reference in New Issue
Block a user