Skip to content

Commit

Permalink
AYS-307 | User Activation Endpoint Created (#351)
Browse files Browse the repository at this point in the history
Co-authored-by: Egehan Asal <[email protected]>
Co-authored-by: MenekseYuncu <[email protected]>
  • Loading branch information
3 people authored Jul 30, 2024
1 parent 39c4990 commit e359f7d
Show file tree
Hide file tree
Showing 7 changed files with 361 additions and 0 deletions.
19 changes: 19 additions & 0 deletions src/main/java/org/ays/auth/controller/AysUserController.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
Expand Down Expand Up @@ -131,4 +132,22 @@ public AysResponse<Void> update(@PathVariable @UUID final String id,
return AysResponse.SUCCESS;
}


/**
* PATCH /user/{id}/activate : Activates a user account with the given ID.
* <p>
* This endpoint is protected and requires the caller to have the authority
* 'user:update'. The user ID must be a valid UUID.
* </p>
*
* @param id The UUID of the user to be activated.
* @return An {@link AysResponse} indicating the success of the operation.
*/
@PatchMapping("user/{id}/activate")
@PreAuthorize("hasAnyAuthority('user:update')")
public AysResponse<Void> activate(@PathVariable @UUID final String id) {
userUpdateService.activate(id);
return AysResponse.SUCCESS;
}

}
11 changes: 11 additions & 0 deletions src/main/java/org/ays/auth/service/AysUserUpdateService.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.ays.auth.service;

import org.ays.auth.model.request.AysUserUpdateRequest;
import org.ays.auth.util.exception.AysUserNotExistByIdException;
import org.ays.auth.util.exception.AysUserNotPassiveException;

/**
* Service interface for updating users.
Expand All @@ -14,4 +16,13 @@ public interface AysUserUpdateService {
*/
void update(String id, AysUserUpdateRequest updateRequest);

/**
* Activates a user by ID if the user is currently passive.
*
* @param id The unique identifier of the user to be activated.
* @throws AysUserNotExistByIdException if a user with the given ID does not exist.
* @throws AysUserNotPassiveException if the user is not in a passive state.
*/
void activate(String id);

}
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.AysRole;
import org.ays.auth.model.AysUser;
import org.ays.auth.model.enums.AysUserStatus;
import org.ays.auth.model.request.AysUserUpdateRequest;
import org.ays.auth.port.AysRoleReadPort;
import org.ays.auth.port.AysUserReadPort;
Expand All @@ -14,6 +15,7 @@
import org.ays.auth.util.exception.AysUserAlreadyExistsByPhoneNumberException;
import org.ays.auth.util.exception.AysUserIsNotActiveOrPassiveException;
import org.ays.auth.util.exception.AysUserNotExistByIdException;
import org.ays.auth.util.exception.AysUserNotPassiveException;
import org.ays.common.model.AysPhoneNumber;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand Down Expand Up @@ -76,6 +78,30 @@ public void update(final String id,
}


/**
* Activates a user by ID if the user is currently passive.
* This method retrieves the user by the provided ID and activates the user
*
* @param id The unique identifier of the user to be activated.
* @throws AysUserNotExistByIdException if a user with the given ID does not exist.
* @throws AysUserNotPassiveException if the user is not in a passive state and cannot be activated.
*/
@Override
public void activate(String id) {

final AysUser user = userReadPort.findById(id)
.filter(userFromDatabase -> identity.getInstitutionId().equals(userFromDatabase.getInstitution().getId()))
.orElseThrow(() -> new AysUserNotExistByIdException(id));

if (!user.isPassive()) {
throw new AysUserNotPassiveException(AysUserStatus.PASSIVE);
}

user.activate();
userSavePort.save(user);
}


/**
* Validates the uniqueness of the provided phone number.
* Checks if there is any existing user with the same phone number.
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.AysUserStatus;
import org.ays.common.util.exception.AysNotExistException;

import java.io.Serial;

/**
Exception thrown when a user does not in a passive state.
* This exception extends {@link AysNotExistException}.
*/
public final class AysUserNotPassiveException extends AysNotExistException {

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

/**
* Constructs a new {@link AysNotExistException} with the specified detail message.
*
* @param status the detail message.
*/
public AysUserNotPassiveException(AysUserStatus status) {
super("user status is not with " + status.toString().toLowerCase() + "!");
}
}
79 changes: 79 additions & 0 deletions src/test/java/org/ays/auth/controller/AysUserControllerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -638,4 +638,83 @@ void givenValidIdAndInvalidUserUpdateRequest_whenRoleIdIsNotValid_thenReturnVali
.update(Mockito.anyString(), Mockito.any(AysUserUpdateRequest.class));
}


@Test
void givenValidId_whenActivateUser_thenReturnSuccess() throws Exception {

// Given
String mockId = "793fcc5d-31cc-4704-9f0a-627ac7da517d";

// When
Mockito.doNothing()
.when(userUpdateService)
.activate(Mockito.anyString());

// Then
String endpoint = BASE_PATH.concat("/user/").concat(mockId).concat("/activate");
MockHttpServletRequestBuilder mockHttpServletRequestBuilder = AysMockMvcRequestBuilders
.patch(endpoint, mockAdminToken.getAccessToken());

AysResponse<Void> mockResponse = AysResponseBuilder.SUCCESS;

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

// Verify
Mockito.verify(userUpdateService, Mockito.times(1))
.activate(Mockito.anyString());
}

@Test
void givenValidId_whenUserUnauthorized_thenReturnAccessDeniedException() throws Exception {

// Given
String mockId = "201aec72-ecd8-49fc-86f5-2b5458871edb";

// Then
String endpoint = BASE_PATH.concat("/user/").concat(mockId).concat("/activate");
MockHttpServletRequestBuilder mockHttpServletRequestBuilder = AysMockMvcRequestBuilders
.patch(endpoint, mockUserToken.getAccessToken());

AysErrorResponse mockErrorResponse = AysErrorBuilder.FORBIDDEN;

aysMockMvc.perform(mockHttpServletRequestBuilder, mockErrorResponse)
.andExpect(AysMockResultMatchersBuilders.status()
.isForbidden())
.andExpect(AysMockResultMatchersBuilders.response()
.doesNotExist());

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

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

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

AysErrorResponse mockErrorResponse = AysErrorBuilder.VALIDATION_ERROR;

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

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

}
45 changes: 45 additions & 0 deletions src/test/java/org/ays/auth/controller/AysUserEndToEndTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.ays.common.model.response.AysPageResponse;
import org.ays.common.model.response.AysResponse;
import org.ays.common.model.response.AysResponseBuilder;
import org.ays.common.util.AysRandomUtil;
import org.ays.institution.model.Institution;
import org.ays.institution.model.InstitutionBuilder;
import org.ays.util.AysMockMvcRequestBuilders;
Expand Down Expand Up @@ -372,4 +373,48 @@ void givenValidIdAndUserUpdateRequest_whenUserUpdated_thenReturnSuccess() throws
Assertions.assertNotNull(userFromDatabase.get().getUpdatedAt());
}

@Test
void givenValidId_whenActivateUser_thenReturnSuccess() throws Exception {

// Initialize
Institution institution = new InstitutionBuilder()
.withId(AysValidTestData.Admin.INSTITUTION_ID)
.build();

List<AysRole> roles = roleReadPort.findAllActivesByInstitutionId(institution.getId());

AysUser user = userSavePort.save(
new AysUserBuilder()
.withId(AysRandomUtil.generateUUID())
.withValidValues()
.withRoles(roles)
.withInstitution(institution)
.withStatus(AysUserStatus.PASSIVE)
.build()
);

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

// Then
String endpoint = BASE_PATH.concat("/user/").concat(id).concat("/activate");
MockHttpServletRequestBuilder mockHttpServletRequestBuilder = AysMockMvcRequestBuilders
.patch(endpoint, adminToken.getAccessToken());

AysResponse<Void> mockResponse = AysResponseBuilder.SUCCESS;

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

// Verify
Optional<AysUser> userFromDatabase = userReadPort.findById(user.getId());

Assertions.assertTrue(userFromDatabase.isPresent());
Assertions.assertEquals(userFromDatabase.get().getId(), user.getId());
Assertions.assertEquals(AysUserStatus.ACTIVE, userFromDatabase.get().getStatus());
}

}
Loading

0 comments on commit e359f7d

Please sign in to comment.