Add feature to allow nested include and filtering relationships/associations #36
@ -85,3 +85,13 @@ test('multiple includes /team?include[]=players&include[]=city', async (t) => {
|
|||||||
t.truthy(playerIds.includes(player2.id));
|
t.truthy(playerIds.includes(player2.id));
|
||||||
t.is(result.City.id, city1.id);
|
t.is(result.City.id, city1.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('inlcude filter /teams?include[]={"model": "City", "where": {"name": "Healdsburg"}}'
|
||||||
|
, async (t) => {
|
||||||
|
const { server } = t.context;
|
||||||
|
const url = '/teams?include[]={"model": "City", "where": {"name": "Healdsburg"}}';
|
||||||
|
const method = 'GET';
|
||||||
|
|
||||||
|
const { statusCode } = await server.inject({ url, method });
|
||||||
|
t.is(statusCode, STATUS_OK);
|
||||||
|
});
|
19
src/crud.js
19
src/crud.js
@ -13,7 +13,7 @@ const createAll = ({
|
|||||||
prefix,
|
prefix,
|
||||||
config,
|
config,
|
||||||
attributeValidation,
|
attributeValidation,
|
||||||
associationValidation,
|
modelAssociations,
|
||||||
scopes,
|
scopes,
|
||||||
}) => {
|
}) => {
|
||||||
Object.keys(methods).forEach((method) => {
|
Object.keys(methods).forEach((method) => {
|
||||||
@ -24,7 +24,7 @@ const createAll = ({
|
|||||||
config: getConfigForMethod({
|
config: getConfigForMethod({
|
||||||
method,
|
method,
|
||||||
attributeValidation,
|
attributeValidation,
|
||||||
associationValidation,
|
modelAssociations,
|
||||||
config,
|
config,
|
||||||
scopes,
|
scopes,
|
||||||
}),
|
}),
|
||||||
@ -71,13 +71,6 @@ export default (server, model, { prefix, defaultConfig: config, models: permissi
|
|||||||
return params;
|
return params;
|
||||||
}, {});
|
}, {});
|
||||||
|
|
||||||
const validAssociations = modelAssociations.length
|
|
||||||
? joi.string().valid(...modelAssociations)
|
|
||||||
: joi.valid(null);
|
|
||||||
const associationValidation = {
|
|
||||||
include: [joi.array().items(validAssociations), validAssociations],
|
|
||||||
};
|
|
||||||
|
|
||||||
const scopes = Object.keys(model.options.scopes);
|
const scopes = Object.keys(model.options.scopes);
|
||||||
|
|
||||||
// if we don't have any permissions set, just create all the methods
|
// if we don't have any permissions set, just create all the methods
|
||||||
@ -88,7 +81,7 @@ export default (server, model, { prefix, defaultConfig: config, models: permissi
|
|||||||
prefix,
|
prefix,
|
||||||
config,
|
config,
|
||||||
attributeValidation,
|
attributeValidation,
|
||||||
associationValidation,
|
modelAssociations,
|
||||||
scopes,
|
scopes,
|
||||||
});
|
});
|
||||||
// if permissions are set, but we can't parse them, throw an error
|
// if permissions are set, but we can't parse them, throw an error
|
||||||
@ -103,7 +96,7 @@ export default (server, model, { prefix, defaultConfig: config, models: permissi
|
|||||||
prefix,
|
prefix,
|
||||||
config,
|
config,
|
||||||
attributeValidation,
|
attributeValidation,
|
||||||
associationValidation,
|
modelAssociations,
|
||||||
scopes,
|
scopes,
|
||||||
});
|
});
|
||||||
// if we've gotten here, we have complex permissions and need to set them
|
// if we've gotten here, we have complex permissions and need to set them
|
||||||
@ -125,7 +118,7 @@ export default (server, model, { prefix, defaultConfig: config, models: permissi
|
|||||||
config: getConfigForMethod({
|
config: getConfigForMethod({
|
||||||
method,
|
method,
|
||||||
attributeValidation,
|
attributeValidation,
|
||||||
associationValidation,
|
modelAssociations,
|
||||||
scopes,
|
scopes,
|
||||||
config: permissionConfig,
|
config: permissionConfig,
|
||||||
}),
|
}),
|
||||||
@ -137,7 +130,7 @@ export default (server, model, { prefix, defaultConfig: config, models: permissi
|
|||||||
model,
|
model,
|
||||||
prefix,
|
prefix,
|
||||||
attributeValidation,
|
attributeValidation,
|
||||||
associationValidation,
|
modelAssociations,
|
||||||
scopes,
|
scopes,
|
||||||
config: permissionConfig,
|
config: permissionConfig,
|
||||||
});
|
});
|
||||||
|
@ -72,7 +72,7 @@ export const restrictMethods = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export default ({
|
export default ({
|
||||||
method, attributeValidation, associationValidation, scopes = [], config = {},
|
method, attributeValidation, modelAssociations, scopes = [], config = {},
|
||||||
}) => {
|
}) => {
|
||||||
const hasWhere = whereMethods.includes(method);
|
const hasWhere = whereMethods.includes(method);
|
||||||
const hasInclude = includeMethods.includes(method);
|
const hasInclude = includeMethods.includes(method);
|
||||||
@ -96,9 +96,27 @@ export default ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (hasInclude) {
|
if (hasInclude) {
|
||||||
|
const modelsHasAssociations = modelAssociations && modelAssociations.length;
|
||||||
|
const validAssociationsString = modelsHasAssociations
|
||||||
|
? joi.string().valid(...modelAssociations)
|
||||||
|
: joi.valid(null);
|
||||||
|
const validAssociationsObject = modelsHasAssociations
|
||||||
|
? joi.object().keys({
|
||||||
|
model: joi.string().valid(...modelAssociations),
|
||||||
|
where: joi.object().keys({
|
||||||
|
...attributeValidation,
|
||||||
|
...sequelizeOperators,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
: joi.valid(null);
|
||||||
const query = concatToJoiObject(joi.object()
|
const query = concatToJoiObject(joi.object()
|
||||||
.keys({
|
.keys({
|
||||||
...associationValidation,
|
include: [
|
||||||
|
joi.array().items(validAssociationsString),
|
||||||
|
joi.array().items(validAssociationsObject),
|
||||||
|
validAssociationsString,
|
||||||
|
validAssociationsObject,
|
||||||
|
],
|
||||||
}),
|
}),
|
||||||
get(methodConfig, 'validate.query')
|
get(methodConfig, 'validate.query')
|
||||||
);
|
);
|
||||||
|
@ -25,13 +25,16 @@ export const parseInclude = request => {
|
|||||||
const models = getModels(request);
|
const models = getModels(request);
|
||||||
|
|||||||
if (models.isBoom) return models;
|
if (models.isBoom) return models;
|
||||||
|
|
||||||
return include.map(a => {
|
return include.map(b => {
|
||||||
|
const a = /^{.*}$/.test(b) ? JSON.parse(b) : b;
|
||||||
|
if (typeof a !== 'object') {
|
||||||
const singluarOrPluralMatch = Object.keys(models).find((modelName) => {
|
const singluarOrPluralMatch = Object.keys(models).find((modelName) => {
|
||||||
const { _singular, _plural } = models[modelName];
|
const { _singular, _plural } = models[modelName];
|
||||||
return _singular === a || _plural === a;
|
return _singular === a || _plural === a;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (singluarOrPluralMatch) return models[singluarOrPluralMatch];
|
if (singluarOrPluralMatch) return models[singluarOrPluralMatch];
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof a === 'string') return models[a];
|
if (typeof a === 'string') return models[a];
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user
maybe like this @joeybaker?
Kinda! The try/catch part is right, but the joi check should be a part of the validation. What you have here will always pass the
if
condition, becausejoi.string()
always returns a truthy value.What do you think of something like this in
crud.js
on line 77?but with this code, it won't validate the "model" name and the "where" object isn't it?
if the JSON string doesn't parse good so it caught as a string, but later it won't pass because crud.js that i pushed has something like
joi.string().valid(...modelAssociations)
currently my version of crud.js has this to validate its association object:
I get it. I was opting for: "if you pass JSON, we're going to skip validating it … it's too complex to do correctly".
Here's the problem I see with:
It ensures you get a valid JS object to validate. But you won't. You'll get a string that contains JSON. That's why I was opting for the regex of a string. Did I get confused there?
sorry @joeybaker, but i still didn't get it, what the main purpose of adding the json validation you want? because i have tried several cases of using include parameter and i didn't find any include parameter validation problem