diff --git a/docs/docs.go b/docs/docs.go index aad76131fffdf4511f3699ffc3dafc59aa1bc6a5..cb0a9f15cd85048e3c31dbb8c21139959e23b3dd 100644 --- a/docs/docs.go +++ b/docs/docs.go @@ -33,6 +33,35 @@ const docTemplate = `{ } } } + }, + "put": { + "description": "Add new course", + "produces": [ + "application/json" + ], + "tags": [ + "course" + ], + "summary": "Add new course", + "parameters": [ + { + "description": "Payload", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/add.AddCourseRequestPayload" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } } }, "/admin/user": { @@ -551,6 +580,51 @@ const docTemplate = `{ } }, "definitions": { + "add.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" + }, + "major_id": { + "description": "Major Id, will be set by the server", + "type": "string" + }, + "name": { + "description": "Course Name", + "type": "string" + } + } + }, "admin.AdminAddUserPayload": { "type": "object", "required": [ diff --git a/docs/swagger.json b/docs/swagger.json index 5e40a0e52c5c837cf1a9c167f1e4ab064fce87f3..f89ae6961a309970cc486d8abe9fdcab656521dd 100644 --- a/docs/swagger.json +++ b/docs/swagger.json @@ -25,6 +25,35 @@ } } } + }, + "put": { + "description": "Add new course", + "produces": [ + "application/json" + ], + "tags": [ + "course" + ], + "summary": "Add new course", + "parameters": [ + { + "description": "Payload", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/add.AddCourseRequestPayload" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/web.BaseResponse" + } + } + } } }, "/admin/user": { @@ -543,6 +572,51 @@ } }, "definitions": { + "add.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" + }, + "major_id": { + "description": "Major Id, will be set by the server", + "type": "string" + }, + "name": { + "description": "Course Name", + "type": "string" + } + } + }, "admin.AdminAddUserPayload": { "type": "object", "required": [ diff --git a/docs/swagger.yaml b/docs/swagger.yaml index d2280c2b16106e29102e0ed33e5e14d8ac794bc6..72093ff9594e71e7ccb9005fbf5c1d2c8f52474a 100644 --- a/docs/swagger.yaml +++ b/docs/swagger.yaml @@ -1,4 +1,38 @@ definitions: + add.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 admin.AdminAddUserPayload: properties: email: @@ -171,6 +205,25 @@ paths: summary: Index page tags: - common + put: + description: Add new course + parameters: + - description: Payload + in: body + name: data + required: true + schema: + $ref: '#/definitions/add.AddCourseRequestPayload' + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: '#/definitions/web.BaseResponse' + summary: Add new course + tags: + - course /admin/user: get: description: Get all users from database diff --git a/handler/course/addCourse.go b/handler/course/addCourse.go index fd02a41075794b23625bc3a106a9914eb48372f7..ebe3d703a68bc143f720be2dbb8d6cd22405c9f1 100644 --- a/handler/course/addCourse.go +++ b/handler/course/addCourse.go @@ -21,7 +21,6 @@ import ( // @Router / [put] func (c CourseHandlerImpl) AddCourse(w http.ResponseWriter, r *http.Request) { payload := add.AddCourseRequestPayload{} - validate := validator.New() // Validate payload if r.Header.Get("Content-Type") != "application/json" { @@ -36,6 +35,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) diff --git a/handler/di.go b/handler/di.go index 0f3eae80a5bd38faa6a1e07e63e13a9c6c41e11e..643a1fcfdc45a1bb37cd1e2e197932b5e320cb55 100644 --- a/handler/di.go +++ b/handler/di.go @@ -6,6 +6,7 @@ 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/reset" "gitlab.informatika.org/ocw/ocw-backend/handler/swagger" ) @@ -18,11 +19,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 +35,8 @@ 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)), ) diff --git a/handler/material/add_content.go b/handler/material/add_content.go new file mode 100644 index 0000000000000000000000000000000000000000..3a6823fa495d825d95be1b612c8647c20fa94dc7 --- /dev/null +++ b/handler/material/add_content.go @@ -0,0 +1,99 @@ +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" +) + +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..de44a1baadaab8130098fc59a8fa88c9e5c44846 --- /dev/null +++ b/handler/material/create_material.go @@ -0,0 +1,94 @@ +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" +) + +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..8b5cfe0a3cbaf79f7eb209fa8cbaccb4b2a58608 --- /dev/null +++ b/handler/material/delete_content.go @@ -0,0 +1,77 @@ +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" +) + +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..6451f0709eddf11bb429f64576ced94c67ef1c39 --- /dev/null +++ b/handler/material/delete_material.go @@ -0,0 +1,62 @@ +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" +) + +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/get_material.go b/handler/material/get_material.go new file mode 100644 index 0000000000000000000000000000000000000000..f39195f784281d6dfa53f40535f6783b762c8699 --- /dev/null +++ b/handler/material/get_material.go @@ -0,0 +1,40 @@ +package material + +import ( + "net/http" + + "github.com/go-chi/chi/v5" + "gitlab.informatika.org/ocw/ocw-backend/model/web" +) + +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) + + 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/material/impl.go b/handler/material/impl.go new file mode 100644 index 0000000000000000000000000000000000000000..647ce941833b3c696b66271c41dd2565bb23a874 --- /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 + wrapper.WrapperUtil + course.CourseRepository + logger.Logger +} diff --git a/handler/material/types.go b/handler/material/types.go new file mode 100644 index 0000000000000000000000000000000000000000..1c7cb4534dbb5780a6e3fbe5bd38f31d7bbb6369 --- /dev/null +++ b/handler/material/types.go @@ -0,0 +1,12 @@ +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) + DeleteMaterial(w http.ResponseWriter, r *http.Request) + GetMaterial(w http.ResponseWriter, r *http.Request) +} 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/material/content.go b/model/domain/material/content.go index e65e43733da4a1da2c2c71a68780f56cc8f97a72..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 `json:"id" gorm:"primaryKey"` + Id uuid.UUID `gorm:"primaryKey" json:"id"` Type MaterialType `json:"type"` Link string `json:"link"` - MaterialId uuid.UUID `json:"material_id"` + 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 e2fce4d5d6a6cd31d802468fb65eb8bc5f3d54fc..f3b11b7f8820cd71ecf03415eb9339b9a739d2fb 100644 --- a/model/domain/material/material.go +++ b/model/domain/material/material.go @@ -2,17 +2,14 @@ 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 `json:"id" gorm:"primaryKey"` - CourseId string `json:"course_id"` - CreatorEmail string `json:"creator_email"` - Creator *user.User `json:"creator" gorm:"foreignKey:CreatorEmail;references:Email"` - Course *course.Course `json:"course" gorm:"foreignKey:CourseId;references:Id"` - Contents []Content `json:"contents" 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"` + 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 index de062c3cc531f34b4572016248e2085a2d1f1d99..d2bdddbf7da440ce8b48385cd7a5d5b0f118bcef 100644 --- a/model/domain/quiz/options.go +++ b/model/domain/quiz/options.go @@ -3,10 +3,10 @@ package quiz import "github.com/google/uuid" type AnswerOption struct { - Id uuid.UUID `gorm:"primaryKey"` - QuizProblemId uuid.UUID `gorm:"primaryKey"` - Statement string - IsAnswer bool + Id uuid.UUID `gorm:"primaryKey" json:"id"` + QuizProblemId uuid.UUID `gorm:"primaryKey" json:"problem_id"` + Statement string `json:"statement"` + IsAnswer bool `json:"isAnswer"` } func (AnswerOption) TableName() string { diff --git a/model/domain/quiz/quiz.go b/model/domain/quiz/quiz.go index 6caade417ae857ec2a78fdf8c13b5ef007a8529a..f4dcce5a8155d1c0a670dd962bd549233d95b2d8 100644 --- a/model/domain/quiz/quiz.go +++ b/model/domain/quiz/quiz.go @@ -7,13 +7,13 @@ import ( ) 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:"name"` + CourseId string `json:"course_id"` + CreatorEmail string `json:"creator_email"` + Creator user.User `gorm:"foreignKey:CreatorEmail;references:Email" json:"creator"` + Course course.Course `gorm:"foreignKey:CourseId;references:Id" json:"course"` + Problems []QuizProblem `gorm:"foreignKey:QuizId;references:Id" json:"problems"` } func (Quiz) TableName() string { diff --git a/model/domain/quiz/quiz_problem.go b/model/domain/quiz/quiz_problem.go index 3ce4195465d2fd477567f18858296d949c60020c..1bcdcf880ad9eaeeadd164cf60d253fd9eedb8db 100644 --- a/model/domain/quiz/quiz_problem.go +++ b/model/domain/quiz/quiz_problem.go @@ -3,11 +3,11 @@ 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"` + Id uuid.UUID `gorm:"primaryKey" json:"id"` + Statement string `json:"statement"` + Type ProblemType `json:"type"` + QuizId uuid.UUID `json:"quiz_id"` + Options []AnswerOption `gorm:"foreignKey:QuizProblemId;references:Id" json:"options"` } func (QuizProblem) TableName() string { diff --git a/model/domain/quiz/take.go b/model/domain/quiz/take.go index c66263f60f1e4009019e5dcd428e7c905784f243..2c707ab601da28e630420015393607aef0f9d228 100644 --- a/model/domain/quiz/take.go +++ b/model/domain/quiz/take.go @@ -8,15 +8,15 @@ import ( ) 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"` + Quiz `gorm:"foreignKey:QuizId;references:Id" json:"quiz"` + user.User `gorm:"foreignKey:Email;references:Email" json:"user"` + ChoiceAnswers []TakeChoiceAnswer `gorm:"foreignKey:QuizTakeId;references:Id" json:"-"` } func (QuizTake) TableName() string { 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/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/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/repository/course/course.go b/repository/course/course.go index cc092bd007f4a0346c668db6425bc2acb5f19db5..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) 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/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..9cecd65f3fec546c254cfd731fde21660b4f684e 100644 --- a/repository/material/material.go +++ b/repository/material/material.go @@ -3,27 +3,42 @@ 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) 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) (uuid.UUID, error) { +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 +47,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 +64,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..4a0d0920746e05e4ccdfead1096db39dda8402db 100644 --- a/repository/material/type.go +++ b/repository/material/type.go @@ -7,16 +7,19 @@ 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 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/routes/course/route.go b/routes/course/route.go index 6fb5f6ac2019b24687bbc28d593011846bed1057..ccafadb198e645425c1374f6edf70265111a8cb5 100644 --- a/routes/course/route.go +++ b/routes/course/route.go @@ -3,10 +3,15 @@ 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) { @@ -34,5 +39,11 @@ func (c CourseRoutes) Register(r chi.Router) { // 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.BuildSimple(user.Contributor)) + r.Post("/", c.MaterialHandler.CreateMaterial) }) } diff --git a/routes/di.go b/routes/di.go index a51997ed13bc22c420ab390bcd1c46de58b20bce..e05148661337dd26d5400800ca389644f93aa3be 100644 --- a/routes/di.go +++ b/routes/di.go @@ -6,6 +6,7 @@ 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/reset" "gitlab.informatika.org/ocw/ocw-backend/routes/swagger" ) @@ -17,6 +18,7 @@ var routesCollectionSet = wire.NewSet( wire.Struct(new(admin.AdminRoutes), "*"), wire.Struct(new(reset.ResetRoutes), "*"), wire.Struct(new(course.CourseRoutes), "*"), + wire.Struct(new(material.MaterialRoutes), "*"), ) var RoutesSet = wire.NewSet( diff --git a/routes/material/route.go b/routes/material/route.go new file mode 100644 index 0000000000000000000000000000000000000000..31fc398e8a45b45537ac0c72b661c5f404fb239b --- /dev/null +++ b/routes/material/route.go @@ -0,0 +1,26 @@ +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.Use(c.GuardBuilder.BuildSimple(user.Contributor)) + + // Add + r.Post("/content", c.AddContent) + + // Delete + r.Delete("/", c.DeleteMaterial) + r.Delete("/content/{content-id}", c.DeleteContent) + }) +} diff --git a/routes/routes.go b/routes/routes.go index da4d5b4a4f8efd320ee9b8151f0b8ae4cf4a3757..63e1d79fb448d35384bc6f062d360cdb2a7a258f 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -5,6 +5,7 @@ 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/reset" "gitlab.informatika.org/ocw/ocw-backend/routes/swagger" @@ -19,6 +20,7 @@ type AppRouter struct { auth.AuthRoutes reset.ResetRoutes course.CourseRoutes + material.MaterialRoutes // Utility Logger logger.Logger 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..5d59d826c1d80689e479b198b9b714ec94ef1bd2 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,39 @@ 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) 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..1e129d48229c705cd961b4cb8d33c35459b414b9 100644 --- a/service/material/type.go +++ b/service/material/type.go @@ -3,16 +3,15 @@ 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) } 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/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) {