Add feature to allow nested include and filtering relationships/associations #36
Labels
No Label
bug
duplicate
enhancement
help wanted
invalid
question
wontfix
No Milestone
No project
No Assignees
1 Participants
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: thereadme/hapi-sequelize-crud#36
Loading…
Reference in New Issue
Block a user
No description provided.
Delete Branch "labibramadhan/include-with-filter"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Added feature to allow filtering relationships/associations based on http://docs.sequelizejs.com/en/latest/docs/querying/#relations-associations
oops sorry, forgot to fix associationValidation of get-config-for-method.test.js
@labibramadhan Thanks Muhammad. May you please document the changes in README, too? The code looks fine to me (though there are lots of space changes, I'm not sure if this style persists throughout the whole codebase) but @joeybaker should take a look, too. 👍
Current coverage is 72.50% (diff: 79.36%)
Hi @mdibaiee, i have added some guide text about this feature in README, please have a check 😃
Why remove this section? 🤔
@ -116,0 +119,4 @@
// GET /players?include={"model": "Master", "as": "Couch"}
// results in a Sequelize query:
Players.findAll({include: Master, as: 'Couch'})
Please get rid of the
include
key of the JSON passed toinclude
, it's an unnecessary repetition.It should look like this:
/teams?include={"model": "City", "where": { "name": "Healdsburg" }}
Thanks for the work @labibramadhan!
Generally:
get-config-for-method.js
. You should be able to do all your changes incrud.js
get-config-for-method
joi.string().regex(/^\{.*?"model":.*?\}$/)
Let us know if you have questions, and if you don't have time to get to this, that's okay too!
Why move this validation into
get-config-for-method
? All other validation is defined in this method already.hmmm… it's weird that we're adding in custom validation here instead of just trusting that the
associationValidation
is correct when we received it. We should fix this at the source incrud.js
@ -20,0 +21,4 @@
const getModelInstance = (models, includeItem) => {
return new Promise(async(resolve) => {
if (includeItem) {
if (typeof includeItem !== 'object') {
JSON.parse
will throw if not handed valid JSON. We should use a try/catch here.@ -20,0 +21,4 @@
const getModelInstance = (models, includeItem) => {
return new Promise(async(resolve) => {
if (includeItem) {
if (typeof includeItem !== 'object') {
what code inside the catch do you prefer?
Sorry @joeybaker i have fixed this, moved to crud.js
By the way i got different indentation settings with your existing scripts, is it OK? what IDE you guys using?
Hi @mdibaiee, sorry for this README.md lines that i've removed before, i thought it was for relationship query example, but i just realized maybe it was for object property method filter, but it's now in README.md again
@ -116,0 +119,4 @@
// GET /players?include={"model": "Master", "as": "Couch"}
// results in a Sequelize query:
Players.findAll({include: Master, as: 'Couch'})
Fixed in my last commit, could you verify now?
@labibramadhan we use two spaces for indentation, almost all IDEs and editors support indentation settings. Thanks for looking at the mentioned issues.
@ -20,0 +21,4 @@
const getModelInstance = (models, includeItem) => {
return new Promise(async(resolve) => {
if (includeItem) {
if (typeof includeItem !== 'object') {
5ba9d7d261/src/utils.js (L50-L54)
and5ba9d7d261/src/utils.js (L78-L83)
are both options. Thanks!anytime @mdibaiee, thank you too @mdibaiee and @joeybaker for this amazing module! i'll give this module a try to my project, but i need to find ACL module to go along, any suggestion?
@ -20,0 +22,4 @@
return new Promise(async(resolve) => {
if (includeItem) {
if (typeof includeItem !== 'object') {
const singluarOrPluralMatch = Object.keys(models).find((modelName) => {
maybe like this @joeybaker?
@ -20,0 +22,4 @@
return new Promise(async(resolve) => {
if (includeItem) {
if (typeof includeItem !== 'object') {
const singluarOrPluralMatch = Object.keys(models).find((modelName) => {
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?@ -20,0 +22,4 @@
return new Promise(async(resolve) => {
if (includeItem) {
if (typeof includeItem !== 'object') {
const singluarOrPluralMatch = Object.keys(models).find((modelName) => {
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)
@ -20,0 +22,4 @@
return new Promise(async(resolve) => {
if (includeItem) {
if (typeof includeItem !== 'object') {
const singluarOrPluralMatch = Object.keys(models).find((modelName) => {
currently my version of crud.js has this to validate its association object:
@ -20,0 +22,4 @@
return new Promise(async(resolve) => {
if (includeItem) {
if (typeof includeItem !== 'object') {
const singluarOrPluralMatch = Object.keys(models).find((modelName) => {
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?
@labibramadhan google tells me that ACL = "access control list", which I take to mean you want find-grain user permissions over REST access?
If so: we're solving this in 2 ways: using the built-in Hapi auth and adding it to our REST routes in
defaultConfig
, and via scopes.since nested includes feature may loop the getModelInstance function, parseInclude is now a promise based function, to call this function needs await
@ -16,1 +16,4 @@
});
models.Player.belongsTo(models.Master, {
foreignKey: 'coachId',
as: 'Coach',
needed for association/relationship alias test
@joeybaker and @mdibaiee, i need help to apply include child joi validation to be the same as its parent include validation (nested validations with the same schema)
this things was formatted
${prefix}/${names.a.singular}/{aid}/${names.b.plural}
, but if prefix equals "/", it will produce path like//team/{aid}/player
and will output404 not found
response, i decided to remove the slash after${prefix}
, so every prefix option definition must have trailing slash@ -20,0 +22,4 @@
return new Promise(async(resolve) => {
if (includeItem) {
if (typeof includeItem !== 'object') {
const singluarOrPluralMatch = Object.keys(models).find((modelName) => {
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
alright @joeybaker, i will try that method, thank you!
@ -16,1 +16,4 @@
});
models.Player.belongsTo(models.Master, {
foreignKey: 'coachId',
as: 'Coach',
Can you not use Teams and Players? Just so we don't get an overly-complex mock schema?
@ -8,3 +8,3 @@
test('belongsTo /team?include=city', async (t) => {
test('belongsTo /team?include=city', async(t) => {
const { server, instances } = t.context;
Why remove all these spaces?
So… I'm still confused. How would we be getting an Object in here? Is joi smart enough to parse the JSON string?
hmmm… so two questions:
getModelInstance
looks really dense, maybe it should be it's own method instead of a closure? It would allow us to unit test it, which might be a good idea.@ -16,1 +16,4 @@
});
models.Player.belongsTo(models.Master, {
foreignKey: 'coachId',
as: 'Coach',
Ah! I see. Makes sense.
because i have tried not to use promise while looping, and it didn't work so i gotta use async. yes you're right, better to separate getModelInstance function, i will refactor it
@ -16,1 +16,4 @@
});
models.Player.belongsTo(models.Master, {
foreignKey: 'coachId',
as: 'Coach',
is it ok then?
@ -8,3 +8,3 @@
test('belongsTo /team?include=city', async (t) => {
test('belongsTo /team?include=city', async(t) => {
const { server, instances } = t.context;
sorry this is from my IDE settings, but i don't see any eslint error
i think the only answer is we need to try. if it's not smart enough, maybe later we will find bug report
We can try with the integration tests no?
@ -16,1 +16,4 @@
});
models.Player.belongsTo(models.Master, {
foreignKey: 'coachId',
as: 'Coach',
Yup!
Cool. I'm still not sure why we need promises though?
because getModelInstance may loop itself, for example i got object like this:
the getModelInstance function will loop through all of its include children to convert each model name string to be its model instance, notice that conversion here needs logic like looping all existing models to find the right model instance
yes maybe. especially if you can express what the possibility of vulnerability that might happen to this include feature. i have tried something like this to mess the JSON include parameter:
this is the both result:
Checkout
From your project repository, check out a new branch and test the changes.