Skip to content

Commit

Permalink
Feedback resource phase2 (#47)
Browse files Browse the repository at this point in the history
* Phase 2 - Step 4.2: complete FeedbackResource

	•Implemented API endpoints for get Status, update Status, get Filtered Feedbacks

* fix to--> it's cleaner if you do:
          if (no permission) return

* Response resource (#49)

* Phase 2 - Step 4: Implement HR Response API

	•Added FeedbackResponseResource.kt to handle feedback response submissions.
	•Integrated role and permission checks to ensure authorized access for response submission.

* Phase 2 - Step 4: Implement HR Response API

	•Added FeedbackResponseResource.kt to handle feedback response submissions.
	•Integrated role and permission checks to ensure authorized access for response submission.

* fix to--> it's cleaner if you do:
          if (no permission) return

* Refactored permission validation in RolePermissionValidator to make it less dependent on specific API endpoints. Moved role and permission checks closer to where they are used in resource classes, enhancing maintainability and adhering to clean code principles. (#51)

recognize abortWith doesn't work properly
  • Loading branch information
RonAzar authored Sep 26, 2024
1 parent dc5b700 commit 728b908
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 39 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package com.hibob.academy.employee_feedback_feature.resource

import com.hibob.academy.employee_feedback_feature.dao.FeedbackRequest
import com.hibob.academy.employee_feedback_feature.dao.FeedbackSubmission
import com.hibob.academy.employee_feedback_feature.dao.*
import com.hibob.academy.employee_feedback_feature.service.FeedbackService
import jakarta.ws.rs.*
import jakarta.ws.rs.container.ContainerRequestContext
Expand All @@ -10,9 +9,7 @@ import jakarta.ws.rs.core.MediaType
import jakarta.ws.rs.core.Response
import org.springframework.stereotype.Controller
import com.hibob.academy.employee_feedback_feature.validation.RolePermissionValidator.Companion.extractClaimAsLong
import com.hibob.academy.employee_feedback_feature.validation.RolePermissionValidator.Companion.extractRole
import com.hibob.academy.employee_feedback_feature.validation.RolePermissionValidator.Companion.hasPermission
import com.hibob.academy.employee_feedback_feature.validation.RolePermissionValidator.Companion.Permissions
import com.hibob.academy.employee_feedback_feature.validation.RolePermissionValidator.Companion.validatePermissions

@Controller
@Path("/api/feedback")
Expand All @@ -21,14 +18,24 @@ class FeedbackResource(private val feedbackService: FeedbackService) {
@POST
@Consumes(MediaType.APPLICATION_JSON)
fun submitFeedback(@Context requestContext: ContainerRequestContext, newFeedback: FeedbackRequest): Response {
val companyId = extractClaimAsLong(requestContext, "companyId")!!
val companyId = validatePermissions(
requestContext, setOf(EmployeeRole.EMPLOYEE, EmployeeRole.ADMIN, EmployeeRole.HR, EmployeeRole.MANAGER),
"UNAUTHORIZED: You do not have access to submit feedbacks"
)

val employeeId = if (!newFeedback.isAnonymous) {
extractClaimAsLong(requestContext, "employeeId")!!
extractClaimAsLong(requestContext, "employeeId")
} else {
null
}

val feedbackSubmission = FeedbackSubmission(employeeId, companyId, newFeedback.feedbackText, newFeedback.isAnonymous, newFeedback.department)
val feedbackSubmission = FeedbackSubmission(
employeeId,
companyId,
newFeedback.feedbackText,
newFeedback.isAnonymous,
newFeedback.department
)

return Response.ok(feedbackService.submitFeedback(feedbackSubmission)).build()
}
Expand All @@ -37,8 +44,11 @@ class FeedbackResource(private val feedbackService: FeedbackService) {
@GET
@Produces(MediaType.APPLICATION_JSON)
fun getFeedbackHistory(@Context requestContext: ContainerRequestContext): Response {
val companyId = extractClaimAsLong(requestContext, "companyId")!!
val employeeId = extractClaimAsLong(requestContext, "employeeId")!!
val companyId = validatePermissions(
requestContext, setOf(EmployeeRole.EMPLOYEE, EmployeeRole.ADMIN, EmployeeRole.HR, EmployeeRole.MANAGER),
"UNAUTHORIZED: You do not have access to get history"
)
val employeeId = extractClaimAsLong(requestContext, "employeeId")

return Response.ok(feedbackService.getFeedbackHistory(companyId, employeeId)).build()
}
Expand All @@ -47,13 +57,51 @@ class FeedbackResource(private val feedbackService: FeedbackService) {
@GET
@Produces(MediaType.APPLICATION_JSON)
fun getAllFeedbacks(@Context requestContext: ContainerRequestContext): Response {
val companyId = extractClaimAsLong(requestContext, "companyId")!!
val employeeRole = extractRole(requestContext)!!
val companyId = validatePermissions(
requestContext, setOf(EmployeeRole.HR, EmployeeRole.ADMIN),
"UNAUTHORIZED: You do not have access to view feedbacks"
)

return if (hasPermission(employeeRole, Permissions.VIEW_ALL_FEEDBACKS)) {
Response.ok(feedbackService.getAllFeedbacks(companyId)).build()
} else {
Response.status(Response.Status.FORBIDDEN).entity("UNAUTHORIZED: You do not have access to view feedbacks").build()
}
return Response.ok(feedbackService.getAllFeedbacks(companyId)).build()
}

@Path("view/filter")
@POST
@Produces(MediaType.APPLICATION_JSON)
fun getFilteredFeedbacks(@Context requestContext: ContainerRequestContext, filter: FeedbackFilter): Response {
val companyId = validatePermissions(
requestContext, setOf(EmployeeRole.HR, EmployeeRole.ADMIN),
"UNAUTHORIZED: You do not have access to view feedbacks"
)

return Response.ok(feedbackService.getFeedbacksUsingFilter(filter, companyId)).build()
}

@Path("view/status/{feedbackId}")
@GET
@Produces(MediaType.APPLICATION_JSON)
fun getStatus(
@Context requestContext: ContainerRequestContext,
@PathParam("feedbackId") feedbackId: Long
): Response {
val companyId = validatePermissions(
requestContext, setOf(EmployeeRole.HR, EmployeeRole.ADMIN),
"UNAUTHORIZED: You do not have access to view feedback status!"
)
val searchedFeedback = SearchedFeedback(companyId, feedbackId)

return Response.ok("feedback status: ${feedbackService.getFeedbackStatus(searchedFeedback)}").build()
}

@Path("update/status")
@PUT
@Produces(MediaType.APPLICATION_JSON)
fun updateStatus(@Context requestContext: ContainerRequestContext, updateFeedback: UpdateFeedbackStatus): Response {
val companyId = validatePermissions(
requestContext, setOf(EmployeeRole.HR),
"UNAUTHORIZED: You do not have access to change feedback status!"
)

return Response.ok(feedbackService.updateFeedbackStatus(updateFeedback, companyId)).build()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.hibob.academy.employee_feedback_feature.resource

import com.hibob.academy.employee_feedback_feature.dao.EmployeeRole
import com.hibob.academy.employee_feedback_feature.dao.ResponseSubmission
import com.hibob.academy.employee_feedback_feature.service.ResponseService
import com.hibob.academy.employee_feedback_feature.validation.RolePermissionValidator.Companion.extractClaimAsLong
import com.hibob.academy.employee_feedback_feature.validation.RolePermissionValidator.Companion.validatePermissions
import jakarta.ws.rs.Consumes
import jakarta.ws.rs.POST
import jakarta.ws.rs.Path
import jakarta.ws.rs.container.ContainerRequestContext
import jakarta.ws.rs.core.Context
import jakarta.ws.rs.core.MediaType
import jakarta.ws.rs.core.Response
import org.springframework.stereotype.Controller

@Controller
@Path("/api/response")
class ResponseResource(private val responseService: ResponseService) {
@Path("/submit")
@POST
@Consumes(MediaType.APPLICATION_JSON)
fun submitResponse(@Context requestContext: ContainerRequestContext, newFeedbackResponse: ResponseSubmission): Response {
val companyId = validatePermissions(
requestContext, setOf(EmployeeRole.HR),
"UNAUTHORIZED: You do not have permission to respond to feedbacks"
)
val employeeId = extractClaimAsLong(requestContext, "employeeId")

val responseId = responseService.submitResponse(newFeedbackResponse, employeeId, companyId)

return Response.ok("Response submitted successfully, response id: $responseId").build()
}
}
Original file line number Diff line number Diff line change
@@ -1,47 +1,49 @@
package com.hibob.academy.employee_feedback_feature.validation

import com.hibob.academy.employee_feedback_feature.dao.EmployeeRole
import jakarta.ws.rs.BadRequestException
import jakarta.ws.rs.container.ContainerRequestContext
import jakarta.ws.rs.core.Response

class RolePermissionValidator {
companion object {
fun extractClaimAsLong(requestContext: ContainerRequestContext, claim: String): Long? {
fun extractClaimAsLong(requestContext: ContainerRequestContext, claim: String): Long {
return (requestContext.getProperty(claim) as? Number)?.toLong() ?: run {
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).entity("UNAUTHORIZED: Missing or invalid $claim").build())
null

throw BadRequestException("UNAUTHORIZED: Missing or invalid $claim")
}
}

fun extractRole(requestContext: ContainerRequestContext): EmployeeRole? {
private fun extractRole(requestContext: ContainerRequestContext): EmployeeRole {
return (requestContext.getProperty("role") as? String)?.uppercase()?.let {
try {
EmployeeRole.valueOf(it)
} catch (e: IllegalArgumentException) {
requestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).entity("UNAUTHORIZED: Invalid role").build())
null

throw BadRequestException("UNAUTHORIZED: Invalid role")
}
} ?: run {
requestContext.abortWith(
Response.status(Response.Status.UNAUTHORIZED).entity("UNAUTHORIZED: Missing role").build()
)
null

throw BadRequestException("UNAUTHORIZED: Missing role")
}
}
enum class Permissions {
VIEW_ALL_FEEDBACKS,
CHANGE_FEEDBACK_STATUS,
RESPONSE_TO_FEEDBACK
}

fun validatePermissions(
requestContext: ContainerRequestContext,
requiredRoles: Set<EmployeeRole>,
errorMessage: String
): Long {
val companyId = extractClaimAsLong(requestContext, "companyId")
val role = extractRole(requestContext)

private val rolePermissions = mapOf(
EmployeeRole.HR to setOf(Permissions.VIEW_ALL_FEEDBACKS, Permissions.CHANGE_FEEDBACK_STATUS, Permissions.RESPONSE_TO_FEEDBACK),
EmployeeRole.ADMIN to setOf(Permissions.VIEW_ALL_FEEDBACKS),
)
if (!hasPermission(role, requiredRoles)) {
throw BadRequestException(errorMessage)
}

return companyId
}

fun hasPermission(role: EmployeeRole, permission: Permissions): Boolean {
return rolePermissions[role]?.contains(permission) ?: false
private fun hasPermission(role: EmployeeRole, requiredRoles: Set<EmployeeRole>): Boolean {
return requiredRoles.contains(role)
}
}
}

0 comments on commit 728b908

Please sign in to comment.