Skip to content

Commit

Permalink
AYS-244 | Role Delete Endpoint Has Been Created (#337)
Browse files Browse the repository at this point in the history
  • Loading branch information
MenekseYuncu authored Jul 6, 2024
1 parent ec4ff6d commit 84e90fc
Show file tree
Hide file tree
Showing 11 changed files with 415 additions and 1 deletion.
19 changes: 19 additions & 0 deletions src/main/java/org/ays/auth/controller/AysRoleController.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.hibernate.validator.constraints.UUID;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
Expand Down Expand Up @@ -158,4 +159,22 @@ public AysResponse<Void> update(@PathVariable @UUID final String id,
return AysResponse.SUCCESS;
}


/**
* Delete an existing role by its ID.
* <p>
* This method is mapped to handle HTTP DELETE requests to "/role/{id}". It requires
* the user to have the 'role:delete' authority to access.
* </p>
*
* @param id The ID of the role to delete.
* @return An {@link AysResponse} indicating the success of the operation.
*/
@DeleteMapping("/role/{id}")
@PreAuthorize("hasAnyAuthority('role:delete')")
public AysResponse<Void> delete(@PathVariable @UUID final String id) {
roleUpdateService.delete(id);
return AysResponse.SUCCESS;
}

}
8 changes: 8 additions & 0 deletions src/main/java/org/ays/auth/port/AysRoleReadPort.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,12 @@ public interface AysRoleReadPort {
*/
Optional<AysRole> findByName(String name);

/**
* Checks if any users are assigned to a role identified by its ID.
*
* @param id The ID of the role to check.
* @return true if users are assigned to the role, false otherwise.
*/
boolean isRoleUsing(String id);

}
12 changes: 12 additions & 0 deletions src/main/java/org/ays/auth/port/impl/AysRoleAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,18 @@ public Optional<AysRole> findByName(final String name) {
}


/**
* Checks if any users are assigned to a role identified by its ID.
*
* @param id The ID of the role to check.
* @return true if users are assigned to the role, false otherwise.
*/
@Override
public boolean isRoleUsing(String id) {
return roleRepository.isRoleAssignedToUser(id);
}


/**
* Saves an {@link AysRole} to the database.
*
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/org/ays/auth/repository/AysRoleRepository.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.ays.auth.model.enums.AysRoleStatus;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;

import java.util.List;
import java.util.Optional;
Expand Down Expand Up @@ -37,4 +38,13 @@ public interface AysRoleRepository extends JpaRepository<AysRoleEntity, String>,
*/
Optional<AysRoleEntity> findByName(String name);

/**
* Checks if any users are assigned to the role identified by the given role ID.
*
* @param id The ID of the role to check for assigned users.
* @return true if there are users assigned to the role, false otherwise.
*/
@Query("SELECT COUNT(user) > 0 FROM AysUserEntity user JOIN user.roles role WHERE role.id = :id")
boolean isRoleAssignedToUser(String id);

}
7 changes: 7 additions & 0 deletions src/main/java/org/ays/auth/service/AysRoleUpdateService.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,11 @@ public interface AysRoleUpdateService {
*/
void update(String id, AysRoleUpdateRequest updateRequest);

/**
* Deletes a role by its unique identifier.
*
* @param id The unique identifier of the role to be deleted.
*/
void delete(String id);

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
import org.ays.auth.port.AysRoleSavePort;
import org.ays.auth.service.AysRoleUpdateService;
import org.ays.auth.util.exception.AysPermissionNotExistException;
import org.ays.auth.util.exception.AysRoleAlreadyDeletedException;
import org.ays.auth.util.exception.AysRoleAlreadyExistsByNameException;
import org.ays.auth.util.exception.AysRoleAssignedToUserException;
import org.ays.auth.util.exception.AysRoleNotExistByIdException;
import org.ays.auth.util.exception.AysUserNotSuperAdminException;
import org.springframework.stereotype.Service;
Expand Down Expand Up @@ -63,6 +65,32 @@ public void update(final String id,
roleSavePort.save(role);
}

/**
* Deletes an existing role identified by its ID.
*
* @param id The ID of the role to delete.
* @throws AysRoleNotExistByIdException if no role exists with the provided ID.
* @throws AysRoleAssignedToUserException if users are assigned to the role.
* @throws AysRoleAlreadyDeletedException if the role is already marked as deleted.
*/
@Override
public void delete(final String id) {

final AysRole role = roleReadPort.findById(id)
.orElseThrow(() -> new AysRoleNotExistByIdException(id));

if (roleReadPort.isRoleUsing(id)) {
throw new AysRoleAssignedToUserException(id);
}

if (role.isDeleted()) {
throw new AysRoleAlreadyDeletedException(id);
}

role.delete();
roleSavePort.save(role);
}

/**
* Checks the existence of another role with the same name, excluding the current role ID.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.ays.auth.util.exception;

import org.ays.common.util.exception.AysAlreadyException;

import java.io.Serial;

/**
* Exception thrown when a role is deleted and attempting to perform an action that requires a deleted role.
*/
public final class AysRoleAlreadyDeletedException extends AysAlreadyException {

/**
* Unique serial version ID.
*/
@Serial
private static final long serialVersionUID = -7631303999314005771L;

/**
* Constructs a new {@link AysRoleAlreadyDeletedException} with the specified detail message.
*
* @param id the id of the deleted user
*/
public AysRoleAlreadyDeletedException(String id) {
super("role is already deleted! id:" + id);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.ays.auth.util.exception;

import org.ays.common.util.exception.AysAlreadyException;

import java.io.Serial;

/**
* Exception thrown when a role is deleted and attempting to perform an action that requires a deleted role.
*/
public class AysRoleAssignedToUserException extends AysAlreadyException {

/**
* Unique serial version ID.
*/
@Serial
private static final long serialVersionUID = 7298457193066796371L;

/**
* Constructs a new {@link AysRoleAssignedToUserException} with the specified detail message.
*
* @param id the detail message.
*/
public AysRoleAssignedToUserException(String id) {
super("the role is assigned to one or more users id:" + id);
}

}
120 changes: 119 additions & 1 deletion src/test/java/org/ays/auth/controller/AysRoleControllerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import org.ays.AysRestControllerTest;
import org.ays.auth.model.AysRole;
import org.ays.auth.model.AysRoleBuilder;
import org.ays.auth.model.enums.AysRoleStatus;
import org.ays.auth.model.mapper.AysRoleToResponseMapper;
import org.ays.auth.model.mapper.AysRoleToRolesResponseMapper;
import org.ays.auth.model.mapper.AysRoleToRolesSummaryResponseMapper;
Expand All @@ -18,6 +19,8 @@
import org.ays.auth.service.AysRoleCreateService;
import org.ays.auth.service.AysRoleReadService;
import org.ays.auth.service.AysRoleUpdateService;
import org.ays.auth.util.exception.AysRoleAlreadyDeletedException;
import org.ays.auth.util.exception.AysRoleAssignedToUserException;
import org.ays.common.model.AysPage;
import org.ays.common.model.AysPageBuilder;
import org.ays.common.model.response.AysErrorResponse;
Expand Down Expand Up @@ -449,7 +452,6 @@ void givenInvalidRoleCreateRequest_whenPermissionIdIsNotValid_thenReturnValidati
.create(Mockito.any(AysRoleCreateRequest.class));
}


@Test
void givenValidIdAndRoleUpdateRequest_whenRoleUpdated_thenReturnSuccess() throws Exception {
// Given
Expand Down Expand Up @@ -660,4 +662,120 @@ void givenValidIdAndInvalidRoleUpdateRequest_whenPermissionIdIsNotValid_thenRetu
.update(Mockito.anyString(), Mockito.any(AysRoleUpdateRequest.class));
}


@Test
void givenValidId_whenRoleDeleted_thenReturnSuccess() throws Exception {
// Given
String mockId = AysRandomUtil.generateUUID();

// When
Mockito.doNothing()
.when(roleUpdateService)
.delete(Mockito.any());

// Then
String endpoint = BASE_PATH.concat("/role/").concat(mockId);
MockHttpServletRequestBuilder mockHttpServletRequestBuilder = AysMockMvcRequestBuilders
.delete(endpoint, mockAdminToken.getAccessToken());

AysResponse<Void> mockResponse = AysResponseBuilder.SUCCESS;

aysMockMvc.perform(mockHttpServletRequestBuilder, mockResponse)
.andExpect(AysMockResultMatchersBuilders.status()
.isOk())
.andExpect(AysMockResultMatchersBuilders.response()
.doesNotExist());

// Verify
Mockito.verify(roleUpdateService, Mockito.times(1))
.delete(mockId);
}

@ParameterizedTest
@ValueSource(strings = {
"A",
"493268349068342"
})
void givenId_whenIdDoesNotValid_thenReturnValidationError(String invalidId) throws Exception {

// Then
String endpoint = BASE_PATH.concat("/role/").concat(invalidId);
MockHttpServletRequestBuilder mockHttpServletRequestBuilder = AysMockMvcRequestBuilders
.delete(endpoint, mockAdminToken.getAccessToken());

AysErrorResponse mockErrorResponse = AysErrorBuilder.VALIDATION_ERROR;

aysMockMvc.perform(mockHttpServletRequestBuilder, mockErrorResponse)
.andExpect(AysMockResultMatchersBuilders.status()
.isBadRequest())
.andExpect(AysMockResultMatchersBuilders.subErrors()
.isNotEmpty());

// Verify
Mockito.verify(roleUpdateService, Mockito.never())
.delete(Mockito.anyString());
}

@Test
void givenValidId_whenRoleAlreadyDeleted_thenReturnConflict() throws Exception {
// Given
String mockId = AysRandomUtil.generateUUID();

// When
AysRole mockRole = new AysRoleBuilder()
.withValidValues()
.withId(mockId)
.withStatus(AysRoleStatus.DELETED)
.build();

Mockito.doThrow(new AysRoleAlreadyDeletedException(mockId))
.when(roleUpdateService)
.delete(mockRole.getId());

// Then
String endpoint = BASE_PATH.concat("/role/").concat(mockId);
MockHttpServletRequestBuilder mockHttpServletRequestBuilder = AysMockMvcRequestBuilders
.delete(endpoint, mockAdminToken.getAccessToken());

AysErrorResponse mockErrorResponse = AysErrorBuilder.ALREADY_EXIST;

aysMockMvc.perform(mockHttpServletRequestBuilder, mockErrorResponse)
.andExpect(AysMockResultMatchersBuilders.status()
.isConflict())
.andExpect(AysMockResultMatchersBuilders.subErrors()
.doesNotExist());

// Verify
Mockito.verify(roleUpdateService, Mockito.times(1))
.delete(mockRole.getId());
}

@Test
void givenValidId_whenRoleUsing_thenReturnConflict() throws Exception {
// Given
String mockId = AysRandomUtil.generateUUID();

// When
Mockito.doThrow(new AysRoleAssignedToUserException(mockId))
.when(roleUpdateService)
.delete(mockId);

// Then
String endpoint = BASE_PATH.concat("/role/").concat(mockId);
MockHttpServletRequestBuilder mockHttpServletRequestBuilder = AysMockMvcRequestBuilders
.delete(endpoint, mockAdminToken.getAccessToken());

AysErrorResponse mockErrorResponse = AysErrorBuilder.ALREADY_EXIST;

aysMockMvc.perform(mockHttpServletRequestBuilder, mockErrorResponse)
.andExpect(AysMockResultMatchersBuilders.status()
.isConflict())
.andExpect(AysMockResultMatchersBuilders.subErrors()
.doesNotExist());

// Verify
Mockito.verify(roleUpdateService, Mockito.times(1))
.delete(mockId);
}

}
40 changes: 40 additions & 0 deletions src/test/java/org/ays/auth/controller/AysRoleEndToEndTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -411,4 +411,44 @@ void givenValidRoleUpdateRequest_whenRoleUpdated_thenReturnSuccess() throws Exce
));
}


@Test
void givenValidId_whenRoleAlreadyDeleted_thenReturnSuccess() throws Exception {
// Initialize
List<AysPermission> permissions = permissionReadPort.findAll();

AysRole role = roleSavePort.save(
new AysRoleBuilder()
.withValidValues()
.withoutId()
.withName("Admin Role 1")
.withPermissions(permissions)
.withInstitution(new InstitutionBuilder().withId(AysValidTestData.Admin.INSTITUTION_ID).build())
.build()
);

// Given
String id = role.getId();

// Then
String endpoint = BASE_PATH.concat("/role/").concat(id);
MockHttpServletRequestBuilder mockHttpServletRequestBuilder = AysMockMvcRequestBuilders
.delete(endpoint, adminToken.getAccessToken());

AysResponse<Void> mockResponse = AysResponseBuilder.SUCCESS;

aysMockMvc.perform(mockHttpServletRequestBuilder, mockResponse)
.andExpect(AysMockResultMatchersBuilders.status()
.isOk())
.andExpect(AysMockResultMatchersBuilders.response()
.doesNotExist());

// Verify
Optional<AysRole> roleFromDatabase = roleReadPort.findById(id);

Assertions.assertTrue(roleFromDatabase.isPresent());
Assertions.assertNotNull(roleFromDatabase.get().getId());
Assertions.assertEquals(AysRoleStatus.DELETED, roleFromDatabase.get().getStatus());
}

}
Loading

0 comments on commit 84e90fc

Please sign in to comment.