Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
a54683e29a | |||
a855665777 | |||
034287672c | |||
e8c0e61c6b | |||
a64a55af0d |
@ -1,7 +0,0 @@
|
|||||||
Commit Message
|
|
||||||
===============
|
|
||||||
Please follow [this convention](http://karma-runner.github.io/1.0/dev/git-commit-msg.html) for git commit message.
|
|
||||||
|
|
||||||
Lint
|
|
||||||
====
|
|
||||||
Please lint your code using `npm run lint` (also `npm run lint -- --fix` to auto-fix).
|
|
24
README.md
24
README.md
@ -30,25 +30,17 @@ await register({
|
|||||||
prefix: '/v1',
|
prefix: '/v1',
|
||||||
name: 'db', // the same name you used for configuring `hapi-sequelize` (options.name)
|
name: 'db', // the same name you used for configuring `hapi-sequelize` (options.name)
|
||||||
defaultConfig: { ... }, // passed as `config` to all routes created
|
defaultConfig: { ... }, // passed as `config` to all routes created
|
||||||
|
|
||||||
// You can specify which models must have routes defined for using the
|
|
||||||
// `models` property. If you omit this property, all models will have
|
|
||||||
// models defined for them. e.g.
|
|
||||||
models: ['cat', 'dog'] // only the cat and dog models will have routes created
|
models: ['cat', 'dog'] // only the cat and dog models will have routes created
|
||||||
// or
|
// or
|
||||||
models: [
|
models: {
|
||||||
// possible methods: list, get, scope, create, destroy, destroyAll, destroyScope, update
|
// possible methods: list, get, scope, create, destroy, destroyAll, destroyScope, update
|
||||||
// the cat model only has get and list methods enabled
|
cat: ['get', 'list'], // the cat model only has get and list methods enabled
|
||||||
{model: 'cat', methods: ['get', 'list']},
|
dog: true, // the dog model has all methods enabled
|
||||||
// the dog model has all methods enabled
|
bat: {
|
||||||
{model: 'dog'},
|
methods: ['list'],
|
||||||
// the cow model also has all methods enabled
|
config: { ... } // if provided, overrides the default config
|
||||||
'cow',
|
}
|
||||||
// the bat model as a custom config for the list method, but uses the default config for create.
|
}
|
||||||
// `config` if provided, overrides the default config
|
|
||||||
{model: 'bat', methods: ['list'], config: { ... }},
|
|
||||||
{model: 'bat', methods: ['create']}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
10
package.json
10
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hapi-sequelize-crud",
|
"name": "@getable/hapi-sequelize-crud",
|
||||||
"version": "2.5.4",
|
"version": "2.4.0",
|
||||||
"description": "Hapi plugin that automatically generates RESTful API for CRUD",
|
"description": "Hapi plugin that automatically generates RESTful API for CRUD",
|
||||||
"main": "build/index.js",
|
"main": "build/index.js",
|
||||||
"config": {
|
"config": {
|
||||||
@ -15,7 +15,7 @@
|
|||||||
"watch": "scripty"
|
"watch": "scripty"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"git": "https://github.com/mdibaiee/hapi-sequelize-crud"
|
"git": "https://github.com/Getable/hapi-sequelize-crud"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"build"
|
"build"
|
||||||
@ -35,11 +35,9 @@
|
|||||||
"scripty": "^1.6.0"
|
"scripty": "^1.6.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"babel": "5.8.3",
|
||||||
"boom": "^3.2.2",
|
"boom": "^3.2.2",
|
||||||
"joi": "7.2.1",
|
"joi": "7.2.1",
|
||||||
"lodash": "4.0.0"
|
"lodash": "4.0.0"
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"babel-polyfill": "^6.13.0"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
66
src/crud.js
66
src/crud.js
@ -3,16 +3,13 @@ import error from './error';
|
|||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { parseInclude, parseWhere } from './utils';
|
import { parseInclude, parseWhere } from './utils';
|
||||||
import { notFound } from 'boom';
|
import { notFound } from 'boom';
|
||||||
import * as associations from './associations/index';
|
|
||||||
|
|
||||||
const createAll = ({ server, model, prefix, config }) => {
|
const createAll = ({server, model, prefix, config}) => {
|
||||||
Object.keys(methods).forEach((method) => {
|
Object.keys(methods).forEach((method) => {
|
||||||
methods[method]({ server, model, prefix, config });
|
methods[method]({server, model, prefix, config});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export { associations };
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The `models` option, becomes `permissions`, and can look like:
|
The `models` option, becomes `permissions`, and can look like:
|
||||||
|
|
||||||
@ -31,42 +28,42 @@ models: {
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default (server, model, { prefix, defaultConfig: config, models: permissions }) => {
|
export default (server, model, {prefix, defaultConfig: config, models: permissions}) => {
|
||||||
const modelName = model._singular;
|
const modelName = model._singular;
|
||||||
|
|
||||||
if (!permissions) {
|
if (!permissions) {
|
||||||
createAll({ server, model, prefix, config });
|
createAll({server, model, prefix, config});
|
||||||
} else if (!Array.isArray(permissions)) {
|
}
|
||||||
throw new Error('hapi-sequelize-crud: `models` property must be an array');
|
else if (Array.isArray(permissions) && permissions.includes(modelName)) {
|
||||||
} else if (permissions.includes(modelName)) {
|
createAll({server, model, prefix, config});
|
||||||
createAll({ server, model, prefix, config });
|
}
|
||||||
} else {
|
else if (_.isPlainObject(permissions)) {
|
||||||
const permissionOptions = permissions.filter((permission) => {
|
const permittedModels = Object.keys(permissions);
|
||||||
return permission.model === modelName;
|
|
||||||
|
if (permissions[modelName] === true) {
|
||||||
|
createAll({server, model, prefix, config});
|
||||||
|
}
|
||||||
|
else if (permittedModels.includes(modelName)) {
|
||||||
|
if (Array.isArray(permissions[modelName])) {
|
||||||
|
permissions[modelName].forEach((method) => {
|
||||||
|
methods[method]({server, model, prefix, config});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
permissionOptions.forEach((permissionOption) => {
|
else if (_.isPlainObject(permissions[modelName])) {
|
||||||
if (_.isPlainObject(permissionOption)) {
|
permissions[modelName].methods.forEach((method) => {
|
||||||
const permissionConfig = permissionOption.config || config;
|
|
||||||
|
|
||||||
if (permissionOption.methods) {
|
|
||||||
permissionOption.methods.forEach((method) => {
|
|
||||||
methods[method]({
|
methods[method]({
|
||||||
server,
|
server,
|
||||||
model,
|
model,
|
||||||
prefix,
|
prefix,
|
||||||
config: permissionConfig,
|
config: permissions[modelName].config || config,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
createAll({ server, model, prefix, config: permissionConfig });
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const list = ({ server, model, prefix, config }) => {
|
export const list = ({server, model, prefix, config}) => {
|
||||||
server.route({
|
server.route({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path: `${prefix}/${model._plural}`,
|
path: `${prefix}/${model._plural}`,
|
||||||
@ -89,7 +86,7 @@ export const list = ({ server, model, prefix, config }) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const get = ({ server, model, prefix, config }) => {
|
export const get = ({server, model, prefix, config}) => {
|
||||||
server.route({
|
server.route({
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
path: `${prefix}/${model._singular}/{id?}`,
|
path: `${prefix}/${model._singular}/{id?}`,
|
||||||
@ -117,7 +114,7 @@ export const get = ({ server, model, prefix, config }) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const scope = ({ server, model, prefix, config }) => {
|
export const scope = ({server, model, prefix, config}) => {
|
||||||
const scopes = Object.keys(model.options.scopes);
|
const scopes = Object.keys(model.options.scopes);
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
@ -143,7 +140,7 @@ export const scope = ({ server, model, prefix, config }) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const create = ({ server, model, prefix, config }) => {
|
export const create = ({server, model, prefix, config}) => {
|
||||||
server.route({
|
server.route({
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
path: `${prefix}/${model._singular}`,
|
path: `${prefix}/${model._singular}`,
|
||||||
@ -159,7 +156,7 @@ export const create = ({ server, model, prefix, config }) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const destroy = ({ server, model, prefix, config }) => {
|
export const destroy = ({server, model, prefix, config}) => {
|
||||||
server.route({
|
server.route({
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
path: `${prefix}/${model._singular}/{id?}`,
|
path: `${prefix}/${model._singular}/{id?}`,
|
||||||
@ -180,7 +177,7 @@ export const destroy = ({ server, model, prefix, config }) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const destroyAll = ({ server, model, prefix, config }) => {
|
export const destroyAll = ({server, model, prefix, config}) => {
|
||||||
server.route({
|
server.route({
|
||||||
method: 'DELETE',
|
method: 'DELETE',
|
||||||
path: `${prefix}/${model._plural}`,
|
path: `${prefix}/${model._plural}`,
|
||||||
@ -200,7 +197,7 @@ export const destroyAll = ({ server, model, prefix, config }) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const destroyScope = ({ server, model, prefix, config }) => {
|
export const destroyScope = ({server, model, prefix, config}) => {
|
||||||
const scopes = Object.keys(model.options.scopes);
|
const scopes = Object.keys(model.options.scopes);
|
||||||
|
|
||||||
server.route({
|
server.route({
|
||||||
@ -228,7 +225,7 @@ export const destroyScope = ({ server, model, prefix, config }) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const update = ({ server, model, prefix, config }) => {
|
export const update = ({server, model, prefix, config}) => {
|
||||||
server.route({
|
server.route({
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
path: `${prefix}/${model._singular}/{id}`,
|
path: `${prefix}/${model._singular}/{id}`,
|
||||||
@ -257,6 +254,9 @@ export const update = ({ server, model, prefix, config }) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
import * as associations from './associations/index';
|
||||||
|
export { associations };
|
||||||
|
|
||||||
const methods = {
|
const methods = {
|
||||||
list, get, scope, create, destroy, destroyAll, destroyScope, update,
|
list, get, scope, create, destroy, destroyAll, destroyScope, update,
|
||||||
};
|
};
|
||||||
|
55
src/error.js
55
src/error.js
@ -1,5 +1,3 @@
|
|||||||
import Boom from 'boom';
|
|
||||||
|
|
||||||
export default (target, key, descriptor) => {
|
export default (target, key, descriptor) => {
|
||||||
const fn = descriptor.value;
|
const fn = descriptor.value;
|
||||||
|
|
||||||
@ -7,60 +5,9 @@ export default (target, key, descriptor) => {
|
|||||||
try {
|
try {
|
||||||
await fn(request, reply);
|
await fn(request, reply);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.original) {
|
console.error(e);
|
||||||
const { code, detail, hint } = e.original;
|
|
||||||
let error;
|
|
||||||
|
|
||||||
// pg error codes https://www.postgresql.org/docs/9.5/static/errcodes-appendix.html
|
|
||||||
if (code && (code.startsWith('22') || code.startsWith('23'))) {
|
|
||||||
error = Boom.wrap(e, 406);
|
|
||||||
} else if (code && (code.startsWith('42'))) {
|
|
||||||
error = Boom.wrap(e, 422);
|
|
||||||
// TODO: we could get better at parse postgres error codes
|
|
||||||
} else {
|
|
||||||
// use a 502 error code since the issue is upstream with postgres, not
|
|
||||||
// this server
|
|
||||||
error = Boom.wrap(e, 502);
|
|
||||||
}
|
|
||||||
|
|
||||||
// detail tends to be more specific information. So, if we have it, use.
|
|
||||||
if (detail) {
|
|
||||||
error.message += `: ${detail}`;
|
|
||||||
error.reformat();
|
|
||||||
}
|
|
||||||
|
|
||||||
// hint might provide useful information about how to fix the problem
|
|
||||||
if (hint) {
|
|
||||||
error.message += ` Hint: ${hint}`;
|
|
||||||
error.reformat();
|
|
||||||
}
|
|
||||||
|
|
||||||
reply(error);
|
|
||||||
} else if (!e.isBoom) {
|
|
||||||
const { message } = e;
|
|
||||||
let err;
|
|
||||||
|
|
||||||
if (e.name === 'SequelizeValidationError')
|
|
||||||
err = Boom.badData(message);
|
|
||||||
else if (e.name === 'SequelizeConnectionTimedOutError')
|
|
||||||
err = Boom.gatewayTimeout(message);
|
|
||||||
else if (e.name === 'SequelizeHostNotReachableError')
|
|
||||||
err = Boom.serverUnavailable(message);
|
|
||||||
else if (e.name === 'SequelizeUniqueConstraintError')
|
|
||||||
err = Boom.conflict(message);
|
|
||||||
else if (e.name === 'SequelizeForeignKeyConstraintError')
|
|
||||||
err = Boom.expectationFailed(message);
|
|
||||||
else if (e.name === 'SequelizeExclusionConstraintError')
|
|
||||||
err = Boom.expectationFailed(message);
|
|
||||||
else if (e.name === 'SequelizeConnectionError')
|
|
||||||
err = Boom.badGateway(message);
|
|
||||||
else err = Boom.badImplementation(message);
|
|
||||||
|
|
||||||
reply(err);
|
|
||||||
} else {
|
|
||||||
reply(e);
|
reply(e);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return descriptor;
|
return descriptor;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
if (!global._babelPolyfill) {
|
if (!global._babelPolyfill) {
|
||||||
require('babel-polyfill');
|
require('babel/polyfill');
|
||||||
}
|
}
|
||||||
|
|
||||||
import crud, { associations } from './crud';
|
import crud, { associations } from './crud';
|
||||||
|
Reference in New Issue
Block a user