-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: initial version of skillLab integration for testing
- Loading branch information
1 parent
069a625
commit 3c40283
Showing
41 changed files
with
1,303 additions
and
89 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
103 changes: 103 additions & 0 deletions
103
...vice/src/main/kotlin/com/aamdigital/aambackendservice/skill/controller/SkillController.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
package com.aamdigital.aambackendservice.skill.controller | ||
|
||
import com.aamdigital.aambackendservice.domain.UseCaseOutcome | ||
import com.aamdigital.aambackendservice.error.HttpErrorDto | ||
import com.aamdigital.aambackendservice.skill.core.FetchUserProfileUpdatesRequest | ||
import com.aamdigital.aambackendservice.skill.core.FetchUserProfileUpdatesUseCase | ||
import com.aamdigital.aambackendservice.skill.core.SearchUserProfileRequest | ||
import com.aamdigital.aambackendservice.skill.core.SearchUserProfileUseCase | ||
import com.aamdigital.aambackendservice.skill.skilllab.SkillLabFetchUserProfileUpdatesErrorCode | ||
import org.springframework.http.ResponseEntity | ||
import org.springframework.web.bind.annotation.GetMapping | ||
import org.springframework.web.bind.annotation.PostMapping | ||
import org.springframework.web.bind.annotation.RequestBody | ||
import org.springframework.web.bind.annotation.RequestMapping | ||
import org.springframework.web.bind.annotation.RestController | ||
|
||
@RestController | ||
@RequestMapping("/v1/skill") | ||
class SkillController( | ||
private val fetchUserProfileUpdatesUseCase: FetchUserProfileUpdatesUseCase, // todo needs no-op implementation | ||
private val searchUserProfileUseCase: SearchUserProfileUseCase, // todo needs no-op implementation | ||
) { | ||
|
||
@GetMapping("/user-profile") | ||
fun fetchUserProfiles( | ||
fullName: String?, | ||
email: String?, | ||
phone: String?, | ||
): ResponseEntity<Any> { | ||
val result = searchUserProfileUseCase.run( | ||
request = SearchUserProfileRequest( | ||
fullName = fullName, | ||
email = email, | ||
phone = phone, | ||
), | ||
) | ||
|
||
return when (result) { | ||
is UseCaseOutcome.Failure<*> -> { | ||
when (result.errorCode) { | ||
// SkillLabFetchUserProfileUpdatesErrorCode.EXTERNAL_SYSTEM_ERROR | ||
// -> ResponseEntity.internalServerError().body( | ||
// HttpErrorDto( | ||
// errorCode = result.errorCode.toString(), | ||
// errorMessage = result.errorMessage | ||
// ) | ||
// ) | ||
|
||
else -> ResponseEntity.badRequest().body( | ||
ResponseEntity.internalServerError().body( | ||
HttpErrorDto( | ||
errorCode = result.errorCode.toString(), | ||
errorMessage = result.errorMessage | ||
) | ||
) | ||
) | ||
} | ||
ResponseEntity.badRequest().body( | ||
result.errorMessage | ||
) | ||
} | ||
|
||
is UseCaseOutcome.Success<*> -> ResponseEntity.ok().body(result.data) | ||
} | ||
} | ||
|
||
@PostMapping | ||
fun fetchUserProfileUpdates( | ||
@RequestBody request: FetchUserProfileUpdatesRequest, | ||
): ResponseEntity<Any> { | ||
val result = fetchUserProfileUpdatesUseCase.run( | ||
request = request | ||
) | ||
|
||
return when (result) { | ||
is UseCaseOutcome.Failure<*> -> { | ||
when (result.errorCode) { | ||
SkillLabFetchUserProfileUpdatesErrorCode.EXTERNAL_SYSTEM_ERROR | ||
-> ResponseEntity.internalServerError().body( | ||
HttpErrorDto( | ||
errorCode = result.errorCode.toString(), | ||
errorMessage = result.errorMessage | ||
) | ||
) | ||
|
||
else -> ResponseEntity.badRequest().body( | ||
ResponseEntity.internalServerError().body( | ||
HttpErrorDto( | ||
errorCode = result.errorCode.toString(), | ||
errorMessage = result.errorMessage | ||
) | ||
) | ||
) | ||
} | ||
ResponseEntity.badRequest().body( | ||
result.errorMessage | ||
) | ||
} | ||
|
||
is UseCaseOutcome.Success<*> -> ResponseEntity.ok().body(result.data) | ||
} | ||
} | ||
} |
66 changes: 66 additions & 0 deletions
66
...in/kotlin/com/aamdigital/aambackendservice/skill/core/DefaultUserProfileUpdateConsumer.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package com.aamdigital.aambackendservice.skill.core | ||
|
||
import com.aamdigital.aambackendservice.domain.DomainReference | ||
import com.aamdigital.aambackendservice.error.AamException | ||
import com.aamdigital.aambackendservice.queue.core.QueueMessageParser | ||
import com.aamdigital.aambackendservice.skill.core.event.UserProfileUpdateEvent | ||
import com.aamdigital.aambackendservice.skill.di.UserProfileUpdateEventQueueConfiguration.Companion.USER_PROFILE_UPDATE_QUEUE | ||
import com.rabbitmq.client.Channel | ||
import org.slf4j.LoggerFactory | ||
import org.springframework.amqp.AmqpRejectAndDontRequeueException | ||
import org.springframework.amqp.core.Message | ||
import org.springframework.amqp.rabbit.annotation.RabbitListener | ||
|
||
open class DefaultUserProfileUpdateConsumer( | ||
private val messageParser: QueueMessageParser, | ||
private val syncUserProfileUseCase: SyncUserProfileUseCase, | ||
) : UserProfileUpdateConsumer { | ||
|
||
private val logger = LoggerFactory.getLogger(javaClass) | ||
|
||
@RabbitListener( | ||
queues = [USER_PROFILE_UPDATE_QUEUE], | ||
concurrency = "1-1" | ||
) | ||
override fun consume(rawMessage: String, message: Message, channel: Channel) { | ||
val type = try { | ||
messageParser.getTypeKClass(rawMessage.toByteArray()) | ||
} catch (ex: AamException) { | ||
throw AmqpRejectAndDontRequeueException("[${ex.code}] ${ex.localizedMessage}", ex) | ||
} | ||
|
||
when (type.qualifiedName) { | ||
UserProfileUpdateEvent::class.qualifiedName -> { | ||
val payload = messageParser.getPayload( | ||
body = rawMessage.toByteArray(), | ||
kClass = UserProfileUpdateEvent::class | ||
) | ||
try { | ||
syncUserProfileUseCase.run( | ||
SyncUserProfileRequest( | ||
userProfile = DomainReference(payload.userProfileId), | ||
project = DomainReference(payload.projectId), | ||
) | ||
) | ||
} catch (ex: Exception) { | ||
throw AmqpRejectAndDontRequeueException( | ||
"[USECASE_ERROR] ${ex.localizedMessage}", | ||
ex | ||
) | ||
} | ||
return | ||
} | ||
|
||
else -> { | ||
logger.warn( | ||
"[DefaultUserProfileUpdateConsumer] Could not find any use case for this EventType: {}", | ||
type.qualifiedName, | ||
) | ||
|
||
throw AmqpRejectAndDontRequeueException( | ||
"[NO_USECASE_CONFIGURED] Could not found matching use case for: ${type.qualifiedName}", | ||
) | ||
} | ||
} | ||
} | ||
} |
60 changes: 60 additions & 0 deletions
60
...n/kotlin/com/aamdigital/aambackendservice/skill/core/DefaultUserProfileUpdatePublisher.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
package com.aamdigital.aambackendservice.skill.core | ||
|
||
import com.aamdigital.aambackendservice.error.AamErrorCode | ||
import com.aamdigital.aambackendservice.error.AamException | ||
import com.aamdigital.aambackendservice.error.InternalServerException | ||
import com.aamdigital.aambackendservice.queue.core.QueueMessage | ||
import com.aamdigital.aambackendservice.skill.core.event.UserProfileUpdateEvent | ||
import com.fasterxml.jackson.databind.ObjectMapper | ||
import org.slf4j.LoggerFactory | ||
import org.springframework.amqp.AmqpException | ||
import org.springframework.amqp.rabbit.core.RabbitTemplate | ||
import java.time.Instant | ||
import java.time.ZoneOffset | ||
import java.time.format.DateTimeFormatter | ||
import java.util.* | ||
|
||
class DefaultUserProfileUpdatePublisher( | ||
private val objectMapper: ObjectMapper, | ||
private val rabbitTemplate: RabbitTemplate, | ||
) : UserProfileUpdatePublisher { | ||
|
||
enum class DefaultUserProfileUpdatePublisherErrorCode : AamErrorCode { | ||
EVENT_PUBLISH_ERROR | ||
} | ||
|
||
private val logger = LoggerFactory.getLogger(javaClass) | ||
|
||
@Throws(AamException::class) | ||
override fun publish(channel: String, event: UserProfileUpdateEvent): QueueMessage { | ||
val message = QueueMessage( | ||
id = UUID.randomUUID(), | ||
eventType = UserProfileUpdateEvent::class.java.canonicalName, | ||
event = event, | ||
createdAt = Instant.now() | ||
.atOffset(ZoneOffset.UTC) | ||
.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) | ||
) | ||
|
||
try { | ||
rabbitTemplate.convertAndSend( | ||
channel, | ||
objectMapper.writeValueAsString(message) | ||
) | ||
} catch (ex: AmqpException) { | ||
throw InternalServerException( | ||
message = "Could not publish UserProfileUpdateEvent: $event", | ||
code = DefaultUserProfileUpdatePublisherErrorCode.EVENT_PUBLISH_ERROR, | ||
cause = ex | ||
) | ||
} | ||
|
||
logger.trace( | ||
"[DefaultNotificationEventPublisher]: publish message to channel '{}' Payload: {}", | ||
channel, | ||
objectMapper.writeValueAsString(message) | ||
) | ||
|
||
return message | ||
} | ||
} |
20 changes: 20 additions & 0 deletions
20
...main/kotlin/com/aamdigital/aambackendservice/skill/core/FetchUserProfileUpdatesUseCase.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package com.aamdigital.aambackendservice.skill.core | ||
|
||
import com.aamdigital.aambackendservice.domain.DomainReference | ||
import com.aamdigital.aambackendservice.domain.DomainUseCase | ||
import com.aamdigital.aambackendservice.domain.UseCaseData | ||
import com.aamdigital.aambackendservice.domain.UseCaseRequest | ||
|
||
data class FetchUserProfileUpdatesRequest( | ||
val projectId: String, | ||
) : UseCaseRequest | ||
|
||
/** | ||
* result will be a list of user profiles with updates available. | ||
*/ | ||
data class FetchUserProfileUpdatesData( | ||
val result: List<DomainReference> | ||
) : UseCaseData | ||
|
||
abstract class FetchUserProfileUpdatesUseCase : | ||
DomainUseCase<FetchUserProfileUpdatesRequest, FetchUserProfileUpdatesData>() |
19 changes: 19 additions & 0 deletions
19
...e/src/main/kotlin/com/aamdigital/aambackendservice/skill/core/SearchUserProfileUseCase.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
package com.aamdigital.aambackendservice.skill.core | ||
|
||
import com.aamdigital.aambackendservice.domain.DomainUseCase | ||
import com.aamdigital.aambackendservice.domain.UseCaseData | ||
import com.aamdigital.aambackendservice.domain.UseCaseRequest | ||
import com.aamdigital.aambackendservice.skill.domain.UserProfile | ||
|
||
data class SearchUserProfileRequest( | ||
val fullName: String?, | ||
val email: String?, | ||
val phone: String?, | ||
) : UseCaseRequest | ||
|
||
data class SearchUserProfileData( | ||
val result: List<UserProfile> | ||
) : UseCaseData | ||
|
||
abstract class SearchUserProfileUseCase : | ||
DomainUseCase<SearchUserProfileRequest, SearchUserProfileData>() |
15 changes: 15 additions & 0 deletions
15
...ckend-service/src/main/kotlin/com/aamdigital/aambackendservice/skill/core/SkillStorage.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
package com.aamdigital.aambackendservice.skill.core | ||
|
||
import com.aamdigital.aambackendservice.domain.DomainReference | ||
import com.aamdigital.aambackendservice.error.AamException | ||
import com.aamdigital.aambackendservice.skill.domain.EscoSkill | ||
import org.springframework.data.domain.Pageable | ||
|
||
interface SkillStorage { | ||
|
||
@Throws(AamException::class) | ||
fun fetchSkill(externalIdentifier: DomainReference): EscoSkill | ||
|
||
@Throws(AamException::class) | ||
fun fetchSkills(pageable: Pageable): List<EscoSkill> | ||
} |
77 changes: 77 additions & 0 deletions
77
...rc/main/kotlin/com/aamdigital/aambackendservice/skill/core/SqlSearchUserProfileUseCase.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package com.aamdigital.aambackendservice.skill.core | ||
|
||
import com.aamdigital.aambackendservice.domain.UseCaseOutcome | ||
import com.aamdigital.aambackendservice.skill.domain.EscoSkill | ||
import com.aamdigital.aambackendservice.skill.domain.SkillUsage | ||
import com.aamdigital.aambackendservice.skill.domain.UserProfile | ||
import com.aamdigital.aambackendservice.skill.repository.SkillLabUserProfileEntity | ||
import com.aamdigital.aambackendservice.skill.repository.SkillLabUserProfileRepository | ||
import org.springframework.data.domain.Example | ||
import org.springframework.data.domain.ExampleMatcher | ||
import org.springframework.data.domain.Pageable | ||
|
||
class SqlSearchUserProfileUseCase( | ||
private val userProfileRepository: SkillLabUserProfileRepository, | ||
) : SearchUserProfileUseCase() { | ||
override fun apply(request: SearchUserProfileRequest): UseCaseOutcome<SearchUserProfileData> { | ||
|
||
val matcher = ExampleMatcher.matchingAll() | ||
.withIgnorePaths( | ||
"id", | ||
"externalIdentifier", | ||
"skills", | ||
"updatedAt", | ||
"latestSyncAt", | ||
"importedAt", | ||
) | ||
.withIgnoreCase() | ||
.withIncludeNullValues() | ||
.withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING) | ||
|
||
val userProfile = SkillLabUserProfileEntity( | ||
id = 0, | ||
externalIdentifier = "", | ||
fullName = request.fullName, | ||
mobileNumber = request.phone, | ||
email = request.email, | ||
skills = emptySet(), | ||
updatedAt = "", | ||
latestSyncAt = null, | ||
importedAt = null, | ||
) | ||
|
||
val example = Example.of(userProfile, matcher) | ||
|
||
val searchResults = userProfileRepository.findAll(example, Pageable.ofSize(10)) | ||
|
||
if (searchResults.isEmpty) { | ||
return UseCaseOutcome.Success( | ||
data = SearchUserProfileData( | ||
result = emptyList(), | ||
) | ||
) | ||
} | ||
|
||
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.externalIdentifier | ||
) | ||
}, | ||
latestSyncAt = it.latestSyncAt?.toInstant(), | ||
importedAt = it.importedAt?.toInstant(), | ||
updatedAtExternalSystem = it.updatedAt | ||
) | ||
} | ||
) | ||
) | ||
} | ||
} |
Oops, something went wrong.