Skip to content

Commit

Permalink
feat: improve user profile search
Browse files Browse the repository at this point in the history
  • Loading branch information
tomwwinter committed Dec 3, 2024
1 parent d377f01 commit 238d789
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import org.springframework.data.repository.CrudRepository
import org.springframework.stereotype.Repository
import java.util.*

@Entity
@Entity(name = "couchdb_sync_entry")
data class SyncEntry(
@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,47 @@ import com.aamdigital.aambackendservice.skill.repository.SkillLabUserProfileEnti
import com.aamdigital.aambackendservice.skill.repository.SkillLabUserProfileRepository
import org.springframework.data.domain.Example
import org.springframework.data.domain.ExampleMatcher
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable

/**
* Search for an SkillLabUserProfile with provides information from SearchUserProfileRequest
*
* 1. search for exact email matches
* 2. search for mobile phone matches
* 3. search for name
*
* The first exact match will be returned. Otherwise, a list of possible matches is returned
*
*/
class SqlSearchUserProfileUseCase(
private val userProfileRepository: SkillLabUserProfileRepository,
) : SearchUserProfileUseCase() {
override fun apply(request: SearchUserProfileRequest): UseCaseOutcome<SearchUserProfileData> {

val matcher = ExampleMatcher.matchingAll()
companion object {
private const val MAX_RESULTS = 10;

private val MATCHER_EMAIL = ExampleMatcher.matchingAny()
.withIgnorePaths(
"id",
"externalIdentifier",
"fullName",
"mobileNumber",
"skills",
"updatedAt",
"latestSyncAt",
"importedAt",
)
.withIgnoreCase()
.withIncludeNullValues()
.withStringMatcher(ExampleMatcher.StringMatcher.EXACT)

private val MATCHER_PHONE = ExampleMatcher.matchingAny()
.withIgnorePaths(
"id",
"externalIdentifier",
"fullName",
"email",
"skills",
"updatedAt",
"latestSyncAt",
Expand All @@ -28,6 +58,24 @@ class SqlSearchUserProfileUseCase(
.withIncludeNullValues()
.withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING)

private val MATCHER_NAME = ExampleMatcher.matchingAll()
.withIgnorePaths(
"id",
"externalIdentifier",
"mobileNumber",
"email",
"skills",
"updatedAt",
"latestSyncAt",
"importedAt",
)
.withIgnoreCase()
.withIncludeNullValues()
.withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING)
}

override fun apply(request: SearchUserProfileRequest): UseCaseOutcome<SearchUserProfileData> {

val userProfile = SkillLabUserProfileEntity(
id = 0,
externalIdentifier = "",
Expand All @@ -40,38 +88,75 @@ class SqlSearchUserProfileUseCase(
importedAt = null,
)

val example = Example.of(userProfile, matcher)
var searchResults: Page<SkillLabUserProfileEntity>;

val searchResults = userProfileRepository.findAll(example, Pageable.ofSize(10))
// check for exact matches for email
if (!userProfile.email.isNullOrBlank()) {
searchResults = userProfileRepository.findAll(
Example.of(userProfile, MATCHER_EMAIL), Pageable.ofSize(MAX_RESULTS)
)

if (searchResults.isEmpty) {
return UseCaseOutcome.Success(
data = SearchUserProfileData(
result = emptyList(),
if (!searchResults.isEmpty) {
return UseCaseOutcome.Success(
data = SearchUserProfileData(
result = searchResults.toList().map {
toDto(it)
}
)
)
}
}

// check for matches for phone
if (!userProfile.mobileNumber.isNullOrBlank()) {
searchResults = userProfileRepository.findAll(
Example.of(userProfile, MATCHER_PHONE), Pageable.ofSize(MAX_RESULTS)
)

if (!searchResults.isEmpty) {
return UseCaseOutcome.Success(
data = SearchUserProfileData(
result = searchResults.toList().map {
toDto(it)
}
)
)
}
}

// check for name
searchResults = if (!userProfile.fullName.isNullOrBlank()) {
userProfileRepository.findAll(
Example.of(userProfile, MATCHER_NAME), Pageable.ofSize(MAX_RESULTS)
)
} else {
Page.empty()
}

return UseCaseOutcome.Success(
data = SearchUserProfileData(
result = searchResults.toList().map {
UserProfile(
id = it.externalIdentifier,
fullName = it.fullName,
email = it.email,
phone = it.mobileNumber,
skills = it.skills.map { skill ->
EscoSkill(
usage = SkillUsage.valueOf(skill.usage.uppercase()),
escoUri = skill.escoUri
)
},
latestSyncAt = it.latestSyncAt?.toInstant(),
importedAt = it.importedAt?.toInstant(),
updatedAtExternalSystem = it.updatedAt
)
toDto(it)
}
)
)
}

private fun toDto(it: SkillLabUserProfileEntity): UserProfile {
return UserProfile(
id = it.externalIdentifier,
fullName = it.fullName,
email = it.email,
phone = it.mobileNumber,
skills = it.skills.map { skill ->
EscoSkill(
usage = SkillUsage.valueOf(skill.usage.uppercase()),
escoUri = skill.escoUri
)
},
latestSyncAt = it.latestSyncAt?.toInstant(),
importedAt = it.importedAt?.toInstant(),
updatedAtExternalSystem = it.updatedAt
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ import org.springframework.web.client.RestClient
havingValue = "true",
matchIfMissing = false
)
class AamRenderApiClientConfiguration(
class SkillLabApiClientConfiguration(
val basePath: String,
val apiKey: String,
val projectId: String,
val responseTimeoutInSeconds: Int = 30,
)

Expand All @@ -49,7 +50,7 @@ class SkillConfiguration {
matchIfMissing = false
)
fun skillLabApiClient(
configuration: AamRenderApiClientConfiguration
configuration: SkillLabApiClientConfiguration
): RestClient {
val clientBuilder = RestClient.builder().baseUrl(configuration.basePath)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@ package com.aamdigital.aambackendservice.skill.job

import com.aamdigital.aambackendservice.skill.core.FetchUserProfileUpdatesRequest
import com.aamdigital.aambackendservice.skill.core.FetchUserProfileUpdatesUseCase
import com.aamdigital.aambackendservice.skill.di.SkillLabApiClientConfiguration
import org.slf4j.LoggerFactory
import org.springframework.context.annotation.Configuration
import org.springframework.scheduling.annotation.Scheduled

@Configuration
class SyncSkillsJob(
private val skillLabFetchUserProfileUpdatesUseCase: FetchUserProfileUpdatesUseCase
private val skillLabFetchUserProfileUpdatesUseCase: FetchUserProfileUpdatesUseCase,
private val skillLabApiClientConfiguration: SkillLabApiClientConfiguration,
) {

private val logger = LoggerFactory.getLogger(javaClass)
Expand All @@ -18,7 +20,7 @@ class SyncSkillsJob(
private var MAX_ERROR_COUNT: Int = 5
}

@Scheduled(fixedDelay = (60000 * 10))
@Scheduled(fixedDelay = (60000 * 10)) // every 10 minutes
fun checkForCouchDbChanges() {
if (ERROR_COUNTER >= MAX_ERROR_COUNT) {
logger.trace("${this.javaClass.name}: MAX_ERROR_COUNT reached. Not starting job again.")
Expand All @@ -28,7 +30,7 @@ class SyncSkillsJob(
try {
skillLabFetchUserProfileUpdatesUseCase.run(
request = FetchUserProfileUpdatesRequest(
projectId = "343"
projectId = skillLabApiClientConfiguration.projectId
)
)
} catch (ex: Exception) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,10 @@ class SkillLabSyncUserProfileUseCase(
result = UserProfile(
id = userProfileEntity.externalIdentifier,
fullName = userProfileEntity.fullName,
phone = userProfileEntity.mobileNumber,
phone = userProfileEntity.mobileNumber
?.replace(" ", "")
?.replace("-", "")
?.trim(),
email = userProfileEntity.email,
skills = allSkillsEntities.map { skill ->
EscoSkill(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ sqs-client-configuration:
skilllab-api-client-configuration:
enabled: true
api-key: skilllab-api-key
project-id: dummy-project
base-path: http://localhost:9005/skilllab
response-timeout-in-seconds: 15

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ sqs-client-configuration:
skilllab-api-client-configuration:
enabled: false
api-key: skilllab-api-key
project-id: dummy-project
base-path: http://localhost:9005/skilllab # todo test container
response-timeout-in-seconds: 15

Expand Down

0 comments on commit 238d789

Please sign in to comment.