Unquatification

This commit is contained in:
itzaname 2024-12-12 17:29:20 -05:00
parent 3c0028f308
commit fdc1f1aadf
13 changed files with 207 additions and 194 deletions

View File

@ -2,16 +2,16 @@ package cmds
import ( import (
"fmt" "fmt"
"git.itzana.me/strafesnet/go-grpc/auth"
"git.itzana.me/strafesnet/maps-service/pkg/api" "git.itzana.me/strafesnet/maps-service/pkg/api"
"git.itzana.me/strafesnet/maps-service/pkg/datastore/gormstore" "git.itzana.me/strafesnet/maps-service/pkg/datastore/gormstore"
"git.itzana.me/strafesnet/maps-service/pkg/service" "git.itzana.me/strafesnet/maps-service/pkg/service"
"github.com/nats-io/nats.go"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"net/http"
"google.golang.org/grpc" "google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/credentials/insecure"
"git.itzana.me/strafesnet/go-grpc/auth" "net/http"
"github.com/nats-io/nats.go"
) )
func NewServeCommand() *cli.Command { func NewServeCommand() *cli.Command {
@ -93,8 +93,8 @@ func serve(ctx *cli.Context) error {
} }
_, err = js.AddStream(&nats.StreamConfig{ _, err = js.AddStream(&nats.StreamConfig{
Name:"maptest", Name: "maptest",
Subjects: []string{"maptest.>"}, Subjects: []string{"maptest.>"},
Retention: nats.WorkQueuePolicy, Retention: nats.WorkQueuePolicy,
}) })
if err != nil { if err != nil {
@ -102,7 +102,7 @@ func serve(ctx *cli.Context) error {
} }
svc := &service.Service{ svc := &service.Service{
DB: db, DB: db,
Nats: js, Nats: js,
} }

View File

@ -7,7 +7,7 @@ import (
) )
var ( var (
ErrNotExist = errors.New("resource does not exist") ErrNotExist = errors.New("resource does not exist")
ErroNoRowsAffected = errors.New("query did not affect any rows") ErroNoRowsAffected = errors.New("query did not affect any rows")
) )

View File

@ -53,7 +53,7 @@ func (env *Scripts) Update(ctx context.Context, id int64, values datastore.Optio
// the update can only occur if the status matches one of the provided values. // the update can only occur if the status matches one of the provided values.
func (env *Scripts) IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.Status, values datastore.OptionalMap) error { func (env *Scripts) IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.Status, values datastore.OptionalMap) error {
if err := env.db.Model(&model.Script{}).Where("id = ?", id).Where("status IN ?",statuses).Updates(values.Map()).Error; err != nil { if err := env.db.Model(&model.Script{}).Where("id = ?", id).Where("status IN ?", statuses).Updates(values.Map()).Error; err != nil {
if err == gorm.ErrRecordNotFound { if err == gorm.ErrRecordNotFound {
return datastore.ErrNotExist return datastore.ErrNotExist
} }

View File

@ -54,7 +54,7 @@ func (env *Submissions) Update(ctx context.Context, id int64, values datastore.O
// the update can only occur if the status matches one of the provided values. // 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 { func (env *Submissions) IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.Status, values datastore.OptionalMap) error {
if err := env.db.Model(&model.Submission{}).Where("id = ?", id).Where("status_id IN ?",statuses).Updates(values.Map()).Error; err != nil { if err := env.db.Model(&model.Submission{}).Where("id = ?", id).Where("status_id IN ?", statuses).Updates(values.Map()).Error; err != nil {
if err == gorm.ErrRecordNotFound { if err == gorm.ErrRecordNotFound {
return datastore.ErrNotExist return datastore.ErrNotExist
} }
@ -71,7 +71,7 @@ func (env *Submissions) IfStatusThenUpdateAndGet(ctx context.Context, id int64,
result := env.db.Model(&submission). result := env.db.Model(&submission).
Clauses(clause.Returning{}). Clauses(clause.Returning{}).
Where("id = ?", id). Where("id = ?", id).
Where("status_id IN ?",statuses). Where("status_id IN ?", statuses).
Updates(values.Map()) Updates(values.Map())
if result.Error != nil { if result.Error != nil {
if result.Error == gorm.ErrRecordNotFound { if result.Error == gorm.ErrRecordNotFound {

View File

@ -5,7 +5,7 @@ package model
// Requests are sent from maps-service to validator // Requests are sent from maps-service to validator
type ValidateRequest struct{ type ValidateRequest struct {
// submission_id is passed back in the response message // submission_id is passed back in the response message
SubmissionID int64 SubmissionID int64
ModelID uint64 ModelID uint64
@ -14,7 +14,7 @@ type ValidateRequest struct{
} }
// Create a new map // Create a new map
type PublishNewRequest struct{ type PublishNewRequest struct {
SubmissionID int64 SubmissionID int64
ModelID uint64 ModelID uint64
ModelVersion uint64 ModelVersion uint64
@ -24,7 +24,7 @@ type PublishNewRequest struct{
//games HashSet<GameID> //games HashSet<GameID>
} }
type PublishFixRequest struct{ type PublishFixRequest struct {
SubmissionID int64 SubmissionID int64
ModelID uint64 ModelID uint64
ModelVersion uint64 ModelVersion uint64

View File

@ -4,15 +4,15 @@ import "time"
type Policy int32 type Policy int32
const( const (
ScriptPolicyAllowed Policy=0 ScriptPolicyAllowed Policy = 0
ScriptPolicyBlocked Policy=1 ScriptPolicyBlocked Policy = 1
ScriptPolicyDelete Policy=2 ScriptPolicyDelete Policy = 2
ScriptPolicyReplace Policy=3 ScriptPolicyReplace Policy = 3
) )
type ScriptPolicy struct { type ScriptPolicy struct {
ID int64 `gorm:"primaryKey"` ID int64 `gorm:"primaryKey"`
// Hash of the source code that leads to this policy. // Hash of the source code that leads to this policy.
// If this is a replacement mapping, the original source may not be pointed to by any policy. // If this is a replacement mapping, the original source may not be pointed to by any policy.
// The original source should still exist in the scripts table, which can be located by the same hash. // The original source should still exist in the scripts table, which can be located by the same hash.
@ -20,8 +20,8 @@ type ScriptPolicy struct {
// The ID of the replacement source (ScriptPolicyReplace) // The ID of the replacement source (ScriptPolicyReplace)
// or verbatim source (ScriptPolicyAllowed) // or verbatim source (ScriptPolicyAllowed)
// or 0 (other) // or 0 (other)
ToScriptID int64 ToScriptID int64
Policy Policy Policy Policy
CreatedAt time.Time CreatedAt time.Time
UpdatedAt time.Time UpdatedAt time.Time
} }

View File

@ -3,10 +3,10 @@ package model
import "time" import "time"
type Script struct { type Script struct {
ID int64 `gorm:"primaryKey"` ID int64 `gorm:"primaryKey"`
Hash uint64 Hash uint64
Source string Source string
SubmissionID int64 // which submission did this script first appear in SubmissionID int64 // which submission did this script first appear in
CreatedAt time.Time CreatedAt time.Time
UpdatedAt time.Time UpdatedAt time.Time
} }

View File

@ -4,31 +4,31 @@ import "time"
type Status int32 type Status int32
const( const (
StatusPublished Status=8 StatusPublished Status = 8
StatusRejected Status=7 StatusRejected Status = 7
StatusPublishing Status=6 StatusPublishing Status = 6
StatusValidated Status=5 StatusValidated Status = 5
StatusValidating Status=4 StatusValidating Status = 4
StatusAccepted Status=3 StatusAccepted Status = 3
StatusChangesRequested Status=2 StatusChangesRequested Status = 2
StatusSubmitted Status=1 StatusSubmitted Status = 1
StatusUnderConstruction Status=0 StatusUnderConstruction Status = 0
) )
type Submission struct { type Submission struct {
ID int64 `gorm:"primaryKey"` ID int64 `gorm:"primaryKey"`
DisplayName string DisplayName string
Creator string Creator string
GameID int32 GameID int32
CreatedAt time.Time CreatedAt time.Time
UpdatedAt time.Time UpdatedAt time.Time
Submitter uint64 // UserID Submitter uint64 // UserID
AssetID uint64 AssetID uint64
AssetVersion uint64 AssetVersion uint64
Completed bool // Has this version of the map been completed at least once on maptest Completed bool // Has this version of the map been completed at least once on maptest
TargetAssetID uint64 // where to upload map fix. if the TargetAssetID is 0, it's a new map. TargetAssetID uint64 // where to upload map fix. if the TargetAssetID is 0, it's a new map.
StatusID Status StatusID Status
} }

View File

@ -15,18 +15,18 @@ import (
// Create a new script policy. // Create a new script policy.
// //
// POST /script-policy // POST /script-policy
func (svc *Service) CreateScriptPolicy(ctx context.Context, req *api.ScriptPolicyCreate) (*api.ID, error){ func (svc *Service) CreateScriptPolicy(ctx context.Context, req *api.ScriptPolicyCreate) (*api.ID, error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfo) userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok{ if !ok {
return nil, ErrUserInfo return nil, ErrUserInfo
} }
if !userInfo.Roles.ScriptWrite{ if !userInfo.Roles.ScriptWrite {
return nil, ErrPermissionDenied return nil, ErrPermissionDenied
} }
from_script, err := svc.DB.Scripts().Get(ctx,req.FromScriptID) from_script, err := svc.DB.Scripts().Get(ctx, req.FromScriptID)
if err != nil{ if err != nil {
return nil, err return nil, err
} }
@ -38,64 +38,67 @@ func (svc *Service) CreateScriptPolicy(ctx context.Context, req *api.ScriptPolic
ToScriptID: req.ToScriptID, ToScriptID: req.ToScriptID,
Policy: model.Policy(req.Policy), Policy: model.Policy(req.Policy),
}) })
if err != nil{ if err != nil {
return nil, err return nil, err
} }
return &api.ID{ return &api.ID{
ID:script.ID, ID: script.ID,
}, nil }, nil
} }
// DeleteScriptPolicy implements deleteScriptPolicy operation. // DeleteScriptPolicy implements deleteScriptPolicy operation.
// //
// Delete the specified script policy by ID. // Delete the specified script policy by ID.
// //
// DELETE /script-policy/id/{ScriptPolicyID} // DELETE /script-policy/id/{ScriptPolicyID}
func (svc *Service) DeleteScriptPolicy(ctx context.Context, params api.DeleteScriptPolicyParams) error{ func (svc *Service) DeleteScriptPolicy(ctx context.Context, params api.DeleteScriptPolicyParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo) userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok{ if !ok {
return ErrUserInfo return ErrUserInfo
} }
if !userInfo.Roles.ScriptWrite{ if !userInfo.Roles.ScriptWrite {
return ErrPermissionDenied return ErrPermissionDenied
} }
return svc.DB.ScriptPolicy().Delete(ctx, params.ScriptPolicyID) return svc.DB.ScriptPolicy().Delete(ctx, params.ScriptPolicyID)
} }
// GetScriptPolicy implements getScriptPolicy operation. // GetScriptPolicy implements getScriptPolicy operation.
// //
// Get the specified script policy by ID. // Get the specified script policy by ID.
// //
// GET /script-policy/id/{ScriptPolicyID} // GET /script-policy/id/{ScriptPolicyID}
func (svc *Service) GetScriptPolicy(ctx context.Context, params api.GetScriptPolicyParams) (*api.ScriptPolicy, error){ func (svc *Service) GetScriptPolicy(ctx context.Context, params api.GetScriptPolicyParams) (*api.ScriptPolicy, error) {
_, ok := ctx.Value("UserInfo").(UserInfo) _, ok := ctx.Value("UserInfo").(UserInfo)
if !ok{ if !ok {
return nil, ErrUserInfo return nil, ErrUserInfo
} }
// Read permission for script policy only requires you to be logged in // Read permission for script policy only requires you to be logged in
policy, err := svc.DB.ScriptPolicy().Get(ctx, params.ScriptPolicyID) policy, err := svc.DB.ScriptPolicy().Get(ctx, params.ScriptPolicyID)
if err != nil{ if err != nil {
return nil, err return nil, err
} }
return &api.ScriptPolicy{ return &api.ScriptPolicy{
ID: policy.ID, ID: policy.ID,
FromScriptHash: fmt.Sprintf("%x",policy.FromScriptHash), FromScriptHash: fmt.Sprintf("%x", policy.FromScriptHash),
ToScriptID: policy.ToScriptID, ToScriptID: policy.ToScriptID,
Policy: int32(policy.Policy), Policy: int32(policy.Policy),
}, nil }, nil
} }
// GetScriptPolicyFromHash implements getScriptPolicyFromHash operation. // GetScriptPolicyFromHash implements getScriptPolicyFromHash operation.
// //
// Get the policy for the given hash of script source code. // Get the policy for the given hash of script source code.
// //
// GET /script-policy/hash/{FromScriptHash} // GET /script-policy/hash/{FromScriptHash}
func (svc *Service) GetScriptPolicyFromHash(ctx context.Context, params api.GetScriptPolicyFromHashParams) (*api.ScriptPolicy, error){ func (svc *Service) GetScriptPolicyFromHash(ctx context.Context, params api.GetScriptPolicyFromHashParams) (*api.ScriptPolicy, error) {
_, ok := ctx.Value("UserInfo").(UserInfo) _, ok := ctx.Value("UserInfo").(UserInfo)
if !ok{ if !ok {
return nil, ErrUserInfo return nil, ErrUserInfo
} }
@ -103,50 +106,51 @@ func (svc *Service) GetScriptPolicyFromHash(ctx context.Context, params api.GetS
// parse hash from hex // parse hash from hex
hash, err := strconv.ParseUint(params.FromScriptHash, 16, 64) hash, err := strconv.ParseUint(params.FromScriptHash, 16, 64)
if err != nil{ if err != nil {
return nil, err return nil, err
} }
policy, err := svc.DB.ScriptPolicy().GetFromHash(ctx, hash) policy, err := svc.DB.ScriptPolicy().GetFromHash(ctx, hash)
if err != nil{ if err != nil {
return nil, err return nil, err
} }
return &api.ScriptPolicy{ return &api.ScriptPolicy{
ID: policy.ID, ID: policy.ID,
FromScriptHash: fmt.Sprintf("%x",policy.FromScriptHash), FromScriptHash: fmt.Sprintf("%x", policy.FromScriptHash),
ToScriptID: policy.ToScriptID, ToScriptID: policy.ToScriptID,
Policy: int32(policy.Policy), Policy: int32(policy.Policy),
}, nil }, nil
} }
// UpdateScriptPolicy implements updateScriptPolicy operation. // UpdateScriptPolicy implements updateScriptPolicy operation.
// //
// Update the specified script policy by ID. // Update the specified script policy by ID.
// //
// PATCH /script-policy/id/{ScriptPolicyID} // PATCH /script-policy/id/{ScriptPolicyID}
func (svc *Service) UpdateScriptPolicy(ctx context.Context, req *api.ScriptPolicyUpdate, params api.UpdateScriptPolicyParams) error{ func (svc *Service) UpdateScriptPolicy(ctx context.Context, req *api.ScriptPolicyUpdate, params api.UpdateScriptPolicyParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo) userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok{ if !ok {
return ErrUserInfo return ErrUserInfo
} }
if !userInfo.Roles.ScriptWrite{ if !userInfo.Roles.ScriptWrite {
return ErrPermissionDenied return ErrPermissionDenied
} }
pmap := datastore.Optional() pmap := datastore.Optional()
if from_script_id,ok:=req.FromScriptID.Get();ok{ if from_script_id, ok := req.FromScriptID.Get(); ok {
from_script, err := svc.DB.Scripts().Get(ctx,from_script_id) from_script, err := svc.DB.Scripts().Get(ctx, from_script_id)
if err != nil{ if err != nil {
return err return err
} }
pmap.Add("from_script_hash",from_script.Hash) pmap.Add("from_script_hash", from_script.Hash)
} }
if to_script_id,ok:=req.ToScriptID.Get();ok{ if to_script_id, ok := req.ToScriptID.Get(); ok {
pmap.Add("to_script_id",to_script_id) pmap.Add("to_script_id", to_script_id)
} }
if policy,ok:=req.Policy.Get();ok{ if policy, ok := req.Policy.Get(); ok {
pmap.Add("policy",policy) pmap.Add("policy", policy)
} }
return svc.DB.ScriptPolicy().Update(ctx, req.ID, pmap) return svc.DB.ScriptPolicy().Update(ctx, req.ID, pmap)
} }

View File

@ -15,94 +15,97 @@ import (
// Create a new script. // Create a new script.
// //
// POST /scripts // POST /scripts
func (svc *Service) CreateScript(ctx context.Context, req *api.ScriptCreate) (*api.ID, error){ func (svc *Service) CreateScript(ctx context.Context, req *api.ScriptCreate) (*api.ID, error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfo) userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok{ if !ok {
return nil, ErrUserInfo return nil, ErrUserInfo
} }
if !userInfo.Roles.ScriptWrite{ if !userInfo.Roles.ScriptWrite {
return nil, ErrPermissionDenied return nil, ErrPermissionDenied
} }
script, err := svc.DB.Scripts().Create(ctx, model.Script{ script, err := svc.DB.Scripts().Create(ctx, model.Script{
ID: 0, ID: 0,
Hash: siphash.Hash(0,0,[]byte(req.Source)), Hash: siphash.Hash(0, 0, []byte(req.Source)),
Source: req.Source, Source: req.Source,
SubmissionID: req.SubmissionID.Or(0), SubmissionID: req.SubmissionID.Or(0),
}) })
if err != nil{ if err != nil {
return nil, err return nil, err
} }
return &api.ID{ return &api.ID{
ID:script.ID, ID: script.ID,
}, nil }, nil
} }
// DeleteScript implements deleteScript operation. // DeleteScript implements deleteScript operation.
// //
// Delete the specified script by ID. // Delete the specified script by ID.
// //
// DELETE /scripts/{ScriptID} // DELETE /scripts/{ScriptID}
func (svc *Service) DeleteScript(ctx context.Context, params api.DeleteScriptParams) error{ func (svc *Service) DeleteScript(ctx context.Context, params api.DeleteScriptParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo) userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok{ if !ok {
return ErrUserInfo return ErrUserInfo
} }
if !userInfo.Roles.ScriptWrite{ if !userInfo.Roles.ScriptWrite {
return ErrPermissionDenied return ErrPermissionDenied
} }
return svc.DB.Scripts().Delete(ctx, params.ScriptID) return svc.DB.Scripts().Delete(ctx, params.ScriptID)
} }
// GetScript implements getScript operation. // GetScript implements getScript operation.
// //
// Get the specified script by ID. // Get the specified script by ID.
// //
// GET /scripts/{ScriptID} // GET /scripts/{ScriptID}
func (svc *Service) GetScript(ctx context.Context, params api.GetScriptParams) (*api.Script, error){ func (svc *Service) GetScript(ctx context.Context, params api.GetScriptParams) (*api.Script, error) {
_, ok := ctx.Value("UserInfo").(UserInfo) _, ok := ctx.Value("UserInfo").(UserInfo)
if !ok{ if !ok {
return nil, ErrUserInfo return nil, ErrUserInfo
} }
// Read permission for scripts only requires you to be logged in // Read permission for scripts only requires you to be logged in
script, err := svc.DB.Scripts().Get(ctx, params.ScriptID) script, err := svc.DB.Scripts().Get(ctx, params.ScriptID)
if err != nil{ if err != nil {
return nil, err return nil, err
} }
return &api.Script{ return &api.Script{
ID: script.ID, ID: script.ID,
Hash: fmt.Sprintf("%x",script.Hash), Hash: fmt.Sprintf("%x", script.Hash),
Source: script.Source, Source: script.Source,
SubmissionID: script.SubmissionID, SubmissionID: script.SubmissionID,
}, nil }, nil
} }
// UpdateScript implements updateScript operation. // UpdateScript implements updateScript operation.
// //
// Update the specified script by ID. // Update the specified script by ID.
// //
// PATCH /scripts/{ScriptID} // PATCH /scripts/{ScriptID}
func (svc *Service) UpdateScript(ctx context.Context, req *api.ScriptUpdate, params api.UpdateScriptParams) error{ func (svc *Service) UpdateScript(ctx context.Context, req *api.ScriptUpdate, params api.UpdateScriptParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo) userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok{ if !ok {
return ErrUserInfo return ErrUserInfo
} }
if !userInfo.Roles.ScriptWrite{ if !userInfo.Roles.ScriptWrite {
return ErrPermissionDenied return ErrPermissionDenied
} }
pmap := datastore.Optional() pmap := datastore.Optional()
if source,ok:=req.Source.Get();ok{ if source, ok := req.Source.Get(); ok {
pmap.Add("source",source) pmap.Add("source", source)
pmap.Add("hash",siphash.Hash(0,0,[]byte(source))) pmap.Add("hash", siphash.Hash(0, 0, []byte(source)))
} }
if SubmissionID,ok:=req.SubmissionID.Get();ok{ if SubmissionID, ok := req.SubmissionID.Get(); ok {
pmap.Add("submission_id",SubmissionID) pmap.Add("submission_id", SubmissionID)
} }
return svc.DB.Scripts().Update(ctx, req.ID, pmap) return svc.DB.Scripts().Update(ctx, req.ID, pmap)
} }

View File

@ -1,10 +1,10 @@
package service package service
import ( import (
"errors"
"context" "context"
"git.itzana.me/strafesnet/maps-service/pkg/api" "errors"
"git.itzana.me/strafesnet/go-grpc/auth" "git.itzana.me/strafesnet/go-grpc/auth"
"git.itzana.me/strafesnet/maps-service/pkg/api"
) )
var ( var (
@ -16,7 +16,7 @@ var (
var ( var (
// has SubmissionPublish // has SubmissionPublish
RoleMapAdmin int32 = 128 RoleMapAdmin int32 = 128
// has SubmissionReview // has SubmissionReview
RoleMapCouncil int32 = 64 RoleMapCouncil int32 = 64
) )
@ -24,19 +24,19 @@ var (
type Roles struct { type Roles struct {
// human roles // human roles
SubmissionPublish bool SubmissionPublish bool
SubmissionReview bool SubmissionReview bool
ScriptWrite bool ScriptWrite bool
// automated roles // automated roles
Maptest bool Maptest bool
Validator bool Validator bool
} }
type UserInfo struct { type UserInfo struct {
Roles Roles Roles Roles
UserID uint64 UserID uint64
} }
func (usr UserInfo) IsSubmitter(submitter uint64) bool{ func (usr UserInfo) IsSubmitter(submitter uint64) bool {
return usr.UserID == submitter return usr.UserID == submitter
} }
@ -44,7 +44,7 @@ type SecurityHandler struct {
Client auth.AuthServiceClient Client auth.AuthServiceClient
} }
func (svc SecurityHandler) HandleCookieAuth(ctx context.Context, operationName api.OperationName, t api.CookieAuth) (context.Context, error){ func (svc SecurityHandler) HandleCookieAuth(ctx context.Context, operationName api.OperationName, t api.CookieAuth) (context.Context, error) {
sessionId := t.GetAPIKey() sessionId := t.GetAPIKey()
if sessionId == "" { if sessionId == "" {
return nil, ErrMissingSessionID return nil, ErrMissingSessionID
@ -53,41 +53,41 @@ func (svc SecurityHandler) HandleCookieAuth(ctx context.Context, operationName a
session, err := svc.Client.GetSessionUser(ctx, &auth.IdMessage{ session, err := svc.Client.GetSessionUser(ctx, &auth.IdMessage{
SessionID: sessionId, SessionID: sessionId,
}) })
if err != nil{ if err != nil {
return nil, err return nil, err
} }
role, err := svc.Client.GetGroupRole(ctx, &auth.IdMessage{ role, err := svc.Client.GetGroupRole(ctx, &auth.IdMessage{
SessionID: sessionId, SessionID: sessionId,
}) })
if err != nil{ if err != nil {
return nil, err return nil, err
} }
validate, err := svc.Client.ValidateSession(ctx, &auth.IdMessage{ validate, err := svc.Client.ValidateSession(ctx, &auth.IdMessage{
SessionID: sessionId, SessionID: sessionId,
}) })
if err != nil{ if err != nil {
return nil, err return nil, err
} }
if !validate.Valid{ if !validate.Valid {
return nil, ErrInvalidSession return nil, ErrInvalidSession
} }
roles := Roles{} roles := Roles{}
// fix this when roblox udpates group roles // fix this when roblox udpates group roles
for _,r := range role.Roles{ for _, r := range role.Roles {
if RoleMapAdmin<=r.Rank{ if RoleMapAdmin <= r.Rank {
roles.SubmissionPublish = true roles.SubmissionPublish = true
} }
if RoleMapCouncil<=r.Rank{ if RoleMapCouncil <= r.Rank {
roles.SubmissionReview = true roles.SubmissionReview = true
} }
} }
newCtx := context.WithValue(ctx, "UserInfo", UserInfo{ newCtx := context.WithValue(ctx, "UserInfo", UserInfo{
Roles: roles, Roles: roles,
UserID: session.UserID, UserID: session.UserID,
}) })

View File

@ -16,7 +16,7 @@ var (
) )
type Service struct { type Service struct {
DB datastore.Datastore DB datastore.Datastore
Nats nats.JetStreamContext Nats nats.JetStreamContext
} }
@ -25,16 +25,16 @@ type Service struct {
// Used for common default response. // Used for common default response.
func (svc *Service) NewError(ctx context.Context, err error) *api.ErrorStatusCode { func (svc *Service) NewError(ctx context.Context, err error) *api.ErrorStatusCode {
status := 500 status := 500
if errors.Is(err,ErrPermissionDenied){ if errors.Is(err, ErrPermissionDenied) {
status = 403 status = 403
} }
if errors.Is(err,ErrUserInfo){ if errors.Is(err, ErrUserInfo) {
status = 401 status = 401
} }
return &api.ErrorStatusCode{ return &api.ErrorStatusCode{
StatusCode: status, StatusCode: status,
Response: api.Error{ Response: api.Error{
Code: int64(status), Code: int64(status),
Message: err.Error(), Message: err.Error(),
}, },
} }

View File

@ -9,11 +9,10 @@ import (
"git.itzana.me/strafesnet/maps-service/pkg/model" "git.itzana.me/strafesnet/maps-service/pkg/model"
) )
// POST /submissions // POST /submissions
func (svc *Service) CreateSubmission(ctx context.Context, request *api.SubmissionCreate) (*api.ID, error) { func (svc *Service) CreateSubmission(ctx context.Context, request *api.SubmissionCreate) (*api.ID, error) {
userInfo, ok := ctx.Value("UserInfo").(UserInfo) userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok{ if !ok {
return nil, ErrUserInfo return nil, ErrUserInfo
} }
@ -29,11 +28,11 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *api.Submissio
TargetAssetID: uint64(request.TargetAssetID.Value), TargetAssetID: uint64(request.TargetAssetID.Value),
StatusID: model.StatusUnderConstruction, StatusID: model.StatusUnderConstruction,
}) })
if err != nil{ if err != nil {
return nil, err return nil, err
} }
return &api.ID{ return &api.ID{
ID:submission.ID, ID: submission.ID,
}, nil }, nil
} }
@ -44,22 +43,22 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *api.Submissio
// GET /submissions/{SubmissionID} // GET /submissions/{SubmissionID}
func (svc *Service) GetSubmission(ctx context.Context, params api.GetSubmissionParams) (*api.Submission, error) { func (svc *Service) GetSubmission(ctx context.Context, params api.GetSubmissionParams) (*api.Submission, error) {
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID) submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
if err != nil{ if err != nil {
return nil, err return nil, err
} }
return &api.Submission{ return &api.Submission{
ID: submission.ID, ID: submission.ID,
DisplayName: submission.DisplayName, DisplayName: submission.DisplayName,
Creator: submission.Creator, Creator: submission.Creator,
GameID: submission.GameID, GameID: submission.GameID,
CreatedAt: submission.CreatedAt.Unix(), CreatedAt: submission.CreatedAt.Unix(),
UpdatedAt: submission.UpdatedAt.Unix(), UpdatedAt: submission.UpdatedAt.Unix(),
Submitter: int64(submission.Submitter), Submitter: int64(submission.Submitter),
AssetID: int64(submission.AssetID), AssetID: int64(submission.AssetID),
AssetVersion: int64(submission.AssetVersion), AssetVersion: int64(submission.AssetVersion),
Completed: submission.Completed, Completed: submission.Completed,
TargetAssetID: api.NewOptInt64(int64(submission.TargetAssetID)), TargetAssetID: api.NewOptInt64(int64(submission.TargetAssetID)),
StatusID: int32(submission.StatusID), StatusID: int32(submission.StatusID),
}, nil }, nil
} }
@ -81,25 +80,25 @@ func (svc *Service) ListSubmissions(ctx context.Context, request api.ListSubmiss
Number: request.Page.GetPage(), Number: request.Page.GetPage(),
Size: request.Page.GetLimit(), Size: request.Page.GetLimit(),
}) })
if err != nil{ if err != nil {
return nil, err return nil, err
} }
var resp []api.Submission var resp []api.Submission
for i := 0; i < len(items); i++ { for i := 0; i < len(items); i++ {
resp = append(resp, api.Submission{ resp = append(resp, api.Submission{
ID: items[i].ID, ID: items[i].ID,
DisplayName: items[i].DisplayName, DisplayName: items[i].DisplayName,
Creator: items[i].Creator, Creator: items[i].Creator,
GameID: items[i].GameID, GameID: items[i].GameID,
CreatedAt: items[i].CreatedAt.Unix(), CreatedAt: items[i].CreatedAt.Unix(),
UpdatedAt: items[i].UpdatedAt.Unix(), UpdatedAt: items[i].UpdatedAt.Unix(),
Submitter: int64(items[i].Submitter), Submitter: int64(items[i].Submitter),
AssetID: int64(items[i].AssetID), AssetID: int64(items[i].AssetID),
AssetVersion: int64(items[i].AssetVersion), AssetVersion: int64(items[i].AssetVersion),
Completed: items[i].Completed, Completed: items[i].Completed,
TargetAssetID: api.NewOptInt64(int64(items[i].TargetAssetID)), TargetAssetID: api.NewOptInt64(int64(items[i].TargetAssetID)),
StatusID: int32(items[i].StatusID), StatusID: int32(items[i].StatusID),
}) })
} }
@ -113,12 +112,12 @@ func (svc *Service) ListSubmissions(ctx context.Context, request api.ListSubmiss
// POST /submissions/{SubmissionID}/completed // POST /submissions/{SubmissionID}/completed
func (svc *Service) SetSubmissionCompleted(ctx context.Context, params api.SetSubmissionCompletedParams) error { func (svc *Service) SetSubmissionCompleted(ctx context.Context, params api.SetSubmissionCompletedParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo) userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok{ if !ok {
return ErrUserInfo return ErrUserInfo
} }
// check if caller has MaptestGame role (request must originate from a maptest roblox game) // check if caller has MaptestGame role (request must originate from a maptest roblox game)
if !userInfo.Roles.Maptest{ if !userInfo.Roles.Maptest {
return ErrPermissionDenied return ErrPermissionDenied
} }
@ -135,18 +134,18 @@ func (svc *Service) SetSubmissionCompleted(ctx context.Context, params api.SetSu
// POST /submissions/{SubmissionID}/model // POST /submissions/{SubmissionID}/model
func (svc *Service) UpdateSubmissionModel(ctx context.Context, params api.UpdateSubmissionModelParams) error { func (svc *Service) UpdateSubmissionModel(ctx context.Context, params api.UpdateSubmissionModelParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo) userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok{ if !ok {
return ErrUserInfo return ErrUserInfo
} }
// read submission (this could be done with a transaction WHERE clause) // read submission (this could be done with a transaction WHERE clause)
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID) submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
if err != nil{ if err != nil {
return err return err
} }
// check if caller is the submitter // check if caller is the submitter
if !userInfo.IsSubmitter(submission.Submitter){ if !userInfo.IsSubmitter(submission.Submitter) {
return ErrPermissionDenied return ErrPermissionDenied
} }
@ -155,8 +154,8 @@ func (svc *Service) UpdateSubmissionModel(ctx context.Context, params api.Update
pmap.AddNotNil("asset_id", params.ModelID) pmap.AddNotNil("asset_id", params.ModelID)
pmap.AddNotNil("asset_version", params.VersionID) pmap.AddNotNil("asset_version", params.VersionID)
//always reset completed when model changes //always reset completed when model changes
pmap.Add("completed",false) pmap.Add("completed", false)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusChangesRequested,model.StatusSubmitted,model.StatusUnderConstruction}, pmap) return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusChangesRequested, model.StatusSubmitted, model.StatusUnderConstruction}, pmap)
} }
// ActionSubmissionPublish invokes actionSubmissionPublish operation. // ActionSubmissionPublish invokes actionSubmissionPublish operation.
@ -169,9 +168,10 @@ func (svc *Service) ActionSubmissionPublish(ctx context.Context, params api.Acti
// transaction // transaction
smap := datastore.Optional() smap := datastore.Optional()
smap.Add("status_id",model.StatusPublished) smap.Add("status_id", model.StatusPublished)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusPublishing}, smap) return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusPublishing}, smap)
} }
// ActionSubmissionReject invokes actionSubmissionReject operation. // ActionSubmissionReject invokes actionSubmissionReject operation.
// //
// Role Reviewer changes status from Submitted -> Rejected. // Role Reviewer changes status from Submitted -> Rejected.
@ -179,20 +179,21 @@ func (svc *Service) ActionSubmissionPublish(ctx context.Context, params api.Acti
// POST /submissions/{SubmissionID}/status/reject // POST /submissions/{SubmissionID}/status/reject
func (svc *Service) ActionSubmissionReject(ctx context.Context, params api.ActionSubmissionRejectParams) error { func (svc *Service) ActionSubmissionReject(ctx context.Context, params api.ActionSubmissionRejectParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo) userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok{ if !ok {
return ErrUserInfo return ErrUserInfo
} }
// check if caller has required role // check if caller has required role
if !userInfo.Roles.SubmissionReview{ if !userInfo.Roles.SubmissionReview {
return ErrPermissionDenied return ErrPermissionDenied
} }
// transaction // transaction
smap := datastore.Optional() smap := datastore.Optional()
smap.Add("status_id",model.StatusRejected) smap.Add("status_id", model.StatusRejected)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted}, smap) return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted}, smap)
} }
// ActionSubmissionRequestChanges invokes actionSubmissionRequestChanges operation. // ActionSubmissionRequestChanges invokes actionSubmissionRequestChanges operation.
// //
// Role Reviewer changes status from Validated|Accepted|Submitted -> ChangesRequested. // Role Reviewer changes status from Validated|Accepted|Submitted -> ChangesRequested.
@ -200,20 +201,21 @@ func (svc *Service) ActionSubmissionReject(ctx context.Context, params api.Actio
// POST /submissions/{SubmissionID}/status/request-changes // POST /submissions/{SubmissionID}/status/request-changes
func (svc *Service) ActionSubmissionRequestChanges(ctx context.Context, params api.ActionSubmissionRequestChangesParams) error { func (svc *Service) ActionSubmissionRequestChanges(ctx context.Context, params api.ActionSubmissionRequestChangesParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo) userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok{ if !ok {
return ErrUserInfo return ErrUserInfo
} }
// check if caller has required role // check if caller has required role
if !userInfo.Roles.SubmissionReview{ if !userInfo.Roles.SubmissionReview {
return ErrPermissionDenied return ErrPermissionDenied
} }
// transaction // transaction
smap := datastore.Optional() smap := datastore.Optional()
smap.Add("status_id",model.StatusChangesRequested) smap.Add("status_id", model.StatusChangesRequested)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusValidated,model.StatusAccepted,model.StatusSubmitted}, smap) return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusValidated, model.StatusAccepted, model.StatusSubmitted}, smap)
} }
// ActionSubmissionRevoke invokes actionSubmissionRevoke operation. // ActionSubmissionRevoke invokes actionSubmissionRevoke operation.
// //
// Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction. // Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction.
@ -221,26 +223,27 @@ func (svc *Service) ActionSubmissionRequestChanges(ctx context.Context, params a
// POST /submissions/{SubmissionID}/status/revoke // POST /submissions/{SubmissionID}/status/revoke
func (svc *Service) ActionSubmissionRevoke(ctx context.Context, params api.ActionSubmissionRevokeParams) error { func (svc *Service) ActionSubmissionRevoke(ctx context.Context, params api.ActionSubmissionRevokeParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo) userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok{ if !ok {
return ErrUserInfo return ErrUserInfo
} }
// read submission (this could be done with a transaction WHERE clause) // read submission (this could be done with a transaction WHERE clause)
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID) submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
if err != nil{ if err != nil {
return err return err
} }
// check if caller is the submitter // check if caller is the submitter
if !userInfo.IsSubmitter(submission.Submitter){ if !userInfo.IsSubmitter(submission.Submitter) {
return ErrPermissionDenied return ErrPermissionDenied
} }
// transaction // transaction
smap := datastore.Optional() smap := datastore.Optional()
smap.Add("status_id",model.StatusUnderConstruction) smap.Add("status_id", model.StatusUnderConstruction)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted,model.StatusChangesRequested}, smap) return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted, model.StatusChangesRequested}, smap)
} }
// ActionSubmissionSubmit invokes actionSubmissionSubmit operation. // ActionSubmissionSubmit invokes actionSubmissionSubmit operation.
// //
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted. // Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitted.
@ -248,26 +251,27 @@ func (svc *Service) ActionSubmissionRevoke(ctx context.Context, params api.Actio
// POST /submissions/{SubmissionID}/status/submit // POST /submissions/{SubmissionID}/status/submit
func (svc *Service) ActionSubmissionSubmit(ctx context.Context, params api.ActionSubmissionSubmitParams) error { func (svc *Service) ActionSubmissionSubmit(ctx context.Context, params api.ActionSubmissionSubmitParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo) userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok{ if !ok {
return ErrUserInfo return ErrUserInfo
} }
// read submission (this could be done with a transaction WHERE clause) // read submission (this could be done with a transaction WHERE clause)
submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID) submission, err := svc.DB.Submissions().Get(ctx, params.SubmissionID)
if err != nil{ if err != nil {
return err return err
} }
// check if caller is the submitter // check if caller is the submitter
if !userInfo.IsSubmitter(submission.Submitter){ if !userInfo.IsSubmitter(submission.Submitter) {
return ErrPermissionDenied return ErrPermissionDenied
} }
// transaction // transaction
smap := datastore.Optional() smap := datastore.Optional()
smap.Add("status_id",model.StatusSubmitted) smap.Add("status_id", model.StatusSubmitted)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusUnderConstruction,model.StatusChangesRequested}, smap) return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusUnderConstruction, model.StatusChangesRequested}, smap)
} }
// ActionSubmissionTriggerPublish invokes actionSubmissionTriggerPublish operation. // ActionSubmissionTriggerPublish invokes actionSubmissionTriggerPublish operation.
// //
// Role Admin changes status from Validated -> Publishing. // Role Admin changes status from Validated -> Publishing.
@ -275,25 +279,25 @@ func (svc *Service) ActionSubmissionSubmit(ctx context.Context, params api.Actio
// POST /submissions/{SubmissionID}/status/trigger-publish // POST /submissions/{SubmissionID}/status/trigger-publish
func (svc *Service) ActionSubmissionTriggerPublish(ctx context.Context, params api.ActionSubmissionTriggerPublishParams) error { func (svc *Service) ActionSubmissionTriggerPublish(ctx context.Context, params api.ActionSubmissionTriggerPublishParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo) userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok{ if !ok {
return ErrUserInfo return ErrUserInfo
} }
// check if caller has required role // check if caller has required role
if !userInfo.Roles.SubmissionPublish{ if !userInfo.Roles.SubmissionPublish {
return ErrPermissionDenied return ErrPermissionDenied
} }
// transaction // transaction
smap := datastore.Optional() smap := datastore.Optional()
smap.Add("status_id",model.StatusPublishing) smap.Add("status_id", model.StatusPublishing)
submission, err := svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.Status{model.StatusValidated}, smap) submission, err := svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.Status{model.StatusValidated}, smap)
if err != nil{ if err != nil {
return err return err
} }
// sentinel value because we are not using rust // sentinel value because we are not using rust
if submission.TargetAssetID == 0{ if submission.TargetAssetID == 0 {
// this is a new map // this is a new map
publish_new_request := model.PublishNewRequest{ publish_new_request := model.PublishNewRequest{
SubmissionID: submission.ID, SubmissionID: submission.ID,
@ -305,12 +309,12 @@ func (svc *Service) ActionSubmissionTriggerPublish(ctx context.Context, params a
} }
j, err := json.Marshal(publish_new_request) j, err := json.Marshal(publish_new_request)
if err != nil{ if err != nil {
return err return err
} }
svc.Nats.Publish("maptest.submissions.publish.new", []byte(j)) svc.Nats.Publish("maptest.submissions.publish.new", []byte(j))
}else{ } else {
// this is a map fix // this is a map fix
publish_fix_request := model.PublishFixRequest{ publish_fix_request := model.PublishFixRequest{
SubmissionID: submission.ID, SubmissionID: submission.ID,
@ -320,7 +324,7 @@ func (svc *Service) ActionSubmissionTriggerPublish(ctx context.Context, params a
} }
j, err := json.Marshal(publish_fix_request) j, err := json.Marshal(publish_fix_request)
if err != nil{ if err != nil {
return err return err
} }
@ -329,6 +333,7 @@ func (svc *Service) ActionSubmissionTriggerPublish(ctx context.Context, params a
return nil return nil
} }
// ActionSubmissionTriggerValidate invokes actionSubmissionTriggerValidate operation. // ActionSubmissionTriggerValidate invokes actionSubmissionTriggerValidate operation.
// //
// Role Reviewer triggers validation and changes status from Submitted|Accepted -> Validating. // Role Reviewer triggers validation and changes status from Submitted|Accepted -> Validating.
@ -336,20 +341,20 @@ func (svc *Service) ActionSubmissionTriggerPublish(ctx context.Context, params a
// POST /submissions/{SubmissionID}/status/trigger-validate // POST /submissions/{SubmissionID}/status/trigger-validate
func (svc *Service) ActionSubmissionTriggerValidate(ctx context.Context, params api.ActionSubmissionTriggerValidateParams) error { func (svc *Service) ActionSubmissionTriggerValidate(ctx context.Context, params api.ActionSubmissionTriggerValidateParams) error {
userInfo, ok := ctx.Value("UserInfo").(UserInfo) userInfo, ok := ctx.Value("UserInfo").(UserInfo)
if !ok{ if !ok {
return ErrUserInfo return ErrUserInfo
} }
// check if caller has required role // check if caller has required role
if !userInfo.Roles.SubmissionReview{ if !userInfo.Roles.SubmissionReview {
return ErrPermissionDenied return ErrPermissionDenied
} }
// transaction // transaction
smap := datastore.Optional() smap := datastore.Optional()
smap.Add("status_id",model.StatusValidating) smap.Add("status_id", model.StatusValidating)
submission, err := svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted,model.StatusAccepted}, smap) submission, err := svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted, model.StatusAccepted}, smap)
if err != nil{ if err != nil {
return err return err
} }
@ -361,7 +366,7 @@ func (svc *Service) ActionSubmissionTriggerValidate(ctx context.Context, params
} }
j, err := json.Marshal(validate_request) j, err := json.Marshal(validate_request)
if err != nil{ if err != nil {
return err return err
} }
@ -369,6 +374,7 @@ func (svc *Service) ActionSubmissionTriggerValidate(ctx context.Context, params
return nil return nil
} }
// ActionSubmissionValidate invokes actionSubmissionValidate operation. // ActionSubmissionValidate invokes actionSubmissionValidate operation.
// //
// Role Validator changes status from Validating -> Validated. // Role Validator changes status from Validating -> Validated.
@ -379,6 +385,6 @@ func (svc *Service) ActionSubmissionValidate(ctx context.Context, params api.Act
// transaction // transaction
smap := datastore.Optional() smap := datastore.Optional()
smap.Add("status_id",model.StatusValidated) smap.Add("status_id", model.StatusValidated)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusValidating}, smap) return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusValidating}, smap)
} }