diff --git a/buildtime/server/src/scpq-editor-server/controller/propertyGroupsController.go b/buildtime/server/src/scpq-editor-server/controller/propertyGroupsController.go new file mode 100644 index 000000000..192cfd29a --- /dev/null +++ b/buildtime/server/src/scpq-editor-server/controller/propertyGroupsController.go @@ -0,0 +1,163 @@ +package controller + +import ( + "encoding/json" + "io" + "log" + "net/http" + "scpq-editor-server/models" + "scpq-editor-server/services" + "strconv" + + "github.com/gin-gonic/gin" +) + +type PropertygroupsController struct { + PropertygroupsService *services.PropertygroupsService +} + +func NewPropertygroupsController(PropertygroupsService *services.PropertygroupsService) *PropertygroupsController { + return &PropertygroupsController{ + PropertygroupsService: PropertygroupsService, + } +} + +func (lc PropertygroupsController) CreatePropertygroup(ctx *gin.Context) { + + PropertygroupParentId := ctx.Param("parentid") + lId, err := strconv.Atoi(PropertygroupParentId) + if err != nil { + log.Println("Error PropertygroupId not int", err) + ctx.AbortWithError(http.StatusInternalServerError, err) + return + } + parent, responseErr := lc.PropertygroupsService.GetPropertygroup(lId) + if responseErr != nil { + ctx.JSON(responseErr.Status, responseErr) + return + } + + body, err := io.ReadAll(ctx.Request.Body) + if err != nil { + log.Println("Error while reading create Propertygroup request body", err) + ctx.AbortWithError(http.StatusInternalServerError, err) + return + } + + var Propertygroup models.Propertygroup + err = json.Unmarshal(body, &Propertygroup) + if err != nil { + log.Println("Error while unmarshaling create runner request body", err) + ctx.AbortWithError(http.StatusInternalServerError, err) + return + } + response, responseErr := lc.PropertygroupsService.CreatePropertygroup(&Propertygroup, parent) + if responseErr != nil { + ctx.AbortWithStatusJSON(responseErr.Status, responseErr) + return + } + ctx.JSON(http.StatusOK, response) +} + +func (lc PropertygroupsController) UpdatePropertygroup(ctx *gin.Context) { + body, err := io.ReadAll(ctx.Request.Body) + if err != nil { + log.Println("Error while reading update Propertygroup request body", err) + ctx.AbortWithError(http.StatusInternalServerError, err) + return + } + var Propertygroup models.Propertygroup + err = json.Unmarshal(body, &Propertygroup) + if err != nil { + log.Println("Error while unmarshaling update Propertygroup request body", err) + ctx.AbortWithError(http.StatusInternalServerError, err) + return + } + responseErr := lc.PropertygroupsService.UpdatePropertygroup(&Propertygroup) + if responseErr != nil { + ctx.AbortWithStatusJSON(responseErr.Status, responseErr) + return + } + ctx.Status(http.StatusNoContent) +} + +func (lc PropertygroupsController) ChangePropertygroupParent(ctx *gin.Context) { + params := ctx.Request.URL.Query() + oldPropertygroupParentId := params.Get("oldparentid") + lId, err := strconv.Atoi(oldPropertygroupParentId) + if err != nil { + log.Println("Error PropertygroupId not int", err) + ctx.AbortWithError(http.StatusInternalServerError, err) + return + } + oldparent, responseErr := lc.PropertygroupsService.GetPropertygroup(lId) + if responseErr != nil { + ctx.JSON(responseErr.Status, responseErr) + return + } + newPropertygroupParentId := params.Get("newparentid") + lId, err = strconv.Atoi(newPropertygroupParentId) + if err != nil { + log.Println("Error PropertygroupId not int", err) + ctx.AbortWithError(http.StatusInternalServerError, err) + return + } + newparent, responseErr := lc.PropertygroupsService.GetPropertygroup(lId) + if responseErr != nil { + ctx.JSON(responseErr.Status, responseErr) + return + } + + body, err := io.ReadAll(ctx.Request.Body) + if err != nil { + log.Println("Error while reading update Propertygroup request body", err) + ctx.AbortWithError(http.StatusInternalServerError, err) + return + } + var Propertygroup models.Propertygroup + err = json.Unmarshal(body, &Propertygroup) + if err != nil { + log.Println("Error while unmarshaling update Propertygroup request body", err) + ctx.AbortWithError(http.StatusInternalServerError, err) + return + } + responseErr = lc.PropertygroupsService.ChangePropertygroupParent(&Propertygroup, oldparent, newparent) + if responseErr != nil { + ctx.AbortWithStatusJSON(responseErr.Status, responseErr) + return + } + ctx.Status(http.StatusNoContent) +} + +func (lc PropertygroupsController) DeletePropertygroup(ctx *gin.Context) { + PropertygroupId := ctx.Param("id") + lId, err := strconv.Atoi(PropertygroupId) + if err != nil { + log.Println("Error PropertygroupId not int", err) + ctx.AbortWithError(http.StatusInternalServerError, err) + return + } + + responseErr := lc.PropertygroupsService.DeletePropertygroup(lId) + if responseErr != nil { + ctx.JSON(responseErr.Status, responseErr) + return + } + ctx.Status(http.StatusNoContent) +} + +func (lc PropertygroupsController) GetPropertygroup(ctx *gin.Context) { + PropertygroupId := ctx.Param("id") + lId, err := strconv.Atoi(PropertygroupId) + if err != nil { + log.Println("Error PropertygroupId not int", err) + ctx.AbortWithError(http.StatusInternalServerError, err) + return + } + response, responseErr := lc.PropertygroupsService.GetPropertygroup(lId) + if responseErr != nil { + ctx.JSON(responseErr.Status, responseErr) + return + } + ctx.JSON(http.StatusOK, response) +} diff --git a/buildtime/server/src/scpq-editor-server/models/language.go b/buildtime/server/src/scpq-editor-server/models/language.go index d85558ce5..a7f1b8616 100644 --- a/buildtime/server/src/scpq-editor-server/models/language.go +++ b/buildtime/server/src/scpq-editor-server/models/language.go @@ -1,7 +1,7 @@ package models type Language struct { - ID int `json:"id"` - Locale string `json:"locale"` + ID int `json:"id"` + Locale string `json:"locale"` Language string `json:"language"` } diff --git a/buildtime/server/src/scpq-editor-server/models/propertygroup.go b/buildtime/server/src/scpq-editor-server/models/propertygroup.go new file mode 100644 index 000000000..03a873558 --- /dev/null +++ b/buildtime/server/src/scpq-editor-server/models/propertygroup.go @@ -0,0 +1,8 @@ +package models + +type Propertygroup struct { + ID int `json:"id"` + Label string `json:"label"` + Description string `json:"description"` + SubPropertygroups []*Propertygroup `json:"subPropertygroups"` +} diff --git a/buildtime/server/src/scpq-editor-server/repositories/propertygroupsRepository.go b/buildtime/server/src/scpq-editor-server/repositories/propertygroupsRepository.go new file mode 100644 index 000000000..9443dd8f8 --- /dev/null +++ b/buildtime/server/src/scpq-editor-server/repositories/propertygroupsRepository.go @@ -0,0 +1,278 @@ +package repositories + +import ( + "database/sql" + "log" + "net/http" + "scpq-editor-server/models" + "strconv" +) + +type PropertygroupsRepository struct { + dbHandler *sql.DB + m map[int]*models.Propertygroup +} + +func NewPropertiesgroupsRepository(dbHandler *sql.DB) *PropertygroupsRepository { + return &PropertygroupsRepository{ + dbHandler: dbHandler, + m: make(map[int]*models.Propertygroup), + } +} + +func (rr PropertygroupsRepository) CreatePropertygroup(Propertygroup *models.Propertygroup, parent *models.Propertygroup) (*models.Propertygroup, *models.ResponseError) { + query := `INSERT INTO property_groups(label, description, parent_property_group_id) VALUES($1,$2,$2) RETURNING id` + rows, err := rr.dbHandler.Query(query, Propertygroup.Label, Propertygroup.Description, parent.ID) + if err != nil { + return nil, &models.ResponseError{ + Message: err.Error(), + Status: http.StatusInternalServerError, + } + } + + defer rows.Close() + var idstr string + for rows.Next() { + err := rows.Scan(&idstr) + if err != nil { + return nil, &models.ResponseError{ + Message: err.Error(), + Status: http.StatusInternalServerError, + } + } + } + if rows.Err() != nil { + return nil, &models.ResponseError{ + Message: err.Error(), + Status: http.StatusInternalServerError, + } + } + PropertygroupId, err := strconv.Atoi(idstr) + if err != nil { + return nil, &models.ResponseError{ + Message: "Propertygroup id not int", + Status: http.StatusInternalServerError, + } + } + + pg := &models.Propertygroup{ + ID: PropertygroupId, + Label: Propertygroup.Label, + Description: Propertygroup.Description, + SubPropertygroups: []*models.Propertygroup{}, + } + + if parent != nil { + parent.SubPropertygroups = append(parent.SubPropertygroups, pg) + } + rr.m[pg.ID] = pg + return pg, nil +} + +func (rr PropertygroupsRepository) UpdatePropertygroup(Propertygroup *models.Propertygroup) *models.ResponseError { + query := `UPDATE property_groups SET label=$1, description=$2 WHERE id=$3` + res, err := rr.dbHandler.Exec(query, Propertygroup.Label, Propertygroup.Description, Propertygroup.ID) + if err != nil { + return &models.ResponseError{ + Message: err.Error(), + Status: http.StatusInternalServerError, + } + } + + rowsAffeced, err := res.RowsAffected() + if err != nil { + return &models.ResponseError{ + Message: err.Error(), + Status: http.StatusInternalServerError, + } + } + if rowsAffeced == 0 { + return &models.ResponseError{ + Message: "No Propertygroup found with the given id", + Status: http.StatusNotFound, + } + } + rr.m[Propertygroup.ID] = Propertygroup + return nil +} + +func (rr PropertygroupsRepository) ChangePropertygroupParent(Propertygroup *models.Propertygroup, opPropertygroup *models.Propertygroup, npPropertygroup *models.Propertygroup) *models.ResponseError { + query := `UPDATE property_groups SET parent=$1 WHERE id=$2` + res, err := rr.dbHandler.Exec(query, npPropertygroup.ID, Propertygroup.ID) + if err != nil { + return &models.ResponseError{ + Message: err.Error(), + Status: http.StatusInternalServerError, + } + } + + rowsAffeced, err := res.RowsAffected() + if err != nil { + return &models.ResponseError{ + Message: err.Error(), + Status: http.StatusInternalServerError, + } + } + if rowsAffeced == 0 { + return &models.ResponseError{ + Message: "No Propertygroup found with the given id", + Status: http.StatusNotFound, + } + } + + index := findIndex(opPropertygroup.SubPropertygroups, *Propertygroup) + if index != -1 { + opPropertygroup.SubPropertygroups = RemoveAt(opPropertygroup.SubPropertygroups, index) + } + npPropertygroup.SubPropertygroups = append(npPropertygroup.SubPropertygroups, Propertygroup) + + return nil +} + +func findIndex(slice []*models.Propertygroup, target models.Propertygroup) int { + for i, v := range slice { + if v.ID == target.ID { + return i + } + } + return -1 +} + +func RemoveAt[T any](slice []T, index int) []T { + return append(slice[:index], slice[index+1:]...) +} + +func (rr PropertygroupsRepository) DeletePropertygroup(PropertygroupId int) *models.ResponseError { + query := `DELETE FROM property_groups WHERE id=$1` + res, err := rr.dbHandler.Exec(query, PropertygroupId) + if err != nil { + return &models.ResponseError{ + Message: err.Error(), + Status: http.StatusInternalServerError, + } + } + + rowsAffeced, err := res.RowsAffected() + if err != nil { + return &models.ResponseError{ + Message: err.Error(), + Status: http.StatusInternalServerError, + } + } + if rowsAffeced == 0 { + return &models.ResponseError{ + Message: "No Propertygroup found with the given id", + Status: http.StatusNotFound, + } + } + rr.m[PropertygroupId] = nil + return nil +} + +func (rr PropertygroupsRepository) GetPropertygroup(PropertygroupId int) (*models.Propertygroup, *models.ResponseError) { + + if pg, exists := rr.m[PropertygroupId]; exists { + return pg, nil + } + + query := `SELECT id, label, description FROM property_groups WHERE id=$1` + rows, err := rr.dbHandler.Query(query, PropertygroupId) + if err != nil { + return nil, &models.ResponseError{ + Message: err.Error(), + Status: http.StatusInternalServerError, + } + } + + defer rows.Close() + var idstr, label, description string + for rows.Next() { + err := rows.Scan(&idstr, &label, &description) + if err != nil { + return nil, &models.ResponseError{ + Message: err.Error(), + Status: http.StatusInternalServerError, + } + } + } + if rows.Err() != nil { + return nil, &models.ResponseError{ + Message: err.Error(), + Status: http.StatusInternalServerError, + } + } + id, err := strconv.Atoi(idstr) + if err != nil { + return nil, &models.ResponseError{ + Message: "Propertygroup id not int", + Status: http.StatusInternalServerError, + } + } + + pg := &models.Propertygroup{ + ID: id, + Label: label, + Description: description, + } + + err2 := rr.GetSubPropertygroups(pg) + if err2 != nil { + return nil, &models.ResponseError{ + Message: "Error reading sub propertygroups for propertygroup with id " + strconv.Itoa(pg.ID) + " : " + err2.Message, + Status: http.StatusInternalServerError, + } + } + + rr.m[pg.ID] = pg + return pg, nil +} + +func (rr PropertygroupsRepository) GetSubPropertygroups(Propertygroup *models.Propertygroup) *models.ResponseError { + query := `SELECT id FROM property_groups WHERE parent_property_group_id=$1` + rows, err := rr.dbHandler.Query(query, Propertygroup.ID) + if err != nil { + return &models.ResponseError{ + Message: err.Error(), + Status: http.StatusInternalServerError, + } + } + + defer rows.Close() + var idstr string + for rows.Next() { + err := rows.Scan(&idstr) + log.Println("Sub Propertygroup id", idstr) + if err != nil { + return &models.ResponseError{ + Message: err.Error(), + Status: http.StatusInternalServerError, + } + } + id, err := strconv.Atoi(idstr) + if err != nil { + return &models.ResponseError{ + Message: "Sub Propertygroup id not int " + idstr, + Status: http.StatusInternalServerError, + } + } + pg, err2 := rr.GetPropertygroup(id) + if err2 != nil { + return &models.ResponseError{ + Message: "SubPropertygroup not found with the given id " + idstr, + Status: http.StatusInternalServerError, + } + } + if pg != nil { + rr.m[pg.ID] = pg + Propertygroup.SubPropertygroups = append(Propertygroup.SubPropertygroups, pg) + } + + } + if rows.Err() != nil { + return &models.ResponseError{ + Message: err.Error(), + Status: http.StatusInternalServerError, + } + } + return nil +} diff --git a/buildtime/server/src/scpq-editor-server/server/httpServer.go b/buildtime/server/src/scpq-editor-server/server/httpServer.go index 7953f0831..88ccef746 100644 --- a/buildtime/server/src/scpq-editor-server/server/httpServer.go +++ b/buildtime/server/src/scpq-editor-server/server/httpServer.go @@ -1,34 +1,38 @@ package server import ( + "database/sql" "log" "net/http" - "database/sql" "scpq-editor-server/controller" "scpq-editor-server/repositories" "scpq-editor-server/services" + "github.com/gin-gonic/gin" "github.com/spf13/viper" ) type HttpServer struct { - config *viper.Viper - router *gin.Engine - dbHandler *sql.DB - languageController *controller.LanguagesController + config *viper.Viper + router *gin.Engine + dbHandler *sql.DB + languageController *controller.LanguagesController + propertygroupsController *controller.PropertygroupsController } func InitHttpServer(config *viper.Viper, dbHandler *sql.DB) HttpServer { // repository initialisation: languagesRepository := repositories.NewLanguagesRepository(dbHandler) + propertygroupsRepository := repositories.NewPropertiesgroupsRepository(dbHandler) // service initialisation: languagesService := services.NewLanguagesService(languagesRepository) + propertygroupsService := services.NewPropertygroupsService(propertygroupsRepository) // controller initialisation: languageController := controller.NewLanguagesController(languagesService) - + propertygroupsController := controller.NewPropertygroupsController(propertygroupsService) // Service API router := gin.Default() @@ -41,10 +45,17 @@ func InitHttpServer(config *viper.Viper, dbHandler *sql.DB) HttpServer { router.GET("/language/:id", languageController.GetLanguage) router.GET("/languages", languageController.GetAllLanguages) + // propertygroup centric routing + router.POST("/propertygroup/:parentid", propertygroupsController.CreatePropertygroup) + router.PUT("/propertygroup", propertygroupsController.UpdatePropertygroup) + router.PUT("/propertygroup/:id/change-parent/:newparentid", propertygroupsController.ChangePropertygroupParent) + router.DELETE("/propertygroup/:id", propertygroupsController.DeletePropertygroup) + router.GET("/propertygroup/:id", propertygroupsController.GetPropertygroup) + // server return HttpServer{ - config: config, - router: router, + config: config, + router: router, dbHandler: dbHandler, } } diff --git a/buildtime/server/src/scpq-editor-server/services/propertygroupsService.go b/buildtime/server/src/scpq-editor-server/services/propertygroupsService.go new file mode 100644 index 000000000..a5535f00c --- /dev/null +++ b/buildtime/server/src/scpq-editor-server/services/propertygroupsService.go @@ -0,0 +1,59 @@ +package services + +import ( + "net/http" + "scpq-editor-server/models" + "scpq-editor-server/repositories" +) + +type PropertygroupsService struct { + PropertygroupsRepository *repositories.PropertygroupsRepository +} + +func NewPropertygroupsService(PropertygroupsRepository *repositories.PropertygroupsRepository) *PropertygroupsService { + return &PropertygroupsService{ + PropertygroupsRepository: PropertygroupsRepository, + } +} + +func (ls PropertygroupsService) CreatePropertygroup(Propertygroup *models.Propertygroup, parent *models.Propertygroup) (*models.Propertygroup, *models.ResponseError) { + responseErr := validatePropertygroup(Propertygroup) + if responseErr != nil { + return nil, responseErr + } + return ls.PropertygroupsRepository.CreatePropertygroup(Propertygroup, parent) +} + +func (ls PropertygroupsService) UpdatePropertygroup(Propertygroup *models.Propertygroup) *models.ResponseError { + responseErr := validatePropertygroup(Propertygroup) + if responseErr != nil { + return responseErr + } + return ls.PropertygroupsRepository.UpdatePropertygroup(Propertygroup) +} + +func (ls PropertygroupsService) ChangePropertygroupParent(Propertygroup *models.Propertygroup, opPropertygroup *models.Propertygroup, npPropertygroup *models.Propertygroup) *models.ResponseError { + responseErr := validatePropertygroup(Propertygroup) + if responseErr != nil { + return responseErr + } + return ls.PropertygroupsRepository.ChangePropertygroupParent(Propertygroup, opPropertygroup, npPropertygroup) +} + +func (ls PropertygroupsService) DeletePropertygroup(PropertygroupId int) *models.ResponseError { + return ls.PropertygroupsRepository.DeletePropertygroup(PropertygroupId) +} + +func (ls PropertygroupsService) GetPropertygroup(PropertygroupId int) (*models.Propertygroup, *models.ResponseError) { + return ls.PropertygroupsRepository.GetPropertygroup(PropertygroupId) +} + +func validatePropertygroup(Propertygroup *models.Propertygroup) *models.ResponseError { + if Propertygroup.Label == "" { + return &models.ResponseError{ + Message: "Invalid label", + Status: http.StatusBadRequest, + } + } + return nil +}