Skip to content
This repository was archived by the owner on Nov 23, 2025. It is now read-only.
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
7 changes: 3 additions & 4 deletions .github/workflows/buildtest.yaml
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
name: Build and Test Time Logging Service

on:
push:
branches:
- '**'
pull_request:
branches:
- '**'
- main
- dev
- devOps

jobs:
build-test:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.techtorque.time_logging_service.dto.request.TimeLogUpdateRequest;
import com.techtorque.time_logging_service.dto.response.TimeLogResponse;
import com.techtorque.time_logging_service.dto.response.TimeLogSummaryResponse;
import com.techtorque.time_logging_service.exception.UnauthorizedAccessException;
import com.techtorque.time_logging_service.service.TimeLogService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
Expand Down Expand Up @@ -63,12 +64,12 @@ public TimeLogController(TimeLogService timeLogService) {
@ApiResponse(responseCode = "403", description = "Forbidden - insufficient permissions")
})
@PostMapping
@PreAuthorize("hasRole('EMPLOYEE')")
@PreAuthorize("hasAnyRole('EMPLOYEE', 'ADMIN', 'SUPER_ADMIN')")
public ResponseEntity<TimeLogResponse> createTimeLog(
@Parameter(description = "Employee ID from authentication token", required = true)
@RequestHeader(value = "X-User-Subject") String employeeId,
@Valid @RequestBody TimeLogRequest request) {

TimeLogResponse response = timeLogService.createTimeLog(employeeId, request);
return ResponseEntity.status(HttpStatus.CREATED).body(response);
}
Expand All @@ -88,7 +89,7 @@ public ResponseEntity<TimeLogResponse> createTimeLog(
@ApiResponse(responseCode = "403", description = "Forbidden")
})
@GetMapping
@PreAuthorize("hasAnyRole('EMPLOYEE', 'ADMIN')")
@PreAuthorize("hasAnyRole('EMPLOYEE', 'ADMIN', 'SUPER_ADMIN')")
public ResponseEntity<List<TimeLogResponse>> getMyTimeLogs(
@Parameter(description = "Employee ID from authentication token", required = true)
@RequestHeader("X-User-Subject") String userId,
Expand All @@ -97,11 +98,11 @@ public ResponseEntity<List<TimeLogResponse>> getMyTimeLogs(
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate from,
@Parameter(description = "End date for filtering (YYYY-MM-DD)")
@RequestParam(required = false) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate to) {

List<TimeLogResponse> responses;
// Admin can see all time logs
if (roles.contains("ADMIN")) {

// Admin and Super Admin can see all time logs
if (roles.contains("ADMIN") || roles.contains("SUPER_ADMIN")) {
if (from != null && to != null) {
// For admin with date range, get all logs and filter (or create new method)
responses = timeLogService.getAllTimeLogs().stream()
Expand All @@ -118,7 +119,7 @@ public ResponseEntity<List<TimeLogResponse>> getMyTimeLogs(
responses = timeLogService.getAllTimeLogsByEmployee(userId);
}
}

return ResponseEntity.ok(responses);
}

Expand All @@ -138,15 +139,15 @@ public ResponseEntity<List<TimeLogResponse>> getMyTimeLogs(
@ApiResponse(responseCode = "404", description = "Time log not found")
})
@GetMapping("/{logId}")
@PreAuthorize("hasAnyRole('EMPLOYEE', 'ADMIN')")
@PreAuthorize("hasAnyRole('EMPLOYEE', 'ADMIN', 'SUPER_ADMIN')")
public ResponseEntity<TimeLogResponse> getTimeLogById(
@Parameter(description = "Time log ID", required = true)
@PathVariable String logId,
@Parameter(description = "User ID from authentication token")
@RequestHeader(value = "X-User-Subject", required = false) String userId,
@Parameter(description = "User role from authentication token")
@RequestHeader(value = "X-User-Role", required = false) String userRole) {

TimeLogResponse response = timeLogService.getTimeLogByIdWithAuthorization(logId, userId, userRole);
return ResponseEntity.ok(response);
}
Expand All @@ -167,14 +168,14 @@ public ResponseEntity<TimeLogResponse> getTimeLogById(
@ApiResponse(responseCode = "404", description = "Time log not found")
})
@PutMapping("/{logId}")
@PreAuthorize("hasRole('EMPLOYEE')")
@PreAuthorize("hasAnyRole('EMPLOYEE', 'ADMIN', 'SUPER_ADMIN')")
public ResponseEntity<TimeLogResponse> updateTimeLog(
@Parameter(description = "Time log ID", required = true)
@PathVariable String logId,
@Parameter(description = "Employee ID from authentication token", required = true)
@RequestHeader("X-User-Subject") String employeeId,
@Valid @RequestBody TimeLogUpdateRequest request) {

TimeLogResponse response = timeLogService.updateTimeLogWithAuthorization(logId, employeeId, request);
return ResponseEntity.ok(response);
}
Expand All @@ -194,13 +195,20 @@ public ResponseEntity<TimeLogResponse> updateTimeLog(
@ApiResponse(responseCode = "404", description = "Time log not found")
})
@DeleteMapping("/{logId}")
@PreAuthorize("hasRole('EMPLOYEE')")
@PreAuthorize("hasAnyRole('EMPLOYEE', 'ADMIN', 'SUPER_ADMIN')")
public ResponseEntity<Void> deleteTimeLog(
@Parameter(description = "Time log ID", required = true)
@PathVariable String logId,
@Parameter(description = "Employee ID from authentication token", required = true)
@RequestHeader("X-User-Subject") String employeeId) {

@Parameter(description = "Employee ID from authentication token", required = false)
@RequestHeader(value = "X-User-Subject", required = false) String employeeId,
@Parameter(description = "User role from authentication token", required = false)
@RequestHeader(value = "X-User-Roles", required = false) String userRoles) {

// If header is missing, check if user is admin (can delete any log)
if (employeeId == null || employeeId.isEmpty()) {
throw new UnauthorizedAccessException("User identification missing. Please ensure you are logged in.");
}

timeLogService.deleteTimeLogWithAuthorization(logId, employeeId);
return ResponseEntity.noContent().build();
}
Expand Down Expand Up @@ -246,15 +254,15 @@ public ResponseEntity<List<TimeLogResponse>> getTimeLogsForService(
@ApiResponse(responseCode = "403", description = "Forbidden")
})
@GetMapping("/summary")
@PreAuthorize("hasRole('EMPLOYEE')")
@PreAuthorize("hasAnyRole('EMPLOYEE', 'ADMIN', 'SUPER_ADMIN')")
public ResponseEntity<TimeLogSummaryResponse> getSummary(
@Parameter(description = "Employee ID from authentication token", required = true)
@RequestHeader("X-User-Subject") String employeeId,
@Parameter(description = "Period type: 'daily' or 'weekly'", required = true)
@RequestParam String period,
@Parameter(description = "Reference date (YYYY-MM-DD)", required = true)
@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date) {

TimeLogSummaryResponse summary = timeLogService.getEmployeeSummaryByPeriod(employeeId, period, date);
return ResponseEntity.ok(summary);
}
Expand Down Expand Up @@ -288,11 +296,11 @@ public ResponseEntity<List<TimeLogResponse>> getTimeLogsForProject(
description = "Get quick statistics including total hours logged, number of logs, and breakdown by service/project."
)
@GetMapping("/stats")
@PreAuthorize("hasRole('EMPLOYEE')")
@PreAuthorize("hasAnyRole('EMPLOYEE', 'ADMIN', 'SUPER_ADMIN')")
public ResponseEntity<Map<String, Object>> getEmployeeStats(
@Parameter(description = "Employee ID from authentication token", required = true)
@RequestHeader("X-User-Subject") String employeeId) {

Map<String, Object> stats = timeLogService.getEmployeeStatistics(employeeId);
return ResponseEntity.ok(stats);
}
Expand Down