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

Access (auth) refactor & database transactions #46

Closed
wants to merge 52 commits into from

Conversation

jean-pasquier
Copy link
Contributor

Changes:

  • Move accesses functions from routes/accesses.py to crud/accesses.py
  • Use database transaction for atomicity for user/device creation

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.

@jean-pasquier jean-pasquier added this to the 0.1.0 milestone Nov 19, 2020
@jean-pasquier jean-pasquier self-assigned this Nov 19, 2020
Copy link
Member

@frgfm frgfm left a 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"])
Copy link
Member

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 ;)

src/app/api/routes/users.py Show resolved Hide resolved
src/app/db/tables.py Show resolved Hide resolved
@@ -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),
Copy link
Member

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?

Copy link
Contributor

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?

Copy link
Contributor Author

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 !

Copy link
Contributor Author

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)
Copy link
Member

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)

Copy link
Contributor Author

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 ?

Copy link
Member

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]
Copy link
Member

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)
Copy link
Member

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()
Copy link
Contributor

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?

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 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 ?

Copy link
Member

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)
Copy link
Contributor

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?

Copy link
Member

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

Copy link
Contributor Author

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),
Copy link
Contributor

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?

@frgfm
Copy link
Member

frgfm commented Nov 24, 2020

@jeanpasquier75 any update? I'm changing the release target as this isn't critical for the incoming demo

@frgfm frgfm modified the milestones: 0.1.0, 0.1.1 Nov 24, 2020
@frgfm frgfm added the question Further information is requested label Nov 28, 2020
@frgfm
Copy link
Member

frgfm commented Nov 30, 2020

So @jeanpasquier75, what should we do with this PR?
Considering the (very favorable) pace of PRs/modif of the repo, it's better to process PR rather quickly 😅

@jean-pasquier
Copy link
Contributor Author

So @jeanpasquier75, what should we do with this PR?
Considering the (very favorable) pace of PRs/modif of the repo, it's better to process PR rather quickly 😅

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

@frgfm
Copy link
Member

frgfm commented Dec 5, 2020

@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!

@jean-pasquier
Copy link
Contributor Author

Closing this outdated PR resolved in #90

@jean-pasquier jean-pasquier deleted the access-refacto branch December 5, 2020 16:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
endpoint: cameras endpoint: users help wanted Extra attention is needed question Further information is requested type: improvement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants