1212import lombok .extern .slf4j .Slf4j ;
1313import org .springframework .beans .factory .annotation .Value ;
1414import org .springframework .http .ResponseEntity ;
15- import org .springframework .web .client .RestTemplate ;
16- import org .springframework .http .HttpHeaders ;
17- import com .fasterxml .jackson .core .type .TypeReference ;
18- import com .fasterxml .jackson .databind .JsonNode ;
19- import com .fasterxml .jackson .databind .ObjectMapper ;
20- import org .springframework .http .HttpEntity ;
21- import org .springframework .http .HttpMethod ;
2215import jakarta .servlet .http .HttpServletRequest ;
2316import org .springframework .web .bind .annotation .*;
2417
3124public class CourseController {
3225
3326 private final CourseService courseService ;
34- private final RestTemplate restTemplate = new RestTemplate ();
3527
36- @ Value ("${user.service.uri:http://user-service:8082}" )
37- private String userServiceUri ;
28+ @ Value ("${user.service.uri:http://user-service:8082}" )
29+ private String userServiceUri ;
3830
3931 @ PostMapping
4032 public ResponseEntity <CourseResponse > createCourse (@ RequestBody CourseRequest request ) {
@@ -65,9 +57,9 @@ public ResponseEntity<List<CourseSummaryResponse>> getPublicCourses() {
6557 }
6658
6759 @ GetMapping ("/public/published" )
68- public ResponseEntity <List <CourseResponse >> getPublicPublishedCourses () {
60+ public ResponseEntity <List <CourseSummaryResponse >> getPublishedCourses () {
6961 log .info ("Fetching public and published courses for landing page" );
70- List <CourseResponse > responses = courseService .getPublicPublishedCourses ();
62+ List <CourseSummaryResponse > responses = courseService .getPublishedCourses ();
7163 return ResponseEntity .ok (responses );
7264 }
7365
@@ -129,18 +121,23 @@ public ResponseEntity<Void> unbookmarkCourse(@PathVariable String courseId, @Pat
129121
130122 @ GetMapping ("/search" )
131123 public ResponseEntity <List <CourseResponse >> searchCourses (
132- @ RequestParam (required = false ) String instructor ,
133- @ RequestParam (required = false ) Level level ,
134- @ RequestParam (required = false ) Language language ,
135- @ RequestParam (required = false ) String skill ,
136- @ RequestParam (required = false ) String category ,
137- @ RequestParam (required = false ) String title
124+ @ RequestParam (required = false ) String instructor ,
125+ @ RequestParam (required = false ) Level level ,
126+ @ RequestParam (required = false ) Language language ,
127+ @ RequestParam (required = false ) String skill ,
128+ @ RequestParam (required = false ) String category ,
129+ @ RequestParam (required = false ) String title ,
130+ @ RequestParam (required = false ) boolean isPublished ,
131+ @ RequestParam (required = false ) boolean isPublic
138132 ) {
139- log .info ("Advanced search: instructor={}, level={}, language={}, skill={}, category={}, title={}" , instructor , level , language , skill , category , title );
140- List <CourseResponse > responses = courseService .advancedSearch (instructor , level , language , skill , category , title );
141- return ResponseEntity .ok (responses );
142- }
143-
133+ log .info ("Advanced search: instructor={}, level={}, language={}, skill={}, category={}, title={}, isPublished={}, isPublic={}" ,
134+ instructor , level , language , skill , category , title , isPublished , isPublic );
135+ List <CourseResponse > responses = courseService .advancedSearch (instructor , level , language , skill , category , title , isPublished , isPublic );
136+ return ResponseEntity .ok (responses );
137+ }
138+
139+
140+
144141 @ GetMapping ("/search/instructor/{instructor}" )
145142 public ResponseEntity <List <CourseResponse >> getCoursesByInstructor (@ PathVariable String instructor ) {
146143 log .info ("Fetching courses by instructor: {}" , instructor );
@@ -184,54 +181,32 @@ public ResponseEntity<List<CourseResponse>> searchCoursesByTitle(@PathVariable S
184181 }
185182
186183 /**
187- * Generates a brand-new course via GenAI + RAG, then persists & returns it.
188- * Chosen as POST because we are **creating** a new server-side resource
189- * (the course) – even though the body only contains “input” data.
190- */
191- /*
192- * New variant that accepts userId in path.
193- * Example: POST /api/v1/courses/generate/learning_path/{userId}
194- */
184+ * Generates a brand-new course via GenAI + RAG, then persists & returns it.
185+ * Chosen as POST because we are **creating** a new server-side resource
186+ */
195187 @ PostMapping ("/generate/learning_path/{userId}" )
196- public ResponseEntity <CourseResponse > generateCourseForUser (@ PathVariable String userId ,
197- @ RequestBody LearningPathRequest req ,
198- HttpServletRequest servletRequest ) {
199-
200-
201- // Fetch user profile from user-service just for logging/demo
202- try {
203- String profileUrl = userServiceUri + "/api/v1/users/" + userId + "/profile" ;
204- String authHeader = servletRequest .getHeader ("Authorization" );
205- HttpHeaders headers = new HttpHeaders ();
206- if (authHeader != null && !authHeader .isBlank ()) {
207- headers .set ("Authorization" , authHeader );
208- }
209- HttpEntity <Void > entity = new HttpEntity <>(headers );
210- String profileJson = restTemplate .exchange (profileUrl , HttpMethod .GET , entity , String .class ).getBody ();
211- log .info ("User profile fetched via user-service: {}" , profileJson );
212- // extract skills field from JSON
213- List <String > extractedSkills = null ;
214- try {
215- ObjectMapper mapper = new ObjectMapper ();
216- JsonNode root = mapper .readTree (profileJson );
217- JsonNode skillsNode = root .get ("skills" );
218- if (skillsNode != null && skillsNode .isArray ()) {
219- extractedSkills = mapper .convertValue (skillsNode , new TypeReference <List <String >>() {});
220- }
221- } catch (Exception parseEx ) {
222- log .warn ("Could not parse skills from user profile: {}" , parseEx .getMessage ());
223- }
224- log .info ("Generating course for user={} prompt='{}' skills={}" , userId , req .prompt (), extractedSkills );
225- req = new LearningPathRequest (req .prompt (), extractedSkills );
226-
227- } catch (Exception ex ) {
228- log .warn ("Failed to fetch user profile for {}: {}" , userId , ex .getMessage ());
229- }
230-
231- // create the course after we logged the profile
232- CourseResponse generated = courseService .generateFromGenAi (req );
233- CourseResponse enrolled = courseService .enrollUserInCourse (generated .getId (), userId );
234- return ResponseEntity .ok (enrolled );
188+ public ResponseEntity <CourseRequest > generateCourseForUser (@ PathVariable String userId , @ RequestBody LearningPathRequest req , HttpServletRequest servletRequest ) {
189+ log .info ("Generating course for user: {}" , userId );
190+ CourseRequest generated = courseService .generateCourseFromGenAi (req , userId , servletRequest .getHeader ("Authorization" ));
191+ return ResponseEntity .ok (generated );
235192 }
236193
194+
195+ /**
196+ * Confirms the generation of a course from a Learning Path request.
197+ * This method is called after the course has been generated and the user has reviewed it.
198+ * It retrieves the last generated course details, creates the course in the database, enrolls the user, and returns the course response.
199+ *
200+ * @param userId The ID of the user confirming the course generation.
201+ * @return CourseResponse containing the confirmed course details.
202+ */
203+ @ PostMapping ("/generate/learning_path/{userId}/confirm" )
204+ public ResponseEntity <CourseResponse > confirmGeneratedCourse (@ PathVariable String userId ) {
205+ log .info ("Confirming course generation for user: {}" , userId );
206+ CourseResponse confirmed = courseService .confirmCourseGeneration (userId );
207+ return ResponseEntity .ok (confirmed );
208+ }
209+
210+
211+
237212}
0 commit comments