diff --git a/application/aam-backend-service/src/main/kotlin/com/aamdigital/aambackendservice/skill/controller/SkillController.kt b/application/aam-backend-service/src/main/kotlin/com/aamdigital/aambackendservice/skill/controller/SkillController.kt index 19d4705..8d139b2 100644 --- a/application/aam-backend-service/src/main/kotlin/com/aamdigital/aambackendservice/skill/controller/SkillController.kt +++ b/application/aam-backend-service/src/main/kotlin/com/aamdigital/aambackendservice/skill/controller/SkillController.kt @@ -2,6 +2,7 @@ package com.aamdigital.aambackendservice.skill.controller import com.aamdigital.aambackendservice.domain.UseCaseOutcome import com.aamdigital.aambackendservice.error.HttpErrorDto +import com.aamdigital.aambackendservice.skill.core.SearchUserProfileData import com.aamdigital.aambackendservice.skill.core.SearchUserProfileRequest import com.aamdigital.aambackendservice.skill.core.SearchUserProfileUseCase import com.aamdigital.aambackendservice.skill.domain.EscoSkill @@ -17,6 +18,18 @@ import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RestController +data class PaginationDto( + val currentPage: Int, + val pageSize: Int, + val totalPages: Int, + val totalElements: Int, +) + +data class FetchUserProfilesDto( + val pagination: PaginationDto, + val results: List, +) + @RestController @RequestMapping("/v1/skill") @ConditionalOnProperty( @@ -36,12 +49,43 @@ class SkillController( fullName: String = "", email: String = "", phone: String = "", + page: Int = 1, + pageSize: Int = 10, ): ResponseEntity { + if (page < 1) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body( + HttpErrorDto( + errorCode = "BAD_REQUEST", + errorMessage = "Page must be greater than 0", + ) + ) + } + + if (pageSize < 1) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body( + HttpErrorDto( + errorCode = "BAD_REQUEST", + errorMessage = "Page size must not be less than one", + ) + ) + } + + if (pageSize > 100) { + return ResponseEntity.status(HttpStatus.BAD_REQUEST).body( + HttpErrorDto( + errorCode = "BAD_REQUEST", + errorMessage = "Max pageSize limit is 100", + ) + ) + } + val result = searchUserProfileUseCase.run( request = SearchUserProfileRequest( fullName = fullName, email = email, phone = phone, + page = page, + pageSize = pageSize ), ) @@ -62,7 +106,17 @@ class SkillController( ) } - is UseCaseOutcome.Success<*> -> ResponseEntity.ok().body(result.data) + is UseCaseOutcome.Success -> ResponseEntity.ok().body( + FetchUserProfilesDto( + pagination = PaginationDto( + currentPage = page, + pageSize = pageSize, + totalElements = result.data.totalElements, + totalPages = result.data.totalPages, + ), + results = result.data.result + ) + ) } } diff --git a/application/aam-backend-service/src/main/kotlin/com/aamdigital/aambackendservice/skill/core/SearchUserProfileUseCase.kt b/application/aam-backend-service/src/main/kotlin/com/aamdigital/aambackendservice/skill/core/SearchUserProfileUseCase.kt index 5e52d54..3649e46 100644 --- a/application/aam-backend-service/src/main/kotlin/com/aamdigital/aambackendservice/skill/core/SearchUserProfileUseCase.kt +++ b/application/aam-backend-service/src/main/kotlin/com/aamdigital/aambackendservice/skill/core/SearchUserProfileUseCase.kt @@ -9,10 +9,14 @@ data class SearchUserProfileRequest( val fullName: String?, val email: String?, val phone: String?, + val page: Int, + val pageSize: Int, ) : UseCaseRequest data class SearchUserProfileData( - val result: List + val result: List, + val totalElements: Int, + val totalPages: Int, ) : UseCaseData abstract class SearchUserProfileUseCase : diff --git a/application/aam-backend-service/src/main/kotlin/com/aamdigital/aambackendservice/skill/core/SqlSearchUserProfileUseCase.kt b/application/aam-backend-service/src/main/kotlin/com/aamdigital/aambackendservice/skill/core/SqlSearchUserProfileUseCase.kt index bf44360..4be7a50 100644 --- a/application/aam-backend-service/src/main/kotlin/com/aamdigital/aambackendservice/skill/core/SqlSearchUserProfileUseCase.kt +++ b/application/aam-backend-service/src/main/kotlin/com/aamdigital/aambackendservice/skill/core/SqlSearchUserProfileUseCase.kt @@ -26,8 +26,6 @@ class SqlSearchUserProfileUseCase( ) : SearchUserProfileUseCase() { companion object { - private const val MAX_RESULTS = 10; - private val MATCHER_EMAIL = ExampleMatcher.matchingAny() .withIgnorePaths( "id", @@ -88,60 +86,55 @@ class SqlSearchUserProfileUseCase( importedAt = null, ) - var searchResults: Page; + var searchResults: Page + val pageable = Pageable.ofSize(request.pageSize).withPage(request.page - 1) // check for exact matches for email if (!userProfile.email.isNullOrBlank()) { searchResults = userProfileRepository.findAll( - Example.of(userProfile, MATCHER_EMAIL), Pageable.ofSize(MAX_RESULTS) + Example.of(userProfile, MATCHER_EMAIL), pageable ) if (!searchResults.isEmpty) { - return UseCaseOutcome.Success( - data = SearchUserProfileData( - result = searchResults.toList().map { - toDto(it) - } - ) - ) + return asSuccessResponse(searchResults); } } // check for matches for phone if (!userProfile.mobileNumber.isNullOrBlank()) { searchResults = userProfileRepository.findAll( - Example.of(userProfile, MATCHER_PHONE), Pageable.ofSize(MAX_RESULTS) + Example.of(userProfile, MATCHER_PHONE), pageable ) if (!searchResults.isEmpty) { - return UseCaseOutcome.Success( - data = SearchUserProfileData( - result = searchResults.toList().map { - toDto(it) - } - ) - ) + return asSuccessResponse(searchResults); } } // check for name searchResults = if (!userProfile.fullName.isNullOrBlank()) { userProfileRepository.findAll( - Example.of(userProfile, MATCHER_NAME), Pageable.ofSize(MAX_RESULTS) + Example.of(userProfile, MATCHER_NAME), pageable ) } else { Page.empty() } - return UseCaseOutcome.Success( - data = SearchUserProfileData( - result = searchResults.toList().map { - toDto(it) - } - ) - ) + return asSuccessResponse(searchResults); } + private fun asSuccessResponse( + results: Page, + ): UseCaseOutcome.Success = UseCaseOutcome.Success( + data = SearchUserProfileData( + result = results.toList().map { + toDto(it) + }, + totalElements = results.totalElements.toInt(), + totalPages = results.totalPages + ) + ) + private fun toDto(it: SkillLabUserProfileEntity): UserProfile { return UserProfile( id = it.externalIdentifier, diff --git a/docs/api-specs/skill-api-v1.yaml b/docs/api-specs/skill-api-v1.yaml index 5b3db63..4ba531f 100644 --- a/docs/api-specs/skill-api-v1.yaml +++ b/docs/api-specs/skill-api-v1.yaml @@ -32,6 +32,25 @@ paths: schema: type: string description: Optional phone filter + - name: pageSize + in: query + required: false + schema: + type: number + minimum: 1 + maximum: 100 + default: 1 + required: false + description: Number of elements returned by each page + - name: page + in: query + required: false + schema: + type: number + minimum: 1 + default: 1 + required: false + description: The page to return responses: '200': description: Successful response @@ -40,7 +59,7 @@ paths: schema: type: array items: - $ref: '#/components/schemas/UserProfile' + $ref: '#/components/schemas/UserProfilePage' /user-profile/{userProfileId}: get: @@ -112,6 +131,35 @@ paths: components: schemas: + Pagination: + type: object + properties: + currentPage: + type: number + minimum: 1 + default: 1 + pageSize: + type: number + minimum: 1 + maximum: 100 + default: 10 + totalPages: + type: number + example: 5 + totalElements: + type: number + example: 42 + + UserProfilePage: + type: object + properties: + pagination: + $ref: '#/components/schemas/Pagination' + results: + type: array + items: + $ref: '#/components/schemas/UserProfile' + UserProfile: type: object properties: