feat(simple): simple CRUD REST API (no associations)
feat(associations): one-to-one associations feat(associations): one-to-many associations
This commit is contained in:
4
src/associations/index.js
Normal file
4
src/associations/index.js
Normal file
@@ -0,0 +1,4 @@
|
||||
import oneToOne from './one-to-one';
|
||||
import oneToMany from './one-to-many';
|
||||
|
||||
export { oneToOne, oneToMany };
|
0
src/associations/many-to-many.js
Normal file
0
src/associations/many-to-many.js
Normal file
73
src/associations/one-to-many.js
Normal file
73
src/associations/one-to-many.js
Normal file
@@ -0,0 +1,73 @@
|
||||
import joi from 'joi';
|
||||
import error from '../error';
|
||||
|
||||
let prefix;
|
||||
|
||||
export default (server, a, b, options) => {
|
||||
prefix = options.prefix;
|
||||
|
||||
list(server, a, b);
|
||||
destroy(server, a, b);
|
||||
update(server, a, b);
|
||||
}
|
||||
|
||||
export const list = (server, a, b) => {
|
||||
server.route({
|
||||
method: 'GET',
|
||||
path: `${prefix}/${a._singular}/{aid}/${b._plural}`,
|
||||
|
||||
@error
|
||||
async handler(request, reply) {
|
||||
let list = await request.models[b.name].findAll({
|
||||
where: {
|
||||
...request.query,
|
||||
[a.name + 'Id']: request.params.aid
|
||||
}
|
||||
});
|
||||
|
||||
reply(list);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const destroy = (server, a, b) => {
|
||||
server.route({
|
||||
method: 'DELETE',
|
||||
path: `${prefix}/${a._singular}/{aid}/${b._plural}`,
|
||||
|
||||
@error
|
||||
async handler(request, reply) {
|
||||
let list = await request.models[b.name].findAll({
|
||||
where: {
|
||||
...request.query,
|
||||
[a.name + 'Id']: request.params.aid
|
||||
}
|
||||
});
|
||||
|
||||
await* list.map(instance => instance.destroy());
|
||||
|
||||
reply();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const update = (server, a, b) => {
|
||||
server.route({
|
||||
method: 'PUT',
|
||||
path: `${prefix}/${a._singular}/{aid}/${b._plural}`,
|
||||
|
||||
@error
|
||||
async handler(request, reply) {
|
||||
let list = await request.models[b.name].findOne({
|
||||
where: {
|
||||
...request.query,
|
||||
[a.name + 'Id']: request.params.aid
|
||||
}
|
||||
});
|
||||
|
||||
await* list.map(instance => instance.update(request.payload));
|
||||
|
||||
reply(list);
|
||||
}
|
||||
})
|
||||
}
|
89
src/associations/one-to-one.js
Normal file
89
src/associations/one-to-one.js
Normal file
@@ -0,0 +1,89 @@
|
||||
import joi from 'joi';
|
||||
import error from '../error';
|
||||
|
||||
let prefix;
|
||||
|
||||
export default (server, a, b, options) => {
|
||||
prefix = options.prefix;
|
||||
|
||||
get(server, a, b);
|
||||
create(server, a, b);
|
||||
destroy(server, a, b);
|
||||
update(server, a, b);
|
||||
}
|
||||
|
||||
export const get = (server, a, b) => {
|
||||
server.route({
|
||||
method: 'GET',
|
||||
path: `${prefix}/${a._singular}/{aid}/${b._singular}/{bid}`,
|
||||
|
||||
@error
|
||||
async handler(request, reply) {
|
||||
let instance = await request.models[b.name].findOne({
|
||||
where: {
|
||||
id: request.params.bid,
|
||||
[a.name + 'Id']: request.params.aid
|
||||
}
|
||||
});
|
||||
|
||||
reply(instance);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const create = (server, a, b) => {
|
||||
server.route({
|
||||
method: 'POST',
|
||||
path: `${prefix}/${a._singular}/{id}/${b._singular}`,
|
||||
|
||||
@error
|
||||
async handler(request, reply) {
|
||||
request.payload[a.name + 'Id'] = request.params.id;
|
||||
let instance = await request.models[b.name].create(request.payload);
|
||||
|
||||
reply(instance);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const destroy = (server, a, b) => {
|
||||
server.route({
|
||||
method: 'DELETE',
|
||||
path: `${prefix}/${a._singular}/{aid}/${b._singular}/{bid}`,
|
||||
|
||||
@error
|
||||
async handler(request, reply) {
|
||||
let instance = await request.models[b.name].findOne({
|
||||
where: {
|
||||
id: request.params.bid,
|
||||
[a.name + 'Id']: request.params.aid
|
||||
}
|
||||
});
|
||||
|
||||
await instance.destroy();
|
||||
|
||||
reply();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const update = (server, a, b) => {
|
||||
server.route({
|
||||
method: 'PUT',
|
||||
path: `${prefix}/${a._singular}/{aid}/${b._singular}/{bid}`,
|
||||
|
||||
@error
|
||||
async handler(request, reply) {
|
||||
let instance = await request.models[b.name].findOne({
|
||||
where: {
|
||||
id: request.params.bid,
|
||||
[a.name + 'Id']: request.params.aid
|
||||
}
|
||||
});
|
||||
|
||||
await instance.update(request.payload);
|
||||
|
||||
reply(instance);
|
||||
}
|
||||
})
|
||||
}
|
133
src/crud.js
Normal file
133
src/crud.js
Normal file
@@ -0,0 +1,133 @@
|
||||
import joi from 'joi';
|
||||
import error from './error';
|
||||
|
||||
let prefix;
|
||||
|
||||
export default (server, model, options) => {
|
||||
prefix = options.prefix;
|
||||
|
||||
list(server, model);
|
||||
get(server, model);
|
||||
scope(server, model);
|
||||
create(server, model);
|
||||
destroy(server, model);
|
||||
update(server, model);
|
||||
}
|
||||
|
||||
export const list = (server, model) => {
|
||||
server.route({
|
||||
method: 'GET',
|
||||
path: `${prefix}/${model._plural}`,
|
||||
|
||||
@error
|
||||
async handler(request, reply) {
|
||||
console.log(request.models[model.name], request.query);
|
||||
let list = await request.models[model.name].findAll({
|
||||
where: request.query
|
||||
});
|
||||
|
||||
reply(list);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export const get = (server, model) => {
|
||||
server.route({
|
||||
method: 'GET',
|
||||
path: `${prefix}/${model._singular}/{id?}`,
|
||||
|
||||
@error
|
||||
async handler(request, reply) {
|
||||
let where = request.params.id ? { id : request.params.id } : request.query;
|
||||
|
||||
let instance = await request.models[model.name].findOne({ where });
|
||||
|
||||
reply(instance);
|
||||
},
|
||||
config: {
|
||||
validate: {
|
||||
params: joi.object().keys({
|
||||
id: joi.number().integer()
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const scope = (server, model) => {
|
||||
let scopes = Object.keys(model.options.scopes);
|
||||
|
||||
server.route({
|
||||
method: 'GET',
|
||||
path: `${prefix}/${model._plural}/{scope}`,
|
||||
|
||||
@error
|
||||
async handler(request, reply) {
|
||||
let list = await request.models[model.name].scope(request.params.scope).findAll();
|
||||
|
||||
reply(list);
|
||||
},
|
||||
config: {
|
||||
validate: {
|
||||
params: joi.object().keys({
|
||||
scope: joi.string().valid(...scopes)
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export const create = (server, model) => {
|
||||
server.route({
|
||||
method: 'POST',
|
||||
path: `${prefix}/${model._singular}`,
|
||||
|
||||
@error
|
||||
async handler(request, reply) {
|
||||
let instance = await request.models[model.name].create(request.payload);
|
||||
|
||||
reply(instance);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const destroy = (server, model) => {
|
||||
server.route({
|
||||
method: 'DELETE',
|
||||
path: `${prefix}/${model._singular}/{id?}`,
|
||||
|
||||
@error
|
||||
async handler(request, reply) {
|
||||
let where = request.params.id ? { id : request.params.id } : request.query;
|
||||
|
||||
let list = await request.models[model.name].findAll({ where });
|
||||
|
||||
await* list.map(instance => instance.destroy());
|
||||
|
||||
reply();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export const update = (server, model) => {
|
||||
server.route({
|
||||
method: 'PUT',
|
||||
path: `/v1/${model._singular}/{id}`,
|
||||
|
||||
@error
|
||||
async handler(request, reply) {
|
||||
let instance = await request.models[model.name].findOne({
|
||||
where: {
|
||||
id: request.params.id
|
||||
}
|
||||
});
|
||||
|
||||
await instance.update(request.payload);
|
||||
|
||||
reply(instance);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
import * as associations from './associations/index';
|
||||
export { associations };
|
13
src/error.js
Normal file
13
src/error.js
Normal file
@@ -0,0 +1,13 @@
|
||||
export default (target, key, descriptor) => {
|
||||
let fn = descriptor.value;
|
||||
|
||||
descriptor.value = (request, reply) => {
|
||||
try {
|
||||
fn(request, reply);
|
||||
} catch(e) {
|
||||
reply(e);
|
||||
}
|
||||
}
|
||||
|
||||
return descriptor;
|
||||
}
|
60
src/index.js
Normal file
60
src/index.js
Normal file
@@ -0,0 +1,60 @@
|
||||
import crud, { associations } from './crud';
|
||||
|
||||
const register = (server, options = {}, next) => {
|
||||
options.prefix = options.prefix || '';
|
||||
|
||||
let db = server.plugins['hapi-sequelize'].db;
|
||||
let models = db.sequelize.models;
|
||||
|
||||
for (let modelName of Object.keys(models)) {
|
||||
let model = models[modelName];
|
||||
let { plural, singular } = model.options.name;
|
||||
model._plural = plural.toLowerCase();
|
||||
model._singular = singular.toLowerCase();
|
||||
|
||||
// Join tables
|
||||
if (model.options.name.singular !== model.name) continue;
|
||||
|
||||
crud(server, model, options);
|
||||
|
||||
for (let key of Object.keys(model.associations)) {
|
||||
let association = model.associations[key];
|
||||
let { associationType, source, target } = association;
|
||||
|
||||
let sourceName = source.options.name;
|
||||
let targetName = target.options.name;
|
||||
|
||||
target._plural = targetName.plural.toLowerCase();
|
||||
target._singular = targetName.singular.toLowerCase();
|
||||
|
||||
let targetAssociations = target.associations[sourceName.plural] || target.associations[sourceName.singular];
|
||||
let sourceType = association.associationType,
|
||||
targetType = (targetAssociations || {}).associationType;
|
||||
|
||||
try {
|
||||
if (sourceType === 'BelongsTo' && (targetType === 'BelongsTo' || !targetType)) {
|
||||
associations.oneToOne(server, source, target, options);
|
||||
associations.oneToOne(server, target, source, options);
|
||||
}
|
||||
|
||||
if (sourceType === 'BelongsTo' && (targetType === 'HasMany')) {
|
||||
associations.oneToOne(server, source, target, options);
|
||||
associations.oneToOne(server, target, source, 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);
|
||||
}
|
||||
}
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
register.attributes = {
|
||||
pkg: require('../package.json')
|
||||
}
|
||||
|
||||
export { register };
|
Reference in New Issue
Block a user