From 52ad030d0dc1d4fd7e9a55eb5286a21ae451c91e Mon Sep 17 00:00:00 2001 From: Mahdi Dibaiee Date: Tue, 19 Jan 2016 10:03:29 +0330 Subject: [PATCH] feat(associations): many-to-many associations fix(associations): fix association queries, must use `include` instead of ForeignKey fix(error): error decorator was missing await, which prevented it from catching errors fix(error): console.error the error refactor(crud): don't use `request.models[name]`, use the model directly chore: README added --- README.md | 74 ++++++++++++++++++++++++++++++++ package.json | 2 +- src/associations/many-to-many.js | 0 src/associations/one-to-many.js | 40 ++++++++++++----- src/associations/one-to-one.js | 42 ++++++++++++------ src/crud.js | 13 +++--- src/error.js | 5 ++- src/index.js | 11 ++++- 8 files changed, 152 insertions(+), 35 deletions(-) create mode 100644 README.md delete mode 100644 src/associations/many-to-many.js diff --git a/README.md b/README.md new file mode 100644 index 0000000..83a8779 --- /dev/null +++ b/README.md @@ -0,0 +1,74 @@ +hapi-sequelize-crud +=================== + +Automatically generate a RESTful API for your models and associations + +This plugin depends on [`hapi-sequelize`](https://github.com/danecando/hapi-sequelize). + +``` +npm install -S hapi-sequelize-crud +``` + +##Configure + +```javascript +// First, register hapi-sequelize +await register({ + register: require('hapi-sequelize'), + options: { ... } +}); + +// Then, define your associations +let db = server.plugins['hapi-sequelize'].db; +let models = db.sequelize.models; +associations(models); // pretend this function defines our associations + +// Now, register hapi-sequelize-crud +await register({ + register: require('hapi-sequelize-crud'), + options: { + prefix: '/v1' + } +}); +``` + +Please note that you should register `hapi-sequelize-crud` after defining your +associations. + +##What do I get + +Let's say you have a `many-to-many` association like this: + +```javascript +Team.belongsToMany(Role, { through: 'TeamRoles' }); +Role.belongsToMany(Team, { through: 'TeamRoles' }); +``` + +You get these: + +``` +# get an array of records +GET /team/{id}/roles +GET /role/{id}/teams +#might also append query parameters to search for +GET /role/{id}/teams?members=5 + +# get a single record +GET /team/{id}/role/{id} +GET /role/{id}/team/{id} + +# create +POST /team/{id}/role +POST /role/{id}/team + +# update +PUT /team/{id}/role/{id} +PUT /role/{id}/team/{id} + +# delete +DELETE /team/{id}/roles #search and destroy +DELETE /role/{id}/teams?members=5 + +DELETE /team/{id}/role/{id} +DELETE /role/{id}/team/{id} +``` diff --git a/package.json b/package.json index 7ec4169..9309ed8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hapi-sequelize-crud", - "version": "1.0.0", + "version": "1.1.0", "description": "Hapi plugin that automatically generates RESTful API for CRUD", "main": "build/index.js", "scripts": { diff --git a/src/associations/many-to-many.js b/src/associations/many-to-many.js deleted file mode 100644 index e69de29..0000000 diff --git a/src/associations/one-to-many.js b/src/associations/one-to-many.js index eb87382..ab7b915 100644 --- a/src/associations/one-to-many.js +++ b/src/associations/one-to-many.js @@ -18,11 +18,17 @@ export const list = (server, a, b) => { @error async handler(request, reply) { - let list = await request.models[b.name].findAll({ + let list = await b.findAll({ where: { ...request.query, - [a.name + 'Id']: request.params.aid - } + }, + + include: [{ + model: a, + where: { + id: request.params.aid + } + }] }); reply(list); @@ -37,11 +43,17 @@ export const destroy = (server, a, b) => { @error async handler(request, reply) { - let list = await request.models[b.name].findAll({ + let list = await b.findAll({ where: { - ...request.query, - [a.name + 'Id']: request.params.aid - } + ...request.query + }, + + include: [{ + model: a, + where: { + id: request.params.aid + } + }] }); await* list.map(instance => instance.destroy()); @@ -58,11 +70,17 @@ export const update = (server, a, b) => { @error async handler(request, reply) { - let list = await request.models[b.name].findOne({ + let list = await b.findOne({ where: { - ...request.query, - [a.name + 'Id']: request.params.aid - } + ...request.query + }, + + include: [{ + model: a, + where: { + id: request.params.aid + } + }] }); await* list.map(instance => instance.update(request.payload)); diff --git a/src/associations/one-to-one.js b/src/associations/one-to-one.js index dfb0497..dcd3723 100644 --- a/src/associations/one-to-one.js +++ b/src/associations/one-to-one.js @@ -19,11 +19,17 @@ export const get = (server, a, b) => { @error async handler(request, reply) { - let instance = await request.models[b.name].findOne({ + let instance = await b.findOne({ where: { - id: request.params.bid, - [a.name + 'Id']: request.params.aid - } + id: request.params.bid + }, + + include: [{ + model: a, + where: { + id: request.params.aid + } + }] }); reply(instance); @@ -53,11 +59,17 @@ export const destroy = (server, a, b) => { @error async handler(request, reply) { - let instance = await request.models[b.name].findOne({ + let instance = await b.findOne({ where: { - id: request.params.bid, - [a.name + 'Id']: request.params.aid - } + id: request.params.bid + }, + + include: [{ + model: a, + where: { + id: request.params.aid + } + }] }); await instance.destroy(); @@ -74,11 +86,17 @@ export const update = (server, a, b) => { @error async handler(request, reply) { - let instance = await request.models[b.name].findOne({ + let instance = await b.findOne({ where: { - id: request.params.bid, - [a.name + 'Id']: request.params.aid - } + id: request.params.bid + }, + + include: [{ + model: a, + where: { + id: request.params.aid + } + }] }); await instance.update(request.payload); diff --git a/src/crud.js b/src/crud.js index 814d052..2b2b99b 100644 --- a/src/crud.js +++ b/src/crud.js @@ -21,8 +21,7 @@ export const list = (server, model) => { @error async handler(request, reply) { - console.log(request.models[model.name], request.query); - let list = await request.models[model.name].findAll({ + let list = await model.findAll({ where: request.query }); @@ -40,7 +39,7 @@ export const get = (server, model) => { async handler(request, reply) { let where = request.params.id ? { id : request.params.id } : request.query; - let instance = await request.models[model.name].findOne({ where }); + let instance = await model.findOne({ where }); reply(instance); }, @@ -63,7 +62,7 @@ export const scope = (server, model) => { @error async handler(request, reply) { - let list = await request.models[model.name].scope(request.params.scope).findAll(); + let list = await model.scope(request.params.scope).findAll(); reply(list); }, @@ -84,7 +83,7 @@ export const create = (server, model) => { @error async handler(request, reply) { - let instance = await request.models[model.name].create(request.payload); + let instance = await model.create(request.payload); reply(instance); } @@ -100,7 +99,7 @@ export const destroy = (server, model) => { async handler(request, reply) { let where = request.params.id ? { id : request.params.id } : request.query; - let list = await request.models[model.name].findAll({ where }); + let list = await model.findAll({ where }); await* list.map(instance => instance.destroy()); @@ -116,7 +115,7 @@ export const update = (server, model) => { @error async handler(request, reply) { - let instance = await request.models[model.name].findOne({ + let instance = await model.findOne({ where: { id: request.params.id } diff --git a/src/error.js b/src/error.js index 8939093..36a1cdb 100644 --- a/src/error.js +++ b/src/error.js @@ -1,10 +1,11 @@ export default (target, key, descriptor) => { let fn = descriptor.value; - descriptor.value = (request, reply) => { + descriptor.value = async (request, reply) => { try { - fn(request, reply); + await fn(request, reply); } catch(e) { + console.error(e); reply(e); } } diff --git a/src/index.js b/src/index.js index 9bd97b6..c168caa 100644 --- a/src/index.js +++ b/src/index.js @@ -37,16 +37,23 @@ const register = (server, options = {}, next) => { associations.oneToOne(server, target, source, options); } - if (sourceType === 'BelongsTo' && (targetType === 'HasMany')) { + if (sourceType === 'BelongsTo' && targetType === 'HasMany') { associations.oneToOne(server, source, target, options); associations.oneToOne(server, target, source, options); associations.oneToMany(server, target, source, options); } + + if (sourceType === 'BelongsToMany' && targetType === 'BelongsToMany') { + associations.oneToOne(server, source, target, options); + associations.oneToOne(server, target, source, options); + + associations.oneToMany(server, source, target, options); + associations.oneToMany(server, target, source, options); + } } catch(e) { // There might be conflicts in case of models associated with themselves and some other // rare cases. } - console.log(sourceName.singular, sourceType, targetName.singular, ' & ', targetName.singular, targetType, sourceName.singular); } }