package service_internal import ( "context" "encoding/json" "errors" "fmt" "git.itzana.me/strafesnet/maps-service/pkg/datastore" internal "git.itzana.me/strafesnet/maps-service/pkg/internal" "git.itzana.me/strafesnet/maps-service/pkg/model" ) var( // prevent two mapfixes with same asset id ActiveSubmissionStatuses = []model.SubmissionStatus{ model.SubmissionStatusUploading, model.SubmissionStatusValidated, model.SubmissionStatusValidating, model.SubmissionStatusAcceptedUnvalidated, model.SubmissionStatusChangesRequested, model.SubmissionStatusSubmitted, model.SubmissionStatusUnderConstruction, } ) var( ErrActiveSubmissionSameAssetID = errors.New("There is an active submission with the same AssetID") ) // UpdateSubmissionValidatedModel implements patchSubmissionModel operation. // // Update model following role restrictions. // // POST /submissions/{SubmissionID}/validated-model func (svc *Service) UpdateSubmissionValidatedModel(ctx context.Context, params internal.UpdateSubmissionValidatedModelParams) error { ValidatedModelID := uint64(params.ValidatedModelID) ValidatedModelVersion := uint64(params.ValidatedModelVersion) // check if Status is ChangesRequested|Submitted|UnderConstruction pmap := datastore.Optional() pmap.Add("validated_asset_id", ValidatedModelID) pmap.Add("validated_asset_version", ValidatedModelVersion) // DO NOT reset completed when validated model is updated // pmap.Add("completed", false) err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, pmap) if err != nil { return err } event_data := model.AuditEventDataChangeValidatedModel{ ValidatedModelID: ValidatedModelID, ValidatedModelVersion: ValidatedModelVersion, } EventData, err := json.Marshal(event_data) if err != nil { return err } _, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{ ID: 0, User: ValidtorUserID, ResourceType: model.ResourceSubmission, ResourceID: params.SubmissionID, EventType: model.AuditEventTypeChangeValidatedModel, EventData: EventData, }) if err != nil { return err } return nil } // ActionSubmissionSubmitted invokes actionSubmissionSubmitted operation. // // Role Validator changes status from Submitting -> Submitted. // // POST /submissions/{SubmissionID}/status/validator-submitted func (svc *Service) ActionSubmissionSubmitted(ctx context.Context, params internal.ActionSubmissionSubmittedParams) error { // transaction target_status := model.SubmissionStatusSubmitted smap := datastore.Optional() smap.Add("status_id", target_status) smap.Add("asset_version", params.ModelVersion) err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitting}, smap) if err != nil { return err } event_data := model.AuditEventDataAction{ TargetStatus: uint32(target_status), } EventData, err := json.Marshal(event_data) if err != nil { return err } _, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{ ID: 0, User: ValidtorUserID, ResourceType: model.ResourceSubmission, ResourceID: params.SubmissionID, EventType: model.AuditEventTypeAction, EventData: EventData, }) if err != nil { return err } return nil } // ActionSubmissionRequestChanges implements actionSubmissionRequestChanges operation. // // (Internal endpoint) Role Validator changes status from Submitting -> RequestChanges. // // POST /submissions/{SubmissionID}/status/validator-request-changes func (svc *Service) ActionSubmissionRequestChanges(ctx context.Context, params internal.ActionSubmissionRequestChangesParams) error { // transaction target_status := model.SubmissionStatusChangesRequested smap := datastore.Optional() smap.Add("status_id", target_status) err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusSubmitting}, smap) if err != nil { return err } //push an error audit event { event_data := model.AuditEventDataError{ Error: params.ErrorMessage, } EventData, err := json.Marshal(event_data) if err != nil { return err } _, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{ ID: 0, User: ValidtorUserID, ResourceType: model.ResourceSubmission, ResourceID: params.SubmissionID, EventType: model.AuditEventTypeError, EventData: EventData, }) if err != nil { return err } } // push an action audit event event_data := model.AuditEventDataAction{ TargetStatus: uint32(target_status), } EventData, err := json.Marshal(event_data) if err != nil { return err } _, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{ ID: 0, User: ValidtorUserID, ResourceType: model.ResourceSubmission, ResourceID: params.SubmissionID, EventType: model.AuditEventTypeAction, EventData: EventData, }) if err != nil { return err } return nil } // ActionSubmissionValidate invokes actionSubmissionValidate operation. // // Role Validator changes status from Validating -> Validated. // // POST /submissions/{SubmissionID}/status/validator-validated func (svc *Service) ActionSubmissionValidated(ctx context.Context, params internal.ActionSubmissionValidatedParams) error { // transaction target_status := model.SubmissionStatusValidated smap := datastore.Optional() smap.Add("status_id", target_status) err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, smap) if err != nil { return err } event_data := model.AuditEventDataAction{ TargetStatus: uint32(target_status), } EventData, err := json.Marshal(event_data) if err != nil { return err } _, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{ ID: 0, User: ValidtorUserID, ResourceType: model.ResourceSubmission, ResourceID: params.SubmissionID, EventType: model.AuditEventTypeAction, EventData: EventData, }) if err != nil { return err } return nil } // ActionSubmissionAccepted implements actionSubmissionAccepted operation. // // (Internal endpoint) Role Validator changes status from Validating -> Accepted. // // POST /submissions/{SubmissionID}/status/validator-failed func (svc *Service) ActionSubmissionAccepted(ctx context.Context, params internal.ActionSubmissionAcceptedParams) error { // transaction target_status := model.SubmissionStatusAcceptedUnvalidated smap := datastore.Optional() smap.Add("status_id", target_status) err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusValidating}, smap) if err != nil { return err } //push an error audit event { event_data := model.AuditEventDataError{ Error: params.ErrorMessage, } EventData, err := json.Marshal(event_data) if err != nil { return err } _, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{ ID: 0, User: ValidtorUserID, ResourceType: model.ResourceSubmission, ResourceID: params.SubmissionID, EventType: model.AuditEventTypeError, EventData: EventData, }) if err != nil { return err } } // push an action audit event event_data := model.AuditEventDataAction{ TargetStatus: uint32(target_status), } EventData, err := json.Marshal(event_data) if err != nil { return err } _, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{ ID: 0, User: ValidtorUserID, ResourceType: model.ResourceSubmission, ResourceID: params.SubmissionID, EventType: model.AuditEventTypeAction, EventData: EventData, }) if err != nil { return err } return nil } // ActionSubmissionUploaded implements actionSubmissionUploaded operation. // // (Internal endpoint) Role Validator changes status from Uploading -> Uploaded. // // POST /submissions/{SubmissionID}/status/validator-uploaded func (svc *Service) ActionSubmissionUploaded(ctx context.Context, params internal.ActionSubmissionUploadedParams) error { // transaction target_status := model.SubmissionStatusUploaded smap := datastore.Optional() smap.Add("status_id", target_status) smap.Add("uploaded_asset_id", params.UploadedAssetID) err := svc.DB.Submissions().IfStatusThenUpdate(ctx, params.SubmissionID, []model.SubmissionStatus{model.SubmissionStatusUploading}, smap) if err != nil { return err } event_data := model.AuditEventDataAction{ TargetStatus: uint32(target_status), } EventData, err := json.Marshal(event_data) if err != nil { return err } _, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{ ID: 0, User: ValidtorUserID, ResourceType: model.ResourceSubmission, ResourceID: params.SubmissionID, EventType: model.AuditEventTypeAction, EventData: EventData, }) if err != nil { return err } return nil } // POST /submissions func (svc *Service) CreateSubmission(ctx context.Context, request *internal.SubmissionCreate) (*internal.SubmissionID, error) { // sanitization if request.GameID<0|| request.AssetOwner<0|| request.AssetID<0|| request.AssetVersion<0{ return nil, ErrNegativeID } var GameID=uint32(request.GameID); var Submitter=uint64(request.AssetOwner); var AssetID=uint64(request.AssetID); var AssetVersion=uint64(request.AssetVersion); // Check if an active submission with the same asset id exists { filter := datastore.Optional() filter.Add("asset_id", request.AssetID) filter.Add("asset_version", request.AssetVersion) filter.Add("status_id", ActiveSubmissionStatuses) active_submissions, err := svc.DB.Submissions().List(ctx, filter, model.Page{ Number: 1, Size: 1, },datastore.ListSortDisabled) if err != nil { return nil, err } if len(active_submissions) != 0{ return nil, ErrActiveSubmissionSameAssetID } } operation, err := svc.DB.Operations().Get(ctx, request.OperationID) if err != nil { return nil, err } // check if user owns asset // TODO: allow bypass by admin if operation.Owner != Submitter { return nil, ErrNotAssetOwner } submission, err := svc.DB.Submissions().Create(ctx, model.Submission{ ID: 0, DisplayName: request.DisplayName, Creator: request.Creator, GameID: GameID, Submitter: Submitter, AssetID: AssetID, AssetVersion: AssetVersion, Completed: false, StatusID: model.SubmissionStatusUnderConstruction, }) if err != nil { return nil, err } // mark the operation as completed and provide the path pmap := datastore.Optional() pmap.Add("status_id", model.OperationStatusCompleted) pmap.Add("path", fmt.Sprintf("/submissions/%d", submission.ID)) err = svc.DB.Operations().Update(ctx, request.OperationID, pmap) if err != nil { return nil, err } return &internal.SubmissionID{ SubmissionID: submission.ID, }, nil }