Skip to content

Commit

Permalink
feat: pagination for search results
Browse files Browse the repository at this point in the history
  • Loading branch information
tomwwinter committed Dec 4, 2024
1 parent c11e9a2 commit a506c78
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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<UserProfile>,
)

@RestController
@RequestMapping("/v1/skill")
@ConditionalOnProperty(
Expand All @@ -36,12 +49,43 @@ class SkillController(
fullName: String = "",
email: String = "",
phone: String = "",
page: Int = 1,
pageSize: Int = 10,
): ResponseEntity<Any> {
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
),
)

Expand All @@ -62,7 +106,17 @@ class SkillController(
)
}

is UseCaseOutcome.Success<*> -> ResponseEntity.ok().body(result.data)
is UseCaseOutcome.Success<SearchUserProfileData> -> ResponseEntity.ok().body(
FetchUserProfilesDto(
pagination = PaginationDto(
currentPage = page,
pageSize = pageSize,
totalElements = result.data.totalElements,
totalPages = result.data.totalPages,
),
results = result.data.result
)
)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<UserProfile>
val result: List<UserProfile>,
val totalElements: Int,
val totalPages: Int,
) : UseCaseData

abstract class SearchUserProfileUseCase :
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ class SqlSearchUserProfileUseCase(
) : SearchUserProfileUseCase() {

companion object {
private const val MAX_RESULTS = 10;

private val MATCHER_EMAIL = ExampleMatcher.matchingAny()
.withIgnorePaths(
"id",
Expand Down Expand Up @@ -88,60 +86,55 @@ class SqlSearchUserProfileUseCase(
importedAt = null,
)

var searchResults: Page<SkillLabUserProfileEntity>;
var searchResults: Page<SkillLabUserProfileEntity>
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<SkillLabUserProfileEntity>,
): UseCaseOutcome.Success<SearchUserProfileData> = 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,
Expand Down
50 changes: 49 additions & 1 deletion docs/api-specs/skill-api-v1.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -40,7 +59,7 @@ paths:
schema:
type: array
items:
$ref: '#/components/schemas/UserProfile'
$ref: '#/components/schemas/UserProfilePage'

/user-profile/{userProfileId}:
get:
Expand Down Expand Up @@ -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:
Expand Down

0 comments on commit a506c78

Please sign in to comment.