erste Implementierung PropertyGroups

This commit is contained in:
2026-07-27 21:34:48 +02:00
parent 28fb522f70
commit d78355d49e
6 changed files with 529 additions and 10 deletions
@@ -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)
}
@@ -1,7 +1,7 @@
package models package models
type Language struct { type Language struct {
ID int `json:"id"` ID int `json:"id"`
Locale string `json:"locale"` Locale string `json:"locale"`
Language string `json:"language"` Language string `json:"language"`
} }
@@ -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"`
}
@@ -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
}
@@ -1,34 +1,38 @@
package server package server
import ( import (
"database/sql"
"log" "log"
"net/http" "net/http"
"database/sql"
"scpq-editor-server/controller" "scpq-editor-server/controller"
"scpq-editor-server/repositories" "scpq-editor-server/repositories"
"scpq-editor-server/services" "scpq-editor-server/services"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
type HttpServer struct { type HttpServer struct {
config *viper.Viper config *viper.Viper
router *gin.Engine router *gin.Engine
dbHandler *sql.DB dbHandler *sql.DB
languageController *controller.LanguagesController languageController *controller.LanguagesController
propertygroupsController *controller.PropertygroupsController
} }
func InitHttpServer(config *viper.Viper, dbHandler *sql.DB) HttpServer { func InitHttpServer(config *viper.Viper, dbHandler *sql.DB) HttpServer {
// repository initialisation: // repository initialisation:
languagesRepository := repositories.NewLanguagesRepository(dbHandler) languagesRepository := repositories.NewLanguagesRepository(dbHandler)
propertygroupsRepository := repositories.NewPropertiesgroupsRepository(dbHandler)
// service initialisation: // service initialisation:
languagesService := services.NewLanguagesService(languagesRepository) languagesService := services.NewLanguagesService(languagesRepository)
propertygroupsService := services.NewPropertygroupsService(propertygroupsRepository)
// controller initialisation: // controller initialisation:
languageController := controller.NewLanguagesController(languagesService) languageController := controller.NewLanguagesController(languagesService)
propertygroupsController := controller.NewPropertygroupsController(propertygroupsService)
// Service API // Service API
router := gin.Default() router := gin.Default()
@@ -41,10 +45,17 @@ func InitHttpServer(config *viper.Viper, dbHandler *sql.DB) HttpServer {
router.GET("/language/:id", languageController.GetLanguage) router.GET("/language/:id", languageController.GetLanguage)
router.GET("/languages", languageController.GetAllLanguages) 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 // server
return HttpServer{ return HttpServer{
config: config, config: config,
router: router, router: router,
dbHandler: dbHandler, dbHandler: dbHandler,
} }
} }
@@ -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
}