diff --git a/docs/docs.go b/docs/docs.go index aad76131fffdf4511f3699ffc3dafc59aa1bc6a5..49b75f3556829fa1845dddc0737e82c9ab85bc01 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -451,37 +451,100 @@ const docTemplate = `{ } } }, - "/reset/confirm": { + "/course": { + "get": { + "description": "Retrieve a list of all available courses.", + "produces": [ + "application/json" + ], + "tags": [ + "course" + ], + "summary": "Get all courses", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + }, "put": { - "description": "Do confirmation to reset password", + "description": "Add a new course with the given details", + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], "tags": [ - "reset" + "course" ], - "summary": "Confirm Reset Password", + "summary": "Add a new course", "parameters": [ { "type": "string", - "description": "Email validation token", + "description": "AddCourseToken", "name": "Authorization", "in": "header", "required": true }, { - "description": "payload", + "description": "Add Course payload", "name": "data", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/confirm.ConfirmRequestPayload" + "$ref": "#/definitions/course.AddCourseRequestPayload" } } ], "responses": { "200": { - "description": "Login Success", + "description": "Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", "schema": { "$ref": "#/definitions/web.BaseResponse" } @@ -489,30 +552,100 @@ const docTemplate = `{ } } }, - "/reset/request": { - "post": { - "description": "Send Reset password token to email", + "/course/faculty": { + "get": { + "description": "Retrieves a list of all faculties", "produces": [ "application/json" ], "tags": [ - "reset" + "course" ], - "summary": "Request Reset Password Token", + "summary": "Get all faculties", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + }, + "put": { + "description": "Adds a new faculty with the given details", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "course" + ], + "summary": "Add a new faculty", "parameters": [ { - "description": "payload", + "type": "string", + "description": "AddFacultyToken", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "description": "Add Faculty payload", "name": "data", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/request.RequestRequestPayload" + "$ref": "#/definitions/faculty.AddFacultyRequestPayload" } } ], "responses": { "200": { - "description": "Login Success", + "description": "Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", "schema": { "$ref": "#/definitions/web.BaseResponse" } @@ -520,108 +653,1514 @@ const docTemplate = `{ } } }, - "/reset/validate": { + "/course/faculty/courses/{id}": { "get": { - "description": "Send Reset password token to email", + "description": "Retrieve all courses that belong to the faculty with the given ID", "produces": [ "application/json" ], "tags": [ - "reset" + "course" ], - "summary": "Request Reset Password Token", + "summary": "Get all courses by faculty ID", "parameters": [ { "type": "string", - "description": "Email validation token", - "name": "Authorization", - "in": "header", + "description": "Faculty ID (UUID)", + "name": "id", + "in": "path", "required": true } ], "responses": { "200": { - "description": "Login Success", + "description": "Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "400": { + "description": "Invalid UUID provided in request path", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", "schema": { "$ref": "#/definitions/web.BaseResponse" } } } } - } - }, - "definitions": { - "admin.AdminAddUserPayload": { - "type": "object", - "required": [ - "email", - "name", - "role" - ], - "properties": { - "email": { - "description": "User Email", - "type": "string", - "example": "someone@example.com" - }, - "name": { - "description": "User name", - "type": "string", - "example": "someone" - }, - "role": { - "description": "User Role", - "type": "string", - "example": "admin" - } - } }, - "admin.AdminUpdateUserPayload": { - "type": "object", - "required": [ - "email", - "name", - "role" - ], - "properties": { - "email": { - "description": "User Email", - "type": "string", - "example": "someone@example.com" - }, - "name": { - "description": "User name", - "type": "string", - "example": "someone" - }, - "role": { - "description": "User Role", - "type": "string", - "example": "admin" + "/course/faculty/majors/{id}": { + "get": { + "description": "Returns a list of majors associated with the given faculty ID", + "produces": [ + "application/json" + ], + "tags": [ + "course" + ], + "summary": "Get majors by faculty ID", + "parameters": [ + { + "type": "string", + "description": "Faculty ID (UUID)", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "400": { + "description": "Invalid UUID provided in request path", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } } } }, - "confirm.ConfirmRequestPayload": { - "description": "Information that should be available when you confirm a password reset", - "type": "object", - "required": [ - "password", - "password_validation" - ], - "properties": { + "/course/faculty/{id}": { + "get": { + "description": "Retrieve faculty data by UUID", + "produces": [ + "application/json" + ], + "tags": [ + "course" + ], + "summary": "Get faculty by ID", + "parameters": [ + { + "type": "string", + "description": "Faculty ID (UUID)", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "400": { + "description": "Invalid UUID provided in request path", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + }, + "patch": { + "description": "Update a faculty with the given ID", + "tags": [ + "course" + ], + "summary": "Update a faculty", + "parameters": [ + { + "type": "string", + "description": "Faculty ID (UUID)", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Update Faculty Payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/faculty.UpdateFacultyRequestPayload" + } + }, + { + "type": "string", + "description": "UpdateFacultyToken", + "name": "Authorization", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + } + }, + "/course/major": { + "get": { + "description": "Get a list of all majors", + "produces": [ + "application/json" + ], + "tags": [ + "course" + ], + "summary": "Get all majors", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + }, + "put": { + "description": "Add a new major to a faculty", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "course" + ], + "summary": "Add Major", + "parameters": [ + { + "type": "string", + "description": "AddMajorToken", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "description": "Add Major payload", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/major.AddMajorRequestPayload" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + } + }, + "/course/major/courses/{id}": { + "get": { + "description": "Retrieve all courses related to a major", + "produces": [ + "application/json" + ], + "tags": [ + "course" + ], + "summary": "Get courses by major", + "parameters": [ + { + "type": "string", + "description": "Major ID (UUID)", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "400": { + "description": "Invalid UUID provided in request path", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + } + }, + "/course/major/{id}": { + "get": { + "description": "Get a major by ID", + "produces": [ + "application/json" + ], + "tags": [ + "course" + ], + "summary": "Get a major by ID", + "parameters": [ + { + "type": "string", + "description": "Major ID (UUID)", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "400": { + "description": "Invalid UUID provided in request path", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + }, + "put": { + "description": "Update a major with the given ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "course" + ], + "summary": "Update a major", + "parameters": [ + { + "type": "string", + "description": "Major ID (UUID)", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "UpdateMajorToken", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "description": "Update Major payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/major.UpdateMajorRequestPayload" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + } + }, + "/course/{id}": { + "get": { + "description": "Retrieve a course by ID", + "produces": [ + "application/json" + ], + "tags": [ + "course" + ], + "summary": "Get a course by ID", + "parameters": [ + { + "type": "string", + "description": "ID of the course to retrieve", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + }, + "delete": { + "description": "Delete a course with the specified ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "course" + ], + "summary": "Delete course by ID", + "parameters": [ + { + "type": "string", + "description": "Course ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "DeleteCourseToken", + "name": "Authorization", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + }, + "patch": { + "description": "Update an existing course.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "course" + ], + "summary": "Update Course", + "parameters": [ + { + "type": "integer", + "description": "Course ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "UpdateCourseToken", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "description": "Update Course Payload", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/course.UpdateCourseRequestPayload" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + } + }, + "/course/{id}/material": { + "post": { + "description": "Add new material", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "content" + ], + "summary": "Add Material", + "parameters": [ + { + "type": "string", + "description": "Access token", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "description": "Material Request", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/material.CreateMaterialRequest" + } + }, + { + "type": "string", + "example": "IF3230", + "description": "Course id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/web.BaseResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/material.CreateMaterialResponse" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + } + }, + "/course/{id}/materials": { + "get": { + "description": "Get materials", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "content" + ], + "summary": "Get materials", + "parameters": [ + { + "type": "string", + "example": "IF3270", + "description": "Course id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/web.BaseResponse" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/material.Material" + } + } + } + } + ] + } + } + } + } + }, + "/course/{id}/quiz": { + "get": { + "description": "Get all cours", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "course" + ], + "summary": "Get Course quiz", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Course id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/web.BaseResponse" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/quiz.Quiz" + } + } + } + } + ] + } + } + } + } + }, + "/material/{id}": { + "get": { + "description": "Get material detail", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "content" + ], + "summary": "Get material detail", + "parameters": [ + { + "type": "string", + "example": "IF3270", + "description": "Material id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/web.BaseResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/material.Material" + } + } + } + ] + } + } + } + }, + "post": { + "description": "Add content of material", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "content" + ], + "summary": "Add Content", + "parameters": [ + { + "type": "string", + "description": "Access token", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "description": "Add content request", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/material.NewContentRequest" + } + }, + { + "type": "string", + "format": "uuid", + "description": "Material id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/web.BaseResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/material.NewContentResponse" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + }, + "delete": { + "description": "Delete material", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "content" + ], + "summary": "Delete material", + "parameters": [ + { + "type": "string", + "description": "Access token", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Material id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + } + }, + "/material/{id}/content/{content-id}": { + "delete": { + "description": "Delete content of material", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "content" + ], + "summary": "Delete Content", + "parameters": [ + { + "type": "string", + "description": "Access token", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Material id", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Content id", + "name": "content-id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + } + }, + "/quiz/{id}": { + "get": { + "description": "Get Quiz Detail", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "quiz" + ], + "summary": "Get Quiz Detail", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Quiz id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/web.BaseResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/quiz.Quiz" + } + } + } + ] + } + } + } + } + }, + "/quiz/{id}/solution": { + "get": { + "description": "Take a quiz", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "quiz" + ], + "summary": "Get Quiz Solution", + "parameters": [ + { + "type": "string", + "description": "Authenticate User (any role)", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Quiz id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/web.BaseResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/quiz.QuizDetail" + } + } + } + ] + } + } + } + } + }, + "/quiz/{id}/take": { + "post": { + "description": "Finish quiz session and get the score", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "quiz" + ], + "summary": "Finish Quiz", + "parameters": [ + { + "type": "string", + "description": "Authenticate User (any role)", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "description": "Quiz Finish payload", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/quiz.FinishQuizPayload" + } + }, + { + "type": "string", + "format": "uuid", + "description": "Quiz id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/web.BaseResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/quiz.QuizDetail" + } + } + } + ] + } + } + } + } + }, + "/reset/confirm": { + "put": { + "description": "Do confirmation to reset password", + "produces": [ + "application/json" + ], + "tags": [ + "reset" + ], + "summary": "Confirm Reset Password", + "parameters": [ + { + "type": "string", + "description": "Email validation token", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "description": "payload", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/confirm.ConfirmRequestPayload" + } + } + ], + "responses": { + "200": { + "description": "Login Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + } + }, + "/reset/request": { + "post": { + "description": "Send Reset password token to email", + "produces": [ + "application/json" + ], + "tags": [ + "reset" + ], + "summary": "Request Reset Password Token", + "parameters": [ + { + "description": "payload", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.RequestRequestPayload" + } + } + ], + "responses": { + "200": { + "description": "Login Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + } + }, + "/reset/validate": { + "get": { + "description": "Send Reset password token to email", + "produces": [ + "application/json" + ], + "tags": [ + "reset" + ], + "summary": "Request Reset Password Token", + "parameters": [ + { + "type": "string", + "description": "Email validation token", + "name": "Authorization", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "Login Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + } + } + }, + "definitions": { + "admin.AdminAddUserPayload": { + "type": "object", + "required": [ + "email", + "name", + "role" + ], + "properties": { + "email": { + "description": "User Email", + "type": "string", + "example": "someone@example.com" + }, + "name": { + "description": "User name", + "type": "string", + "example": "someone" + }, + "role": { + "description": "User Role", + "type": "string", + "example": "admin" + } + } + }, + "admin.AdminUpdateUserPayload": { + "type": "object", + "required": [ + "email", + "name", + "role" + ], + "properties": { + "email": { + "description": "User Email", + "type": "string", + "example": "someone@example.com" + }, + "name": { + "description": "User name", + "type": "string", + "example": "someone" + }, + "role": { + "description": "User Role", + "type": "string", + "example": "admin" + } + } + }, + "confirm.ConfirmRequestPayload": { + "description": "Information that should be available when you confirm a password reset", + "type": "object", + "required": [ + "password", + "password_validation" + ], + "properties": { "confirmToken": { "description": "Web Token that was appended to the link", "type": "string" }, - "password": { - "description": "User Password", - "type": "string", - "example": "secret" + "password": { + "description": "User Password", + "type": "string", + "example": "secret" + }, + "password_validation": { + "description": "User Password Validation, must be same as user", + "type": "string", + "example": "secret" + } + } + }, + "course.AddCourseRequestPayload": { + "description": "Information that should be available when you add a course", + "type": "object", + "required": [ + "abbreviation", + "email", + "id", + "name" + ], + "properties": { + "abbreviation": { + "description": "Course Name Abbreviation", + "type": "string" + }, + "addCourseToken": { + "description": "Web Token that was appended to the link", + "type": "string" + }, + "description": { + "description": "Course Description (Can be left empty)", + "type": "string" + }, + "email": { + "description": "Contributor Email", + "type": "string", + "example": "someone@example.com" + }, + "id": { + "description": "Course ID", + "type": "string" + }, + "majabbr": { + "description": "Course Major Abbreviation", + "type": "string" }, - "password_validation": { - "description": "User Password Validation, must be same as user", + "major_id": { + "description": "Major Id, will be set by the server", + "type": "string" + }, + "name": { + "description": "Course Name", + "type": "string" + } + } + }, + "course.UpdateCourseRequestPayload": { + "description": "Information that should be available when you add a course", + "type": "object", + "required": [ + "abbreviation", + "email", + "lecturer", + "name" + ], + "properties": { + "abbreviation": { + "description": "Course Name Abbreviation", + "type": "string" + }, + "description": { + "description": "Course Description (Can be left empty)", + "type": "string" + }, + "email": { + "description": "Contributor Email", "type": "string", - "example": "secret" + "example": "someone@example.com" + }, + "id": { + "description": "Course ID, Provided by query", + "type": "string" + }, + "lecturer": { + "description": "Course Lecturer", + "type": "string" + }, + "majabbr": { + "description": "Course Major Abbreviation", + "type": "string" + }, + "major_id": { + "description": "Major Id, will be set by the server", + "type": "string" + }, + "name": { + "description": "Course Name", + "type": "string" + }, + "updateCourseToken": { + "description": "Web Token that was appended to the link", + "type": "string" + } + } + }, + "faculty.AddFacultyRequestPayload": { + "description": "Information that should be available when you add a faculty", + "type": "object", + "required": [ + "abbreviation", + "name" + ], + "properties": { + "abbreviation": { + "description": "Faculty Name Abbreviation", + "type": "string" + }, + "faculty_token": { + "description": "Web Token that was appended to the link", + "type": "string" + }, + "name": { + "description": "Faculty Name", + "type": "string" + } + } + }, + "faculty.UpdateFacultyRequestPayload": { + "description": "Information that should be available when you update a faculty", + "type": "object", + "required": [ + "abbreviation", + "name" + ], + "properties": { + "abbreviation": { + "description": "Faculty Name Abbreviation", + "type": "string" + }, + "id": { + "description": "Faculty ID, Provided by Query", + "type": "string" + }, + "name": { + "description": "Faculty Name", + "type": "string" + }, + "updateFacultyToken": { + "description": "Web Token that was appended to the link", + "type": "string" } } }, @@ -659,6 +2198,295 @@ const docTemplate = `{ } } }, + "major.AddMajorRequestPayload": { + "description": "Information that should be available when you add a major", + "type": "object", + "required": [ + "abbreviation", + "name" + ], + "properties": { + "abbreviation": { + "description": "Major Name Abbreviation", + "type": "string" + }, + "facabbr": { + "description": "Major Faculty Abbreviation", + "type": "string" + }, + "faculty_id": { + "description": "Faculty Id, will be set by the server", + "type": "string" + }, + "major_token": { + "description": "Web Token that was appended to the link", + "type": "string" + }, + "name": { + "description": "Major Name", + "type": "string" + } + } + }, + "major.UpdateMajorRequestPayload": { + "description": "Information that should be available when you update a major", + "type": "object", + "required": [ + "abbreviation", + "name" + ], + "properties": { + "abbreviation": { + "description": "Major Name Abbreviation", + "type": "string" + }, + "facabbr": { + "description": "Major Faculty Abbreviation", + "type": "string" + }, + "faculty_id": { + "description": "Faculty Id, will be set by the server", + "type": "string" + }, + "id": { + "description": "Major ID, provided by query", + "type": "string" + }, + "name": { + "description": "Major Name", + "type": "string" + }, + "updateMajorToken": { + "description": "Web Token that was appended to the link", + "type": "string" + } + } + }, + "material.Content": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "link": { + "type": "string" + }, + "material_id": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/material.MaterialType" + } + } + }, + "material.CreateMaterialRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + } + } + }, + "material.CreateMaterialResponse": { + "type": "object", + "properties": { + "material_id": { + "type": "string" + } + } + }, + "material.Material": { + "type": "object", + "properties": { + "contents": { + "type": "array", + "items": { + "$ref": "#/definitions/material.Content" + } + }, + "course_id": { + "type": "string" + }, + "creator_email": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "week": { + "type": "integer" + } + } + }, + "material.MaterialType": { + "type": "integer", + "enum": [ + 1, + 2, + 3 + ], + "x-enum-varnames": [ + "Handout", + "Video", + "External" + ] + }, + "material.NewContentRequest": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "link": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/material.MaterialType" + } + } + }, + "material.NewContentResponse": { + "type": "object", + "properties": { + "upload_link": { + "type": "string" + } + } + }, + "quiz.AnswerOption": { + "type": "object", + "properties": { + "answer": { + "type": "string" + }, + "id": { + "type": "string" + }, + "is_solution": { + "type": "boolean" + }, + "media_id": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "quiz.FinishQuizPayload": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/quiz.Response" + } + } + } + }, + "quiz.Quiz": { + "type": "object", + "properties": { + "course_id": { + "type": "string" + }, + "creator_email": { + "type": "string" + }, + "id": { + "type": "string" + }, + "nama": { + "type": "string" + } + } + }, + "quiz.QuizDetail": { + "type": "object", + "properties": { + "course_id": { + "type": "string" + }, + "description": { + "type": "string" + }, + "help": { + "type": "string" + }, + "id": { + "type": "string" + }, + "media": { + "type": "array", + "items": { + "$ref": "#/definitions/quiz.QuizMedia" + } + }, + "name": { + "type": "string" + }, + "problems": { + "type": "array", + "items": { + "$ref": "#/definitions/quiz.QuizProblem" + } + } + } + }, + "quiz.QuizMedia": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "quiz.QuizProblem": { + "type": "object", + "properties": { + "answers": { + "type": "array", + "items": { + "$ref": "#/definitions/quiz.AnswerOption" + } + }, + "id": { + "type": "string" + }, + "media_id": { + "type": "array", + "items": { + "type": "string" + } + }, + "question": { + "type": "string" + } + } + }, + "quiz.Response": { + "type": "object", + "properties": { + "answer_id": { + "type": "string" + }, + "problem_id": { + "type": "string" + } + } + }, "refresh.RefreshResponsePayload": { "description": "Refresh endpoint response when process success", "type": "object", diff --git a/docs/swagger.json b/docs/swagger.json index 5e40a0e52c5c837cf1a9c167f1e4ab064fce87f3..883d3d799d994fab91eb3dbf9e1aee4d8483c36f 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -443,37 +443,100 @@ } } }, - "/reset/confirm": { + "/course": { + "get": { + "description": "Retrieve a list of all available courses.", + "produces": [ + "application/json" + ], + "tags": [ + "course" + ], + "summary": "Get all courses", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + }, "put": { - "description": "Do confirmation to reset password", + "description": "Add a new course with the given details", + "consumes": [ + "application/json" + ], "produces": [ "application/json" ], "tags": [ - "reset" + "course" ], - "summary": "Confirm Reset Password", + "summary": "Add a new course", "parameters": [ { "type": "string", - "description": "Email validation token", + "description": "AddCourseToken", "name": "Authorization", "in": "header", "required": true }, { - "description": "payload", + "description": "Add Course payload", "name": "data", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/confirm.ConfirmRequestPayload" + "$ref": "#/definitions/course.AddCourseRequestPayload" } } ], "responses": { "200": { - "description": "Login Success", + "description": "Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", "schema": { "$ref": "#/definitions/web.BaseResponse" } @@ -481,30 +544,100 @@ } } }, - "/reset/request": { - "post": { - "description": "Send Reset password token to email", + "/course/faculty": { + "get": { + "description": "Retrieves a list of all faculties", "produces": [ "application/json" ], "tags": [ - "reset" + "course" ], - "summary": "Request Reset Password Token", + "summary": "Get all faculties", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + }, + "put": { + "description": "Adds a new faculty with the given details", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "course" + ], + "summary": "Add a new faculty", "parameters": [ { - "description": "payload", + "type": "string", + "description": "AddFacultyToken", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "description": "Add Faculty payload", "name": "data", "in": "body", "required": true, "schema": { - "$ref": "#/definitions/request.RequestRequestPayload" + "$ref": "#/definitions/faculty.AddFacultyRequestPayload" } } ], "responses": { "200": { - "description": "Login Success", + "description": "Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", "schema": { "$ref": "#/definitions/web.BaseResponse" } @@ -512,108 +645,1514 @@ } } }, - "/reset/validate": { + "/course/faculty/courses/{id}": { "get": { - "description": "Send Reset password token to email", + "description": "Retrieve all courses that belong to the faculty with the given ID", "produces": [ "application/json" ], "tags": [ - "reset" + "course" ], - "summary": "Request Reset Password Token", + "summary": "Get all courses by faculty ID", "parameters": [ { "type": "string", - "description": "Email validation token", - "name": "Authorization", - "in": "header", + "description": "Faculty ID (UUID)", + "name": "id", + "in": "path", "required": true } ], "responses": { "200": { - "description": "Login Success", + "description": "Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "400": { + "description": "Invalid UUID provided in request path", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", "schema": { "$ref": "#/definitions/web.BaseResponse" } } } } - } - }, - "definitions": { - "admin.AdminAddUserPayload": { - "type": "object", - "required": [ - "email", - "name", - "role" - ], - "properties": { - "email": { - "description": "User Email", - "type": "string", - "example": "someone@example.com" - }, - "name": { - "description": "User name", - "type": "string", - "example": "someone" - }, - "role": { - "description": "User Role", - "type": "string", - "example": "admin" - } - } }, - "admin.AdminUpdateUserPayload": { - "type": "object", - "required": [ - "email", - "name", - "role" - ], - "properties": { - "email": { - "description": "User Email", - "type": "string", - "example": "someone@example.com" - }, - "name": { - "description": "User name", - "type": "string", - "example": "someone" - }, - "role": { - "description": "User Role", - "type": "string", - "example": "admin" + "/course/faculty/majors/{id}": { + "get": { + "description": "Returns a list of majors associated with the given faculty ID", + "produces": [ + "application/json" + ], + "tags": [ + "course" + ], + "summary": "Get majors by faculty ID", + "parameters": [ + { + "type": "string", + "description": "Faculty ID (UUID)", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "400": { + "description": "Invalid UUID provided in request path", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } } } }, - "confirm.ConfirmRequestPayload": { - "description": "Information that should be available when you confirm a password reset", - "type": "object", - "required": [ - "password", - "password_validation" - ], - "properties": { + "/course/faculty/{id}": { + "get": { + "description": "Retrieve faculty data by UUID", + "produces": [ + "application/json" + ], + "tags": [ + "course" + ], + "summary": "Get faculty by ID", + "parameters": [ + { + "type": "string", + "description": "Faculty ID (UUID)", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "400": { + "description": "Invalid UUID provided in request path", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + }, + "patch": { + "description": "Update a faculty with the given ID", + "tags": [ + "course" + ], + "summary": "Update a faculty", + "parameters": [ + { + "type": "string", + "description": "Faculty ID (UUID)", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Update Faculty Payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/faculty.UpdateFacultyRequestPayload" + } + }, + { + "type": "string", + "description": "UpdateFacultyToken", + "name": "Authorization", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + } + }, + "/course/major": { + "get": { + "description": "Get a list of all majors", + "produces": [ + "application/json" + ], + "tags": [ + "course" + ], + "summary": "Get all majors", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + }, + "put": { + "description": "Add a new major to a faculty", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "course" + ], + "summary": "Add Major", + "parameters": [ + { + "type": "string", + "description": "AddMajorToken", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "description": "Add Major payload", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/major.AddMajorRequestPayload" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + } + }, + "/course/major/courses/{id}": { + "get": { + "description": "Retrieve all courses related to a major", + "produces": [ + "application/json" + ], + "tags": [ + "course" + ], + "summary": "Get courses by major", + "parameters": [ + { + "type": "string", + "description": "Major ID (UUID)", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "400": { + "description": "Invalid UUID provided in request path", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + } + }, + "/course/major/{id}": { + "get": { + "description": "Get a major by ID", + "produces": [ + "application/json" + ], + "tags": [ + "course" + ], + "summary": "Get a major by ID", + "parameters": [ + { + "type": "string", + "description": "Major ID (UUID)", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "400": { + "description": "Invalid UUID provided in request path", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + }, + "put": { + "description": "Update a major with the given ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "course" + ], + "summary": "Update a major", + "parameters": [ + { + "type": "string", + "description": "Major ID (UUID)", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "UpdateMajorToken", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "description": "Update Major payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/major.UpdateMajorRequestPayload" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + } + }, + "/course/{id}": { + "get": { + "description": "Retrieve a course by ID", + "produces": [ + "application/json" + ], + "tags": [ + "course" + ], + "summary": "Get a course by ID", + "parameters": [ + { + "type": "string", + "description": "ID of the course to retrieve", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + }, + "delete": { + "description": "Delete a course with the specified ID", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "course" + ], + "summary": "Delete course by ID", + "parameters": [ + { + "type": "string", + "description": "Course ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "DeleteCourseToken", + "name": "Authorization", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + }, + "patch": { + "description": "Update an existing course.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "course" + ], + "summary": "Update Course", + "parameters": [ + { + "type": "integer", + "description": "Course ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "UpdateCourseToken", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "description": "Update Course Payload", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/course.UpdateCourseRequestPayload" + } + } + ], + "responses": { + "200": { + "description": "Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "403": { + "description": "Forbidden", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + } + }, + "/course/{id}/material": { + "post": { + "description": "Add new material", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "content" + ], + "summary": "Add Material", + "parameters": [ + { + "type": "string", + "description": "Access token", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "description": "Material Request", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/material.CreateMaterialRequest" + } + }, + { + "type": "string", + "example": "IF3230", + "description": "Course id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/web.BaseResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/material.CreateMaterialResponse" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + } + }, + "/course/{id}/materials": { + "get": { + "description": "Get materials", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "content" + ], + "summary": "Get materials", + "parameters": [ + { + "type": "string", + "example": "IF3270", + "description": "Course id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/web.BaseResponse" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/material.Material" + } + } + } + } + ] + } + } + } + } + }, + "/course/{id}/quiz": { + "get": { + "description": "Get all cours", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "course" + ], + "summary": "Get Course quiz", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Course id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/web.BaseResponse" + }, + { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/quiz.Quiz" + } + } + } + } + ] + } + } + } + } + }, + "/material/{id}": { + "get": { + "description": "Get material detail", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "content" + ], + "summary": "Get material detail", + "parameters": [ + { + "type": "string", + "example": "IF3270", + "description": "Material id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/web.BaseResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/material.Material" + } + } + } + ] + } + } + } + }, + "post": { + "description": "Add content of material", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "content" + ], + "summary": "Add Content", + "parameters": [ + { + "type": "string", + "description": "Access token", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "description": "Add content request", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/material.NewContentRequest" + } + }, + { + "type": "string", + "format": "uuid", + "description": "Material id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/web.BaseResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/material.NewContentResponse" + } + } + } + ] + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + }, + "delete": { + "description": "Delete material", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "content" + ], + "summary": "Delete material", + "parameters": [ + { + "type": "string", + "description": "Access token", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Material id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + } + }, + "/material/{id}/content/{content-id}": { + "delete": { + "description": "Delete content of material", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "content" + ], + "summary": "Delete Content", + "parameters": [ + { + "type": "string", + "description": "Access token", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Material id", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Content id", + "name": "content-id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + } + }, + "/quiz/{id}": { + "get": { + "description": "Get Quiz Detail", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "quiz" + ], + "summary": "Get Quiz Detail", + "parameters": [ + { + "type": "string", + "format": "uuid", + "description": "Quiz id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/web.BaseResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/quiz.Quiz" + } + } + } + ] + } + } + } + } + }, + "/quiz/{id}/solution": { + "get": { + "description": "Take a quiz", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "quiz" + ], + "summary": "Get Quiz Solution", + "parameters": [ + { + "type": "string", + "description": "Authenticate User (any role)", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "type": "string", + "format": "uuid", + "description": "Quiz id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/web.BaseResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/quiz.QuizDetail" + } + } + } + ] + } + } + } + } + }, + "/quiz/{id}/take": { + "post": { + "description": "Finish quiz session and get the score", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "quiz" + ], + "summary": "Finish Quiz", + "parameters": [ + { + "type": "string", + "description": "Authenticate User (any role)", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "description": "Quiz Finish payload", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/quiz.FinishQuizPayload" + } + }, + { + "type": "string", + "format": "uuid", + "description": "Quiz id", + "name": "id", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "allOf": [ + { + "$ref": "#/definitions/web.BaseResponse" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/definitions/quiz.QuizDetail" + } + } + } + ] + } + } + } + } + }, + "/reset/confirm": { + "put": { + "description": "Do confirmation to reset password", + "produces": [ + "application/json" + ], + "tags": [ + "reset" + ], + "summary": "Confirm Reset Password", + "parameters": [ + { + "type": "string", + "description": "Email validation token", + "name": "Authorization", + "in": "header", + "required": true + }, + { + "description": "payload", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/confirm.ConfirmRequestPayload" + } + } + ], + "responses": { + "200": { + "description": "Login Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + } + }, + "/reset/request": { + "post": { + "description": "Send Reset password token to email", + "produces": [ + "application/json" + ], + "tags": [ + "reset" + ], + "summary": "Request Reset Password Token", + "parameters": [ + { + "description": "payload", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/request.RequestRequestPayload" + } + } + ], + "responses": { + "200": { + "description": "Login Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + } + }, + "/reset/validate": { + "get": { + "description": "Send Reset password token to email", + "produces": [ + "application/json" + ], + "tags": [ + "reset" + ], + "summary": "Request Reset Password Token", + "parameters": [ + { + "type": "string", + "description": "Email validation token", + "name": "Authorization", + "in": "header", + "required": true + } + ], + "responses": { + "200": { + "description": "Login Success", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } + } + } + }, + "definitions": { + "admin.AdminAddUserPayload": { + "type": "object", + "required": [ + "email", + "name", + "role" + ], + "properties": { + "email": { + "description": "User Email", + "type": "string", + "example": "someone@example.com" + }, + "name": { + "description": "User name", + "type": "string", + "example": "someone" + }, + "role": { + "description": "User Role", + "type": "string", + "example": "admin" + } + } + }, + "admin.AdminUpdateUserPayload": { + "type": "object", + "required": [ + "email", + "name", + "role" + ], + "properties": { + "email": { + "description": "User Email", + "type": "string", + "example": "someone@example.com" + }, + "name": { + "description": "User name", + "type": "string", + "example": "someone" + }, + "role": { + "description": "User Role", + "type": "string", + "example": "admin" + } + } + }, + "confirm.ConfirmRequestPayload": { + "description": "Information that should be available when you confirm a password reset", + "type": "object", + "required": [ + "password", + "password_validation" + ], + "properties": { "confirmToken": { "description": "Web Token that was appended to the link", "type": "string" }, - "password": { - "description": "User Password", - "type": "string", - "example": "secret" + "password": { + "description": "User Password", + "type": "string", + "example": "secret" + }, + "password_validation": { + "description": "User Password Validation, must be same as user", + "type": "string", + "example": "secret" + } + } + }, + "course.AddCourseRequestPayload": { + "description": "Information that should be available when you add a course", + "type": "object", + "required": [ + "abbreviation", + "email", + "id", + "name" + ], + "properties": { + "abbreviation": { + "description": "Course Name Abbreviation", + "type": "string" + }, + "addCourseToken": { + "description": "Web Token that was appended to the link", + "type": "string" + }, + "description": { + "description": "Course Description (Can be left empty)", + "type": "string" + }, + "email": { + "description": "Contributor Email", + "type": "string", + "example": "someone@example.com" + }, + "id": { + "description": "Course ID", + "type": "string" + }, + "majabbr": { + "description": "Course Major Abbreviation", + "type": "string" }, - "password_validation": { - "description": "User Password Validation, must be same as user", + "major_id": { + "description": "Major Id, will be set by the server", + "type": "string" + }, + "name": { + "description": "Course Name", + "type": "string" + } + } + }, + "course.UpdateCourseRequestPayload": { + "description": "Information that should be available when you add a course", + "type": "object", + "required": [ + "abbreviation", + "email", + "lecturer", + "name" + ], + "properties": { + "abbreviation": { + "description": "Course Name Abbreviation", + "type": "string" + }, + "description": { + "description": "Course Description (Can be left empty)", + "type": "string" + }, + "email": { + "description": "Contributor Email", "type": "string", - "example": "secret" + "example": "someone@example.com" + }, + "id": { + "description": "Course ID, Provided by query", + "type": "string" + }, + "lecturer": { + "description": "Course Lecturer", + "type": "string" + }, + "majabbr": { + "description": "Course Major Abbreviation", + "type": "string" + }, + "major_id": { + "description": "Major Id, will be set by the server", + "type": "string" + }, + "name": { + "description": "Course Name", + "type": "string" + }, + "updateCourseToken": { + "description": "Web Token that was appended to the link", + "type": "string" + } + } + }, + "faculty.AddFacultyRequestPayload": { + "description": "Information that should be available when you add a faculty", + "type": "object", + "required": [ + "abbreviation", + "name" + ], + "properties": { + "abbreviation": { + "description": "Faculty Name Abbreviation", + "type": "string" + }, + "faculty_token": { + "description": "Web Token that was appended to the link", + "type": "string" + }, + "name": { + "description": "Faculty Name", + "type": "string" + } + } + }, + "faculty.UpdateFacultyRequestPayload": { + "description": "Information that should be available when you update a faculty", + "type": "object", + "required": [ + "abbreviation", + "name" + ], + "properties": { + "abbreviation": { + "description": "Faculty Name Abbreviation", + "type": "string" + }, + "id": { + "description": "Faculty ID, Provided by Query", + "type": "string" + }, + "name": { + "description": "Faculty Name", + "type": "string" + }, + "updateFacultyToken": { + "description": "Web Token that was appended to the link", + "type": "string" } } }, @@ -651,6 +2190,295 @@ } } }, + "major.AddMajorRequestPayload": { + "description": "Information that should be available when you add a major", + "type": "object", + "required": [ + "abbreviation", + "name" + ], + "properties": { + "abbreviation": { + "description": "Major Name Abbreviation", + "type": "string" + }, + "facabbr": { + "description": "Major Faculty Abbreviation", + "type": "string" + }, + "faculty_id": { + "description": "Faculty Id, will be set by the server", + "type": "string" + }, + "major_token": { + "description": "Web Token that was appended to the link", + "type": "string" + }, + "name": { + "description": "Major Name", + "type": "string" + } + } + }, + "major.UpdateMajorRequestPayload": { + "description": "Information that should be available when you update a major", + "type": "object", + "required": [ + "abbreviation", + "name" + ], + "properties": { + "abbreviation": { + "description": "Major Name Abbreviation", + "type": "string" + }, + "facabbr": { + "description": "Major Faculty Abbreviation", + "type": "string" + }, + "faculty_id": { + "description": "Faculty Id, will be set by the server", + "type": "string" + }, + "id": { + "description": "Major ID, provided by query", + "type": "string" + }, + "name": { + "description": "Major Name", + "type": "string" + }, + "updateMajorToken": { + "description": "Web Token that was appended to the link", + "type": "string" + } + } + }, + "material.Content": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "link": { + "type": "string" + }, + "material_id": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/material.MaterialType" + } + } + }, + "material.CreateMaterialRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + } + } + }, + "material.CreateMaterialResponse": { + "type": "object", + "properties": { + "material_id": { + "type": "string" + } + } + }, + "material.Material": { + "type": "object", + "properties": { + "contents": { + "type": "array", + "items": { + "$ref": "#/definitions/material.Content" + } + }, + "course_id": { + "type": "string" + }, + "creator_email": { + "type": "string" + }, + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "week": { + "type": "integer" + } + } + }, + "material.MaterialType": { + "type": "integer", + "enum": [ + 1, + 2, + 3 + ], + "x-enum-varnames": [ + "Handout", + "Video", + "External" + ] + }, + "material.NewContentRequest": { + "type": "object", + "required": [ + "type" + ], + "properties": { + "link": { + "type": "string" + }, + "type": { + "$ref": "#/definitions/material.MaterialType" + } + } + }, + "material.NewContentResponse": { + "type": "object", + "properties": { + "upload_link": { + "type": "string" + } + } + }, + "quiz.AnswerOption": { + "type": "object", + "properties": { + "answer": { + "type": "string" + }, + "id": { + "type": "string" + }, + "is_solution": { + "type": "boolean" + }, + "media_id": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "quiz.FinishQuizPayload": { + "type": "object", + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/quiz.Response" + } + } + } + }, + "quiz.Quiz": { + "type": "object", + "properties": { + "course_id": { + "type": "string" + }, + "creator_email": { + "type": "string" + }, + "id": { + "type": "string" + }, + "nama": { + "type": "string" + } + } + }, + "quiz.QuizDetail": { + "type": "object", + "properties": { + "course_id": { + "type": "string" + }, + "description": { + "type": "string" + }, + "help": { + "type": "string" + }, + "id": { + "type": "string" + }, + "media": { + "type": "array", + "items": { + "$ref": "#/definitions/quiz.QuizMedia" + } + }, + "name": { + "type": "string" + }, + "problems": { + "type": "array", + "items": { + "$ref": "#/definitions/quiz.QuizProblem" + } + } + } + }, + "quiz.QuizMedia": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "type": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "quiz.QuizProblem": { + "type": "object", + "properties": { + "answers": { + "type": "array", + "items": { + "$ref": "#/definitions/quiz.AnswerOption" + } + }, + "id": { + "type": "string" + }, + "media_id": { + "type": "array", + "items": { + "type": "string" + } + }, + "question": { + "type": "string" + } + } + }, + "quiz.Response": { + "type": "object", + "properties": { + "answer_id": { + "type": "string" + }, + "problem_id": { + "type": "string" + } + } + }, "refresh.RefreshResponsePayload": { "description": "Refresh endpoint response when process success", "type": "object", diff --git a/docs/swagger.yaml b/docs/swagger.yaml index d2280c2b16106e29102e0ed33e5e14d8ac794bc6..58ea9348fb473426c5890cff161bd98360f62bf0 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -56,6 +56,112 @@ definitions: - password - password_validation type: object + course.AddCourseRequestPayload: + description: Information that should be available when you add a course + properties: + abbreviation: + description: Course Name Abbreviation + type: string + addCourseToken: + description: Web Token that was appended to the link + type: string + description: + description: Course Description (Can be left empty) + type: string + email: + description: Contributor Email + example: someone@example.com + type: string + id: + description: Course ID + type: string + majabbr: + description: Course Major Abbreviation + type: string + major_id: + description: Major Id, will be set by the server + type: string + name: + description: Course Name + type: string + required: + - abbreviation + - email + - id + - name + type: object + course.UpdateCourseRequestPayload: + description: Information that should be available when you add a course + properties: + abbreviation: + description: Course Name Abbreviation + type: string + description: + description: Course Description (Can be left empty) + type: string + email: + description: Contributor Email + example: someone@example.com + type: string + id: + description: Course ID, Provided by query + type: string + lecturer: + description: Course Lecturer + type: string + majabbr: + description: Course Major Abbreviation + type: string + major_id: + description: Major Id, will be set by the server + type: string + name: + description: Course Name + type: string + updateCourseToken: + description: Web Token that was appended to the link + type: string + required: + - abbreviation + - email + - lecturer + - name + type: object + faculty.AddFacultyRequestPayload: + description: Information that should be available when you add a faculty + properties: + abbreviation: + description: Faculty Name Abbreviation + type: string + faculty_token: + description: Web Token that was appended to the link + type: string + name: + description: Faculty Name + type: string + required: + - abbreviation + - name + type: object + faculty.UpdateFacultyRequestPayload: + description: Information that should be available when you update a faculty + properties: + abbreviation: + description: Faculty Name Abbreviation + type: string + id: + description: Faculty ID, Provided by Query + type: string + name: + description: Faculty Name + type: string + updateFacultyToken: + description: Web Token that was appended to the link + type: string + required: + - abbreviation + - name + type: object login.LoginRequestPayload: description: Information that should be available when do a login process properties: @@ -81,6 +187,200 @@ definitions: description: Token that used to generate new access token type: string type: object + major.AddMajorRequestPayload: + description: Information that should be available when you add a major + properties: + abbreviation: + description: Major Name Abbreviation + type: string + facabbr: + description: Major Faculty Abbreviation + type: string + faculty_id: + description: Faculty Id, will be set by the server + type: string + major_token: + description: Web Token that was appended to the link + type: string + name: + description: Major Name + type: string + required: + - abbreviation + - name + type: object + major.UpdateMajorRequestPayload: + description: Information that should be available when you update a major + properties: + abbreviation: + description: Major Name Abbreviation + type: string + facabbr: + description: Major Faculty Abbreviation + type: string + faculty_id: + description: Faculty Id, will be set by the server + type: string + id: + description: Major ID, provided by query + type: string + name: + description: Major Name + type: string + updateMajorToken: + description: Web Token that was appended to the link + type: string + required: + - abbreviation + - name + type: object + material.Content: + properties: + id: + type: string + link: + type: string + material_id: + type: string + type: + $ref: '#/definitions/material.MaterialType' + type: object + material.CreateMaterialRequest: + properties: + name: + type: string + required: + - name + type: object + material.CreateMaterialResponse: + properties: + material_id: + type: string + type: object + material.Material: + properties: + contents: + items: + $ref: '#/definitions/material.Content' + type: array + course_id: + type: string + creator_email: + type: string + id: + type: string + name: + type: string + week: + type: integer + type: object + material.MaterialType: + enum: + - 1 + - 2 + - 3 + type: integer + x-enum-varnames: + - Handout + - Video + - External + material.NewContentRequest: + properties: + link: + type: string + type: + $ref: '#/definitions/material.MaterialType' + required: + - type + type: object + material.NewContentResponse: + properties: + upload_link: + type: string + type: object + quiz.AnswerOption: + properties: + answer: + type: string + id: + type: string + is_solution: + type: boolean + media_id: + items: + type: string + type: array + type: object + quiz.FinishQuizPayload: + properties: + data: + items: + $ref: '#/definitions/quiz.Response' + type: array + type: object + quiz.Quiz: + properties: + course_id: + type: string + creator_email: + type: string + id: + type: string + nama: + type: string + type: object + quiz.QuizDetail: + properties: + course_id: + type: string + description: + type: string + help: + type: string + id: + type: string + media: + items: + $ref: '#/definitions/quiz.QuizMedia' + type: array + name: + type: string + problems: + items: + $ref: '#/definitions/quiz.QuizProblem' + type: array + type: object + quiz.QuizMedia: + properties: + id: + type: string + type: + type: string + url: + type: string + type: object + quiz.QuizProblem: + properties: + answers: + items: + $ref: '#/definitions/quiz.AnswerOption' + type: array + id: + type: string + media_id: + items: + type: string + type: array + question: + type: string + type: object + quiz.Response: + properties: + answer_id: + type: string + problem_id: + type: string + type: object refresh.RefreshResponsePayload: description: Refresh endpoint response when process success properties: @@ -435,6 +735,916 @@ paths: summary: Send Email Verification tags: - auth + /course: + get: + description: Retrieve a list of all available courses. + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/web.BaseResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/web.BaseResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/web.BaseResponse' + summary: Get all courses + tags: + - course + put: + consumes: + - application/json + description: Add a new course with the given details + parameters: + - description: AddCourseToken + in: header + name: Authorization + required: true + type: string + - description: Add Course payload + in: body + name: data + required: true + schema: + $ref: '#/definitions/course.AddCourseRequestPayload' + produces: + - application/json + responses: + "200": + description: Success + schema: + $ref: '#/definitions/web.BaseResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/web.BaseResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/web.BaseResponse' + "403": + description: Forbidden + schema: + $ref: '#/definitions/web.BaseResponse' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/web.BaseResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/web.BaseResponse' + summary: Add a new course + tags: + - course + /course/{id}: + delete: + consumes: + - application/json + description: Delete a course with the specified ID + parameters: + - description: Course ID + in: path + name: id + required: true + type: string + - description: DeleteCourseToken + in: header + name: Authorization + required: true + type: string + produces: + - application/json + responses: + "200": + description: Success + schema: + $ref: '#/definitions/web.BaseResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/web.BaseResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/web.BaseResponse' + "403": + description: Forbidden + schema: + $ref: '#/definitions/web.BaseResponse' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/web.BaseResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/web.BaseResponse' + summary: Delete course by ID + tags: + - course + get: + description: Retrieve a course by ID + parameters: + - description: ID of the course to retrieve + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/web.BaseResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/web.BaseResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/web.BaseResponse' + summary: Get a course by ID + tags: + - course + patch: + consumes: + - application/json + description: Update an existing course. + parameters: + - description: Course ID + in: path + name: id + required: true + type: integer + - description: UpdateCourseToken + in: header + name: Authorization + required: true + type: string + - description: Update Course Payload + in: body + name: data + required: true + schema: + $ref: '#/definitions/course.UpdateCourseRequestPayload' + produces: + - application/json + responses: + "200": + description: Success + schema: + $ref: '#/definitions/web.BaseResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/web.BaseResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/web.BaseResponse' + "403": + description: Forbidden + schema: + $ref: '#/definitions/web.BaseResponse' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/web.BaseResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/web.BaseResponse' + summary: Update Course + tags: + - course + /course/{id}/material: + post: + consumes: + - application/json + description: Add new material + parameters: + - description: Access token + in: header + name: Authorization + required: true + type: string + - description: Material Request + in: body + name: data + required: true + schema: + $ref: '#/definitions/material.CreateMaterialRequest' + - description: Course id + example: IF3230 + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/web.BaseResponse' + - properties: + data: + $ref: '#/definitions/material.CreateMaterialResponse' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/web.BaseResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/web.BaseResponse' + summary: Add Material + tags: + - content + /course/{id}/materials: + get: + consumes: + - application/json + description: Get materials + parameters: + - description: Course id + example: IF3270 + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/web.BaseResponse' + - properties: + data: + items: + $ref: '#/definitions/material.Material' + type: array + type: object + summary: Get materials + tags: + - content + /course/{id}/quiz: + get: + consumes: + - application/json + description: Get all cours + parameters: + - description: Course id + format: uuid + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/web.BaseResponse' + - properties: + data: + items: + $ref: '#/definitions/quiz.Quiz' + type: array + type: object + summary: Get Course quiz + tags: + - course + /course/faculty: + get: + description: Retrieves a list of all faculties + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/web.BaseResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/web.BaseResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/web.BaseResponse' + summary: Get all faculties + tags: + - course + put: + consumes: + - application/json + description: Adds a new faculty with the given details + parameters: + - description: AddFacultyToken + in: header + name: Authorization + required: true + type: string + - description: Add Faculty payload + in: body + name: data + required: true + schema: + $ref: '#/definitions/faculty.AddFacultyRequestPayload' + produces: + - application/json + responses: + "200": + description: Success + schema: + $ref: '#/definitions/web.BaseResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/web.BaseResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/web.BaseResponse' + "403": + description: Forbidden + schema: + $ref: '#/definitions/web.BaseResponse' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/web.BaseResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/web.BaseResponse' + summary: Add a new faculty + tags: + - course + /course/faculty/{id}: + get: + description: Retrieve faculty data by UUID + parameters: + - description: Faculty ID (UUID) + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: Success + schema: + $ref: '#/definitions/web.BaseResponse' + "400": + description: Invalid UUID provided in request path + schema: + $ref: '#/definitions/web.BaseResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/web.BaseResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/web.BaseResponse' + summary: Get faculty by ID + tags: + - course + patch: + description: Update a faculty with the given ID + parameters: + - description: Faculty ID (UUID) + in: path + name: id + required: true + type: string + - description: Update Faculty Payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/faculty.UpdateFacultyRequestPayload' + - description: UpdateFacultyToken + in: header + name: Authorization + required: true + type: string + responses: + "200": + description: Success + schema: + $ref: '#/definitions/web.BaseResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/web.BaseResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/web.BaseResponse' + "403": + description: Forbidden + schema: + $ref: '#/definitions/web.BaseResponse' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/web.BaseResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/web.BaseResponse' + summary: Update a faculty + tags: + - course + /course/faculty/courses/{id}: + get: + description: Retrieve all courses that belong to the faculty with the given + ID + parameters: + - description: Faculty ID (UUID) + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: Success + schema: + $ref: '#/definitions/web.BaseResponse' + "400": + description: Invalid UUID provided in request path + schema: + $ref: '#/definitions/web.BaseResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/web.BaseResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/web.BaseResponse' + summary: Get all courses by faculty ID + tags: + - course + /course/faculty/majors/{id}: + get: + description: Returns a list of majors associated with the given faculty ID + parameters: + - description: Faculty ID (UUID) + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: Success + schema: + $ref: '#/definitions/web.BaseResponse' + "400": + description: Invalid UUID provided in request path + schema: + $ref: '#/definitions/web.BaseResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/web.BaseResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/web.BaseResponse' + summary: Get majors by faculty ID + tags: + - course + /course/major: + get: + description: Get a list of all majors + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/web.BaseResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/web.BaseResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/web.BaseResponse' + summary: Get all majors + tags: + - course + put: + consumes: + - application/json + description: Add a new major to a faculty + parameters: + - description: AddMajorToken + in: header + name: Authorization + required: true + type: string + - description: Add Major payload + in: body + name: data + required: true + schema: + $ref: '#/definitions/major.AddMajorRequestPayload' + produces: + - application/json + responses: + "200": + description: Success + schema: + $ref: '#/definitions/web.BaseResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/web.BaseResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/web.BaseResponse' + "403": + description: Forbidden + schema: + $ref: '#/definitions/web.BaseResponse' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/web.BaseResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/web.BaseResponse' + summary: Add Major + tags: + - course + /course/major/{id}: + get: + description: Get a major by ID + parameters: + - description: Major ID (UUID) + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: Success + schema: + $ref: '#/definitions/web.BaseResponse' + "400": + description: Invalid UUID provided in request path + schema: + $ref: '#/definitions/web.BaseResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/web.BaseResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/web.BaseResponse' + summary: Get a major by ID + tags: + - course + put: + consumes: + - application/json + description: Update a major with the given ID + parameters: + - description: Major ID (UUID) + in: path + name: id + required: true + type: string + - description: UpdateMajorToken + in: header + name: Authorization + required: true + type: string + - description: Update Major payload + in: body + name: payload + required: true + schema: + $ref: '#/definitions/major.UpdateMajorRequestPayload' + produces: + - application/json + responses: + "200": + description: Success + schema: + $ref: '#/definitions/web.BaseResponse' + "400": + description: Bad Request + schema: + $ref: '#/definitions/web.BaseResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/web.BaseResponse' + "403": + description: Forbidden + schema: + $ref: '#/definitions/web.BaseResponse' + "422": + description: Unprocessable Entity + schema: + $ref: '#/definitions/web.BaseResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/web.BaseResponse' + summary: Update a major + tags: + - course + /course/major/courses/{id}: + get: + description: Retrieve all courses related to a major + parameters: + - description: Major ID (UUID) + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: Success + schema: + $ref: '#/definitions/web.BaseResponse' + "400": + description: Invalid UUID provided in request path + schema: + $ref: '#/definitions/web.BaseResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/web.BaseResponse' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/web.BaseResponse' + summary: Get courses by major + tags: + - course + /material/{id}: + delete: + consumes: + - application/json + description: Delete material + parameters: + - description: Access token + in: header + name: Authorization + required: true + type: string + - description: Material id + format: uuid + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/web.BaseResponse' + summary: Delete material + tags: + - content + get: + consumes: + - application/json + description: Get material detail + parameters: + - description: Material id + example: IF3270 + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/web.BaseResponse' + - properties: + data: + $ref: '#/definitions/material.Material' + type: object + summary: Get material detail + tags: + - content + post: + consumes: + - application/json + description: Add content of material + parameters: + - description: Access token + in: header + name: Authorization + required: true + type: string + - description: Add content request + in: body + name: data + required: true + schema: + $ref: '#/definitions/material.NewContentRequest' + - description: Material id + format: uuid + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/web.BaseResponse' + - properties: + data: + $ref: '#/definitions/material.NewContentResponse' + type: object + "400": + description: Bad Request + schema: + $ref: '#/definitions/web.BaseResponse' + "401": + description: Unauthorized + schema: + $ref: '#/definitions/web.BaseResponse' + summary: Add Content + tags: + - content + /material/{id}/content/{content-id}: + delete: + consumes: + - application/json + description: Delete content of material + parameters: + - description: Access token + in: header + name: Authorization + required: true + type: string + - description: Material id + format: uuid + in: path + name: id + required: true + type: string + - description: Content id + format: uuid + in: path + name: content-id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/web.BaseResponse' + summary: Delete Content + tags: + - content + /quiz/{id}: + get: + consumes: + - application/json + description: Get Quiz Detail + parameters: + - description: Quiz id + format: uuid + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/web.BaseResponse' + - properties: + data: + $ref: '#/definitions/quiz.Quiz' + type: object + summary: Get Quiz Detail + tags: + - quiz + /quiz/{id}/solution: + get: + consumes: + - application/json + description: Take a quiz + parameters: + - description: Authenticate User (any role) + in: header + name: Authorization + required: true + type: string + - description: Quiz id + format: uuid + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/web.BaseResponse' + - properties: + data: + $ref: '#/definitions/quiz.QuizDetail' + type: object + summary: Get Quiz Solution + tags: + - quiz + /quiz/{id}/take: + post: + consumes: + - application/json + description: Finish quiz session and get the score + parameters: + - description: Authenticate User (any role) + in: header + name: Authorization + required: true + type: string + - description: Quiz Finish payload + in: body + name: data + required: true + schema: + $ref: '#/definitions/quiz.FinishQuizPayload' + - description: Quiz id + format: uuid + in: path + name: id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + allOf: + - $ref: '#/definitions/web.BaseResponse' + - properties: + data: + $ref: '#/definitions/quiz.QuizDetail' + type: object + summary: Finish Quiz + tags: + - quiz /reset/confirm: put: description: Do confirmation to reset password diff --git a/handler/admin/addUser.go b/handler/admin/addUser.go index 02c631be6d59ed66f628b57dfa4ace8191b05249..71adae1097c94697a7666f4798f3ea65abfb43fb 100644 --- a/handler/admin/addUser.go +++ b/handler/admin/addUser.go @@ -7,14 +7,14 @@ import ( // Index godoc // -// @Tags admin -// @Summary Add User +// @Tags admin +// @Summary Add User // @Description Add a user to database -// @Produce json -// @Accept json -// @Param data body req.AdminAddUserPayload true "Admin Add User Payload" -// @Success 200 {object} web.BaseResponse -// @Router /admin/user [post] +// @Produce json +// @Accept json +// @Param data body req.AdminAddUserPayload true "Admin Add User Payload" +// @Success 200 {object} web.BaseResponse +// @Router /admin/user [post] func (route AdminHandlerImpl) AddUser(w http.ResponseWriter, r *http.Request){ payload := req.AdminAddUserPayload{} diff --git a/handler/admin/deleteUser.go b/handler/admin/deleteUser.go index 9979a47a2279814f9de6c9302c3a555531a659aa..c9757d2d663584b0e675a1a27f3c4bc89a445a3f 100644 --- a/handler/admin/deleteUser.go +++ b/handler/admin/deleteUser.go @@ -7,13 +7,13 @@ import ( // Index godoc // -// @Tags admin -// @Summary Delete User By Id +// @Tags admin +// @Summary Delete User By Id // @Description Delete a user from database -// @Produce json -// @Accept json -// @Success 200 {object} web.BaseResponse -// @Router /admin/user/{email} [delete] +// @Produce json +// @Accept json +// @Success 200 {object} web.BaseResponse +// @Router /admin/user/{email} [delete] func (route AdminHandlerImpl) DeleteUser(w http.ResponseWriter, r *http.Request){ email := path.Base(r.URL.Path) diff --git a/handler/admin/getAllUser.go b/handler/admin/getAllUser.go index 32771d8d99fcf9d82f53279bd70706a35d30f1b7..31732077a1ef80d9745efb59dc2891a95e0c0366 100644 --- a/handler/admin/getAllUser.go +++ b/handler/admin/getAllUser.go @@ -6,12 +6,12 @@ import ( // Index godoc // -// @Tags admin -// @Summary Get All User +// @Tags admin +// @Summary Get All User // @Description Get all users from database -// @Produce json -// @Success 200 {object} web.BaseResponse -// @Router /admin/user [get] +// @Produce json +// @Success 200 {object} web.BaseResponse +// @Router /admin/user [get] func (route AdminHandlerImpl) GetAllUser(w http.ResponseWriter, r *http.Request){ // get all user from service users, err := route.AdminService.GetAllUser() diff --git a/handler/admin/getUserByEmail.go b/handler/admin/getUserByEmail.go index fd21301db325bf6d59eb8df4404f352ba4fd2b29..7326be4653cd8fc165d60e49b24c152e14e4ea97 100644 --- a/handler/admin/getUserByEmail.go +++ b/handler/admin/getUserByEmail.go @@ -7,12 +7,12 @@ import ( // Index godoc // -// @Tags admin -// @Summary Get User By Email +// @Tags admin +// @Summary Get User By Email // @Description Get a user from database -// @Produce json -// @Success 200 {object} web.BaseResponse -// @Router /admin/user/{email} [get] +// @Produce json +// @Success 200 {object} web.BaseResponse +// @Router /admin/user/{email} [get] func (route AdminHandlerImpl) GetUserByEmail(w http.ResponseWriter, r *http.Request) { // email := r.URL.Query().Get("email") email := path.Base(r.URL.Path) diff --git a/handler/admin/updateUser.go b/handler/admin/updateUser.go index 877d5c84a3785626e9736ecf1d433d37fdf1df40..a2f91cbd93fb181c23983e9aae3b1ef9d89293e1 100644 --- a/handler/admin/updateUser.go +++ b/handler/admin/updateUser.go @@ -8,14 +8,14 @@ import ( // Index godoc // -// @Tags admin -// @Summary Update User By Id +// @Tags admin +// @Summary Update User By Id // @Description Update a user from database -// @Produce json -// @Accept json -// @Param data body req.AdminUpdateUserPayload true "Admin Update User Payload" -// @Success 200 {object} web.BaseResponse -// @Router /admin/user/{email} [patch] +// @Produce json +// @Accept json +// @Param data body req.AdminUpdateUserPayload true "Admin Update User Payload" +// @Success 200 {object} web.BaseResponse +// @Router /admin/user/{email} [patch] func (route AdminHandlerImpl) UpdateUser(w http.ResponseWriter, r *http.Request){ email := path.Base(r.URL.Path) // TODO: how to change email diff --git a/handler/auth/login.go b/handler/auth/login.go index 3ab6f0759cde01fa545c1299fb1d2f9b056afafa..309db782e4547e931c301bf4a4dd5a452aeb37a6 100644 --- a/handler/auth/login.go +++ b/handler/auth/login.go @@ -11,19 +11,19 @@ import ( // Index godoc // -// @Tags auth -// @Summary Login -// @Description Login and generate new pair of token -// @Produce json -// @Accept json -// @Param data body login.LoginRequestPayload true "Login payload" -// @Success 200 {object} web.BaseResponse{data=login.LoginResponsePayload} "Login Success" -// @Failure 400 {object} web.BaseResponse{data=[]string} "Bad Input" -// @Failure 403 {object} web.BaseResponse "Login Credential Error" -// @Failure 415 {object} web.BaseResponse "Not a json request" -// @Failure 422 {object} web.BaseResponse "Invalid JSON input" -// @Failure 500 {object} web.BaseResponse "Unknown Internal Error" -// @Router /auth/login [post] +// @Tags auth +// @Summary Login +// @Description Login and generate new pair of token +// @Produce json +// @Accept json +// @Param data body login.LoginRequestPayload true "Login payload" +// @Success 200 {object} web.BaseResponse{data=login.LoginResponsePayload} "Login Success" +// @Failure 400 {object} web.BaseResponse{data=[]string} "Bad Input" +// @Failure 403 {object} web.BaseResponse "Login Credential Error" +// @Failure 415 {object} web.BaseResponse "Not a json request" +// @Failure 422 {object} web.BaseResponse "Invalid JSON input" +// @Failure 500 {object} web.BaseResponse "Unknown Internal Error" +// @Router /auth/login [post] func (a AuthHandlerImpl) Login(w http.ResponseWriter, r *http.Request) { payload := login.LoginRequestPayload{} validate := validator.New() diff --git a/handler/auth/refresh.go b/handler/auth/refresh.go index 20eaed5d8803d078beed923cf0855b9bc394f438..78a8a9b03f7386b7830f20fc30725e50e39b67d7 100644 --- a/handler/auth/refresh.go +++ b/handler/auth/refresh.go @@ -11,17 +11,17 @@ import ( // Index godoc // -// @Tags auth -// @Summary Refresh Token -// @Description Generate new access token -// @Produce json -// @Accept json -// @Param Authorization header string true "Refresh token" -// @Success 200 {object} web.BaseResponse{data=refresh.RefreshResponsePayload} -// @Failure 400 {object} web.BaseResponse -// @Failure 401 {object} web.BaseResponse -// @Failure 500 {object} web.BaseResponse -// @Router /auth/refresh [post] +// @Tags auth +// @Summary Refresh Token +// @Description Generate new access token +// @Produce json +// @Accept json +// @Param Authorization header string true "Refresh token" +// @Success 200 {object} web.BaseResponse{data=refresh.RefreshResponsePayload} +// @Failure 400 {object} web.BaseResponse +// @Failure 401 {object} web.BaseResponse +// @Failure 500 {object} web.BaseResponse +// @Router /auth/refresh [post] func (a AuthHandlerImpl) Refresh(w http.ResponseWriter, r *http.Request) { payload := refresh.RefreshRequestPayload{} diff --git a/handler/auth/register.go b/handler/auth/register.go index 8a24f5da071967f00ddcea7e973bd6a46230fa1b..37a50b67dc97cdb86f925e54ff07439629a35571 100644 --- a/handler/auth/register.go +++ b/handler/auth/register.go @@ -12,16 +12,16 @@ import ( // Index godoc // -// @Tags auth -// @Summary Register New Account -// @Description Generate New Account as Member -// @Produce json -// @Accept json -// @Param data body register.RegisterRequestPayload true "Register Payload" -// @Success 200 {object} web.BaseResponse -// @Failure 400 {object} web.BaseResponse -// @Failure 500 {object} web.BaseResponse -// @Router /auth/register [post] +// @Tags auth +// @Summary Register New Account +// @Description Generate New Account as Member +// @Produce json +// @Accept json +// @Param data body register.RegisterRequestPayload true "Register Payload" +// @Success 200 {object} web.BaseResponse +// @Failure 400 {object} web.BaseResponse +// @Failure 500 {object} web.BaseResponse +// @Router /auth/register [post] func (a AuthHandlerImpl) Register(w http.ResponseWriter, r *http.Request) { payload := register.RegisterRequestPayload{} validate := validator.New() diff --git a/handler/auth/send_verify.go b/handler/auth/send_verify.go index b2b4a2e70eaca89ac5de5f26a76fb4210d894416..b2b41d0473dcdd91c72840c20e903ac635fce7b3 100644 --- a/handler/auth/send_verify.go +++ b/handler/auth/send_verify.go @@ -11,16 +11,16 @@ import ( // Index godoc // -// @Tags auth -// @Summary Send Email Verification -// @Description Send Email Verification to user -// @Produce json -// @Accept json -// @Param data body verification.VerificationSendRequestPayload true "Register Payload" -// @Success 200 {object} web.BaseResponse -// @Failure 400 {object} web.BaseResponse -// @Failure 500 {object} web.BaseResponse -// @Router /auth/verify/resend [post] +// @Tags auth +// @Summary Send Email Verification +// @Description Send Email Verification to user +// @Produce json +// @Accept json +// @Param data body verification.VerificationSendRequestPayload true "Register Payload" +// @Success 200 {object} web.BaseResponse +// @Failure 400 {object} web.BaseResponse +// @Failure 500 {object} web.BaseResponse +// @Router /auth/verify/resend [post] func (a AuthHandlerImpl) SendEmailVerify(w http.ResponseWriter, r *http.Request) { payload := verification.VerificationSendRequestPayload{} validate := validator.New() diff --git a/handler/auth/verify.go b/handler/auth/verify.go index a118518f67d6d8e4dff329541d93d406f545cbca..1e264a7cb810bad925d579dc67df080987961244 100644 --- a/handler/auth/verify.go +++ b/handler/auth/verify.go @@ -11,16 +11,16 @@ import ( // Index godoc // -// @Tags auth -// @Summary Do Email Verification -// @Description Do Email Verification to user -// @Produce json -// @Accept json -// @Param data body verification.VerificationRequestPayload true "Register Payload" -// @Success 200 {object} web.BaseResponse -// @Failure 400 {object} web.BaseResponse -// @Failure 500 {object} web.BaseResponse -// @Router /auth/verify [post] +// @Tags auth +// @Summary Do Email Verification +// @Description Do Email Verification to user +// @Produce json +// @Accept json +// @Param data body verification.VerificationRequestPayload true "Register Payload" +// @Success 200 {object} web.BaseResponse +// @Failure 400 {object} web.BaseResponse +// @Failure 500 {object} web.BaseResponse +// @Router /auth/verify [post] func (a AuthHandlerImpl) EmailVerify(w http.ResponseWriter, r *http.Request) { payload := verification.VerificationRequestPayload{} validate := validator.New() diff --git a/handler/common/home.go b/handler/common/home.go index 7266a95ec32bcbccd657df123bb8fad5e0ef689a..272eda7d2f19bce70362e0de12c23c749323b23f 100644 --- a/handler/common/home.go +++ b/handler/common/home.go @@ -6,12 +6,12 @@ import ( // Index godoc // -// @Tags common -// @Summary Index page +// @Tags common +// @Summary Index page // @Description Give server index page response -// @Produce json -// @Success 200 {object} web.BaseResponse -// @Router / [get] +// @Produce json +// @Success 200 {object} web.BaseResponse +// @Router / [get] func (route CommonHandlerImpl) Home(w http.ResponseWriter, r *http.Request) { payload := route.WrapperUtil.SuccessResponseWrap(route.CommonService.Home()) route.HttpUtil.WriteSuccessJson(w, payload) diff --git a/handler/course/addCourse.go b/handler/course/addCourse.go index c07f9ce00468bf9589df78b295922432342e307e..4db1e673301c5b16d6de69ee20de09df593af416 100644 --- a/handler/course/addCourse.go +++ b/handler/course/addCourse.go @@ -7,12 +7,27 @@ import ( "github.com/go-playground/validator/v10" "gitlab.informatika.org/ocw/ocw-backend/model/web" - "gitlab.informatika.org/ocw/ocw-backend/model/web/course/add" + "gitlab.informatika.org/ocw/ocw-backend/model/web/course" ) +// Index godoc +// +// @Summary Add a new course +// @Description Add a new course with the given details +// @Tags course +// @Accept json +// @Produce json +// @Param Authorization header string true "AddCourseToken" +// @Param data body course.AddCourseRequestPayload true "Add Course payload" +// @Success 200 {object} web.BaseResponse "Success" +// @Failure 400 {object} web.BaseResponse "Bad Request" +// @Failure 401 {object} web.BaseResponse "Unauthorized" +// @Failure 403 {object} web.BaseResponse "Forbidden" +// @Failure 422 {object} web.BaseResponse "Unprocessable Entity" +// @Failure 500 {object} web.BaseResponse "Internal Server Error" +// @Router /course [put] func (c CourseHandlerImpl) AddCourse(w http.ResponseWriter, r *http.Request) { - payload := add.AddCourseRequestPayload{} - validate := validator.New() + payload := course.AddCourseRequestPayload{} // Validate payload if r.Header.Get("Content-Type") != "application/json" { @@ -27,6 +42,7 @@ func (c CourseHandlerImpl) AddCourse(w http.ResponseWriter, r *http.Request) { return } + validate := validator.New() if err := validate.Struct(payload); err != nil { if _, ok := err.(*validator.InvalidValidationError); ok { payload := c.WrapperUtil.ErrorResponseWrap(err.Error(), nil) @@ -83,4 +99,4 @@ func (c CourseHandlerImpl) AddCourse(w http.ResponseWriter, r *http.Request) { responsePayload := c.WrapperUtil.SuccessResponseWrap(nil) c.HttpUtil.WriteSuccessJson(w, responsePayload) -} \ No newline at end of file +} diff --git a/handler/course/addFaculty.go b/handler/course/addFaculty.go index a0430da8c4541a971771b86ef0d8cd3a1a002927..02269011fae0b1eb68de8ed75d52037dc02704df 100644 --- a/handler/course/addFaculty.go +++ b/handler/course/addFaculty.go @@ -7,11 +7,27 @@ import ( "github.com/go-playground/validator/v10" "gitlab.informatika.org/ocw/ocw-backend/model/web" - "gitlab.informatika.org/ocw/ocw-backend/model/web/course/faculty/add" + "gitlab.informatika.org/ocw/ocw-backend/model/web/course/faculty" ) +// Index godoc +// +// @Summary Add a new faculty +// @Description Adds a new faculty with the given details +// @Tags course +// @Accept json +// @Produce json +// @Param Authorization header string true "AddFacultyToken" +// @Param data body faculty.AddFacultyRequestPayload true "Add Faculty payload" +// @Success 200 {object} web.BaseResponse "Success" +// @Failure 400 {object} web.BaseResponse "Bad Request" +// @Failure 401 {object} web.BaseResponse "Unauthorized" +// @Failure 403 {object} web.BaseResponse "Forbidden" +// @Failure 422 {object} web.BaseResponse "Unprocessable Entity" +// @Failure 500 {object} web.BaseResponse "Internal Server Error" +// @Router /course/faculty [put] func (c CourseHandlerImpl) AddFaculty(w http.ResponseWriter, r *http.Request) { - payload := add.AddFacultyRequestPayload{} + payload := faculty.AddFacultyRequestPayload{} validate := validator.New() // Validate payload diff --git a/handler/course/addMajor.go b/handler/course/addMajor.go index 3f3c3ab0faaf177ab6fc12a1da902dac2b8d7ad8..e73fb420cc51a7b8efe71b666a741e909b14cc8b 100644 --- a/handler/course/addMajor.go +++ b/handler/course/addMajor.go @@ -7,11 +7,27 @@ import ( "github.com/go-playground/validator/v10" "gitlab.informatika.org/ocw/ocw-backend/model/web" - "gitlab.informatika.org/ocw/ocw-backend/model/web/course/major/add" + "gitlab.informatika.org/ocw/ocw-backend/model/web/course/major" ) +// Index godoc +// +// @Summary Add Major +// @Description Add a new major to a faculty +// @Tags course +// @Accept json +// @Produce json +// @Param Authorization header string true "AddMajorToken" +// @Param data body major.AddMajorRequestPayload true "Add Major payload" +// @Success 200 {object} web.BaseResponse "Success" +// @Failure 400 {object} web.BaseResponse "Bad Request" +// @Failure 401 {object} web.BaseResponse "Unauthorized" +// @Failure 403 {object} web.BaseResponse "Forbidden" +// @Failure 422 {object} web.BaseResponse "Unprocessable Entity" +// @Failure 500 {object} web.BaseResponse "Internal Server Error" +// @Router /course/major [put] func (c CourseHandlerImpl) AddMajor(w http.ResponseWriter, r *http.Request) { - payload := add.AddMajorRequestPayload{} + payload := major.AddMajorRequestPayload{} validate := validator.New() // Validate payload diff --git a/handler/course/deleteCourse.go b/handler/course/deleteCourse.go index a06a7a3e41c72742cb6bda478ecb34def0791056..c97221b8a230396c95353715e99f76b37e500592 100644 --- a/handler/course/deleteCourse.go +++ b/handler/course/deleteCourse.go @@ -3,14 +3,32 @@ package course import ( "fmt" "net/http" + "path" "strings" "gitlab.informatika.org/ocw/ocw-backend/model/web" - "gitlab.informatika.org/ocw/ocw-backend/model/web/course/delete" + "gitlab.informatika.org/ocw/ocw-backend/model/web/course" ) +// Index godoc +// +// @Summary Delete course by ID +// @Description Delete a course with the specified ID +// @Tags course +// @Accept json +// @Produce json +// @Param id path string true "Course ID" +// @Param Authorization header string true "DeleteCourseToken" +// @Success 200 {object} web.BaseResponse "Success" +// @Failure 400 {object} web.BaseResponse "Bad Request" +// @Failure 401 {object} web.BaseResponse "Unauthorized" +// @Failure 403 {object} web.BaseResponse "Forbidden" +// @Failure 422 {object} web.BaseResponse "Unprocessable Entity" +// @Failure 500 {object} web.BaseResponse "Internal Server Error" +// @Router /course/{id} [delete] func (c CourseHandlerImpl) DeleteCourse(w http.ResponseWriter, r *http.Request) { - payload := delete.DeleteByStringRequestPayload{} + payload := course.DeleteByStringRequestPayload{} + payload.ID = path.Base(r.URL.Path) validateTokenHeader := r.Header.Get("Authorization") if validateTokenHeader == "" { diff --git a/handler/course/getCourse.go b/handler/course/getCourse.go index 290087fda55ac0219d3ad037f5c3a38415453056..7f20ae8af1d132fd515b2a6507ba54a8948cbc04 100644 --- a/handler/course/getCourse.go +++ b/handler/course/getCourse.go @@ -6,11 +6,22 @@ import ( "path" "gitlab.informatika.org/ocw/ocw-backend/model/web" - "gitlab.informatika.org/ocw/ocw-backend/model/web/course/get" + "gitlab.informatika.org/ocw/ocw-backend/model/web/course" ) +// Index godoc +// +// @Summary Get a course by ID +// @Description Retrieve a course by ID +// @Tags course +// @Produce json +// @Param id path string true "ID of the course to retrieve" +// @Success 200 {object} web.BaseResponse "OK" +// @Failure 401 {object} web.BaseResponse "Unauthorized" +// @Failure 500 {object} web.BaseResponse "Internal Server Error" +// @Router /course/{id} [get] func (c CourseHandlerImpl) GetCourse(w http.ResponseWriter, r *http.Request) { - payload := get.GetByStringRequestPayload{} + payload := course.GetByStringRequestPayload{} payload.ID = path.Base(r.URL.Path) packet, err := c.CourseService.GetCourse(payload) diff --git a/handler/course/getCourses.go b/handler/course/getCourses.go index 9280e47c7d96abd7f12d5188cc1c11c6ce927bab..e22ed91542d2442ffae6b580aba3505bec8b857a 100644 --- a/handler/course/getCourses.go +++ b/handler/course/getCourses.go @@ -7,6 +7,16 @@ import ( "gitlab.informatika.org/ocw/ocw-backend/model/web" ) +// Index godoc +// +// @Summary Get all courses +// @Description Retrieve a list of all available courses. +// @Tags course +// @Produce json +// @Success 200 {object} web.BaseResponse "OK" +// @Failure 401 {object} web.BaseResponse "Unauthorized" +// @Failure 500 {object} web.BaseResponse "Internal Server Error" +// @Router /course [get] func (c CourseHandlerImpl) GetCourses(w http.ResponseWriter, r *http.Request) { packet, err := c.CourseService.GetAllCourse() diff --git a/handler/course/getCoursesByFaculty.go b/handler/course/getCoursesByFaculty.go index a59f6a82342b6f3989c4f7b95585c4e59f1b7ff1..c708d4ecaf0984319d1d7a945b15622ceeefa2ea 100644 --- a/handler/course/getCoursesByFaculty.go +++ b/handler/course/getCoursesByFaculty.go @@ -7,11 +7,23 @@ import ( "github.com/google/uuid" "gitlab.informatika.org/ocw/ocw-backend/model/web" - "gitlab.informatika.org/ocw/ocw-backend/model/web/course/get" + "gitlab.informatika.org/ocw/ocw-backend/model/web/course" ) +// Index godoc +// +// @Summary Get all courses by faculty ID +// @Description Retrieve all courses that belong to the faculty with the given ID +// @Tags course +// @Produce json +// @Param id path string true "Faculty ID (UUID)" +// @Success 200 {object} web.BaseResponse "Success" +// @Failure 400 {object} web.BaseResponse "Invalid UUID provided in request path" +// @Failure 401 {object} web.BaseResponse "Unauthorized" +// @Failure 500 {object} web.BaseResponse "Internal Server Error" +// @Router /course/faculty/courses/{id} [get] func (c CourseHandlerImpl) GetCoursesByFaculty(w http.ResponseWriter, r *http.Request) { - payload := get.GetByUUIDRequestPayload{} + payload := course.GetByUUIDRequestPayload{} id, err := uuid.Parse(path.Base(r.URL.Path)) if err != nil { diff --git a/handler/course/getCoursesByMajor.go b/handler/course/getCoursesByMajor.go index 3f8413871825b2bcc11ad1579f3c82d56b294f58..5bfeadbbd47bd56fdab00cf9ef1b3ce4ad168601 100644 --- a/handler/course/getCoursesByMajor.go +++ b/handler/course/getCoursesByMajor.go @@ -7,11 +7,23 @@ import ( "github.com/google/uuid" "gitlab.informatika.org/ocw/ocw-backend/model/web" - "gitlab.informatika.org/ocw/ocw-backend/model/web/course/get" + "gitlab.informatika.org/ocw/ocw-backend/model/web/course" ) +// Index godoc +// +// @Summary Get courses by major +// @Description Retrieve all courses related to a major +// @Tags course +// @Produce json +// @Param id path string true "Major ID (UUID)" +// @Success 200 {object} web.BaseResponse "Success" +// @Failure 400 {object} web.BaseResponse "Invalid UUID provided in request path" +// @Failure 401 {object} web.BaseResponse "Unauthorized" +// @Failure 500 {object} web.BaseResponse "Internal Server Error" +// @Router /course/major/courses/{id} [get] func (c CourseHandlerImpl) GetCoursesByMajor(w http.ResponseWriter, r *http.Request) { - payload := get.GetByUUIDRequestPayload{} + payload := course.GetByUUIDRequestPayload{} id, err := uuid.Parse(path.Base(r.URL.Path)) if err != nil { diff --git a/handler/course/getFaculties.go b/handler/course/getFaculties.go index 65473e5b47aa33f10c47f696dd4dd974896773d2..d51c8090ad70355bd0c3c9b0903c57c8bb10dde7 100644 --- a/handler/course/getFaculties.go +++ b/handler/course/getFaculties.go @@ -6,7 +6,16 @@ import ( "gitlab.informatika.org/ocw/ocw-backend/model/web" ) - +// Index godoc +// +// @Summary Get all faculties +// @Description Retrieves a list of all faculties +// @Tags course +// @Produce json +// @Success 200 {object} web.BaseResponse "OK" +// @Failure 401 {object} web.BaseResponse "Unauthorized" +// @Failure 500 {object} web.BaseResponse "Internal Server Error" +// @Router /course/faculty [get] func (c CourseHandlerImpl) GetFaculties(w http.ResponseWriter, r *http.Request) { packet, err := c.CourseService.GetAllFaculty() diff --git a/handler/course/getFaculty.go b/handler/course/getFaculty.go index e50d77b17d5ec0482e1b96ee3a9901dc7b58b43b..494d3c0aeeb91699c6c62c92fc12d3f3a807b1a5 100644 --- a/handler/course/getFaculty.go +++ b/handler/course/getFaculty.go @@ -7,11 +7,23 @@ import ( "github.com/google/uuid" "gitlab.informatika.org/ocw/ocw-backend/model/web" - "gitlab.informatika.org/ocw/ocw-backend/model/web/course/get" + "gitlab.informatika.org/ocw/ocw-backend/model/web/course" ) +// Index godoc +// +// @Summary Get faculty by ID +// @Description Retrieve faculty data by UUID +// @Tags course +// @Produce json +// @Param id path string true "Faculty ID (UUID)" +// @Success 200 {object} web.BaseResponse "Success" +// @Failure 400 {object} web.BaseResponse "Invalid UUID provided in request path" +// @Failure 401 {object} web.BaseResponse "Unauthorized" +// @Failure 500 {object} web.BaseResponse "Internal Server Error" +// @Router /course/faculty/{id} [get] func (c CourseHandlerImpl) GetFaculty(w http.ResponseWriter, r *http.Request) { - payload := get.GetByUUIDRequestPayload{} + payload := course.GetByUUIDRequestPayload{} id, err := uuid.Parse(path.Base(r.URL.Path)) if err != nil { diff --git a/handler/course/getMajor.go b/handler/course/getMajor.go index 8413ce7f474dc9a6e4259ea3a65fed1ee770cd02..fe3a4952790af76aee0ccf74423146390963dddb 100644 --- a/handler/course/getMajor.go +++ b/handler/course/getMajor.go @@ -7,11 +7,23 @@ import ( "github.com/google/uuid" "gitlab.informatika.org/ocw/ocw-backend/model/web" - "gitlab.informatika.org/ocw/ocw-backend/model/web/course/get" + "gitlab.informatika.org/ocw/ocw-backend/model/web/course" ) +// Index godoc +// +// @Summary Get a major by ID +// @Description Get a major by ID +// @Tags course +// @Produce json +// @Param id path string true "Major ID (UUID)" +// @Success 200 {object} web.BaseResponse "Success" +// @Failure 400 {object} web.BaseResponse "Invalid UUID provided in request path" +// @Failure 401 {object} web.BaseResponse "Unauthorized" +// @Failure 500 {object} web.BaseResponse "Internal Server Error" +// @Router /course/major/{id} [get] func (c CourseHandlerImpl) GetMajor(w http.ResponseWriter, r *http.Request) { - payload := get.GetByUUIDRequestPayload{} + payload := course.GetByUUIDRequestPayload{} id, err := uuid.Parse(path.Base(r.URL.Path)) if err != nil { diff --git a/handler/course/getMajors.go b/handler/course/getMajors.go index 05466ce40e6e430b284a0e98f905fd0c14fe3869..cdb9d01fc3abd37c2faab0f53e02c69cc00adc5b 100644 --- a/handler/course/getMajors.go +++ b/handler/course/getMajors.go @@ -7,6 +7,16 @@ import ( "gitlab.informatika.org/ocw/ocw-backend/model/web" ) +// Index godoc +// +// @Summary Get all majors +// @Description Get a list of all majors +// @Tags course +// @Produce json +// @Success 200 {object} web.BaseResponse "OK" +// @Failure 401 {object} web.BaseResponse "Unauthorized" +// @Failure 500 {object} web.BaseResponse "Internal Server Error" +// @Router /course/major [get] func (c CourseHandlerImpl) GetMajors(w http.ResponseWriter, r *http.Request) { packet, err := c.CourseService.GetAllMajor() diff --git a/handler/course/getMajorsByFaculty.go b/handler/course/getMajorsByFaculty.go index abdedb8b72337808f4f092806784ddcf3bdefd5b..9eafc3d088a810f5879c27b2b20d7e21da995d3e 100644 --- a/handler/course/getMajorsByFaculty.go +++ b/handler/course/getMajorsByFaculty.go @@ -7,11 +7,22 @@ import ( "github.com/google/uuid" "gitlab.informatika.org/ocw/ocw-backend/model/web" - "gitlab.informatika.org/ocw/ocw-backend/model/web/course/get" + "gitlab.informatika.org/ocw/ocw-backend/model/web/course" ) - +// Index godoc +// +// @Summary Get majors by faculty ID +// @Description Returns a list of majors associated with the given faculty ID +// @Tags course +// @Produce json +// @Param id path string true "Faculty ID (UUID)" +// @Success 200 {object} web.BaseResponse "Success" +// @Failure 400 {object} web.BaseResponse "Invalid UUID provided in request path" +// @Failure 401 {object} web.BaseResponse "Unauthorized" +// @Failure 500 {object} web.BaseResponse "Internal Server Error" +// @Router /course/faculty/majors/{id} [get] func (c CourseHandlerImpl) GetMajorsByFaculty(w http.ResponseWriter, r *http.Request) { - payload := get.GetByUUIDRequestPayload{} + payload := course.GetByUUIDRequestPayload{} id, err := uuid.Parse(path.Base(r.URL.Path)) if err != nil { diff --git a/handler/course/updateCourse.go b/handler/course/updateCourse.go index 92dfef946154ecfd72775d44be63a3bb31bdb505..4e2ded686a6281dd2e6baebd5b8868fdcf92c150 100644 --- a/handler/course/updateCourse.go +++ b/handler/course/updateCourse.go @@ -5,13 +5,31 @@ import ( "net/http" "strings" + "github.com/go-chi/chi/v5" "github.com/go-playground/validator/v10" "gitlab.informatika.org/ocw/ocw-backend/model/web" - "gitlab.informatika.org/ocw/ocw-backend/model/web/course/update" + "gitlab.informatika.org/ocw/ocw-backend/model/web/course" ) +// Index godoc +// +// @Summary Update Course +// @Description Update an existing course. +// @Tags course +// @Accept json +// @Produce json +// @Param id path int true "Course ID" +// @Param Authorization header string true "UpdateCourseToken" +// @Param data body course.UpdateCourseRequestPayload true "Update Course Payload" +// @Success 200 {object} web.BaseResponse "Success" +// @Failure 400 {object} web.BaseResponse "Bad Request" +// @Failure 401 {object} web.BaseResponse "Unauthorized" +// @Failure 403 {object} web.BaseResponse "Forbidden" +// @Failure 422 {object} web.BaseResponse "Unprocessable Entity" +// @Failure 500 {object} web.BaseResponse "Internal Server Error" +// @Router /course/{id} [patch] func (c CourseHandlerImpl) UpdateCourse(w http.ResponseWriter, r *http.Request) { - payload := update.UpdateCourseRequestPayload{} + payload := course.UpdateCourseRequestPayload{} validate := validator.New() // Validate payload @@ -64,6 +82,7 @@ func (c CourseHandlerImpl) UpdateCourse(w http.ResponseWriter, r *http.Request) } payload.UpdateCourseToken = token[1] + payload.ID = chi.URLParam(r, "id") err := c.CourseService.UpdateCourse(payload) if err != nil { @@ -83,4 +102,4 @@ func (c CourseHandlerImpl) UpdateCourse(w http.ResponseWriter, r *http.Request) responsePayload := c.WrapperUtil.SuccessResponseWrap(nil) c.HttpUtil.WriteSuccessJson(w, responsePayload) -} \ No newline at end of file +} diff --git a/handler/course/updateFaculty.go b/handler/course/updateFaculty.go index e79c7e51b717479b76ed11e0ff17a80a0f0d52ec..c27eca05f9aff3ab7f3d50984e0760a69e67d3b8 100644 --- a/handler/course/updateFaculty.go +++ b/handler/course/updateFaculty.go @@ -5,13 +5,30 @@ import ( "net/http" "strings" + "github.com/go-chi/chi/v5" "github.com/go-playground/validator/v10" + "github.com/google/uuid" "gitlab.informatika.org/ocw/ocw-backend/model/web" - "gitlab.informatika.org/ocw/ocw-backend/model/web/course/faculty/update" + "gitlab.informatika.org/ocw/ocw-backend/model/web/course/faculty" ) +// Index godoc +// +// @Summary Update a faculty +// @Description Update a faculty with the given ID +// @Tags course +// @Param id path string true "Faculty ID (UUID)" +// @Param payload body faculty.UpdateFacultyRequestPayload true "Update Faculty Payload" +// @Param Authorization header string true "UpdateFacultyToken" +// @Success 200 {object} web.BaseResponse "Success" +// @Failure 400 {object} web.BaseResponse "Bad Request" +// @Failure 401 {object} web.BaseResponse "Unauthorized" +// @Failure 403 {object} web.BaseResponse "Forbidden" +// @Failure 422 {object} web.BaseResponse "Unprocessable Entity" +// @Failure 500 {object} web.BaseResponse "Internal Server Error" +// @Router /course/faculty/{id} [patch] func (c CourseHandlerImpl) UpdateFaculty(w http.ResponseWriter, r *http.Request) { - payload := update.UpdateFacultyRequestPayload{} + payload := faculty.UpdateFacultyRequestPayload{} validate := validator.New() // Validate payload @@ -63,8 +80,17 @@ func (c CourseHandlerImpl) UpdateFaculty(w http.ResponseWriter, r *http.Request) return } + id, err := uuid.Parse(chi.URLParam(r, "id")) + + if err != nil { + payload := c.WrapperUtil.ErrorResponseWrap("invalid id", nil) + c.HttpUtil.WriteJson(w, http.StatusBadRequest, payload) + return + } + payload.UpdateFacultyToken = token[1] - err := c.CourseService.UpdateFaculty(payload) + payload.ID = id + err = c.CourseService.UpdateFaculty(payload) if err != nil { if errData, ok := err.(web.ResponseError); ok { @@ -83,4 +109,4 @@ func (c CourseHandlerImpl) UpdateFaculty(w http.ResponseWriter, r *http.Request) responsePayload := c.WrapperUtil.SuccessResponseWrap(nil) c.HttpUtil.WriteSuccessJson(w, responsePayload) -} \ No newline at end of file +} diff --git a/handler/course/updateMajor.go b/handler/course/updateMajor.go index b302b6e638f80206d3756f2af292b86373f2658a..ec802574680fb8f046bae1b7bb2e11031a41f282 100644 --- a/handler/course/updateMajor.go +++ b/handler/course/updateMajor.go @@ -5,13 +5,29 @@ import ( "net/http" "strings" + "github.com/go-chi/chi/v5" "github.com/go-playground/validator/v10" + "github.com/google/uuid" "gitlab.informatika.org/ocw/ocw-backend/model/web" - "gitlab.informatika.org/ocw/ocw-backend/model/web/course/major/update" + "gitlab.informatika.org/ocw/ocw-backend/model/web/course/major" ) - +// @Summary Update a major +// @Description Update a major with the given ID +// @Tags course +// @Accept json +// @Produce json +// @Param id path string true "Major ID (UUID)" +// @Param Authorization header string true "UpdateMajorToken" +// @Param payload body major.UpdateMajorRequestPayload true "Update Major payload" +// @Success 200 {object} web.BaseResponse "Success" +// @Failure 400 {object} web.BaseResponse "Bad Request" +// @Failure 401 {object} web.BaseResponse "Unauthorized" +// @Failure 403 {object} web.BaseResponse "Forbidden" +// @Failure 422 {object} web.BaseResponse "Unprocessable Entity" +// @Failure 500 {object} web.BaseResponse "Internal Server Error" +// @Router /course/major/{id} [put] func (c CourseHandlerImpl) UpdateMajor(w http.ResponseWriter, r *http.Request) { - payload := update.UpdateMajorRequestPayload{} + payload := major.UpdateMajorRequestPayload{} validate := validator.New() // Validate payload @@ -63,8 +79,17 @@ func (c CourseHandlerImpl) UpdateMajor(w http.ResponseWriter, r *http.Request) { return } + id, err := uuid.Parse(chi.URLParam(r, "id")) + + if err != nil { + payload := c.WrapperUtil.ErrorResponseWrap("invalid id", nil) + c.HttpUtil.WriteJson(w, http.StatusBadRequest, payload) + return + } + payload.UpdateMajorToken = token[1] - err := c.CourseService.UpdateMajor(payload) + payload.ID = id + err = c.CourseService.UpdateMajor(payload) if err != nil { if errData, ok := err.(web.ResponseError); ok { @@ -83,4 +108,4 @@ func (c CourseHandlerImpl) UpdateMajor(w http.ResponseWriter, r *http.Request) { responsePayload := c.WrapperUtil.SuccessResponseWrap(nil) c.HttpUtil.WriteSuccessJson(w, responsePayload) -} \ No newline at end of file +} diff --git a/handler/di.go b/handler/di.go index 0f3eae80a5bd38faa6a1e07e63e13a9c6c41e11e..399ef81ecbb23c9d70b0431c6129610c92d5dadc 100644 --- a/handler/di.go +++ b/handler/di.go @@ -6,6 +6,8 @@ import ( "gitlab.informatika.org/ocw/ocw-backend/handler/auth" "gitlab.informatika.org/ocw/ocw-backend/handler/common" "gitlab.informatika.org/ocw/ocw-backend/handler/course" + "gitlab.informatika.org/ocw/ocw-backend/handler/material" + "gitlab.informatika.org/ocw/ocw-backend/handler/quiz" "gitlab.informatika.org/ocw/ocw-backend/handler/reset" "gitlab.informatika.org/ocw/ocw-backend/handler/swagger" ) @@ -18,11 +20,11 @@ var HandlerSet = wire.NewSet( // Swagger wire.Struct(new(swagger.SwaggerHandlerImpl), "*"), wire.Bind(new(swagger.SwaggerHandler), new(*swagger.SwaggerHandlerImpl)), - + // Admin wire.Struct(new(admin.AdminHandlerImpl), "*"), wire.Bind(new(admin.AdminHandler), new(*admin.AdminHandlerImpl)), - + // Auth wire.Struct(new(auth.AuthHandlerImpl), "*"), wire.Bind(new(auth.AuthHandler), new(*auth.AuthHandlerImpl)), @@ -34,4 +36,12 @@ var HandlerSet = wire.NewSet( // Course wire.Struct(new(course.CourseHandlerImpl), "*"), wire.Bind(new(course.CourseHandler), new(*course.CourseHandlerImpl)), + + // Material + wire.Struct(new(material.MaterialHandlerImpl), "*"), + wire.Bind(new(material.MaterialHandler), new(*material.MaterialHandlerImpl)), + + // Quiz + wire.Struct(new(quiz.QuizHandlerImpl), "*"), + wire.Bind(new(quiz.QuizHandler), new(*quiz.QuizHandlerImpl)), ) diff --git a/handler/material/add_content.go b/handler/material/add_content.go new file mode 100644 index 0000000000000000000000000000000000000000..1f5511f2e9ddc83640d3429d67128fdbc6a23ff1 --- /dev/null +++ b/handler/material/add_content.go @@ -0,0 +1,113 @@ +package material + +import ( + "net/http" + + "github.com/go-chi/chi/v5" + "github.com/go-playground/validator/v10" + "github.com/google/uuid" + "gitlab.informatika.org/ocw/ocw-backend/middleware/guard" + materialDomain "gitlab.informatika.org/ocw/ocw-backend/model/domain/material" + "gitlab.informatika.org/ocw/ocw-backend/model/web" + authToken "gitlab.informatika.org/ocw/ocw-backend/model/web/auth/token" + "gitlab.informatika.org/ocw/ocw-backend/model/web/material" +) + +// Index godoc +// +// @Tags content +// @Summary Add Content +// @Description Add content of material +// @Produce json +// @Accept json +// @Param Authorization header string true "Access token" +// @Param data body material.NewContentRequest true "Add content request" +// @Param id path string true "Material id" Format(uuid) +// @Success 200 {object} web.BaseResponse{data=material.NewContentResponse} +// @Success 400 {object} web.BaseResponse +// @Success 401 {object} web.BaseResponse +// @Router /material/{id} [post] +func (m MaterialHandlerImpl) AddContent(w http.ResponseWriter, r *http.Request) { + payload := material.NewContentRequest{} + user, ok := r.Context().Value(guard.UserContext).(authToken.UserClaim) + + materialId := chi.URLParam(r, "material-id") + if materialId == "" { + payload := m.WrapperUtil.ErrorResponseWrap("material id is required", nil) + m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload) + } + + materialIdUUID, err := uuid.Parse(materialId) + if err != nil { + payload := m.WrapperUtil.ErrorResponseWrap("material id is invalid", err) + m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload) + } + + payload.MaterialId = materialIdUUID + + if !ok { + payload := m.WrapperUtil.ErrorResponseWrap("internal server error", nil) + m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload) + return + } + + validate := validator.New() + + if r.Header.Get("Content-Type") != "application/json" { + payload := m.WrapperUtil.ErrorResponseWrap("this service only receive json input", nil) + m.HttpUtil.WriteJson(w, http.StatusUnsupportedMediaType, payload) + return + } + + if err := m.HttpUtil.ParseJson(r, &payload); err != nil { + payload := m.WrapperUtil.ErrorResponseWrap("invalid json input", err.Error()) + m.HttpUtil.WriteJson(w, http.StatusUnprocessableEntity, payload) + return + } + + if err := validate.Struct(payload); err != nil { + if _, ok := err.(*validator.InvalidValidationError); ok { + payload := m.WrapperUtil.ErrorResponseWrap(err.Error(), nil) + m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload) + return + } + + errPayload := web.NewResponseErrorFromValidator(err.(validator.ValidationErrors)) + payload := m.WrapperUtil.ErrorResponseWrap(errPayload.Error(), errPayload) + m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload) + return + } + + if payload.Type != materialDomain.Handout && payload.Link == "" { + payload := m.WrapperUtil.ErrorResponseWrap("link is required", nil) + m.HttpUtil.WriteJson(w, http.StatusUnprocessableEntity, payload) + return + } + + uploadLink, err := m.MaterialContentService.AddContent(payload.MaterialId, user.Email, materialDomain.Content{ + Type: payload.Type, + Link: payload.Link, + }) + + if err != nil { + respErr, ok := err.(web.ResponseError) + if ok { + payload := m.WrapperUtil.ErrorResponseWrap(respErr.Error(), respErr) + + if respErr.Code != "NOT_OWNER" { + m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload) + } else { + m.HttpUtil.WriteJson(w, http.StatusForbidden, payload) + } + } else { + payload := m.WrapperUtil.ErrorResponseWrap("internal server error", nil) + m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload) + } + return + } + + responsePayload := m.WrapperUtil.SuccessResponseWrap(material.NewContentResponse{ + UploadLink: uploadLink, + }) + m.HttpUtil.WriteSuccessJson(w, responsePayload) +} diff --git a/handler/material/create_material.go b/handler/material/create_material.go new file mode 100644 index 0000000000000000000000000000000000000000..55565566908a318265a647ba142e3047d875a003 --- /dev/null +++ b/handler/material/create_material.go @@ -0,0 +1,108 @@ +package material + +import ( + "net/http" + + "github.com/go-chi/chi/v5" + "github.com/go-playground/validator/v10" + "gitlab.informatika.org/ocw/ocw-backend/middleware/guard" + "gitlab.informatika.org/ocw/ocw-backend/model/web" + authToken "gitlab.informatika.org/ocw/ocw-backend/model/web/auth/token" + "gitlab.informatika.org/ocw/ocw-backend/model/web/material" +) + +// Index godoc +// +// @Tags content +// @Summary Add Material +// @Description Add new material +// @Produce json +// @Accept json +// @Param Authorization header string true "Access token" +// @Param data body material.CreateMaterialRequest true "Material Request" +// @Param id path string true "Course id" example(IF3230) +// @Success 200 {object} web.BaseResponse{data=material.CreateMaterialResponse} +// @Success 400 {object} web.BaseResponse +// @Success 401 {object} web.BaseResponse +// @Router /course/{id}/material [post] +func (m MaterialHandlerImpl) CreateMaterial(w http.ResponseWriter, r *http.Request) { + payload := material.CreateMaterialRequest{} + courseId := chi.URLParam(r, "id") + + // START OF VALIDATE + if r.Header.Get("Content-Type") != "application/json" { + payload := m.WrapperUtil.ErrorResponseWrap("this service only receive json input", nil) + m.HttpUtil.WriteJson(w, http.StatusUnsupportedMediaType, payload) + return + } + + if err := m.HttpUtil.ParseJson(r, &payload); err != nil { + payload := m.WrapperUtil.ErrorResponseWrap("invalid json input", err.Error()) + m.HttpUtil.WriteJson(w, http.StatusUnprocessableEntity, payload) + return + } + + validate := validator.New() + if err := validate.Struct(payload); err != nil { + if _, ok := err.(*validator.InvalidValidationError); ok { + payload := m.WrapperUtil.ErrorResponseWrap(err.Error(), nil) + m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload) + return + } + + errPayload := web.NewResponseErrorFromValidator(err.(validator.ValidationErrors)) + payload := m.WrapperUtil.ErrorResponseWrap(errPayload.Error(), errPayload) + m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload) + return + } + // END OF VALIDATE + + if courseId == "" { + payload := m.WrapperUtil.ErrorResponseWrap("course id is required", nil) + m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload) + return + } + + if isExist, _ := m.CourseRepository.IsCourseExist(courseId); !isExist { + payload := m.WrapperUtil.ErrorResponseWrap("course id not found", nil) + m.HttpUtil.WriteJson(w, http.StatusNotFound, payload) + return + } + + user, ok := r.Context().Value(guard.UserContext).(authToken.UserClaim) + + if !ok { + m.Logger.Error("Context is not found") + payload := m.WrapperUtil.ErrorResponseWrap("internal server error", nil) + m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload) + return + } + + id, err := m.MaterialService.Create( + courseId, + user.Email, + payload.Name, + ) + + if err != nil { + respErr, ok := err.(web.ResponseError) + if ok { + payload := m.WrapperUtil.ErrorResponseWrap(respErr.Error(), respErr) + + if respErr.Code != "NOT_OWNER" { + m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload) + } else { + m.HttpUtil.WriteJson(w, http.StatusForbidden, payload) + } + } else { + payload := m.WrapperUtil.ErrorResponseWrap("internal server error", nil) + m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload) + } + return + } + + responsePayload := m.WrapperUtil.SuccessResponseWrap(material.CreateMaterialResponse{ + MaterialId: id, + }) + m.HttpUtil.WriteSuccessJson(w, responsePayload) +} diff --git a/handler/material/delete_content.go b/handler/material/delete_content.go new file mode 100644 index 0000000000000000000000000000000000000000..f67bcc3aec47601aeaec8dbfacfc4e3c79285095 --- /dev/null +++ b/handler/material/delete_content.go @@ -0,0 +1,89 @@ +package material + +import ( + "net/http" + + "github.com/go-chi/chi/v5" + "github.com/google/uuid" + "gitlab.informatika.org/ocw/ocw-backend/middleware/guard" + "gitlab.informatika.org/ocw/ocw-backend/model/web" + authToken "gitlab.informatika.org/ocw/ocw-backend/model/web/auth/token" +) + +// Index godoc +// +// @Tags content +// @Summary Delete Content +// @Description Delete content of material +// @Produce json +// @Accept json +// @Param Authorization header string true "Access token" +// @Param id path string true "Material id" Format(uuid) +// @Param content-id path string true "Content id" Format(uuid) +// @Success 200 {object} web.BaseResponse +// @Router /material/{id}/content/{content-id} [delete] +func (m MaterialHandlerImpl) DeleteContent(w http.ResponseWriter, r *http.Request) { + materialIdUnparsed := chi.URLParam(r, "material-id") + + if materialIdUnparsed == "" { + payload := m.WrapperUtil.ErrorResponseWrap("material id is required", nil) + m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload) + return + } + + materialId, err := uuid.Parse(materialIdUnparsed) + if err != nil { + payload := m.WrapperUtil.ErrorResponseWrap("material id is invalid", nil) + m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload) + return + } + + contentIdUnparsed := chi.URLParam(r, "content-id") + + if contentIdUnparsed == "" { + payload := m.WrapperUtil.ErrorResponseWrap("content id is required", nil) + m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload) + return + } + + contentId, err := uuid.Parse(contentIdUnparsed) + if err != nil { + payload := m.WrapperUtil.ErrorResponseWrap("material id is invalid", nil) + m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload) + return + } + + user, ok := r.Context().Value(guard.UserContext).(authToken.UserClaim) + + if !ok { + payload := m.WrapperUtil.ErrorResponseWrap("internal server error", nil) + m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload) + return + } + + err = m.MaterialContentService.DeleteContent( + materialId, + user.Email, + contentId, + ) + + if err != nil { + respErr, ok := err.(web.ResponseError) + if ok { + payload := m.WrapperUtil.ErrorResponseWrap(respErr.Error(), respErr) + + if respErr.Code != "NOT_OWNER" { + m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload) + } else { + m.HttpUtil.WriteJson(w, http.StatusForbidden, payload) + } + } else { + payload := m.WrapperUtil.ErrorResponseWrap("internal server error", nil) + m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload) + } + return + } + + responsePayload := m.WrapperUtil.SuccessResponseWrap(nil) + m.HttpUtil.WriteSuccessJson(w, responsePayload) +} diff --git a/handler/material/delete_material.go b/handler/material/delete_material.go new file mode 100644 index 0000000000000000000000000000000000000000..1bd275c35e0d690045f67f9b61b5f4f5cc705e92 --- /dev/null +++ b/handler/material/delete_material.go @@ -0,0 +1,73 @@ +package material + +import ( + "net/http" + + "github.com/go-chi/chi/v5" + "github.com/google/uuid" + "gitlab.informatika.org/ocw/ocw-backend/middleware/guard" + "gitlab.informatika.org/ocw/ocw-backend/model/web" + authToken "gitlab.informatika.org/ocw/ocw-backend/model/web/auth/token" +) + +// Index godoc +// +// @Tags content +// @Summary Delete material +// @Description Delete material +// @Produce json +// @Accept json +// @Param Authorization header string true "Access token" +// @Param id path string true "Material id" Format(uuid) +// @Success 200 {object} web.BaseResponse +// @Router /material/{id} [delete] +func (m MaterialHandlerImpl) DeleteMaterial(w http.ResponseWriter, r *http.Request) { + materialIdUnparsed := chi.URLParam(r, "material-id") + + if materialIdUnparsed == "" { + payload := m.WrapperUtil.ErrorResponseWrap("material id is required", nil) + m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload) + return + } + + materialId, err := uuid.Parse(materialIdUnparsed) + if err != nil { + payload := m.WrapperUtil.ErrorResponseWrap("material id is invalid", nil) + m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload) + return + } + + user, ok := r.Context().Value(guard.UserContext).(authToken.UserClaim) + + if !ok { + payload := m.WrapperUtil.ErrorResponseWrap("internal server error", nil) + m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload) + return + } + + err = m.MaterialService.Delete( + materialId, + user.Email, + ) + + if err != nil { + respErr, ok := err.(web.ResponseError) + if ok { + payload := m.WrapperUtil.ErrorResponseWrap(respErr.Error(), respErr) + + if respErr.Code != "NOT_OWNER" { + m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload) + } else { + m.HttpUtil.WriteJson(w, http.StatusForbidden, payload) + } + } else { + payload := m.WrapperUtil.ErrorResponseWrap("internal server error", nil) + m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload) + } + return + } + + responsePayload := m.WrapperUtil.SuccessResponseWrap(nil) + m.HttpUtil.WriteSuccessJson(w, responsePayload) + +} diff --git a/handler/material/detail_material.go b/handler/material/detail_material.go new file mode 100644 index 0000000000000000000000000000000000000000..87c428d09d68ddea8b6741dac355083f0082125b --- /dev/null +++ b/handler/material/detail_material.go @@ -0,0 +1,57 @@ +package material + +import ( + "net/http" + + "github.com/go-chi/chi/v5" + "github.com/google/uuid" + "gitlab.informatika.org/ocw/ocw-backend/model/web" +) + +// Index godoc +// +// @Tags content +// @Summary Get material detail +// @Description Get material detail +// @Produce json +// @Accept json +// @Param id path string true "Material id" example(IF3270) +// @Success 200 {object} web.BaseResponse{data=material.Material} +// @Router /material/{id} [get] +func (m MaterialHandlerImpl) DetailMaterial(w http.ResponseWriter, r *http.Request) { + idString := chi.URLParam(r, "material-id") + + if idString == "" { + payload := m.WrapperUtil.ErrorResponseWrap("material id is required", nil) + m.HttpUtil.WriteJson(w, http.StatusUnsupportedMediaType, payload) + return + } + + id, err := uuid.Parse(idString) + + if err != nil { + // invalid uuid + payload := m.WrapperUtil.ErrorResponseWrap(err.Error(), nil) + m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload) + return + } + + res, err := m.MaterialService.GetById(id) + + if err != nil { + respErr, ok := err.(web.ResponseError) + if ok { + payload := m.WrapperUtil.ErrorResponseWrap(respErr.Error(), respErr) + + m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload) + } else { + payload := m.WrapperUtil.ErrorResponseWrap("internal server error", nil) + m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload) + } + return + } + + responsePayload := m.WrapperUtil.SuccessResponseWrap(res) + m.HttpUtil.WriteSuccessJson(w, responsePayload) + +} diff --git a/handler/material/get_material.go b/handler/material/get_material.go new file mode 100644 index 0000000000000000000000000000000000000000..0ce5b5ab59c0aff2c604bb3965093ff3e9d242aa --- /dev/null +++ b/handler/material/get_material.go @@ -0,0 +1,46 @@ +package material + +import ( + "net/http" + + "github.com/go-chi/chi/v5" + "gitlab.informatika.org/ocw/ocw-backend/model/web" +) + +// Index godoc +// +// @Tags content +// @Summary Get materials +// @Description Get materials +// @Produce json +// @Accept json +// @Param id path string true "Course id" example(IF3270) +// @Success 200 {object} web.BaseResponse{data=[]material.Material} +// @Router /course/{id}/materials [get] +func (m MaterialHandlerImpl) GetMaterial(w http.ResponseWriter, r *http.Request) { + courseId := chi.URLParam(r, "id") + + if courseId == "" { + payload := m.WrapperUtil.ErrorResponseWrap("course id is required", nil) + m.HttpUtil.WriteJson(w, http.StatusUnsupportedMediaType, payload) + return + } + + result, err := m.MaterialService.Get(courseId) + + if err != nil { + respErr, ok := err.(web.ResponseError) + if ok { + payload := m.WrapperUtil.ErrorResponseWrap(respErr.Error(), respErr) + + m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload) + } else { + payload := m.WrapperUtil.ErrorResponseWrap("internal server error", nil) + m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload) + } + return + } + + responsePayload := m.WrapperUtil.SuccessResponseWrap(result) + m.HttpUtil.WriteSuccessJson(w, responsePayload) +} diff --git a/handler/material/impl.go b/handler/material/impl.go new file mode 100644 index 0000000000000000000000000000000000000000..f8aab59ca45cb8ce12796599e6b197e1a0d8c418 --- /dev/null +++ b/handler/material/impl.go @@ -0,0 +1,18 @@ +package material + +import ( + "gitlab.informatika.org/ocw/ocw-backend/repository/course" + "gitlab.informatika.org/ocw/ocw-backend/service/logger" + "gitlab.informatika.org/ocw/ocw-backend/service/material" + "gitlab.informatika.org/ocw/ocw-backend/utils/httputil" + "gitlab.informatika.org/ocw/ocw-backend/utils/wrapper" +) + +type MaterialHandlerImpl struct { + material.MaterialService + material.MaterialContentService + httputil.HttpUtil + logger.Logger + wrapper.WrapperUtil + course.CourseRepository +} diff --git a/handler/material/types.go b/handler/material/types.go new file mode 100644 index 0000000000000000000000000000000000000000..8d968e0f8332c295dcc480310efa3461457d8654 --- /dev/null +++ b/handler/material/types.go @@ -0,0 +1,13 @@ +package material + +import "net/http" + +type MaterialHandler interface { + AddContent(w http.ResponseWriter, r *http.Request) + DeleteContent(w http.ResponseWriter, r *http.Request) + + CreateMaterial(w http.ResponseWriter, r *http.Request) + DetailMaterial(w http.ResponseWriter, r *http.Request) + DeleteMaterial(w http.ResponseWriter, r *http.Request) + GetMaterial(w http.ResponseWriter, r *http.Request) +} diff --git a/handler/quiz/get.go b/handler/quiz/get.go new file mode 100644 index 0000000000000000000000000000000000000000..799ca822a991240a0c6febccb29640720f1b5e67 --- /dev/null +++ b/handler/quiz/get.go @@ -0,0 +1,57 @@ +package quiz + +import ( + "net/http" + + "github.com/go-chi/chi/v5" + "github.com/google/uuid" + "gitlab.informatika.org/ocw/ocw-backend/model/web" +) + +// Index godoc +// +// @Tags quiz +// @Summary Get Quiz Detail +// @Description Get Quiz Detail +// @Produce json +// @Accept json +// @Param id path string true "Quiz id" Format(uuid) +// @Success 200 {object} web.BaseResponse{data=quiz.Quiz} +// @Router /quiz/{id} [get] +func (m QuizHandlerImpl) GetQuizDetail(w http.ResponseWriter, r *http.Request) { + quizId := chi.URLParam(r, "id") + + if quizId == "" { + payload := m.WrapperUtil.ErrorResponseWrap("quiz id is required", nil) + m.HttpUtil.WriteJson(w, http.StatusUnsupportedMediaType, payload) + return + } + + id, err := uuid.Parse(quizId) + + if err != nil { + // invalid uuid + payload := m.WrapperUtil.ErrorResponseWrap(err.Error(), nil) + m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload) + return + } + + result, err := m.QuizService.GetQuizDetail(id) + + if err != nil { + respErr, ok := err.(web.ResponseError) + if ok { + payload := m.WrapperUtil.ErrorResponseWrap(respErr.Error(), respErr) + + m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload) + } else { + payload := m.WrapperUtil.ErrorResponseWrap("internal server error", nil) + m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload) + } + return + } + + responsePayload := m.WrapperUtil.SuccessResponseWrap(result) + m.HttpUtil.WriteSuccessJson(w, responsePayload) + +} diff --git a/handler/quiz/impl.go b/handler/quiz/impl.go new file mode 100644 index 0000000000000000000000000000000000000000..f95f7e5f0ca56fe2c605c5c3692eb5c4dab1008a --- /dev/null +++ b/handler/quiz/impl.go @@ -0,0 +1,15 @@ +package quiz + +import ( + "gitlab.informatika.org/ocw/ocw-backend/service/logger" + "gitlab.informatika.org/ocw/ocw-backend/service/quiz" + "gitlab.informatika.org/ocw/ocw-backend/utils/httputil" + "gitlab.informatika.org/ocw/ocw-backend/utils/wrapper" +) + +type QuizHandlerImpl struct { + quiz.QuizService + wrapper.WrapperUtil + httputil.HttpUtil + logger.Logger +} diff --git a/handler/quiz/list.go b/handler/quiz/list.go new file mode 100644 index 0000000000000000000000000000000000000000..544894fcc9985ee8ebea4f75fd3032da1ff0fd2d --- /dev/null +++ b/handler/quiz/list.go @@ -0,0 +1,50 @@ +package quiz + +import ( + "net/http" + + "github.com/go-chi/chi/v5" + "gitlab.informatika.org/ocw/ocw-backend/model/web" +) + +// Index godoc +// +// @Tags course +// @Summary Get Course quiz +// @Description Get all cours +// @Produce json +// @Accept json +// @Param id path string true "Course id" Format(uuid) +// @Success 200 {object} web.BaseResponse{data=[]quiz.Quiz} +// @Router /course/{id}/quiz [get] +func (m QuizHandlerImpl) GetAllQuizes(w http.ResponseWriter, r *http.Request) { + courseId := chi.URLParam(r, "id") + + if courseId == "" { + payload := m.WrapperUtil.ErrorResponseWrap("course id is required", nil) + m.HttpUtil.WriteJson(w, http.StatusUnsupportedMediaType, payload) + return + } + + result, err := m.QuizService.ListAllQuiz(courseId) + + if err != nil { + respErr, ok := err.(web.ResponseError) + if ok { + payload := m.WrapperUtil.ErrorResponseWrap(respErr.Error(), respErr) + + if respErr.Code != "NOT_OWNER" { + m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload) + } else { + m.HttpUtil.WriteJson(w, http.StatusForbidden, payload) + } + } else { + payload := m.WrapperUtil.ErrorResponseWrap("internal server error", nil) + m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload) + } + return + } + + responsePayload := m.WrapperUtil.SuccessResponseWrap(result) + m.HttpUtil.WriteSuccessJson(w, responsePayload) +} diff --git a/handler/quiz/take.go b/handler/quiz/take.go new file mode 100644 index 0000000000000000000000000000000000000000..530f4f77823001b1a5027b76d610b8c9ccafcaad --- /dev/null +++ b/handler/quiz/take.go @@ -0,0 +1,219 @@ +package quiz + +import ( + "net/http" + + "github.com/go-chi/chi/v5" + "github.com/go-playground/validator/v10" + "github.com/google/uuid" + "gitlab.informatika.org/ocw/ocw-backend/middleware/guard" + "gitlab.informatika.org/ocw/ocw-backend/model/web" + authToken "gitlab.informatika.org/ocw/ocw-backend/model/web/auth/token" + "gitlab.informatika.org/ocw/ocw-backend/model/web/quiz" +) + +// Index godoc +// +// @Tags quiz +// @Summary Take Quiz +// @Description Take a quiz +// @Produce json +// @Accept json +// @Param Authorization header string true "Authenticate User (any role)" +// @Param id path string true "Quiz id" Format(uuid) +// @Success 200 {object} web.BaseResponse{data=quiz.QuizDetail} +// @Router /quiz/{id}/take [post] +func (m QuizHandlerImpl) TakeQuiz(w http.ResponseWriter, r *http.Request) { + rawQuizId := chi.URLParam(r, "id") + + if rawQuizId == "" { + payload := m.WrapperUtil.ErrorResponseWrap("quiz id is required", nil) + m.HttpUtil.WriteJson(w, http.StatusUnsupportedMediaType, payload) + return + } + + quizId, err := uuid.Parse(rawQuizId) + + if err != nil { + payload := m.WrapperUtil.ErrorResponseWrap("quiz id is not valid", nil) + m.HttpUtil.WriteJson(w, http.StatusUnsupportedMediaType, payload) + return + } + + user, ok := r.Context().Value(guard.UserContext).(authToken.UserClaim) + + if !ok { + m.Logger.Error("Context is not found") + payload := m.WrapperUtil.ErrorResponseWrap("internal server error", nil) + m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload) + return + } + + detail, err := m.DoTakeQuiz(r.Context(), quizId, user.Email) + + if err != nil { + respErr, ok := err.(web.ResponseError) + if ok { + payload := m.WrapperUtil.ErrorResponseWrap(respErr.Error(), respErr) + m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload) + } else { + payload := m.WrapperUtil.ErrorResponseWrap("internal server error", nil) + m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload) + } + return + } + + responsePayload := m.WrapperUtil.SuccessResponseWrap(detail) + m.HttpUtil.WriteSuccessJson(w, responsePayload) +} + +// Index godoc +// +// @Tags quiz +// @Summary Get Quiz Solution +// @Description Take a quiz +// @Produce json +// @Accept json +// @Param Authorization header string true "Authenticate User (any role)" +// @Param id path string true "Quiz id" Format(uuid) +// @Success 200 {object} web.BaseResponse{data=quiz.QuizDetail} +// @Router /quiz/{id}/solution [get] +func (m QuizHandlerImpl) GetQuizSolution(w http.ResponseWriter, r *http.Request) { + rawQuizId := chi.URLParam(r, "id") + + if rawQuizId == "" { + payload := m.WrapperUtil.ErrorResponseWrap("quiz id is required", nil) + m.HttpUtil.WriteJson(w, http.StatusUnsupportedMediaType, payload) + return + } + + quizId, err := uuid.Parse(rawQuizId) + + if err != nil { + payload := m.WrapperUtil.ErrorResponseWrap("quiz id is not valid", nil) + m.HttpUtil.WriteJson(w, http.StatusUnsupportedMediaType, payload) + return + } + + user, ok := r.Context().Value(guard.UserContext).(authToken.UserClaim) + + if !ok { + m.Logger.Error("Context is not found") + payload := m.WrapperUtil.ErrorResponseWrap("internal server error", nil) + m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload) + return + } + + detail, err := m.GetSolutionQuiz(r.Context(), quizId, user.Email) + + if err != nil { + respErr, ok := err.(web.ResponseError) + if ok { + if respErr.Code == "ERR_NOT_ALLOWED" { + payload := m.WrapperUtil.ErrorResponseWrap(respErr.Error(), respErr) + m.HttpUtil.WriteJson(w, http.StatusForbidden, payload) + } else { + payload := m.WrapperUtil.ErrorResponseWrap(respErr.Error(), respErr) + m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload) + } + } else { + payload := m.WrapperUtil.ErrorResponseWrap("internal server error", nil) + m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload) + } + return + } + + responsePayload := m.WrapperUtil.SuccessResponseWrap(detail) + m.HttpUtil.WriteSuccessJson(w, responsePayload) +} + +// Index godoc +// +// @Tags quiz +// @Summary Finish Quiz +// @Description Finish quiz session and get the score +// @Produce json +// @Accept json +// @Param Authorization header string true "Authenticate User (any role)" +// @Param data body quiz.FinishQuizPayload true "Quiz Finish payload" +// @Param id path string true "Quiz id" Format(uuid) +// @Success 200 {object} web.BaseResponse{data=quiz.QuizDetail} +// @Router /quiz/{id}/take [post] +func (m QuizHandlerImpl) FinishQuiz(w http.ResponseWriter, r *http.Request) { + payload := quiz.FinishQuizPayload{} + + /* Get user */ + user, ok := r.Context().Value(guard.UserContext).(authToken.UserClaim) + + if !ok { + m.Logger.Error("Context is not found") + payload := m.WrapperUtil.ErrorResponseWrap("internal server error", nil) + m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload) + return + } + /* Get user */ + + /* Validate input */ + validate := validator.New() + + if r.Header.Get("Content-Type") != "application/json" { + payload := m.WrapperUtil.ErrorResponseWrap("this service only receive json input", nil) + m.HttpUtil.WriteJson(w, http.StatusUnsupportedMediaType, payload) + return + } + + if err := m.HttpUtil.ParseJson(r, &payload); err != nil { + payload := m.WrapperUtil.ErrorResponseWrap("invalid json input", err.Error()) + m.HttpUtil.WriteJson(w, http.StatusUnprocessableEntity, payload) + return + } + + if err := validate.Struct(payload); err != nil { + if _, ok := err.(*validator.InvalidValidationError); ok { + payload := m.WrapperUtil.ErrorResponseWrap(err.Error(), nil) + m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload) + return + } + + errPayload := web.NewResponseErrorFromValidator(err.(validator.ValidationErrors)) + payload := m.WrapperUtil.ErrorResponseWrap(errPayload.Error(), errPayload) + m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload) + return + } + /* End of validate */ + + /* Get quiz id */ + rawQuizId := chi.URLParam(r, "id") + + if rawQuizId == "" { + payload := m.WrapperUtil.ErrorResponseWrap("quiz id is required", nil) + m.HttpUtil.WriteJson(w, http.StatusUnsupportedMediaType, payload) + return + } + + quizId, err := uuid.Parse(rawQuizId) + + if err != nil { + payload := m.WrapperUtil.ErrorResponseWrap("quiz id is not valid", nil) + m.HttpUtil.WriteJson(w, http.StatusUnsupportedMediaType, payload) + return + } + /* end of get quiz id */ + + res, err := m.DoFinishQuiz(r.Context(), quizId, user.Email, payload.Data) + + if err != nil { + respErr, ok := err.(web.ResponseError) + if ok { + payload := m.WrapperUtil.ErrorResponseWrap(respErr.Error(), respErr) + m.HttpUtil.WriteJson(w, http.StatusBadRequest, payload) + } else { + payload := m.WrapperUtil.ErrorResponseWrap("internal server error", nil) + m.HttpUtil.WriteJson(w, http.StatusInternalServerError, payload) + } + return + } + + responsePayload := m.WrapperUtil.SuccessResponseWrap(res) + m.HttpUtil.WriteSuccessJson(w, responsePayload) +} diff --git a/handler/quiz/type.go b/handler/quiz/type.go new file mode 100644 index 0000000000000000000000000000000000000000..8c3bf6f5567cf7f04be9e42b67558279a0f6159d --- /dev/null +++ b/handler/quiz/type.go @@ -0,0 +1,12 @@ +package quiz + +import "net/http" + +type QuizHandler interface { + GetAllQuizes(w http.ResponseWriter, r *http.Request) + GetQuizDetail(w http.ResponseWriter, r *http.Request) + + TakeQuiz(w http.ResponseWriter, r *http.Request) + GetQuizSolution(w http.ResponseWriter, r *http.Request) + FinishQuiz(w http.ResponseWriter, r *http.Request) +} diff --git a/handler/reset/confirm.go b/handler/reset/confirm.go index da9420190d78f8c5938860b8bfca3e9375c90c16..24e5b0934e4e5eb06a639bd63bca7b1946b162c4 100644 --- a/handler/reset/confirm.go +++ b/handler/reset/confirm.go @@ -12,14 +12,14 @@ import ( // Index godoc // -// @Tags reset -// @Summary Confirm Reset Password +// @Tags reset +// @Summary Confirm Reset Password // @Description Do confirmation to reset password -// @Produce json -// @Param Authorization header string true "Email validation token" -// @Param data body confirm.ConfirmRequestPayload true "payload" -// @Success 200 {object} web.BaseResponse "Login Success" -// @Router /reset/confirm [put] +// @Produce json +// @Param Authorization header string true "Email validation token" +// @Param data body confirm.ConfirmRequestPayload true "payload" +// @Success 200 {object} web.BaseResponse "Login Success" +// @Router /reset/confirm [put] func (rs ResetHandlerImpl) Confirm(w http.ResponseWriter, r *http.Request) { payload := confirm.ConfirmRequestPayload{} validate := validator.New() diff --git a/handler/reset/request.go b/handler/reset/request.go index e118a0261b97d7bd0dbec2cde86644293d786313..ecdea1bf67e9eb60eb729760ca15ea4538e130e0 100644 --- a/handler/reset/request.go +++ b/handler/reset/request.go @@ -12,13 +12,13 @@ import ( // Index godoc // -// @Tags reset -// @Summary Request Reset Password Token +// @Tags reset +// @Summary Request Reset Password Token // @Description Send Reset password token to email -// @Produce json -// @Param data body request.RequestRequestPayload true "payload" -// @Success 200 {object} web.BaseResponse "Login Success" -// @Router /reset/request [post] +// @Produce json +// @Param data body request.RequestRequestPayload true "payload" +// @Success 200 {object} web.BaseResponse "Login Success" +// @Router /reset/request [post] func (rs ResetHandlerImpl) Request(w http.ResponseWriter, r *http.Request) { payload := request.RequestRequestPayload{} validate := validator.New() diff --git a/handler/reset/validate.go b/handler/reset/validate.go index 1bfa4dec36c2c1f3f2d2ff94110c80070906c3db..84be1fb797e3217866538dce680306f72bbf8dbb 100644 --- a/handler/reset/validate.go +++ b/handler/reset/validate.go @@ -11,13 +11,13 @@ import ( // Index godoc // -// @Tags reset -// @Summary Request Reset Password Token +// @Tags reset +// @Summary Request Reset Password Token // @Description Send Reset password token to email -// @Produce json -// @Param Authorization header string true "Email validation token" -// @Success 200 {object} web.BaseResponse "Login Success" -// @Router /reset/validate [get] +// @Produce json +// @Param Authorization header string true "Email validation token" +// @Success 200 {object} web.BaseResponse "Login Success" +// @Router /reset/validate [get] func (rs ResetHandlerImpl) Validate(w http.ResponseWriter, r *http.Request) { payload := validate.ValidateRequestPayload{} validateTokenHeader := r.Header.Get("Authorization") diff --git a/main.go b/main.go index 3510a8d8544bff0deb21be27d74fdfeabec819cd..bfd1104b3b07646a97ee856600db25c929e34255 100644 --- a/main.go +++ b/main.go @@ -1,8 +1,8 @@ package main -// @title Open Courseware Application -// @version 1.0.1 -// @description This is Open Couseware backend +// @title Open Courseware Application +// @version 1.0.1 +// @description This is Open Couseware backend func main() { server, err := CreateServer() diff --git a/middleware/guard/builder.go b/middleware/guard/builder.go index 75850ebf45a8ef08662c76311156cd2a067a4ff3..c0f06a9e6c2324734c1db6f64e5b5eb8e34f46d8 100644 --- a/middleware/guard/builder.go +++ b/middleware/guard/builder.go @@ -10,7 +10,9 @@ import ( ) type GuardBuilder struct { - GuardMiddleware + token.TokenUtil + logger.Logger + wrapper.WrapperUtil } func NewBuilder( @@ -19,25 +21,19 @@ func NewBuilder( wrapper wrapper.WrapperUtil, ) *GuardBuilder { return &GuardBuilder{ - GuardMiddleware{ - Token: token, - Role: []user.UserRole{}, - Logger: logger, - WrapperUtil: wrapper, - }, + token, + logger, + wrapper, } } -func (g *GuardBuilder) AddRole(role ...user.UserRole) *GuardBuilder { - g.GuardMiddleware.Role = role - return g -} - -func (g *GuardBuilder) Build() func(http.Handler) http.Handler { - return g.GuardMiddleware.Handle -} +func (g *GuardBuilder) Build(role ...user.UserRole) func(http.Handler) http.Handler { + handler := &GuardMiddleware{ + Token: g.TokenUtil, + Role: role, + Logger: g.Logger, + WrapperUtil: g.WrapperUtil, + } -func (g *GuardBuilder) BuildSimple(role user.UserRole) func(http.Handler) http.Handler { - g.AddRole(role) - return g.Build() + return handler.Handle } diff --git a/middleware/guard/guard.go b/middleware/guard/guard.go index 3b71984a870188559f7345793d786ccfce0f1e6d..487b5cf8e050ef23a52ae79f4afdbab5ca050871 100644 --- a/middleware/guard/guard.go +++ b/middleware/guard/guard.go @@ -83,7 +83,7 @@ func (g GuardMiddleware) Handle(next http.Handler) http.Handler { return } - ctx := context.WithValue(r.Context(), UserContext, claim) + ctx := context.WithValue(r.Context(), UserContext, *claim) next.ServeHTTP(w, r.WithContext(ctx)) return } diff --git a/model/domain/course/course.go b/model/domain/course/course.go index 3d11918d8bea39471ab99306e5dfda5f5d9de3a1..17d592f2301d2bd0097a7fb465e5b8474b46cc09 100644 --- a/model/domain/course/course.go +++ b/model/domain/course/course.go @@ -4,28 +4,28 @@ import "github.com/google/uuid" // TODO: Abbreviations should be unique constrainted as identifiers type Faculty struct { - ID uuid.UUID `gorm:"primaryKey"` - Name string - Abbreviation string + ID uuid.UUID `json:"id" gorm:"primaryKey"` + Name string `json:"name"` + Abbreviation string `json:"abbreviation"` } type Major struct { - ID uuid.UUID `gorm:"primaryKey;type:uuid"` - Name string - Fac_id uuid.UUID `gorm:"type:uuid"` - Faculty Faculty `gorm:"foreignKey:Fac_id"` - Abbreviation string + ID uuid.UUID `json:"id" gorm:"primaryKey;type:uuid"` + Name string `json:"name"` + Fac_id uuid.UUID `json:"fac_id" gorm:"type:uuid"` + Faculty *Faculty `json:"faculty" gorm:"foreignKey:Fac_id"` + Abbreviation string `json:"abbreviation"` } type Course struct { - ID string `gorm:"primaryKey"` - Name string - Major_id uuid.UUID `gorm:"type:uuid"` - Major Major `gorm:"foreignKey:Major_id"` - Description string - Email string - Abbreviation string - Lecturer string + ID string `json:"id" gorm:"primaryKey"` + Name string `json:"name"` + Major_id uuid.UUID `json:"major_id" gorm:"type:uuid"` + Major *Major `json:"major" gorm:"foreignKey:Major_id"` + Description string `json:"description"` + Email string `json:"email"` + Abbreviation string `json:"abbreviation"` + Lecturer string `json:"lecturer"` } func (Faculty) TableName() string { diff --git a/model/domain/material/content.go b/model/domain/material/content.go index 0e181045ba1141a363e07b196bf985c9fc0f4adf..c68f1587cd6dd653f04433cca81a1946d1ad0277 100644 --- a/model/domain/material/content.go +++ b/model/domain/material/content.go @@ -3,10 +3,10 @@ package material import "github.com/google/uuid" type Content struct { - Id uuid.UUID `gorm:"primaryKey"` - Type MaterialType - Link string - MaterialId uuid.UUID + Id uuid.UUID `gorm:"primaryKey" json:"id"` + Type MaterialType `json:"type"` + Link string `json:"link"` + MaterialID uuid.UUID `json:"material_id"` } func (Content) TableName() string { diff --git a/model/domain/material/material.go b/model/domain/material/material.go index 55d6bfbf7d002af41a7edc6bdb50beed800650a0..fa4d788dc7dad157bdc6e9c310c824722dad0bc4 100644 --- a/model/domain/material/material.go +++ b/model/domain/material/material.go @@ -2,17 +2,15 @@ package material import ( "github.com/google/uuid" - "gitlab.informatika.org/ocw/ocw-backend/model/domain/course" - "gitlab.informatika.org/ocw/ocw-backend/model/domain/user" ) type Material struct { - Id uuid.UUID `gorm:"primaryKey"` - CourseId string - CreatorEmail string - Creator user.User `gorm:"foreignKey:CreatorEmail;references:Email"` - Course course.Course `gorm:"foreignKey:CourseId;references:Id"` - Contents []Content `gorm:"foreignKey:MaterialId;references:Id"` + ID uuid.UUID `json:"id" gorm:"primaryKey"` + CourseId string `json:"course_id"` + CreatorEmail string `json:"creator_email"` + Name string `json:"name"` + Week int `json:"week"` + Contents []Content `json:"contents"` } func (Material) TableName() string { diff --git a/model/domain/material/material_type.go b/model/domain/material/material_type.go index 10e8abdc730e658f28f99da007bb755fc6896795..b8c7aec0193c2c71384f36b5ce2731d24be5f850 100644 --- a/model/domain/material/material_type.go +++ b/model/domain/material/material_type.go @@ -10,8 +10,8 @@ import ( type MaterialType int const ( - Video MaterialType = iota - Handout + Handout MaterialType = iota + 1 + Video External ) diff --git a/model/domain/quiz/options.go b/model/domain/quiz/options.go deleted file mode 100644 index de062c3cc531f34b4572016248e2085a2d1f1d99..0000000000000000000000000000000000000000 --- a/model/domain/quiz/options.go +++ /dev/null @@ -1,14 +0,0 @@ -package quiz - -import "github.com/google/uuid" - -type AnswerOption struct { - Id uuid.UUID `gorm:"primaryKey"` - QuizProblemId uuid.UUID `gorm:"primaryKey"` - Statement string - IsAnswer bool -} - -func (AnswerOption) TableName() string { - return "quiz_choice_answer" -} diff --git a/model/domain/quiz/problem.go b/model/domain/quiz/problem.go new file mode 100644 index 0000000000000000000000000000000000000000..7794b7f4355177f0ce5a948d11c6ff3f62aa89de --- /dev/null +++ b/model/domain/quiz/problem.go @@ -0,0 +1,38 @@ +package quiz + +import "github.com/google/uuid" + +type QuizMedia struct { + Id uuid.UUID `json:"id"` + Url string `json:"url"` + Type string `json:"type"` +} + +type AnswerOption struct { + Id uuid.UUID `json:"id"` + MediaId []uuid.UUID `json:"media_id"` + Answer string `json:"answer"` + IsSolution *bool `json:"is_solution"` +} + +type QuizProblem struct { + Id uuid.UUID `json:"id"` + MediaId []uuid.UUID `json:"media_id"` + Question string `json:"question"` + Answer []AnswerOption `json:"answers"` +} + +type QuizDetail struct { + Id uuid.UUID `json:"id"` + Name string `json:"name"` + CourseId string `json:"course_id"` + Description string `json:"description"` + Help string `json:"help"` + Media []QuizMedia `json:"media"` + Problems []QuizProblem `json:"problems"` +} + +type Response struct { + ProblemId uuid.UUID `json:"problem_id"` + AnswerId uuid.UUID `json:"answer_id"` +} diff --git a/model/domain/quiz/problem_type.go b/model/domain/quiz/problem_type.go deleted file mode 100644 index c03621526c635dc524f1caee1518665931b80d10..0000000000000000000000000000000000000000 --- a/model/domain/quiz/problem_type.go +++ /dev/null @@ -1,67 +0,0 @@ -package quiz - -import ( - "database/sql/driver" - "encoding/json" - "errors" - "fmt" -) - -type ProblemType int - -const ( - Choice ProblemType = iota -) - -var roleMapping = map[ProblemType]string{ - Choice: "choice", -} - -func (ur *ProblemType) Scan(value interface{}) error { - val := value.(string) - - for key, label := range roleMapping { - if label == val { - *ur = key - return nil - } - } - - return fmt.Errorf("invalid user role") -} - -func (u ProblemType) Value() (driver.Value, error) { - value, ok := roleMapping[u] - - if !ok { - return nil, fmt.Errorf("invalid user role") - } - - return value, nil -} - -func (u *ProblemType) UnmarshalJSON(b []byte) error { - var s string - if err := json.Unmarshal(b, &s); err != nil { - return err - } - - for key, label := range roleMapping { - if label == s { - *u = key - return nil - } - } - - return fmt.Errorf("unkown role, given %s", s) -} - -func (u ProblemType) MarshalJSON() ([]byte, error) { - s, ok := roleMapping[u] - - if !ok { - return nil, errors.New("unkown user role") - } - - return json.Marshal(s) -} diff --git a/model/domain/quiz/quiz.go b/model/domain/quiz/quiz.go index 6caade417ae857ec2a78fdf8c13b5ef007a8529a..896536cc66c3ffbca9840836c446019b139e8074 100644 --- a/model/domain/quiz/quiz.go +++ b/model/domain/quiz/quiz.go @@ -2,18 +2,14 @@ package quiz import ( "github.com/google/uuid" - "gitlab.informatika.org/ocw/ocw-backend/model/domain/course" - "gitlab.informatika.org/ocw/ocw-backend/model/domain/user" ) type Quiz struct { - Id uuid.UUID `gorm:"primaryKey"` - Name string - CourseId string - CreatorEmail string - Creator user.User `gorm:"foreignKey:CreatorEmail;references:Email"` - Course course.Course `gorm:"foreignKey:CourseId;references:Id"` - Problems []QuizProblem `gorm:"foreignKey:QuizId;references:Id"` + Id uuid.UUID `gorm:"primaryKey" json:"id"` + Name string `json:"nama"` + CourseId string `json:"course_id"` + CreatorEmail string `json:"creator_email"` + QuizPath string `json:"-"` } func (Quiz) TableName() string { diff --git a/model/domain/quiz/quiz_problem.go b/model/domain/quiz/quiz_problem.go deleted file mode 100644 index 3ce4195465d2fd477567f18858296d949c60020c..0000000000000000000000000000000000000000 --- a/model/domain/quiz/quiz_problem.go +++ /dev/null @@ -1,15 +0,0 @@ -package quiz - -import "github.com/google/uuid" - -type QuizProblem struct { - Id uuid.UUID `gorm:"primaryKey"` - Statement string - Type ProblemType - QuizId uuid.UUID - Options []AnswerOption `gorm:"foreignKey:QuizProblemId;references:Id"` -} - -func (QuizProblem) TableName() string { - return "quiz_problem" -} diff --git a/model/domain/quiz/take.go b/model/domain/quiz/take.go index c66263f60f1e4009019e5dcd428e7c905784f243..399989d2db63432372643f615caecafee0cbc896 100644 --- a/model/domain/quiz/take.go +++ b/model/domain/quiz/take.go @@ -1,22 +1,18 @@ package quiz import ( - "os/user" "time" "github.com/google/uuid" ) type QuizTake struct { - Id uuid.UUID `gorm:"primaryKey"` - QuizId uuid.UUID - Email string - StartTime time.Time - IsFinished bool - Score int - Quiz `gorm:"foreignKey:QuizId;references:Id"` - user.User `gorm:"foreignKey:Email;references:Email"` - ChoiceAnswers []TakeChoiceAnswer `gorm:"foreignKey:QuizTakeId;references:Id"` + Id uuid.UUID `gorm:"primaryKey" json:"id"` + QuizId uuid.UUID `json:"quiz_id"` + Email string `json:"email"` + StartTime time.Time `json:"start"` + IsFinished bool `json:"finished"` + Score int `json:"score"` } func (QuizTake) TableName() string { diff --git a/model/domain/quiz/take_choice_answer.go b/model/domain/quiz/take_choice_answer.go deleted file mode 100644 index 25e76746d8951251d3dbdee694986205ce364d53..0000000000000000000000000000000000000000 --- a/model/domain/quiz/take_choice_answer.go +++ /dev/null @@ -1,14 +0,0 @@ -package quiz - -import "github.com/google/uuid" - -type TakeChoiceAnswer struct { - QuizTakeId uuid.UUID `gorm:"primaryKey"` - AnswerChoice uuid.UUID - QuizProblemId uuid.UUID `gorm:"primaryKey"` - AnswerOption `gorm:"foreignKey:AnswerChoice,QuizProblemId;references:Id,QuizProblemId"` -} - -func (TakeChoiceAnswer) TableName() string { - return "quiz_take_choice_answer" -} diff --git a/model/domain/user/user.go b/model/domain/user/user.go index f2fbb565f6dd4aa94cbf7f15b260980ead0351f9..ad56084aa4fddda0f60e71d8de8819589e467bf1 100644 --- a/model/domain/user/user.go +++ b/model/domain/user/user.go @@ -3,13 +3,13 @@ package user import "time" type User struct { - Email string `gorm:"primaryKey"` - Password string - Name string - Role UserRole `gorm:"type:user_role"` - IsActivated bool - CreatedAt time.Time - UpdatedAt time.Time + Email string `gorm:"primaryKey" json:"email"` + Password string `json:"-"` + Name string `json:"name"` + Role UserRole `gorm:"type:user_role" json:"role"` + IsActivated bool `json:"activated"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` } func (User) TableName() string { diff --git a/model/web/admin/addUser/request.go b/model/web/admin/addUser/request.go index c08f743abc16843b3c0ca60c3725ac24d348db05..d1166ee9bdbcf0b870f7452c317c1fa295592c36 100644 --- a/model/web/admin/addUser/request.go +++ b/model/web/admin/addUser/request.go @@ -1,7 +1,7 @@ package admin // AdminAddUserPayload Request Payload -// @Description Information that should be available when admin add user +// @Description Information that should be available when admin add user // TODO: find a way to make default password for new user diff --git a/model/web/admin/updateUser/request.go b/model/web/admin/updateUser/request.go index 437d35fa7e7f5f415fea0ab49ec84f09c246ff7e..de60152fab122e1d48b48a172096c5d7a0db334c 100644 --- a/model/web/admin/updateUser/request.go +++ b/model/web/admin/updateUser/request.go @@ -1,7 +1,7 @@ package admin // AdminUpdateUserPayload Request Payload -// @Description Information that should be available when admin update user +// @Description Information that should be available when admin update user type AdminUpdateUserPayload struct { // User name diff --git a/model/web/auth/login/request.go b/model/web/auth/login/request.go index 716c505d9eb29cfcaf8a55c7561d4885fb1f2e7c..a5b9fa5000728b8884fdcc9c6118c7876739f480 100644 --- a/model/web/auth/login/request.go +++ b/model/web/auth/login/request.go @@ -1,7 +1,7 @@ package login // Login Request Payload -// @Description Information that should be available when do a login process +// @Description Information that should be available when do a login process type LoginRequestPayload struct { // User Email Email string `json:"email" validate:"required,email" example:"someone@example.com"` diff --git a/model/web/auth/login/response.go b/model/web/auth/login/response.go index 8cb68ca68d2c5491b090dba2f96240d9d6f143af..c961e2e24673721959fc1999c8210ad10d45399b 100644 --- a/model/web/auth/login/response.go +++ b/model/web/auth/login/response.go @@ -1,7 +1,7 @@ package login // Login Response Payload -// @Description Login response when process success +// @Description Login response when process success type LoginResponsePayload struct { // Token that used to generate new access token RefreshToken string `json:"refresh_token"` diff --git a/model/web/auth/refresh/response.go b/model/web/auth/refresh/response.go index f349ba6e3e2d9b70ad0fd08680d046e1a6ad9ba8..80e35164afd41fd54b1d02902a72ed1872e57232 100644 --- a/model/web/auth/refresh/response.go +++ b/model/web/auth/refresh/response.go @@ -1,7 +1,7 @@ package refresh // Refresh Response Payload -// @Description Refresh endpoint response when process success +// @Description Refresh endpoint response when process success type RefreshResponsePayload struct { // Token that used to access the resources AccessToken string `json:"access_token"` diff --git a/model/web/auth/register/request.go b/model/web/auth/register/request.go index 48d58105956364d445c27ea82129f2d0c27581eb..7a8a7d1394b5c151c4926fdc6e3795d9e1464fd5 100644 --- a/model/web/auth/register/request.go +++ b/model/web/auth/register/request.go @@ -1,7 +1,7 @@ package register // Register Request Payload -// @Description Information that should be available when do a registration process +// @Description Information that should be available when do a registration process type RegisterRequestPayload struct { // User Email Email string `json:"email" validate:"required,email" example:"someone@example.com"` diff --git a/model/web/auth/token/token.go b/model/web/auth/token/token.go index 497287e4d77c377c904e1161d0797f04ba022e04..fc57bf693dfd7092d889462a751f6979df6a09e0 100644 --- a/model/web/auth/token/token.go +++ b/model/web/auth/token/token.go @@ -7,8 +7,9 @@ import ( type UserClaim struct { jwt.RegisteredClaims - Name string `json:"name"` - Email string `json:"email"` - Role user.UserRole `json:"role"` - Type TokenType `json:"type"` + Name string `json:"name"` + Email string `json:"email"` + Role user.UserRole `json:"role"` + Type TokenType `json:"type"` + IsVerified bool `json:"is_verified"` } diff --git a/model/web/auth/verification/request.go b/model/web/auth/verification/request.go index dfddab4ba7adb0585a2147daa85ecfbfce77b25e..cd408e92fdd61b01d43fd857cfecbeefd314a006 100644 --- a/model/web/auth/verification/request.go +++ b/model/web/auth/verification/request.go @@ -1,7 +1,7 @@ package verification // Email Verification Request Payload -// @Description Information that should be passed when request verify +// @Description Information that should be passed when request verify type VerificationSendRequestPayload struct { // User Email Email string `json:"email" validate:"required,email" example:"someone@example.com"` diff --git a/model/web/course/add/request.go b/model/web/course/add/request.go deleted file mode 100644 index 0b86f3ab6618ef4ec1f7f299b0fd45c81bfb61dc..0000000000000000000000000000000000000000 --- a/model/web/course/add/request.go +++ /dev/null @@ -1,31 +0,0 @@ -package add - -import "github.com/google/uuid" - -// AddCourse Request Payload -// @Description Information that should be available when you add a course -type AddCourseRequestPayload struct { - // Web Token that was appended to the link - AddCourseToken string - - // Course ID - ID string `json:"id" validate:"required"` - - // Course Name - Name string `json:"name" validate:"required"` - - // Course Major Abbreviation - MajAbbr string `json:"majabbr" validate:"required_without=MajorID"` - - // Major Id, will be set by the server - MajorID uuid.UUID `json:"major_id"` - - // Course Description (Can be left empty) - Description string `json:"description"` - - // Contributor Email - Email string `json:"email" validate:"required,email" example:"someone@example.com"` - - // Course Name Abbreviation - Abbreviation string `json:"abbreviation" validate:"required"` -} diff --git a/model/web/course/delete/request.go b/model/web/course/delete/request.go deleted file mode 100644 index fdfcdbc2809b1e2ad428cae959569dc84703b6d3..0000000000000000000000000000000000000000 --- a/model/web/course/delete/request.go +++ /dev/null @@ -1,11 +0,0 @@ -package delete - -// DeleteCourse Request Payload -// @Description Information that should be available when you delete using course id (string) -type DeleteByStringRequestPayload struct { - // Web Token that was appended to the link - DeleteCourseToken string - - // Course ID, provided by query - ID string -} diff --git a/model/web/course/faculty/add/request.go b/model/web/course/faculty/add/request.go deleted file mode 100644 index fb7c2da86ab96c2c326748d03704ef7c62e07d39..0000000000000000000000000000000000000000 --- a/model/web/course/faculty/add/request.go +++ /dev/null @@ -1,14 +0,0 @@ -package add - -// AddFaculty Request Payload -// @Description Information that should be available when you add a faculty -type AddFacultyRequestPayload struct { - // Web Token that was appended to the link - AddFacultyToken string - - // Faculty Name - Name string `json:"name" validate:"required"` - - // Faculty Name Abbreviation - Abbreviation string `json:"abbreviation" validate:"required"` -} diff --git a/model/web/course/faculty/request.go b/model/web/course/faculty/request.go new file mode 100644 index 0000000000000000000000000000000000000000..1c08bddf6fc2410cc7bdec8542c9edc17f5a95e9 --- /dev/null +++ b/model/web/course/faculty/request.go @@ -0,0 +1,32 @@ +package faculty + +import "github.com/google/uuid" + +// AddFaculty Request Payload +// @Description Information that should be available when you add a faculty +type AddFacultyRequestPayload struct { + // Web Token that was appended to the link + AddFacultyToken string `json:"faculty_token"` + + // Faculty Name + Name string `json:"name" validate:"required"` + + // Faculty Name Abbreviation + Abbreviation string `json:"abbreviation" validate:"required"` +} + +// UpdateFaculty Request Payload +// @Description Information that should be available when you update a faculty +type UpdateFacultyRequestPayload struct { + // Web Token that was appended to the link + UpdateFacultyToken string + + // Faculty ID, Provided by Query + ID uuid.UUID + + // Faculty Name + Name string `json:"name" validate:"required"` + + // Faculty Name Abbreviation + Abbreviation string `json:"abbreviation" validate:"required"` +} \ No newline at end of file diff --git a/model/web/course/faculty/update/request.go b/model/web/course/faculty/update/request.go deleted file mode 100644 index 38f04860abe184ab7e61182c538c8a4b39003885..0000000000000000000000000000000000000000 --- a/model/web/course/faculty/update/request.go +++ /dev/null @@ -1,19 +0,0 @@ -package update - -import "github.com/google/uuid" - -// UpdateFaculty Request Payload -// @Description Information that should be available when you update a faculty -type UpdateFacultyRequestPayload struct { - // Web Token that was appended to the link - UpdateFacultyToken string - - // Faculty ID, Provided by Query - ID uuid.UUID - - // Faculty Name - Name string `json:"name" validate:"required"` - - // Faculty Name Abbreviation - Abbreviation string `json:"abbreviation" validate:"required"` -} diff --git a/model/web/course/get/request.go b/model/web/course/get/request.go deleted file mode 100644 index 7f1853ea4ad2051e145d01cb86b45f68c7804f44..0000000000000000000000000000000000000000 --- a/model/web/course/get/request.go +++ /dev/null @@ -1,17 +0,0 @@ -package get - -import "github.com/google/uuid" - -// GetCourse Request Payload -// @Description Information that should be available when you get using course id (string) -type GetByStringRequestPayload struct { - // Course ID, provided by query - ID string -} - -// GetCourse Request Payload -// @Description Information that should be available when you get using major/faculty id (string) -type GetByUUIDRequestPayload struct { - // Major/Faculty ID, provided by query - ID uuid.UUID -} \ No newline at end of file diff --git a/model/web/course/major/add/request.go b/model/web/course/major/add/request.go deleted file mode 100644 index fbc207644da74f83915b6ac16456d03b9a8efd1d..0000000000000000000000000000000000000000 --- a/model/web/course/major/add/request.go +++ /dev/null @@ -1,22 +0,0 @@ -package add - -import "github.com/google/uuid" - -// AddMajor Request Payload -// @Description Information that should be available when you add a major -type AddMajorRequestPayload struct { - // Web Token that was appended to the link - AddMajorToken string - - // Major Name - Name string `json:"name" validate:"required"` - - // Major Faculty Abbreviation - FacAbbr string `json:"facabbr" validate:"required_without=FacultyID"` - - // Faculty Id, will be set by the server - FacultyID uuid.UUID `json:"faculty_id"` - - // Major Name Abbreviation - Abbreviation string `json:"abbreviation" validate:"required"` -} diff --git a/model/web/course/major/request.go b/model/web/course/major/request.go new file mode 100644 index 0000000000000000000000000000000000000000..53bc733584b8c437905b7bf7ef9d3a636866262b --- /dev/null +++ b/model/web/course/major/request.go @@ -0,0 +1,45 @@ +package major + +import "github.com/google/uuid" + +// AddMajor Request Payload +// @Description Information that should be available when you add a major +type AddMajorRequestPayload struct { + // Web Token that was appended to the link + AddMajorToken string `json:"major_token"` + + // Major Name + Name string `json:"name" validate:"required"` + + // Major Faculty Abbreviation + FacAbbr string `json:"facabbr" validate:"required_without=FacultyID"` + + // Faculty Id, will be set by the server + FacultyID uuid.UUID `json:"faculty_id"` + + // Major Name Abbreviation + Abbreviation string `json:"abbreviation" validate:"required"` +} + + +// UpdateMajor Request Payload +// @Description Information that should be available when you update a major +type UpdateMajorRequestPayload struct { + // Web Token that was appended to the link + UpdateMajorToken string + + // Major ID, provided by query + ID uuid.UUID + + // Major Name + Name string `json:"name" validate:"required"` + + // Major Faculty Abbreviation + FacAbbr string `json:"facabbr" validate:"required_without=FacultyID"` + + // Faculty Id, will be set by the server + FacultyID uuid.UUID `json:"faculty_id"` + + // Major Name Abbreviation + Abbreviation string `json:"abbreviation" validate:"required"` +} diff --git a/model/web/course/major/update/request.go b/model/web/course/major/update/request.go deleted file mode 100644 index b024427a8fdc6ed1d3851d10be75bec67a40aa71..0000000000000000000000000000000000000000 --- a/model/web/course/major/update/request.go +++ /dev/null @@ -1,25 +0,0 @@ -package update - -import "github.com/google/uuid" - -// UpdateMajor Request Payload -// @Description Information that should be available when you update a major -type UpdateMajorRequestPayload struct { - // Web Token that was appended to the link - UpdateMajorToken string - - // Major ID, provided by query - ID uuid.UUID - - // Major Name - Name string `json:"name" validate:"required"` - - // Major Faculty Abbreviation - FacAbbr string `json:"facabbr" validate:"required_without=FacultyID"` - - // Faculty Id, will be set by the server - FacultyID uuid.UUID `json:"faculty_id"` - - // Major Name Abbreviation - Abbreviation string `json:"abbreviation" validate:"required"` -} diff --git a/model/web/course/request.go b/model/web/course/request.go new file mode 100644 index 0000000000000000000000000000000000000000..0fad2befd53b36b1722fa4d295123606277841e3 --- /dev/null +++ b/model/web/course/request.go @@ -0,0 +1,89 @@ +package course + +import "github.com/google/uuid" + +// AddCourse Request Payload +// @Description Information that should be available when you add a course +type AddCourseRequestPayload struct { + // Web Token that was appended to the link + AddCourseToken string + + // Course ID + ID string `json:"id" validate:"required"` + + // Course Name + Name string `json:"name" validate:"required"` + + // Course Major Abbreviation + MajAbbr string `json:"majabbr" validate:"required_without=MajorID"` + + // Major Id, will be set by the server + MajorID uuid.UUID `json:"major_id"` + + // Course Description (Can be left empty) + Description string `json:"description"` + + // Contributor Email + Email string `json:"email" validate:"required,email" example:"someone@example.com"` + + // Course Name Abbreviation + Abbreviation string `json:"abbreviation" validate:"required"` +} + +// DeleteCourse Request Payload +// @Description Information that should be available when you delete using course id (string) +type DeleteByStringRequestPayload struct { + // Web Token that was appended to the link + DeleteCourseToken string + + // Course ID, provided by query + ID string +} + + +// GetID Request Payload +// @Description Information that should be available when you get using course id (string) +type GetByStringRequestPayload struct { + // Course ID, provided by query + ID string +} + +// GetUUID Request Payload +// @Description Information that should be available when you get using major/faculty id (string) +type GetByUUIDRequestPayload struct { + // Major/Faculty ID, provided by query + ID uuid.UUID +} + +// UpdateCourse Request Payload +// @Description Information that should be available when you add a course +type UpdateCourseRequestPayload struct { + // Web Token that was appended to the link + UpdateCourseToken string + + // Course ID, Provided by query + ID string `json:"id"` + + // Course Name + Name string `json:"name" validate:"required"` + + // Course Major Abbreviation + MajAbbr string `json:"majabbr" validate:"required_without=MajorID"` + + // Major Id, will be set by the server + MajorID uuid.UUID `json:"major_id"` + + // Course Description (Can be left empty) + Description string `json:"description"` + + // Contributor Email + Email string `json:"email" validate:"required,email" example:"someone@example.com"` + + // Course Name Abbreviation + Abbreviation string `json:"abbreviation" validate:"required"` + + // Course Lecturer + Lecturer string `json:"lecturer" validate:"required"` +} + + diff --git a/model/web/course/update/request.go b/model/web/course/update/request.go deleted file mode 100644 index 59d2b4c9f91503df99ca15808f7b73d2412e8e07..0000000000000000000000000000000000000000 --- a/model/web/course/update/request.go +++ /dev/null @@ -1,34 +0,0 @@ -package update - -import "github.com/google/uuid" - -// UpdateCourse Request Payload -// @Description Information that should be available when you add a course -type UpdateCourseRequestPayload struct { - // Web Token that was appended to the link - UpdateCourseToken string - - // Course ID, Provided by query - ID string - - // Course Name - Name string `json:"name" validate:"required"` - - // Course Major Abbreviation - MajAbbr string `json:"majabbr" validate:"required_without=MajorID"` - - // Major Id, will be set by the server - MajorID uuid.UUID `json:"major_id"` - - // Course Description (Can be left empty) - Description string `json:"description"` - - // Contributor Email - Email string `json:"email" validate:"required,email" example:"someone@example.com"` - - // Course Name Abbreviation - Abbreviation string `json:"abbreviation" validate:"required"` - - // Course Lecturer - Lecturer string `json:"lecturer" validate:"required"` -} diff --git a/model/web/material/content.go b/model/web/material/content.go new file mode 100644 index 0000000000000000000000000000000000000000..d3575b4a9314057b0816d2316bade8e250f610aa --- /dev/null +++ b/model/web/material/content.go @@ -0,0 +1,21 @@ +package material + +import ( + "github.com/google/uuid" + "gitlab.informatika.org/ocw/ocw-backend/model/domain/material" +) + +type NewContentRequest struct { + Type material.MaterialType `json:"type" validate:"required"` + Link string `json:"link"` + MaterialId uuid.UUID `json:"-"` +} + +type NewContentResponse struct { + UploadLink string `json:"upload_link"` +} + +type DeleteContentRequest struct { + ContentId uuid.UUID `json:"content_id"` + MaterialId uuid.UUID `json:"-"` +} diff --git a/model/web/material/material.go b/model/web/material/material.go new file mode 100644 index 0000000000000000000000000000000000000000..98f8c6cdae84025cf0079c2df683a3be1767420b --- /dev/null +++ b/model/web/material/material.go @@ -0,0 +1,15 @@ +package material + +import "github.com/google/uuid" + +type CreateMaterialRequest struct { + Name string `json:"name" validate:"required"` +} + +type CreateMaterialResponse struct { + MaterialId uuid.UUID `json:"material_id"` +} + +type DeleteMaterialRequest struct { + MaterialId uuid.UUID `json:"material_id"` +} diff --git a/model/web/quiz/finish.go b/model/web/quiz/finish.go new file mode 100644 index 0000000000000000000000000000000000000000..80c7335963d5977527460cf5ada30201865723e4 --- /dev/null +++ b/model/web/quiz/finish.go @@ -0,0 +1,7 @@ +package quiz + +import "gitlab.informatika.org/ocw/ocw-backend/model/domain/quiz" + +type FinishQuizPayload struct { + Data []quiz.Response `json:"data"` +} diff --git a/model/web/reset/confirm/request.go b/model/web/reset/confirm/request.go index 6d864a35e49373198dfff3fe18da82094519c052..78e16a1ca1ade345d4b76c693034a17d8f6e9043 100644 --- a/model/web/reset/confirm/request.go +++ b/model/web/reset/confirm/request.go @@ -1,7 +1,7 @@ package confirm // Confirm Request Payload -// @Description Information that should be available when you confirm a password reset +// @Description Information that should be available when you confirm a password reset type ConfirmRequestPayload struct { // Web Token that was appended to the link ConfirmToken string diff --git a/model/web/reset/request/request.go b/model/web/reset/request/request.go index cdd5d15bfbbf4cdccbe381d1af776bf58a299b36..c589cce1e380c001b0f8d5d248f7f2c513bf19d9 100644 --- a/model/web/reset/request/request.go +++ b/model/web/reset/request/request.go @@ -1,7 +1,7 @@ package request // Request Request Payload -// @Description Information that should be available when password reset is requested +// @Description Information that should be available when password reset is requested type RequestRequestPayload struct { // User Email Email string `json:"email" validate:"required,email" example:"someone@example.com"` diff --git a/model/web/reset/validate/request.go b/model/web/reset/validate/request.go index f13973a086b4048d637a660d08c75076a5ec3378..72f94995730c4a8410eb5efe37dcd24d6bb9b349 100644 --- a/model/web/reset/validate/request.go +++ b/model/web/reset/validate/request.go @@ -1,7 +1,7 @@ package validate // Validate Request Payload -// @Description Information that should be available when link validation is done +// @Description Information that should be available when link validation is done type ValidateRequestPayload struct { // Web Token that was appended to the link ValidateToken string diff --git a/provider/storage/manager.go b/provider/storage/manager.go index cc87edd73acda90734470345e5fd3ea1230a1280..cfeae57144fe4d23729a2382984360253e5d4659 100644 --- a/provider/storage/manager.go +++ b/provider/storage/manager.go @@ -9,3 +9,16 @@ import ( func (s S3Storage) Delete(ctx context.Context, path string) error { return s.minio.RemoveObject(ctx, s.env.BucketName, path, minio.RemoveObjectOptions{}) } + +func (s S3Storage) Get(ctx context.Context, path string) ([]byte, error) { + result := []byte{} + obj, err := s.minio.GetObject(ctx, s.env.BucketName, path, minio.GetObjectOptions{}) + + if err != nil { + return result, err + } + + _, err = obj.Read(result) + + return result, err +} diff --git a/provider/storage/s3.go b/provider/storage/s3.go index 3f746712f3fa34c1ae77ea87fe4491282e4a6609..52aaaaba702a12c947c98df8ecef737db8ed3009 100644 --- a/provider/storage/s3.go +++ b/provider/storage/s3.go @@ -14,10 +14,15 @@ type S3Storage struct { func NewS3( env *env.Environment, ) (*S3Storage, error) { - client, err := minio.New(env.BucketEndpoint, &minio.Options{ + if !env.UseBucket { + return nil, nil + } + + settings := &minio.Options{ Creds: credentials.NewStaticV4(env.BucketAccessKey, env.BucketSecretKey, env.BucketTokenKey), Secure: env.BucketUseSSL, - }) + } + client, err := minio.New(env.BucketEndpoint, settings) if err != nil { return nil, err diff --git a/provider/storage/type.go b/provider/storage/type.go index dc7cdd2f97597d8760d26aa2b75791989a12ff9f..19f2a46fb4d9044839660ba4234f47f5f661f0d2 100644 --- a/provider/storage/type.go +++ b/provider/storage/type.go @@ -9,4 +9,5 @@ type Storage interface { CreatePutSignedLink(ctx context.Context, path string) (string, error) CreateGetSignedLink(ctx context.Context, path string, reqParam url.Values) (string, error) Delete(ctx context.Context, path string) error + Get(ctx context.Context, path string) ([]byte, error) } diff --git a/repository/course/course.go b/repository/course/course.go index 6b5d06ee1121b393081d5f4421b41d825614a844..1dbeee3dd4f0ed06fbb3daf9655e10fc67a8c579 100644 --- a/repository/course/course.go +++ b/repository/course/course.go @@ -33,6 +33,20 @@ func (repo CourseRepositoryImpl) IsCourseExist(id string) (bool, error) { return true, nil } +func (repo CourseRepositoryImpl) IsUserCourseContributor(id string, email string) (bool, error) { + err := repo.db.Where("course_id = ? AND email = ?").Find(&course.Course{}).Error + + if err == nil { + return true, nil + } + + if errors.Is(err, gorm.ErrRecordNotFound) { + return false, nil + } + + return false, err +} + func (repo CourseRepositoryImpl) IsMajorExist(id uuid.UUID) (bool, error) { _, err := repo.GetMajor(id) @@ -66,10 +80,18 @@ func (repo CourseRepositoryImpl) AddCourse(course course.Course) error { } func (repo CourseRepositoryImpl) AddMajor(major course.Major) error { + if id, err := uuid.NewUUID(); err != nil { + major.ID = id + } + return repo.db.Create(&major).Error } func (repo CourseRepositoryImpl) AddFaculty(faculty course.Faculty) error { + if id, err := uuid.NewUUID(); err != nil { + faculty.ID = id + } + return repo.db.Create(&faculty).Error } @@ -141,7 +163,7 @@ func (repo CourseRepositoryImpl) GetAllFaculty() ([]course.Faculty, error) { func (repo CourseRepositoryImpl) GetAllCourseByMajor(id uuid.UUID) ([]course.Course, error) { var result []course.Course - err := repo.db.InnerJoins("Major", repo.db.Where(&course.Major{ID: id})).Find(&result).Error + err := repo.db.Where("major_id = ?", id).Find(&result).Error if err != nil { return nil, err @@ -152,7 +174,11 @@ func (repo CourseRepositoryImpl) GetAllCourseByMajor(id uuid.UUID) ([]course.Cou func (repo CourseRepositoryImpl) GetAllCourseByFaculty(id uuid.UUID) ([]course.Course, error) { var result []course.Course - err := repo.db.InnerJoins("Faculty", repo.db.Where(&course.Faculty{ID: id})).InnerJoins("Major").Find(&result).Error + err := repo.db. + Joins("JOIN major ON major.id = course.major_id"). + Joins("JOIN faculty ON faculty.id = major.fac_id"). + Where("faculty.id = ?", id). + Find(&result).Error if err != nil { return nil, err @@ -163,7 +189,7 @@ func (repo CourseRepositoryImpl) GetAllCourseByFaculty(id uuid.UUID) ([]course.C func (repo CourseRepositoryImpl) GetAllMajorByFaculty(id uuid.UUID) ([]course.Major, error) { var result []course.Major - err := repo.db.InnerJoins("Faculty", repo.db.Where(&course.Faculty{ID: id})).Find(&result).Error + err := repo.db.Where("fac_id = ?", id).Find(&result).Error if err != nil { return nil, err @@ -208,4 +234,4 @@ func (repo CourseRepositoryImpl) GetFacultyByAbbr(abbr string) (*course.Faculty, } return result, nil -} \ No newline at end of file +} diff --git a/repository/course/type.go b/repository/course/type.go index 252eb470be01f16794cbbb37dc3f85fee1ecfca0..7346d0a0551bed19e126fb1922a8cb4cacacdc1d 100644 --- a/repository/course/type.go +++ b/repository/course/type.go @@ -25,9 +25,10 @@ type CourseRepository interface { IsCourseExist(id string) (bool, error) IsMajorExist(id uuid.UUID) (bool, error) IsFacultyExist(id uuid.UUID) (bool, error) + IsUserCourseContributor(id string, email string) (bool, error) // Internal Method Only - + GetMajorByAbbr(abbr string) (*course.Major, error) GetFacultyByAbbr(abbr string) (*course.Faculty, error) } diff --git a/repository/di.go b/repository/di.go index 9136c031788ba4f56d8d3bfe4c46f2864b641571..b564bd5406f7d94427c5c0f80ccc518bc23fd5b2 100644 --- a/repository/di.go +++ b/repository/di.go @@ -2,12 +2,13 @@ package repository import ( "github.com/google/wire" - "gitlab.informatika.org/ocw/ocw-backend/repository/user" - "gitlab.informatika.org/ocw/ocw-backend/repository/course" "gitlab.informatika.org/ocw/ocw-backend/repository/cache" "gitlab.informatika.org/ocw/ocw-backend/repository/content" + "gitlab.informatika.org/ocw/ocw-backend/repository/course" "gitlab.informatika.org/ocw/ocw-backend/repository/material" + "gitlab.informatika.org/ocw/ocw-backend/repository/quiz" "gitlab.informatika.org/ocw/ocw-backend/repository/transaction" + "gitlab.informatika.org/ocw/ocw-backend/repository/user" ) var RepositoryBasicSet = wire.NewSet( @@ -37,6 +38,9 @@ var RepositoryBasicSet = wire.NewSet( transaction.NewBuilder, wire.Bind(new(transaction.Transaction), new(*transaction.TransactionRepositoryImpl)), wire.Bind(new(transaction.TransactionBuilder), new(*transaction.TransactionBuilderImpl)), + + quiz.New, + wire.Bind(new(quiz.QuizRepository), new(*quiz.QuizRepositoryImpl)), ) var RepositorySet = wire.NewSet( diff --git a/repository/material/content.go b/repository/material/content.go index 0f321a1b58f824fecb3bfeb5b5ea5b26c331b434..919d17b8fa531fe88c867194c154b52a3bd710ae 100644 --- a/repository/material/content.go +++ b/repository/material/content.go @@ -3,17 +3,35 @@ package material import ( "github.com/google/uuid" "gitlab.informatika.org/ocw/ocw-backend/model/domain/material" + "gitlab.informatika.org/ocw/ocw-backend/provider/db" + "gitlab.informatika.org/ocw/ocw-backend/repository/course" "gitlab.informatika.org/ocw/ocw-backend/repository/transaction" + "gorm.io/gorm" ) type MaterialContentRepositoryImpl struct { builder transaction.TransactionBuilder + course.CourseRepository + MaterialRepository + db *gorm.DB } func NewMaterialContent( builder transaction.TransactionBuilder, + course course.CourseRepository, + database db.Database, + material MaterialRepository, ) *MaterialContentRepositoryImpl { - return &MaterialContentRepositoryImpl{builder} + return &MaterialContentRepositoryImpl{builder, course, material, database.Connect()} +} + +func (m MaterialContentRepositoryImpl) IsUserContributor(id uuid.UUID, email string) (bool, error) { + result := &material.Content{} + if err := m.db.Where("id = ?", id).Find(result).Error; err != nil { + return false, err + } + + return m.MaterialRepository.IsUserContributor(result.MaterialID, email) } func (m MaterialContentRepositoryImpl) New( @@ -35,7 +53,8 @@ func (m MaterialContentRepositoryImpl) NewWithTransaction( materialType material.MaterialType, link string) (uuid.UUID, error) { contentData := material.Content{ - MaterialId: materialId, + Id: uuid.New(), + MaterialID: materialId, Type: materialType, Link: link, } diff --git a/repository/material/material.go b/repository/material/material.go index a20281eab9223275befc37993d0a0a3383e9dc86..87cbd435c379d505d4be048f8d1d5e878ffba019 100644 --- a/repository/material/material.go +++ b/repository/material/material.go @@ -3,27 +3,48 @@ package material import ( "github.com/google/uuid" "gitlab.informatika.org/ocw/ocw-backend/model/domain/material" + "gitlab.informatika.org/ocw/ocw-backend/provider/db" "gitlab.informatika.org/ocw/ocw-backend/repository/transaction" + "gorm.io/gorm" ) type MaterialRepositoryImpl struct { builder transaction.TransactionBuilder + db *gorm.DB } func NewMaterial( builder transaction.TransactionBuilder, + db db.Database, ) *MaterialRepositoryImpl { - return &MaterialRepositoryImpl{builder} + return &MaterialRepositoryImpl{builder, db.Connect()} } -func (m MaterialRepositoryImpl) New(courseId string, creatorEmail string) (uuid.UUID, error) { - return m.NewWithTransaction(m.builder.Build(), courseId, creatorEmail) +func (m MaterialRepositoryImpl) Get(materialId uuid.UUID) (*material.Material, error) { + res := &material.Material{} + err := m.db.Preload("Contents").Where("id = ?", materialId).Find(res).Error + return res, err } -func (m MaterialRepositoryImpl) NewWithTransaction(tx transaction.Transaction, courseId string, creatorEmail string) (uuid.UUID, error) { +func (m MaterialRepositoryImpl) IsUserContributor(id uuid.UUID, email string) (bool, error) { + err := m.db.Where("creator_email = ? AND id = ?", email, id).Find(&material.Material{}).Error + if err != nil { + return false, err + } + + return true, nil +} + +func (m MaterialRepositoryImpl) New(courseId string, creatorEmail string, name string) (uuid.UUID, error) { + return m.NewWithTransaction(m.builder.Build(), courseId, creatorEmail, name) +} + +func (m MaterialRepositoryImpl) NewWithTransaction(tx transaction.Transaction, courseId string, creatorEmail string, name string) (uuid.UUID, error) { materialData := &material.Material{ + ID: uuid.New(), CourseId: courseId, CreatorEmail: creatorEmail, + Name: name, } err := tx.GetTransaction().Create(materialData).Error @@ -32,7 +53,7 @@ func (m MaterialRepositoryImpl) NewWithTransaction(tx transaction.Transaction, c return uuid.Nil, err } - return materialData.Id, nil + return materialData.ID, nil } func (m MaterialRepositoryImpl) Delete(id uuid.UUID) error { @@ -49,7 +70,12 @@ func (m MaterialRepositoryImpl) GetAll(courseId string) ([]material.Material, er func (m MaterialRepositoryImpl) GetAllWithTransaction(tx transaction.Transaction, courseId string) ([]material.Material, error) { result := []material.Material{} - err := tx.GetTransaction().Joins("Contents").Where("CourseId = ?", courseId).Find(&result).Error + trx := tx.GetTransaction() + err := trx. + Model(&material.Material{}). + Preload("Contents"). + Where("course_id = ?", courseId). + Find(&result).Error return result, err } diff --git a/repository/material/type.go b/repository/material/type.go index 8343c4df1757d7f4f585efd43fe3cc18e4f325d4..4014763d006c08c2eabeb294a8f043ae023d0b17 100644 --- a/repository/material/type.go +++ b/repository/material/type.go @@ -7,16 +7,20 @@ import ( ) type MaterialRepository interface { - New(courseId string, creatorEmail string) (uuid.UUID, error) + New(courseId string, creatorEmail string, name string) (uuid.UUID, error) Delete(id uuid.UUID) error + Get(materialId uuid.UUID) (*material.Material, error) GetAll(courseId string) ([]material.Material, error) - NewWithTransaction(tx transaction.Transaction, courseId string, creatorEmail string) (uuid.UUID, error) + IsUserContributor(id uuid.UUID, email string) (bool, error) + + NewWithTransaction(tx transaction.Transaction, courseId string, creatorEmail string, name string) (uuid.UUID, error) DeleteWithTransaction(tx transaction.Transaction, id uuid.UUID) error GetAllWithTransaction(tx transaction.Transaction, courseId string) ([]material.Material, error) } type MaterialContentRepository interface { + IsUserContributor(id uuid.UUID, email string) (bool, error) New(materialId uuid.UUID, materialType material.MaterialType, link string) (uuid.UUID, error) GetAll(materialId uuid.UUID) ([]material.Content, error) Delete(contentId uuid.UUID) error diff --git a/repository/quiz/impl.go b/repository/quiz/impl.go new file mode 100644 index 0000000000000000000000000000000000000000..e51495422f1a10911dd47bf00b1764eb541446ab --- /dev/null +++ b/repository/quiz/impl.go @@ -0,0 +1,97 @@ +package quiz + +import ( + "errors" + "time" + + "github.com/google/uuid" + "gitlab.informatika.org/ocw/ocw-backend/model/domain/quiz" + "gitlab.informatika.org/ocw/ocw-backend/model/web" + "gitlab.informatika.org/ocw/ocw-backend/provider/db" + "gorm.io/gorm" +) + +type QuizRepositoryImpl struct { + db *gorm.DB +} + +func New( + db db.Database, +) *QuizRepositoryImpl { + return &QuizRepositoryImpl{db.Connect()} +} + +func (q *QuizRepositoryImpl) GetQuizes(courseId string) ([]quiz.Quiz, error) { + result := &[]quiz.Quiz{} + err := q.db.Where("course_id = ?", courseId).Find(result).Error + + return *result, err +} + +func (q *QuizRepositoryImpl) GetQuizDetail(quizId uuid.UUID) (*quiz.Quiz, error) { + result := &quiz.Quiz{} + err := q.db.Where("id = ?", quizId).First(result).Error + + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, web.NewResponseError("Record not found", "ERR_NOT_FOUND") + } + + return result, nil +} + +func (q *QuizRepositoryImpl) UpdateScore(takeId uuid.UUID, score int) error { + return q.db. + Model(&quiz.QuizTake{}). + Update("score", score). + Update("is_finished", true). + Where("id = ?", takeId).Error +} + +func (q *QuizRepositoryImpl) NewTake(quizId uuid.UUID, userEmail string) (uuid.UUID, error) { + id := uuid.New() + err := q.db.Create( + &quiz.QuizTake{ + Id: id, + Email: userEmail, + StartTime: time.Now(), + QuizId: quizId, + IsFinished: false, + Score: 0, + }, + ).Error + + return id, err +} + +func (q *QuizRepositoryImpl) IsActiveTake(quizId uuid.UUID, userEmail string) (bool, error) { + result := struct{ cnt int }{} + err := q.db. + Select("COUNT(*) as cnt"). + Where("quiz_id = ? AND email = ? AND is_finished = false", quizId, userEmail). + Find(result). + Error + + if err != nil { + return false, nil + } + + return result.cnt > 0, nil +} + +func (q *QuizRepositoryImpl) GetAllTake(quizId uuid.UUID, userEmail string) ([]quiz.QuizTake, error) { + result := []quiz.QuizTake{} + err := q.db. + Where("quiz_id = ? AND email = ?", quizId, userEmail). + Find(result).Error + + return result, err +} + +func (q *QuizRepositoryImpl) GetLastTake(quizId uuid.UUID, userEmail string) (*quiz.QuizTake, error) { + result := &quiz.QuizTake{} + err := q.db. + Where("quiz_id = ? AND email = ?", quizId, userEmail). + Last(result).Error + + return result, err +} diff --git a/repository/quiz/type.go b/repository/quiz/type.go new file mode 100644 index 0000000000000000000000000000000000000000..1b4e0cbeca391ec136dfc8c696d1c058d49e55e4 --- /dev/null +++ b/repository/quiz/type.go @@ -0,0 +1,16 @@ +package quiz + +import ( + "github.com/google/uuid" + "gitlab.informatika.org/ocw/ocw-backend/model/domain/quiz" +) + +type QuizRepository interface { + GetQuizes(courseId string) ([]quiz.Quiz, error) + GetQuizDetail(quizId uuid.UUID) (*quiz.Quiz, error) + UpdateScore(takeId uuid.UUID, score int) error + NewTake(quizId uuid.UUID, userEmail string) (uuid.UUID, error) + IsActiveTake(quizId uuid.UUID, userEmail string) (bool, error) + GetAllTake(quizId uuid.UUID, userEmail string) ([]quiz.QuizTake, error) + GetLastTake(quizId uuid.UUID, userEmail string) (*quiz.QuizTake, error) +} diff --git a/routes/admin/route.go b/routes/admin/route.go index d1ff589b86faa181e6e671bf7111ab3efed0b239..d1fe8e2213ad86d7b7bdd1ecf4fcac90ab8f60a5 100644 --- a/routes/admin/route.go +++ b/routes/admin/route.go @@ -14,7 +14,7 @@ type AdminRoutes struct { func (adr AdminRoutes) Register(r chi.Router) { r.Route("/admin", func(r chi.Router) { - r.Use(adr.GuardBuilder.BuildSimple(user.Admin)) + r.Use(adr.GuardBuilder.Build(user.Admin)) r.Get("/user", adr.AdminHandler.GetAllUser) r.Get("/user/{email}", adr.AdminHandler.GetUserByEmail) diff --git a/routes/course/route.go b/routes/course/route.go index 6fb5f6ac2019b24687bbc28d593011846bed1057..43a8305f4187ad99c9e7baadea375a59f373ad37 100644 --- a/routes/course/route.go +++ b/routes/course/route.go @@ -3,15 +3,19 @@ package course import ( "github.com/go-chi/chi/v5" "gitlab.informatika.org/ocw/ocw-backend/handler/course" + "gitlab.informatika.org/ocw/ocw-backend/handler/material" + "gitlab.informatika.org/ocw/ocw-backend/middleware/guard" + "gitlab.informatika.org/ocw/ocw-backend/model/domain/user" ) type CourseRoutes struct { course.CourseHandler + material.MaterialHandler + *guard.GuardBuilder } func (c CourseRoutes) Register(r chi.Router) { r.Route("/course", func(r chi.Router) { - // Get r.Get("/", c.CourseHandler.GetCourses) r.Get("/{id}", c.CourseHandler.GetCourse) r.Get("/faculty", c.CourseHandler.GetFaculties) @@ -21,18 +25,18 @@ func (c CourseRoutes) Register(r chi.Router) { r.Get("/major", c.CourseHandler.GetMajors) r.Get("/major/{id}", c.CourseHandler.GetMajor) r.Get("/major/courses/{id}", c.CourseHandler.GetCoursesByMajor) - - // Add r.Put("/", c.CourseHandler.AddCourse) r.Put("/faculty", c.CourseHandler.AddFaculty) r.Put("/major", c.CourseHandler.AddMajor) - - // Update r.Patch("/{id}", c.CourseHandler.UpdateCourse) r.Patch("/faculty/{id}", c.CourseHandler.UpdateFaculty) r.Patch("/major/{id}", c.CourseHandler.UpdateMajor) - - // Delete r.Delete("/{id}", c.CourseHandler.DeleteCourse) + r.Get("/{id}/materials", c.MaterialHandler.GetMaterial) + }) + + r.Route("/course/{id}/material", func(r chi.Router) { + r.Use(c.Build(user.Contributor)) + r.Post("/", c.MaterialHandler.CreateMaterial) }) } diff --git a/routes/di.go b/routes/di.go index a51997ed13bc22c420ab390bcd1c46de58b20bce..ddae40f95c5cde473aadc4f6d26a357c98b5f089 100644 --- a/routes/di.go +++ b/routes/di.go @@ -6,6 +6,8 @@ import ( "gitlab.informatika.org/ocw/ocw-backend/routes/auth" "gitlab.informatika.org/ocw/ocw-backend/routes/common" "gitlab.informatika.org/ocw/ocw-backend/routes/course" + "gitlab.informatika.org/ocw/ocw-backend/routes/material" + "gitlab.informatika.org/ocw/ocw-backend/routes/quiz" "gitlab.informatika.org/ocw/ocw-backend/routes/reset" "gitlab.informatika.org/ocw/ocw-backend/routes/swagger" ) @@ -17,6 +19,8 @@ var routesCollectionSet = wire.NewSet( wire.Struct(new(admin.AdminRoutes), "*"), wire.Struct(new(reset.ResetRoutes), "*"), wire.Struct(new(course.CourseRoutes), "*"), + wire.Struct(new(material.MaterialRoutes), "*"), + wire.Struct(new(quiz.QuizRoutes), "*"), ) var RoutesSet = wire.NewSet( diff --git a/routes/material/route.go b/routes/material/route.go new file mode 100644 index 0000000000000000000000000000000000000000..41126ecf7b9b60c1f56f9bd77208e9486db51ef0 --- /dev/null +++ b/routes/material/route.go @@ -0,0 +1,30 @@ +package material + +import ( + "github.com/go-chi/chi/v5" + "gitlab.informatika.org/ocw/ocw-backend/handler/material" + "gitlab.informatika.org/ocw/ocw-backend/middleware/guard" + "gitlab.informatika.org/ocw/ocw-backend/model/domain/user" +) + +type MaterialRoutes struct { + material.MaterialHandler + *guard.GuardBuilder +} + +func (c MaterialRoutes) Register(r chi.Router) { + r.Route("/material/{material-id}", func(r chi.Router) { + r.Get("/", c.DetailMaterial) + + r.Route("/", func(r chi.Router) { + r.Use(c.GuardBuilder.Build(user.Contributor)) + + // Add + r.Post("/content", c.AddContent) + + // Delete + r.Delete("/", c.DeleteMaterial) + r.Delete("/content/{content-id}", c.DeleteContent) + }) + }) +} diff --git a/routes/quiz/route.go b/routes/quiz/route.go new file mode 100644 index 0000000000000000000000000000000000000000..b89269412c9add8ccc39a190a965f187a13c15b9 --- /dev/null +++ b/routes/quiz/route.go @@ -0,0 +1,39 @@ +package quiz + +import ( + "github.com/go-chi/chi/v5" + "gitlab.informatika.org/ocw/ocw-backend/handler/quiz" + "gitlab.informatika.org/ocw/ocw-backend/middleware/guard" + "gitlab.informatika.org/ocw/ocw-backend/model/domain/user" +) + +type QuizRoutes struct { + quiz.QuizHandler + *guard.GuardBuilder +} + +func (q QuizRoutes) Register(r chi.Router) { + r.Get("/course/{id}/quiz", q.QuizHandler.GetAllQuizes) + r.Get("/quiz/{id}", q.QuizHandler.GetQuizDetail) + + guard := q.GuardBuilder.Build( + user.Student, + user.Contributor, + user.Admin, + ) + + r.Route("/quiz/{id}/take", func(r chi.Router) { + r.Use(guard) + r.Post("/", q.QuizHandler.TakeQuiz) + }) + + r.Route("/quiz/{id}/finish", func(r chi.Router) { + r.Use(guard) + r.Post("/", q.QuizHandler.FinishQuiz) + }) + + r.Route("/quiz/{id}/solution", func(r chi.Router) { + r.Use(guard) + r.Get("/", q.QuizHandler.GetQuizSolution) + }) +} diff --git a/routes/routes.go b/routes/routes.go index da4d5b4a4f8efd320ee9b8151f0b8ae4cf4a3757..096f4d5005e2bfe01f82bfe53552edc99b1d0258 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -5,6 +5,8 @@ import ( "gitlab.informatika.org/ocw/ocw-backend/routes/auth" "gitlab.informatika.org/ocw/ocw-backend/routes/common" "gitlab.informatika.org/ocw/ocw-backend/routes/course" + "gitlab.informatika.org/ocw/ocw-backend/routes/material" + "gitlab.informatika.org/ocw/ocw-backend/routes/quiz" "gitlab.informatika.org/ocw/ocw-backend/routes/reset" "gitlab.informatika.org/ocw/ocw-backend/routes/swagger" @@ -18,7 +20,9 @@ type AppRouter struct { common.CommonRoutes auth.AuthRoutes reset.ResetRoutes + quiz.QuizRoutes course.CourseRoutes + material.MaterialRoutes // Utility Logger logger.Logger diff --git a/service/auth/login.go b/service/auth/login.go index 33d52237aec5b52a8a56796b5c410f54e0fe6189..8d4b77df57c9a7689688c1d7d9c85ad20d279e3c 100644 --- a/service/auth/login.go +++ b/service/auth/login.go @@ -31,15 +31,16 @@ func (auth AuthServiceImpl) Login(payload login.LoginRequestPayload) (*login.Log return nil, web.NewResponseError("username and password combination not found", web.InvalidLogin) } - if !user.IsActivated { - return nil, web.NewResponseError("user is not activated yet", web.InactiveUser) - } + // if !user.IsActivated { + // return nil, web.NewResponseError("user is not activated yet", web.InactiveUser) + // } refreshClaim := tokenModel.UserClaim{ - Name: user.Name, - Email: user.Email, - Role: user.Role, - Type: tokenModel.Refresh, + Name: user.Name, + Email: user.Email, + Role: user.Role, + Type: tokenModel.Refresh, + IsVerified: user.IsActivated, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(auth.TokenRefreshExpired) * time.Millisecond)), Issuer: auth.TokenIssuer, @@ -48,10 +49,11 @@ func (auth AuthServiceImpl) Login(payload login.LoginRequestPayload) (*login.Log } accessClaim := tokenModel.UserClaim{ - Name: user.Name, - Email: user.Email, - Role: user.Role, - Type: tokenModel.Access, + Name: user.Name, + Email: user.Email, + Role: user.Role, + Type: tokenModel.Access, + IsVerified: user.IsActivated, RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(auth.TokenAccessExpired) * time.Millisecond)), Issuer: auth.TokenIssuer, diff --git a/service/course/add.go b/service/course/add.go index 531d0ec4410bfa1fc3b87de40826ed94d93f3c91..a94a59b6e56c8d465adf46559d3877dff50b34e0 100644 --- a/service/course/add.go +++ b/service/course/add.go @@ -3,17 +3,18 @@ package course import ( "errors" - "gitlab.informatika.org/ocw/ocw-backend/model/domain/course" + "github.com/google/uuid" + domCourse "gitlab.informatika.org/ocw/ocw-backend/model/domain/course" "gitlab.informatika.org/ocw/ocw-backend/model/domain/user" "gitlab.informatika.org/ocw/ocw-backend/model/web" "gitlab.informatika.org/ocw/ocw-backend/model/web/auth/token" - cadd "gitlab.informatika.org/ocw/ocw-backend/model/web/course/add" - fadd "gitlab.informatika.org/ocw/ocw-backend/model/web/course/faculty/add" - madd "gitlab.informatika.org/ocw/ocw-backend/model/web/course/major/add" + "gitlab.informatika.org/ocw/ocw-backend/model/web/course" + "gitlab.informatika.org/ocw/ocw-backend/model/web/course/faculty" + "gitlab.informatika.org/ocw/ocw-backend/model/web/course/major" "gorm.io/gorm" ) -func (c CourseServiceImpl) AddCourse(payload cadd.AddCourseRequestPayload) error { +func (c CourseServiceImpl) AddCourse(payload course.AddCourseRequestPayload) error { // Validate Role claim, err := c.TokenUtil.Validate(payload.AddCourseToken, token.Access) @@ -53,7 +54,7 @@ func (c CourseServiceImpl) AddCourse(payload cadd.AddCourseRequestPayload) error return web.NewResponseError("Course ID Already Exists", web.IDExists) } - err = c.CourseRepository.AddCourse(course.Course{ + err = c.CourseRepository.AddCourse(domCourse.Course{ ID: payload.ID, Name: payload.Name, Major_id: payload.MajorID, @@ -70,7 +71,7 @@ func (c CourseServiceImpl) AddCourse(payload cadd.AddCourseRequestPayload) error return nil } -func (c CourseServiceImpl) AddMajor(payload madd.AddMajorRequestPayload) error { +func (c CourseServiceImpl) AddMajor(payload major.AddMajorRequestPayload) error { // Validate Role claim, err := c.TokenUtil.Validate(payload.AddMajorToken, token.Access) @@ -99,7 +100,14 @@ func (c CourseServiceImpl) AddMajor(payload madd.AddMajorRequestPayload) error { payload.FacultyID = faculty.ID } - err = c.CourseRepository.AddMajor(course.Major{ + id, err := uuid.NewUUID() + + if err != nil { + return err + } + + err = c.CourseRepository.AddMajor(domCourse.Major{ + ID: id, Name: payload.Name, Fac_id: payload.FacultyID, Abbreviation: payload.Abbreviation, @@ -113,7 +121,7 @@ func (c CourseServiceImpl) AddMajor(payload madd.AddMajorRequestPayload) error { return nil } -func (c CourseServiceImpl) AddFaculty(payload fadd.AddFacultyRequestPayload) error { +func (c CourseServiceImpl) AddFaculty(payload faculty.AddFacultyRequestPayload) error { // Validate Role claim, err := c.TokenUtil.Validate(payload.AddFacultyToken, token.Access) @@ -124,10 +132,17 @@ func (c CourseServiceImpl) AddFaculty(payload fadd.AddFacultyRequestPayload) err // Unauthorized Role if claim.Role != user.Admin { - return web.NewResponseErrorFromError(err, web.UnauthorizedAccess) + return web.NewResponseError("user is not allowed to access this resources", web.UnauthorizedAccess) } - - err = c.CourseRepository.AddFaculty(course.Faculty{ + + id, err := uuid.NewUUID() + + if err != nil { + return err + } + + err = c.CourseRepository.AddFaculty(domCourse.Faculty{ + ID: id, Name: payload.Name, Abbreviation: payload.Abbreviation, }) diff --git a/service/course/delete.go b/service/course/delete.go index a8bda5bc6baf2331980b2b7056282b34b2b272d8..92b73e22d8bd6f3f9380cd421550108d67b280d0 100644 --- a/service/course/delete.go +++ b/service/course/delete.go @@ -4,12 +4,10 @@ import ( "gitlab.informatika.org/ocw/ocw-backend/model/domain/user" "gitlab.informatika.org/ocw/ocw-backend/model/web" "gitlab.informatika.org/ocw/ocw-backend/model/web/auth/token" - "gitlab.informatika.org/ocw/ocw-backend/model/web/course/delete" + "gitlab.informatika.org/ocw/ocw-backend/model/web/course" ) -// TODO: Authorization Checks - -func (c CourseServiceImpl) DeleteCourse(payload delete.DeleteByStringRequestPayload) error { +func (c CourseServiceImpl) DeleteCourse(payload course.DeleteByStringRequestPayload) error { // Validate Role claim, err := c.TokenUtil.Validate(payload.DeleteCourseToken, token.Access) diff --git a/service/course/get.go b/service/course/get.go index 001217d32d6604eae10f6150b1519935e2ffbe7a..3745ea3d47fa2182e3968050fe5a0011c5edf98b 100644 --- a/service/course/get.go +++ b/service/course/get.go @@ -3,13 +3,13 @@ package course import ( "errors" - "gitlab.informatika.org/ocw/ocw-backend/model/domain/course" + domCourse "gitlab.informatika.org/ocw/ocw-backend/model/domain/course" "gitlab.informatika.org/ocw/ocw-backend/model/web" - "gitlab.informatika.org/ocw/ocw-backend/model/web/course/get" + "gitlab.informatika.org/ocw/ocw-backend/model/web/course" "gorm.io/gorm" ) -func (c CourseServiceImpl) GetCourse(payload get.GetByStringRequestPayload) (*course.Course, error) { +func (c CourseServiceImpl) GetCourse(payload course.GetByStringRequestPayload) (*domCourse.Course, error) { packet, err := c.CourseRepository.GetCourse(payload.ID) if err != nil { @@ -23,7 +23,7 @@ func (c CourseServiceImpl) GetCourse(payload get.GetByStringRequestPayload) (*co return packet, nil } -func (c CourseServiceImpl) GetMajor(payload get.GetByUUIDRequestPayload) (*course.Major, error) { +func (c CourseServiceImpl) GetMajor(payload course.GetByUUIDRequestPayload) (*domCourse.Major, error) { packet, err := c.CourseRepository.GetMajor(payload.ID) if err != nil { @@ -37,7 +37,7 @@ func (c CourseServiceImpl) GetMajor(payload get.GetByUUIDRequestPayload) (*cours return packet, nil } -func (c CourseServiceImpl) GetFaculty(payload get.GetByUUIDRequestPayload) (*course.Faculty, error) { +func (c CourseServiceImpl) GetFaculty(payload course.GetByUUIDRequestPayload) (*domCourse.Faculty, error) { packet, err := c.CourseRepository.GetFaculty(payload.ID) if err != nil { @@ -51,7 +51,7 @@ func (c CourseServiceImpl) GetFaculty(payload get.GetByUUIDRequestPayload) (*cou return packet, nil } -func (c CourseServiceImpl) GetAllCourse() ([]course.Course, error) { +func (c CourseServiceImpl) GetAllCourse() ([]domCourse.Course, error) { packet, err := c.CourseRepository.GetAllCourse() if err != nil { @@ -65,7 +65,7 @@ func (c CourseServiceImpl) GetAllCourse() ([]course.Course, error) { return packet, nil } -func (c CourseServiceImpl) GetAllMajor() ([]course.Major, error) { +func (c CourseServiceImpl) GetAllMajor() ([]domCourse.Major, error) { packet, err := c.CourseRepository.GetAllMajor() if err != nil { @@ -79,7 +79,7 @@ func (c CourseServiceImpl) GetAllMajor() ([]course.Major, error) { return packet, nil } -func (c CourseServiceImpl) GetAllFaculty() ([]course.Faculty, error) { +func (c CourseServiceImpl) GetAllFaculty() ([]domCourse.Faculty, error) { packet, err := c.CourseRepository.GetAllFaculty() if err != nil { @@ -94,7 +94,7 @@ func (c CourseServiceImpl) GetAllFaculty() ([]course.Faculty, error) { return packet, nil } -func (c CourseServiceImpl) GetAllCourseByMajor(payload get.GetByUUIDRequestPayload) ([]course.Course, error) { +func (c CourseServiceImpl) GetAllCourseByMajor(payload course.GetByUUIDRequestPayload) ([]domCourse.Course, error) { packet, err := c.CourseRepository.GetAllCourseByMajor(payload.ID) if err != nil { @@ -108,7 +108,7 @@ func (c CourseServiceImpl) GetAllCourseByMajor(payload get.GetByUUIDRequestPaylo return packet, nil } -func (c CourseServiceImpl) GetAllCourseByFaculty(payload get.GetByUUIDRequestPayload) ([]course.Course, error) { +func (c CourseServiceImpl) GetAllCourseByFaculty(payload course.GetByUUIDRequestPayload) ([]domCourse.Course, error) { packet, err := c.CourseRepository.GetAllCourseByFaculty(payload.ID) if err != nil { @@ -122,7 +122,7 @@ func (c CourseServiceImpl) GetAllCourseByFaculty(payload get.GetByUUIDRequestPay return packet, nil } -func (c CourseServiceImpl) GetAllMajorByFaculty(payload get.GetByUUIDRequestPayload) ([]course.Major, error) { +func (c CourseServiceImpl) GetAllMajorByFaculty(payload course.GetByUUIDRequestPayload) ([]domCourse.Major, error) { packet, err := c.CourseRepository.GetAllMajorByFaculty(payload.ID) if err != nil { diff --git a/service/course/type.go b/service/course/type.go index 33f45503ef7a54876001a7d15f15464c9a6cdfe4..0fbc7ecc14c129a318fe93f77d37f3299f0e2f4c 100644 --- a/service/course/type.go +++ b/service/course/type.go @@ -1,33 +1,28 @@ package course import ( - "gitlab.informatika.org/ocw/ocw-backend/model/domain/course" - "gitlab.informatika.org/ocw/ocw-backend/model/web/course/get" - "gitlab.informatika.org/ocw/ocw-backend/model/web/course/delete" - cadd "gitlab.informatika.org/ocw/ocw-backend/model/web/course/add" - madd "gitlab.informatika.org/ocw/ocw-backend/model/web/course/major/add" - fadd "gitlab.informatika.org/ocw/ocw-backend/model/web/course/faculty/add" - cupdate "gitlab.informatika.org/ocw/ocw-backend/model/web/course/update" - mupdate "gitlab.informatika.org/ocw/ocw-backend/model/web/course/major/update" - fupdate "gitlab.informatika.org/ocw/ocw-backend/model/web/course/faculty/update" + domCourse "gitlab.informatika.org/ocw/ocw-backend/model/domain/course" + "gitlab.informatika.org/ocw/ocw-backend/model/web/course" + "gitlab.informatika.org/ocw/ocw-backend/model/web/course/major" + "gitlab.informatika.org/ocw/ocw-backend/model/web/course/faculty" ) type CourseService interface { - AddCourse(payload cadd.AddCourseRequestPayload) error - AddMajor(payload madd.AddMajorRequestPayload) error - AddFaculty(payload fadd.AddFacultyRequestPayload) error - GetCourse(payload get.GetByStringRequestPayload) (*course.Course, error) - GetMajor(payload get.GetByUUIDRequestPayload) (*course.Major, error) - GetFaculty(payload get.GetByUUIDRequestPayload) (*course.Faculty, error) - GetAllCourse() ([]course.Course, error) - GetAllMajor() ([]course.Major, error) - GetAllFaculty() ([]course.Faculty, error) - GetAllCourseByMajor(payload get.GetByUUIDRequestPayload) ([]course.Course, error) - GetAllCourseByFaculty(payload get.GetByUUIDRequestPayload) ([]course.Course, error) - GetAllMajorByFaculty(payload get.GetByUUIDRequestPayload) ([]course.Major, error) - UpdateCourse(payload cupdate.UpdateCourseRequestPayload) error - UpdateMajor(payload mupdate.UpdateMajorRequestPayload) error - UpdateFaculty(payload fupdate.UpdateFacultyRequestPayload) error - DeleteCourse(payload delete.DeleteByStringRequestPayload) error + AddCourse(payload course.AddCourseRequestPayload) error + AddMajor(payload major.AddMajorRequestPayload) error + AddFaculty(payload faculty.AddFacultyRequestPayload) error + GetCourse(payload course.GetByStringRequestPayload) (*domCourse.Course, error) + GetMajor(payload course.GetByUUIDRequestPayload) (*domCourse.Major, error) + GetFaculty(payload course.GetByUUIDRequestPayload) (*domCourse.Faculty, error) + GetAllCourse() ([]domCourse.Course, error) + GetAllMajor() ([]domCourse.Major, error) + GetAllFaculty() ([]domCourse.Faculty, error) + GetAllCourseByMajor(payload course.GetByUUIDRequestPayload) ([]domCourse.Course, error) + GetAllCourseByFaculty(payload course.GetByUUIDRequestPayload) ([]domCourse.Course, error) + GetAllMajorByFaculty(payload course.GetByUUIDRequestPayload) ([]domCourse.Major, error) + UpdateCourse(payload course.UpdateCourseRequestPayload) error + UpdateMajor(payload major.UpdateMajorRequestPayload) error + UpdateFaculty(payload faculty.UpdateFacultyRequestPayload) error + DeleteCourse(payload course.DeleteByStringRequestPayload) error } diff --git a/service/course/update.go b/service/course/update.go index a08d0b3573c456651ab7287c4e9c9f160285edd3..76df6c292d130c3595bbd85a1d0061c1a20ff553 100644 --- a/service/course/update.go +++ b/service/course/update.go @@ -3,19 +3,19 @@ package course import ( "errors" - "gitlab.informatika.org/ocw/ocw-backend/model/domain/course" + domCourse "gitlab.informatika.org/ocw/ocw-backend/model/domain/course" "gitlab.informatika.org/ocw/ocw-backend/model/domain/user" "gitlab.informatika.org/ocw/ocw-backend/model/web" "gitlab.informatika.org/ocw/ocw-backend/model/web/auth/token" - fupdate "gitlab.informatika.org/ocw/ocw-backend/model/web/course/faculty/update" - mupdate "gitlab.informatika.org/ocw/ocw-backend/model/web/course/major/update" - cupdate "gitlab.informatika.org/ocw/ocw-backend/model/web/course/update" + "gitlab.informatika.org/ocw/ocw-backend/model/web/course/faculty" + "gitlab.informatika.org/ocw/ocw-backend/model/web/course/major" + "gitlab.informatika.org/ocw/ocw-backend/model/web/course" "gorm.io/gorm" ) // TODO: Authorization Checks -func (c CourseServiceImpl) UpdateCourse(payload cupdate.UpdateCourseRequestPayload) error { +func (c CourseServiceImpl) UpdateCourse(payload course.UpdateCourseRequestPayload) error { // Validate Role claim, err := c.TokenUtil.Validate(payload.UpdateCourseToken, token.Access) @@ -44,7 +44,7 @@ func (c CourseServiceImpl) UpdateCourse(payload cupdate.UpdateCourseRequestPaylo payload.MajorID = major.ID } - err = c.CourseRepository.UpdateCourse(course.Course{ + err = c.CourseRepository.UpdateCourse(domCourse.Course{ ID: payload.ID, Name: payload.Name, Major_id: payload.MajorID, @@ -65,7 +65,7 @@ func (c CourseServiceImpl) UpdateCourse(payload cupdate.UpdateCourseRequestPaylo return nil } -func (c CourseServiceImpl) UpdateMajor(payload mupdate.UpdateMajorRequestPayload) error { +func (c CourseServiceImpl) UpdateMajor(payload major.UpdateMajorRequestPayload) error { // Validate Role claim, err := c.TokenUtil.Validate(payload.UpdateMajorToken, token.Access) @@ -95,7 +95,7 @@ func (c CourseServiceImpl) UpdateMajor(payload mupdate.UpdateMajorRequestPayload payload.FacultyID = faculty.ID } - err = c.CourseRepository.UpdateMajor(course.Major{ + err = c.CourseRepository.UpdateMajor(domCourse.Major{ ID: payload.ID, Name: payload.Name, Fac_id: payload.FacultyID, @@ -113,7 +113,7 @@ func (c CourseServiceImpl) UpdateMajor(payload mupdate.UpdateMajorRequestPayload return nil } -func (c CourseServiceImpl) UpdateFaculty(payload fupdate.UpdateFacultyRequestPayload) error { +func (c CourseServiceImpl) UpdateFaculty(payload faculty.UpdateFacultyRequestPayload) error { // Validate Role claim, err := c.TokenUtil.Validate(payload.UpdateFacultyToken, token.Access) @@ -127,7 +127,7 @@ func (c CourseServiceImpl) UpdateFaculty(payload fupdate.UpdateFacultyRequestPay return web.NewResponseErrorFromError(err, web.UnauthorizedAccess) } - err = c.CourseRepository.UpdateFaculty(course.Faculty{ + err = c.CourseRepository.UpdateFaculty(domCourse.Faculty{ ID: payload.ID, Name: payload.Name, Abbreviation: payload.Abbreviation, diff --git a/service/di.go b/service/di.go index 6cdb051667f746ccdc3ff0244fd29bdf19f69561..3a133f5fc0ccdaba5c7260c52fa6a068306cf8a2 100644 --- a/service/di.go +++ b/service/di.go @@ -5,13 +5,14 @@ import ( "gitlab.informatika.org/ocw/ocw-backend/service/admin" "gitlab.informatika.org/ocw/ocw-backend/service/auth" "gitlab.informatika.org/ocw/ocw-backend/service/common" + "gitlab.informatika.org/ocw/ocw-backend/service/course" "gitlab.informatika.org/ocw/ocw-backend/service/logger" "gitlab.informatika.org/ocw/ocw-backend/service/logger/hooks" "gitlab.informatika.org/ocw/ocw-backend/service/material" + "gitlab.informatika.org/ocw/ocw-backend/service/quiz" "gitlab.informatika.org/ocw/ocw-backend/service/reporter" "gitlab.informatika.org/ocw/ocw-backend/service/reset" "gitlab.informatika.org/ocw/ocw-backend/service/verification" - "gitlab.informatika.org/ocw/ocw-backend/service/course" ) var ServiceTestSet = wire.NewSet( @@ -64,6 +65,12 @@ var ServiceTestSet = wire.NewSet( wire.Bind(new(material.MaterialContentService), new(*material.MaterialContentServiceImpl)), wire.Bind(new(material.MaterialService), new(*material.MaterialServiceImpl)), ), + + // Quiz service + wire.NewSet( + wire.Struct(new(quiz.QuizServiceImpl), "*"), + wire.Bind(new(quiz.QuizService), new(*quiz.QuizServiceImpl)), + ), ) var ServiceSet = wire.NewSet( diff --git a/service/material/content.go b/service/material/content.go index 5bf0b5638d0e44d4857932fc3d782a6d0a36c45b..e54a1a0ac1eca2445939847036620231d885d801 100644 --- a/service/material/content.go +++ b/service/material/content.go @@ -1,47 +1,98 @@ package material import ( + "context" + "errors" + "fmt" + "strings" + "github.com/google/uuid" materialDomain "gitlab.informatika.org/ocw/ocw-backend/model/domain/material" - "gitlab.informatika.org/ocw/ocw-backend/model/domain/user" + "gitlab.informatika.org/ocw/ocw-backend/model/web" + "gitlab.informatika.org/ocw/ocw-backend/provider/storage" "gitlab.informatika.org/ocw/ocw-backend/repository/material" "gitlab.informatika.org/ocw/ocw-backend/repository/transaction" + "gitlab.informatika.org/ocw/ocw-backend/service/logger" + "gitlab.informatika.org/ocw/ocw-backend/utils/env" + "gorm.io/gorm" ) type MaterialContentServiceImpl struct { transaction.TransactionBuilder material.MaterialContentRepository + material.MaterialRepository + storage.Storage + logger.Logger + *env.Environment +} + +func (m MaterialContentServiceImpl) isMaterialContributor(materialId uuid.UUID, email string) error { + _, err := m.MaterialRepository.IsUserContributor(materialId, email) + + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return web.NewResponseError("materials and user combination not found", "NOT_OWNER") + } + + return err + } + + return nil } -func (m MaterialContentServiceImpl) AddContent(materialId uuid.UUID, user user.User, contents []materialDomain.Content) error { +func (m MaterialContentServiceImpl) AddContent(materialId uuid.UUID, user string, content materialDomain.Content) (string, error) { + // TODO : Check user aman ga nambah konten + if err := m.isMaterialContributor(materialId, user); err != nil { + return "", err + } + isSuccess := false tx := m.Build() tx.Begin() defer tx.Auto(&isSuccess) - // TODO : Check user aman ga nambah konten + if content.Type == materialDomain.Handout { + path := fmt.Sprintf("%s/%s.pdf", m.BucketMaterialBasePath, strings.ReplaceAll(uuid.New().String(), "-", "")) + uploadLink, err := m.Storage.CreatePutSignedLink(context.Background(), path) - for _, content := range contents { - _, err := m.MaterialContentRepository.NewWithTransaction(tx, materialId, content.Type, content.Link) + if err != nil { + m.Logger.Error("Some error happened when generate link") + m.Logger.Error(err.Error()) + return "", err + } + + _, err = m.MaterialContentRepository.NewWithTransaction(tx, materialId, content.Type, path) if err != nil { - return err + return "", err } - } - isSuccess = true - return nil + isSuccess = true + + return uploadLink, nil + } else { + if content.Link == "" { + return "", web.NewResponseError("content is empty", "ERR_CONTENT_LINK_EMPTY") + } + + _, err := m.MaterialContentRepository.NewWithTransaction(tx, materialId, content.Type, content.Link) + + if err == nil { + isSuccess = true + } + + return "", err + } } func (m MaterialContentServiceImpl) DeleteContent( - materialId uuid.UUID, user user.User, contentId uuid.UUID, + materialId uuid.UUID, user string, contentId uuid.UUID, ) error { // TODO: check user aman ga delete konten - return m.MaterialContentRepository.Delete(contentId) -} + if err := m.isMaterialContributor(materialId, user); err != nil { + return err + } -func (m MaterialContentServiceImpl) UpdateContentLink(materialId uuid.UUID, user user.User, contentId uuid.UUID, link string) error { - // TODO: Check user aman ga update link - return m.MaterialContentRepository.UpdateLink(contentId, link) + return m.MaterialContentRepository.Delete(contentId) } diff --git a/service/material/impl.go b/service/material/impl.go index 1e7c95ff7014ec943f19982a4b95892baa52e2bf..c50b67586123fcf70acf3c2c0a4ee7f42b53034c 100644 --- a/service/material/impl.go +++ b/service/material/impl.go @@ -1,11 +1,14 @@ package material import ( + "errors" + "github.com/google/uuid" - materialRepo "gitlab.informatika.org/ocw/ocw-backend/model/domain/material" - "gitlab.informatika.org/ocw/ocw-backend/model/domain/user" + materialDomain "gitlab.informatika.org/ocw/ocw-backend/model/domain/material" + "gitlab.informatika.org/ocw/ocw-backend/model/web" "gitlab.informatika.org/ocw/ocw-backend/repository/material" "gitlab.informatika.org/ocw/ocw-backend/repository/transaction" + "gorm.io/gorm" ) type MaterialServiceImpl struct { @@ -14,32 +17,49 @@ type MaterialServiceImpl struct { material.MaterialRepository } -func (m MaterialServiceImpl) Create(courseId string, user user.User, contents []materialRepo.Content) (uuid.UUID, error) { +func (m MaterialServiceImpl) Get(courseId string) ([]materialDomain.Material, error) { + materials, err := m.MaterialRepository.GetAll(courseId) + return materials, err +} + +func (m MaterialServiceImpl) GetById(materialId uuid.UUID) (*materialDomain.Material, error) { + material, err := m.MaterialRepository.Get(materialId) + + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, web.NewResponseErrorFromError(err, "ID_NOT_FOUND") + } + + return material, err +} + +func (m MaterialServiceImpl) Create(courseId string, email string, name string) (uuid.UUID, error) { isSuccess := false tx := m.TransactionBuilder.Build() tx.Begin() defer tx.Auto(&isSuccess) - id, err := m.MaterialRepository.NewWithTransaction(tx, courseId, user.Email) + id, err := m.MaterialRepository.NewWithTransaction(tx, courseId, email, name) if err != nil { return uuid.Nil, err } - for _, content := range contents { - _, err = m.MaterialContentRepository.NewWithTransaction(tx, id, content.Type, content.Link) - - if err != nil { - return uuid.Nil, err - } - } - isSuccess = true return id, err } -func (m MaterialServiceImpl) Delete(materialId uuid.UUID, user user.User) error { +func (m MaterialServiceImpl) Delete(materialId uuid.UUID, email string) error { // TODO: Pengecekan user apakah kontributor course bukan + _, err := m.MaterialRepository.IsUserContributor(materialId, email) + + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return web.NewResponseError("User is not the owner of material", "NOT_OWNER") + } + + return err + } + return m.MaterialRepository.Delete(materialId) } diff --git a/service/material/type.go b/service/material/type.go index 66fe99b49e176c375812c020389aaf855fb3d7bb..ca617ce6fdce82217b439b2f4b1f0a0b58d3545d 100644 --- a/service/material/type.go +++ b/service/material/type.go @@ -3,16 +3,16 @@ package material import ( "github.com/google/uuid" "gitlab.informatika.org/ocw/ocw-backend/model/domain/material" - "gitlab.informatika.org/ocw/ocw-backend/model/domain/user" ) type MaterialService interface { - Create(courseId string, user user.User, contents []material.Content) (uuid.UUID, error) - Delete(materialId uuid.UUID, user user.User) error + Create(courseId string, email string, name string) (uuid.UUID, error) + Delete(materialId uuid.UUID, email string) error + Get(courseId string) ([]material.Material, error) + GetById(materialId uuid.UUID) (*material.Material, error) } type MaterialContentService interface { - AddContent(materialId uuid.UUID, user user.User, contents []material.Content) error - DeleteContent(materialId uuid.UUID, user user.User, contentId uuid.UUID) error - UpdateContentLink(materialId uuid.UUID, user user.User, contentId uuid.UUID, link string) error + AddContent(materialId uuid.UUID, email string, content material.Content) (string, error) + DeleteContent(materialId uuid.UUID, email string, contentId uuid.UUID) error } diff --git a/service/quiz/impl.go b/service/quiz/impl.go new file mode 100644 index 0000000000000000000000000000000000000000..3898cd448978c6604f71f68e7561077c9c396a00 --- /dev/null +++ b/service/quiz/impl.go @@ -0,0 +1,172 @@ +package quiz + +import ( + "bytes" + "context" + "encoding/json" + + "github.com/google/uuid" + "gitlab.informatika.org/ocw/ocw-backend/model/domain/quiz" + "gitlab.informatika.org/ocw/ocw-backend/model/web" + "gitlab.informatika.org/ocw/ocw-backend/provider/storage" + quizRepo "gitlab.informatika.org/ocw/ocw-backend/repository/quiz" +) + +type QuizServiceImpl struct { + quizRepo.QuizRepository + storage.Storage +} + +func (q QuizServiceImpl) ListAllQuiz(courseId string) ([]quiz.Quiz, error) { + return q.QuizRepository.GetQuizes(courseId) +} + +func (q QuizServiceImpl) GetQuizDetail(quizId uuid.UUID) (*quiz.Quiz, error) { + return q.QuizRepository.GetQuizDetail(quizId) +} + +func (q QuizServiceImpl) getQuizDetail(ctx context.Context, quizId uuid.UUID) (*quiz.QuizDetail, error) { + detail, err := q.QuizRepository.GetQuizDetail(quizId) + + if err != nil { + return nil, err + } + + payload, err := q.Storage.Get(ctx, detail.QuizPath) + + if err != nil { + return nil, err + } + + result := &quiz.QuizDetail{} + + decoder := json.NewDecoder(bytes.NewReader(payload)) + err = decoder.Decode(result) + + return result, err +} + +func (q QuizServiceImpl) DoTakeQuiz(ctx context.Context, quizId uuid.UUID, email string) (*quiz.QuizDetail, error) { + result, err := q.getQuizDetail(ctx, quizId) + + if err != nil { + return nil, err + } + + taken, err := q.IsActiveTake(quizId, email) + + if err != nil { + return nil, err + } + + if !taken { + _, err = q.NewTake(quizId, email) + + if err != nil { + return nil, err + } + } + + for i := range result.Problems { + for j := range result.Problems[i].Answer { + result.Problems[i].Answer[j].IsSolution = nil + } + } + + return result, nil +} + +func (q QuizServiceImpl) GetSolutionQuiz(ctx context.Context, quizId uuid.UUID, email string) (*quiz.QuizDetail, error) { + result, err := q.getQuizDetail(ctx, quizId) + + if err != nil { + return nil, err + } + + _, err = q.GetLastTake(quizId, email) + + if err != nil { + return nil, err + } + + taken, err := q.IsActiveTake(quizId, email) + + if err != nil { + return nil, err + } + + if taken { + return nil, web.NewResponseError("user is not allow to access this data", "ERR_NOT_ALLOWED") + } + + return result, nil +} + +func (q QuizServiceImpl) checkAnswer(detail *quiz.QuizDetail, studentAnswer []quiz.Response) float64 { + answerDict := map[uuid.UUID][]uuid.UUID{} + totalProblem := len(detail.Problems) + + for _, problem := range detail.Problems { + correctAnswerId := []uuid.UUID{} + for _, answer := range problem.Answer { + if *answer.IsSolution { + correctAnswerId = append(correctAnswerId, answer.Id) + } + } + + answerDict[problem.Id] = correctAnswerId + } + + correctAnswer := 0 + for _, responseItem := range studentAnswer { + numCorrect := 0 + + for _, correctId := range answerDict[responseItem.ProblemId] { + if responseItem.AnswerId == correctId { + numCorrect++ + } + } + + if numCorrect == len(answerDict[responseItem.ProblemId]) { + correctAnswer++ + } + } + + return float64(correctAnswer) / float64(totalProblem) * 100 +} + +func (q QuizServiceImpl) DoFinishQuiz(ctx context.Context, quizId uuid.UUID, email string, studentAnswer []quiz.Response) (*quiz.QuizTake, error) { + taken, err := q.IsActiveTake(quizId, email) + + if err != nil { + return nil, err + } + + if !taken { + return nil, web.NewResponseError("user not yet do take the quiz", "NOT_TAKEN_QUIZ_YET") + } + + result, err := q.getQuizDetail(ctx, quizId) + + if err != nil { + return nil, err + } + + score := q.checkAnswer(result, studentAnswer) + + data, err := q.QuizRepository.GetLastTake(quizId, email) + data.IsFinished = true + data.Score = int(score) + + if err != nil { + return nil, err + } + + err = q.QuizRepository.UpdateScore(data.Id, int(score)) + + if err != nil { + return nil, err + } + + return data, nil +} diff --git a/service/quiz/type.go b/service/quiz/type.go new file mode 100644 index 0000000000000000000000000000000000000000..7c97e4965500fb25f75c19a4117c536559fcf9a8 --- /dev/null +++ b/service/quiz/type.go @@ -0,0 +1,17 @@ +package quiz + +import ( + "context" + + "github.com/google/uuid" + "gitlab.informatika.org/ocw/ocw-backend/model/domain/quiz" +) + +type QuizService interface { + ListAllQuiz(courseId string) ([]quiz.Quiz, error) + GetQuizDetail(quizId uuid.UUID) (*quiz.Quiz, error) + + DoTakeQuiz(ctx context.Context, quizId uuid.UUID, email string) (*quiz.QuizDetail, error) + DoFinishQuiz(ctx context.Context, quizId uuid.UUID, email string, studentAnswer []quiz.Response) (*quiz.QuizTake, error) + GetSolutionQuiz(ctx context.Context, quizId uuid.UUID, email string) (*quiz.QuizDetail, error) +} diff --git a/service/reset/request.go b/service/reset/request.go index f3d645f4a08c7f809a117614acde439cca870a08..746fc55ea1365108ad29b6a74ce45d40d3214ec8 100644 --- a/service/reset/request.go +++ b/service/reset/request.go @@ -41,6 +41,8 @@ func (rs ResetServiceImpl) Request(payload request.RequestRequestPayload) error Email: user.Email, Role: user.Role, Type: tokenModel.Access, + IsVerified: user.IsActivated, + RegisteredClaims: jwt.RegisteredClaims{ ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(rs.TokenAccessExpired*6) * time.Millisecond)), Issuer: rs.TokenIssuer, diff --git a/utils/env/env.go b/utils/env/env.go index f53df6088f224fa4822983591509d93e7f4e93c4..ff09c00b0ff32c969ddd850d791ca8e35ad8fc9d 100644 --- a/utils/env/env.go +++ b/utils/env/env.go @@ -62,7 +62,9 @@ type Environment struct { BucketSignedPutDuration int64 `env:"BUCKET_SIGNED_PUT_DURATION_S" envDefault:"36000"` BucketSignedGetDuration int64 `env:"BUCKET_SIGNED_GET_DURATION_S" envDefault:"1800"` - BucketMaterialBasePath string `env:"BUCKET_MATERIAL_BASE_PATH" envDefault:"materials/"` + BucketMaterialBasePath string `env:"BUCKET_MATERIAL_BASE_PATH" envDefault:"materials"` + + UseBucket bool `env:"USE_BUCKET" envDefault:"true"` } func New() (*Environment, error) {