Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(fix) Warn for duplicated inverseBy relationships #15217

Merged
merged 11 commits into from Jan 23, 2023

Conversation

Marc-Roig
Copy link
Contributor

@Marc-Roig Marc-Roig commented Dec 20, 2022

What does it do?

Warn users to fix bidirectional relations where both sides use inversedBy.

Why is it needed?

There was this bug resolved, which was placing inversedBy in both sides of relations. Which resulted in creating two db tables for each relation.

This brought this problem for some users #14912.

Switching one of the sides to mapped by fixes the issue, but if you switch the wrong side the join table with data would be removed.

How to test it?

Create Cat content type:

{
  "kind": "collectionType",
  "collectionName": "cats",
  "info": {
    "singularName": "cat",
    "pluralName": "cats",
    "displayName": "Cat"
  },
  "options": {
    "draftAndPublish": true
  },
  "pluginOptions": {},
  "attributes": {
    "dogs": {
      "type": "relation",
      "relation": "manyToMany",
      "target": "api::dog.dog",
      "inversedBy": "cats"
    },
    "name": {
      "type": "string"
    }
  }
}

Create Dog content type:

{
  "kind": "collectionType",
  "collectionName": "dogs",
  "info": {
    "singularName": "dog",
    "pluralName": "dogs",
    "displayName": "Dog"
  },
  "options": {
    "draftAndPublish": true
  },
  "pluginOptions": {},
  "attributes": {
    "name": {
      "type": "string"
    },
    "cats": {
      "type": "relation",
      "relation": "manyToMany",
      "target": "api::cat.cat",
      "inversedBy": "dogs"
    }
  }
}

This should bring an error saying you should change one of the sides inversedBy.

To test it properly, you could try to add data in that relation and switching to this branch, to see if makes you switch the inversedBy wich removes the empty join table.

Related issue(s)/PR(s)

#14912

@Marc-Roig Marc-Roig changed the title look for duplicated inverseBy relationships (fix) Warn for duplicated inverseBy relationships Dec 20, 2022
@codecov
Copy link

codecov bot commented Dec 20, 2022

Codecov Report

Base: 61.25% // Head: 65.96% // Increases project coverage by +4.71% 🎉

Coverage data is based on head (d2636e5) compared to base (f5e87de).
Patch has no changes to coverable lines.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #15217      +/-   ##
==========================================
+ Coverage   61.25%   65.96%   +4.71%     
==========================================
  Files        1353     1056     -297     
  Lines       33435    22981   -10454     
  Branches     6438     4120    -2318     
==========================================
- Hits        20481    15160    -5321     
+ Misses      11129     6889    -4240     
+ Partials     1825      932     -893     
Flag Coverage Δ
back ?
front 65.96% <ø> (-0.05%) ⬇️
unit_back ?
unit_front 65.96% <ø> (-0.05%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
packages/core/admin/admin/src/hooks/index.js 100.00% <0.00%> (ø)
packages/core/admin/admin/src/pages/App/index.js 66.66% <0.00%> (ø)
packages/core/database/lib/index.js
packages/core/database/lib/metadata/relations.js
packages/core/utils/lib/print-value.js
packages/core/permissions/lib/index.js
packages/core/database/lib/query/index.js
.../plugins/i18n/server/content-types/locale/index.js
packages/core/utils/lib/template-configuration.js
packages/plugins/i18n/server/validation/locales.js
... and 289 more

Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here.

☔ View full report at Codecov.
📢 Do you have feedback about the report comment? Let us know in this issue.

@Marc-Roig Marc-Roig marked this pull request as ready for review January 2, 2023 10:28
` instead of inversedBy in the relation "${invRelation.inversedBy}".`
);
} else {
// Both sides have data in the join table
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to discuss what should we do if this happens. It should be extremely rare and would only happen if useres are tweaking with the tables manually.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't we have both directions of the relationship in the relationship table? 🤔

Copy link
Contributor Author

@Marc-Roig Marc-Roig Jan 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bad wording sorry, that comment should be:
Both sides of the relation have a different join table, and both have data

Only one join table should exist, which would contain all necessary info of the relation.

In the other two cases, only one of the join tables holds data, so it's safe to remove the other. But in this case both have data, which leaves the question of what do we communicate to the user.

Comment on lines 76 to 80
errorList.push(
`Error on attribute "${relation.inversedBy}" in model "${contentType.tableName}"(${contentType.uid}):` +
` One of the sides of the relationship must be the owning side. You should use mappedBy` +
` instead of inversedBy in the relation "${relation.inversedBy}".`
);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pending for a discussion.

Should this be an error?
It could be a little bit intrusive for the users updating to the new version , when everything was working for them before, and now getting this error.

What do you think to make this a warning?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do the relations work if they didn't do this correctly?
If so, I guess it should be a warning.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer warning too. At the same time we can add some sense of urgency in the message:

"Please modify your schema by renaming the key inversedBy by mappedBy. Ex: { inverseBy: 'api::dog.dog' } -> { mappedBy: 'api::dog.dog' }"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed error to a warning and also the message inside, let me know what you think. @petersg83
Also, do we have a better way to log a warning than process.emitWarning? I don't feel it's quite visible when starting the application.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do this way as apparently it's the official way and allows some features (choosing what to log depending on server config etc.). I would say it's enough, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alright :) asking just in case

@Marc-Roig Marc-Roig added source: core:database Source is core/database package pr: fix This PR is fixing a bug labels Jan 2, 2023
Copy link
Contributor

@petersg83 petersg83 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice job, I find the code structure clean!
I have put some comments.

const linkTableEmpty = await isLinkTableEmpty(db, joinTableName);
const inverseLinkTableEmpty = await isLinkTableEmpty(db, inverseJoinTableName);

if (linkTableEmpty) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering if it's not the opposite? Based on what the error message says I would put if (inverseLinkTableEmpty) here and else if (linkTableEmpty) {.
(or exchange the error message)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just tried and I think it's just the message that as to be rewritten. I did a relation between country and categories and the error message is :
Error on attribute "categories" in model "categories"(api::category.category): One of the sides of the relationship must be the owning side. You should use mappedBy instead of inversedBy in the relation "categories".
It doesn't mention country at all and it feels it should :p

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, rephrased it , let me know if it sounds better now.

Comment on lines 76 to 80
errorList.push(
`Error on attribute "${relation.inversedBy}" in model "${contentType.tableName}"(${contentType.uid}):` +
` One of the sides of the relationship must be the owning side. You should use mappedBy` +
` instead of inversedBy in the relation "${relation.inversedBy}".`
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer warning too. At the same time we can add some sense of urgency in the message:

"Please modify your schema by renaming the key inversedBy by mappedBy. Ex: { inverseBy: 'api::dog.dog' } -> { mappedBy: 'api::dog.dog' }"

Copy link
Contributor

@petersg83 petersg83 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It works very well! It's awesome 😊 I've put a suggestion for the message. What do you think?

// Its safe to delete the inverse join table
process.emitWarning(
`Error on attribute "${relation.inversedBy}" in model "${invContentType.singularName}" (${invContentType.uid}).` +
` Please modify your schema by renaming the key inversedBy by mappedBy.` +
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
` Please modify your schema by renaming the key inversedBy by mappedBy.` +
` Please modify your ${invContentType.singularName} schema by renaming the key "inversedBy" by "mappedBy".` +

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Same for the other message)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done :D

Copy link
Contributor

@petersg83 petersg83 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Well done :)

@Marc-Roig Marc-Roig merged commit 07f5c0a into main Jan 23, 2023
@Marc-Roig Marc-Roig deleted the fix/duplicate-join-tables branch January 23, 2023 08:48
@Marc-Roig Marc-Roig added this to the 4.6.0 milestone Jan 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
pr: fix This PR is fixing a bug source: core:database Source is core/database package
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants