Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
2c2955f
feat: add Role enum to define user roles in the system
abdulazizacc Jun 2, 2026
d22da41
feat(product-rating): implement product rating system
mohamedshemees Jun 9, 2026
2e2ce9b
fix(product-rating): restrict review eligibility to only COMPLETED or…
mohamedshemees Jun 9, 2026
dea81ea
feat: implement custom domain exceptions for product ratings
mohamedshemees Jun 13, 2026
03cb232
Merge pull request #148 from Sellio-Squad/product-review
mohamedshemees Jun 17, 2026
60d5e18
feat: add roles to User model for role-based access control
abdulazizacc Jun 18, 2026
bd545a9
feat: add activeRole field to RefreshToken entity for role management
abdulazizacc Jun 18, 2026
d451075
feat: add MissingActiveRoleException for handling missing active role…
abdulazizacc Jun 18, 2026
88c300d
refactor: enhance JWT authentication to include active role validation
abdulazizacc Jun 18, 2026
98f04ab
refactor: integrate active role management in authentication and regi…
abdulazizacc Jun 18, 2026
789c9de
refactor: remove email validation from CreateUserRequest
abdulazizacc Jun 18, 2026
b16a075
feat: add role validation to LoginRequest, RefreshTokenRequest, and V…
abdulazizacc Jun 21, 2026
5cf7a62
refactor: update authentication requests to include dynamic role hand…
abdulazizacc Jun 21, 2026
b836665
refactor: add role request parameter for refresh-token endpoint
abdulazizacc Jun 21, 2026
c1b154d
Merge pull request #149 from Sellio-Squad/feature/rules-for-authentic…
abdulazizacc Jun 25, 2026
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 @@ -8,6 +8,7 @@ import org.shangahi.sellio_backend.api.dto.response.AuthResponse
import org.shangahi.sellio_backend.api.dto.response.MessageResponse
import org.shangahi.sellio_backend.api.dto.response.OtpResponse
import org.shangahi.sellio_backend.api.swagger.doc.AccountDoc
import org.shangahi.sellio_backend.model.Role
import org.shangahi.sellio_backend.service.AccountService
import org.shangahi.sellio_backend.service.AuthenticationService
import org.shangahi.sellio_backend.service.RegisterService
Expand All @@ -31,7 +32,7 @@ class AccountController(
fun login(
@RequestBody request: LoginRequest
): AuthResponse {
return authenticationService.login(request.phoneNumber, request.password)
return authenticationService.login(request.phoneNumber, request.password, request.role)
}

@PostMapping("/create")
Expand All @@ -44,15 +45,15 @@ class AccountController(
@PostMapping("/create/verify-otp")
@AccountDoc.VerifyOtp
fun verifyOtp(@RequestBody request: VerifyOtpRequest): AuthResponse {
return registerService.verifyOtpAndCreateUser(request.sessionId, request.otp)
return registerService.verifyOtpAndCreateUser(request.sessionId, request.otp, request.role)
}

@PostMapping("/refresh-token")
@AccountDoc.RefreshToken
fun refreshToken(
@RequestBody request: RefreshTokenRequest
): AuthResponse {
return authenticationService.refreshToken(request.refreshToken)
return authenticationService.refreshToken(request.refreshToken, request.role)
}

@PostMapping("/reset-password")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package org.shangahi.sellio_backend.api.controller
import jakarta.validation.Valid
import org.shangahi.sellio_backend.api.dto.request.RequestOtpRequest
import org.shangahi.sellio_backend.api.dto.request.ResetPasswordRequest
import org.shangahi.sellio_backend.api.dto.request.VerifyOtpRequest
import org.shangahi.sellio_backend.api.dto.request.VerifyForgotPasswordOtpRequest
import org.shangahi.sellio_backend.api.dto.response.OtpResponse
import org.shangahi.sellio_backend.api.swagger.doc.ForgotPasswordDoc
import org.shangahi.sellio_backend.service.ForgotPasswordService
Expand Down Expand Up @@ -31,7 +31,7 @@ class ForgotPasswordController(

@PostMapping("/verify")
@ForgotPasswordDoc.VerifyOtp
fun verifyOtp(@RequestBody request: VerifyOtpRequest) {
fun verifyOtp(@RequestBody request: VerifyForgotPasswordOtpRequest) {
otpFlowService.verifyOtpForSession(request.sessionId, request.otp)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package org.shangahi.sellio_backend.api.controller

import io.swagger.v3.oas.annotations.tags.Tag
import jakarta.validation.Valid
import org.shangahi.sellio_backend.api.dto.request.ProductRatingRequest
import org.shangahi.sellio_backend.api.dto.response.MessageResponse
import org.shangahi.sellio_backend.api.dto.response.PageResponse
import org.shangahi.sellio_backend.api.dto.response.ProductRatingResponse
import org.shangahi.sellio_backend.api.dto.response.ProductRatingSummaryResponse
import org.shangahi.sellio_backend.api.swagger.doc.ProductRatingDoc
import org.shangahi.sellio_backend.service.ProductRatingService
import org.springframework.data.domain.Pageable
import org.springframework.web.bind.annotation.*
import java.util.*

@RestController
@RequestMapping("/v1/product-rating")
@Tag(name = "Product rating", description = "Endpoints for managing product ratings")
class ProductRatingController(
private val productRatingService: ProductRatingService
) {

@ProductRatingDoc.AddRating
@PostMapping("/{productId}")
fun addRating(
@PathVariable productId: UUID,
@Valid @RequestBody request: ProductRatingRequest
): MessageResponse {
return productRatingService.addRating(productId, request)
}

@ProductRatingDoc.GetProductRatings
@GetMapping("/{productId}")
fun getProductRatings(
@PathVariable productId: UUID,
pageable: Pageable
): PageResponse<ProductRatingResponse> {
return productRatingService.getProductRatings(productId, pageable)
}

@ProductRatingDoc.GetProductRatingSummary
@GetMapping("/{productId}/summary")
fun getProductRatingSummary(
@PathVariable productId: UUID
): ProductRatingSummaryResponse {
return productRatingService.getProductRatingSummary(productId)
}

@ProductRatingDoc.DeleteRating
@DeleteMapping("/{ratingId}")
fun deleteRating(
@PathVariable ratingId: UUID
) {
productRatingService.deleteRating(ratingId)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package org.shangahi.sellio_backend.api.dto.request

import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.Size
import java.util.UUID

data class CreateUserRequest(
@field:NotBlank(message = "Full name is required")
Expand All @@ -24,6 +23,5 @@ data class CreateUserRequest(
@field:NotBlank(message = "Region is required")
val region: String,
val avatarUrl: String?,
@field:NotBlank(message = "Email is required")
val email: String?
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ package org.shangahi.sellio_backend.api.dto.request

import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.Size
import org.shangahi.sellio_backend.model.Role

data class LoginRequest(
@field:NotBlank(message = "phoneNumber can not be blank")
val phoneNumber: String,
@field:Size(min = 8, message = "password must be at least 8 characters long")
val password: String
val password: String,
@field:NotBlank(message = "Role can not be null")
val role: Role
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.shangahi.sellio_backend.api.dto.request

import jakarta.validation.constraints.Max
import jakarta.validation.constraints.Min
import jakarta.validation.constraints.NotBlank
import jakarta.validation.constraints.Size

data class ProductRatingRequest(
@field:Min(1)
@field:Max(5)
val ratingValue: Int,

@field:NotBlank
@field:Size(max = 500)
val comment: String
)
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package org.shangahi.sellio_backend.api.dto.request

import jakarta.validation.constraints.NotBlank
import org.shangahi.sellio_backend.model.Role

data class RefreshTokenRequest(
@field:NotBlank(message = "refreshToken can not be blank")
val refreshToken: String
val refreshToken: String,
@field:NotBlank(message = "Role can not be null")
val role: Role
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.shangahi.sellio_backend.api.dto.request

import jakarta.validation.constraints.NotBlank
import org.hibernate.validator.constraints.Length
import org.hibernate.validator.constraints.UUID

data class VerifyForgotPasswordOtpRequest(
@field:NotBlank(message = "OTP value must not be blank")
@field:Length(min = 4, max = 4, message = "OTP must be 4 digits")
val otp: String,

@field:UUID(message = "must be in a valid UUID format for sessionId")
val sessionId: String,
)
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@ package org.shangahi.sellio_backend.api.dto.request
import jakarta.validation.constraints.NotBlank
import org.hibernate.validator.constraints.Length
import org.hibernate.validator.constraints.UUID
import org.shangahi.sellio_backend.model.Role

data class VerifyOtpRequest(
@field:NotBlank(message = "OTP value must not be blank")
@field:Length(min = 4, max = 4, message = "OTP must be 4 digits")
val otp: String,

@field:UUID(message = "must be in a valid UUID format for sessionId")
val sessionId: String
val sessionId: String,

@field:NotBlank(message = "Role can not be null")
val role: Role
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.shangahi.sellio_backend.api.dto.response

import java.time.Instant
import java.util.*

data class ProductRatingResponse(
val id: UUID,
val userName: String,
val userAvatarUrl: String?,
val ratingValue: Int,
val comment: String,
val createdAt: Instant,
val deletable: Boolean,
val editable: Boolean
)

data class ProductRatingSummaryResponse(
val productId: UUID,
val averageRating: Double,
val totalRatings: Long,
val ratingCategorize: Map<Int, Long>,
val canReview: Boolean,
val userReview: ProductRatingResponse?,
val recentReviews: List<ProductRatingResponse>
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.shangahi.sellio_backend.api.mapper

import org.shangahi.sellio_backend.api.dto.response.ProductRatingResponse
import org.shangahi.sellio_backend.entity.ProductRating
import java.time.Instant

fun ProductRating.toResponse(isOwnRating: Boolean = false): ProductRatingResponse {
return ProductRatingResponse(
id = id!!,
userName = user.fullName,
userAvatarUrl = user.avatarUrl,
ratingValue = ratingValue,
comment = comment,
createdAt = createdAt ?: Instant.now(),
deletable = isOwnRating,
editable = isOwnRating
)
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package org.shangahi.sellio_backend.api.swagger

import io.swagger.v3.oas.models.Components
import io.swagger.v3.oas.models.OpenAPI
import io.swagger.v3.oas.models.info.Info
import io.swagger.v3.oas.models.info.License
import io.swagger.v3.oas.models.security.SecurityRequirement
import io.swagger.v3.oas.models.security.SecurityScheme
import io.swagger.v3.oas.models.servers.Server
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
Expand All @@ -12,7 +15,20 @@ class OpenApiConfig {

@Bean
fun sellioOpenAPI(): OpenAPI {
val securitySchemeName = "bearerAuth"
return OpenAPI()
.addSecurityItem(SecurityRequirement().addList(securitySchemeName))
.components(
Components()
.addSecuritySchemes(
securitySchemeName,
SecurityScheme()
.name(securitySchemeName)
.type(SecurityScheme.Type.HTTP)
.scheme("bearer")
.bearerFormat("JWT")
)
)
.info(
Info()
.title("Sellio Backend API")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ annotation class AccountDoc {
value = """
{
"phoneNumber": "+96407712345678",
"password": "12345678"
"password": "12345678",
"role":"CUSTOMER"
}
"""
)
Expand Down Expand Up @@ -219,7 +220,8 @@ annotation class AccountDoc {
value = """
{
"otp": "9999",
"sessionId": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
"sessionId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"role":"CUSTOMER"
}
"""
)
Expand Down Expand Up @@ -320,7 +322,8 @@ annotation class AccountDoc {
name = "RefreshTokenRequestExample",
value = """
{
"refreshToken": "refresh_token_here"
"refreshToken": "refresh_token_here",
"role":"CUSTOMER"
}
"""
)
Expand Down
Loading
Loading