diff --git a/pkg/datastore/datastore.go b/pkg/datastore/datastore.go index 1cc3f48..82eda8f 100644 --- a/pkg/datastore/datastore.go +++ b/pkg/datastore/datastore.go @@ -23,6 +23,7 @@ const ( type Datastore interface { Mapfixes() Mapfixes + Operations() Operations Submissions() Submissions Scripts() Scripts ScriptPolicy() ScriptPolicy @@ -39,6 +40,13 @@ type Mapfixes interface { List(ctx context.Context, filters OptionalMap, page model.Page, sort ListSort) ([]model.Mapfix, error) } +type Operations interface { + Get(ctx context.Context, id int32) (model.Operation, error) + Create(ctx context.Context, smap model.Operation) (model.Operation, error) + Update(ctx context.Context, id int32, values OptionalMap) error + Delete(ctx context.Context, id int32) error +} + type Submissions interface { Get(ctx context.Context, id int64) (model.Submission, error) GetList(ctx context.Context, id []int64) ([]model.Submission, error) diff --git a/pkg/datastore/gormstore/db.go b/pkg/datastore/gormstore/db.go index 01d001c..e4d0ed3 100644 --- a/pkg/datastore/gormstore/db.go +++ b/pkg/datastore/gormstore/db.go @@ -32,6 +32,7 @@ func New(ctx *cli.Context) (datastore.Datastore, error) { if ctx.Bool("migrate") { if err := db.AutoMigrate( &model.Mapfix{}, + &model.Operation{}, &model.Submission{}, &model.Script{}, &model.ScriptPolicy{}, diff --git a/pkg/datastore/gormstore/gormstore.go b/pkg/datastore/gormstore/gormstore.go index da16895..7d88b12 100644 --- a/pkg/datastore/gormstore/gormstore.go +++ b/pkg/datastore/gormstore/gormstore.go @@ -13,6 +13,10 @@ func (g Gormstore) Mapfixes() datastore.Mapfixes { return &Mapfixes{db: g.db} } +func (g Gormstore) Operations() datastore.Operations { + return &Operations{db: g.db} +} + func (g Gormstore) Submissions() datastore.Submissions { return &Submissions{db: g.db} } diff --git a/pkg/datastore/gormstore/operations.go b/pkg/datastore/gormstore/operations.go new file mode 100644 index 0000000..bf49c86 --- /dev/null +++ b/pkg/datastore/gormstore/operations.go @@ -0,0 +1,55 @@ +package gormstore + +import ( + "context" + "errors" + + "git.itzana.me/strafesnet/maps-service/pkg/datastore" + "git.itzana.me/strafesnet/maps-service/pkg/model" + "gorm.io/gorm" +) + +type Operations struct { + db *gorm.DB +} + +func (env *Operations) Get(ctx context.Context, id int32) (model.Operation, error) { + var operation model.Operation + if err := env.db.First(&operation, id).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return operation, datastore.ErrNotExist + } + return operation, err + } + return operation, nil +} + +func (env *Operations) Create(ctx context.Context, smap model.Operation) (model.Operation, error) { + if err := env.db.Create(&smap).Error; err != nil { + return smap, err + } + + return smap, nil +} + +func (env *Operations) Update(ctx context.Context, id int32, values datastore.OptionalMap) error { + if err := env.db.Model(&model.Operation{}).Where("id = ?", id).Updates(values.Map()).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return datastore.ErrNotExist + } + return err + } + + return nil +} + +func (env *Operations) Delete(ctx context.Context, id int32) error { + if err := env.db.Delete(&model.Operation{}, id).Error; err != nil { + if err == gorm.ErrRecordNotFound { + return datastore.ErrNotExist + } + return err + } + + return nil +} diff --git a/pkg/model/operation.go b/pkg/model/operation.go new file mode 100644 index 0000000..4949a92 --- /dev/null +++ b/pkg/model/operation.go @@ -0,0 +1,19 @@ +package model + +import "time" + +type OperationStatus int32 +const ( + OperationStatusCreated OperationStatus = 0 + OperationStatusCompleted OperationStatus = 1 + OperationStatusFailed OperationStatus = 2 +) + +type Operation struct { + ID int32 `gorm:"primaryKey"` + CreatedAt time.Time + Owner int64 // UserID + StatusID OperationStatus + StatusMessage string + Path string // redirect to view completed operation e.g. "/mapfixes/4" +} diff --git a/pkg/service/operations.go b/pkg/service/operations.go new file mode 100644 index 0000000..eb48adc --- /dev/null +++ b/pkg/service/operations.go @@ -0,0 +1,44 @@ +package service + +import ( + "context" + + "git.itzana.me/strafesnet/maps-service/pkg/api" +) + +// GetOperation implements getOperation operation. +// +// Get the specified operation by ID. +// +// GET /operations/{OperationID} +func (svc *Service) GetOperation(ctx context.Context, params api.GetOperationParams) (*api.Operation, error) { + userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle) + if !ok { + return nil, ErrUserInfo + } + + // You must be the operation owner to read it + + operation, err := svc.DB.Operations().Get(ctx, params.OperationID) + if err != nil { + return nil, err + } + + has_role, err := userInfo.IsSubmitter(uint64(operation.Owner)) + if err != nil { + return nil, err + } + // check if caller is operation owner + if !has_role { + return nil, ErrPermissionDeniedNotSubmitter + } + + return &api.Operation{ + OperationID: operation.ID, + Date: operation.CreatedAt.Unix(), + Owner: operation.Owner, + Status: int32(operation.StatusID), + StatusMessage: operation.StatusMessage, + Path: operation.Path, + }, nil +}