Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestPart
import org.springframework.web.bind.annotation.RestController
import reactor.core.publisher.Flux
import reactor.core.publisher.Mono
Expand Down Expand Up @@ -69,4 +70,46 @@ class ProfileController(
profileService.deleteOne(request.id, profileId)
return Mono.empty()
}

@PostMapping("/cv")
open fun uploadCv(
request: ServerHttpRequest,
principal: Principal,
@RequestPart file: ByteArray
): Mono<Unit> {
profileService.setOneCv(request.id, principal.name, file)
return Mono.empty()
}

@GetMapping(
value = ["/cv"],
produces = ["application/pdf"]
)
open fun downloadCv(
request: ServerHttpRequest,
principal: Principal
): Mono<ByteArray> {
return Mono.just(profileService.findOneCv(request.id, principal.name))
}

@PostMapping("/photo")
open fun uploadPhoto(
request: ServerHttpRequest,
principal: Principal,
@RequestPart file: ByteArray
): Mono<Unit> {
profileService.setOneProfilePicture(request.id, principal.name, file)
return Mono.empty()
}

@GetMapping(
value = ["/photo"],
produces = ["image/png"]
)
open fun downloadPhoto(
request: ServerHttpRequest,
principal: Principal
): Mono<ByteArray> {
return Mono.just(profileService.findOneProfilePicture(request.id, principal.name))
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.linkedout.backend.service

import com.google.protobuf.ByteString
import com.linkedout.backend.converter.profile.SetProfileDtoToProto
import com.linkedout.backend.converter.profile.UpdateProfileDtoToProto
import com.linkedout.backend.dto.profile.ProfileWithStatsDto
Expand All @@ -12,8 +13,12 @@ import com.linkedout.common.utils.RequestResponseFactory
import com.linkedout.proto.models.ProfileOuterClass
import com.linkedout.proto.services.Profile.DeleteProfileRequest
import com.linkedout.proto.services.Profile.GetProfilesRequestingDeletionRequest
import com.linkedout.proto.services.Profile.GetUserCvRequest
import com.linkedout.proto.services.Profile.GetUserProfilePictureRequest
import com.linkedout.proto.services.Profile.GetUserProfileRequest
import com.linkedout.proto.services.Profile.RequestUserProfileDeletionRequest
import com.linkedout.proto.services.Profile.SetUserCvRequest
import com.linkedout.proto.services.Profile.SetUserProfilePictureRequest
import com.linkedout.proto.services.Profile.SetUserProfileRequest
import com.linkedout.proto.services.Profile.UpdateUserProfileRequest
import org.springframework.beans.factory.annotation.Value
Expand All @@ -29,7 +34,11 @@ class ProfileService(
@Value("\${app.services.profile.subjects.findAllRequestingDeletion}") private val findAllRequestingDeletionSubject: String,
@Value("\${app.services.profile.subjects.requestDeletionOfUser}") private val requestDeletionOfUserSubject: String,
@Value("\${app.services.profile.subjects.saveOneOfUser}") private val saveOneOfUserSubject: String,
@Value("\${app.services.profile.subjects.updateOneOfUser}") private val updateOneOfUserSubject: String
@Value("\${app.services.profile.subjects.updateOneOfUser}") private val updateOneOfUserSubject: String,
@Value("\${app.services.profile.subjects.findOneCvOfUser}") private val findOneCvOfUserSubject: String,
@Value("\${app.services.profile.subjects.saveOneCvOfUser}") private val saveOneCvOfUserSubject: String,
@Value("\${app.services.profile.subjects.findOneProfilePictureOfUser}") private val findOneProfilePictureOfUserSubject: String,
@Value("\${app.services.profile.subjects.saveOneProfilePictureOfUser}") private val saveOneProfilePictureOfUserSubject: String
) {
fun findOne(requestId: String, userId: String): ProfileWithStatsDto {
// Request profile from the profile service
Expand Down Expand Up @@ -168,6 +177,82 @@ class ProfileService(
}
}

fun findOneCv(requestId: String, userId: String): ByteArray {
// Request a CV from the profile service
val request = RequestResponseFactory.newRequest(requestId)
.setGetUserCvRequest(
GetUserCvRequest.newBuilder()
.setUserId(userId)
)
.build()

val response = natsService.requestWithReply(findOneCvOfUserSubject, request)

// Handle the response
if (!response.hasGetUserCvResponse()) {
throw Exception("Invalid response")
}

val getUserCvResponse = response.getUserCvResponse
return getUserCvResponse.cv.toByteArray()
}

fun setOneCv(requestId: String, userId: String, cv: ByteArray) {
// Set a CV using the profile service
val request = RequestResponseFactory.newRequest(requestId)
.setSetUserCvRequest(
SetUserCvRequest.newBuilder()
.setUserId(userId)
.setCv(ByteString.copyFrom(cv))
)
.build()

val response = natsService.requestWithReply(saveOneCvOfUserSubject, request)

// Handle the response
if (!response.hasSetUserCvResponse()) {
throw Exception("Invalid response")
}
}

fun findOneProfilePicture(requestId: String, userId: String): ByteArray {
// Request a profile picture from the profile service
val request = RequestResponseFactory.newRequest(requestId)
.setGetUserProfilePictureRequest(
GetUserProfilePictureRequest.newBuilder()
.setUserId(userId)
)
.build()

val response = natsService.requestWithReply(findOneProfilePictureOfUserSubject, request)

// Handle the response
if (!response.hasGetUserProfilePictureResponse()) {
throw Exception("Invalid response")
}

val getUserProfilePictureResponse = response.getUserProfilePictureResponse
return getUserProfilePictureResponse.picture.toByteArray()
}

fun setOneProfilePicture(requestId: String, userId: String, picture: ByteArray) {
// Set a profile picture using the profile service
val request = RequestResponseFactory.newRequest(requestId)
.setSetUserProfilePictureRequest(
SetUserProfilePictureRequest.newBuilder()
.setUserId(userId)
.setPicture(ByteString.copyFrom(picture))
)
.build()

val response = natsService.requestWithReply(saveOneProfilePictureOfUserSubject, request)

// Handle the response
if (!response.hasSetUserProfilePictureResponse()) {
throw Exception("Invalid response")
}
}

private fun convertProfileFromProto(source: ProfileOuterClass.Profile): Profile {
val birthday = Date(source.birthday)

Expand Down
4 changes: 4 additions & 0 deletions backend/api_gateway/src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ app:
requestDeletionOfUser: profile.requestProfileDeletionOfUser
saveOneOfUser: profile.saveOneOfUser
updateOneOfUser: profile.updateOneOfUser
findOneCvOfUser: profile.findOneCvOfUser
saveOneCvOfUser: profile.saveOneCvOfUser
findOneProfilePictureOfUser: profile.findOneProfilePictureOfUser
saveOneProfilePictureOfUser: profile.saveOneProfilePictureOfUser
availability:
subjects:
createOneOfUser: availability.createOneOfUser
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package com.linkedout.common.utils

import com.linkedout.proto.ResponseOuterClass.Response
import org.springframework.web.ErrorResponseException

inline fun handleRequestError(block: () -> Response): Response {
return try {
block()
} catch (e: ErrorResponseException) {
e.printStackTrace()
RequestResponseFactory.newFailedResponse(e.message, e.statusCode).build()
} catch (e: Throwable) {
e.printStackTrace()
RequestResponseFactory.newFailedResponse(e.message ?: "Unknown error").build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.linkedout.common.utils
import com.linkedout.proto.RequestOuterClass
import com.linkedout.proto.ResponseOuterClass
import org.springframework.http.HttpStatus
import org.springframework.http.HttpStatusCode

class RequestResponseFactory private constructor() {
companion object {
Expand All @@ -15,7 +16,7 @@ class RequestResponseFactory private constructor() {
return ResponseOuterClass.Response.newBuilder()
}

fun newFailedResponse(message: String, errorCode: HttpStatus = HttpStatus.INTERNAL_SERVER_ERROR): ResponseOuterClass.Response.Builder {
fun newFailedResponse(message: String, errorCode: HttpStatusCode = HttpStatus.INTERNAL_SERVER_ERROR): ResponseOuterClass.Response.Builder {
return ResponseOuterClass.Response.newBuilder()
.setError(
ResponseOuterClass.Error.newBuilder()
Expand Down
1 change: 1 addition & 0 deletions backend/profile/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ dependencies {
implementation("org.postgresql:r2dbc-postgresql:1.0.2.RELEASE")
implementation("jakarta.validation:jakarta.validation-api:3.0.2")
implementation("org.jetbrains.kotlin:kotlin-reflect:1.9.20")
implementation("io.minio:minio:8.5.7")
developmentOnly("org.springframework.boot:spring-boot-devtools")
runtimeOnly("org.postgresql:postgresql")
testImplementation("org.springframework.boot:spring-boot-starter-test")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.linkedout.profile.config

import io.minio.MinioClient
import org.springframework.boot.context.properties.EnableConfigurationProperties
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration
@EnableConfigurationProperties(S3ClientConfigurationProperties::class)
class S3ClientConfiguration {
@Bean
fun s3Client(s3props: S3ClientConfigurationProperties): MinioClient {
return MinioClient.builder()
.endpoint(s3props.endpoint)
.credentials(s3props.accessKey, s3props.secretKey)
.build()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.linkedout.profile.config

import org.springframework.boot.context.properties.ConfigurationProperties
import java.net.URL

@ConfigurationProperties(prefix = "s3")
data class S3ClientConfigurationProperties(
val endpoint: URL,
val accessKey: String,
val secretKey: String,
val bucket: String,
val multipartMinPartSize: Long = 5 * 1024 * 1024
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.linkedout.profile.function.attachment

import com.google.protobuf.ByteString
import com.linkedout.common.utils.RequestResponseFactory
import com.linkedout.common.utils.handleRequestError
import com.linkedout.profile.service.AttachmentService
import com.linkedout.proto.RequestOuterClass.Request
import com.linkedout.proto.ResponseOuterClass.Response
import com.linkedout.proto.services.Profile.GetUserCvResponse
import org.springframework.stereotype.Component
import java.util.*
import java.util.function.Function

@Component
class GetCvOfUser(private val attachmentService: AttachmentService) : Function<Request, Response> {
override fun apply(t: Request): Response = handleRequestError {
// Extract the request
val request = t.getUserCvRequest
val userId = UUID.fromString(request.userId)

// Download the CV
val cv = attachmentService.findCvOfUser(userId)

return RequestResponseFactory.newSuccessfulResponse()
.setGetUserCvResponse(
GetUserCvResponse.newBuilder()
.setCv(ByteString.copyFrom(cv))
.build()
)
.build()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.linkedout.profile.function.attachment

import com.google.protobuf.ByteString
import com.linkedout.common.utils.RequestResponseFactory
import com.linkedout.common.utils.handleRequestError
import com.linkedout.profile.service.AttachmentService
import com.linkedout.proto.RequestOuterClass.Request
import com.linkedout.proto.ResponseOuterClass.Response
import com.linkedout.proto.services.Profile
import org.springframework.stereotype.Component
import java.util.*
import java.util.function.Function

@Component
class GetProfilePictureOfUser(private val attachmentService: AttachmentService) : Function<Request, Response> {
override fun apply(t: Request): Response = handleRequestError {
// Extract the request
val request = t.getUserProfilePictureRequest
val userId = UUID.fromString(request.userId)

// Download the profile picture
val profilePicture = attachmentService.findProfilePictureOfUser(userId)

return RequestResponseFactory.newSuccessfulResponse()
.setGetUserProfilePictureResponse(
Profile.GetUserProfilePictureResponse.newBuilder()
.setPicture(ByteString.copyFrom(profilePicture))
.build()
)
.build()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.linkedout.profile.function.attachment

import com.linkedout.common.utils.RequestResponseFactory
import com.linkedout.common.utils.handleRequestError
import com.linkedout.profile.service.AttachmentService
import com.linkedout.proto.RequestOuterClass.Request
import com.linkedout.proto.ResponseOuterClass.Response
import com.linkedout.proto.services.Profile
import org.springframework.stereotype.Component
import java.util.*
import java.util.function.Function

@Component
class SetCvOfUser(private val attachmentService: AttachmentService) : Function<Request, Response> {
override fun apply(t: Request): Response = handleRequestError {
// Extract the request
val request = t.setUserCvRequest
val userId = UUID.fromString(request.userId)
val cv = request.cv.toByteArray()

// Upload the CV
attachmentService.setCvOfUser(userId, cv)

return RequestResponseFactory.newSuccessfulResponse()
.setSetUserCvResponse(Profile.SetUserCvResponse.getDefaultInstance())
.build()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.linkedout.profile.function.attachment

import com.linkedout.common.utils.RequestResponseFactory
import com.linkedout.common.utils.handleRequestError
import com.linkedout.profile.service.AttachmentService
import com.linkedout.proto.RequestOuterClass.Request
import com.linkedout.proto.ResponseOuterClass.Response
import com.linkedout.proto.services.Profile
import org.springframework.stereotype.Component
import java.util.UUID
import java.util.function.Function

@Component
class SetProfilePictureOfUser(private val attachmentService: AttachmentService) : Function<Request, Response> {
override fun apply(t: Request): Response = handleRequestError {
// Extract the request
val request = t.setUserProfilePictureRequest
val userId = UUID.fromString(request.userId)
val profilePicture = request.picture.toByteArray()

// Upload the profile picture
attachmentService.setProfilePictureOfUser(userId, profilePicture)

return RequestResponseFactory.newSuccessfulResponse()
.setSetUserProfilePictureResponse(Profile.SetUserProfilePictureResponse.getDefaultInstance())
.build()
}
}
Loading