-
-
Notifications
You must be signed in to change notification settings - Fork 9
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
Access (auth) refactor & database transactions #46
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR! You can revert the changes on imports and crud patching in the unittests, that should solve most of the unittest issues!
@@ -26,14 +24,15 @@ async def update_my_info(payload: UserInfo, me: UserRead = Security(get_current_ | |||
@router.put("/update-pwd", response_model=UserInfo) | |||
async def update_my_password(payload: Cred, me: UserRead = Security(get_current_user, scopes=["me"])): | |||
entry = await crud.get_entry(users, me.id) | |||
await update_access_pwd(payload, entry["access_id"]) | |||
await crud.update_access_pwd(payload, entry["access_id"]) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You forgot to do the same for the admin version further down below ;)
@@ -75,7 +75,7 @@ class EventType(str, enum.Enum): | |||
Column("id", Integer, primary_key=True), | |||
Column("login", String(50)), | |||
Column("owner_id", Integer, ForeignKey("users.id")), | |||
Column("access_id", Integer, ForeignKey("accesses.id"), unique=True), | |||
Column("access_id", Integer, ForeignKey("accesses.id", ondelete="CASCADE"), unique=True), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So deleting the device will delete the access? If so, shouldn't we do the same for the user?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And shall we do it also for the owner_id ? If we delete the users, the devices will disappear?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I thought that I also had added the on delete cascade on user but I forgot.
I figured out that it is the opposite: deleting the access deletes the device/user. I am working on fixing the device/user deletion.
I will add the mechanism owner id as well !
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another issue when we use the on cascade mechanism -> as it perform at db level we have to find a way to mock it in our unit tests
monkeypatch.setattr(crud, "delete", pytest.mock_delete) | ||
monkeypatch.setattr(base, "get", pytest.mock_get) | ||
monkeypatch.setattr(base, "fetch_all", pytest.mock_fetch_all) | ||
monkeypatch.setattr(base, "fetch_one", pytest.mock_fetch_one) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since you made the crud init, you can keep the previous version (you have to patch the thin imported in the route, and does not import base, but crud)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually it is not that simple (I think, I am not masting import mechanisms) because it seams that mocking crud.get will keep untouched the original crud.base.get function and if it imported afterward the original is used instead of the mocked version. So far the only way I could mock all crud function was to mock both crud._ and crud.base._ (_ beeing each function). Do you see ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mmmh, what error did you get with my suggestion?
Because the import for the accesses is over here: https://github.com/pyronear/pyro-api/blob/master/src/app/api/routes/accesses.py#L3
So if we patch this very import, it should be working without any problem 🤔
@@ -62,100 +58,3 @@ def test_fetch_accesses(test_app, monkeypatch): | |||
assert response.status_code == 200 | |||
assert response.json() == [{k: v for k, v in entry.items() if k != "hashed_password"} | |||
for entry in mock_access_table] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we remove the unittests for the routes, we need to adapt them to test the crud.accesses functions. I don't think it would be wise to decrease coverage by that much
monkeypatch.setattr(base, "fetch_all", pytest.mock_fetch_all) | ||
monkeypatch.setattr(base, "post", pytest.mock_post) | ||
monkeypatch.setattr(base, "put", pytest.mock_put) | ||
monkeypatch.setattr(base, "delete", pytest.mock_delete) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here
|
||
router = APIRouter() | ||
|
||
|
||
@router.post("/", response_model=DeviceOut, status_code=201) | ||
@database.transaction() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why add this decorator only for route that create objects?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I planned to add the decorator when transaction is needed (writing in several tables with atomicity), user/device creation was the first use case, did I miss other ones ?
I realised that route decorator was not the best practice: it makes it impossible to test. We may need to find a way to mock a database transaction or we'll have to rewrite operations into pytest mocks. @frgfm do you see what I mean ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am no where close to be knowledgeable about database transactions since I just discovered that thanks to you @jeanpasquier75. What is the expected behaviour difference after decorating the route? reducing execution time? output type change?
If it's beneficial to the API, even if we can't test it per se, I would argue that we should still put it. If it doesn't bring much and we can't test it, then perhaps let's leave it out
async def create_access(payload: AccessAuth): | ||
return await post_access(payload.login, payload.password, payload.scopes) | ||
|
||
|
||
@router.get("/{access_id}/", response_model=AccessRead) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we want to remove the access routes (it is fine by me if we think we don't need it anymore for debugging purpose), why leave those routes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think he mentioned that we don't want to expose creation/modification of accesses but reading is fine
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes reading is fine while it does not break database consistency
@@ -75,7 +75,7 @@ class EventType(str, enum.Enum): | |||
Column("id", Integer, primary_key=True), | |||
Column("login", String(50)), | |||
Column("owner_id", Integer, ForeignKey("users.id")), | |||
Column("access_id", Integer, ForeignKey("accesses.id"), unique=True), | |||
Column("access_id", Integer, ForeignKey("accesses.id", ondelete="CASCADE"), unique=True), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And shall we do it also for the owner_id ? If we delete the users, the devices will disappear?
@jeanpasquier75 any update? I'm changing the release target as this isn't critical for the incoming demo |
So @jeanpasquier75, what should we do with this PR? |
Sorry for the delay 😅 I will dry to come up with something by the end of the week. As you mentioned in #76 this PR was supposed to fix access deletion with the SQL cascade mechanism as well |
@jeanpasquier75 #76 was merged, let us know if you think we should keep this PR for th upcoming release or staging it for the next one! |
Closing this outdated PR resolved in #90 |
Changes:
1st point lead to split crud module into submodules base (legacy) & accesses (new). I would need some feedback on this part. I needed to change patching during unit tests, fetch_all does not seem to work.