diff --git a/docs/docs.go b/docs/docs.go index e207ffb..03e3943 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -231,37 +231,22 @@ const docTemplate = `{ }, "/api/v1/assessment/questions": { "get": { - "description": "Returns all active questions used for initial knowledge assessment", - "consumes": [ - "application/json" - ], + "description": "Returns all active assessment questions with their options or answers", "produces": [ "application/json" ], "tags": [ - "assessment" + "assessment-question" ], - "summary": "Get active initial assessment questions", + "summary": "List assessment questions", "responses": { "200": { "description": "OK", "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.AssessmentQuestion" - } - } - } - } - ] + "type": "array", + "items": { + "$ref": "#/definitions/domain.QuestionWithDetails" + } } }, "500": { @@ -273,7 +258,7 @@ const docTemplate = `{ } }, "post": { - "description": "Creates a new question for the initial knowledge assessment", + "description": "Creates a new assessment question with options or short answer depending on question type", "consumes": [ "application/json" ], @@ -281,17 +266,17 @@ const docTemplate = `{ "application/json" ], "tags": [ - "assessment" + "assessment-question" ], "summary": "Create assessment question", "parameters": [ { - "description": "Assessment question payload", - "name": "question", + "description": "Create question payload", + "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.AssessmentQuestion" + "$ref": "#/definitions/domain.CreateAssessmentQuestionInput" } } ], @@ -299,19 +284,7 @@ const docTemplate = `{ "201": { "description": "Created", "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/domain.AssessmentQuestion" - } - } - } - ] + "$ref": "#/definitions/domain.Response" } }, "400": { @@ -329,6 +302,71 @@ const docTemplate = `{ } } }, + "/api/v1/assessment/questions/{id}": { + "get": { + "description": "Returns a single assessment question with its options or answer", + "produces": [ + "application/json" + ], + "tags": [ + "assessment-question" + ], + "summary": "Get assessment question by ID", + "parameters": [ + { + "type": "integer", + "description": "Question ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/domain.QuestionWithDetails" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/google/callback": { + "get": { + "tags": [ + "auth" + ], + "summary": "Google login callback", + "responses": {} + } + }, + "/api/v1/auth/google/login": { + "get": { + "tags": [ + "auth" + ], + "summary": "Google login redirect", + "responses": {} + } + }, "/api/v1/auth/logout": { "post": { "description": "Logout user", @@ -1504,7 +1542,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/authentication.LoginRequest" + "$ref": "#/definitions/domain.LoginRequest" } } ], @@ -1682,6 +1720,65 @@ const docTemplate = `{ } } }, + "/api/v1/user": { + "put": { + "description": "Updates user profile information (partial updates supported)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "user" + ], + "summary": "Update user profile", + "parameters": [ + { + "type": "integer", + "description": "User ID", + "name": "user_id", + "in": "path", + "required": true + }, + { + "description": "Update user payload", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/domain.UpdateUserReq" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/domain.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + } + } + } + }, "/api/v1/user/delete/{id}": { "delete": { "description": "Delete a user by their ID", @@ -1960,9 +2057,9 @@ const docTemplate = `{ } } }, - "/api/v1/user/{user_name}/is-unique": { + "/api/v1/user/{user_id}/is-pending": { "get": { - "description": "Returns whether the specified user_name is available (unique)", + "description": "Returns whether the specified user has a status of \"pending\"", "consumes": [ "application/json" ], @@ -1972,12 +2069,12 @@ const docTemplate = `{ "tags": [ "user" ], - "summary": "Check if user_name is unique", + "summary": "Check if user status is pending", "parameters": [ { "type": "string", - "description": "User Name", - "name": "user_name", + "description": "User ID", + "name": "user_id", "in": "path", "required": true } @@ -1995,6 +2092,12 @@ const docTemplate = `{ "$ref": "#/definitions/domain.ErrorResponse" } }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + }, "500": { "description": "Internal Server Error", "schema": { @@ -2004,6 +2107,129 @@ const docTemplate = `{ } } }, + "/api/v1/user/{user_id}/is-profile-completed": { + "get": { + "description": "Returns whether the specified user's profile is completed", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "user" + ], + "summary": "Check if user profile is completed", + "parameters": [ + { + "type": "integer", + "description": "User ID", + "name": "user_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/domain.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + } + } + } + }, + "/api/v1/users": { + "get": { + "description": "Get users with optional filters", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "user" + ], + "summary": "Get all users", + "parameters": [ + { + "type": "string", + "description": "Role filter", + "name": "role", + "in": "query" + }, + { + "type": "string", + "description": "Search query", + "name": "query", + "in": "query" + }, + { + "type": "integer", + "description": "Page number", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size", + "name": "page_size", + "in": "query" + }, + { + "type": "string", + "description": "Created before (RFC3339)", + "name": "created_before", + "in": "query" + }, + { + "type": "string", + "description": "Created after (RFC3339)", + "name": "created_after", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.APIResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.APIResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.APIResponse" + } + } + } + } + }, "/api/v1/{tenant_slug}/admin-login": { "post": { "description": "Login user", @@ -2024,7 +2250,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/authentication.LoginRequest" + "$ref": "#/definitions/domain.LoginRequest" } } ], @@ -2102,65 +2328,6 @@ const docTemplate = `{ } } }, - "/api/v1/{tenant_slug}/user": { - "put": { - "description": "Updates user profile information (partial updates supported)", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "user" - ], - "summary": "Update user profile", - "parameters": [ - { - "type": "integer", - "description": "User ID", - "name": "user_id", - "in": "path", - "required": true - }, - { - "description": "Update user payload", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.UpdateUserReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "404": { - "description": "Not Found", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, "/api/v1/{tenant_slug}/user-login": { "post": { "description": "Login user", @@ -2181,7 +2348,7 @@ const docTemplate = `{ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/authentication.LoginRequest" + "$ref": "#/definitions/domain.LoginRequest" } } ], @@ -2581,188 +2748,59 @@ const docTemplate = `{ } } } - }, - "/api/v1/{tenant_slug}/user/{user_name}/is-pending": { - "get": { - "description": "Returns whether the specified user has a status of \"pending\"", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "user" - ], - "summary": "Check if user status is pending", - "parameters": [ - { - "type": "string", - "description": "User Name", - "name": "user_name", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "404": { - "description": "Not Found", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/users": { - "get": { - "description": "Get users with optional filters", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "user" - ], - "summary": "Get all users", - "parameters": [ - { - "type": "string", - "description": "Role filter", - "name": "role", - "in": "query" - }, - { - "type": "string", - "description": "Search query", - "name": "query", - "in": "query" - }, - { - "type": "integer", - "description": "Page number", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Page size", - "name": "page_size", - "in": "query" - }, - { - "type": "string", - "description": "Created before (RFC3339)", - "name": "created_before", - "in": "query" - }, - { - "type": "string", - "description": "Created after (RFC3339)", - "name": "created_after", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } } }, "definitions": { - "authentication.LoginRequest": { - "type": "object", - "properties": { - "email": { - "type": "string" - }, - "otp_code": { - "type": "string" - }, - "password": { - "type": "string" - }, - "phone_number": { - "type": "string" - } - } - }, - "domain.AssessmentOption": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "isCorrect": { - "type": "boolean" - }, - "optionText": { - "type": "string" - } - } + "domain.AgeGroup": { + "type": "string", + "enum": [ + "UNDER_13", + "13_17", + "18_24", + "25_34", + "35_44", + "45_54", + "55_PLUS" + ], + "x-enum-varnames": [ + "AgeUnder13", + "Age13To17", + "Age18To24", + "Age25To34", + "Age35To44", + "Age45To54", + "Age55Plus" + ] }, "domain.AssessmentQuestion": { "type": "object", "properties": { + "created_at": { + "type": "string" + }, "description": { "type": "string" }, - "difficultyLevel": { + "difficulty_level": { "type": "string" }, "id": { - "type": "integer", - "format": "int64" + "type": "integer" }, - "options": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.AssessmentOption" - } + "is_active": { + "type": "boolean" }, - "questionType": { + "points": { + "type": "integer" + }, + "question_type": { "type": "string" }, "title": { "type": "string" + }, + "updated_at": { + "type": "string" } } }, @@ -2807,6 +2845,56 @@ const docTemplate = `{ } } }, + "domain.CreateAssessmentQuestionInput": { + "type": "object", + "properties": { + "correctAnswer": { + "description": "Short Answer only", + "type": "string" + }, + "description": { + "type": "string" + }, + "difficultyLevel": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "options": { + "description": "Multiple Choice only", + "type": "array", + "items": { + "$ref": "#/definitions/domain.CreateQuestionOptionInput" + } + }, + "points": { + "type": "integer", + "format": "int32" + }, + "questionType": { + "$ref": "#/definitions/domain.QuestionType" + }, + "title": { + "type": "string" + } + } + }, + "domain.CreateQuestionOptionInput": { + "type": "object", + "properties": { + "isCorrect": { + "type": "boolean" + }, + "order": { + "type": "integer", + "format": "int32" + }, + "text": { + "type": "string" + } + } + }, "domain.ErrorResponse": { "type": "object", "properties": { @@ -2901,6 +2989,23 @@ const docTemplate = `{ } } }, + "domain.LoginRequest": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "otp_code": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone_number": { + "type": "string" + } + } + }, "domain.Module": { "type": "object", "properties": { @@ -3104,45 +3209,50 @@ const docTemplate = `{ } } }, + "domain.QuestionOption": { + "type": "object", + "properties": { + "option_text": { + "type": "string" + }, + "question_id": { + "type": "integer" + } + } + }, + "domain.QuestionType": { + "type": "string", + "enum": [ + "MULTIPLE_CHOICE", + "TRUE_FALSE", + "SHORT_ANSWER" + ], + "x-enum-varnames": [ + "MultipleChoice", + "TrueFalse", + "ShortAnswer" + ] + }, + "domain.QuestionWithDetails": { + "type": "object", + "properties": { + "options": { + "type": "array", + "items": { + "$ref": "#/definitions/domain.QuestionOption" + } + }, + "question": { + "$ref": "#/definitions/domain.AssessmentQuestion" + } + } + }, "domain.RegisterUserReq": { "type": "object", "properties": { - "age": { - "type": "integer" - }, - "country": { - "type": "string" - }, - "education_level": { - "type": "string" - }, "email": { "type": "string" }, - "favoutite_topic": { - "type": "string" - }, - "first_name": { - "type": "string" - }, - "language_challange": { - "type": "string" - }, - "language_goal": { - "type": "string" - }, - "last_name": { - "type": "string" - }, - "learning_goal": { - "type": "string" - }, - "nick_name": { - "type": "string" - }, - "occupation": { - "type": "string" - }, "otp_medium": { "$ref": "#/definitions/domain.OtpMedium" }, @@ -3152,17 +3262,8 @@ const docTemplate = `{ "phone_number": { "type": "string" }, - "preferred_language": { - "type": "string" - }, - "region": { - "type": "string" - }, "role": { "type": "string" - }, - "user_name": { - "type": "string" } } }, @@ -3225,76 +3326,74 @@ const docTemplate = `{ "domain.UpdateUserReq": { "type": "object", "properties": { - "age": { - "$ref": "#/definitions/domain.ValidInt" + "age_group": { + "$ref": "#/definitions/domain.AgeGroup" + }, + "birth_day": { + "description": "YYYY-MM-DD", + "type": "string" }, "country": { - "$ref": "#/definitions/domain.ValidString" + "type": "string" }, - "educationLevel": { - "$ref": "#/definitions/domain.ValidString" + "education_level": { + "type": "string" }, - "favoutiteTopic": { - "$ref": "#/definitions/domain.ValidString" + "favourite_topic": { + "type": "string" }, - "firstName": { - "$ref": "#/definitions/domain.ValidString" + "first_name": { + "type": "string" }, - "knowledgeLevel": { - "description": "Profile fields", - "allOf": [ - { - "$ref": "#/definitions/domain.ValidString" - } - ] + "gender": { + "type": "string" }, - "languageChallange": { - "$ref": "#/definitions/domain.ValidString" + "initial_assessment_completed": { + "type": "boolean" }, - "languageGoal": { - "$ref": "#/definitions/domain.ValidString" + "knowledge_level": { + "type": "string" }, - "lastName": { - "$ref": "#/definitions/domain.ValidString" + "language_challange": { + "type": "string" }, - "learningGoal": { - "$ref": "#/definitions/domain.ValidString" + "language_goal": { + "type": "string" }, - "nickName": { - "$ref": "#/definitions/domain.ValidString" + "last_name": { + "type": "string" + }, + "learning_goal": { + "type": "string" + }, + "nick_name": { + "type": "string" }, "occupation": { - "$ref": "#/definitions/domain.ValidString" + "type": "string" }, - "preferredLanguage": { - "$ref": "#/definitions/domain.ValidString" + "preferred_language": { + "type": "string" }, - "profileCompleted": { - "$ref": "#/definitions/domain.ValidBool" + "profile_completed": { + "type": "boolean" }, - "profilePictureURL": { - "$ref": "#/definitions/domain.ValidString" + "profile_picture_url": { + "type": "string" }, "region": { - "$ref": "#/definitions/domain.ValidString" - }, - "status": { - "$ref": "#/definitions/domain.ValidString" - }, - "userID": { - "type": "integer", - "format": "int64" - }, - "userName": { - "$ref": "#/definitions/domain.ValidString" + "type": "string" } } }, "domain.UserProfileResponse": { "type": "object", "properties": { - "age": { - "type": "integer" + "age_group": { + "type": "string" + }, + "birth_day": { + "type": "string" }, "country": { "type": "string" @@ -3306,6 +3405,7 @@ const docTemplate = `{ "type": "string" }, "email": { + "description": "UserName string ` + "`" + `json:\"user_name,omitempty\"` + "`" + `", "type": "string" }, "email_verified": { @@ -3317,6 +3417,9 @@ const docTemplate = `{ "first_name": { "type": "string" }, + "gender": { + "type": "string" + }, "id": { "type": "integer" }, @@ -3371,9 +3474,6 @@ const docTemplate = `{ }, "updated_at": { "type": "string" - }, - "user_name": { - "type": "string" } } }, @@ -3392,39 +3492,6 @@ const docTemplate = `{ "UserStatusDeactivated" ] }, - "domain.ValidBool": { - "type": "object", - "properties": { - "valid": { - "type": "boolean" - }, - "value": { - "type": "boolean" - } - } - }, - "domain.ValidInt": { - "type": "object", - "properties": { - "valid": { - "type": "boolean" - }, - "value": { - "type": "integer" - } - } - }, - "domain.ValidString": { - "type": "object", - "properties": { - "valid": { - "type": "boolean" - }, - "value": { - "type": "string" - } - } - }, "domain.VerifyOtpReq": { "type": "object", "required": [ @@ -3620,27 +3687,7 @@ const docTemplate = `{ } }, "handlers.ResetPasswordReq": { - "type": "object", - "required": [ - "otp", - "password", - "user_name" - ], - "properties": { - "otp": { - "type": "string", - "example": "123456" - }, - "password": { - "type": "string", - "minLength": 8, - "example": "newpassword123" - }, - "user_name": { - "type": "string", - "example": "johndoe" - } - } + "type": "object" }, "handlers.SearchUserByNameOrPhoneReq": { "type": "object", diff --git a/docs/swagger.json b/docs/swagger.json index 8b402ec..a2149dd 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -223,37 +223,22 @@ }, "/api/v1/assessment/questions": { "get": { - "description": "Returns all active questions used for initial knowledge assessment", - "consumes": [ - "application/json" - ], + "description": "Returns all active assessment questions with their options or answers", "produces": [ "application/json" ], "tags": [ - "assessment" + "assessment-question" ], - "summary": "Get active initial assessment questions", + "summary": "List assessment questions", "responses": { "200": { "description": "OK", "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.AssessmentQuestion" - } - } - } - } - ] + "type": "array", + "items": { + "$ref": "#/definitions/domain.QuestionWithDetails" + } } }, "500": { @@ -265,7 +250,7 @@ } }, "post": { - "description": "Creates a new question for the initial knowledge assessment", + "description": "Creates a new assessment question with options or short answer depending on question type", "consumes": [ "application/json" ], @@ -273,17 +258,17 @@ "application/json" ], "tags": [ - "assessment" + "assessment-question" ], "summary": "Create assessment question", "parameters": [ { - "description": "Assessment question payload", - "name": "question", + "description": "Create question payload", + "name": "body", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/domain.AssessmentQuestion" + "$ref": "#/definitions/domain.CreateAssessmentQuestionInput" } } ], @@ -291,19 +276,7 @@ "201": { "description": "Created", "schema": { - "allOf": [ - { - "$ref": "#/definitions/domain.Response" - }, - { - "type": "object", - "properties": { - "data": { - "$ref": "#/definitions/domain.AssessmentQuestion" - } - } - } - ] + "$ref": "#/definitions/domain.Response" } }, "400": { @@ -321,6 +294,71 @@ } } }, + "/api/v1/assessment/questions/{id}": { + "get": { + "description": "Returns a single assessment question with its options or answer", + "produces": [ + "application/json" + ], + "tags": [ + "assessment-question" + ], + "summary": "Get assessment question by ID", + "parameters": [ + { + "type": "integer", + "description": "Question ID", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/domain.QuestionWithDetails" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + } + } + } + }, + "/api/v1/auth/google/callback": { + "get": { + "tags": [ + "auth" + ], + "summary": "Google login callback", + "responses": {} + } + }, + "/api/v1/auth/google/login": { + "get": { + "tags": [ + "auth" + ], + "summary": "Google login redirect", + "responses": {} + } + }, "/api/v1/auth/logout": { "post": { "description": "Logout user", @@ -1496,7 +1534,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/authentication.LoginRequest" + "$ref": "#/definitions/domain.LoginRequest" } } ], @@ -1674,6 +1712,65 @@ } } }, + "/api/v1/user": { + "put": { + "description": "Updates user profile information (partial updates supported)", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "user" + ], + "summary": "Update user profile", + "parameters": [ + { + "type": "integer", + "description": "User ID", + "name": "user_id", + "in": "path", + "required": true + }, + { + "description": "Update user payload", + "name": "body", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/domain.UpdateUserReq" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/domain.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + } + } + } + }, "/api/v1/user/delete/{id}": { "delete": { "description": "Delete a user by their ID", @@ -1952,9 +2049,9 @@ } } }, - "/api/v1/user/{user_name}/is-unique": { + "/api/v1/user/{user_id}/is-pending": { "get": { - "description": "Returns whether the specified user_name is available (unique)", + "description": "Returns whether the specified user has a status of \"pending\"", "consumes": [ "application/json" ], @@ -1964,12 +2061,12 @@ "tags": [ "user" ], - "summary": "Check if user_name is unique", + "summary": "Check if user status is pending", "parameters": [ { "type": "string", - "description": "User Name", - "name": "user_name", + "description": "User ID", + "name": "user_id", "in": "path", "required": true } @@ -1987,6 +2084,12 @@ "$ref": "#/definitions/domain.ErrorResponse" } }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + }, "500": { "description": "Internal Server Error", "schema": { @@ -1996,6 +2099,129 @@ } } }, + "/api/v1/user/{user_id}/is-profile-completed": { + "get": { + "description": "Returns whether the specified user's profile is completed", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "user" + ], + "summary": "Check if user profile is completed", + "parameters": [ + { + "type": "integer", + "description": "User ID", + "name": "user_id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/domain.Response" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/domain.ErrorResponse" + } + } + } + } + }, + "/api/v1/users": { + "get": { + "description": "Get users with optional filters", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "user" + ], + "summary": "Get all users", + "parameters": [ + { + "type": "string", + "description": "Role filter", + "name": "role", + "in": "query" + }, + { + "type": "string", + "description": "Search query", + "name": "query", + "in": "query" + }, + { + "type": "integer", + "description": "Page number", + "name": "page", + "in": "query" + }, + { + "type": "integer", + "description": "Page size", + "name": "page_size", + "in": "query" + }, + { + "type": "string", + "description": "Created before (RFC3339)", + "name": "created_before", + "in": "query" + }, + { + "type": "string", + "description": "Created after (RFC3339)", + "name": "created_after", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/response.APIResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/response.APIResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/response.APIResponse" + } + } + } + } + }, "/api/v1/{tenant_slug}/admin-login": { "post": { "description": "Login user", @@ -2016,7 +2242,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/authentication.LoginRequest" + "$ref": "#/definitions/domain.LoginRequest" } } ], @@ -2094,65 +2320,6 @@ } } }, - "/api/v1/{tenant_slug}/user": { - "put": { - "description": "Updates user profile information (partial updates supported)", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "user" - ], - "summary": "Update user profile", - "parameters": [ - { - "type": "integer", - "description": "User ID", - "name": "user_id", - "in": "path", - "required": true - }, - { - "description": "Update user payload", - "name": "body", - "in": "body", - "required": true, - "schema": { - "$ref": "#/definitions/domain.UpdateUserReq" - } - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "404": { - "description": "Not Found", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, "/api/v1/{tenant_slug}/user-login": { "post": { "description": "Login user", @@ -2173,7 +2340,7 @@ "in": "body", "required": true, "schema": { - "$ref": "#/definitions/authentication.LoginRequest" + "$ref": "#/definitions/domain.LoginRequest" } } ], @@ -2573,188 +2740,59 @@ } } } - }, - "/api/v1/{tenant_slug}/user/{user_name}/is-pending": { - "get": { - "description": "Returns whether the specified user has a status of \"pending\"", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "user" - ], - "summary": "Check if user status is pending", - "parameters": [ - { - "type": "string", - "description": "User Name", - "name": "user_name", - "in": "path", - "required": true - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/domain.Response" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "404": { - "description": "Not Found", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/domain.ErrorResponse" - } - } - } - } - }, - "/api/v1/{tenant_slug}/users": { - "get": { - "description": "Get users with optional filters", - "consumes": [ - "application/json" - ], - "produces": [ - "application/json" - ], - "tags": [ - "user" - ], - "summary": "Get all users", - "parameters": [ - { - "type": "string", - "description": "Role filter", - "name": "role", - "in": "query" - }, - { - "type": "string", - "description": "Search query", - "name": "query", - "in": "query" - }, - { - "type": "integer", - "description": "Page number", - "name": "page", - "in": "query" - }, - { - "type": "integer", - "description": "Page size", - "name": "page_size", - "in": "query" - }, - { - "type": "string", - "description": "Created before (RFC3339)", - "name": "created_before", - "in": "query" - }, - { - "type": "string", - "description": "Created after (RFC3339)", - "name": "created_after", - "in": "query" - } - ], - "responses": { - "200": { - "description": "OK", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "400": { - "description": "Bad Request", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - }, - "500": { - "description": "Internal Server Error", - "schema": { - "$ref": "#/definitions/response.APIResponse" - } - } - } - } } }, "definitions": { - "authentication.LoginRequest": { - "type": "object", - "properties": { - "email": { - "type": "string" - }, - "otp_code": { - "type": "string" - }, - "password": { - "type": "string" - }, - "phone_number": { - "type": "string" - } - } - }, - "domain.AssessmentOption": { - "type": "object", - "properties": { - "id": { - "type": "integer", - "format": "int64" - }, - "isCorrect": { - "type": "boolean" - }, - "optionText": { - "type": "string" - } - } + "domain.AgeGroup": { + "type": "string", + "enum": [ + "UNDER_13", + "13_17", + "18_24", + "25_34", + "35_44", + "45_54", + "55_PLUS" + ], + "x-enum-varnames": [ + "AgeUnder13", + "Age13To17", + "Age18To24", + "Age25To34", + "Age35To44", + "Age45To54", + "Age55Plus" + ] }, "domain.AssessmentQuestion": { "type": "object", "properties": { + "created_at": { + "type": "string" + }, "description": { "type": "string" }, - "difficultyLevel": { + "difficulty_level": { "type": "string" }, "id": { - "type": "integer", - "format": "int64" + "type": "integer" }, - "options": { - "type": "array", - "items": { - "$ref": "#/definitions/domain.AssessmentOption" - } + "is_active": { + "type": "boolean" }, - "questionType": { + "points": { + "type": "integer" + }, + "question_type": { "type": "string" }, "title": { "type": "string" + }, + "updated_at": { + "type": "string" } } }, @@ -2799,6 +2837,56 @@ } } }, + "domain.CreateAssessmentQuestionInput": { + "type": "object", + "properties": { + "correctAnswer": { + "description": "Short Answer only", + "type": "string" + }, + "description": { + "type": "string" + }, + "difficultyLevel": { + "type": "string" + }, + "isActive": { + "type": "boolean" + }, + "options": { + "description": "Multiple Choice only", + "type": "array", + "items": { + "$ref": "#/definitions/domain.CreateQuestionOptionInput" + } + }, + "points": { + "type": "integer", + "format": "int32" + }, + "questionType": { + "$ref": "#/definitions/domain.QuestionType" + }, + "title": { + "type": "string" + } + } + }, + "domain.CreateQuestionOptionInput": { + "type": "object", + "properties": { + "isCorrect": { + "type": "boolean" + }, + "order": { + "type": "integer", + "format": "int32" + }, + "text": { + "type": "string" + } + } + }, "domain.ErrorResponse": { "type": "object", "properties": { @@ -2893,6 +2981,23 @@ } } }, + "domain.LoginRequest": { + "type": "object", + "properties": { + "email": { + "type": "string" + }, + "otp_code": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone_number": { + "type": "string" + } + } + }, "domain.Module": { "type": "object", "properties": { @@ -3096,45 +3201,50 @@ } } }, + "domain.QuestionOption": { + "type": "object", + "properties": { + "option_text": { + "type": "string" + }, + "question_id": { + "type": "integer" + } + } + }, + "domain.QuestionType": { + "type": "string", + "enum": [ + "MULTIPLE_CHOICE", + "TRUE_FALSE", + "SHORT_ANSWER" + ], + "x-enum-varnames": [ + "MultipleChoice", + "TrueFalse", + "ShortAnswer" + ] + }, + "domain.QuestionWithDetails": { + "type": "object", + "properties": { + "options": { + "type": "array", + "items": { + "$ref": "#/definitions/domain.QuestionOption" + } + }, + "question": { + "$ref": "#/definitions/domain.AssessmentQuestion" + } + } + }, "domain.RegisterUserReq": { "type": "object", "properties": { - "age": { - "type": "integer" - }, - "country": { - "type": "string" - }, - "education_level": { - "type": "string" - }, "email": { "type": "string" }, - "favoutite_topic": { - "type": "string" - }, - "first_name": { - "type": "string" - }, - "language_challange": { - "type": "string" - }, - "language_goal": { - "type": "string" - }, - "last_name": { - "type": "string" - }, - "learning_goal": { - "type": "string" - }, - "nick_name": { - "type": "string" - }, - "occupation": { - "type": "string" - }, "otp_medium": { "$ref": "#/definitions/domain.OtpMedium" }, @@ -3144,17 +3254,8 @@ "phone_number": { "type": "string" }, - "preferred_language": { - "type": "string" - }, - "region": { - "type": "string" - }, "role": { "type": "string" - }, - "user_name": { - "type": "string" } } }, @@ -3217,76 +3318,74 @@ "domain.UpdateUserReq": { "type": "object", "properties": { - "age": { - "$ref": "#/definitions/domain.ValidInt" + "age_group": { + "$ref": "#/definitions/domain.AgeGroup" + }, + "birth_day": { + "description": "YYYY-MM-DD", + "type": "string" }, "country": { - "$ref": "#/definitions/domain.ValidString" + "type": "string" }, - "educationLevel": { - "$ref": "#/definitions/domain.ValidString" + "education_level": { + "type": "string" }, - "favoutiteTopic": { - "$ref": "#/definitions/domain.ValidString" + "favourite_topic": { + "type": "string" }, - "firstName": { - "$ref": "#/definitions/domain.ValidString" + "first_name": { + "type": "string" }, - "knowledgeLevel": { - "description": "Profile fields", - "allOf": [ - { - "$ref": "#/definitions/domain.ValidString" - } - ] + "gender": { + "type": "string" }, - "languageChallange": { - "$ref": "#/definitions/domain.ValidString" + "initial_assessment_completed": { + "type": "boolean" }, - "languageGoal": { - "$ref": "#/definitions/domain.ValidString" + "knowledge_level": { + "type": "string" }, - "lastName": { - "$ref": "#/definitions/domain.ValidString" + "language_challange": { + "type": "string" }, - "learningGoal": { - "$ref": "#/definitions/domain.ValidString" + "language_goal": { + "type": "string" }, - "nickName": { - "$ref": "#/definitions/domain.ValidString" + "last_name": { + "type": "string" + }, + "learning_goal": { + "type": "string" + }, + "nick_name": { + "type": "string" }, "occupation": { - "$ref": "#/definitions/domain.ValidString" + "type": "string" }, - "preferredLanguage": { - "$ref": "#/definitions/domain.ValidString" + "preferred_language": { + "type": "string" }, - "profileCompleted": { - "$ref": "#/definitions/domain.ValidBool" + "profile_completed": { + "type": "boolean" }, - "profilePictureURL": { - "$ref": "#/definitions/domain.ValidString" + "profile_picture_url": { + "type": "string" }, "region": { - "$ref": "#/definitions/domain.ValidString" - }, - "status": { - "$ref": "#/definitions/domain.ValidString" - }, - "userID": { - "type": "integer", - "format": "int64" - }, - "userName": { - "$ref": "#/definitions/domain.ValidString" + "type": "string" } } }, "domain.UserProfileResponse": { "type": "object", "properties": { - "age": { - "type": "integer" + "age_group": { + "type": "string" + }, + "birth_day": { + "type": "string" }, "country": { "type": "string" @@ -3298,6 +3397,7 @@ "type": "string" }, "email": { + "description": "UserName string `json:\"user_name,omitempty\"`", "type": "string" }, "email_verified": { @@ -3309,6 +3409,9 @@ "first_name": { "type": "string" }, + "gender": { + "type": "string" + }, "id": { "type": "integer" }, @@ -3363,9 +3466,6 @@ }, "updated_at": { "type": "string" - }, - "user_name": { - "type": "string" } } }, @@ -3384,39 +3484,6 @@ "UserStatusDeactivated" ] }, - "domain.ValidBool": { - "type": "object", - "properties": { - "valid": { - "type": "boolean" - }, - "value": { - "type": "boolean" - } - } - }, - "domain.ValidInt": { - "type": "object", - "properties": { - "valid": { - "type": "boolean" - }, - "value": { - "type": "integer" - } - } - }, - "domain.ValidString": { - "type": "object", - "properties": { - "valid": { - "type": "boolean" - }, - "value": { - "type": "string" - } - } - }, "domain.VerifyOtpReq": { "type": "object", "required": [ @@ -3612,27 +3679,7 @@ } }, "handlers.ResetPasswordReq": { - "type": "object", - "required": [ - "otp", - "password", - "user_name" - ], - "properties": { - "otp": { - "type": "string", - "example": "123456" - }, - "password": { - "type": "string", - "minLength": 8, - "example": "newpassword123" - }, - "user_name": { - "type": "string", - "example": "johndoe" - } - } + "type": "object" }, "handlers.SearchUserByNameOrPhoneReq": { "type": "object", diff --git a/docs/swagger.yaml b/docs/swagger.yaml index ad5a3dd..e3b82b5 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,42 +1,42 @@ definitions: - authentication.LoginRequest: - properties: - email: - type: string - otp_code: - type: string - password: - type: string - phone_number: - type: string - type: object - domain.AssessmentOption: - properties: - id: - format: int64 - type: integer - isCorrect: - type: boolean - optionText: - type: string - type: object + domain.AgeGroup: + enum: + - UNDER_13 + - "13_17" + - "18_24" + - "25_34" + - "35_44" + - "45_54" + - 55_PLUS + type: string + x-enum-varnames: + - AgeUnder13 + - Age13To17 + - Age18To24 + - Age25To34 + - Age35To44 + - Age45To54 + - Age55Plus domain.AssessmentQuestion: properties: + created_at: + type: string description: type: string - difficultyLevel: + difficulty_level: type: string id: - format: int64 type: integer - options: - items: - $ref: '#/definitions/domain.AssessmentOption' - type: array - questionType: + is_active: + type: boolean + points: + type: integer + question_type: type: string title: type: string + updated_at: + type: string type: object domain.Course: properties: @@ -66,6 +66,40 @@ definitions: description: '"Learning English", "Other Courses"' type: string type: object + domain.CreateAssessmentQuestionInput: + properties: + correctAnswer: + description: Short Answer only + type: string + description: + type: string + difficultyLevel: + type: string + isActive: + type: boolean + options: + description: Multiple Choice only + items: + $ref: '#/definitions/domain.CreateQuestionOptionInput' + type: array + points: + format: int32 + type: integer + questionType: + $ref: '#/definitions/domain.QuestionType' + title: + type: string + type: object + domain.CreateQuestionOptionInput: + properties: + isCorrect: + type: boolean + order: + format: int32 + type: integer + text: + type: string + type: object domain.ErrorResponse: properties: error: @@ -129,6 +163,17 @@ definitions: pagination: $ref: '#/definitions/domain.Pagination' type: object + domain.LoginRequest: + properties: + email: + type: string + otp_code: + type: string + password: + type: string + phone_number: + type: string + type: object domain.Module: properties: content: @@ -268,46 +313,44 @@ definitions: description: '"public", "private", "unlisted"' type: string type: object + domain.QuestionOption: + properties: + option_text: + type: string + question_id: + type: integer + type: object + domain.QuestionType: + enum: + - MULTIPLE_CHOICE + - TRUE_FALSE + - SHORT_ANSWER + type: string + x-enum-varnames: + - MultipleChoice + - TrueFalse + - ShortAnswer + domain.QuestionWithDetails: + properties: + options: + items: + $ref: '#/definitions/domain.QuestionOption' + type: array + question: + $ref: '#/definitions/domain.AssessmentQuestion' + type: object domain.RegisterUserReq: properties: - age: - type: integer - country: - type: string - education_level: - type: string email: type: string - favoutite_topic: - type: string - first_name: - type: string - language_challange: - type: string - language_goal: - type: string - last_name: - type: string - learning_goal: - type: string - nick_name: - type: string - occupation: - type: string otp_medium: $ref: '#/definitions/domain.OtpMedium' password: type: string phone_number: type: string - preferred_language: - type: string - region: - type: string role: type: string - user_name: - type: string type: object domain.ResendOtpReq: properties: @@ -351,52 +394,52 @@ definitions: type: object domain.UpdateUserReq: properties: - age: - $ref: '#/definitions/domain.ValidInt' + age_group: + $ref: '#/definitions/domain.AgeGroup' + birth_day: + description: YYYY-MM-DD + type: string country: - $ref: '#/definitions/domain.ValidString' - educationLevel: - $ref: '#/definitions/domain.ValidString' - favoutiteTopic: - $ref: '#/definitions/domain.ValidString' - firstName: - $ref: '#/definitions/domain.ValidString' - knowledgeLevel: - allOf: - - $ref: '#/definitions/domain.ValidString' - description: Profile fields - languageChallange: - $ref: '#/definitions/domain.ValidString' - languageGoal: - $ref: '#/definitions/domain.ValidString' - lastName: - $ref: '#/definitions/domain.ValidString' - learningGoal: - $ref: '#/definitions/domain.ValidString' - nickName: - $ref: '#/definitions/domain.ValidString' + type: string + education_level: + type: string + favourite_topic: + type: string + first_name: + type: string + gender: + type: string + initial_assessment_completed: + type: boolean + knowledge_level: + type: string + language_challange: + type: string + language_goal: + type: string + last_name: + type: string + learning_goal: + type: string + nick_name: + type: string occupation: - $ref: '#/definitions/domain.ValidString' - preferredLanguage: - $ref: '#/definitions/domain.ValidString' - profileCompleted: - $ref: '#/definitions/domain.ValidBool' - profilePictureURL: - $ref: '#/definitions/domain.ValidString' + type: string + preferred_language: + type: string + profile_completed: + type: boolean + profile_picture_url: + type: string region: - $ref: '#/definitions/domain.ValidString' - status: - $ref: '#/definitions/domain.ValidString' - userID: - format: int64 - type: integer - userName: - $ref: '#/definitions/domain.ValidString' + type: string type: object domain.UserProfileResponse: properties: - age: - type: integer + age_group: + type: string + birth_day: + type: string country: type: string created_at: @@ -404,6 +447,7 @@ definitions: education_level: type: string email: + description: UserName string `json:"user_name,omitempty"` type: string email_verified: type: boolean @@ -411,6 +455,8 @@ definitions: type: string first_name: type: string + gender: + type: string id: type: integer initial_assessment_completed: @@ -448,8 +494,6 @@ definitions: $ref: '#/definitions/domain.UserStatus' updated_at: type: string - user_name: - type: string type: object domain.UserStatus: enum: @@ -463,27 +507,6 @@ definitions: - UserStatusActive - UserStatusSuspended - UserStatusDeactivated - domain.ValidBool: - properties: - valid: - type: boolean - value: - type: boolean - type: object - domain.ValidInt: - properties: - valid: - type: boolean - value: - type: integer - type: object - domain.ValidString: - properties: - valid: - type: boolean - value: - type: string - type: object domain.VerifyOtpReq: properties: email: @@ -615,21 +638,6 @@ definitions: type: string type: object handlers.ResetPasswordReq: - properties: - otp: - example: "123456" - type: string - password: - example: newpassword123 - minLength: 8 - type: string - user_name: - example: johndoe - type: string - required: - - otp - - password - - user_name type: object handlers.SearchUserByNameOrPhoneReq: properties: @@ -740,7 +748,7 @@ paths: name: login required: true schema: - $ref: '#/definitions/authentication.LoginRequest' + $ref: '#/definitions/domain.LoginRequest' produces: - application/json responses: @@ -793,45 +801,6 @@ paths: summary: Resend OTP tags: - otp - /api/v1/{tenant_slug}/user: - put: - consumes: - - application/json - description: Updates user profile information (partial updates supported) - parameters: - - description: User ID - in: path - name: user_id - required: true - type: integer - - description: Update user payload - in: body - name: body - required: true - schema: - $ref: '#/definitions/domain.UpdateUserReq' - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.Response' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "404": - description: Not Found - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Update user profile - tags: - - user /api/v1/{tenant_slug}/user-login: post: consumes: @@ -843,7 +812,7 @@ paths: name: login required: true schema: - $ref: '#/definitions/authentication.LoginRequest' + $ref: '#/definitions/domain.LoginRequest' produces: - application/json responses: @@ -866,39 +835,6 @@ paths: summary: Login user tags: - auth - /api/v1/{tenant_slug}/user/{user_name}/is-pending: - get: - consumes: - - application/json - description: Returns whether the specified user has a status of "pending" - parameters: - - description: User Name - in: path - name: user_name - required: true - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/domain.Response' - "400": - description: Bad Request - schema: - $ref: '#/definitions/domain.ErrorResponse' - "404": - description: Not Found - schema: - $ref: '#/definitions/domain.ErrorResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/domain.ErrorResponse' - summary: Check if user status is pending - tags: - - user /api/v1/{tenant_slug}/user/admin-profile: get: consumes: @@ -1139,54 +1075,6 @@ paths: summary: Get user profile tags: - user - /api/v1/{tenant_slug}/users: - get: - consumes: - - application/json - description: Get users with optional filters - parameters: - - description: Role filter - in: query - name: role - type: string - - description: Search query - in: query - name: query - type: string - - description: Page number - in: query - name: page - type: integer - - description: Page size - in: query - name: page_size - type: integer - - description: Created before (RFC3339) - in: query - name: created_before - type: string - - description: Created after (RFC3339) - in: query - name: created_after - type: string - produces: - - application/json - responses: - "200": - description: OK - schema: - $ref: '#/definitions/response.APIResponse' - "400": - description: Bad Request - schema: - $ref: '#/definitions/response.APIResponse' - "500": - description: Internal Server Error - schema: - $ref: '#/definitions/response.APIResponse' - summary: Get all users - tags: - - user /api/v1/admin: get: consumes: @@ -1324,53 +1212,42 @@ paths: - admin /api/v1/assessment/questions: get: - consumes: - - application/json - description: Returns all active questions used for initial knowledge assessment + description: Returns all active assessment questions with their options or answers produces: - application/json responses: "200": description: OK schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - items: - $ref: '#/definitions/domain.AssessmentQuestion' - type: array - type: object + items: + $ref: '#/definitions/domain.QuestionWithDetails' + type: array "500": description: Internal Server Error schema: $ref: '#/definitions/domain.ErrorResponse' - summary: Get active initial assessment questions + summary: List assessment questions tags: - - assessment + - assessment-question post: consumes: - application/json - description: Creates a new question for the initial knowledge assessment + description: Creates a new assessment question with options or short answer + depending on question type parameters: - - description: Assessment question payload + - description: Create question payload in: body - name: question + name: body required: true schema: - $ref: '#/definitions/domain.AssessmentQuestion' + $ref: '#/definitions/domain.CreateAssessmentQuestionInput' produces: - application/json responses: "201": description: Created schema: - allOf: - - $ref: '#/definitions/domain.Response' - - properties: - data: - $ref: '#/definitions/domain.AssessmentQuestion' - type: object + $ref: '#/definitions/domain.Response' "400": description: Bad Request schema: @@ -1381,7 +1258,50 @@ paths: $ref: '#/definitions/domain.ErrorResponse' summary: Create assessment question tags: - - assessment + - assessment-question + /api/v1/assessment/questions/{id}: + get: + description: Returns a single assessment question with its options or answer + parameters: + - description: Question ID + in: path + name: id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/domain.QuestionWithDetails' + "400": + description: Bad Request + schema: + $ref: '#/definitions/domain.ErrorResponse' + "404": + description: Not Found + schema: + $ref: '#/definitions/domain.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/domain.ErrorResponse' + summary: Get assessment question by ID + tags: + - assessment-question + /api/v1/auth/google/callback: + get: + responses: {} + summary: Google login callback + tags: + - auth + /api/v1/auth/google/login: + get: + responses: {} + summary: Google login redirect + tags: + - auth /api/v1/auth/logout: post: consumes: @@ -2102,7 +2022,7 @@ paths: name: login required: true schema: - $ref: '#/definitions/authentication.LoginRequest' + $ref: '#/definitions/domain.LoginRequest' produces: - application/json responses: @@ -2221,15 +2141,54 @@ paths: summary: Check if phone number or email exist tags: - user - /api/v1/user/{user_name}/is-unique: + /api/v1/user: + put: + consumes: + - application/json + description: Updates user profile information (partial updates supported) + parameters: + - description: User ID + in: path + name: user_id + required: true + type: integer + - description: Update user payload + in: body + name: body + required: true + schema: + $ref: '#/definitions/domain.UpdateUserReq' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/domain.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/domain.ErrorResponse' + "404": + description: Not Found + schema: + $ref: '#/definitions/domain.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/domain.ErrorResponse' + summary: Update user profile + tags: + - user + /api/v1/user/{user_id}/is-pending: get: consumes: - application/json - description: Returns whether the specified user_name is available (unique) + description: Returns whether the specified user has a status of "pending" parameters: - - description: User Name + - description: User ID in: path - name: user_name + name: user_id required: true type: string produces: @@ -2243,11 +2202,48 @@ paths: description: Bad Request schema: $ref: '#/definitions/domain.ErrorResponse' + "404": + description: Not Found + schema: + $ref: '#/definitions/domain.ErrorResponse' "500": description: Internal Server Error schema: $ref: '#/definitions/domain.ErrorResponse' - summary: Check if user_name is unique + summary: Check if user status is pending + tags: + - user + /api/v1/user/{user_id}/is-profile-completed: + get: + consumes: + - application/json + description: Returns whether the specified user's profile is completed + parameters: + - description: User ID + in: path + name: user_id + required: true + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/domain.Response' + "400": + description: Bad Request + schema: + $ref: '#/definitions/domain.ErrorResponse' + "404": + description: Not Found + schema: + $ref: '#/definitions/domain.ErrorResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/domain.ErrorResponse' + summary: Check if user profile is completed tags: - user /api/v1/user/delete/{id}: @@ -2432,6 +2428,54 @@ paths: summary: Verify OTP tags: - user + /api/v1/users: + get: + consumes: + - application/json + description: Get users with optional filters + parameters: + - description: Role filter + in: query + name: role + type: string + - description: Search query + in: query + name: query + type: string + - description: Page number + in: query + name: page + type: integer + - description: Page size + in: query + name: page_size + type: integer + - description: Created before (RFC3339) + in: query + name: created_before + type: string + - description: Created after (RFC3339) + in: query + name: created_after + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/response.APIResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/response.APIResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/response.APIResponse' + summary: Get all users + tags: + - user securityDefinitions: Bearer: in: header diff --git a/internal/domain/initial_assessment.go b/internal/domain/initial_assessment.go index 20f93b1..417852b 100644 --- a/internal/domain/initial_assessment.go +++ b/internal/domain/initial_assessment.go @@ -1,6 +1,8 @@ package domain -import dbgen "Yimaru-Backend/gen/db" +import ( + "time" +) type QuestionType string @@ -10,8 +12,20 @@ const ( ShortAnswer QuestionType = "SHORT_ANSWER" ) +type AssessmentQuestion struct { + ID int64 `json:"id"` + Title string `json:"title"` + Description string `json:"description"` + QuestionType string `json:"question_type"` + DifficultyLevel string `json:"difficulty_level"` + Points int32 `json:"points"` + IsActive bool `json:"is_active"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` +} + type QuestionWithDetails struct { - Question dbgen.AssessmentQuestion + Question AssessmentQuestion Options []QuestionOption } diff --git a/internal/services/assessment/initial_assessment.go b/internal/services/assessment/initial_assessment.go index 6f925f4..0da7a54 100644 --- a/internal/services/assessment/initial_assessment.go +++ b/internal/services/assessment/initial_assessment.go @@ -5,6 +5,7 @@ import ( "Yimaru-Backend/internal/domain" "context" "errors" + "encoding/json" "github.com/jackc/pgx/v5/pgtype" ) @@ -117,7 +118,15 @@ func (s *Service) ListQuestions(ctx context.Context) ([]domain.QuestionWithDetai out := make([]domain.QuestionWithDetails, 0, len(questions)) for _, q := range questions { - item := domain.QuestionWithDetails{Question: q} + var dq domain.AssessmentQuestion + b, err := json.Marshal(q) + if err != nil { + return nil, err + } + if err := json.Unmarshal(b, &dq); err != nil { + return nil, err + } + item := domain.QuestionWithDetails{Question: dq} switch domain.QuestionType(q.QuestionType) { case domain.MultipleChoice, domain.TrueFalse: @@ -156,7 +165,15 @@ func (s *Service) GetQuestionByID(ctx context.Context, id int64) (domain.Questio return domain.QuestionWithDetails{}, err } - item := domain.QuestionWithDetails{Question: q} + var dq domain.AssessmentQuestion + b, err := json.Marshal(q) + if err != nil { + return domain.QuestionWithDetails{}, err + } + if err := json.Unmarshal(b, &dq); err != nil { + return domain.QuestionWithDetails{}, err + } + item := domain.QuestionWithDetails{Question: dq} switch domain.QuestionType(q.QuestionType) { case domain.MultipleChoice, domain.TrueFalse: opts, err := repo.GetQuestionOptions(ctx, q.ID)