construct transactional update

This commit is contained in:
Quaternions 2024-11-27 18:57:42 -08:00
parent f4fea2963d
commit f5fb895aeb
3 changed files with 38 additions and 80 deletions

View File

@ -19,6 +19,7 @@ type Submissions interface {
GetList(ctx context.Context, id []int64) ([]model.Submission, error)
Create(ctx context.Context, smap model.Submission) (model.Submission, error)
Update(ctx context.Context, id int64, values OptionalMap) error
IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.Status, values OptionalMap) error
Delete(ctx context.Context, id int64) error
List(ctx context.Context, filters OptionalMap, page model.Page) ([]model.Submission, error)
}

View File

@ -3,6 +3,9 @@ package gormstore
import (
"context"
"errors"
"fmt"
"strings"
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
"git.itzana.me/strafesnet/maps-service/pkg/model"
"gorm.io/gorm"
@ -50,6 +53,24 @@ func (env *Submissions) Update(ctx context.Context, id int64, values datastore.O
return nil
}
// the update can only occur if the status matches one of the provided values.
func (env *Submissions) IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.Status, values datastore.OptionalMap) error {
tx := env.db.Model(&model.Submission{}).Where("id = ?", id)
expr := make([]string, 0,len(statuses))
for i, status := range statuses{
expr[i] = fmt.Sprintf("status = %d", int32(status))
}
tx = tx.Where(strings.Join(expr, " OR "))
if err := tx.Updates(values.Map()).Error; err != nil {
if err == gorm.ErrRecordNotFound {
return datastore.ErrNotExist
}
return err
}
return nil
}
func (env *Submissions) Delete(ctx context.Context, id int64) error {
if err := env.db.Delete(&model.Submission{}, id).Error; err != nil {
if err == gorm.ErrRecordNotFound {

View File

@ -144,22 +144,14 @@ func (svc *Service) PatchSubmissionModel(ctx context.Context, params api.PatchSu
//
// PATCH /submissions/{SubmissionID}/status/publish
func (svc *Service) ActionSubmissionPublish(ctx context.Context, params api.ActionSubmissionPublishParams) error {
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
if err != nil{
return err
}
// check if caller has required role
// if !CALLER_ROLES.INCLUDES(ROLE_VALIDATOR){
// return ErrPermissionDenied
// }
// check source status
if submission.StatusID != model.StatusPublishing{
return ErrInvalidSourceStatus
}
// set status
// transaction
smap := datastore.Optional()
smap.Add("status_id",model.StatusPublished)
return svc.DB.Submissions().Update(ctx, params.SubmissionID, smap)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusPublishing}, smap)
}
// ActionSubmissionReject invokes actionSubmissionReject operation.
//
@ -167,22 +159,14 @@ func (svc *Service) ActionSubmissionPublish(ctx context.Context, params api.Acti
//
// PATCH /submissions/{SubmissionID}/status/reject
func (svc *Service) ActionSubmissionReject(ctx context.Context, params api.ActionSubmissionRejectParams) error {
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
if err != nil{
return err
}
// check if caller has required role
// if !CALLER_ROLES.INCLUDES(ROLE_Reviewer){
// return ErrPermissionDenied
// }
// check source status
if submission.StatusID != model.StatusSubmitted{
return ErrInvalidSourceStatus
}
// set status
// transaction
smap := datastore.Optional()
smap.Add("status_id",model.StatusRejected)
return svc.DB.Submissions().Update(ctx, params.SubmissionID, smap)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted}, smap)
}
// ActionSubmissionRequestChanges invokes actionSubmissionRequestChanges operation.
//
@ -190,22 +174,14 @@ func (svc *Service) ActionSubmissionReject(ctx context.Context, params api.Actio
//
// PATCH /submissions/{SubmissionID}/status/request-changes
func (svc *Service) ActionSubmissionRequestChanges(ctx context.Context, params api.ActionSubmissionRequestChangesParams) error {
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
if err != nil{
return err
}
// check if caller has required role
// if !CALLER_ROLES.INCLUDES(ROLE_Reviewer){
// return ErrPermissionDenied
// }
// check source status
if !(submission.StatusID == model.StatusValidated||submission.StatusID == model.StatusAccepted||submission.StatusID == model.StatusSubmitted){
return ErrInvalidSourceStatus
}
// set status
// transaction
smap := datastore.Optional()
smap.Add("status_id",model.StatusChangesRequested)
return svc.DB.Submissions().Update(ctx, params.SubmissionID, smap)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusValidated,model.StatusAccepted,model.StatusSubmitted}, smap)
}
// ActionSubmissionRevoke invokes actionSubmissionRevoke operation.
//
@ -213,22 +189,14 @@ func (svc *Service) ActionSubmissionRequestChanges(ctx context.Context, params a
//
// PATCH /submissions/{SubmissionID}/status/revoke
func (svc *Service) ActionSubmissionRevoke(ctx context.Context, params api.ActionSubmissionRevokeParams) error {
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
if err != nil{
return err
}
// check if caller has required role
// if !CALLER_ROLES.INCLUDES(ROLE_Submitter){
// return ErrPermissionDenied
// }
// check source status
if !(submission.StatusID == model.StatusSubmitted||submission.StatusID == model.StatusChangesRequested){
return ErrInvalidSourceStatus
}
// set status
// transaction
smap := datastore.Optional()
smap.Add("status_id",model.StatusUnderConstruction)
return svc.DB.Submissions().Update(ctx, params.SubmissionID, smap)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted,model.StatusChangesRequested}, smap)
}
// ActionSubmissionSubmit invokes actionSubmissionSubmit operation.
//
@ -236,22 +204,14 @@ func (svc *Service) ActionSubmissionRevoke(ctx context.Context, params api.Actio
//
// PATCH /submissions/{SubmissionID}/status/submit
func (svc *Service) ActionSubmissionSubmit(ctx context.Context, params api.ActionSubmissionSubmitParams) error {
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
if err != nil{
return err
}
// check if caller has required role
// if !CALLER_ROLES.INCLUDES(ROLE_Submitter){
// return ErrPermissionDenied
// }
// check source status
if !(submission.StatusID == model.StatusUnderConstruction||submission.StatusID == model.StatusChangesRequested){
return ErrInvalidSourceStatus
}
// set status
// transaction
smap := datastore.Optional()
smap.Add("status_id",model.StatusSubmitted)
return svc.DB.Submissions().Update(ctx, params.SubmissionID, smap)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusUnderConstruction,model.StatusChangesRequested}, smap)
}
// ActionSubmissionTriggerPublish invokes actionSubmissionTriggerPublish operation.
//
@ -259,22 +219,14 @@ func (svc *Service) ActionSubmissionSubmit(ctx context.Context, params api.Actio
//
// PATCH /submissions/{SubmissionID}/status/trigger-publish
func (svc *Service) ActionSubmissionTriggerPublish(ctx context.Context, params api.ActionSubmissionTriggerPublishParams) error {
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
if err != nil{
return err
}
// check if caller has required role
// if !CALLER_ROLES.INCLUDES(ROLE_Admin){
// return ErrPermissionDenied
// }
// check source status
if !(submission.StatusID == model.StatusValidated){
return ErrInvalidSourceStatus
}
// set status
// transaction
smap := datastore.Optional()
smap.Add("status_id",model.StatusPublishing)
return svc.DB.Submissions().Update(ctx, params.SubmissionID, smap)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusValidated}, smap)
}
// ActionSubmissionTriggerValidate invokes actionSubmissionTriggerValidate operation.
//
@ -282,22 +234,14 @@ func (svc *Service) ActionSubmissionTriggerPublish(ctx context.Context, params a
//
// PATCH /submissions/{SubmissionID}/status/trigger-validate
func (svc *Service) ActionSubmissionTriggerValidate(ctx context.Context, params api.ActionSubmissionTriggerValidateParams) error {
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
if err != nil{
return err
}
// check if caller has required role
// if !CALLER_ROLES.INCLUDES(ROLE_Reviewer){
// return ErrPermissionDenied
// }
// check source status
if !(submission.StatusID == model.StatusSubmitted||submission.StatusID == model.StatusAccepted){
return ErrInvalidSourceStatus
}
// set status
// transaction
smap := datastore.Optional()
smap.Add("status_id",model.StatusValidating)
return svc.DB.Submissions().Update(ctx, params.SubmissionID, smap)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted,model.StatusAccepted}, smap)
}
// ActionSubmissionValidate invokes actionSubmissionValidate operation.
//
@ -305,20 +249,12 @@ func (svc *Service) ActionSubmissionTriggerValidate(ctx context.Context, params
//
// PATCH /submissions/{SubmissionID}/status/validate
func (svc *Service) ActionSubmissionValidate(ctx context.Context, params api.ActionSubmissionValidateParams) error {
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
if err != nil{
return err
}
// check if caller has required role
// if !CALLER_ROLES.INCLUDES(ROLE_VALIDATOR){
// return ErrPermissionDenied
// }
// check source status
if submission.StatusID != model.StatusValidating{
return ErrInvalidSourceStatus
}
// set status
// transaction
smap := datastore.Optional()
smap.Add("status_id",model.StatusValidated)
return svc.DB.Submissions().Update(ctx, params.SubmissionID, smap)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusValidating}, smap)
}