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

Add organization deletion route #4298

Merged
merged 5 commits into from
Oct 4, 2023
Merged

Conversation

dantb
Copy link
Contributor

@dantb dantb commented Sep 26, 2023

Key implementation points:

  • Added prune parameter to DELETE /orgs/{label} defaulting to false and requiring a new organizations/delete permission. Previously only deprecation was allowed for a specific rev. Not sure how great it is to ignore rev even if specified, maybe slightly confusing for the client? If the org is empty, return 409 Conflict.
  • OrganizationDeleter has all the logic. If the org is non-empty, we do these steps
    1. Drop the org partitions for scoped tables
    2. Delete the org records from the global tables
    3. Delete the ACL records from the global tables

I used this ticket to try to understand some of the patterns in the codebase so I have a few questions / comments, some specific to this and some more general:

  1. For the ACLs I manually wrote the deletion queries rather than reusing Acls, is this ok? The main reason was so it could all be in one transaction, since this feels like a bad thing to get stuck in a half-way state. It would also add a bigger dependency.
  2. What places do I need to update with the new permission? Is it in infra somewhere?
  3. Spent far too long trying to make the integration tests sane 😅 I wasn't sure the best approach and ended up using the old school arrange / act / assert. Preferably the tests should be independent so the tables get truncated before each. I don't really like the dodgy Monix type signatures though... Guess it comes down to trying to wrangle errors into a single type - I just didn't bother and opted for Any grimness. Is there a better way?
  4. The unit tests do a lot of global state mutating and depend on each other a lot (as we've discussed before). It's a bit like a mishmash of pure FP and side effects which makes it quite difficult to reason about. I just threw more crap on the pile for this change. It would be nice to try to improve them at some point though - are there any plans for that? I could imagine tests like these being quite nice using CE, http4s, munit and scalacheck. Maybe that's just a personal preference though 🙂

@dantb dantb force-pushed the delete-org-support branch 3 times, most recently from dcb4f83 to b90801e Compare September 27, 2023 19:41
@dantb dantb marked this pull request as ready for review September 27, 2023 19:41
@dantb
Copy link
Contributor Author

dantb commented Sep 27, 2023

Failing test is just to do with how we respond when rev is missing, will fix tomorrow

} yield ()

private def deleteGlobalQuery(id: IriOrBNode.Iri, tpe: EntityType, table: String): ConnectionIO[Unit] =
Update[(EntityType, IriOrBNode.Iri)](s"DELETE FROM $table WHERE" ++ " type = ? AND id = ?").run((tpe, id)).void
Copy link
Contributor

Choose a reason for hiding this comment

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

You could use the sql interpolator here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It's not possible when parameterising over the table name since substitutions only work for values. This is maybe a bit overkill but I was curious to see if it was possible 😅

Copy link
Contributor

@imsdu imsdu Oct 3, 2023

Choose a reason for hiding this comment

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

Fragment.const could also help here ? (not sure)

@imsdu
Copy link
Contributor

imsdu commented Sep 28, 2023

For the ACLs I manually wrote the deletion queries rather than reusing Acls, is this ok? The main reason was so it could all be in one transaction, since this feels like a bad thing to get stuck in a half-way state. It would also add a bigger dependency.

I am ok with that

What places do I need to update with the new permission? Is it in infra somewhere?

Nothing to do with the permission itself but we will need to assign it to a user to test it

Spent far too long trying to make the integration tests sane 😅 I wasn't sure the best approach and ended up using the old school arrange / act / assert. Preferably the tests should be independent so the tables get truncated before each. I don't really like the dodgy Monix type signatures though... Guess it comes down to trying to wrangle errors into a single type - I just didn't bother and opted for Any grimness. Is there a better way?

Not sure we can do much better with this kind of PR but as this part is mostly independent from the rest, you may have opted for Cats-Effect in the end :) .

The unit tests do a lot of global state mutating and depend on each other a lot (as we've discussed before). It's a bit like a mishmash of pure FP and side effects which makes it quite difficult to reason about. I just threw more crap on the pile for this change. It would be nice to try to improve them at some point though - are there any plans for that? I could imagine tests like these being quite nice using CE, http4s, munit and scalacheck. Maybe that's just a personal preference though

There is definitely room to improve things by making the code more modular, by improving the way we use MUnit (like setting up CE Resources and transform to MUnit fixtures the latest possible as those don't compose), by designing how to test the lifecycle of a resource without too much repetition and slowing them down too much...


private lazy val orgs = OrganizationsImpl(Set(aopd), config, xas)
private lazy val orgDeleter: OrganizationDeleter = id =>
if (id == org1.label) IO.raiseError(OrganizationNonEmpty(id))
Copy link
Contributor

@imsdu imsdu Oct 3, 2023

Choose a reason for hiding this comment

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

IO.raiseWhen would make it shorter

) {
)(implicit s: Scheduler) {

def readCE: Transactor[IO] = read.mapK(BIO.liftTo)
Copy link
Contributor

Choose a reason for hiding this comment

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

Nice that something like this is possible

@@ -123,6 +123,12 @@ Creating an archive now requires only the `resources/read` permission instead of

Tarball archives are no longer supported due to unnecessary restrictions. ZIP is now the only allowed format and clients should send `application/zip` in the `Accept` header when creating archives.

### Organizations

#### Support deletion of empty organizations
Copy link
Contributor

Choose a reason for hiding this comment

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

Link to the operation in the API ?

imsdu
imsdu previously approved these changes Oct 3, 2023
@dantb dantb merged commit d8ab831 into BlueBrain:master Oct 4, 2023
6 checks passed
@dantb dantb deleted the delete-org-support branch October 4, 2023 09:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants