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

Mrc-5298-Role Update Endpoints #64

Merged
merged 42 commits into from May 14, 2024
Merged

Mrc-5298-Role Update Endpoints #64

merged 42 commits into from May 14, 2024

Conversation

absternator
Copy link
Contributor

The work here contains 2 endpoints that encompass role update. They are addPermissionsToRole and removePermissionsToRole. They respectively add and remove permissions from a role ..

NOTE:
For removePermissionsToRole we had to delete the rolepermission rows directly. Hibernate didn't work with just removing the rolePermission from the role. I will create a new ticket that can come back and investigate this further

Copy link

codecov bot commented May 1, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 93.98%. Comparing base (52c32c2) to head (6be3222).
Report is 10 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main      #64   +/-   ##
=======================================
  Coverage   93.98%   93.98%           
=======================================
  Files          67       67           
  Lines         532      532           
  Branches      131      131           
=======================================
  Hits          500      500           
  Misses         30       30           
  Partials        2        2           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

Copy link
Contributor

@EmmaLRussell EmmaLRussell left a comment

Choose a reason for hiding this comment

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

Could you explain a bit more about the need for equality/hash checking for the role permissions? Is it related to trying to get hibernate to handle the deletion of the rows in the link table? Or was it to support this line in RoleService:
if (rolePermissionsToAdd.any { role.rolePermissions.contains(it) })
...
or in RolePermissionService
val matchedPermission = role.rolePermissions.find { rolePermissionToRemove == it }
?
Those would be easier if you were dealing with the data classes (dto's) which would support equality automatically.


return ResponseEntity.ok(mapOf("message" to "Role deleted"))
}

@PutMapping("/add-permissions/{roleName}")
Copy link
Contributor

Choose a reason for hiding this comment

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

These endpoints don't seem very RESTful.
Would usually be something like
POST or DELETE /role/{name}/permission

Copy link
Contributor Author

Choose a reason for hiding this comment

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

its not really a post or delete because we are not creating/deleting the role. We are updating the role object i.e its a update operation. technically should be patch but spring didn't like patch - said it doesn't accept them

Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe not POST, but should at least be PUT and DELETE then, as described here:
https://www.mscharhag.com/api-design/rest-many-to-many-relations

REST is generally disapproving of using verbs like "add" which could be expressed as http verbs. I guess it's a bit muddier if you're not providing the GET equivalent, but I still think it would be better than embedding the verb in the endpoint name.

And even if we do keep add-permissions, it should go after the roleName not before (route to the role, then say what you're going to do to it).

Is this discussion all irrelevant though, as didn't you say you were going to replace these with a single PUT update endpoint?

{
roleService.addPermissionsToRole(roleName, addRolePermissions)

return ResponseEntity.ok(mapOf("message" to "Permissions added"))
Copy link
Contributor

Choose a reason for hiding this comment

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

Again, not sure this is useful. Empty response or updated role would be more so.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yup agree have created ticjet fir all these as prev pr comment

Comment on lines +5 to +11
data class UpdateRolePermission(
@field:NotNull
val permission: String,
val packetId: String? = null,
val tagId: Int? = null,
val packetGroupId: Int? = null
)
Copy link
Contributor

Choose a reason for hiding this comment

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

So we're only allowing global permissions when we create a role, but can update with scoped permissions? That seems inconsistent. Could accept a list of these on create role, rather than just permission names.. and then wouldn't this type also be the scoped permission type returned from the GET request too? ...oh, minus the id of course

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 think for the create the FE most likely wont pass any permissions.. And here update is where it can pass in scoped if needed...The idea was it would be weird adding scoped permissions when creating a role as well... But adding a scoped permission when updating makes sense also.... This logic will be mirrored on the FE.

Copy link
Contributor

Choose a reason for hiding this comment

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

OK, but you still want to allow global permissions on create to make life easier for the admin cli/setup scripts?
I'm not really convinced it's a useful distinction. Setup might want to make roles with scoped permissions in some cases for example. I think it would be fine to make role create take no perms and require that to be done in a second request.

Comment on lines +13 to +19
init
{
val nonNullFields = listOf(packetId, tagId, packetGroupId).count { it != null }
require(nonNullFields <= 1) {
"Either all of packetId, tagId, packetGroupId should be null or only one of them should be not null"
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, this is nice, and I think we could still keep this anyway, even this type was also re-used as the RolePermission dto.

Comment on lines +32 to +39
{
init
{
val nonNullFields = listOf(packet, tag, packetGroup).count { it != null }
require(nonNullFields <= 1) {
"Either all of packet, tag, packetGroup should be null or only one of them should be not null"
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Repeated logic... Given that you've got this constraint on the db, and also in the dto, maybe you don't need it here as well. But if so, should be able to implement once and use from both types.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yeah i thought that too but couldnt find a way to implement once and use both places.. Also they a bit different as one is ids and other is whole objects... And probs don't need but put here to for safety and mirror this constraint on DB and make more visible (as many people don't check db constraints haha)

Comment on lines 84 to 86
val role = roleRepository.findByName(roleName)
?: throw PackitException("roleNotFound", HttpStatus.BAD_REQUEST)
rolePermissionService.removeRolePermissionsFromRole(role, removeRolePermissions)
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems a bit odd to check that permissions being added don't yet exist here in RoleService but that permissions deing deleted do exist, in RolePermissionService service. Should both be in the same service, probably RolePermissionsService

{
override fun getRolePermissionsToUpdate(
role: Role,
addRolePermissions: List<UpdateRolePermission>
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
addRolePermissions: List<UpdateRolePermission>
updateRolePermissions: List<UpdateRolePermission>

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 named like that to differentiate the adding and deleting of role permissions

Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

oh yup was mistaken have updated

Comment on lines 31 to 49
return addRolePermissions.map { addRolePermission ->
val permission = permissionRepository.findByName(addRolePermission.permission)
?: throw PackitException("permissionNotFound", HttpStatus.BAD_REQUEST)
RolePermission(
role = role,
permission = permission,
packet = addRolePermission.packetId?.let {
packetRepository.findById(it)
.orElseThrow { PackitException("packetNotFound", HttpStatus.BAD_REQUEST) }
},
packetGroup = addRolePermission.packetGroupId?.let {
packetGroupRepository.findById(it)
.orElseThrow { PackitException("packetGroupNotFound", HttpStatus.BAD_REQUEST) }
},
tag = addRolePermission.tagId?.let {
tagRepository.findById(it).orElseThrow { PackitException("tagNotFound", HttpStatus.BAD_REQUEST) }
}
)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This is quite dense visuall,y but makes sense on closer inspection.
I think an empty line at l.34 would make it more readable by splitting off the main chunk.

Comment on lines 54 to 61
val rolePermissionsToRemove = getRolePermissionsToUpdate(role, removeRolePermissions)

val matchedRolePermissionsToRemove = rolePermissionsToRemove.map { rolePermissionToRemove ->
val matchedPermission = role.rolePermissions.find { rolePermissionToRemove == it }
?: throw PackitException("rolePermissionDoesNotExist", HttpStatus.BAD_REQUEST)
matchedPermission
}

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think you really need to do a map here, just a forEach then just map the original rolePermissionsToRemove to ids to pass to the deleteAllByIdIn method

Copy link
Contributor Author

Choose a reason for hiding this comment

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

unsure how the foreach would work here? and the benefits of doing it that way?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah sorry that doesn't work because you don't actually have the ID on the UpdateRolePermissions.

@@ -83,4 +97,50 @@ class RoleControllerTest : IntegrationTest()
assertSuccess(result)
assertNull(roleRepository.findByName("testRole"))
}

@Test
@WithAuthenticatedUser(authorities = ["user.manage"])
Copy link
Contributor

Choose a reason for hiding this comment

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

Could test for unauthorized request too.

@absternator
Copy link
Contributor Author

Could you explain a bit more about the need for equality/hash checking for the role permissions? Is it related to trying to get hibernate to handle the deletion of the rows in the link table? Or was it to support this line in RoleService: if (rolePermissionsToAdd.any { role.rolePermissions.contains(it) }) ... or in RolePermissionService val matchedPermission = role.rolePermissions.find { rolePermissionToRemove == it } ? Those would be easier if you were dealing with the data classes (dto's) which would support equality automatically.

Yup so pretty much used to compare equality fo the entities.. For delete we need to filter out some out and things just in general for match... In general good to have them for these as can be used for comparison.. Sadly if we were to use dtos we would have to first convert and then compare and then convert back to entity

@PutMapping("/add-permissions/{roleName}")
fun addPermissionsToRole(
@RequestBody @Validated addRolePermissions: List<UpdateRolePermission>,
@PutMapping("/update-permissions/{roleName}")
Copy link
Contributor

Choose a reason for hiding this comment

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

OK, this overrides my previous comment!
I think this should just be PUT /role/{roleName}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

have done for all PUT as suggested as above in PR #68.. /role/{rolenmame}/permissions & /role/{rolenmame}/users

rolePermissionRepository.deleteAllByIdIn(matchedRolePermissionsToRemove.map { it.id!! })
}

override fun getAddRolePermissionsFromRole(
Copy link
Contributor

Choose a reason for hiding this comment

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

It's quite confusing having getRolePermissionsToUpdate and getAddRolePermissionsFromRole (that sounds like it's getting the permissions from the role, but if the permissions are already in the role, you're going to throw an exception!). I wouldn't mind just making the method into a checkPermissionsToAddAreNotOnRole or something, and have the main update method call `getRolePermissionsToUpdate directly, and pass the perms into here to just do the check...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yup agree ended up changing to getRolePermissionsToAdd and making the getRolePermissionsToUpdate internal so not visible to public

Base automatically changed from mrc-5297-role-delete to main May 13, 2024 13:39
@absternator absternator merged commit 60e1bbb into main May 14, 2024
5 checks passed
@absternator absternator deleted the mrc-5298-role-update branch May 14, 2024 11:13
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.

None yet

2 participants