From 9dee1e6bbbae98e3f96fb832d7a71f4b329b11ed Mon Sep 17 00:00:00 2001 From: maxogod Date: Sun, 22 Jun 2025 23:40:43 -0300 Subject: [PATCH 1/8] test: Add tests for middlware --- .../handlers/assignments/assignments_test.go | 2 +- internal/handlers/courses/courses_test.go | 2 +- internal/handlers/modules/modules_test.go | 2 +- internal/middleware/jwt_auth_test.go | 96 +++++++++++++++++++ 4 files changed, 99 insertions(+), 3 deletions(-) diff --git a/internal/handlers/assignments/assignments_test.go b/internal/handlers/assignments/assignments_test.go index 0c5b30f..8b1de0e 100644 --- a/internal/handlers/assignments/assignments_test.go +++ b/internal/handlers/assignments/assignments_test.go @@ -318,7 +318,7 @@ func TestAddAttachment_Success(t *testing.T) { _, err = io.Copy(fileWriter, strings.NewReader("test file content")) assert.NoError(t, err) - writer.Close() + _ = writer.Close() router := createRouterWithJWT(presetUserID.String()) diff --git a/internal/handlers/courses/courses_test.go b/internal/handlers/courses/courses_test.go index d539a5f..0df9a6f 100644 --- a/internal/handlers/courses/courses_test.go +++ b/internal/handlers/courses/courses_test.go @@ -363,7 +363,7 @@ func TestAddAttachment_Success(t *testing.T) { _, err = io.Copy(fileWriter, strings.NewReader("test file content")) assert.NoError(t, err) - writer.Close() + _ = writer.Close() router := createRouterWithJWT(presetUserID.String()) diff --git a/internal/handlers/modules/modules_test.go b/internal/handlers/modules/modules_test.go index c70eb47..8730721 100644 --- a/internal/handlers/modules/modules_test.go +++ b/internal/handlers/modules/modules_test.go @@ -305,7 +305,7 @@ func TestAddAttachment_Success(t *testing.T) { _, err = io.Copy(fileWriter, strings.NewReader("test file content")) assert.NoError(t, err) - writer.Close() + _ = writer.Close() router := createRouterWithJWT(presetUserID.String()) diff --git a/internal/middleware/jwt_auth_test.go b/internal/middleware/jwt_auth_test.go index ff7795c..9925d5f 100644 --- a/internal/middleware/jwt_auth_test.go +++ b/internal/middleware/jwt_auth_test.go @@ -2,6 +2,7 @@ package middleware_test import ( "courses-microservice/config" + "courses-microservice/internal/errors" "courses-microservice/internal/middleware" "net/http" "net/http/httptest" @@ -104,3 +105,98 @@ func TestJWTAuthMiddleware_WithErrorHandler(t *testing.T) { assert.Contains(t, w.Body.String(), "Invalid JWT token") }) } + +func TestErrorHandlerMiddleware_NoErrors(t *testing.T) { + router := gin.New() + router.Use(middleware.ErrorHandlerMiddleware()) + router.GET("/test", func(c *gin.Context) { + c.String(http.StatusOK, "ok") + }) + + resp := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/test", nil) + router.ServeHTTP(resp, req) + + assert.Equal(t, http.StatusOK, resp.Code) + assert.Equal(t, "ok", resp.Body.String()) +} + +func TestErrorHandlerMiddleware_ErrorAlreadyWritten(t *testing.T) { + router := gin.New() + router.Use(middleware.ErrorHandlerMiddleware()) + router.GET("/test", func(c *gin.Context) { + c.String(http.StatusBadRequest, "bad") + _ = c.Error(errors.NewBadRequestError("won't be handled")) + }) + + resp := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/test", nil) + router.ServeHTTP(resp, req) + + assert.Equal(t, http.StatusBadRequest, resp.Code) + assert.Equal(t, "bad", resp.Body.String()) +} + +func TestErrorHandlerMiddleware_KnownErrors(t *testing.T) { + tests := []struct { + name string + err error + statusCode int + message string + }{ + { + name: "BadRequestError", + err: errors.NewBadRequestError("bad request"), + statusCode: http.StatusBadRequest, + message: "bad request", + }, + { + name: "UnauthorizedError", + err: errors.NewUnauthorizedError("unauthorized"), + statusCode: http.StatusUnauthorized, + message: "unauthorized", + }, + { + name: "ForbiddenError", + err: errors.NewForbiddenError("forbidden"), + statusCode: http.StatusForbidden, + message: "forbidden", + }, + { + name: "NotFoundError", + err: errors.NewNotFoundError("not found"), + statusCode: http.StatusNotFound, + message: "not found", + }, + { + name: "ConflictError", + err: errors.NewConflictError("conflict"), + statusCode: http.StatusConflict, + message: "conflict", + }, + { + name: "InternalServerError", + err: errors.NewInternalServerError("internal issue"), + statusCode: http.StatusInternalServerError, + message: "internal issue", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + router := gin.New() + router.Use(middleware.ErrorHandlerMiddleware()) + router.GET("/test", func(c *gin.Context) { + _ = c.Error(tt.err) + }) + + req := httptest.NewRequest(http.MethodGet, "/test", nil) + resp := httptest.NewRecorder() + + router.ServeHTTP(resp, req) + + assert.Equal(t, tt.statusCode, resp.Code) + assert.Contains(t, resp.Body.String(), tt.message) + }) + } +} From 44f3e956f024d8b072dd92c97a74fdc7f70a0a98 Mon Sep 17 00:00:00 2001 From: maxogod Date: Mon, 23 Jun 2025 00:34:25 -0300 Subject: [PATCH 2/8] test: Add tests for respositories --- go.mod | 1 + go.sum | 3 + internal/repository/assignments_test.go | 127 +++++++++++ internal/repository/exams_test.go | 291 ++++++++++++++++++++++++ internal/repository/modules.go | 1 - internal/repository/modules_test.go | 234 +++++++++++++++++++ internal/utils/generate_dto_test.go | 118 ++++++++++ 7 files changed, 774 insertions(+), 1 deletion(-) create mode 100644 internal/utils/generate_dto_test.go diff --git a/go.mod b/go.mod index 25e4793..edbd546 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module courses-microservice go 1.24.1 require ( + github.com/DATA-DOG/go-sqlmock v1.5.2 github.com/Masterminds/squirrel v1.5.4 github.com/gin-contrib/cors v1.7.5 github.com/gin-gonic/gin v1.10.0 diff --git a/go.sum b/go.sum index a3dbe28..1905edf 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= +github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= @@ -96,6 +98,7 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= diff --git a/internal/repository/assignments_test.go b/internal/repository/assignments_test.go index 23ccd8d..c6761ad 100644 --- a/internal/repository/assignments_test.go +++ b/internal/repository/assignments_test.go @@ -231,6 +231,133 @@ func TestGetAssignmentByID_NotFound(t *testing.T) { assert.NoError(t, mock.ExpectationsWereMet()) } +func TestCreateOrUpdateAssignmentSubmission_Success(t *testing.T) { + mock, repo := setupMockRepoForAssignments(t) + + assignmentID := uuid.New() + studentID := uuid.New() + submission := &models.AssignmentSubmission{ + Text: "test submission", + Attachment: "file.png", + Comment: "Looks good", + Score: 90, + Reviewed: true, + IgnoreDeadline: false, + } + + // Mock expected query and returned row + mock.ExpectQuery("INSERT INTO assignment_submissions"). + WithArgs( + assignmentID, + studentID, + submission.Text, + submission.Attachment, + submission.Comment, + submission.Score, + submission.Reviewed, + submission.IgnoreDeadline, + ). + WillReturnRows(pgxmock.NewRows([]string{"id", "created_at", "updated_at"}). + AddRow(uuid.New(), time.Now(), time.Now())) + + err := repo.CreateOrUpdateAssignmentSubmission(assignmentID, studentID, submission) + assert.NoError(t, err) + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestGetAssignmentSubmissionByID_Success(t *testing.T) { + mock, repo := setupMockRepoForAssignments(t) + + submissionID := uuid.New() + expected := &models.AssignmentSubmission{ + Id: submissionID, + AssignmentId: uuid.New(), + StudentId: uuid.New(), + Text: "Great work!", + Attachment: "submission.pdf", + Comment: "Reviewed", + Score: 95, + Reviewed: true, + IgnoreDeadline: false, + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + } + + mock.ExpectQuery("SELECT id, assignment_id, student_id, text, attachment, comment"). + WithArgs(submissionID). + WillReturnRows( + pgxmock.NewRows([]string{ + "id", "assignment_id", "student_id", "text", "attachment", "comment", "score", + "reviewed", "ignore_deadline", "created_at", "updated_at", + }).AddRow( + expected.Id, + expected.AssignmentId, + expected.StudentId, + expected.Text, + expected.Attachment, + expected.Comment, + expected.Score, + expected.Reviewed, + expected.IgnoreDeadline, + expected.CreatedAt, + expected.UpdatedAt, + ), + ) + + result, err := repo.GetAssignmentSubmissionByID(submissionID) + assert.NoError(t, err) + assert.Equal(t, expected.Id, result.Id) + assert.Equal(t, expected.Text, result.Text) + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestGetAssignmentSubmissionByStudent_Success(t *testing.T) { + mock, repo := setupMockRepoForAssignments(t) + + assignmentID := uuid.New() + studentID := uuid.New() + expected := &models.AssignmentSubmission{ + Id: uuid.New(), + AssignmentId: assignmentID, + StudentId: studentID, + Text: "My submission", + Attachment: "homework.docx", + Comment: "Looks good", + Score: 88, + Reviewed: true, + IgnoreDeadline: false, + CreatedAt: time.Now(), + UpdatedAt: time.Now(), + } + + mock.ExpectQuery("SELECT id, assignment_id, student_id, text, attachment, comment"). + WithArgs(assignmentID, studentID). + WillReturnRows( + pgxmock.NewRows([]string{ + "id", "assignment_id", "student_id", "text", "attachment", "comment", "score", + "reviewed", "ignore_deadline", "created_at", "updated_at", + }).AddRow( + expected.Id, + expected.AssignmentId, + expected.StudentId, + expected.Text, + expected.Attachment, + expected.Comment, + expected.Score, + expected.Reviewed, + expected.IgnoreDeadline, + expected.CreatedAt, + expected.UpdatedAt, + ), + ) + + result, err := repo.GetAssignmentSubmissionByStudent(assignmentID, studentID) + assert.NoError(t, err) + assert.Equal(t, expected.Id, result.Id) + assert.Equal(t, expected.Text, result.Text) + assert.NoError(t, mock.ExpectationsWereMet()) +} + /* HELPER FUNCTIONS */ func setupMockRepoForAssignments(t *testing.T) (pgxmock.PgxConnIface, repository.CoursesRepository) { diff --git a/internal/repository/exams_test.go b/internal/repository/exams_test.go index 2bc2714..07a4286 100644 --- a/internal/repository/exams_test.go +++ b/internal/repository/exams_test.go @@ -1 +1,292 @@ package repository_test + +import ( + "courses-microservice/config" + "courses-microservice/internal/dto" + "courses-microservice/internal/models" + "courses-microservice/internal/repository" + "testing" + "time" + + "github.com/google/uuid" + "github.com/pashagolub/pgxmock/v2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestCreateExam_Success(t *testing.T) { + mock, repo := setupMockRepoForExams(t) + + now := time.Now() + exam := &models.Exam{ + CourseId: uuid.New(), + Title: "Final Exam", + Instructions: "Answer all questions", + MaxScore: 100, + PassingScore: 60, + Deadline: now.Add(24 * time.Hour), + } + + examID := uuid.New() + mock.ExpectQuery("INSERT INTO exams"). + WithArgs( + exam.CourseId, + exam.Title, + exam.Instructions, + exam.ExamData, + exam.MaxScore, + exam.PassingScore, + exam.Deadline, + ).WillReturnRows( + pgxmock.NewRows([]string{"id", "created_at", "updated_at"}). + AddRow(examID, now, now), + ) + + err := repo.CreateExam(exam) + require.NoError(t, err) + assert.Equal(t, examID, exam.Id) + assert.WithinDuration(t, now, exam.CreatedAt, time.Second) + assert.WithinDuration(t, now, exam.UpdatedAt, time.Second) + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestDeleteExam_Success(t *testing.T) { + mock, repo := setupMockRepoForExams(t) + + examID := uuid.New() + mock.ExpectExec("DELETE FROM exams"). + WithArgs(examID). + WillReturnResult(pgxmock.NewResult("DELETE", 1)) // simulate 1 row deleted + + err := repo.DeleteExam(examID) + require.NoError(t, err) + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestUpdateExam_Success(t *testing.T) { + mock, repo := setupMockRepoForExams(t) + examID := uuid.New() + + expectedUpdatedAt := time.Now() + exam := &models.Exam{ + Title: "Updated Exam", + Instructions: "Updated instructions", + MaxScore: 100, + PassingScore: 60, + Deadline: expectedUpdatedAt.Add(24 * time.Hour), + } + + mock.ExpectQuery("UPDATE exams"). + WithArgs( + exam.Title, + exam.Instructions, + exam.ExamData, + exam.MaxScore, + exam.PassingScore, + exam.Deadline, + examID, + ). + WillReturnRows(pgxmock.NewRows([]string{"updated_at"}).AddRow(expectedUpdatedAt)) + + err := repo.UpdateExam(examID, exam) + require.NoError(t, err) + assert.Equal(t, expectedUpdatedAt, exam.UpdatedAt) + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestGetExamsByStudent_Success(t *testing.T) { + mock, repo := setupMockRepoForExams(t) + userID := uuid.New() + page := 1 + + examID := uuid.New() + courseID := uuid.New() + now := time.Now() + + examData := models.ExamQuestionsData{} + + rows := pgxmock.NewRows([]string{ + "id", "course_id", "title", "instructions", "exam_data", "max_score", "passing_score", "deadline", "created_at", "updated_at", + }).AddRow( + examID, courseID, "Sample Exam", "Instructions", + examData, + 100, 60, + now.Add(24*time.Hour), now, now, + ) + + mock.ExpectQuery("SELECT e.id, e.course_id, e.title.*FROM exams e.*WHERE er.student_id"). + WithArgs(userID, config.CoursesPerPage, config.CoursesPerPage*(page-1)). + WillReturnRows(rows) + + exams, err := repo.GetExamsByStudent(userID, page) + require.NoError(t, err) + require.Len(t, exams, 1) + assert.Equal(t, "Sample Exam", exams[0].Title) + assert.Equal(t, 100, exams[0].MaxScore) + assert.Equal(t, examData, exams[0].ExamData) + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestGetExamsByCourse_Success(t *testing.T) { + mock, repo := setupMockRepoForExams(t) + courseID := uuid.New() + page := 1 + now := time.Now() + examID := uuid.New() + examData := models.ExamQuestionsData{} + + rows := pgxmock.NewRows([]string{ + "id", "course_id", "title", "instructions", "exam_data", "max_score", "passing_score", "deadline", "created_at", "updated_at", + }).AddRow( + examID, courseID, "Midterm", "Read all questions", + examData, + 100, 60, + now.Add(48*time.Hour), now, now, + ) + + mock.ExpectQuery(`SELECT id, course_id, title.*FROM exams.*WHERE course_id =`). // keep regex loose + WithArgs(courseID, config.CoursesPerPage, config.CoursesPerPage*(page-1)). + WillReturnRows(rows) + + _, err := repo.GetExamsByCourse(courseID, page) + require.NoError(t, err) + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestGetExamByID_Success(t *testing.T) { + mock, repo := setupMockRepoForExams(t) + examID := uuid.New() + courseID := uuid.New() + now := time.Now() + examData := models.ExamQuestionsData{} + + rows := pgxmock.NewRows([]string{ + "id", "course_id", "title", "instructions", "exam_data", "max_score", "passing_score", "deadline", "created_at", "updated_at", + }).AddRow( + examID, + courseID, + "Final Exam", + "Follow instructions carefully", + examData, + 100, + 70, + now.Add(72*time.Hour), + now, + now, + ) + + mock.ExpectQuery("SELECT id, course_id, title, instructions, exam_data, max_score, passing_score, deadline, created_at, updated_at FROM exams WHERE id ="). + WithArgs(examID). + WillReturnRows(rows) + + exam, err := repo.GetExamByID(examID) + require.NoError(t, err) + require.NotNil(t, exam) + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestCreateOrUpdateExamSubmission_Success(t *testing.T) { + mock, repo := setupMockRepoForExams(t) + + examID := uuid.New() + studentID := uuid.New() + now := time.Now() + + submission := &models.ExamSubmission{} + + mock.ExpectQuery("INSERT INTO exam_submissions"). + WithArgs( + examID, + studentID, + submission.Answers, + submission.Comment, + submission.Score, + submission.Reviewed, + ). + WillReturnRows(pgxmock.NewRows([]string{"id", "created_at", "updated_at"}). + AddRow(uuid.New(), now, now), + ) + + err := repo.CreateOrUpdateExamSubmission(examID, studentID, submission) + require.NoError(t, err) + assert.NotEqual(t, uuid.Nil, submission.Id) + assert.False(t, submission.CreatedAt.IsZero()) + assert.False(t, submission.UpdatedAt.IsZero()) + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestGetExamSubmissionByID_Success(t *testing.T) { + mock, repo := setupMockRepoForExams(t) + + submissionID := uuid.New() + examID := uuid.New() + studentID := uuid.New() + now := time.Now() + answers := []dto.ExamSubmissionAnswersDTO{} + + rows := pgxmock.NewRows([]string{ + "id", "exam_id", "student_id", "answers", "comment", "score", "reviewed", "created_at", "updated_at", + }).AddRow( + submissionID, + examID, + studentID, + answers, + "Good job", + 90, + true, + now, + now, + ) + + mock.ExpectQuery("SELECT id, exam_id, student_id, answers, comment, score, reviewed, created_at, updated_at FROM exam_submissions WHERE id = \\$1"). + WithArgs(submissionID). + WillReturnRows(rows) + + submission, err := repo.GetExamSubmissionByID(submissionID) + require.NoError(t, err) + require.NotNil(t, submission) + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestGetExamSubmissionByStudent_Success(t *testing.T) { + mock, repo := setupMockRepoForExams(t) + + examID := uuid.New() + studentID := uuid.New() + submissionID := uuid.New() + now := time.Now() + answers := []dto.ExamSubmissionAnswersDTO{} + + rows := pgxmock.NewRows([]string{ + "id", "exam_id", "student_id", "answers", "comment", "score", "reviewed", "created_at", "updated_at", + }).AddRow( + submissionID, + examID, + studentID, + answers, + "Well done", + 85, + false, + now, + now, + ) + + mock.ExpectQuery(`SELECT id, exam_id, student_id, answers, comment, score, reviewed, created_at, updated_at FROM exam_submissions WHERE exam_id = \$1 AND student_id = \$2`). + WithArgs(examID, studentID). + WillReturnRows(rows) + + submission, err := repo.GetExamSubmissionByStudent(examID, studentID) + require.NoError(t, err) + require.NotNil(t, submission) + assert.NoError(t, mock.ExpectationsWereMet()) +} + +/* HELPER FUNCTIONS */ + +func setupMockRepoForExams(t *testing.T) (pgxmock.PgxConnIface, repository.CoursesRepository) { + mock, err := pgxmock.NewConn() + assert.NoError(t, err) + + repo := repository.NewCoursesRepository(mock) + return mock, repo +} diff --git a/internal/repository/modules.go b/internal/repository/modules.go index af68435..38e6c80 100644 --- a/internal/repository/modules.go +++ b/internal/repository/modules.go @@ -33,7 +33,6 @@ func (r *CoursesRepositoryImpl) CreateModule(module *models.Module) error { } return nil - } func (r *CoursesRepositoryImpl) UpdateModule(moduleId uuid.UUID, module *models.Module) error { diff --git a/internal/repository/modules_test.go b/internal/repository/modules_test.go index 2bc2714..ff05fee 100644 --- a/internal/repository/modules_test.go +++ b/internal/repository/modules_test.go @@ -1 +1,235 @@ package repository_test + +import ( + "courses-microservice/internal/models" + "courses-microservice/internal/repository" + "fmt" + "testing" + "time" + + "github.com/google/uuid" + "github.com/jackc/pgx/v5" + "github.com/pashagolub/pgxmock/v2" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestCreateModule_Success(t *testing.T) { + mock, repo := setupMockRepoForModules(t) + + module := &models.Module{ + CourseId: uuid.New(), + Title: "Module Title", + Content: "Module Content", + Position: 1, + Attachments: []string{"attachment1.png", "attachment2.pdf"}, + } + + createdAt := time.Now() + updatedAt := createdAt + + rows := pgxmock.NewRows([]string{"id", "attachments", "created_at", "updated_at"}). + AddRow(uuid.New(), module.Attachments, createdAt, updatedAt) + + mock.ExpectQuery(`INSERT INTO modules \(course_id, title, content, position\) VALUES \(\$1, \$2, \$3, \$4\) RETURNING id, attachments, created_at, updated_at`). + WithArgs(module.CourseId, module.Title, module.Content, module.Position). + WillReturnRows(rows) + + err := repo.CreateModule(module) + + require.NoError(t, err) + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestUpdateModule_Success(t *testing.T) { + mock, repo := setupMockRepoForModules(t) + moduleId := uuid.New() + module := &models.Module{ + Title: "Updated Title", + Content: "Updated Content", + Attachments: []string{"file1.png", "file2.pdf"}, + Position: 2, + } + + updatedAt := time.Now() + + mock.ExpectQuery(`UPDATE modules SET title = \$1, content = \$2, attachments = \$3, position = \$4 WHERE id = \$5 RETURNING updated_at`). + WithArgs(module.Title, module.Content, module.Attachments, module.Position, moduleId). + WillReturnRows(pgxmock.NewRows([]string{"updated_at"}).AddRow(updatedAt)) + + err := repo.UpdateModule(moduleId, module) + + require.NoError(t, err) + assert.Equal(t, updatedAt, module.UpdatedAt) + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestDeleteModule(t *testing.T) { + mock, repo := setupMockRepoForModules(t) + moduleId := uuid.New() + + t.Run("Success", func(t *testing.T) { + mock.ExpectExec(`DELETE FROM modules WHERE id = \$1`). + WithArgs(moduleId). + WillReturnResult(pgxmock.NewResult("DELETE", 1)) // 1 row affected + + err := repo.DeleteModule(moduleId) + require.NoError(t, err) + assert.NoError(t, mock.ExpectationsWereMet()) + }) + + t.Run("NotFound", func(t *testing.T) { + mock.ExpectExec(`DELETE FROM modules WHERE id = \$1`). + WithArgs(moduleId). + WillReturnResult(pgxmock.NewResult("DELETE", 0)) // 0 rows affected + + err := repo.DeleteModule(moduleId) + require.Error(t, err) + assert.Contains(t, err.Error(), "No module found with id") + assert.NoError(t, mock.ExpectationsWereMet()) + }) + + t.Run("InternalError", func(t *testing.T) { + mock.ExpectExec(`DELETE FROM modules WHERE id = \$1`). + WithArgs(moduleId). + WillReturnError(fmt.Errorf("db error")) + + err := repo.DeleteModule(moduleId) + require.Error(t, err) + assert.Contains(t, err.Error(), "Internal server error") + assert.NoError(t, mock.ExpectationsWereMet()) + }) +} + +func TestGetModuleById(t *testing.T) { + mock, repo := setupMockRepoForModules(t) + moduleId := uuid.New() + + now := time.Now() + attachments := []string{"file1.pdf", "file2.png"} // Example attachments slice + + t.Run("Success", func(t *testing.T) { + rows := pgxmock.NewRows([]string{ + "id", "course_id", "title", "content", "attachments", "position", "created_at", "updated_at", + }).AddRow( + moduleId, + uuid.New(), + "Module Title", + "Module Content", + attachments, + 1, + now, + now, + ) + + mock.ExpectQuery(`SELECT id, course_id, title, content, attachments, position, created_at, updated_at FROM modules WHERE id = \$1`). + WithArgs(moduleId). + WillReturnRows(rows) + + module, err := repo.GetModuleById(moduleId) + require.NoError(t, err) + require.NotNil(t, module) + assert.Equal(t, moduleId, module.Id) + assert.Equal(t, "Module Title", module.Title) + assert.Equal(t, attachments, module.Attachments) + assert.NoError(t, mock.ExpectationsWereMet()) + }) + + t.Run("NotFound", func(t *testing.T) { + mock.ExpectQuery(`SELECT id, course_id, title, content, attachments, position, created_at, updated_at FROM modules WHERE id = \$1`). + WithArgs(moduleId). + WillReturnError(pgx.ErrNoRows) + + module, err := repo.GetModuleById(moduleId) + require.Error(t, err) + assert.Nil(t, module) + assert.Contains(t, err.Error(), "No module found with id") + assert.NoError(t, mock.ExpectationsWereMet()) + }) + + t.Run("InternalError", func(t *testing.T) { + mock.ExpectQuery(`SELECT id, course_id, title, content, attachments, position, created_at, updated_at FROM modules WHERE id = \$1`). + WithArgs(moduleId). + WillReturnError(fmt.Errorf("db error")) + + module, err := repo.GetModuleById(moduleId) + require.Error(t, err) + assert.Nil(t, module) + assert.Contains(t, err.Error(), "Internal server error") + assert.NoError(t, mock.ExpectationsWereMet()) + }) +} + +func TestGetModulesByCourse(t *testing.T) { + mock, repo := setupMockRepoForModules(t) + courseID := uuid.New() + + now := time.Now() + attachments1 := []string{"file1.pdf"} + attachments2 := []string{"file2.docx"} + + t.Run("Success", func(t *testing.T) { + rows := pgxmock.NewRows([]string{ + "id", "course_id", "title", "content", "attachments", "position", "created_at", "updated_at", + }).AddRow( + uuid.New(), courseID, "Module 1", "Content 1", attachments1, 1, now, now, + ).AddRow( + uuid.New(), courseID, "Module 2", "Content 2", attachments2, 2, now, now, + ) + + mock.ExpectQuery(`SELECT id, course_id, title, content, attachments, position, created_at, updated_at FROM modules WHERE course_id = \$1 ORDER BY position`). + WithArgs(courseID). + WillReturnRows(rows) + + modules, err := repo.GetModulesByCourse(courseID) + require.NoError(t, err) + require.Len(t, modules, 2) + assert.Equal(t, "Module 1", modules[0].Title) + assert.Equal(t, attachments1, modules[0].Attachments) + assert.Equal(t, 1, modules[0].Position) + assert.Equal(t, "Module 2", modules[1].Title) + assert.Equal(t, attachments2, modules[1].Attachments) + assert.Equal(t, 2, modules[1].Position) + assert.NoError(t, mock.ExpectationsWereMet()) + }) + + t.Run("QueryError", func(t *testing.T) { + mock.ExpectQuery(`SELECT id, course_id, title, content, attachments, position, created_at, updated_at FROM modules WHERE course_id = \$1 ORDER BY position`). + WithArgs(courseID). + WillReturnError(fmt.Errorf("query error")) + + modules, err := repo.GetModulesByCourse(courseID) + require.Error(t, err) + assert.Nil(t, modules) + assert.Contains(t, err.Error(), "Internal server error") + assert.NoError(t, mock.ExpectationsWereMet()) + }) + + t.Run("ScanError", func(t *testing.T) { + rows := pgxmock.NewRows([]string{ + "id", "course_id", "title", "content", "attachments", "position", "created_at", "updated_at", + }).AddRow( + "invalid-uuid", courseID, "Module 1", "Content", attachments1, 1, now, now, + ) + + mock.ExpectQuery(`SELECT id, course_id, title, content, attachments, position, created_at, updated_at FROM modules WHERE course_id = \$1 ORDER BY position`). + WithArgs(courseID). + WillReturnRows(rows) + + modules, err := repo.GetModulesByCourse(courseID) + require.Error(t, err) + assert.Nil(t, modules) + assert.Contains(t, err.Error(), "Internal server error") + assert.NoError(t, mock.ExpectationsWereMet()) + }) +} + +/* HELPER FUNCTIONS */ + +func setupMockRepoForModules(t *testing.T) (pgxmock.PgxConnIface, repository.CoursesRepository) { + mock, err := pgxmock.NewConn() + assert.NoError(t, err) + + repo := repository.NewCoursesRepository(mock) + return mock, repo +} diff --git a/internal/utils/generate_dto_test.go b/internal/utils/generate_dto_test.go new file mode 100644 index 0000000..d55bbb8 --- /dev/null +++ b/internal/utils/generate_dto_test.go @@ -0,0 +1,118 @@ +package utils_test + +import ( + "courses-microservice/internal/dto" + "courses-microservice/internal/models" + "courses-microservice/internal/utils" + "testing" + + "github.com/google/uuid" + "github.com/stretchr/testify/assert" +) + +func TestGenerateDTO(t *testing.T) { + course := models.Course{ + Id: uuid.New(), + OwnerID: uuid.New(), + Title: "Test Course", + Description: "This is a test course", + Syllabus: "Syllabus content", + } + auxProfessors := []uuid.UUID{uuid.New(), uuid.New()} + students := []dto.StudentInCourseDTO{ + { + Id: uuid.New(), + FinalGrade: 5, + }, + } + canJoin := true + courseDTO := utils.NewCourseResponseDTO(&course, auxProfessors, students, canJoin) + assert.Equal(t, course.Id, courseDTO.Id) + assert.Equal(t, course.OwnerID, courseDTO.OwnerID) + assert.Equal(t, course.Title, courseDTO.Title) + assert.Equal(t, course.Description, courseDTO.Description) + assert.Equal(t, course.Syllabus, courseDTO.Syllabus) +} + +func TestGenerateAssignmentDTO(t *testing.T) { + assignment := models.Assignment{ + Id: uuid.New(), + CourseId: uuid.New(), + Title: "Test Assignment", + Instructions: "Complete the assignment", + Attachments: []string{"attachment1.pdf", "attachment2.docx"}, + } + status := models.AssignmentPending + submissionID := uuid.New() + assignmentDTO := utils.NewAssignmentResponseDTO(&assignment, status, &submissionID) + assert.Equal(t, assignment.Id, assignmentDTO.Id) + assert.Equal(t, assignment.CourseId, assignmentDTO.CourseId) + assert.Equal(t, assignment.Title, assignmentDTO.Title) + assert.Equal(t, assignment.Instructions, assignmentDTO.Instructions) + assert.Equal(t, assignment.Attachments, assignmentDTO.Attachments) + assert.Equal(t, status, models.AssignmentStatus(assignmentDTO.Status)) + assert.Equal(t, submissionID, *assignmentDTO.SubmissionId) +} + +func TestGenerateAssignmentSubmissionDTO(t *testing.T) { + submission := models.AssignmentSubmission{ + Id: uuid.New(), + AssignmentId: uuid.New(), + StudentId: uuid.New(), + Text: "This is a submission", + Attachment: "submission.pdf", + Comment: "Good job!", + } + isStudent := true + submissionDTO := utils.NewAssignmentSubmissionResponseDTO(&submission, isStudent) + assert.Equal(t, submission.Id, submissionDTO.Id) + assert.Equal(t, submission.AssignmentId, submissionDTO.AssignmentId) + assert.Equal(t, submission.StudentId, submissionDTO.StudentId) + assert.Equal(t, submission.Text, submissionDTO.Text) + assert.Equal(t, submission.Attachment, submissionDTO.Attachment) + assert.Equal(t, submission.Comment, submissionDTO.Comment) +} + +func TestGenerateExamDTO(t *testing.T) { + exam := models.Exam{ + Id: uuid.New(), + CourseId: uuid.New(), + Title: "Test Exam", + Instructions: "Follow the instructions carefully", + MaxScore: 100, + PassingScore: 60, + } + examDTO := utils.NewExamResponseDTO(&exam, models.ExamPending, nil) + assert.Equal(t, exam.Id, examDTO.Id) + assert.Equal(t, exam.CourseId, examDTO.CourseId) + assert.Equal(t, exam.Title, examDTO.Title) + assert.Equal(t, exam.Instructions, examDTO.Instructions) + assert.Equal(t, exam.MaxScore, examDTO.MaxScore) + assert.Equal(t, exam.PassingScore, examDTO.PassingScore) +} + +func TestGenerateExamSubmissionDTO(t *testing.T) { + submission := models.ExamSubmission{ + Id: uuid.New(), + ExamId: uuid.New(), + StudentId: uuid.New(), + Comment: "Well done!", + } + submissionDTO := utils.NewExamSubmissionResponseDTO(&submission, true) + assert.Equal(t, submission.Id, submissionDTO.Id) + assert.Equal(t, submission.ExamId, submissionDTO.ExamId) + assert.Equal(t, submission.StudentId, submissionDTO.StudentId) + assert.Equal(t, submission.Comment, submissionDTO.Comment) +} + +func TestModuleResponseDTO(t *testing.T) { + module := models.Module{ + Id: uuid.New(), + CourseId: uuid.New(), + Title: "Test Module", + } + moduleDTO := utils.NewModuleResponseDTO(&module) + assert.Equal(t, module.Id, moduleDTO.Id) + assert.Equal(t, module.CourseId, moduleDTO.CourseId) + assert.Equal(t, module.Title, moduleDTO.Title) +} From c6ab0eaa35ca7775a48b9349a8aea92f7842eb07 Mon Sep 17 00:00:00 2001 From: maxogod Date: Mon, 23 Jun 2025 00:47:54 -0300 Subject: [PATCH 3/8] test: Fix test failing --- .../repository/memory/repository_memory.go | 2 +- internal/services/modules/modules_test.go | 45 ++++++++++++++----- 2 files changed, 35 insertions(+), 12 deletions(-) diff --git a/internal/repository/memory/repository_memory.go b/internal/repository/memory/repository_memory.go index 23987bd..a5ce03b 100644 --- a/internal/repository/memory/repository_memory.go +++ b/internal/repository/memory/repository_memory.go @@ -565,7 +565,7 @@ func (c *InMemoryCoursesRepository) GetModuleById(moduleId uuid.UUID) (*models.M return module, nil } - return nil, errors.NewNotFoundError("Module not found") + return nil, errors.NewNotFoundError("Module not found [GET]") } func (c *InMemoryCoursesRepository) GetModulesByCourse(courseID uuid.UUID) ([]models.Module, error) { diff --git a/internal/services/modules/modules_test.go b/internal/services/modules/modules_test.go index a71236d..db8b1d4 100644 --- a/internal/services/modules/modules_test.go +++ b/internal/services/modules/modules_test.go @@ -175,26 +175,49 @@ func TestPatchModule_Success(t *testing.T) { } func TestOrganizeModuleAttachments(t *testing.T) { - modules, _ := repo.GetModulesByCourse(courseID) - modulePos := []dto.ModulePositionDTO{} - for i, m := range modules { - modulePos = append(modulePos, dto.ModulePositionDTO{ - Id: m.Id, - Position: i, - }) + course := &models.Course{OwnerID: professorIDs[0], Title: "Test Course"} + _ = repo.AddCourse(course) + _ = repo.AddAuxProfessor(course.Id, professorIDs[1]) + ids := []uuid.UUID{} + modules := []models.Module{ + { + CourseId: course.Id, + Title: "Module 1", + Content: "Content 1", + Position: 0, + }, + { + CourseId: course.Id, + Title: "Module 2", + Content: "Content 2", + Position: 1, + }, + } + for _, module := range modules { + _ = repo.CreateModule(&module) + ids = append(ids, module.Id) + } + + modulePos := []dto.ModulePositionDTO{ + { + Id: ids[0], + Position: 1, + }, + { + Id: ids[1], + Position: 0, + }, } // Not professor - err := service.OrganizeModules(courseID, uuid.New(), modulePos) + err := service.OrganizeModules(course.Id, uuid.New(), modulePos) assert.Error(t, err) assert.IsType(t, err, &errors.UnauthorizedError{}) assert.Contains(t, err.Error(), "not authorized") // Success - err = service.OrganizeModules(courseID, professorIDs[1], modulePos) + err = service.OrganizeModules(course.Id, professorIDs[1], modulePos) assert.NoError(t, err) - organizedModules, _ := repo.GetModulesByCourse(courseID) - assert.Equal(t, len(modules), len(organizedModules)) } /* ATTACHMENTS */ From 8f1918eaffe7bd942f2a1a6c013657ef38dfc23b Mon Sep 17 00:00:00 2001 From: maxogod Date: Mon, 23 Jun 2025 01:02:34 -0300 Subject: [PATCH 4/8] test: Add tests for courses repo --- internal/repository/courses_test.go | 287 ++++++++++++++++++++++++++++ 1 file changed, 287 insertions(+) diff --git a/internal/repository/courses_test.go b/internal/repository/courses_test.go index 2606f69..c42669c 100644 --- a/internal/repository/courses_test.go +++ b/internal/repository/courses_test.go @@ -1,6 +1,7 @@ package repository_test import ( + "courses-microservice/config" "courses-microservice/internal/errors" "courses-microservice/internal/models" "courses-microservice/internal/repository" @@ -11,6 +12,7 @@ import ( "github.com/jackc/pgx/v5" pgxmock "github.com/pashagolub/pgxmock/v2" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestAddCourse(t *testing.T) { @@ -387,6 +389,291 @@ func TestGetEnrollmentsCount(t *testing.T) { assert.NoError(t, mock.ExpectationsWereMet()) } +func TestAddAuxProfessor_Success(t *testing.T) { + mock, repo := setupMockRepoForCourses(t) + courseID := uuid.New() + userID := uuid.New() + + mock.ExpectExec("INSERT INTO aux_professors"). + WithArgs(courseID, userID). + WillReturnResult(pgxmock.NewResult("INSERT", 1)) + + err := repo.AddAuxProfessor(courseID, userID) + require.NoError(t, err) + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestRemoveAuxProfessor_Success(t *testing.T) { + mock, repo := setupMockRepoForCourses(t) + courseID := uuid.New() + userID := uuid.New() + + mock.ExpectExec("DELETE FROM aux_professors"). + WithArgs(courseID, userID). + WillReturnResult(pgxmock.NewResult("DELETE", 1)) + + err := repo.RemoveAuxProfessor(courseID, userID) + require.NoError(t, err) + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestGetAuxProfessorLogs_Success(t *testing.T) { + mock, repo := setupMockRepoForCourses(t) + courseID := uuid.New() + userID := uuid.New() + page := 1 + now := time.Now() + + rows := pgxmock.NewRows([]string{ + "course_id", "user_id", "action", "created_at", + }).AddRow(courseID, userID, "added", now) + + mock.ExpectQuery("SELECT course_id, user_id, action, created_at FROM aux_professor_logs"). + WithArgs(courseID, config.CoursesPerPage, config.CoursesPerPage*(page-1)). + WillReturnRows(rows) + + logs, err := repo.GetAuxProfessorLogs(courseID, page) + require.NoError(t, err) + require.Len(t, logs, 1) + assert.Equal(t, "added", logs[0].Action) + assert.Equal(t, courseID, logs[0].CourseID) + assert.Equal(t, userID, logs[0].ProfessorID) + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestLogAuxProfessorAction_Success(t *testing.T) { + mock, repo := setupMockRepoForCourses(t) + courseID := uuid.New() + userID := uuid.New() + action := "added" + + mock.ExpectExec(`INSERT INTO aux_professor_logs`). + WithArgs(courseID, userID, action). + WillReturnResult(pgxmock.NewResult("INSERT", 1)) + + err := repo.LogAuxProfessorAction(courseID, userID, action) + require.NoError(t, err) + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestIsAuxProfessor_Success(t *testing.T) { + mock, repo := setupMockRepoForCourses(t) + courseID := uuid.New() + userID := uuid.New() + + mock.ExpectQuery(`SELECT EXISTS \(.*FROM aux_professors`). + WithArgs(courseID, userID). + WillReturnRows(pgxmock.NewRows([]string{"exists"}).AddRow(true)) + + isAux := repo.IsAuxProfessor(courseID, userID) + + assert.True(t, isAux) + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestGetAuxProfessorsByCourse_Success(t *testing.T) { + mock, repo := setupMockRepoForCourses(t) + courseID := uuid.New() + userID1 := uuid.New() + userID2 := uuid.New() + + rows := pgxmock.NewRows([]string{"user_id"}). + AddRow(userID1). + AddRow(userID2) + + mock.ExpectQuery(`SELECT user_id FROM aux_professors WHERE course_id = \$1`). + WithArgs(courseID). + WillReturnRows(rows) + + result, err := repo.GetAuxProfessorsByCourse(courseID) + + assert.NoError(t, err) + assert.Equal(t, []uuid.UUID{userID1, userID2}, result) + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestGetStudentInfo_Success(t *testing.T) { + mock, repo := setupMockRepoForCourses(t) + studentID := uuid.New() + expectedCredits := 120 + + rows := pgxmock.NewRows([]string{"credits"}). + AddRow(expectedCredits) + + mock.ExpectQuery(`SELECT credits FROM students_info WHERE student_id = \$1`). + WithArgs(studentID). + WillReturnRows(rows) + + result, err := repo.GetStudentInfo(studentID) + + assert.NoError(t, err) + assert.NotNil(t, result) + assert.Equal(t, studentID, result.StudentId) + assert.Equal(t, expectedCredits, result.Credits) + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestUpdateStudentInfo_Success(t *testing.T) { + mock, repo := setupMockRepoForCourses(t) + studentID := uuid.New() + info := &models.StudentInfo{ + Credits: 150, + } + + mock.ExpectExec(`INSERT INTO students_info \(student_id, credits\)`). + WithArgs(studentID, info.Credits). + WillReturnResult(pgxmock.NewResult("INSERT", 1)) + + err := repo.UpdateStudentInfo(studentID, info) + + assert.NoError(t, err) + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestSetStudentFinalGrade_Success(t *testing.T) { + mock, repo := setupMockRepoForCourses(t) + + courseID := uuid.New() + studentID := uuid.New() + finalGrade := 87.5 + + mock.ExpectExec(`UPDATE enrollments SET final_grade = \$1`). + WithArgs(finalGrade, courseID, studentID). + WillReturnResult(pgxmock.NewResult("UPDATE", 1)) + + err := repo.SetStudentFinalGrade(courseID, studentID, finalGrade) + + assert.NoError(t, err) + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestGetStudentFinalGrade_Success(t *testing.T) { + mock, repo := setupMockRepoForCourses(t) + + courseID := uuid.New() + studentID := uuid.New() + expectedGrade := 92.3 + + rows := pgxmock.NewRows([]string{"final_grade"}).AddRow(expectedGrade) + mock.ExpectQuery(`SELECT final_grade FROM enrollments WHERE course_id = \$1 AND student_id = \$2`). + WithArgs(courseID, studentID). + WillReturnRows(rows) + + finalGrade, err := repo.GetStudentFinalGrade(courseID, studentID) + + assert.NoError(t, err) + assert.Equal(t, expectedGrade, finalGrade) + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestIsCourseProfessor_Success(t *testing.T) { + mock, repo := setupMockRepoForCourses(t) + + courseID := uuid.New() + userID := uuid.New() + + // Simulate that the user is professor (exists = true) + rows := pgxmock.NewRows([]string{"exists"}).AddRow(true) + mock.ExpectQuery(`SELECT EXISTS \(.+\) OR EXISTS \(.+\)`). + WithArgs(courseID, userID). + WillReturnRows(rows) + + isProfessor, err := repo.IsCourseProfessor(courseID, userID) + + assert.NoError(t, err) + assert.True(t, isProfessor) + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestGetExamsStatsByCourse_Success(t *testing.T) { + mock, repo := setupMockRepoForCourses(t) + + courseID := uuid.New() + + // Prepare mocked row with totalExams, completedExams, averageScore + rows := pgxmock.NewRows([]string{"total_exams", "completed_exams", "avg"}). + AddRow(5, 3, 87.5) + + mock.ExpectQuery(`SELECT DISTINCT COUNT\(e\.id\) AS total_exams,.*`). + WithArgs(courseID). + WillReturnRows(rows) + + total, completed, avgScore, err := repo.GetExamsStatsByCourse(courseID) + + assert.NoError(t, err) + assert.Equal(t, 5, total) + assert.Equal(t, 3, completed) + assert.Equal(t, 87.5, avgScore) + + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestGetAssignmentsStatsByCourse_Success(t *testing.T) { + mock, repo := setupMockRepoForCourses(t) + + courseID := uuid.New() + + rows := pgxmock.NewRows([]string{"total_assignments", "completed_assignments", "avg"}). + AddRow(8, 5, 92.3) + + mock.ExpectQuery(`SELECT COUNT\(a\.id\) AS total_assignments,.*`). + WithArgs(courseID). + WillReturnRows(rows) + + total, completed, avgScore, err := repo.GetAssignmentsStatsByCourse(courseID) + + assert.NoError(t, err) + assert.Equal(t, 8, total) + assert.Equal(t, 5, completed) + assert.InDelta(t, 92.3, avgScore, 0.0001) + + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestGetExamsStatsByStudent_Success(t *testing.T) { + mock, repo := setupMockRepoForCourses(t) + + courseID := uuid.New() + studentID := uuid.New() + + rows := pgxmock.NewRows([]string{"count", "avg"}). + AddRow(7, 87.5) + + mock.ExpectQuery(`SELECT COUNT\(es\.id\),\s+COALESCE\(AVG\(es\.score\), 0\)`). + WithArgs(courseID, studentID). + WillReturnRows(rows) + + total, avgScore, err := repo.GetExamsStatsByStudent(courseID, studentID) + + assert.NoError(t, err) + assert.Equal(t, 7, total) + assert.InDelta(t, 87.5, avgScore, 0.0001) + + assert.NoError(t, mock.ExpectationsWereMet()) +} + +func TestGetAssignmentsStatsByStudent_Success(t *testing.T) { + mock, repo := setupMockRepoForCourses(t) + + courseID := uuid.New() + studentID := uuid.New() + + rows := pgxmock.NewRows([]string{"count", "avg"}). + AddRow(5, 92.3) + + mock.ExpectQuery(`SELECT COUNT\(asub\.id\),\s+COALESCE\(AVG\(asub\.score\), 0\)`). + WithArgs(courseID, studentID). + WillReturnRows(rows) + + total, avgScore, err := repo.GetAssignmentsStatsByStudent(courseID, studentID) + + assert.NoError(t, err) + assert.Equal(t, 5, total) + assert.InDelta(t, 92.3, avgScore, 0.0001) + + assert.NoError(t, mock.ExpectationsWereMet()) +} + /* HELPER FUNCTIONS */ func setupMockRepoForCourses(t *testing.T) (pgxmock.PgxConnIface, repository.CoursesRepository) { From 82ef1eb0538d33c0cf6b3253f809c53243094c76 Mon Sep 17 00:00:00 2001 From: maxogod Date: Mon, 23 Jun 2025 02:28:16 -0300 Subject: [PATCH 5/8] docs: new readme --- README.md | 141 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 139 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1375967..71aeccb 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,143 @@ [![codecov](https://codecov.io/gh/ClassConnect-org/courses-microservice/graph/badge.svg?token=35RXOMF06U)](https://codecov.io/gh/ClassConnect-org/courses-microservice) -### Test Coverage Grid +Este repositorio es parte del backend de la aplicacion class-connect, para conocer informacion mas general sobre +el proyecto visita el siguiente link [Organization Homepage](https://github.com/ClassConnect-org). -![coverage grid](https://codecov.io/gh/ClassConnect-org/courses-microservice/graphs/tree.svg?token=35RXOMF06U) +## Descripcion + +Este microservicio es el nucleo de la aplicacion y se ocupa de todas las operaciones relacionadas con cursos, incluyendo +el manejo de inscripciones, examenes, tareas, modulos organizables por los profesores, estadisticas por curso, entre otras. + +## Endpoints + +Los endpoints de este microservicio se pueden encontrar en el [swagger](172.233.5.227/courses/index.html) del mismo (o alternativamente en la carpeta `docs/`), +los mismos son: + +#### Cursos + +* `GET` **/** (Listar cursos con filtros aplicables) +* `POST` **/** (Agregar curso) + +* `GET` **/aux/logs/{id}** (Obtener registros del profesor auxiliar) +* `PUT` **/aux/{id}/{professor_id}** (Agregar o eliminar profesor auxiliar) + +* `PUT` **/picture/{id}** (Agregar imagen al curso) +* `DELETE` **/picture/{id}** (Eliminar imagen del curso) + +* `GET` **/session** (Listar cursos de la sesión) + +* `PATCH` **/status/{id}** (Cambiar estado del curso) + +* `GET` **/students/{id}** (Obtener información del estudiante) + +* `GET` **/{id}** (Obtener curso por ID) +* `DELETE` **/{id}** (Eliminar curso) +* `PATCH` **/{id}** (Actualizar curso) + +#### Inscripciones + +* `GET` **/enrollments/{id}** (Inscribir estudiante) +* `DELETE` **/enrollments/{id}/{student_id}** (Desinscribir estudiante) + +#### Módulos + +* `PUT` **/modules/attachment/{id}** (Agregar archivo adjunto a un módulo) +* `DELETE` **/modules/attachment/{id}/{attachment_pos}** (Eliminar archivo adjunto de un módulo) + +* `GET` **/modules/{id}** (Obtener módulos por curso) + +* `PUT` **/modules/{id}** (Organizar posiciones de módulos) +* `POST` **/modules/{id}** (Crear un módulo nuevo) +* `DELETE` **/modules/{id}** (Eliminar un módulo) +* `PATCH` **/modules/{id}** (Modificar un módulo existente) + +#### Tareas + +* `GET` **/assignments** (Listar tareas de la sesión actual) + +* `PUT` **/assignments/attachment/{id}** (Agregar archivo adjunto a la tarea) +* `DELETE` **/assignments/attachment/{id}/{attachment_pos}** (Eliminar archivo adjunto de la tarea) + +* `GET` **/assignments/course/{id}** (Listar tareas por curso) + +* `GET` **/assignments/{id}** (Obtener tarea por ID) +* `POST` **/assignments/{id}** (Crear tarea) +* `DELETE` **/assignments/{id}** (Eliminar tarea) +* `PATCH` **/assignments/{id}** (Actualizar tarea) + +#### Entregas de tareas + +* `POST` **/assignments/submission/review/{id}** (Revisar entrega de tarea) + +* `GET` **/assignments/submission/states/{id}** (Obtener estados de la tarea por ID) + +* `GET` **/assignments/submission/{id}** (Obtener entrega de tarea por ID) +* `POST` **/assignments/submission/{id}** (Enviar entrega de tarea) + +#### Exámenes + +* `GET` **/exams** (Obtener exámenes de la sesión) +* `GET` **/exams/course/{id}** (Obtener exámenes por curso) + +* `GET` **/exams/{id}** (Obtener examen por ID) +* `POST` **/exams/{id}** (Crear examen) +* `DELETE` **/exams/{id}** (Eliminar examen) +* `PATCH` **/exams/{id}** (Modificar examen) + +#### Entregas de exámenes + +* `POST` **/exams/submission/review/{id}** (Revisar entrega de examen) + +* `GET` **/exams/submission/states/{id}** (Obtener estados del examen por ID) + +* `GET` **/exams/submission/{id}** (Obtener entrega de examen por ID) +* `POST` **/exams/submission/{id}** (Enviar entrega de examen) + +#### Estadísticas + +* `GET` **/stats/{id}** (Obtener estadísticas del curso) +* `GET` **/stats/{id}/{student_id}** (Obtener estadísticas de un estudiante) + +## Estructura + +Se utiliza la arquitectura package by layer, donde los controladores se pueden encontrar en la +carpeta *handlers*, los servicios en *services* y los repositorios en *repository*. + +Para cada grupo de rutas hay una interfaz para el Handler y otra para el Service. + +En la carpeta *models* se encuentran las representaciones en estructura de datos de cada tabla en la base de datos. +A su vez en la carpeta *dto* se encuentran las representaciones de datos que se envian al cliente y se reciven del mismo. + +Todos los cambios a la base de datos estan registrados en la carpeta *migrations*, donde cada +migracion tiene su `up.sql` (accion creativa) y `down.sql` (accion desctructiva). + +## Desarrollo + +### Docker-compose de uso local + +Para correr el microservicio en un entorno de desarrollo local (con base de datos local incluida), +se puede utilizar el siguiente comando: + +```bash +docker compose -f local-dev-compose.yml up --build +``` + +### Migraciones + +Para crear una migracion para cambiar tablas/entradas de la base de datos, se utiliza el comando go-migrate de la siguiente manera: + +```bash +# Instalar go-migrate si no se tiene instalado +go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest + +# Crear una migracion nueva (alternativamente, crear archivos manualmente con el nombre deseado) +migrate create -ext sql -dir ./migrations -seq migration_name +``` + +## Despliegue + +1. Se hace feature branching desde la rama **dev**. +2. Una vez listo para integrar se realiza PR a **dev**, donde corre el pipeline de testing y coverage. +3. Una vez esta listo para desplegar se realiza PR a **main**, se vuelven a correr pipelines de calidad de codigo. +4. Cuando se hace el push a **main** se ejecuta el pipeline de continous delivery, construye la imagen y la desplega a k8s. From 2f730b116bfcc3f6a0968bae9f5fea77b508e5b0 Mon Sep 17 00:00:00 2001 From: Maximo Utrera <105026197+maxogod@users.noreply.github.com> Date: Mon, 23 Jun 2025 02:29:56 -0300 Subject: [PATCH 6/8] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 71aeccb..7dbc763 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ el manejo de inscripciones, examenes, tareas, modulos organizables por los profe ## Endpoints -Los endpoints de este microservicio se pueden encontrar en el [swagger](172.233.5.227/courses/index.html) del mismo (o alternativamente en la carpeta `docs/`), +Los endpoints de este microservicio se pueden encontrar en el [swagger](http://172.233.5.227/courses/swagger/index.html) del mismo (o alternativamente en la carpeta `docs/`), los mismos son: #### Cursos From e7a58b24cd5b09549fb52acb3be19184a82a47e1 Mon Sep 17 00:00:00 2001 From: Maximo Utrera <105026197+maxogod@users.noreply.github.com> Date: Mon, 23 Jun 2025 02:37:59 -0300 Subject: [PATCH 7/8] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 7dbc763..dee16f2 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,10 @@ el manejo de inscripciones, examenes, tareas, modulos organizables por los profe Los endpoints de este microservicio se pueden encontrar en el [swagger](http://172.233.5.227/courses/swagger/index.html) del mismo (o alternativamente en la carpeta `docs/`), los mismos son: +#### Swagger + +* `GET` /swagger/index.html (Documentacion de la API) + #### Cursos * `GET` **/** (Listar cursos con filtros aplicables) From 3020fc1e158525c9ca7e8ed747403ed06fb4f672 Mon Sep 17 00:00:00 2001 From: maxogod Date: Mon, 23 Jun 2025 03:30:19 -0300 Subject: [PATCH 8/8] ops: datadog APM for extra metrics --- cmd/main.go | 9 +- config/config.go | 12 ++ go.mod | 88 ++++++++-- go.sum | 282 ++++++++++++++++++++++++++++----- helm-chart/values.yaml | 2 + internal/router/router.go | 9 +- internal/router/router_test.go | 2 +- internal/server/server.go | 46 ++++++ 8 files changed, 380 insertions(+), 70 deletions(-) diff --git a/cmd/main.go b/cmd/main.go index 08689db..cee02ba 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -12,7 +12,7 @@ package main import ( "courses-microservice/config" - "courses-microservice/internal/router" + "courses-microservice/internal/server" "log" ) @@ -22,9 +22,8 @@ func main() { if err != nil { log.Fatalf("Failed to connect to the database: %v", err) } - r := router.Create_router(db, conf) - err = r.Run() - if err != nil { - log.Fatalf("Failed to connect to the database: %v", err) + sv := server.NewServer(conf, db) + if err := sv.Run(); err != nil { + log.Fatalf("Failed to run the server: %v", err) } } diff --git a/config/config.go b/config/config.go index bc7733c..3c1a038 100644 --- a/config/config.go +++ b/config/config.go @@ -20,6 +20,8 @@ const ( type Config struct { // Server config ENVIRONMENT Environment + SV_ADDR string + SV_PORT string NOTIFICATIONS_SERVICE string @@ -36,11 +38,18 @@ type Config struct { // Authentication config JWT_SECRET string + + // Datadog + DD_SERVICE_NAME string + DD_AGENT_HOST string + DD_AGENT_PORT string } func LoadConfig() *Config { return &Config{ ENVIRONMENT: getServerEnvironment(), + SV_ADDR: getEnvOrDefault("ADDR", "0.0.0.0"), + SV_PORT: getEnvOrDefault("PORT", "8080"), NOTIFICATIONS_SERVICE: "http://" + getEnvOrDefault("NOTIFICATIONS_SERVICE", "") + ":8000", DB_URL: getEnvOrDefault("DB_URL", ""), STORAGE_URL: getEnvOrDefault("STORAGE_URL", ""), @@ -50,6 +59,9 @@ func LoadConfig() *Config { PICTURES_BUCKET_NAME: getEnvOrDefault("PICTURES_BUCKET_NAME", "course-pictures"), MAX_FILE_SIZE: getEnvOrDefaultInt64("MAX_FILE_SIZE", 10*1024*1024), // Default to 10MB JWT_SECRET: getEnvOrDefault("JWT_SECRET", "default_secret"), + DD_SERVICE_NAME: getEnvOrDefault("DD_SERVICE_NAME", "users-microservice"), + DD_AGENT_HOST: getEnvOrDefault("DD_AGENT_HOST", "localhost"), + DD_AGENT_PORT: getEnvOrDefault("DD_AGENT_PORT", "8126"), } } diff --git a/go.mod b/go.mod index edbd546..fafb706 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module courses-microservice go 1.24.1 require ( - github.com/DATA-DOG/go-sqlmock v1.5.2 github.com/Masterminds/squirrel v1.5.4 github.com/gin-contrib/cors v1.7.5 github.com/gin-gonic/gin v1.10.0 @@ -17,26 +16,53 @@ require ( github.com/swaggo/files v1.0.1 github.com/swaggo/gin-swagger v1.6.0 github.com/swaggo/swag v1.16.4 + gopkg.in/DataDog/dd-trace-go.v1 v1.74.0 ) require ( + github.com/DataDog/appsec-internal-go v1.11.2 // indirect + github.com/DataDog/datadog-agent/comp/core/tagger/origindetection v0.64.2 // indirect + github.com/DataDog/datadog-agent/pkg/obfuscate v0.64.2 // indirect + github.com/DataDog/datadog-agent/pkg/proto v0.64.2 // indirect + github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.64.2 // indirect + github.com/DataDog/datadog-agent/pkg/trace v0.64.2 // indirect + github.com/DataDog/datadog-agent/pkg/util/log v0.64.2 // indirect + github.com/DataDog/datadog-agent/pkg/util/scrubber v0.64.2 // indirect + github.com/DataDog/datadog-agent/pkg/version v0.64.2 // indirect + github.com/DataDog/datadog-go/v5 v5.6.0 // indirect + github.com/DataDog/dd-trace-go/v2 v2.0.0 // indirect + github.com/DataDog/go-libddwaf/v3 v3.5.4 // indirect + github.com/DataDog/go-runtime-metrics-internal v0.0.4-0.20250319104955-81009b9bad14 // indirect + github.com/DataDog/go-sqllexer v0.1.3 // indirect + github.com/DataDog/go-tuf v1.1.0-0.5.2 // indirect + github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.26.0 // indirect + github.com/DataDog/sketches-go v1.4.7 // indirect github.com/KyleBanks/depth v1.2.1 // indirect - github.com/PuerkitoBio/purell v1.1.1 // indirect - github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/Masterminds/semver/v3 v3.3.1 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect github.com/bytedance/sonic v1.13.2 // indirect github.com/bytedance/sonic/loader v0.2.4 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 // indirect github.com/cloudwego/base64x v0.1.5 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/eapache/queue/v2 v2.0.0-20230407133247-75960ed334e4 // indirect + github.com/ebitengine/purego v0.8.3 // indirect github.com/gabriel-vasile/mimetype v1.4.8 // indirect github.com/gin-contrib/sse v1.0.0 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.19.6 // indirect + github.com/go-ole/go-ole v1.3.0 // indirect + github.com/go-openapi/jsonpointer v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/spec v0.20.4 // indirect - github.com/go-openapi/swag v0.19.15 // indirect + github.com/go-openapi/swag v0.23.0 // indirect github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.26.0 // indirect + github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/goccy/go-json v0.10.5 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect @@ -49,23 +75,51 @@ require ( github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/leodido/go-urn v1.4.0 // indirect github.com/lib/pq v1.10.9 // indirect - github.com/mailru/easyjson v0.7.6 // indirect + github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect + github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/outcaste-io/ristretto v0.2.3 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect + github.com/puzpuzpuz/xsync/v3 v3.5.1 // indirect + github.com/secure-systems-lab/go-securesystemslib v0.9.0 // indirect + github.com/shirou/gopsutil/v4 v4.25.3 // indirect + github.com/tinylib/msgp v1.2.5 // indirect + github.com/tklauser/go-sysconf v0.3.15 // indirect + github.com/tklauser/numcpus v0.10.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.2.12 // indirect - go.uber.org/atomic v1.7.0 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect + go.opentelemetry.io/collector/component v1.27.0 // indirect + go.opentelemetry.io/collector/pdata v1.27.0 // indirect + go.opentelemetry.io/collector/pdata/pprofile v0.121.0 // indirect + go.opentelemetry.io/collector/semconv v0.123.0 // indirect + go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/otel/metric v1.35.0 // indirect + go.opentelemetry.io/otel/trace v1.35.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect golang.org/x/arch v0.15.0 // indirect - golang.org/x/crypto v0.36.0 // indirect - golang.org/x/net v0.38.0 // indirect - golang.org/x/sync v0.12.0 // indirect - golang.org/x/sys v0.31.0 // indirect - golang.org/x/text v0.23.0 // indirect - golang.org/x/tools v0.24.0 // indirect + golang.org/x/crypto v0.37.0 // indirect + golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac // indirect + golang.org/x/mod v0.24.0 // indirect + golang.org/x/net v0.39.0 // indirect + golang.org/x/sync v0.13.0 // indirect + golang.org/x/sys v0.32.0 // indirect + golang.org/x/text v0.24.0 // indirect + golang.org/x/time v0.11.0 // indirect + golang.org/x/tools v0.32.0 // indirect + golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250409194420-de1ac958c67a // indirect + google.golang.org/grpc v1.70.0 // indirect google.golang.org/protobuf v1.36.6 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 1905edf..167e0af 100644 --- a/go.sum +++ b/go.sum @@ -1,29 +1,73 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= -github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= -github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= +github.com/DataDog/appsec-internal-go v1.11.2 h1:Q00pPMQzqMIw7jT2ObaORIxBzSly+deS0Ely9OZ/Bj0= +github.com/DataDog/appsec-internal-go v1.11.2/go.mod h1:9YppRCpElfGX+emXOKruShFYsdPq7WEPq/Fen4tYYpk= +github.com/DataDog/datadog-agent/comp/core/tagger/origindetection v0.64.2 h1:wEW+nwoLKubvnLLaxMScYO+rEuHGXmvDsrSV9M3aWdU= +github.com/DataDog/datadog-agent/comp/core/tagger/origindetection v0.64.2/go.mod h1:lzCtnMSGZm/3RMk5RBRW/6IuK1TNbDXx1ttHTxN5Ykc= +github.com/DataDog/datadog-agent/pkg/obfuscate v0.64.2 h1:xyKB0aTD0S0wp17Egqr8gNUL8btuaKC2WK08NT0pCFQ= +github.com/DataDog/datadog-agent/pkg/obfuscate v0.64.2/go.mod h1:izbemZjqzBn9upkZj8SyT9igSGPMALaQYgswJ0408vY= +github.com/DataDog/datadog-agent/pkg/proto v0.64.2 h1:JGnb24mKLi+wEJg/bo5FPf1wli3ca2+owIkACl4mwl4= +github.com/DataDog/datadog-agent/pkg/proto v0.64.2/go.mod h1:q324yHcBN5hIeCU8eoinM7lP9c7MOA2FTj7oeWAl3Pc= +github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.64.2 h1:bCRz9YBvQTJNeE+eAPLEcuz4p/2aStxAO9lgf1HsivI= +github.com/DataDog/datadog-agent/pkg/remoteconfig/state v0.64.2/go.mod h1:1AAhFoEuoXs8jfpj7EiGW6lsqvCYgQc0B0pRpYAPEW4= +github.com/DataDog/datadog-agent/pkg/trace v0.64.2 h1:vuwxRGRVnlFYFUoSK5ZV0sHqskJwxknP5/lV+WfkSSw= +github.com/DataDog/datadog-agent/pkg/trace v0.64.2/go.mod h1:e0wLYMuXKwS/yorq1FqTDGR9WFj9RzwCMwUrli7mCAw= +github.com/DataDog/datadog-agent/pkg/util/log v0.64.2 h1:Sx+L6L2h/HN4UZwAFQMYt4eHkaLHe6THj6GUADLgkm0= +github.com/DataDog/datadog-agent/pkg/util/log v0.64.2/go.mod h1:XDJfRmc5FwFNLDFHtOKX8AW8W1N8Yk+V/wPwj98Zi6Q= +github.com/DataDog/datadog-agent/pkg/util/scrubber v0.64.2 h1:5jGvehYy2VVYJCMED3Dj6zIZds4g0O8PMf5uIMAwoAY= +github.com/DataDog/datadog-agent/pkg/util/scrubber v0.64.2/go.mod h1:uzxlZdxJ2yZZ9k+hDM4PyG3tYacoeneZuh+PVk+IVAw= +github.com/DataDog/datadog-agent/pkg/version v0.64.2 h1:clAPToUGyhFWJIfN6pBR808YigQsDP6hNcpEcu8qbtU= +github.com/DataDog/datadog-agent/pkg/version v0.64.2/go.mod h1:DgOVsfSRaNV4GZNl/qgoZjG3hJjoYUNWPPhbfTfTqtY= +github.com/DataDog/datadog-go/v5 v5.6.0 h1:2oCLxjF/4htd55piM75baflj/KoE6VYS7alEUqFvRDw= +github.com/DataDog/datadog-go/v5 v5.6.0/go.mod h1:K9kcYBlxkcPP8tvvjZZKs/m1edNAUFzBbdpTUKfCsuw= +github.com/DataDog/dd-trace-go/v2 v2.0.0 h1:cHMEzD0Wcgtu+Rec9d1GuVgpIN5f+4vCaNzuFHJ0v+Y= +github.com/DataDog/dd-trace-go/v2 v2.0.0/go.mod h1:WBtf7TA9bWr5uA8DjOyw1qlSKe3bw9gN5nc0Ta9dHFE= +github.com/DataDog/go-libddwaf/v3 v3.5.4 h1:cLV5lmGhrUBnHG50EUXdqPQAlJdVCp9n3aQ5bDWJEAg= +github.com/DataDog/go-libddwaf/v3 v3.5.4/go.mod h1:HoLUHdj0NybsPBth/UppTcg8/DKA4g+AXuk8cZ6nuoo= +github.com/DataDog/go-runtime-metrics-internal v0.0.4-0.20250319104955-81009b9bad14 h1:tc5aVw7OcMyfVmJnrY4IOeiV1RTSaBuJBqF14BXxzIo= +github.com/DataDog/go-runtime-metrics-internal v0.0.4-0.20250319104955-81009b9bad14/go.mod h1:quaQJ+wPN41xEC458FCpTwyROZm3MzmTZ8q8XOXQiPs= +github.com/DataDog/go-sqllexer v0.1.3 h1:Kl2T6QVndMEZqQSY8rkoltYP+LVNaA54N+EwAMc9N5w= +github.com/DataDog/go-sqllexer v0.1.3/go.mod h1:KwkYhpFEVIq+BfobkTC1vfqm4gTi65skV/DpDBXtexc= +github.com/DataDog/go-tuf v1.1.0-0.5.2 h1:4CagiIekonLSfL8GMHRHcHudo1fQnxELS9g4tiAupQ4= +github.com/DataDog/go-tuf v1.1.0-0.5.2/go.mod h1:zBcq6f654iVqmkk8n2Cx81E1JnNTMOAx1UEO/wZR+P0= +github.com/DataDog/gostackparse v0.7.0 h1:i7dLkXHvYzHV308hnkvVGDL3BR4FWl7IsXNPz/IGQh4= +github.com/DataDog/gostackparse v0.7.0/go.mod h1:lTfqcJKqS9KnXQGnyQMCugq3u1FP6UZMfWR0aitKFMM= +github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.26.0 h1:GlvoS6hJN0uANUC3fjx72rOgM4StAKYo2HtQGaasC7s= +github.com/DataDog/opentelemetry-mapping-go/pkg/otlp/attributes v0.26.0/go.mod h1:mYQmU7mbHH6DrCaS8N6GZcxwPoeNfyuopUoLQltwSzs= +github.com/DataDog/sketches-go v1.4.7 h1:eHs5/0i2Sdf20Zkj0udVFWuCrXGRFig2Dcfm5rtcTxc= +github.com/DataDog/sketches-go v1.4.7/go.mod h1:eAmQ/EBmtSO+nQp7IZMZVRPT4BQTmIc5RZQ+deGlTPM= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= +github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4= +github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= +github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ= github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575 h1:kHaBemcxl8o/pQ5VM1c8PVE1PubbNx3mjUr09OqWGCs= +github.com/cihub/seelog v0.0.0-20170130134532-f561c5e57575/go.mod h1:9d6lWj8KzO/fd/NrVaLscBKmPigpZpn5YawRPw+e3Yo= github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dhui/dktest v0.4.5 h1:uUfYBIVREmj/Rw6MvgmqNAYzTiKOHJak+enB5Di73MM= github.com/dhui/dktest v0.4.5/go.mod h1:tmcyeHDKagvlDrz7gDKq4UAJOLIfVZYkfD5OnHDwcCo= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= @@ -34,6 +78,13 @@ github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/eapache/queue/v2 v2.0.0-20230407133247-75960ed334e4 h1:8EXxF+tCLqaVk8AOC29zl2mnhQjwyLxxOTuhUazWRsg= +github.com/eapache/queue/v2 v2.0.0-20230407133247-75960ed334e4/go.mod h1:I5sHm0Y0T1u5YjlyqC5GVArM7aNZRUYtTjmJ8mPJFds= +github.com/ebitengine/purego v0.8.3 h1:K+0AjQp63JEZTEMZiwsI9g0+hAMNohwUOtY0RPGexmc= +github.com/ebitengine/purego v0.8.3/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= @@ -50,16 +101,24 @@ github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= +github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= @@ -68,6 +127,8 @@ github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJn github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= +github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= +github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= @@ -76,9 +137,18 @@ github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeD github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang-migrate/migrate/v4 v4.18.3 h1:EYGkoOsvgHHfm5U/naS1RP/6PL/Xv3S4B/swMiAmDLs= github.com/golang-migrate/migrate/v4 v4.18.3/go.mod h1:99BKpIi6ruaaXRM1A77eqZ+FWPQ3cfRa+ZVy5bmWMaY= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/mock v1.7.0-rc.1 h1:YojYx61/OLFsiv6Rw1Z96LpldJIy31o+UHmwAUMJ6/U= +github.com/golang/mock v1.7.0-rc.1/go.mod h1:s42URUywIqd+OcERslBJvOjepvNymP31m3q8d/GkuRs= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8= +github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -98,14 +168,16 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -118,10 +190,13 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 h1:PpXWgLPs+Fqr325bN2FD2ISlRRztXibcX6e8f5FR5Dc= +github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= @@ -136,25 +211,52 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling v0.120.1 h1:lK/3zr73guK9apbXTcnDnYrC0YCQ25V3CIULYz3k2xU= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/sampling v0.120.1/go.mod h1:01TvyaK8x640crO2iFwW/6CFCZgNsOvOGH3B5J239m0= +github.com/open-telemetry/opentelemetry-collector-contrib/processor/probabilisticsamplerprocessor v0.120.1 h1:TCyOus9tym82PD1VYtthLKMVMlVyRwtDI4ck4SR2+Ok= +github.com/open-telemetry/opentelemetry-collector-contrib/processor/probabilisticsamplerprocessor v0.120.1/go.mod h1:Z/S1brD5gU2Ntht/bHxBVnGxXKTvZDr0dNv/riUzPmY= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/outcaste-io/ristretto v0.2.3 h1:AK4zt/fJ76kjlYObOeNwh4T3asEuaCmp26pOvUOL9w0= +github.com/outcaste-io/ristretto v0.2.3/go.mod h1:W8HywhmtlopSB1jeMg3JtdIhf+DYkLAr0VN/s4+MHac= github.com/pashagolub/pgxmock/v2 v2.12.0 h1:IVRmQtVFNCoq7NOZ+PdfvB6fwnLJmEuWDhnc3yrDxBs= github.com/pashagolub/pgxmock/v2 v2.12.0/go.mod h1:D3YslkN/nJ4+umVqWmbwfSXugJIjPMChkGBG47OJpNw= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= +github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c h1:dAMKvw0MlJT1GshSTtih8C2gDs04w8dReiOGXrGLNoY= +github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= +github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= +github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg= +github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= +github.com/richardartoul/molecule v1.0.1-0.20240531184615-7ca0df43c0b3 h1:4+LEVOB87y175cLJC/mbsgKmoDOjrBldtXvioEy96WY= +github.com/richardartoul/molecule v1.0.1-0.20240531184615-7ca0df43c0b3/go.mod h1:vl5+MqJ1nBINuSsUI2mGgH79UweUT/B5Fy8857PqyyI= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/secure-systems-lab/go-securesystemslib v0.9.0 h1:rf1HIbL64nUpEIZnjLZ3mcNEL9NBPB0iuVjyxvq3LZc= +github.com/secure-systems-lab/go-securesystemslib v0.9.0/go.mod h1:DVHKMcZ+V4/woA/peqr+L0joiRXbPpQ042GgJckkFgw= +github.com/shirou/gopsutil/v4 v4.25.3 h1:SeA68lsu8gLggyMbmCn8cmp97V1TI9ld9sVzAUcKcKE= +github.com/shirou/gopsutil/v4 v4.25.3/go.mod h1:xbuxyoZj+UsgnZrENu3lQivsngRR5BdjbJwf2fv4szA= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= @@ -172,52 +274,132 @@ github.com/swaggo/gin-swagger v1.6.0 h1:y8sxvQ3E20/RCyrXeFfg60r6H0Z+SwpTjMYsMm+z github.com/swaggo/gin-swagger v1.6.0/go.mod h1:BG00cCEy294xtVpyIAHG6+e2Qzj/xKlRdOqDkvq0uzo= github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A= github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg= +github.com/tinylib/msgp v1.2.5 h1:WeQg1whrXRFiZusidTQqzETkRpGjFjcIhW6uqWH09po= +github.com/tinylib/msgp v1.2.5/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0= +github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4= +github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4= +github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso= +github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ= github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/vmihailenco/msgpack/v4 v4.3.13 h1:A2wsiTbvp63ilDaWmsk2wjx6xZdxQOvpiNlKBGKKXKI= +github.com/vmihailenco/msgpack/v4 v4.3.13/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= +github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc= +github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/collector/component v1.27.0 h1:6wk0K23YT9lSprX8BH9x5w8ssAORE109ekH/ix2S614= +go.opentelemetry.io/collector/component v1.27.0/go.mod h1:fIyBHoa7vDyZL3Pcidgy45cx24tBe7iHWne097blGgo= +go.opentelemetry.io/collector/component/componentstatus v0.120.0 h1:hzKjI9+AIl8A/saAARb47JqabWsge0kMp8NSPNiCNOQ= +go.opentelemetry.io/collector/component/componentstatus v0.120.0/go.mod h1:kbuAEddxvcyjGLXGmys3nckAj4jTGC0IqDIEXAOr3Ag= +go.opentelemetry.io/collector/component/componenttest v0.120.0 h1:vKX85d3lpxj/RoiFQNvmIpX9lOS80FY5svzOYUyeYX0= +go.opentelemetry.io/collector/component/componenttest v0.120.0/go.mod h1:QDLboWF2akEqAGyvje8Hc7GfXcrZvQ5FhmlWvD5SkzY= +go.opentelemetry.io/collector/consumer v1.26.0 h1:0MwuzkWFLOm13qJvwW85QkoavnGpR4ZObqCs9g1XAvk= +go.opentelemetry.io/collector/consumer v1.26.0/go.mod h1:I/ZwlWM0sbFLhbStpDOeimjtMbWpMFSoGdVmzYxLGDg= +go.opentelemetry.io/collector/consumer/consumertest v0.120.0 h1:iPFmXygDsDOjqwdQ6YZcTmpiJeQDJX+nHvrjTPsUuv4= +go.opentelemetry.io/collector/consumer/consumertest v0.120.0/go.mod h1:HeSnmPfAEBnjsRR5UY1fDTLlSrYsMsUjufg1ihgnFJ0= +go.opentelemetry.io/collector/consumer/xconsumer v0.120.0 h1:dzM/3KkFfMBIvad+NVXDV+mA+qUpHyu5c70TFOjDg68= +go.opentelemetry.io/collector/consumer/xconsumer v0.120.0/go.mod h1:eOf7RX9CYC7bTZQFg0z2GHdATpQDxI0DP36F9gsvXOQ= +go.opentelemetry.io/collector/pdata v1.27.0 h1:66yI7FYkUDia74h48Fd2/KG2Vk8DxZnGw54wRXykCEU= +go.opentelemetry.io/collector/pdata v1.27.0/go.mod h1:18e8/xDZsqyj00h/5HM5GLdJgBzzG9Ei8g9SpNoiMtI= +go.opentelemetry.io/collector/pdata/pprofile v0.121.0 h1:DFBelDRsZYxEaSoxSRtseAazsHJfqfC/Yl64uPicl2g= +go.opentelemetry.io/collector/pdata/pprofile v0.121.0/go.mod h1:j/fjrd7ybJp/PXkba92QLzx7hykUVmU8x/WJvI2JWSg= +go.opentelemetry.io/collector/pdata/testdata v0.120.0 h1:Zp0LBOv3yzv/lbWHK1oht41OZ4WNbaXb70ENqRY7HnE= +go.opentelemetry.io/collector/pdata/testdata v0.120.0/go.mod h1:PfezW5Rzd13CWwrElTZRrjRTSgMGUOOGLfHeBjj+LwY= +go.opentelemetry.io/collector/pipeline v0.123.0 h1:LDcuCrwhCTx2yROJZqhNmq2v0CFkCkUEvxvvcRW0+2c= +go.opentelemetry.io/collector/pipeline v0.123.0/go.mod h1:TO02zju/K6E+oFIOdi372Wk0MXd+Szy72zcTsFQwXl4= +go.opentelemetry.io/collector/processor v0.120.0 h1:No+I65ybBLVy4jc7CxcsfduiBrm7Z6kGfTnekW3hx1A= +go.opentelemetry.io/collector/processor v0.120.0/go.mod h1:4zaJGLZCK8XKChkwlGC/gn0Dj4Yke04gQCu4LGbJGro= +go.opentelemetry.io/collector/processor/processortest v0.120.0 h1:R+VSVSU59W0/mPAcyt8/h1d0PfWN6JI2KY5KeMICXvo= +go.opentelemetry.io/collector/processor/processortest v0.120.0/go.mod h1:me+IVxPsj4IgK99I0pgKLX34XnJtcLwqtgTuVLhhYDI= +go.opentelemetry.io/collector/processor/xprocessor v0.120.0 h1:mBznj/1MtNqmu6UpcoXz6a63tU0931oWH2pVAt2+hzo= +go.opentelemetry.io/collector/processor/xprocessor v0.120.0/go.mod h1:Nsp0sDR3gE+GAhi9d0KbN0RhOP+BK8CGjBRn8+9d/SY= +go.opentelemetry.io/collector/semconv v0.123.0 h1:hFjhLU1SSmsZ67pXVCVbIaejonkYf5XD/6u4qCQQPtc= +go.opentelemetry.io/collector/semconv v0.123.0/go.mod h1:te6VQ4zZJO5Lp8dM2XIhDxDiL45mwX0YAQQWRQ0Qr9U= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= -go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= -go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= -go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= -go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= -go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= -go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= +go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= +go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/sdk v1.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= +go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= +go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= +go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= +go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= +go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/arch v0.15.0 h1:QtOrQd0bTUnhNVNndMpLHNWrDmYzZ2KDqSrEymqInZw= golang.org/x/arch v0.15.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= -golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= +golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= +golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac h1:l5+whBCLH3iH2ZNHYLbAe58bo7yrN4mVcnkHDYz5vvs= +golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac/go.mod h1:hH+7mtFmImwwcMvScyxUhjuVHR3HGaDPMn9rMSUUbxo= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= -golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= +golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= -golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= +golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= +golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -226,21 +408,41 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= +golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= +golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU= +golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250409194420-de1ac958c67a h1:GIqLhp/cYUkuGuiT+vJk8vhOP86L4+SP5j8yXgeVpvI= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250409194420-de1ac958c67a/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= +gopkg.in/DataDog/dd-trace-go.v1 v1.74.0 h1:wScziU1ff6Bnyr8MEyxATPSLJdnLxKz3p6RsA8FUaek= +gopkg.in/DataDog/dd-trace-go.v1 v1.74.0/go.mod h1:ReNBsNfnsjVC7GsCe80zRcykL/n+nxvsNrg3NbjuleM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= @@ -248,4 +450,6 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U= +k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= diff --git a/helm-chart/values.yaml b/helm-chart/values.yaml index b91e2bf..79d779a 100644 --- a/helm-chart/values.yaml +++ b/helm-chart/values.yaml @@ -12,3 +12,5 @@ env: ENVIRONMENT: "production" STORAGE_URL: https://ubyzxhhpotxnttorsrpo.supabase.co/storage/v1/object NOTIFICATIONS_SERVICE: "notifications-microservice" + DD_SERVICE_NAME: "courses-microservice" + DD_AGENT_HOST: "datadog-agent" diff --git a/internal/router/router.go b/internal/router/router.go index a2cd07c..e79f5af 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -4,20 +4,13 @@ import ( "courses-microservice/config" "courses-microservice/internal/middleware" "courses-microservice/internal/repository" - "log" "github.com/gin-contrib/cors" "github.com/gin-gonic/gin" "github.com/jackc/pgx/v5/pgxpool" ) -func Create_router(db *pgxpool.Pool, conf *config.Config) *gin.Engine { - if conf.ENVIRONMENT == config.PRODUCTION { - gin.SetMode(gin.ReleaseMode) - log.Println("Running server in PRODUCTION environment") - } else { - log.Println("Running server in DEVELOPMENT environment") - } +func SetupRouter(db *pgxpool.Pool, conf *config.Config) *gin.Engine { router := gin.Default() router.Use(cors.Default()) router.Use(middleware.ErrorHandlerMiddleware()) diff --git a/internal/router/router_test.go b/internal/router/router_test.go index 12d21ae..7457d5b 100644 --- a/internal/router/router_test.go +++ b/internal/router/router_test.go @@ -13,7 +13,7 @@ func TestSetupRouter_RegistersExpectedRoutes(t *testing.T) { conf := &config.Config{ ENVIRONMENT: config.DEVELOPMENT, } - router := router.Create_router(nil, conf) + router := router.SetupRouter(nil, conf) expectedRoutes := []string{ // Courses diff --git a/internal/server/server.go b/internal/server/server.go index abb4e43..901bd00 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -1 +1,47 @@ package server + +import ( + "courses-microservice/config" + "courses-microservice/internal/logging" + "courses-microservice/internal/router" + "fmt" + + "github.com/gin-gonic/gin" + "github.com/jackc/pgx/v5/pgxpool" + "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer" +) + +type server struct { + conf *config.Config + db *pgxpool.Pool +} + +func NewServer(conf *config.Config, db *pgxpool.Pool) *server { + return &server{ + conf: conf, + db: db, + } +} + +func (s *server) Run() error { + if s.conf.ENVIRONMENT == config.PRODUCTION { + gin.SetMode(gin.ReleaseMode) + tracer.Start( + tracer.WithEnv("production"), + tracer.WithServiceName(s.conf.DD_SERVICE_NAME), + tracer.WithAgentAddr(fmt.Sprintf("%s:%s", s.conf.DD_AGENT_HOST, s.conf.DD_AGENT_PORT)), + ) + defer tracer.Stop() + + logging.GetLogger().Infoln("Running server in PRODUCTION environment") + } else { + logging.GetLogger().Infoln("Running server in DEVELOPMENT environment") + } + + r := router.SetupRouter(s.db, s.conf) + + addr := fmt.Sprintf("%s:%s", s.conf.SV_ADDR, s.conf.SV_PORT) + logging.GetLogger().Infoln("Starting server on", addr) + + return r.Run(addr) +}