Skip to content

Commit

Permalink
AYS-242 | Role Activation Flow Has Been Created (#336)
Browse files Browse the repository at this point in the history
  • Loading branch information
egehanasal authored Jul 7, 2024
1 parent 84e90fc commit be3230a
Show file tree
Hide file tree
Showing 9 changed files with 291 additions and 0 deletions.
18 changes: 18 additions & 0 deletions src/main/java/org/ays/auth/controller/AysRoleController.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
Expand Down Expand Up @@ -159,6 +160,23 @@ public AysResponse<Void> update(@PathVariable @UUID final String id,
return AysResponse.SUCCESS;
}

/**
* PATCH /role/{id}/activate : Activate an existing role.
* <p>
* This endpoint handles the activation of a role based on its ID. The user must have the
* 'role:update' authority to access this endpoint.
* </p>
*
* @param id The ID of the role to activate.
* @return An {@link AysResponse} indicating the success of the operation.
*/
@PatchMapping("/role/{id}/activate")
@PreAuthorize("hasAnyAuthority('role:update')")
public AysResponse<Void> activate(@PathVariable @UUID final String id) {
roleUpdateService.activate(id);
return AysResponse.SUCCESS;
}


/**
* Delete an existing role by its ID.
Expand Down
8 changes: 8 additions & 0 deletions src/main/java/org/ays/auth/model/AysRole.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ public boolean isPassive() {
return AysRoleStatus.PASSIVE.equals(this.status);
}

/**
* Activates the role by setting its status to {@link AysRoleStatus#ACTIVE}.
* This method should be called when the role needs to be marked as active in the system.
*/
public void activate() {
this.setStatus(AysRoleStatus.ACTIVE);
}

/**
* Checks if the role's status is deleted.
*
Expand Down
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,6 +16,13 @@ public interface AysRoleUpdateService {
*/
void update(String id, AysRoleUpdateRequest updateRequest);

/**
* Service interface for activating roles.
* Implementations of this interface should provide functionality to activate an existing role
* based on the provided id.
*/
void activate(String id);

/**
* Deletes a role by its unique identifier.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import org.ays.auth.model.AysIdentity;
import org.ays.auth.model.AysPermission;
import org.ays.auth.model.AysRole;
import org.ays.auth.model.enums.AysRoleStatus;
import org.ays.auth.model.request.AysRoleUpdateRequest;
import org.ays.auth.port.AysPermissionReadPort;
import org.ays.auth.port.AysRoleReadPort;
Expand All @@ -15,6 +16,7 @@
import org.ays.auth.util.exception.AysRoleAssignedToUserException;
import org.ays.auth.util.exception.AysRoleNotExistByIdException;
import org.ays.auth.util.exception.AysUserNotSuperAdminException;
import org.ays.auth.util.exception.AysInvalidRoleStatusException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand Down Expand Up @@ -65,6 +67,33 @@ public void update(final String id,
roleSavePort.save(role);
}


/**
* Activates an existing role.
* <p>
* This method sets the status of the role identified by its ID to active. If the role does not exist,
* an exception is thrown. Additionally, if the role's status is not {@link AysRoleStatus#PASSIVE},
* an exception is thrown.
* </p>
*
* @param id The ID of the role to activate.
* @throws AysRoleNotExistByIdException if a role with the given ID does not exist.
* @throws AysInvalidRoleStatusException if the role's current status is not {@link AysRoleStatus#PASSIVE}.
*/
@Override
public void activate(String id) {
final AysRole role = roleReadPort.findById(id)
.orElseThrow(() -> new AysRoleNotExistByIdException(id));

if (!role.isPassive()) {
throw new AysInvalidRoleStatusException(AysRoleStatus.PASSIVE);
}

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


/**
* Deletes an existing role identified by its ID.
*
Expand All @@ -91,6 +120,7 @@ public void delete(final String id) {
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,28 @@
package org.ays.auth.util.exception;

import org.ays.auth.model.enums.AysRoleStatus;
import org.ays.common.util.exception.AysNotExistException;

import java.io.Serial;

/**
* Exception to be thrown when a role's status is invalid for the requested operation.
*/
public final class AysInvalidRoleStatusException extends AysNotExistException {

/**
* Unique identifier for serialization.
*/
@Serial
private static final long serialVersionUID = -933312733826008379L;

/**
* Constructs a new {@link AysInvalidRoleStatusException} with the specified role status.
*
* @param status the invalid status of the role.
*/
public AysInvalidRoleStatusException(AysRoleStatus status) {
super("role status is not " + status.toString().toLowerCase() + "!");
}

}
56 changes: 56 additions & 0 deletions src/test/java/org/ays/auth/controller/AysRoleControllerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ void givenInvalidRoleCreateRequest_whenPermissionIdIsNotValid_thenReturnValidati
.create(Mockito.any(AysRoleCreateRequest.class));
}


@Test
void givenValidIdAndRoleUpdateRequest_whenRoleUpdated_thenReturnSuccess() throws Exception {
// Given
Expand Down Expand Up @@ -663,6 +664,61 @@ void givenValidIdAndInvalidRoleUpdateRequest_whenPermissionIdIsNotValid_thenRetu
}


@Test
void givenValidId_whenRoleActivated_thenReturnSuccess() throws Exception {

// Given
String mockId = AysRandomUtil.generateUUID();

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

// Then
String endpoint = BASE_PATH.concat("/role/".concat(mockId).concat("/activate"));
MockHttpServletRequestBuilder mockHttpServletRequestBuilder = AysMockMvcRequestBuilders
.patch(endpoint, mockSuperAdminToken.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))
.activate(Mockito.anyString());
}

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

// Then
String endpoint = BASE_PATH.concat("/role/".concat(invalidId).concat("/activate"));
MockHttpServletRequestBuilder mockHttpServletRequestBuilder = AysMockMvcRequestBuilders
.patch(endpoint, mockSuperAdminToken.getAccessToken());

AysErrorResponse mockErrorResponse = AysErrorBuilder.VALIDATION_ERROR;

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

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


@Test
void givenValidId_whenRoleDeleted_thenReturnSuccess() throws Exception {
// Given
Expand Down
45 changes: 45 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,6 +411,51 @@ void givenValidRoleUpdateRequest_whenRoleUpdated_thenReturnSuccess() throws Exce
));
}

@Test
void givenId_whenRoleActivated_thenReturnSuccess() throws Exception {

// Initialize
Institution institution = institutionSavePort.save(
new InstitutionBuilder()
.withValidValues()
.withoutId()
.build()
);
List<AysPermission> permissions = permissionReadPort.findAll();
AysRole role = roleSavePort.save(
new AysRoleBuilder()
.withValidValues()
.withoutId()
.withStatus(AysRoleStatus.PASSIVE)
.withInstitution(institution)
.withPermissions(permissions)
.build()
);

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

// Then
String endpoint = BASE_PATH.concat("/role/".concat(id).concat("/activate"));
MockHttpServletRequestBuilder mockHttpServletRequestBuilder = AysMockMvcRequestBuilders
.patch(endpoint, superAdminToken.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.assertEquals(roleFromDatabase.get().getId(), id);
Assertions.assertEquals(AysRoleStatus.ACTIVE, roleFromDatabase.get().getStatus());
}


@Test
void givenValidId_whenRoleAlreadyDeleted_thenReturnSuccess() throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@
import org.ays.auth.util.exception.AysRoleAssignedToUserException;
import org.ays.auth.util.exception.AysRoleNotExistByIdException;
import org.ays.auth.util.exception.AysUserNotSuperAdminException;
import org.ays.auth.util.exception.AysInvalidRoleStatusException;
import org.ays.common.util.AysRandomUtil;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
Expand Down Expand Up @@ -364,6 +367,96 @@ void givenValidIdAndRoleUpdateRequest_whenPermissionsNotExists_thenThrowAysPermi
.save(Mockito.any(AysRole.class));
}

@Test
void givenValidId_whenRoleIsPassive_thenActivateRole() {
// Given
String mockId = AysRandomUtil.generateUUID();

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

Mockito.when(roleReadPort.findById(Mockito.anyString()))
.thenReturn(Optional.of(mockRole));

Mockito.when(roleSavePort.save(Mockito.any(AysRole.class)))
.thenReturn(Mockito.mock(AysRole.class));

// Then
roleUpdateService.activate(mockId);

// Verify
Mockito.verify(roleReadPort, Mockito.times(1))
.findById(Mockito.anyString());

Mockito.verify(roleSavePort, Mockito.times(1))
.save(Mockito.any(AysRole.class));

}

@ParameterizedTest
@ValueSource(strings = {
"ACTIVE",
"DELETED"
})
void givenValidId_whenRoleIsNotPassive_thenThrowAysInvalidRoleStatusException(String roleStatus) {
// Initialize
AysRoleStatus status = AysRoleStatus.valueOf(roleStatus);

// Given
String mockId = AysRandomUtil.generateUUID();

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

Mockito.when(roleReadPort.findById(Mockito.anyString()))
.thenReturn(Optional.of(mockRole));

// Then
Assertions.assertThrows(
AysInvalidRoleStatusException.class,
() -> roleUpdateService.activate(mockId)
);

// Verify
Mockito.verify(roleReadPort, Mockito.times(1))
.findById(Mockito.anyString());

Mockito.verify(roleSavePort, Mockito.never())
.save(Mockito.any(AysRole.class));

}

@Test
void givenValidId_whenRoleNotFound_thenThrowAysRoleNotExistByIdException() {
// Given
String mockId = AysRandomUtil.generateUUID();

// When
Mockito.when(roleReadPort.findById(Mockito.anyString()))
.thenReturn(Optional.empty());

// Then
Assertions.assertThrows(
AysRoleNotExistByIdException.class,
() -> roleUpdateService.activate(mockId)
);

// Verify
Mockito.verify(roleReadPort, Mockito.times(1))
.findById(Mockito.anyString());

Mockito.verify(roleSavePort, Mockito.never())
.save(Mockito.any(AysRole.class));
}


@Test
void givenValidId_whenRoleFound_thenDeleteRole() {
Expand Down
6 changes: 6 additions & 0 deletions src/test/java/org/ays/util/AysMockMvcRequestBuilders.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ public MockHttpServletRequestBuilder put(String endpoint, String token, Object r
.contentType(MediaType.APPLICATION_JSON);
}

public MockHttpServletRequestBuilder patch(String endpoint, String token) {
return MockMvcRequestBuilders.patch(endpoint)
.header(HttpHeaders.AUTHORIZATION, getTokenWithBearerPrefix(token))
.contentType(MediaType.APPLICATION_JSON);
}

public MockHttpServletRequestBuilder delete(String endpoint, String token) {
return MockMvcRequestBuilders.delete(endpoint)
.header(HttpHeaders.AUTHORIZATION, getTokenWithBearerPrefix(token));
Expand Down

0 comments on commit be3230a

Please sign in to comment.