• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

classconnect-grupo3 / courses-service / 15687239309

16 Jun 2025 05:07PM UTC coverage: 88.681% (-0.1%) from 88.829%
15687239309

push

github

rovifran
fixed tests after refactor of delete urls

3173 of 3578 relevant lines covered (88.68%)

1.0 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.99
/src/controller/courses_controller.go
1
package controller
2

3
import (
4
        "log/slog"
5
        "net/http"
6
        "slices"
7

8
        "courses-service/src/ai"
9
        "courses-service/src/model"
10
        "courses-service/src/schemas"
11
        "courses-service/src/service"
12

13
        "github.com/gin-gonic/gin"
14
)
15

16
type CourseController struct {
17
        service  service.CourseServiceInterface
18
        aiClient *ai.AiClient
19
}
20

21
func NewCourseController(service service.CourseServiceInterface, aiClient *ai.AiClient) *CourseController {
1✔
22
        return &CourseController{service: service, aiClient: aiClient}
1✔
23
}
1✔
24

25
// @Summary Get all courses
26
// @Description Get all courses available in the database
27
// @Tags courses
28
// @Accept json
29
// @Produce json
30
// @Success 200 {array} model.Course
31
// @Router /courses [get]
32
func (c *CourseController) GetCourses(ctx *gin.Context) {
1✔
33
        slog.Debug("Getting courses")
1✔
34

1✔
35
        courses, err := c.service.GetCourses()
1✔
36
        if err != nil {
2✔
37
                slog.Error("Error getting courses", "error", err)
1✔
38
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
1✔
39
                return
1✔
40
        }
1✔
41
        slog.Debug("Courses retrieved", "courses", courses)
1✔
42
        ctx.JSON(http.StatusOK, courses)
1✔
43
}
44

45
// @Summary Course creation
46
// @Description Create a new course
47
// @Tags courses
48
// @Accept json
49
// @Produce json
50
// @Param course body schemas.CreateCourseRequest true "Course to create"
51
// @Success 201 {object} model.Course
52
// @Router /courses [post]
53
func (c *CourseController) CreateCourse(ctx *gin.Context) {
1✔
54
        slog.Debug("Creating course")
1✔
55

1✔
56
        var course schemas.CreateCourseRequest
1✔
57
        if err := ctx.ShouldBindJSON(&course); err != nil {
2✔
58
                slog.Error("Error binding JSON", "error", err)
1✔
59
                ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
1✔
60
                return
1✔
61
        }
1✔
62

63
        createdCourse, err := c.service.CreateCourse(course)
1✔
64
        if err != nil {
2✔
65
                slog.Error("Error creating course", "error", err)
1✔
66
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
1✔
67
                return
1✔
68
        }
1✔
69
        slog.Debug("Course created", "course", createdCourse)
1✔
70
        ctx.JSON(http.StatusCreated, createdCourse)
1✔
71
}
72

73
// @Summary Get a course by ID
74
// @Description Get a course by ID
75
// @Tags courses
76
// @Accept json
77
// @Produce json
78
// @Param id path string true "Course ID"
79
// @Success 200 {object} model.Course
80
// @Router /courses/{id} [get]
81
func (c *CourseController) GetCourseById(ctx *gin.Context) {
1✔
82
        slog.Debug("Getting course by ID")
1✔
83

1✔
84
        id := ctx.Param("id")
1✔
85
        course, err := c.service.GetCourseById(id)
1✔
86
        if err != nil {
2✔
87
                slog.Error("Error getting course by ID", "error", err)
1✔
88
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
1✔
89
                return
1✔
90
        }
1✔
91
        slog.Debug("Course retrieved", "course", course)
1✔
92
        ctx.JSON(http.StatusOK, course)
1✔
93
}
94

95
// @Summary Delete a course
96
// @Description Delete a course by ID
97
// @Tags courses
98
// @Accept json
99
// @Produce json
100
// @Param id path string true "Course ID"
101
// @Param teacherId query string true "Teacher ID"
102
// @Success 200 {object} schemas.DeleteCourseResponse
103
// @Router /courses/{id} [delete]
104
func (c *CourseController) DeleteCourse(ctx *gin.Context) {
1✔
105
        slog.Debug("Deleting course")
1✔
106
        id := ctx.Param("id")
1✔
107
        if id == "" {
1✔
108
                slog.Error("Course ID is required")
×
109
                ctx.JSON(http.StatusBadRequest, gin.H{"error": "Course ID is required"})
×
110
                return
×
111
        }
×
112

113
        teacherId := ctx.Query("teacherId")
1✔
114
        if teacherId == "" {
2✔
115
                slog.Error("Teacher ID is required")
1✔
116
                ctx.JSON(http.StatusBadRequest, gin.H{"error": "Teacher ID is required"})
1✔
117
                return
1✔
118
        }
1✔
119

120
        err := c.service.DeleteCourse(id, teacherId)
1✔
121
        if err != nil {
2✔
122
                slog.Error("Error deleting course", "error", err)
1✔
123
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
1✔
124
                return
1✔
125
        }
1✔
126
        slog.Debug("Course deleted", "id", id)
1✔
127
        ctx.JSON(http.StatusOK, gin.H{"message": "Course deleted successfully"})
1✔
128
}
129

130
// @Summary Get a course by teacher ID
131
// @Description Get a course by teacher ID
132
// @Tags courses
133
// @Accept json
134
// @Produce json
135
// @Param teacherId path string true "Teacher ID"
136
// @Success 200 {array} model.Course
137
// @Router /courses/teacher/{teacherId} [get]
138
func (c *CourseController) GetCourseByTeacherId(ctx *gin.Context) {
1✔
139
        slog.Debug("Getting course by teacher ID")
1✔
140
        teacherId := ctx.Param("teacherId")
1✔
141
        course, err := c.service.GetCourseByTeacherId(teacherId)
1✔
142
        if err != nil {
2✔
143
                slog.Error("Error getting course by teacher ID", "error", err)
1✔
144
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
1✔
145
                return
1✔
146
        }
1✔
147
        slog.Debug("Course retrieved", "course", course)
1✔
148
        ctx.JSON(http.StatusOK, course)
1✔
149
}
150

151
// @Summary Get a course by title
152
// @Description Get a course by title
153
// @Tags courses
154
// @Accept json
155
// @Produce json
156
// @Param title path string true "Course title"
157
// @Success 200 {array} model.Course
158
// @Router /courses/title/{title} [get]
159
func (c *CourseController) GetCourseByTitle(ctx *gin.Context) {
1✔
160
        slog.Debug("Getting course by title")
1✔
161
        title := ctx.Param("title")
1✔
162
        course, err := c.service.GetCourseByTitle(title)
1✔
163
        if err != nil {
2✔
164
                slog.Error("Error getting course by title", "error", err)
1✔
165
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
1✔
166
                return
1✔
167
        }
1✔
168
        slog.Debug("Course retrieved", "course", course)
1✔
169
        ctx.JSON(http.StatusOK, course)
1✔
170
}
171

172
// @Summary Update a course
173
// @Description Update a course by ID
174
// @Tags courses
175
// @Accept json
176
// @Produce json
177
// @Param id path string true "Course ID"
178
// @Param course body schemas.UpdateCourseRequest true "Course to update"
179
// @Success 200 {object} model.Course
180
// @Router /courses/{id} [put]
181
func (c *CourseController) UpdateCourse(ctx *gin.Context) {
1✔
182
        slog.Debug("Updating course")
1✔
183
        id := ctx.Param("id")
1✔
184

1✔
185
        var updateCourseRequest schemas.UpdateCourseRequest
1✔
186
        if err := ctx.ShouldBindJSON(&updateCourseRequest); err != nil {
2✔
187
                slog.Error("Error binding JSON", "error", err)
1✔
188
                ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
1✔
189
                return
1✔
190
        }
1✔
191

192
        updatedCourse, err := c.service.UpdateCourse(id, updateCourseRequest)
1✔
193
        if err != nil {
2✔
194
                slog.Error("Error updating course", "error", err)
1✔
195
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
1✔
196
                return
1✔
197
        }
1✔
198
        slog.Debug("Course updated", "course", updatedCourse)
1✔
199
        ctx.JSON(http.StatusOK, updatedCourse)
1✔
200
}
201

202
// @Summary Get courses by student ID
203
// @Description Get courses by student ID
204
// @Tags courses
205
// @Accept json
206
// @Produce json
207
// @Param studentId path string true "Student ID"
208
// @Success 200 {array} model.Course
209
// @Router /courses/student/{studentId} [get]
210
func (c *CourseController) GetCoursesByStudentId(ctx *gin.Context) {
1✔
211
        slog.Debug("Getting courses by student ID")
1✔
212
        studentId := ctx.Param("studentId")
1✔
213
        courses, err := c.service.GetCoursesByStudentId(studentId)
1✔
214
        if err != nil {
2✔
215
                slog.Error("Error getting courses by student ID", "error", err)
1✔
216
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
1✔
217
                return
1✔
218
        }
1✔
219
        slog.Debug("Courses retrieved", "courses", courses)
1✔
220
        ctx.JSON(http.StatusOK, courses)
1✔
221
}
222

223
// @Summary Get courses by user ID
224
// @Description Get courses by user ID
225
// @Tags courses
226
// @Accept json
227
// @Produce json
228
// @Param userId path string true "User ID"
229
// @Success 200 {array} model.Course
230
// @Router /courses/user/{userId} [get]
231
func (c *CourseController) GetCoursesByUserId(ctx *gin.Context) {
1✔
232
        slog.Debug("Getting courses by user ID")
1✔
233
        userId := ctx.Param("userId")
1✔
234
        courses, err := c.service.GetCoursesByUserId(userId)
1✔
235
        if err != nil {
2✔
236
                slog.Error("Error getting courses by user ID", "error", err)
1✔
237
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
1✔
238
                return
1✔
239
        }
1✔
240
        slog.Debug("Courses retrieved", "courses", courses)
1✔
241
        ctx.JSON(http.StatusOK, courses)
1✔
242
}
243

244
// @Summary Add an aux teacher to a course
245
// @Description Add an aux teacher to a course by ID
246
// @Tags courses
247
// @Accept json
248
// @Produce json
249
// @Param id path string true "Course ID"
250
func (c *CourseController) AddAuxTeacherToCourse(ctx *gin.Context) {
1✔
251
        slog.Debug("Adding aux teacher to course")
1✔
252
        id := ctx.Param("id")
1✔
253
        if id == "" {
2✔
254
                slog.Error("Course ID is required")
1✔
255
                ctx.JSON(http.StatusBadRequest, gin.H{"error": "Course ID is required"})
1✔
256
                return
1✔
257
        }
1✔
258

259
        var auxTeacherRequest schemas.AddAuxTeacherToCourseRequest
1✔
260
        if err := ctx.ShouldBindJSON(&auxTeacherRequest); err != nil {
2✔
261
                slog.Error("Error binding JSON", "error", err)
1✔
262
                ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
1✔
263
                return
1✔
264
        }
1✔
265

266
        teacherId := auxTeacherRequest.TeacherID
1✔
267
        auxTeacherId := auxTeacherRequest.AuxTeacherID
1✔
268
        course, err := c.service.AddAuxTeacherToCourse(id, teacherId, auxTeacherId)
1✔
269
        if err != nil {
2✔
270
                slog.Error("Error adding aux teacher to course", "error", err)
1✔
271
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
1✔
272
                return
1✔
273
        }
1✔
274
        slog.Debug("Aux teacher added to course", "course", course)
1✔
275
        ctx.JSON(http.StatusOK, course)
1✔
276
}
277

278
// @Summary Remove an aux teacher from a course
279
// @Description Remove an aux teacher from a course by ID
280
// @Tags courses
281
// @Accept json
282
// @Produce json
283
// @Param id path string true "Course ID"
284
// @Param teacherId query string true "Teacher ID"
285
// @Param auxTeacherId query string true "Aux teacher ID"
286
// @Success 200 {object} model.Course
287
// @Router /courses/{id}/aux-teacher/remove [delete]
288
func (c *CourseController) RemoveAuxTeacherFromCourse(ctx *gin.Context) {
1✔
289
        slog.Debug("Removing aux teacher from course")
1✔
290
        id := ctx.Param("id")
1✔
291
        if id == "" {
2✔
292
                slog.Error("Course ID is required")
1✔
293
                ctx.JSON(http.StatusBadRequest, gin.H{"error": "Course ID is required"})
1✔
294
                return
1✔
295
        }
1✔
296

297
        teacherId := ctx.Query("teacherId")
1✔
298
        auxTeacherId := ctx.Query("auxTeacherId")
1✔
299

1✔
300
        if teacherId == "" || auxTeacherId == "" {
2✔
301
                slog.Error("Teacher ID and aux teacher ID are required")
1✔
302
                ctx.JSON(http.StatusBadRequest, gin.H{"error": "Teacher ID and aux teacher ID are required"})
1✔
303
                return
1✔
304
        }
1✔
305

306
        course, err := c.service.RemoveAuxTeacherFromCourse(id, teacherId, auxTeacherId)
1✔
307
        if err != nil {
2✔
308
                slog.Error("Error removing aux teacher from course", "error", err)
1✔
309
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
1✔
310
                return
1✔
311
        }
1✔
312
        slog.Debug("Aux teacher removed from course", "course", course)
1✔
313
        ctx.JSON(http.StatusOK, course)
1✔
314
}
315

316
// @Summary Get favourite courses
317
// @Description Get favourite courses by student ID
318
// @Tags courses
319
// @Accept json
320
// @Produce json
321
// @Param studentId path string true "Student ID"
322
// @Success 200 {array} model.Course
323
// @Router /courses/favourite/{studentId} [get]
324
func (c *CourseController) GetFavouriteCourses(ctx *gin.Context) {
1✔
325
        slog.Debug("Getting favourite courses")
1✔
326
        studentId := ctx.Param("studentId")
1✔
327
        if studentId == "" {
2✔
328
                slog.Error("Student ID is required")
1✔
329
                ctx.JSON(http.StatusBadRequest, gin.H{"error": "Student ID is required"})
1✔
330
                return
1✔
331
        }
1✔
332

333
        courses, err := c.service.GetFavouriteCourses(studentId)
1✔
334
        if err != nil {
2✔
335
                slog.Error("Error getting favourite courses", "error", err)
1✔
336
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
1✔
337
                return
1✔
338
        }
1✔
339
        slog.Debug("Favourite courses retrieved", "courses", courses)
1✔
340
        ctx.JSON(http.StatusOK, courses)
1✔
341
}
342

343
// @Summary Create course feedback
344
// @Description Create course feedback by course ID
345
// @Tags courses
346
// @Accept json
347
// @Produce json
348
// @Param id path string true "Course ID"
349
// @Param feedback body schemas.CreateCourseFeedbackRequest true "Course feedback"
350
// @Success 200 {object} model.CourseFeedback
351
// @Router /courses/{id}/feedback [post]
352
func (c *CourseController) CreateCourseFeedback(ctx *gin.Context) {
1✔
353
        slog.Debug("Creating course feedback")
1✔
354
        courseId := ctx.Param("id")
1✔
355
        if courseId == "" {
2✔
356
                slog.Error("Course ID is required")
1✔
357
                ctx.JSON(http.StatusBadRequest, gin.H{"error": "Course ID is required"})
1✔
358
                return
1✔
359
        }
1✔
360

361
        var feedback schemas.CreateCourseFeedbackRequest
1✔
362
        if err := ctx.ShouldBindJSON(&feedback); err != nil {
2✔
363
                slog.Error("Error binding create course feedback request", "error", err)
1✔
364
                ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
1✔
365
                return
1✔
366
        }
1✔
367

368
        if !slices.Contains(model.FeedbackTypes, feedback.FeedbackType) {
2✔
369
                slog.Error("Invalid feedback type")
1✔
370
                ctx.JSON(http.StatusBadRequest, gin.H{"error": "Invalid feedback type"})
1✔
371
                return
1✔
372
        }
1✔
373

374
        feedbackModel, err := c.service.CreateCourseFeedback(courseId, feedback)
1✔
375
        if err != nil {
2✔
376
                slog.Error("Error creating course feedback", "error", err)
1✔
377
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
1✔
378
                return
1✔
379
        }
1✔
380

381
        slog.Debug("Course feedback created", "feedback", feedbackModel)
1✔
382
        ctx.JSON(http.StatusOK, feedbackModel)
1✔
383
}
384

385
// @Summary Get course feedback
386
// @Description Get course feedback by course ID
387
// @Tags courses
388
// @Accept json
389
// @Produce json
390
// @Param id path string true "Course ID"
391
// @Param getCourseFeedbackRequest body schemas.GetCourseFeedbackRequest true "Get course feedback request"
392
// @Success 200 {array} model.CourseFeedback
393
// @Router /courses/{id}/feedback [put]
394
func (c *CourseController) GetCourseFeedback(ctx *gin.Context) {
1✔
395
        slog.Debug("Getting course feedback")
1✔
396
        courseId := ctx.Param("id")
1✔
397
        if courseId == "" {
2✔
398
                slog.Error("Course ID is required")
1✔
399
                ctx.JSON(http.StatusBadRequest, gin.H{"error": "Course ID is required"})
1✔
400
                return
1✔
401
        }
1✔
402

403
        var getCourseFeedbackRequest schemas.GetCourseFeedbackRequest
1✔
404
        if err := ctx.ShouldBindJSON(&getCourseFeedbackRequest); err != nil {
2✔
405
                slog.Error("Error binding get course feedback request", "error", err)
1✔
406
                ctx.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
1✔
407
                return
1✔
408
        }
1✔
409

410
        feedback, err := c.service.GetCourseFeedback(courseId, getCourseFeedbackRequest)
1✔
411
        if err != nil {
2✔
412
                slog.Error("Error getting course feedback", "error", err)
1✔
413
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
1✔
414
                return
1✔
415
        }
1✔
416

417
        slog.Debug("Course feedback retrieved", "feedback", feedback)
1✔
418
        ctx.JSON(http.StatusOK, feedback)
1✔
419
}
420

421
// @Summary Get course feedback summary
422
// @Description Get course feedback summary by course ID
423
// @Tags courses
424
// @Accept json
425
// @Produce json
426
// @Param id path string true "Course ID"
427
// @Success 200 {string} string "Course feedback summary"
428
// @Router /courses/{id}/feedback/summary [get]
429
func (c *CourseController) GetCourseFeedbackSummary(ctx *gin.Context) {
×
430
        slog.Debug("Getting course feedback summary")
×
431
        courseId := ctx.Param("id")
×
432
        if courseId == "" {
×
433
                slog.Error("Course ID is required")
×
434
                ctx.JSON(http.StatusBadRequest, gin.H{"error": "Course ID is required"})
×
435
                return
×
436
        }
×
437

438
        feedbacks, err := c.service.GetCourseFeedback(courseId, schemas.GetCourseFeedbackRequest{})
×
439
        if err != nil {
×
440
                slog.Error("Error getting course feedback", "error", err)
×
441
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
×
442
                return
×
443
        }
×
444

445
        if len(feedbacks) == 0 {
×
446
                slog.Error("No feedbacks found")
×
447
                ctx.JSON(http.StatusNotFound, gin.H{"error": "No feedbacks found"})
×
448
                return
×
449
        }
×
450

451
        summary, err := c.aiClient.SummarizeCourseFeedbacks(feedbacks)
×
452
        if err != nil {
×
453
                slog.Error("Error getting course feedback summary", "error", err)
×
454
                ctx.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
×
455
                return
×
456
        }
×
457

458
        slog.Debug("Course feedback summary retrieved", "summary", summary)
×
459
        ctx.JSON(http.StatusOK, summary)
×
460
}
461

462
// @Summary Get course members
463
// @Description Get all members of a course (teacher, aux teachers, and students)
464
// @Tags courses
465
// @Accept json
466
// @Produce json
467
// @Param id path string true "Course ID"
468
// @Success 200 {object} schemas.CourseMembersResponse
469
// @Failure 400 {object} schemas.ErrorResponse
470
// @Failure 404 {object} schemas.ErrorResponse
471
// @Failure 500 {object} schemas.ErrorResponse
472
// @Router /courses/{id}/members [get]
473
func (c *CourseController) GetCourseMembers(ctx *gin.Context) {
1✔
474
        slog.Debug("Getting course members")
1✔
475

1✔
476
        courseId := ctx.Param("id")
1✔
477
        if courseId == "" {
1✔
478
                ctx.JSON(http.StatusBadRequest, schemas.ErrorResponse{Error: "Course ID is required"})
×
479
                return
×
480
        }
×
481

482
        members, err := c.service.GetCourseMembers(courseId)
1✔
483
        if err != nil {
2✔
484
                slog.Error("Error getting course members", "error", err)
1✔
485
                ctx.JSON(http.StatusInternalServerError, schemas.ErrorResponse{Error: err.Error()})
1✔
486
                return
1✔
487
        }
1✔
488

489
        slog.Debug("Course members retrieved", "course_id", courseId)
1✔
490
        ctx.JSON(http.StatusOK, members)
1✔
491
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc