Prepare To Implement Mapfixes Separately #52

Merged
Quaternions merged 18 commits from separate-mapfixes into staging 2025-04-01 02:27:51 +00:00
35 changed files with 775 additions and 599 deletions

@ -85,8 +85,9 @@ paths:
- Submissions - Submissions
parameters: parameters:
- $ref: '#/components/parameters/SubmissionID' - $ref: '#/components/parameters/SubmissionID'
- name: TargetAssetID - name: UploadedAssetID
in: query in: query
required: true
schema: schema:
type: integer type: integer
format: int64 format: int64
@ -303,7 +304,8 @@ components:
- Name - Name
- Hash - Hash
- Source - Source
- SubmissionID - ResourceType
- ResourceID
type: object type: object
properties: properties:
ID: ID:
@ -319,14 +321,18 @@ components:
Source: Source:
type: string type: string
maxLength: 1048576 maxLength: 1048576
SubmissionID: ResourceType:
type: integer
format: int32
ResourceID:
type: integer type: integer
format: int64 format: int64
ScriptCreate: ScriptCreate:
required: required:
- Name - Name
- Source - Source
# - SubmissionID - ResourceType
# - ResourceID
type: object type: object
properties: properties:
Name: Name:
@ -335,7 +341,10 @@ components:
Source: Source:
type: string type: string
maxLength: 1048576 maxLength: 1048576
SubmissionID: ResourceType:
type: integer
format: int32
ResourceID:
type: integer type: integer
format: int64 format: int64
ScriptPolicy: ScriptPolicy:

@ -737,8 +737,7 @@ components:
- AssetID - AssetID
- AssetVersion - AssetVersion
- Completed - Completed
- SubmissionType # - UploadedAssetID
# - TargetAssetID
- StatusID - StatusID
- StatusMessage - StatusMessage
type: object type: object
@ -772,10 +771,7 @@ components:
format: int64 format: int64
Completed: Completed:
type: boolean type: boolean
SubmissionType: UploadedAssetID:
type: integer
format: int32
TargetAssetID:
type: integer type: integer
format: int64 format: int64
StatusID: StatusID:
@ -791,7 +787,6 @@ components:
- GameID - GameID
- AssetID - AssetID
- AssetVersion - AssetVersion
# - TargetAssetID
type: object type: object
properties: properties:
DisplayName: DisplayName:
@ -809,9 +804,6 @@ components:
AssetVersion: AssetVersion:
type: integer type: integer
format: int64 format: int64
TargetAssetID:
type: integer
format: int64
ReleaseInfo: ReleaseInfo:
required: required:
- SubmissionID - SubmissionID
@ -830,7 +822,8 @@ components:
- Name - Name
- Hash - Hash
- Source - Source
- SubmissionID - ResourceType
- ResourceID
type: object type: object
properties: properties:
ID: ID:
@ -846,14 +839,18 @@ components:
Source: Source:
type: string type: string
maxLength: 1048576 maxLength: 1048576
SubmissionID: ResourceType:
type: integer
format: int32
ResourceID:
type: integer type: integer
format: int64 format: int64
ScriptCreate: ScriptCreate:
required: required:
- Name - Name
- Source - Source
# - SubmissionID - ResourceType
# - ResourceID
type: object type: object
properties: properties:
Name: Name:
@ -862,7 +859,10 @@ components:
Source: Source:
type: string type: string
maxLength: 1048576 maxLength: 1048576
SubmissionID: ResourceType:
type: integer
format: int32
ResourceID:
type: integer type: integer
format: int64 format: int64
ScriptUpdate: ScriptUpdate:
@ -879,7 +879,10 @@ components:
Source: Source:
type: string type: string
maxLength: 1048576 maxLength: 1048576
SubmissionID: ResourceType:
type: integer
format: int32
ResourceID:
type: integer type: integer
format: int64 format: int64
ScriptPolicy: ScriptPolicy:

@ -562,17 +562,22 @@ func (s *Script) encodeFields(e *jx.Encoder) {
e.Str(s.Source) e.Str(s.Source)
} }
{ {
e.FieldStart("SubmissionID") e.FieldStart("ResourceType")
e.Int64(s.SubmissionID) e.Int32(s.ResourceType)
}
{
e.FieldStart("ResourceID")
e.Int64(s.ResourceID)
} }
} }
var jsonFieldsNameOfScript = [5]string{ var jsonFieldsNameOfScript = [6]string{
0: "ID", 0: "ID",
1: "Name", 1: "Name",
2: "Hash", 2: "Hash",
3: "Source", 3: "Source",
4: "SubmissionID", 4: "ResourceType",
5: "ResourceID",
} }
// Decode decodes Script from json. // Decode decodes Script from json.
@ -632,17 +637,29 @@ func (s *Script) Decode(d *jx.Decoder) error {
}(); err != nil { }(); err != nil {
return errors.Wrap(err, "decode field \"Source\"") return errors.Wrap(err, "decode field \"Source\"")
} }
case "SubmissionID": case "ResourceType":
requiredBitSet[0] |= 1 << 4 requiredBitSet[0] |= 1 << 4
if err := func() error { if err := func() error {
v, err := d.Int64() v, err := d.Int32()
s.SubmissionID = int64(v) s.ResourceType = int32(v)
if err != nil { if err != nil {
return err return err
} }
return nil return nil
}(); err != nil { }(); err != nil {
return errors.Wrap(err, "decode field \"SubmissionID\"") return errors.Wrap(err, "decode field \"ResourceType\"")
}
case "ResourceID":
requiredBitSet[0] |= 1 << 5
if err := func() error {
v, err := d.Int64()
s.ResourceID = int64(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"ResourceID\"")
} }
default: default:
return d.Skip() return d.Skip()
@ -654,7 +671,7 @@ func (s *Script) Decode(d *jx.Decoder) error {
// Validate required fields. // Validate required fields.
var failures []validate.FieldError var failures []validate.FieldError
for i, mask := range [1]uint8{ for i, mask := range [1]uint8{
0b00011111, 0b00111111,
} { } {
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 { if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
// Mask only required fields and check equality to mask using XOR. // Mask only required fields and check equality to mask using XOR.
@ -718,17 +735,22 @@ func (s *ScriptCreate) encodeFields(e *jx.Encoder) {
e.Str(s.Source) e.Str(s.Source)
} }
{ {
if s.SubmissionID.Set { e.FieldStart("ResourceType")
e.FieldStart("SubmissionID") e.Int32(s.ResourceType)
s.SubmissionID.Encode(e) }
{
if s.ResourceID.Set {
e.FieldStart("ResourceID")
s.ResourceID.Encode(e)
} }
} }
} }
var jsonFieldsNameOfScriptCreate = [3]string{ var jsonFieldsNameOfScriptCreate = [4]string{
0: "Name", 0: "Name",
1: "Source", 1: "Source",
2: "SubmissionID", 2: "ResourceType",
3: "ResourceID",
} }
// Decode decodes ScriptCreate from json. // Decode decodes ScriptCreate from json.
@ -764,15 +786,27 @@ func (s *ScriptCreate) Decode(d *jx.Decoder) error {
}(); err != nil { }(); err != nil {
return errors.Wrap(err, "decode field \"Source\"") return errors.Wrap(err, "decode field \"Source\"")
} }
case "SubmissionID": case "ResourceType":
requiredBitSet[0] |= 1 << 2
if err := func() error { if err := func() error {
s.SubmissionID.Reset() v, err := d.Int32()
if err := s.SubmissionID.Decode(d); err != nil { s.ResourceType = int32(v)
if err != nil {
return err return err
} }
return nil return nil
}(); err != nil { }(); err != nil {
return errors.Wrap(err, "decode field \"SubmissionID\"") return errors.Wrap(err, "decode field \"ResourceType\"")
}
case "ResourceID":
if err := func() error {
s.ResourceID.Reset()
if err := s.ResourceID.Decode(d); err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"ResourceID\"")
} }
default: default:
return d.Skip() return d.Skip()
@ -784,7 +818,7 @@ func (s *ScriptCreate) Decode(d *jx.Decoder) error {
// Validate required fields. // Validate required fields.
var failures []validate.FieldError var failures []validate.FieldError
for i, mask := range [1]uint8{ for i, mask := range [1]uint8{
0b00000011, 0b00000111,
} { } {
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 { if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
// Mask only required fields and check equality to mask using XOR. // Mask only required fields and check equality to mask using XOR.
@ -1280,18 +1314,25 @@ func (s *ScriptUpdate) encodeFields(e *jx.Encoder) {
} }
} }
{ {
if s.SubmissionID.Set { if s.ResourceType.Set {
e.FieldStart("SubmissionID") e.FieldStart("ResourceType")
s.SubmissionID.Encode(e) s.ResourceType.Encode(e)
}
}
{
if s.ResourceID.Set {
e.FieldStart("ResourceID")
s.ResourceID.Encode(e)
} }
} }
} }
var jsonFieldsNameOfScriptUpdate = [4]string{ var jsonFieldsNameOfScriptUpdate = [5]string{
0: "ID", 0: "ID",
1: "Name", 1: "Name",
2: "Source", 2: "Source",
3: "SubmissionID", 3: "ResourceType",
4: "ResourceID",
} }
// Decode decodes ScriptUpdate from json. // Decode decodes ScriptUpdate from json.
@ -1335,15 +1376,25 @@ func (s *ScriptUpdate) Decode(d *jx.Decoder) error {
}(); err != nil { }(); err != nil {
return errors.Wrap(err, "decode field \"Source\"") return errors.Wrap(err, "decode field \"Source\"")
} }
case "SubmissionID": case "ResourceType":
if err := func() error { if err := func() error {
s.SubmissionID.Reset() s.ResourceType.Reset()
if err := s.SubmissionID.Decode(d); err != nil { if err := s.ResourceType.Decode(d); err != nil {
return err return err
} }
return nil return nil
}(); err != nil { }(); err != nil {
return errors.Wrap(err, "decode field \"SubmissionID\"") return errors.Wrap(err, "decode field \"ResourceType\"")
}
case "ResourceID":
if err := func() error {
s.ResourceID.Reset()
if err := s.ResourceID.Decode(d); err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"ResourceID\"")
} }
default: default:
return d.Skip() return d.Skip()
@ -1451,13 +1502,9 @@ func (s *Submission) encodeFields(e *jx.Encoder) {
e.Bool(s.Completed) e.Bool(s.Completed)
} }
{ {
e.FieldStart("SubmissionType") if s.UploadedAssetID.Set {
e.Int32(s.SubmissionType) e.FieldStart("UploadedAssetID")
} s.UploadedAssetID.Encode(e)
{
if s.TargetAssetID.Set {
e.FieldStart("TargetAssetID")
s.TargetAssetID.Encode(e)
} }
} }
{ {
@ -1470,7 +1517,7 @@ func (s *Submission) encodeFields(e *jx.Encoder) {
} }
} }
var jsonFieldsNameOfSubmission = [14]string{ var jsonFieldsNameOfSubmission = [13]string{
0: "ID", 0: "ID",
1: "DisplayName", 1: "DisplayName",
2: "Creator", 2: "Creator",
@ -1481,10 +1528,9 @@ var jsonFieldsNameOfSubmission = [14]string{
7: "AssetID", 7: "AssetID",
8: "AssetVersion", 8: "AssetVersion",
9: "Completed", 9: "Completed",
10: "SubmissionType", 10: "UploadedAssetID",
11: "TargetAssetID", 11: "StatusID",
12: "StatusID", 12: "StatusMessage",
13: "StatusMessage",
} }
// Decode decodes Submission from json. // Decode decodes Submission from json.
@ -1616,30 +1662,18 @@ func (s *Submission) Decode(d *jx.Decoder) error {
}(); err != nil { }(); err != nil {
return errors.Wrap(err, "decode field \"Completed\"") return errors.Wrap(err, "decode field \"Completed\"")
} }
case "SubmissionType": case "UploadedAssetID":
requiredBitSet[1] |= 1 << 2
if err := func() error { if err := func() error {
v, err := d.Int32() s.UploadedAssetID.Reset()
s.SubmissionType = int32(v) if err := s.UploadedAssetID.Decode(d); err != nil {
if err != nil {
return err return err
} }
return nil return nil
}(); err != nil { }(); err != nil {
return errors.Wrap(err, "decode field \"SubmissionType\"") return errors.Wrap(err, "decode field \"UploadedAssetID\"")
}
case "TargetAssetID":
if err := func() error {
s.TargetAssetID.Reset()
if err := s.TargetAssetID.Decode(d); err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"TargetAssetID\"")
} }
case "StatusID": case "StatusID":
requiredBitSet[1] |= 1 << 4 requiredBitSet[1] |= 1 << 3
if err := func() error { if err := func() error {
v, err := d.Int32() v, err := d.Int32()
s.StatusID = int32(v) s.StatusID = int32(v)
@ -1651,7 +1685,7 @@ func (s *Submission) Decode(d *jx.Decoder) error {
return errors.Wrap(err, "decode field \"StatusID\"") return errors.Wrap(err, "decode field \"StatusID\"")
} }
case "StatusMessage": case "StatusMessage":
requiredBitSet[1] |= 1 << 5 requiredBitSet[1] |= 1 << 4
if err := func() error { if err := func() error {
v, err := d.Str() v, err := d.Str()
s.StatusMessage = string(v) s.StatusMessage = string(v)
@ -1673,7 +1707,7 @@ func (s *Submission) Decode(d *jx.Decoder) error {
var failures []validate.FieldError var failures []validate.FieldError
for i, mask := range [2]uint8{ for i, mask := range [2]uint8{
0b11111111, 0b11111111,
0b00110111, 0b00011011,
} { } {
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 { if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
// Mask only required fields and check equality to mask using XOR. // Mask only required fields and check equality to mask using XOR.
@ -1748,21 +1782,14 @@ func (s *SubmissionCreate) encodeFields(e *jx.Encoder) {
e.FieldStart("AssetVersion") e.FieldStart("AssetVersion")
e.Int64(s.AssetVersion) e.Int64(s.AssetVersion)
} }
{
if s.TargetAssetID.Set {
e.FieldStart("TargetAssetID")
s.TargetAssetID.Encode(e)
}
}
} }
var jsonFieldsNameOfSubmissionCreate = [6]string{ var jsonFieldsNameOfSubmissionCreate = [5]string{
0: "DisplayName", 0: "DisplayName",
1: "Creator", 1: "Creator",
2: "GameID", 2: "GameID",
3: "AssetID", 3: "AssetID",
4: "AssetVersion", 4: "AssetVersion",
5: "TargetAssetID",
} }
// Decode decodes SubmissionCreate from json. // Decode decodes SubmissionCreate from json.
@ -1834,16 +1861,6 @@ func (s *SubmissionCreate) Decode(d *jx.Decoder) error {
}(); err != nil { }(); err != nil {
return errors.Wrap(err, "decode field \"AssetVersion\"") return errors.Wrap(err, "decode field \"AssetVersion\"")
} }
case "TargetAssetID":
if err := func() error {
s.TargetAssetID.Reset()
if err := s.TargetAssetID.Decode(d); err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"TargetAssetID\"")
}
default: default:
return d.Skip() return d.Skip()
} }

@ -314,7 +314,8 @@ type Script struct {
Name string `json:"Name"` Name string `json:"Name"`
Hash string `json:"Hash"` Hash string `json:"Hash"`
Source string `json:"Source"` Source string `json:"Source"`
SubmissionID int64 `json:"SubmissionID"` ResourceType int32 `json:"ResourceType"`
ResourceID int64 `json:"ResourceID"`
} }
// GetID returns the value of ID. // GetID returns the value of ID.
@ -337,9 +338,14 @@ func (s *Script) GetSource() string {
return s.Source return s.Source
} }
// GetSubmissionID returns the value of SubmissionID. // GetResourceType returns the value of ResourceType.
func (s *Script) GetSubmissionID() int64 { func (s *Script) GetResourceType() int32 {
return s.SubmissionID return s.ResourceType
}
// GetResourceID returns the value of ResourceID.
func (s *Script) GetResourceID() int64 {
return s.ResourceID
} }
// SetID sets the value of ID. // SetID sets the value of ID.
@ -362,16 +368,22 @@ func (s *Script) SetSource(val string) {
s.Source = val s.Source = val
} }
// SetSubmissionID sets the value of SubmissionID. // SetResourceType sets the value of ResourceType.
func (s *Script) SetSubmissionID(val int64) { func (s *Script) SetResourceType(val int32) {
s.SubmissionID = val s.ResourceType = val
}
// SetResourceID sets the value of ResourceID.
func (s *Script) SetResourceID(val int64) {
s.ResourceID = val
} }
// Ref: #/components/schemas/ScriptCreate // Ref: #/components/schemas/ScriptCreate
type ScriptCreate struct { type ScriptCreate struct {
Name string `json:"Name"` Name string `json:"Name"`
Source string `json:"Source"` Source string `json:"Source"`
SubmissionID OptInt64 `json:"SubmissionID"` ResourceType int32 `json:"ResourceType"`
ResourceID OptInt64 `json:"ResourceID"`
} }
// GetName returns the value of Name. // GetName returns the value of Name.
@ -384,9 +396,14 @@ func (s *ScriptCreate) GetSource() string {
return s.Source return s.Source
} }
// GetSubmissionID returns the value of SubmissionID. // GetResourceType returns the value of ResourceType.
func (s *ScriptCreate) GetSubmissionID() OptInt64 { func (s *ScriptCreate) GetResourceType() int32 {
return s.SubmissionID return s.ResourceType
}
// GetResourceID returns the value of ResourceID.
func (s *ScriptCreate) GetResourceID() OptInt64 {
return s.ResourceID
} }
// SetName sets the value of Name. // SetName sets the value of Name.
@ -399,9 +416,14 @@ func (s *ScriptCreate) SetSource(val string) {
s.Source = val s.Source = val
} }
// SetSubmissionID sets the value of SubmissionID. // SetResourceType sets the value of ResourceType.
func (s *ScriptCreate) SetSubmissionID(val OptInt64) { func (s *ScriptCreate) SetResourceType(val int32) {
s.SubmissionID = val s.ResourceType = val
}
// SetResourceID sets the value of ResourceID.
func (s *ScriptCreate) SetResourceID(val OptInt64) {
s.ResourceID = val
} }
// Ref: #/components/schemas/ScriptPolicy // Ref: #/components/schemas/ScriptPolicy
@ -542,7 +564,8 @@ type ScriptUpdate struct {
ID int64 `json:"ID"` ID int64 `json:"ID"`
Name OptString `json:"Name"` Name OptString `json:"Name"`
Source OptString `json:"Source"` Source OptString `json:"Source"`
SubmissionID OptInt64 `json:"SubmissionID"` ResourceType OptInt32 `json:"ResourceType"`
ResourceID OptInt64 `json:"ResourceID"`
} }
// GetID returns the value of ID. // GetID returns the value of ID.
@ -560,9 +583,14 @@ func (s *ScriptUpdate) GetSource() OptString {
return s.Source return s.Source
} }
// GetSubmissionID returns the value of SubmissionID. // GetResourceType returns the value of ResourceType.
func (s *ScriptUpdate) GetSubmissionID() OptInt64 { func (s *ScriptUpdate) GetResourceType() OptInt32 {
return s.SubmissionID return s.ResourceType
}
// GetResourceID returns the value of ResourceID.
func (s *ScriptUpdate) GetResourceID() OptInt64 {
return s.ResourceID
} }
// SetID sets the value of ID. // SetID sets the value of ID.
@ -580,9 +608,14 @@ func (s *ScriptUpdate) SetSource(val OptString) {
s.Source = val s.Source = val
} }
// SetSubmissionID sets the value of SubmissionID. // SetResourceType sets the value of ResourceType.
func (s *ScriptUpdate) SetSubmissionID(val OptInt64) { func (s *ScriptUpdate) SetResourceType(val OptInt32) {
s.SubmissionID = val s.ResourceType = val
}
// SetResourceID sets the value of ResourceID.
func (s *ScriptUpdate) SetResourceID(val OptInt64) {
s.ResourceID = val
} }
// SetSubmissionCompletedNoContent is response for SetSubmissionCompleted operation. // SetSubmissionCompletedNoContent is response for SetSubmissionCompleted operation.
@ -590,20 +623,19 @@ type SetSubmissionCompletedNoContent struct{}
// Ref: #/components/schemas/Submission // Ref: #/components/schemas/Submission
type Submission struct { type Submission struct {
ID int64 `json:"ID"` ID int64 `json:"ID"`
DisplayName string `json:"DisplayName"` DisplayName string `json:"DisplayName"`
Creator string `json:"Creator"` Creator string `json:"Creator"`
GameID int32 `json:"GameID"` GameID int32 `json:"GameID"`
CreatedAt int64 `json:"CreatedAt"` CreatedAt int64 `json:"CreatedAt"`
UpdatedAt int64 `json:"UpdatedAt"` UpdatedAt int64 `json:"UpdatedAt"`
Submitter int64 `json:"Submitter"` Submitter int64 `json:"Submitter"`
AssetID int64 `json:"AssetID"` AssetID int64 `json:"AssetID"`
AssetVersion int64 `json:"AssetVersion"` AssetVersion int64 `json:"AssetVersion"`
Completed bool `json:"Completed"` Completed bool `json:"Completed"`
SubmissionType int32 `json:"SubmissionType"` UploadedAssetID OptInt64 `json:"UploadedAssetID"`
TargetAssetID OptInt64 `json:"TargetAssetID"` StatusID int32 `json:"StatusID"`
StatusID int32 `json:"StatusID"` StatusMessage string `json:"StatusMessage"`
StatusMessage string `json:"StatusMessage"`
} }
// GetID returns the value of ID. // GetID returns the value of ID.
@ -656,14 +688,9 @@ func (s *Submission) GetCompleted() bool {
return s.Completed return s.Completed
} }
// GetSubmissionType returns the value of SubmissionType. // GetUploadedAssetID returns the value of UploadedAssetID.
func (s *Submission) GetSubmissionType() int32 { func (s *Submission) GetUploadedAssetID() OptInt64 {
return s.SubmissionType return s.UploadedAssetID
}
// GetTargetAssetID returns the value of TargetAssetID.
func (s *Submission) GetTargetAssetID() OptInt64 {
return s.TargetAssetID
} }
// GetStatusID returns the value of StatusID. // GetStatusID returns the value of StatusID.
@ -726,14 +753,9 @@ func (s *Submission) SetCompleted(val bool) {
s.Completed = val s.Completed = val
} }
// SetSubmissionType sets the value of SubmissionType. // SetUploadedAssetID sets the value of UploadedAssetID.
func (s *Submission) SetSubmissionType(val int32) { func (s *Submission) SetUploadedAssetID(val OptInt64) {
s.SubmissionType = val s.UploadedAssetID = val
}
// SetTargetAssetID sets the value of TargetAssetID.
func (s *Submission) SetTargetAssetID(val OptInt64) {
s.TargetAssetID = val
} }
// SetStatusID sets the value of StatusID. // SetStatusID sets the value of StatusID.
@ -748,12 +770,11 @@ func (s *Submission) SetStatusMessage(val string) {
// Ref: #/components/schemas/SubmissionCreate // Ref: #/components/schemas/SubmissionCreate
type SubmissionCreate struct { type SubmissionCreate struct {
DisplayName string `json:"DisplayName"` DisplayName string `json:"DisplayName"`
Creator string `json:"Creator"` Creator string `json:"Creator"`
GameID int32 `json:"GameID"` GameID int32 `json:"GameID"`
AssetID int64 `json:"AssetID"` AssetID int64 `json:"AssetID"`
AssetVersion int64 `json:"AssetVersion"` AssetVersion int64 `json:"AssetVersion"`
TargetAssetID OptInt64 `json:"TargetAssetID"`
} }
// GetDisplayName returns the value of DisplayName. // GetDisplayName returns the value of DisplayName.
@ -781,11 +802,6 @@ func (s *SubmissionCreate) GetAssetVersion() int64 {
return s.AssetVersion return s.AssetVersion
} }
// GetTargetAssetID returns the value of TargetAssetID.
func (s *SubmissionCreate) GetTargetAssetID() OptInt64 {
return s.TargetAssetID
}
// SetDisplayName sets the value of DisplayName. // SetDisplayName sets the value of DisplayName.
func (s *SubmissionCreate) SetDisplayName(val string) { func (s *SubmissionCreate) SetDisplayName(val string) {
s.DisplayName = val s.DisplayName = val
@ -811,11 +827,6 @@ func (s *SubmissionCreate) SetAssetVersion(val int64) {
s.AssetVersion = val s.AssetVersion = val
} }
// SetTargetAssetID sets the value of TargetAssetID.
func (s *SubmissionCreate) SetTargetAssetID(val OptInt64) {
s.TargetAssetID = val
}
// UpdateScriptNoContent is response for UpdateScript operation. // UpdateScriptNoContent is response for UpdateScript operation.
type UpdateScriptNoContent struct{} type UpdateScriptNoContent struct{}

@ -9,7 +9,16 @@ 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")
ErrInvalidSubmissionListSort = errors.New("invalid submission list sort parameter [1,2,3,4]") ErrInvalidListSort = errors.New("invalid list sort parameter [1,2,3,4]")
)
type ListSort uint32
const (
ListSortDisabled ListSort = 0
ListSortDisplayNameAscending ListSort = 1
ListSortDisplayNameDescending ListSort = 2
ListSortDateAscending ListSort = 3
ListSortDateDescending ListSort = 4
) )
type Datastore interface { type Datastore interface {
@ -23,10 +32,10 @@ type Submissions interface {
GetList(ctx context.Context, id []int64) ([]model.Submission, error) GetList(ctx context.Context, id []int64) ([]model.Submission, error)
Create(ctx context.Context, smap model.Submission) (model.Submission, error) Create(ctx context.Context, smap model.Submission) (model.Submission, error)
Update(ctx context.Context, id int64, values OptionalMap) error Update(ctx context.Context, id int64, values OptionalMap) error
IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.Status, values OptionalMap) error IfStatusThenUpdate(ctx context.Context, id int64, statuses []model.SubmissionStatus, values OptionalMap) error
IfStatusThenUpdateAndGet(ctx context.Context, id int64, statuses []model.Status, values OptionalMap) (model.Submission, error) IfStatusThenUpdateAndGet(ctx context.Context, id int64, statuses []model.SubmissionStatus, values OptionalMap) (model.Submission, error)
Delete(ctx context.Context, id int64) error Delete(ctx context.Context, id int64) error
List(ctx context.Context, filters OptionalMap, page model.Page, sort model.SubmissionListSort) ([]model.Submission, error) List(ctx context.Context, filters OptionalMap, page model.Page, sort ListSort) ([]model.Submission, error)
} }
type Scripts interface { type Scripts interface {

@ -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.SubmissionStatus, 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

@ -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.SubmissionStatus, 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
@ -67,7 +67,7 @@ func (env *Submissions) IfStatusThenUpdate(ctx context.Context, id int64, status
// 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.
// returns the updated value // returns the updated value
func (env *Submissions) IfStatusThenUpdateAndGet(ctx context.Context, id int64, statuses []model.Status, values datastore.OptionalMap) (model.Submission, error) { func (env *Submissions) IfStatusThenUpdateAndGet(ctx context.Context, id int64, statuses []model.SubmissionStatus, values datastore.OptionalMap) (model.Submission, error) {
var submission model.Submission var submission model.Submission
result := env.db.Model(&submission). result := env.db.Model(&submission).
Clauses(clause.Returning{}). Clauses(clause.Returning{}).
@ -99,29 +99,29 @@ func (env *Submissions) Delete(ctx context.Context, id int64) error {
return nil return nil
} }
func (env *Submissions) List(ctx context.Context, filters datastore.OptionalMap, page model.Page, sort model.SubmissionListSort) ([]model.Submission, error) { func (env *Submissions) List(ctx context.Context, filters datastore.OptionalMap, page model.Page, sort datastore.ListSort) ([]model.Submission, error) {
var maps []model.Submission var maps []model.Submission
db := env.db db := env.db
switch sort { switch sort {
case model.SubmissionListSortDisabled: case datastore.ListSortDisabled:
// No sort // No sort
break break
case model.SubmissionListSortDisplayNameAscending: case datastore.ListSortDisplayNameAscending:
db=db.Order("display_name ASC") db=db.Order("display_name ASC")
break break
case model.SubmissionListSortDisplayNameDescending: case datastore.ListSortDisplayNameDescending:
db=db.Order("display_name DESC") db=db.Order("display_name DESC")
break break
case model.SubmissionListSortDateAscending: case datastore.ListSortDateAscending:
db=db.Order("created_at ASC") db=db.Order("created_at ASC")
break break
case model.SubmissionListSortDateDescending: case datastore.ListSortDateDescending:
db=db.Order("created_at DESC") db=db.Order("created_at DESC")
break break
default: default:
return nil, datastore.ErrInvalidSubmissionListSort return nil, datastore.ErrInvalidListSort
} }
if err := db.Where(filters.Map()).Offset(int((page.Number - 1) * page.Size)).Limit(int(page.Size)).Find(&maps).Error; err != nil { if err := db.Where(filters.Map()).Offset(int((page.Number - 1) * page.Size)).Limit(int(page.Size)).Find(&maps).Error; err != nil {

@ -312,18 +312,15 @@ func (c *Client) sendActionSubmissionUploaded(ctx context.Context, params Action
stage = "EncodeQueryParams" stage = "EncodeQueryParams"
q := uri.NewQueryEncoder() q := uri.NewQueryEncoder()
{ {
// Encode "TargetAssetID" parameter. // Encode "UploadedAssetID" parameter.
cfg := uri.QueryParameterEncodingConfig{ cfg := uri.QueryParameterEncodingConfig{
Name: "TargetAssetID", Name: "UploadedAssetID",
Style: uri.QueryStyleForm, Style: uri.QueryStyleForm,
Explode: true, Explode: true,
} }
if err := q.EncodeParam(cfg, func(e uri.Encoder) error { if err := q.EncodeParam(cfg, func(e uri.Encoder) error {
if val, ok := params.TargetAssetID.Get(); ok { return e.EncodeValue(conv.Int64ToString(params.UploadedAssetID))
return e.EncodeValue(conv.Int64ToString(val))
}
return nil
}); err != nil { }); err != nil {
return res, errors.Wrap(err, "encode query") return res, errors.Wrap(err, "encode query")
} }

@ -282,9 +282,9 @@ func (s *Server) handleActionSubmissionUploadedRequest(args [1]string, argsEscap
In: "path", In: "path",
}: params.SubmissionID, }: params.SubmissionID,
{ {
Name: "TargetAssetID", Name: "UploadedAssetID",
In: "query", In: "query",
}: params.TargetAssetID, }: params.UploadedAssetID,
}, },
Raw: r, Raw: r,
} }

@ -282,17 +282,22 @@ func (s *Script) encodeFields(e *jx.Encoder) {
e.Str(s.Source) e.Str(s.Source)
} }
{ {
e.FieldStart("SubmissionID") e.FieldStart("ResourceType")
e.Int64(s.SubmissionID) e.Int32(s.ResourceType)
}
{
e.FieldStart("ResourceID")
e.Int64(s.ResourceID)
} }
} }
var jsonFieldsNameOfScript = [5]string{ var jsonFieldsNameOfScript = [6]string{
0: "ID", 0: "ID",
1: "Name", 1: "Name",
2: "Hash", 2: "Hash",
3: "Source", 3: "Source",
4: "SubmissionID", 4: "ResourceType",
5: "ResourceID",
} }
// Decode decodes Script from json. // Decode decodes Script from json.
@ -352,17 +357,29 @@ func (s *Script) Decode(d *jx.Decoder) error {
}(); err != nil { }(); err != nil {
return errors.Wrap(err, "decode field \"Source\"") return errors.Wrap(err, "decode field \"Source\"")
} }
case "SubmissionID": case "ResourceType":
requiredBitSet[0] |= 1 << 4 requiredBitSet[0] |= 1 << 4
if err := func() error { if err := func() error {
v, err := d.Int64() v, err := d.Int32()
s.SubmissionID = int64(v) s.ResourceType = int32(v)
if err != nil { if err != nil {
return err return err
} }
return nil return nil
}(); err != nil { }(); err != nil {
return errors.Wrap(err, "decode field \"SubmissionID\"") return errors.Wrap(err, "decode field \"ResourceType\"")
}
case "ResourceID":
requiredBitSet[0] |= 1 << 5
if err := func() error {
v, err := d.Int64()
s.ResourceID = int64(v)
if err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"ResourceID\"")
} }
default: default:
return d.Skip() return d.Skip()
@ -374,7 +391,7 @@ func (s *Script) Decode(d *jx.Decoder) error {
// Validate required fields. // Validate required fields.
var failures []validate.FieldError var failures []validate.FieldError
for i, mask := range [1]uint8{ for i, mask := range [1]uint8{
0b00011111, 0b00111111,
} { } {
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 { if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
// Mask only required fields and check equality to mask using XOR. // Mask only required fields and check equality to mask using XOR.
@ -438,17 +455,22 @@ func (s *ScriptCreate) encodeFields(e *jx.Encoder) {
e.Str(s.Source) e.Str(s.Source)
} }
{ {
if s.SubmissionID.Set { e.FieldStart("ResourceType")
e.FieldStart("SubmissionID") e.Int32(s.ResourceType)
s.SubmissionID.Encode(e) }
{
if s.ResourceID.Set {
e.FieldStart("ResourceID")
s.ResourceID.Encode(e)
} }
} }
} }
var jsonFieldsNameOfScriptCreate = [3]string{ var jsonFieldsNameOfScriptCreate = [4]string{
0: "Name", 0: "Name",
1: "Source", 1: "Source",
2: "SubmissionID", 2: "ResourceType",
3: "ResourceID",
} }
// Decode decodes ScriptCreate from json. // Decode decodes ScriptCreate from json.
@ -484,15 +506,27 @@ func (s *ScriptCreate) Decode(d *jx.Decoder) error {
}(); err != nil { }(); err != nil {
return errors.Wrap(err, "decode field \"Source\"") return errors.Wrap(err, "decode field \"Source\"")
} }
case "SubmissionID": case "ResourceType":
requiredBitSet[0] |= 1 << 2
if err := func() error { if err := func() error {
s.SubmissionID.Reset() v, err := d.Int32()
if err := s.SubmissionID.Decode(d); err != nil { s.ResourceType = int32(v)
if err != nil {
return err return err
} }
return nil return nil
}(); err != nil { }(); err != nil {
return errors.Wrap(err, "decode field \"SubmissionID\"") return errors.Wrap(err, "decode field \"ResourceType\"")
}
case "ResourceID":
if err := func() error {
s.ResourceID.Reset()
if err := s.ResourceID.Decode(d); err != nil {
return err
}
return nil
}(); err != nil {
return errors.Wrap(err, "decode field \"ResourceID\"")
} }
default: default:
return d.Skip() return d.Skip()
@ -504,7 +538,7 @@ func (s *ScriptCreate) Decode(d *jx.Decoder) error {
// Validate required fields. // Validate required fields.
var failures []validate.FieldError var failures []validate.FieldError
for i, mask := range [1]uint8{ for i, mask := range [1]uint8{
0b00000011, 0b00000111,
} { } {
if result := (requiredBitSet[i] & mask) ^ mask; result != 0 { if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {
// Mask only required fields and check equality to mask using XOR. // Mask only required fields and check equality to mask using XOR.

@ -145,8 +145,8 @@ func decodeActionSubmissionAcceptedParams(args [1]string, argsEscaped bool, r *h
// ActionSubmissionUploadedParams is parameters of actionSubmissionUploaded operation. // ActionSubmissionUploadedParams is parameters of actionSubmissionUploaded operation.
type ActionSubmissionUploadedParams struct { type ActionSubmissionUploadedParams struct {
// The unique identifier for a submission. // The unique identifier for a submission.
SubmissionID int64 SubmissionID int64
TargetAssetID OptInt64 UploadedAssetID int64
} }
func unpackActionSubmissionUploadedParams(packed middleware.Parameters) (params ActionSubmissionUploadedParams) { func unpackActionSubmissionUploadedParams(packed middleware.Parameters) (params ActionSubmissionUploadedParams) {
@ -159,12 +159,10 @@ func unpackActionSubmissionUploadedParams(packed middleware.Parameters) (params
} }
{ {
key := middleware.ParameterKey{ key := middleware.ParameterKey{
Name: "TargetAssetID", Name: "UploadedAssetID",
In: "query", In: "query",
} }
if v, ok := packed[key]; ok { params.UploadedAssetID = packed[key].(int64)
params.TargetAssetID = v.(OptInt64)
}
} }
return params return params
} }
@ -216,43 +214,38 @@ func decodeActionSubmissionUploadedParams(args [1]string, argsEscaped bool, r *h
Err: err, Err: err,
} }
} }
// Decode query: TargetAssetID. // Decode query: UploadedAssetID.
if err := func() error { if err := func() error {
cfg := uri.QueryParameterDecodingConfig{ cfg := uri.QueryParameterDecodingConfig{
Name: "TargetAssetID", Name: "UploadedAssetID",
Style: uri.QueryStyleForm, Style: uri.QueryStyleForm,
Explode: true, Explode: true,
} }
if err := q.HasParam(cfg); err == nil { if err := q.HasParam(cfg); err == nil {
if err := q.DecodeParam(cfg, func(d uri.Decoder) error { if err := q.DecodeParam(cfg, func(d uri.Decoder) error {
var paramsDotTargetAssetIDVal int64 val, err := d.DecodeValue()
if err := func() error { if err != nil {
val, err := d.DecodeValue()
if err != nil {
return err
}
c, err := conv.ToInt64(val)
if err != nil {
return err
}
paramsDotTargetAssetIDVal = c
return nil
}(); err != nil {
return err return err
} }
params.TargetAssetID.SetTo(paramsDotTargetAssetIDVal)
c, err := conv.ToInt64(val)
if err != nil {
return err
}
params.UploadedAssetID = c
return nil return nil
}); err != nil { }); err != nil {
return err return err
} }
} else {
return err
} }
return nil return nil
}(); err != nil { }(); err != nil {
return params, &ogenerrors.DecodeParamError{ return params, &ogenerrors.DecodeParamError{
Name: "TargetAssetID", Name: "UploadedAssetID",
In: "query", In: "query",
Err: err, Err: err,
} }

@ -231,7 +231,8 @@ type Script struct {
Name string `json:"Name"` Name string `json:"Name"`
Hash string `json:"Hash"` Hash string `json:"Hash"`
Source string `json:"Source"` Source string `json:"Source"`
SubmissionID int64 `json:"SubmissionID"` ResourceType int32 `json:"ResourceType"`
ResourceID int64 `json:"ResourceID"`
} }
// GetID returns the value of ID. // GetID returns the value of ID.
@ -254,9 +255,14 @@ func (s *Script) GetSource() string {
return s.Source return s.Source
} }
// GetSubmissionID returns the value of SubmissionID. // GetResourceType returns the value of ResourceType.
func (s *Script) GetSubmissionID() int64 { func (s *Script) GetResourceType() int32 {
return s.SubmissionID return s.ResourceType
}
// GetResourceID returns the value of ResourceID.
func (s *Script) GetResourceID() int64 {
return s.ResourceID
} }
// SetID sets the value of ID. // SetID sets the value of ID.
@ -279,16 +285,22 @@ func (s *Script) SetSource(val string) {
s.Source = val s.Source = val
} }
// SetSubmissionID sets the value of SubmissionID. // SetResourceType sets the value of ResourceType.
func (s *Script) SetSubmissionID(val int64) { func (s *Script) SetResourceType(val int32) {
s.SubmissionID = val s.ResourceType = val
}
// SetResourceID sets the value of ResourceID.
func (s *Script) SetResourceID(val int64) {
s.ResourceID = val
} }
// Ref: #/components/schemas/ScriptCreate // Ref: #/components/schemas/ScriptCreate
type ScriptCreate struct { type ScriptCreate struct {
Name string `json:"Name"` Name string `json:"Name"`
Source string `json:"Source"` Source string `json:"Source"`
SubmissionID OptInt64 `json:"SubmissionID"` ResourceType int32 `json:"ResourceType"`
ResourceID OptInt64 `json:"ResourceID"`
} }
// GetName returns the value of Name. // GetName returns the value of Name.
@ -301,9 +313,14 @@ func (s *ScriptCreate) GetSource() string {
return s.Source return s.Source
} }
// GetSubmissionID returns the value of SubmissionID. // GetResourceType returns the value of ResourceType.
func (s *ScriptCreate) GetSubmissionID() OptInt64 { func (s *ScriptCreate) GetResourceType() int32 {
return s.SubmissionID return s.ResourceType
}
// GetResourceID returns the value of ResourceID.
func (s *ScriptCreate) GetResourceID() OptInt64 {
return s.ResourceID
} }
// SetName sets the value of Name. // SetName sets the value of Name.
@ -316,9 +333,14 @@ func (s *ScriptCreate) SetSource(val string) {
s.Source = val s.Source = val
} }
// SetSubmissionID sets the value of SubmissionID. // SetResourceType sets the value of ResourceType.
func (s *ScriptCreate) SetSubmissionID(val OptInt64) { func (s *ScriptCreate) SetResourceType(val int32) {
s.SubmissionID = val s.ResourceType = val
}
// SetResourceID sets the value of ResourceID.
func (s *ScriptCreate) SetResourceID(val OptInt64) {
s.ResourceID = val
} }
// Ref: #/components/schemas/ScriptPolicy // Ref: #/components/schemas/ScriptPolicy

@ -23,12 +23,20 @@ func HashParse(hash string) (uint64, error){
return strconv.ParseUint(hash, 16, 64) return strconv.ParseUint(hash, 16, 64)
} }
type ResourceType int32
const (
ResourceUnknown ResourceType = 0
ResourceMapfix ResourceType = 1
ResourceSubmission ResourceType = 2
)
type Script struct { type Script struct {
ID int64 `gorm:"primaryKey"` ID int64 `gorm:"primaryKey"`
Name string Name string
Hash int64 // postgres does not support unsigned integers, so we have to pretend Hash int64 // postgres does not support unsigned integers, so we have to pretend
Source string Source string
SubmissionID int64 // which submission did this script first appear in ResourceType ResourceType // is this a submission or is it a mapfix
ResourceID int64 // which submission / mapfix did this script first appear in
CreatedAt time.Time CreatedAt time.Time
UpdatedAt time.Time UpdatedAt time.Time
} }

@ -2,34 +2,24 @@ package model
import "time" import "time"
type Status int32 type SubmissionStatus int32
const ( const (
// Phase: Final Status // Phase: Final SubmissionStatus
StatusReleased Status = 9 SubmissionStatusReleased SubmissionStatus = 9
StatusRejected Status = 8 SubmissionStatusRejected SubmissionStatus = 8
// Phase: Testing // Phase: Testing
StatusUploaded Status = 7 // uploaded to the group, but pending release SubmissionStatusUploaded SubmissionStatus = 7 // uploaded to the group, but pending release
StatusUploading Status = 6 SubmissionStatusUploading SubmissionStatus = 6
StatusValidated Status = 5 SubmissionStatusValidated SubmissionStatus = 5
StatusValidating Status = 4 SubmissionStatusValidating SubmissionStatus = 4
StatusAccepted Status = 3 // pending script review, can re-trigger validation SubmissionStatusAccepted SubmissionStatus = 3 // pending script review, can re-trigger validation
// Phase: Creation // Phase: Creation
StatusChangesRequested Status = 2 SubmissionStatusChangesRequested SubmissionStatus = 2
StatusSubmitted Status = 1 SubmissionStatusSubmitted SubmissionStatus = 1
StatusUnderConstruction Status = 0 SubmissionStatusUnderConstruction SubmissionStatus = 0
)
type SubmissionListSort uint32
const (
SubmissionListSortDisabled SubmissionListSort = 0
SubmissionListSortDisplayNameAscending SubmissionListSort = 1
SubmissionListSortDisplayNameDescending SubmissionListSort = 2
SubmissionListSortDateAscending SubmissionListSort = 3
SubmissionListSortDateDescending SubmissionListSort = 4
) )
type Submission struct { type Submission struct {
@ -40,16 +30,12 @@ type Submission struct {
CreatedAt time.Time CreatedAt time.Time
UpdatedAt time.Time UpdatedAt time.Time
Submitter int64 // UserID Submitter int64 // UserID
// Who clicked the Accept button
AcceptedBy int64 // UserID
// Who clicked the Upload button
UploadedBy int64 // UserID
AssetID int64 AssetID int64
AssetVersion int64 AssetVersion int64
ValidatedAssetID int64 ValidatedAssetID int64
ValidatedAssetVersion int64 ValidatedAssetVersion int64
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 int64 // where to upload map fix. if the TargetAssetID is 0, it's a new map. UploadedAssetID int64 // where to upload map fix. if the TargetAssetID is 0, it's a new map.
StatusID Status StatusID SubmissionStatus
StatusMessage string StatusMessage string
} }

@ -32,7 +32,8 @@ func (svc *Service) CreateScript(ctx context.Context, req *api.ScriptCreate) (*a
Name: req.Name, Name: req.Name,
Hash: int64(model.HashSource(req.Source)), Hash: int64(model.HashSource(req.Source)),
Source: req.Source, Source: req.Source,
SubmissionID: req.SubmissionID.Or(0), ResourceType: model.ResourceType(req.ResourceType),
ResourceID: req.ResourceID.Or(0),
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@ -82,7 +83,8 @@ func (svc *Service) ListScripts(ctx context.Context, params api.ListScriptsParam
ID: item.ID, ID: item.ID,
Hash: model.HashFormat(uint64(item.Hash)), Hash: model.HashFormat(uint64(item.Hash)),
Source: item.Source, Source: item.Source,
SubmissionID: item.SubmissionID, ResourceType: int32(item.ResourceType),
ResourceID: item.ResourceID,
}) })
} }
@ -134,7 +136,8 @@ func (svc *Service) GetScript(ctx context.Context, params api.GetScriptParams) (
Name: script.Name, Name: script.Name,
Hash: model.HashFormat(uint64(script.Hash)), Hash: model.HashFormat(uint64(script.Hash)),
Source: script.Source, Source: script.Source,
SubmissionID: script.SubmissionID, ResourceType: int32(script.ResourceType),
ResourceID: script.ResourceID,
}, nil }, nil
} }
@ -165,8 +168,11 @@ func (svc *Service) UpdateScript(ctx context.Context, req *api.ScriptUpdate, par
pmap.Add("source", source) pmap.Add("source", source)
pmap.Add("hash", int64(model.HashSource(source))) // No type safety! pmap.Add("hash", int64(model.HashSource(source))) // No type safety!
} }
if SubmissionID, ok := req.SubmissionID.Get(); ok { if ResourceType, ok := req.ResourceType.Get(); ok {
pmap.Add("submission_id", SubmissionID) pmap.Add("resource_type", ResourceType)
}
if ResourceID, ok := req.ResourceID.Get(); ok {
pmap.Add("resource_id", ResourceID)
} }
return svc.DB.Scripts().Update(ctx, req.ID, pmap) return svc.DB.Scripts().Update(ctx, req.ID, pmap)
} }

@ -19,8 +19,8 @@ type Roles int32
var ( var (
RolesSubmissionRelease Roles = 1<<4 RolesSubmissionRelease Roles = 1<<4
RolesScriptWrite Roles = 1<<3 RolesScriptWrite Roles = 1<<3
RolesSubmissionUpload Roles = 1<<2 RolesMapUpload Roles = 1<<2
RolesSubmissionReview Roles = 1<<1 RolesMapReview Roles = 1<<1
RolesMapDownload Roles = 1<<0 RolesMapDownload Roles = 1<<0
RolesEmpty Roles = 0 RolesEmpty Roles = 0
) )
@ -32,13 +32,13 @@ var (
RoleQuat GroupRole = 255 RoleQuat GroupRole = 255
RoleItzaname GroupRole = 254 RoleItzaname GroupRole = 254
RoleStagingDeveloper GroupRole = 240 RoleStagingDeveloper GroupRole = 240
RolesAll Roles = RolesScriptWrite|RolesSubmissionRelease|RolesSubmissionUpload|RolesSubmissionReview|RolesMapDownload RolesAll Roles = RolesScriptWrite|RolesSubmissionRelease|RolesMapUpload|RolesMapReview|RolesMapDownload
// has SubmissionUpload // has SubmissionUpload
RoleMapAdmin GroupRole = 128 RoleMapAdmin GroupRole = 128
RolesMapAdmin Roles = RolesSubmissionRelease|RolesSubmissionUpload|RolesSubmissionReview|RolesMapDownload RolesMapAdmin Roles = RolesSubmissionRelease|RolesMapUpload|RolesMapReview|RolesMapDownload
// has SubmissionReview // has SubmissionReview
RoleMapCouncil GroupRole = 64 RoleMapCouncil GroupRole = 64
RolesMapCouncil Roles = RolesSubmissionReview|RolesSubmissionUpload|RolesMapDownload RolesMapCouncil Roles = RolesMapReview|RolesMapUpload|RolesMapDownload
// access to downloading maps // access to downloading maps
RoleMapAccess GroupRole = 32 RoleMapAccess GroupRole = 32
RolesMapAccess Roles = RolesMapDownload RolesMapAccess Roles = RolesMapDownload
@ -128,17 +128,23 @@ func (usr UserInfoHandle) GetRoles() (Roles, error) {
} }
// RoleThumbnail // RoleThumbnail
func (usr UserInfoHandle) HasRoleMapfixUpload() (bool, error) {
return usr.hasRoles(RolesMapUpload)
}
func (usr UserInfoHandle) HasRoleMapfixReview() (bool, error) {
return usr.hasRoles(RolesMapReview)
}
func (usr UserInfoHandle) HasRoleMapDownload() (bool, error) {
return usr.hasRoles(RolesMapDownload)
}
func (usr UserInfoHandle) HasRoleSubmissionRelease() (bool, error) { func (usr UserInfoHandle) HasRoleSubmissionRelease() (bool, error) {
return usr.hasRoles(RolesSubmissionRelease) return usr.hasRoles(RolesSubmissionRelease)
} }
func (usr UserInfoHandle) HasRoleSubmissionUpload() (bool, error) { func (usr UserInfoHandle) HasRoleSubmissionUpload() (bool, error) {
return usr.hasRoles(RolesSubmissionUpload) return usr.hasRoles(RolesMapUpload)
} }
func (usr UserInfoHandle) HasRoleSubmissionReview() (bool, error) { func (usr UserInfoHandle) HasRoleSubmissionReview() (bool, error) {
return usr.hasRoles(RolesSubmissionReview) return usr.hasRoles(RolesMapReview)
}
func (usr UserInfoHandle) HasRoleMapDownload() (bool, error) {
return usr.hasRoles(RolesMapDownload)
} }
func (usr UserInfoHandle) HasRoleScriptWrite() (bool, error) { func (usr UserInfoHandle) HasRoleScriptWrite() (bool, error) {
return usr.hasRoles(RolesScriptWrite) return usr.hasRoles(RolesScriptWrite)

@ -3,6 +3,7 @@ package service
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"git.itzana.me/strafesnet/go-grpc/maps" "git.itzana.me/strafesnet/go-grpc/maps"
"git.itzana.me/strafesnet/maps-service/pkg/api" "git.itzana.me/strafesnet/maps-service/pkg/api"
@ -15,6 +16,14 @@ var (
ErrPermissionDenied = errors.New("Permission denied") ErrPermissionDenied = errors.New("Permission denied")
// ErrUserInfo user info is missing for some reason // ErrUserInfo user info is missing for some reason
ErrUserInfo = errors.New("Missing user info") ErrUserInfo = errors.New("Missing user info")
ErrDelayReset = errors.New("Please give the validator at least 10 seconds to operate before attempting to reset the status")
ErrPermissionDeniedNotSubmitter = fmt.Errorf("%w: You must be the submitter to perform this action", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleSubmissionRelease = fmt.Errorf("%w: Need Role SubmissionRelease", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMapUpload = fmt.Errorf("%w: Need Role MapUpload", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMapReview = fmt.Errorf("%w: Need Role MapReview", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMapDownload = fmt.Errorf("%w: Need Role MapDownload", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleScriptWrite = fmt.Errorf("%w: Need Role ScriptWrite", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMaptest = fmt.Errorf("%w: Need Role Maptest", ErrPermissionDenied)
) )
type Service struct { type Service struct {

@ -15,45 +15,37 @@ import (
var( var(
CreationPhaseSubmissionsLimit = 20 CreationPhaseSubmissionsLimit = 20
CreationPhaseSubmissionStatuses = []model.Status{ CreationPhaseSubmissionStatuses = []model.SubmissionStatus{
model.StatusChangesRequested, model.SubmissionStatusChangesRequested,
model.StatusSubmitted, model.SubmissionStatusSubmitted,
model.StatusUnderConstruction, model.SubmissionStatusUnderConstruction,
} }
// prevent two mapfixes with same asset id // prevent two mapfixes with same asset id
ActiveSubmissionStatuses = []model.Status{ ActiveSubmissionStatuses = []model.SubmissionStatus{
model.StatusUploading, model.SubmissionStatusUploading,
model.StatusValidated, model.SubmissionStatusValidated,
model.StatusValidating, model.SubmissionStatusValidating,
model.StatusAccepted, model.SubmissionStatusAccepted,
model.StatusChangesRequested, model.SubmissionStatusChangesRequested,
model.StatusSubmitted, model.SubmissionStatusSubmitted,
model.StatusUnderConstruction, model.SubmissionStatusUnderConstruction,
} }
// limit mapfixes in the pipeline to one per target map // limit mapfixes in the pipeline to one per target map
ActiveAcceptedSubmissionStatuses = []model.Status{ ActiveAcceptedSubmissionStatuses = []model.SubmissionStatus{
model.StatusUploading, model.SubmissionStatusUploading,
model.StatusValidated, model.SubmissionStatusValidated,
model.StatusValidating, model.SubmissionStatusValidating,
model.StatusAccepted, model.SubmissionStatusAccepted,
} }
) )
var ( var (
ErrCreationPhaseSubmissionsLimit = errors.New("Active submissions limited to 20") ErrCreationPhaseSubmissionsLimit = errors.New("Active submissions limited to 20")
ErrActiveSubmissionSameAssetID = errors.New("There is an active submission with the same AssetID") ErrActiveSubmissionSameAssetID = errors.New("There is an active submission with the same AssetID")
ErrActiveSubmissionSameTargetAssetID = errors.New("There is an active submission with the same TargetAssetID") ErrUploadedAssetIDAlreadyExists = errors.New("The submission UploadedAssetID is already set")
ErrReleaseInvalidStatus = errors.New("Only submissions with Uploaded status can be released") ErrReleaseInvalidStatus = errors.New("Only submissions with Uploaded status can be released")
ErrReleaseNoTargetAssetID = errors.New("Only submissions with a TargetAssetID can be released") ErrReleaseNoUploadedAssetID = errors.New("Only submissions with a UploadedAssetID can be released")
ErrAcceptOwnSubmission = fmt.Errorf("%w: You cannot accept your own submission as the submitter", ErrPermissionDenied) ErrAcceptOwnSubmission = fmt.Errorf("%w: You cannot accept your own submission as the submitter", ErrPermissionDenied)
ErrDelayReset = errors.New("Please give the validator at least 10 seconds to operate before attempting to reset the status")
ErrPermissionDeniedNotSubmitter = fmt.Errorf("%w: You must be the submitter to perform this action", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleSubmissionRelease = fmt.Errorf("%w: Need Role SubmissionRelease", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleSubmissionUpload = fmt.Errorf("%w: Need Role SubmissionUpload", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleSubmissionReview = fmt.Errorf("%w: Need Role SubmissionReview", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMapDownload = fmt.Errorf("%w: Need Role MapDownload", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleScriptWrite = fmt.Errorf("%w: Need Role ScriptWrite", ErrPermissionDenied)
ErrPermissionDeniedNeedRoleMaptest = fmt.Errorf("%w: Need Role Maptest", ErrPermissionDenied)
) )
// POST /submissions // POST /submissions
@ -76,7 +68,7 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *api.Submissio
creation_submissions, err := svc.DB.Submissions().List(ctx, filter, model.Page{ creation_submissions, err := svc.DB.Submissions().List(ctx, filter, model.Page{
Number: 1, Number: 1,
Size: int32(CreationPhaseSubmissionsLimit), Size: int32(CreationPhaseSubmissionsLimit),
},model.SubmissionListSortDisabled) },datastore.ListSortDisabled)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -95,7 +87,7 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *api.Submissio
active_submissions, err := svc.DB.Submissions().List(ctx, filter, model.Page{ active_submissions, err := svc.DB.Submissions().List(ctx, filter, model.Page{
Number: 1, Number: 1,
Size: 1, Size: 1,
},model.SubmissionListSortDisabled) },datastore.ListSortDisabled)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -113,8 +105,7 @@ func (svc *Service) CreateSubmission(ctx context.Context, request *api.Submissio
AssetID: request.AssetID, AssetID: request.AssetID,
AssetVersion: request.AssetVersion, AssetVersion: request.AssetVersion,
Completed: false, Completed: false,
TargetAssetID: request.TargetAssetID.Value, StatusID: model.SubmissionStatusUnderConstruction,
StatusID: model.StatusUnderConstruction,
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@ -145,7 +136,7 @@ func (svc *Service) GetSubmission(ctx context.Context, params api.GetSubmissionP
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)), UploadedAssetID: api.NewOptInt64(int64(submission.UploadedAssetID)),
StatusID: int32(submission.StatusID), StatusID: int32(submission.StatusID),
StatusMessage: submission.StatusMessage, StatusMessage: submission.StatusMessage,
}, nil }, nil
@ -169,7 +160,7 @@ func (svc *Service) ListSubmissions(ctx context.Context, params api.ListSubmissi
filter.Add("game_id", params.GameID.Value) filter.Add("game_id", params.GameID.Value)
} }
sort := model.SubmissionListSort(params.Sort.Or(int32(model.SubmissionListSortDisabled))) sort := datastore.ListSort(params.Sort.Or(int32(datastore.ListSortDisabled)))
items, err := svc.DB.Submissions().List(ctx, filter, model.Page{ items, err := svc.DB.Submissions().List(ctx, filter, model.Page{
Number: params.Page, Number: params.Page,
@ -192,7 +183,7 @@ func (svc *Service) ListSubmissions(ctx context.Context, params api.ListSubmissi
AssetID: int64(item.AssetID), AssetID: int64(item.AssetID),
AssetVersion: int64(item.AssetVersion), AssetVersion: int64(item.AssetVersion),
Completed: item.Completed, Completed: item.Completed,
TargetAssetID: api.NewOptInt64(int64(item.TargetAssetID)), UploadedAssetID: api.NewOptInt64(int64(item.UploadedAssetID)),
StatusID: int32(item.StatusID), StatusID: int32(item.StatusID),
}) })
} }
@ -257,7 +248,7 @@ func (svc *Service) UpdateSubmissionModel(ctx context.Context, params api.Update
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.SubmissionStatus{model.SubmissionStatusChangesRequested, model.SubmissionStatusSubmitted, model.SubmissionStatusUnderConstruction}, pmap)
} }
// ActionSubmissionReject invokes actionSubmissionReject operation. // ActionSubmissionReject invokes actionSubmissionReject operation.
@ -277,13 +268,13 @@ func (svc *Service) ActionSubmissionReject(ctx context.Context, params api.Actio
} }
// check if caller has required role // check if caller has required role
if !has_role { if !has_role {
return ErrPermissionDeniedNeedRoleSubmissionReview return ErrPermissionDeniedNeedRoleMapReview
} }
// transaction // transaction
smap := datastore.Optional() smap := datastore.Optional()
smap.Add("status_id", model.StatusRejected) smap.Add("status_id", model.SubmissionStatusRejected)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted}, smap) return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitted}, smap)
} }
// ActionSubmissionRequestChanges invokes actionSubmissionRequestChanges operation. // ActionSubmissionRequestChanges invokes actionSubmissionRequestChanges operation.
@ -303,13 +294,13 @@ func (svc *Service) ActionSubmissionRequestChanges(ctx context.Context, params a
} }
// check if caller has required role // check if caller has required role
if !has_role { if !has_role {
return ErrPermissionDeniedNeedRoleSubmissionReview return ErrPermissionDeniedNeedRoleMapReview
} }
// transaction // transaction
smap := datastore.Optional() smap := datastore.Optional()
smap.Add("status_id", model.StatusChangesRequested) smap.Add("status_id", model.SubmissionStatusChangesRequested)
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.SubmissionStatus{model.SubmissionStatusValidated, model.SubmissionStatusAccepted, model.SubmissionStatusSubmitted}, smap)
} }
// ActionSubmissionRevoke invokes actionSubmissionRevoke operation. // ActionSubmissionRevoke invokes actionSubmissionRevoke operation.
@ -340,8 +331,8 @@ func (svc *Service) ActionSubmissionRevoke(ctx context.Context, params api.Actio
// transaction // transaction
smap := datastore.Optional() smap := datastore.Optional()
smap.Add("status_id", model.StatusUnderConstruction) smap.Add("status_id", model.SubmissionStatusUnderConstruction)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted, model.StatusChangesRequested}, smap) return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitted, model.SubmissionStatusChangesRequested}, smap)
} }
// ActionSubmissionSubmit invokes actionSubmissionSubmit operation. // ActionSubmissionSubmit invokes actionSubmissionSubmit operation.
@ -372,8 +363,8 @@ func (svc *Service) ActionSubmissionSubmit(ctx context.Context, params api.Actio
// transaction // transaction
smap := datastore.Optional() smap := datastore.Optional()
smap.Add("status_id", model.StatusSubmitted) smap.Add("status_id", model.SubmissionStatusSubmitted)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusUnderConstruction, model.StatusChangesRequested}, smap) return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusUnderConstruction, model.SubmissionStatusChangesRequested}, smap)
} }
// ActionSubmissionTriggerUpload invokes actionSubmissionTriggerUpload operation. // ActionSubmissionTriggerUpload invokes actionSubmissionTriggerUpload operation.
@ -393,26 +384,19 @@ func (svc *Service) ActionSubmissionTriggerUpload(ctx context.Context, params ap
} }
// check if caller has required role // check if caller has required role
if !has_role { if !has_role {
return ErrPermissionDeniedNeedRoleSubmissionUpload return ErrPermissionDeniedNeedRoleMapUpload
}
// track who is performing the upload action
userId, err := userInfo.GetUserID()
if err != nil {
return err
} }
// transaction // transaction
smap := datastore.Optional() smap := datastore.Optional()
smap.Add("status_id", model.StatusUploading) smap.Add("status_id", model.SubmissionStatusUploading)
smap.Add("uploaded_by", userId) submission, err := svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidated}, 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.UploadedAssetID == 0 {
// this is a new map // this is a new map
upload_new_request := model.UploadNewRequest{ upload_new_request := model.UploadNewRequest{
SubmissionID: submission.ID, SubmissionID: submission.ID,
@ -427,22 +411,10 @@ func (svc *Service) ActionSubmissionTriggerUpload(ctx context.Context, params ap
return err return err
} }
svc.Nats.Publish("maptest.submissions.uploadnew", []byte(j)) svc.Nats.Publish("maptest.submissions.upload", []byte(j))
} else { } else {
// this is a map fix // refuse to operate
upload_fix_request := model.UploadFixRequest{ return ErrUploadedAssetIDAlreadyExists
SubmissionID: submission.ID,
ModelID: submission.ValidatedAssetID,
ModelVersion: submission.ValidatedAssetVersion,
TargetAssetID: submission.TargetAssetID,
}
j, err := json.Marshal(upload_fix_request)
if err != nil {
return err
}
svc.Nats.Publish("maptest.submissions.uploadfix", []byte(j))
} }
return nil return nil
@ -465,7 +437,7 @@ func (svc *Service) ActionSubmissionValidated(ctx context.Context, params api.Ac
} }
// check if caller has required role // check if caller has required role
if !has_role { if !has_role {
return ErrPermissionDeniedNeedRoleSubmissionUpload return ErrPermissionDeniedNeedRoleMapUpload
} }
// check when submission was updated // check when submission was updated
@ -480,8 +452,8 @@ func (svc *Service) ActionSubmissionValidated(ctx context.Context, params api.Ac
// transaction // transaction
smap := datastore.Optional() smap := datastore.Optional()
smap.Add("status_id", model.StatusValidated) smap.Add("status_id", model.SubmissionStatusValidated)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusUploading}, smap) return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusUploading}, smap)
} }
// ActionSubmissionTriggerValidate invokes actionSubmissionTriggerValidate operation. // ActionSubmissionTriggerValidate invokes actionSubmissionTriggerValidate operation.
@ -501,7 +473,7 @@ func (svc *Service) ActionSubmissionTriggerValidate(ctx context.Context, params
} }
// check if caller has required role // check if caller has required role
if !has_role { if !has_role {
return ErrPermissionDeniedNeedRoleSubmissionReview return ErrPermissionDeniedNeedRoleMapReview
} }
// read submission (this could be done with a transaction WHERE clause) // read submission (this could be done with a transaction WHERE clause)
@ -510,38 +482,19 @@ func (svc *Service) ActionSubmissionTriggerValidate(ctx context.Context, params
return err return err
} }
// track who is performing the accept action has_role, err = userInfo.IsSubmitter(uint64(submission.Submitter))
userId, err := userInfo.GetUserID()
if err != nil { if err != nil {
return err return err
} }
// check if caller is NOT the submitter // check if caller is NOT the submitter
if userId == uint64(submission.Submitter) { if has_role {
return ErrAcceptOwnSubmission return ErrAcceptOwnSubmission
} }
// Check if an active submission with the same target asset id exists
if submission.TargetAssetID != 0 {
filter := datastore.Optional()
filter.Add("target_asset_id", submission.TargetAssetID)
filter.Add("status_id", ActiveAcceptedSubmissionStatuses)
active_submissions, err := svc.DB.Submissions().List(ctx, filter, model.Page{
Number: 1,
Size: 1,
},model.SubmissionListSortDisabled)
if err != nil {
return err
}
if len(active_submissions) != 0{
return ErrActiveSubmissionSameTargetAssetID
}
}
// transaction // transaction
smap := datastore.Optional() smap := datastore.Optional()
smap.Add("status_id", model.StatusValidating) smap.Add("status_id", model.SubmissionStatusValidating)
smap.Add("accepted_by", userId) submission, err = svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitted}, smap)
submission, err = svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.Status{model.StatusSubmitted}, smap)
if err != nil { if err != nil {
return err return err
} }
@ -585,13 +538,13 @@ func (svc *Service) ActionSubmissionRetryValidate(ctx context.Context, params ap
} }
// check if caller has required role // check if caller has required role
if !has_role { if !has_role {
return ErrPermissionDeniedNeedRoleSubmissionReview return ErrPermissionDeniedNeedRoleMapReview
} }
// transaction // transaction
smap := datastore.Optional() smap := datastore.Optional()
smap.Add("status_id", model.StatusValidating) smap.Add("status_id", model.SubmissionStatusValidating)
submission, err := svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.Status{model.StatusAccepted}, smap) submission, err := svc.DB.Submissions().IfStatusThenUpdateAndGet(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusAccepted}, smap)
if err != nil { if err != nil {
return err return err
} }
@ -635,7 +588,7 @@ func (svc *Service) ActionSubmissionAccepted(ctx context.Context, params api.Act
} }
// check if caller has required role // check if caller has required role
if !has_role { if !has_role {
return ErrPermissionDeniedNeedRoleSubmissionReview return ErrPermissionDeniedNeedRoleMapReview
} }
// check when submission was updated // check when submission was updated
@ -650,9 +603,9 @@ func (svc *Service) ActionSubmissionAccepted(ctx context.Context, params api.Act
// transaction // transaction
smap := datastore.Optional() smap := datastore.Optional()
smap.Add("status_id", model.StatusAccepted) smap.Add("status_id", model.SubmissionStatusAccepted)
smap.Add("status_message", "Manually forced reset") smap.Add("status_message", "Manually forced reset")
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusValidating}, smap) return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, smap)
} }
// ReleaseSubmissions invokes releaseSubmissions operation. // ReleaseSubmissions invokes releaseSubmissions operation.
@ -688,11 +641,11 @@ func (svc *Service) ReleaseSubmissions(ctx context.Context, request []api.Releas
// check each submission to make sure it is ready to release // check each submission to make sure it is ready to release
for _,submission := range submissions{ for _,submission := range submissions{
if submission.StatusID != model.StatusUploaded{ if submission.StatusID != model.SubmissionStatusUploaded{
return ErrReleaseInvalidStatus return ErrReleaseInvalidStatus
} }
if submission.TargetAssetID == 0{ if submission.UploadedAssetID == 0{
return ErrReleaseNoTargetAssetID return ErrReleaseNoUploadedAssetID
} }
} }
@ -700,7 +653,7 @@ func (svc *Service) ReleaseSubmissions(ctx context.Context, request []api.Releas
date := request[i].Date.Unix() date := request[i].Date.Unix()
// create each map with go-grpc // create each map with go-grpc
_, err := svc.Client.Create(ctx, &maps.MapRequest{ _, err := svc.Client.Create(ctx, &maps.MapRequest{
ID: submission.TargetAssetID, ID: submission.UploadedAssetID,
DisplayName: &submission.DisplayName, DisplayName: &submission.DisplayName,
Creator: &submission.Creator, Creator: &submission.Creator,
GameID: &submission.GameID, GameID: &submission.GameID,
@ -712,8 +665,8 @@ func (svc *Service) ReleaseSubmissions(ctx context.Context, request []api.Releas
// update each status to Released // update each status to Released
smap := datastore.Optional() smap := datastore.Optional()
smap.Add("status_id", model.StatusReleased) smap.Add("status_id", model.SubmissionStatusReleased)
err = svc.DB.Submissions().IfStatusThenUpdate(ctx, submission.ID, []model.Status{model.StatusUploaded}, smap) err = svc.DB.Submissions().IfStatusThenUpdate(ctx, submission.ID, []model.SubmissionStatus{model.SubmissionStatusUploaded}, smap)
if err != nil { if err != nil {
return err return err
} }

@ -19,7 +19,8 @@ func (svc *Service) CreateScript(ctx context.Context, req *api.ScriptCreate) (*a
Name: req.Name, Name: req.Name,
Hash: int64(model.HashSource(req.Source)), Hash: int64(model.HashSource(req.Source)),
Source: req.Source, Source: req.Source,
SubmissionID: req.SubmissionID.Or(0), ResourceType: model.ResourceType(req.ResourceType),
ResourceID: req.ResourceID.Or(0),
}) })
if err != nil { if err != nil {
return nil, err return nil, err
@ -69,7 +70,8 @@ func (svc *Service) ListScripts(ctx context.Context, params api.ListScriptsParam
ID: item.ID, ID: item.ID,
Hash: model.HashFormat(uint64(item.Hash)), Hash: model.HashFormat(uint64(item.Hash)),
Source: item.Source, Source: item.Source,
SubmissionID: item.SubmissionID, ResourceType: int32(item.ResourceType),
ResourceID: item.ResourceID,
}) })
} }
@ -92,6 +94,7 @@ func (svc *Service) GetScript(ctx context.Context, params api.GetScriptParams) (
Name: script.Name, Name: script.Name,
Hash: model.HashFormat(uint64(script.Hash)), Hash: model.HashFormat(uint64(script.Hash)),
Source: script.Source, Source: script.Source,
SubmissionID: script.SubmissionID, ResourceType: int32(script.ResourceType),
ResourceID: script.ResourceID,
}, nil }, nil
} }

@ -20,7 +20,7 @@ func (svc *Service) UpdateSubmissionValidatedModel(ctx context.Context, params i
pmap.AddNotNil("validated_asset_version", params.ValidatedModelVersion) pmap.AddNotNil("validated_asset_version", params.ValidatedModelVersion)
// DO NOT reset completed when validated model is updated // DO NOT reset completed when validated model is updated
// pmap.Add("completed", false) // pmap.Add("completed", false)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusValidating}, pmap) return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, pmap)
} }
// ActionSubmissionValidate invokes actionSubmissionValidate operation. // ActionSubmissionValidate invokes actionSubmissionValidate operation.
@ -29,12 +29,10 @@ func (svc *Service) UpdateSubmissionValidatedModel(ctx context.Context, params i
// //
// POST /submissions/{SubmissionID}/status/validator-validated // POST /submissions/{SubmissionID}/status/validator-validated
func (svc *Service) ActionSubmissionValidated(ctx context.Context, params internal.ActionSubmissionValidatedParams) error { func (svc *Service) ActionSubmissionValidated(ctx context.Context, params internal.ActionSubmissionValidatedParams) error {
println("[ActionSubmissionValidated] Implicit Validator permission granted!")
// transaction // transaction
smap := datastore.Optional() smap := datastore.Optional()
smap.Add("status_id", model.StatusValidated) smap.Add("status_id", model.SubmissionStatusValidated)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusValidating}, smap) return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, smap)
} }
// ActionSubmissionAccepted implements actionSubmissionAccepted operation. // ActionSubmissionAccepted implements actionSubmissionAccepted operation.
@ -43,13 +41,11 @@ func (svc *Service) ActionSubmissionValidated(ctx context.Context, params intern
// //
// POST /submissions/{SubmissionID}/status/validator-failed // POST /submissions/{SubmissionID}/status/validator-failed
func (svc *Service) ActionSubmissionAccepted(ctx context.Context, params internal.ActionSubmissionAcceptedParams) error { func (svc *Service) ActionSubmissionAccepted(ctx context.Context, params internal.ActionSubmissionAcceptedParams) error {
println("[ActionSubmissionAccepted] Implicit Validator permission granted!")
// transaction // transaction
smap := datastore.Optional() smap := datastore.Optional()
smap.Add("status_id", model.StatusAccepted) smap.Add("status_id", model.SubmissionStatusAccepted)
smap.Add("status_message", params.StatusMessage) smap.Add("status_message", params.StatusMessage)
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusValidating}, smap) return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, smap)
} }
// ActionSubmissionUploaded implements actionSubmissionUploaded operation. // ActionSubmissionUploaded implements actionSubmissionUploaded operation.
@ -58,13 +54,9 @@ func (svc *Service) ActionSubmissionAccepted(ctx context.Context, params interna
// //
// POST /submissions/{SubmissionID}/status/validator-uploaded // POST /submissions/{SubmissionID}/status/validator-uploaded
func (svc *Service) ActionSubmissionUploaded(ctx context.Context, params internal.ActionSubmissionUploadedParams) error { func (svc *Service) ActionSubmissionUploaded(ctx context.Context, params internal.ActionSubmissionUploadedParams) error {
println("[ActionSubmissionUploaded] Implicit Validator permission granted!")
// transaction // transaction
smap := datastore.Optional() smap := datastore.Optional()
smap.Add("status_id", model.StatusUploaded) smap.Add("status_id", model.SubmissionStatusUploaded)
if params.TargetAssetID.IsSet() { smap.Add("uploaded_asset_id", params.UploadedAssetID)
smap.AddNotNil("target_asset_id", params.TargetAssetID.Value) return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusUploading}, smap)
}
return svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.Status{model.StatusUploading}, smap)
} }

@ -14,7 +14,7 @@ impl Context{
response_ok( response_ok(
self.0.get(url).await.map_err(Error::Reqwest)? self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)? ).await.map_err(Error::Response)?
.json().await.map_err(Error::Reqwest) .json().await.map_err(Error::ReqwestJson)
} }
pub async fn get_scripts<'a>(&self,config:GetScriptsRequest<'a>)->Result<Vec<ScriptResponse>,Error>{ pub async fn get_scripts<'a>(&self,config:GetScriptsRequest<'a>)->Result<Vec<ScriptResponse>,Error>{
let url_raw=format!("{}/scripts",self.0.base_url); let url_raw=format!("{}/scripts",self.0.base_url);
@ -33,15 +33,18 @@ impl Context{
if let Some(source)=config.Source{ if let Some(source)=config.Source{
query_pairs.append_pair("Source",source); query_pairs.append_pair("Source",source);
} }
if let Some(submission_id)=config.SubmissionID{ if let Some(resource_type)=config.ResourceType{
query_pairs.append_pair("SubmissionID",submission_id.to_string().as_str()); query_pairs.append_pair("ResourceType",(resource_type as i32).to_string().as_str());
}
if let Some(resource_id)=config.ResourceID{
query_pairs.append_pair("ResourceID",resource_id.to_string().as_str());
} }
} }
response_ok( response_ok(
self.0.get(url).await.map_err(Error::Reqwest)? self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)? ).await.map_err(Error::Response)?
.json().await.map_err(Error::Reqwest) .json().await.map_err(Error::ReqwestJson)
} }
pub async fn get_script_from_hash<'a>(&self,config:HashRequest<'a>)->Result<Option<ScriptResponse>,SingleItemError>{ pub async fn get_script_from_hash<'a>(&self,config:HashRequest<'a>)->Result<Option<ScriptResponse>,SingleItemError>{
let scripts=self.get_scripts(GetScriptsRequest{ let scripts=self.get_scripts(GetScriptsRequest{
@ -50,7 +53,8 @@ impl Context{
Hash:Some(config.hash), Hash:Some(config.hash),
Name:None, Name:None,
Source:None, Source:None,
SubmissionID:None, ResourceType:None,
ResourceID:None,
}).await.map_err(SingleItemError::Other)?; }).await.map_err(SingleItemError::Other)?;
if 1<scripts.len(){ if 1<scripts.len(){
return Err(SingleItemError::DuplicateItems); return Err(SingleItemError::DuplicateItems);
@ -66,7 +70,7 @@ impl Context{
response_ok( response_ok(
self.0.post(url,body).await.map_err(Error::Reqwest)? self.0.post(url,body).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)? ).await.map_err(Error::Response)?
.json().await.map_err(Error::Reqwest) .json().await.map_err(Error::ReqwestJson)
} }
pub async fn get_script_policies<'a>(&self,config:GetScriptPoliciesRequest<'a>)->Result<Vec<ScriptPolicyResponse>,Error>{ pub async fn get_script_policies<'a>(&self,config:GetScriptPoliciesRequest<'a>)->Result<Vec<ScriptPolicyResponse>,Error>{
let url_raw=format!("{}/script-policy",self.0.base_url); let url_raw=format!("{}/script-policy",self.0.base_url);
@ -90,7 +94,7 @@ impl Context{
response_ok( response_ok(
self.0.get(url).await.map_err(Error::Reqwest)? self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)? ).await.map_err(Error::Response)?
.json().await.map_err(Error::Reqwest) .json().await.map_err(Error::ReqwestJson)
} }
pub async fn get_script_policy_from_hash<'a>(&self,config:HashRequest<'a>)->Result<Option<ScriptPolicyResponse>,SingleItemError>{ pub async fn get_script_policy_from_hash<'a>(&self,config:HashRequest<'a>)->Result<Option<ScriptPolicyResponse>,SingleItemError>{
let policies=self.get_script_policies(GetScriptPoliciesRequest{ let policies=self.get_script_policies(GetScriptPoliciesRequest{
@ -114,7 +118,7 @@ impl Context{
response_ok( response_ok(
self.0.post(url,body).await.map_err(Error::Reqwest)? self.0.post(url,body).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)? ).await.map_err(Error::Response)?
.json().await.map_err(Error::Reqwest) .json().await.map_err(Error::ReqwestJson)
} }
pub async fn update_script_policy(&self,config:UpdateScriptPolicyRequest)->Result<(),Error>{ pub async fn update_script_policy(&self,config:UpdateScriptPolicyRequest)->Result<(),Error>{
let url_raw=format!("{}/script-policy/{}",self.0.base_url,config.ID.0); let url_raw=format!("{}/script-policy/{}",self.0.base_url,config.ID.0);

@ -29,7 +29,7 @@ impl Context{
response_ok( response_ok(
self.0.get(url).await.map_err(Error::Reqwest)? self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)? ).await.map_err(Error::Response)?
.json().await.map_err(Error::Reqwest) .json().await.map_err(Error::ReqwestJson)
} }
pub async fn get_scripts<'a>(&self,config:GetScriptsRequest<'a>)->Result<Vec<ScriptResponse>,Error>{ pub async fn get_scripts<'a>(&self,config:GetScriptsRequest<'a>)->Result<Vec<ScriptResponse>,Error>{
let url_raw=format!("{}/scripts",self.0.base_url); let url_raw=format!("{}/scripts",self.0.base_url);
@ -48,15 +48,18 @@ impl Context{
if let Some(source)=config.Source{ if let Some(source)=config.Source{
query_pairs.append_pair("Source",source); query_pairs.append_pair("Source",source);
} }
if let Some(submission_id)=config.SubmissionID{ if let Some(resource_type)=config.ResourceType{
query_pairs.append_pair("SubmissionID",submission_id.to_string().as_str()); query_pairs.append_pair("ResourceType",(resource_type as i32).to_string().as_str());
}
if let Some(resource_id)=config.ResourceID{
query_pairs.append_pair("ResourceID",resource_id.to_string().as_str());
} }
} }
response_ok( response_ok(
self.0.get(url).await.map_err(Error::Reqwest)? self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)? ).await.map_err(Error::Response)?
.json().await.map_err(Error::Reqwest) .json().await.map_err(Error::ReqwestJson)
} }
pub async fn get_script_from_hash<'a>(&self,config:HashRequest<'a>)->Result<Option<ScriptResponse>,SingleItemError>{ pub async fn get_script_from_hash<'a>(&self,config:HashRequest<'a>)->Result<Option<ScriptResponse>,SingleItemError>{
let scripts=self.get_scripts(GetScriptsRequest{ let scripts=self.get_scripts(GetScriptsRequest{
@ -65,7 +68,8 @@ impl Context{
Hash:Some(config.hash), Hash:Some(config.hash),
Name:None, Name:None,
Source:None, Source:None,
SubmissionID:None, ResourceType:None,
ResourceID:None,
}).await.map_err(SingleItemError::Other)?; }).await.map_err(SingleItemError::Other)?;
if 1<scripts.len(){ if 1<scripts.len(){
return Err(SingleItemError::DuplicateItems); return Err(SingleItemError::DuplicateItems);
@ -81,7 +85,7 @@ impl Context{
response_ok( response_ok(
self.0.post(url,body).await.map_err(Error::Reqwest)? self.0.post(url,body).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)? ).await.map_err(Error::Response)?
.json().await.map_err(Error::Reqwest) .json().await.map_err(Error::ReqwestJson)
} }
pub async fn get_script_policies<'a>(&self,config:GetScriptPoliciesRequest<'a>)->Result<Vec<ScriptPolicyResponse>,Error>{ pub async fn get_script_policies<'a>(&self,config:GetScriptPoliciesRequest<'a>)->Result<Vec<ScriptPolicyResponse>,Error>{
let url_raw=format!("{}/script-policy",self.0.base_url); let url_raw=format!("{}/script-policy",self.0.base_url);
@ -105,7 +109,7 @@ impl Context{
response_ok( response_ok(
self.0.get(url).await.map_err(Error::Reqwest)? self.0.get(url).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)? ).await.map_err(Error::Response)?
.json().await.map_err(Error::Reqwest) .json().await.map_err(Error::ReqwestJson)
} }
pub async fn get_script_policy_from_hash<'a>(&self,config:HashRequest<'a>)->Result<Option<ScriptPolicyResponse>,SingleItemError>{ pub async fn get_script_policy_from_hash<'a>(&self,config:HashRequest<'a>)->Result<Option<ScriptPolicyResponse>,SingleItemError>{
let policies=self.get_script_policies(GetScriptPoliciesRequest{ let policies=self.get_script_policies(GetScriptPoliciesRequest{
@ -129,7 +133,7 @@ impl Context{
response_ok( response_ok(
self.0.post(url,body).await.map_err(Error::Reqwest)? self.0.post(url,body).await.map_err(Error::Reqwest)?
).await.map_err(Error::Response)? ).await.map_err(Error::Response)?
.json().await.map_err(Error::Reqwest) .json().await.map_err(Error::ReqwestJson)
} }
pub async fn update_submission_validated_model(&self,config:UpdateSubmissionModelRequest)->Result<(),Error>{ pub async fn update_submission_validated_model(&self,config:UpdateSubmissionModelRequest)->Result<(),Error>{
let url_raw=format!("{}/submissions/{}/validated-model",self.0.base_url,config.SubmissionID); let url_raw=format!("{}/submissions/{}/validated-model",self.0.base_url,config.SubmissionID);
@ -151,9 +155,9 @@ impl Context{
let url_raw=format!("{}/submissions/{}/status/validator-uploaded",self.0.base_url,config.SubmissionID); let url_raw=format!("{}/submissions/{}/status/validator-uploaded",self.0.base_url,config.SubmissionID);
let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?; let mut url=reqwest::Url::parse(url_raw.as_str()).map_err(Error::Parse)?;
if let Some(target_asset_id)=config.TargetAssetID{ {
url.query_pairs_mut() url.query_pairs_mut()
.append_pair("TargetAssetID",target_asset_id.to_string().as_str()); .append_pair("UploadedAssetID",config.UploadedAssetID.to_string().as_str());
} }
response_ok( response_ok(
self.0.post_empty_body(url).await.map_err(Error::Reqwest)? self.0.post_empty_body(url).await.map_err(Error::Reqwest)?

@ -2,6 +2,7 @@
pub enum Error{ pub enum Error{
Parse(url::ParseError), Parse(url::ParseError),
Reqwest(reqwest::Error), Reqwest(reqwest::Error),
ReqwestJson(reqwest::Error),
Response(ResponseError), Response(ResponseError),
JSON(serde_json::Error), JSON(serde_json::Error),
} }
@ -65,6 +66,14 @@ pub struct ScriptID(pub(crate)i64);
#[derive(Clone,Copy,Debug,serde::Serialize,serde::Deserialize)] #[derive(Clone,Copy,Debug,serde::Serialize,serde::Deserialize)]
pub struct ScriptPolicyID(pub(crate)i64); pub struct ScriptPolicyID(pub(crate)i64);
#[derive(Clone,Copy,Debug,PartialEq,Eq,serde_repr::Serialize_repr,serde_repr::Deserialize_repr)]
#[repr(i32)]
pub enum ResourceType{
Unknown=0,
Mapfix=1,
Submission=2,
}
#[allow(nonstandard_style)] #[allow(nonstandard_style)]
pub struct GetScriptRequest{ pub struct GetScriptRequest{
pub ScriptID:ScriptID, pub ScriptID:ScriptID,
@ -81,7 +90,9 @@ pub struct GetScriptsRequest<'a>{
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if="Option::is_none")]
pub Source:Option<&'a str>, pub Source:Option<&'a str>,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if="Option::is_none")]
pub SubmissionID:Option<i64>, pub ResourceType:Option<ResourceType>,
#[serde(skip_serializing_if="Option::is_none")]
pub ResourceID:Option<i64>,
} }
#[derive(Clone,Copy,Debug)] #[derive(Clone,Copy,Debug)]
pub struct HashRequest<'a>{ pub struct HashRequest<'a>{
@ -94,15 +105,17 @@ pub struct ScriptResponse{
pub Name:String, pub Name:String,
pub Hash:String, pub Hash:String,
pub Source:String, pub Source:String,
pub SubmissionID:i64, pub ResourceType:ResourceType,
pub ResourceID:i64,
} }
#[allow(nonstandard_style)] #[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Serialize)] #[derive(Clone,Debug,serde::Serialize)]
pub struct CreateScriptRequest<'a>{ pub struct CreateScriptRequest<'a>{
pub Name:&'a str, pub Name:&'a str,
pub Source:&'a str, pub Source:&'a str,
pub ResourceType:ResourceType,
#[serde(skip_serializing_if="Option::is_none")] #[serde(skip_serializing_if="Option::is_none")]
pub SubmissionID:Option<i64>, pub ResourceID:Option<i64>,
} }
#[allow(nonstandard_style)] #[allow(nonstandard_style)]
#[derive(Clone,Debug,serde::Deserialize)] #[derive(Clone,Debug,serde::Deserialize)]
@ -177,7 +190,7 @@ pub struct UpdateSubmissionModelRequest{
#[derive(Clone,Debug)] #[derive(Clone,Debug)]
pub struct ActionSubmissionUploadedRequest{ pub struct ActionSubmissionUploadedRequest{
pub SubmissionID:i64, pub SubmissionID:i64,
pub TargetAssetID:Option<u64>, pub UploadedAssetID:u64,
} }
#[allow(nonstandard_style)] #[allow(nonstandard_style)]

@ -1,10 +1,12 @@
use futures::StreamExt; use futures::StreamExt;
mod nats_types;
mod validator;
mod upload_new;
mod upload_fix;
mod message_handler; mod message_handler;
mod nats_types;
mod types;
mod uploader;
mod upload_submission;
mod validator;
mod validate_submission;
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Debug)] #[derive(Debug)]
@ -52,7 +54,7 @@ async fn main()->Result<(),StartupError>{
.get_or_create_consumer("validation",async_nats::jetstream::consumer::pull::Config{ .get_or_create_consumer("validation",async_nats::jetstream::consumer::pull::Config{
name:Some("validation".to_owned()), name:Some("validation".to_owned()),
durable_name:Some("validation".to_owned()), durable_name:Some("validation".to_owned()),
filter_subject:"maptest.submissions.>".to_owned(), filter_subject:"maptest.>".to_owned(),
..Default::default() ..Default::default()
}).await.map_err(StartupError::NatsConsumer)? }).await.map_err(StartupError::NatsConsumer)?
.messages().await.map_err(StartupError::NatsStream) .messages().await.map_err(StartupError::NatsStream)

@ -5,9 +5,8 @@ pub enum HandleMessageError{
DoubleAck(async_nats::Error), DoubleAck(async_nats::Error),
Json(serde_json::Error), Json(serde_json::Error),
UnknownSubject(String), UnknownSubject(String),
UploadNew(crate::upload_new::UploadError), UploadSubmission(crate::upload_submission::UploadError),
UploadFix(crate::upload_fix::UploadError), ValidateSubmission(crate::validate_submission::ValidateSubmissionError),
Validation(crate::validator::ValidateError),
} }
impl std::fmt::Display for HandleMessageError{ impl std::fmt::Display for HandleMessageError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
@ -23,9 +22,8 @@ fn from_slice<'a,T:serde::de::Deserialize<'a>>(slice:&'a [u8])->Result<T,HandleM
} }
pub struct MessageHandler{ pub struct MessageHandler{
upload_new:crate::upload_new::Uploader, upload_submission:crate::upload_submission::Uploader,
upload_fix:crate::upload_fix::Uploader, validate_submission:crate::validate_submission::Validator,
validator:crate::validator::Validator,
} }
impl MessageHandler{ impl MessageHandler{
@ -35,18 +33,16 @@ impl MessageHandler{
api:submissions_api::internal::Context, api:submissions_api::internal::Context,
)->Self{ )->Self{
Self{ Self{
upload_new:crate::upload_new::Uploader::new(cookie_context.clone(),group_id,api.clone()), upload_submission:crate::upload_submission::Uploader::new(crate::uploader::Uploader::new(cookie_context.clone(),group_id,api.clone())),
upload_fix:crate::upload_fix::Uploader::new(cookie_context.clone(),group_id,api.clone()), validate_submission:crate::validate_submission::Validator::new(crate::validator::Validator::new(cookie_context,api)),
validator:crate::validator::Validator::new(cookie_context,api),
} }
} }
pub async fn handle_message_result(&self,message_result:MessageResult)->Result<(),HandleMessageError>{ pub async fn handle_message_result(&self,message_result:MessageResult)->Result<(),HandleMessageError>{
let message=message_result.map_err(HandleMessageError::Messages)?; let message=message_result.map_err(HandleMessageError::Messages)?;
message.double_ack().await.map_err(HandleMessageError::DoubleAck)?; message.double_ack().await.map_err(HandleMessageError::DoubleAck)?;
match message.subject.as_str(){ match message.subject.as_str(){
"maptest.submissions.uploadnew"=>self.upload_new.upload(from_slice(&message.payload)?).await.map_err(HandleMessageError::UploadNew), "maptest.submissions.upload"=>self.upload_submission.upload(from_slice(&message.payload)?).await.map_err(HandleMessageError::UploadSubmission),
"maptest.submissions.uploadfix"=>self.upload_fix.upload(from_slice(&message.payload)?).await.map_err(HandleMessageError::UploadFix), "maptest.submissions.validate"=>self.validate_submission.validate(from_slice(&message.payload)?).await.map_err(HandleMessageError::ValidateSubmission),
"maptest.submissions.validate"=>self.validator.validate(from_slice(&message.payload)?).await.map_err(HandleMessageError::Validation),
other=>Err(HandleMessageError::UnknownSubject(other.to_owned())) other=>Err(HandleMessageError::UnknownSubject(other.to_owned()))
} }
} }

@ -6,7 +6,7 @@
#[allow(nonstandard_style)] #[allow(nonstandard_style)]
#[derive(serde::Deserialize)] #[derive(serde::Deserialize)]
pub struct ValidateRequest{ pub struct ValidateSubmissionRequest{
// submission_id is passed back in the response message // submission_id is passed back in the response message
pub SubmissionID:i64, pub SubmissionID:i64,
pub ModelID:u64, pub ModelID:u64,
@ -14,21 +14,22 @@ pub struct ValidateRequest{
pub ValidatedModelID:Option<u64>, pub ValidatedModelID:Option<u64>,
} }
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct ValidateMapfixRequest{
// submission_id is passed back in the response message
pub MapfixID:i64,
pub ModelID:u64,
pub ModelVersion:u64,
pub ValidatedModelID:Option<u64>,
}
// Create a new map // Create a new map
#[allow(nonstandard_style)] #[allow(nonstandard_style)]
#[derive(serde::Deserialize)] #[derive(serde::Deserialize)]
pub struct UploadNewRequest{ pub struct UploadSubmissionRequest{
pub SubmissionID:i64, pub SubmissionID:i64,
pub ModelID:u64, pub ModelID:u64,
pub ModelVersion:u64, pub ModelVersion:u64,
pub ModelName:String, pub ModelName:String,
} }
#[allow(nonstandard_style)]
#[derive(serde::Deserialize)]
pub struct UploadFixRequest{
pub SubmissionID:i64,
pub ModelID:u64,
pub ModelVersion:u64,
pub TargetAssetID:u64,
}

4
validation/src/types.rs Normal file

@ -0,0 +1,4 @@
pub enum ResourceID{
Mapfix(i64),
Submission(i64),
}

@ -1,62 +0,0 @@
use crate::nats_types::UploadFixRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum UploadError{
Get(rbx_asset::cookie::GetError),
Json(serde_json::Error),
Upload(rbx_asset::cookie::UploadError),
ApiActionSubmissionUploaded(submissions_api::Error),
}
impl std::fmt::Display for UploadError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for UploadError{}
pub struct Uploader{
roblox_cookie:rbx_asset::cookie::CookieContext,
group_id:Option<u64>,
api_internal:submissions_api::internal::Context,
}
impl Uploader{
pub const fn new(
roblox_cookie:rbx_asset::cookie::CookieContext,
group_id:Option<u64>,
api_internal:submissions_api::internal::Context,
)->Self{
Self{
roblox_cookie,
group_id,
api_internal,
}
}
pub async fn upload(&self,upload_info:UploadFixRequest)->Result<(),UploadError>{
// download the map model version
let model_data=self.roblox_cookie.get_asset(rbx_asset::cookie::GetAssetRequest{
asset_id:upload_info.ModelID,
version:Some(upload_info.ModelVersion),
}).await.map_err(UploadError::Get)?;
// upload the map to the strafesnet group
let _upload_response=self.roblox_cookie.upload(rbx_asset::cookie::UploadRequest{
assetid:upload_info.TargetAssetID,
groupId:self.group_id,
name:None,
description:None,
ispublic:None,
allowComments:None,
},model_data).await.map_err(UploadError::Upload)?;
// that's it, the database entry does not need to be changed.
// mark submission as uploaded, TargetAssetID is unchanged
self.api_internal.action_submission_uploaded(submissions_api::types::ActionSubmissionUploadedRequest{
SubmissionID:upload_info.SubmissionID,
TargetAssetID:None,
}).await.map_err(UploadError::ApiActionSubmissionUploaded)?;
Ok(())
}
}

@ -0,0 +1,50 @@
use crate::nats_types::UploadMapfixRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum UploadError{
Get(rbx_asset::cookie::GetError),
Json(serde_json::Error),
Upload(rbx_asset::cookie::UploadError),
ApiActionMapfixUploaded(submissions_api::Error),
}
impl std::fmt::Display for UploadError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for UploadError{}
pub struct Uploader(crate::uploader::Uploader);
impl Uploader{
pub const fn new(inner:crate::uploader::Uploader)->Self{
Self(inner)
}
pub async fn upload(&self,upload_info:UploadMapfixRequest)->Result<(),UploadError>{
let Self(uploader)=self;
// download the map model version
let model_data=uploader.roblox_cookie.get_asset(rbx_asset::cookie::GetAssetRequest{
asset_id:upload_info.ModelID,
version:Some(upload_info.ModelVersion),
}).await.map_err(UploadError::Get)?;
// upload the map to the strafesnet group
let _upload_response=uploader.roblox_cookie.upload(rbx_asset::cookie::UploadRequest{
assetid:upload_info.TargetAssetID,
groupId:uploader.group_id,
name:None,
description:None,
ispublic:None,
allowComments:None,
},model_data).await.map_err(UploadError::Upload)?;
// that's it, the database entry does not need to be changed.
// mark mapfix as uploaded, TargetAssetID is unchanged
uploader.api.action_mapfix_uploaded(submissions_api::types::ActionMapfixUploadedRequest{
MapfixID:upload_info.MapfixID,
}).await.map_err(UploadError::ApiActionMapfixUploaded)?;
Ok(())
}
}

@ -1,4 +1,4 @@
use crate::nats_types::UploadNewRequest; use crate::nats_types::UploadSubmissionRequest;
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Debug)] #[derive(Debug)]
@ -16,43 +16,32 @@ impl std::fmt::Display for UploadError{
} }
impl std::error::Error for UploadError{} impl std::error::Error for UploadError{}
pub struct Uploader{ pub struct Uploader(crate::uploader::Uploader);
roblox_cookie:rbx_asset::cookie::CookieContext,
group_id:Option<u64>,
api:submissions_api::internal::Context,
}
impl Uploader{ impl Uploader{
pub const fn new( pub const fn new(inner:crate::uploader::Uploader)->Self{
roblox_cookie:rbx_asset::cookie::CookieContext, Self(inner)
group_id:Option<u64>,
api:submissions_api::internal::Context,
)->Self{
Self{
roblox_cookie,
group_id,
api,
}
} }
pub async fn upload(&self,upload_info:UploadNewRequest)->Result<(),UploadError>{ pub async fn upload(&self,upload_info:UploadSubmissionRequest)->Result<(),UploadError>{
let Self(uploader)=self;
// download the map model version // download the map model version
let model_data=self.roblox_cookie.get_asset(rbx_asset::cookie::GetAssetRequest{ let model_data=uploader.roblox_cookie.get_asset(rbx_asset::cookie::GetAssetRequest{
asset_id:upload_info.ModelID, asset_id:upload_info.ModelID,
version:Some(upload_info.ModelVersion), version:Some(upload_info.ModelVersion),
}).await.map_err(UploadError::Get)?; }).await.map_err(UploadError::Get)?;
// upload the map to the strafesnet group // upload the map to the strafesnet group
let upload_response=self.roblox_cookie.create(rbx_asset::cookie::CreateRequest{ let upload_response=uploader.roblox_cookie.create(rbx_asset::cookie::CreateRequest{
name:upload_info.ModelName.clone(), name:upload_info.ModelName.clone(),
description:"".to_owned(), description:"".to_owned(),
ispublic:false, ispublic:false,
allowComments:false, allowComments:false,
groupId:self.group_id, groupId:uploader.group_id,
},model_data).await.map_err(UploadError::Create)?; },model_data).await.map_err(UploadError::Create)?;
// note the asset id of the created model for later release, and mark the submission as uploaded // note the asset id of the created model for later release, and mark the submission as uploaded
self.api.action_submission_uploaded(submissions_api::types::ActionSubmissionUploadedRequest{ uploader.api.action_submission_uploaded(submissions_api::types::ActionSubmissionUploadedRequest{
SubmissionID:upload_info.SubmissionID, SubmissionID:upload_info.SubmissionID,
TargetAssetID:Some(upload_response.AssetId), UploadedAssetID:upload_response.AssetId,
}).await.map_err(UploadError::ApiActionSubmissionUploaded)?; }).await.map_err(UploadError::ApiActionSubmissionUploaded)?;
Ok(()) Ok(())

@ -0,0 +1,18 @@
pub struct Uploader{
pub(crate) roblox_cookie:rbx_asset::cookie::CookieContext,
pub(crate) group_id:Option<u64>,
pub(crate) api:submissions_api::internal::Context,
}
impl Uploader{
pub const fn new(
roblox_cookie:rbx_asset::cookie::CookieContext,
group_id:Option<u64>,
api:submissions_api::internal::Context,
)->Self{
Self{
roblox_cookie,
group_id,
api,
}
}
}

@ -0,0 +1,45 @@
use crate::nats_types::ValidateMapfixRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum ValidateMapfixError{
ApiActionMapfixValidate(submissions_api::Error),
}
impl std::fmt::Display for ValidateMapfixError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for ValidateMapfixError{}
pub struct Validator(crate::validator::Validator);
impl Validator{
pub const fn new(inner:crate::validator::Validator)->Self{
Self(inner)
}
pub async fn validate(&self,validate_info:ValidateMapfixRequest)->Result<(),ValidateMapfixError>{
let Self(validator)=self;
let mapfix_id=validate_info.MapfixID;
let validate_result=validator.validate(validate_info.into()).await;
// update the mapfix depending on the result
match &validate_result{
Ok(())=>{
// update the mapfix model status to validated
validator.api.action_mapfix_validated(
submissions_api::types::MapfixID(mapfix_id)
).await.map_err(ValidateMapfixError::ApiActionMapfixValidate)?;
},
Err(e)=>{
// update the mapfix model status to accepted
validator.api.action_mapfix_accepted(submissions_api::types::ActionMapfixAcceptedRequest{
MapfixID:mapfix_id,
StatusMessage:format!("{e}"),
}).await.map_err(ValidateMapfixError::ApiActionMapfixValidate)?;
},
}
Ok(())
}
}

@ -0,0 +1,45 @@
use crate::nats_types::ValidateSubmissionRequest;
#[allow(dead_code)]
#[derive(Debug)]
pub enum ValidateSubmissionError{
ApiActionSubmissionValidate(submissions_api::Error),
}
impl std::fmt::Display for ValidateSubmissionError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for ValidateSubmissionError{}
pub struct Validator(crate::validator::Validator);
impl Validator{
pub const fn new(inner:crate::validator::Validator)->Self{
Self(inner)
}
pub async fn validate(&self,validate_info:ValidateSubmissionRequest)->Result<(),ValidateSubmissionError>{
let Self(validator)=self;
let submission_id=validate_info.SubmissionID;
let validate_result=validator.validate(validate_info.into()).await;
// update the submission depending on the result
match &validate_result{
Ok(())=>{
// update the submission model status to validated
validator.api.action_submission_validated(
submissions_api::types::SubmissionID(submission_id)
).await.map_err(ValidateSubmissionError::ApiActionSubmissionValidate)?;
},
Err(e)=>{
// update the submission model status to accepted
validator.api.action_submission_accepted(submissions_api::types::ActionSubmissionAcceptedRequest{
SubmissionID:submission_id,
StatusMessage:format!("{e}"),
}).await.map_err(ValidateSubmissionError::ApiActionSubmissionValidate)?;
},
}
Ok(())
}
}

@ -1,6 +1,7 @@
use futures::TryStreamExt; use futures::TryStreamExt;
use submissions_api::types::ResourceType;
use crate::nats_types::ValidateRequest; use crate::types::ResourceID;
const SCRIPT_CONCURRENCY:usize=16; const SCRIPT_CONCURRENCY:usize=16;
@ -41,8 +42,8 @@ pub enum ValidateError{
ApiCreateScript(submissions_api::Error), ApiCreateScript(submissions_api::Error),
ApiCreateScriptPolicy(submissions_api::Error), ApiCreateScriptPolicy(submissions_api::Error),
ApiGetScriptFromHash(submissions_api::types::SingleItemError), ApiGetScriptFromHash(submissions_api::types::SingleItemError),
ApiUpdateMapfixModelUnimplemented,
ApiUpdateSubmissionModel(submissions_api::Error), ApiUpdateSubmissionModel(submissions_api::Error),
ApiActionSubmissionValidate(submissions_api::Error),
ModelFileRootMustHaveOneChild, ModelFileRootMustHaveOneChild,
ModelFileChildRefIsNil, ModelFileChildRefIsNil,
ModelFileEncode(rbx_binary::EncodeError), ModelFileEncode(rbx_binary::EncodeError),
@ -56,9 +57,38 @@ impl std::fmt::Display for ValidateError{
} }
impl std::error::Error for ValidateError{} impl std::error::Error for ValidateError{}
#[allow(nonstandard_style)]
pub struct ValidateRequest{
pub ModelID:u64,
pub ModelVersion:u64,
pub ValidatedModelID:Option<u64>,
pub ResourceID:ResourceID,
}
impl From<crate::nats_types::ValidateMapfixRequest> for ValidateRequest{
fn from(value:crate::nats_types::ValidateMapfixRequest)->Self{
Self{
ModelID:value.ModelID,
ModelVersion:value.ModelVersion,
ValidatedModelID:value.ValidatedModelID,
ResourceID:ResourceID::Mapfix(value.MapfixID),
}
}
}
impl From<crate::nats_types::ValidateSubmissionRequest> for ValidateRequest{
fn from(value:crate::nats_types::ValidateSubmissionRequest)->Self{
Self{
ModelID:value.ModelID,
ModelVersion:value.ModelVersion,
ValidatedModelID:value.ValidatedModelID,
ResourceID:ResourceID::Submission(value.SubmissionID),
}
}
}
pub struct Validator{ pub struct Validator{
roblox_cookie:rbx_asset::cookie::CookieContext, pub(crate) roblox_cookie:rbx_asset::cookie::CookieContext,
api:submissions_api::internal::Context, pub(crate) api:submissions_api::internal::Context,
} }
impl Validator{ impl Validator{
@ -72,29 +102,6 @@ impl Validator{
} }
} }
pub async fn validate(&self,validate_info:ValidateRequest)->Result<(),ValidateError>{ pub async fn validate(&self,validate_info:ValidateRequest)->Result<(),ValidateError>{
let submission_id=validate_info.SubmissionID;
let validate_result=self.validate_inner(validate_info).await;
// update the submission depending on the result
match &validate_result{
Ok(())=>{
// update the submission model status to validated
self.api.action_submission_validated(
submissions_api::types::SubmissionID(submission_id)
).await.map_err(ValidateError::ApiActionSubmissionValidate)?;
},
Err(e)=>{
// update the submission model status to accepted
self.api.action_submission_accepted(submissions_api::types::ActionSubmissionAcceptedRequest{
SubmissionID:submission_id,
StatusMessage:format!("{e}"),
}).await.map_err(ValidateError::ApiActionSubmissionValidate)?;
},
}
validate_result
}
pub async fn validate_inner(&self,validate_info:ValidateRequest)->Result<(),ValidateError>{
// download map // download map
let data=self.roblox_cookie.get_asset(rbx_asset::cookie::GetAssetRequest{ let data=self.roblox_cookie.get_asset(rbx_asset::cookie::GetAssetRequest{
asset_id:validate_info.ModelID, asset_id:validate_info.ModelID,
@ -155,11 +162,17 @@ impl Validator{
}, },
}; };
}else{ }else{
let (resource_type,resource_id)=match validate_info.ResourceID{
ResourceID::Mapfix(mapfix_id)=>(ResourceType::Mapfix,mapfix_id),
ResourceID::Submission(submission_id)=>(ResourceType::Submission,submission_id),
};
// upload the script // upload the script
let script=self.api.create_script(submissions_api::types::CreateScriptRequest{ let script=self.api.create_script(submissions_api::types::CreateScriptRequest{
Name:name.as_str(), Name:name.as_str(),
Source:source.as_str(), Source:source.as_str(),
SubmissionID:Some(validate_info.SubmissionID), ResourceType:resource_type,
ResourceID:Some(resource_id),
}).await.map_err(ValidateError::ApiCreateScript)?; }).await.map_err(ValidateError::ApiCreateScript)?;
// create a None policy (pending review by yours truly) // create a None policy (pending review by yours truly)
@ -252,13 +265,21 @@ impl Validator{
response.AssetId response.AssetId
}; };
// update the submission to use the validated model match validate_info.ResourceID{
self.api.update_submission_validated_model(submissions_api::types::UpdateSubmissionModelRequest{ ResourceID::Mapfix(_mapfix_id)=>{
SubmissionID:validate_info.SubmissionID, // update the mapfix to use the validated model
ModelID:model_id, return Err(ValidateError::ApiUpdateMapfixModelUnimplemented);
ModelVersion:1, //TODO },
}).await.map_err(ValidateError::ApiUpdateSubmissionModel)?; ResourceID::Submission(submission_id)=>{
}; // update the submission to use the validated model
self.api.update_submission_validated_model(submissions_api::types::UpdateSubmissionModelRequest{
SubmissionID:submission_id,
ModelID:model_id,
ModelVersion:1, //TODO
}).await.map_err(ValidateError::ApiUpdateSubmissionModel)?;
},
}
}
Ok(()) Ok(())
} }

@ -1,6 +1,6 @@
"use client" "use client"
import { FormControl, FormControlLabel, Button, TextField, Checkbox } from "@mui/material" import { Button, TextField } from "@mui/material"
import GameSelection from "./_game"; import GameSelection from "./_game";
import SendIcon from '@mui/icons-material/Send'; import SendIcon from '@mui/icons-material/Send';
@ -15,7 +15,6 @@ interface SubmissionPayload {
GameID: number; GameID: number;
AssetID: number; AssetID: number;
AssetVersion: number; AssetVersion: number;
TargetAssetID: number;
} }
interface IdResponse { interface IdResponse {
ID: number; ID: number;
@ -23,7 +22,6 @@ interface IdResponse {
export default function SubmissionInfoPage() { export default function SubmissionInfoPage() {
const [game, setGame] = useState(1); const [game, setGame] = useState(1);
const [isFixingMap, setIsFixingMap] = useState(false);
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => { const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault(); event.preventDefault();
@ -37,7 +35,6 @@ export default function SubmissionInfoPage() {
GameID: game, GameID: game,
AssetID: Number((formData.get("asset-id") as string) ?? "0"), AssetID: Number((formData.get("asset-id") as string) ?? "0"),
AssetVersion: 0, AssetVersion: 0,
TargetAssetID: isFixingMap ? Number((formData.get("target-asset-id") as string) ?? "0") : 0,
}; };
console.log(payload) console.log(payload)
@ -84,15 +81,6 @@ export default function SubmissionInfoPage() {
<TextField className="form-field" id="asset-id" name="asset-id" label="Asset ID" variant="outlined"/> <TextField className="form-field" id="asset-id" name="asset-id" label="Asset ID" variant="outlined"/>
{/* I think this is Quat's job to figure this one out (to be set when someone clicks review(?)) */} {/* <TextField className="form-field" id="asset-version" label="Asset Version" variant="outlined"/> */} {/* I think this is Quat's job to figure this one out (to be set when someone clicks review(?)) */} {/* <TextField className="form-field" id="asset-version" label="Asset Version" variant="outlined"/> */}
<GameSelection game={game} setGame={setGame} /> <GameSelection game={game} setGame={setGame} />
<FormControl>
<FormControlLabel control={<Checkbox sx={{
color: "#646464",
'&.Mui-checked': {
color: "#66BB6A",
},
}} onChange={(e) => setIsFixingMap(e.target.checked)} />} label="Fixing an Existing Map?" />
</FormControl>
<TextField className="form-field" id="target-asset-id" name="target-asset-id" label="Target Asset ID (group model)" variant="outlined"/>
<span className="spacer form-spacer"></span> <span className="spacer form-spacer"></span>
<Button type="submit" variant="contained" startIcon={<SendIcon/>} sx={{ <Button type="submit" variant="contained" startIcon={<SendIcon/>} sx={{
width: "400px", width: "400px",