package validator_controller import ( "context" "errors" "fmt" "time" "git.itzana.me/strafesnet/go-grpc/validator" "git.itzana.me/strafesnet/maps-service/pkg/datastore" "git.itzana.me/strafesnet/maps-service/pkg/model" "git.itzana.me/strafesnet/maps-service/pkg/service" ) type Submissions struct { *validator.UnimplementedValidatorSubmissionServiceServer inner *service.Service } func NewSubmissionsController( inner *service.Service, ) Submissions { return Submissions{ inner: inner, } } var( // prevent two submissions 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 *Submissions) SetValidatedModel(ctx context.Context, params *validator.ValidatedModelRequest) (*validator.NullResponse, error) { SubmissionID := int64(params.ID) // check if Status is ChangesRequested|Submitted|UnderConstruction update := service.NewSubmissionUpdate() update.SetValidatedAssetID(params.ValidatedModelID) update.SetValidatedAssetVersion(params.ValidatedModelVersion) // DO NOT reset completed when validated model is updated // update.Add("completed", false) allowed_statuses := []model.SubmissionStatus{model.SubmissionStatusValidating} err := svc.inner.UpdateSubmissionIfStatus(ctx, SubmissionID, allowed_statuses, update) if err != nil { return nil, err } event_data := model.AuditEventDataChangeValidatedModel{ ValidatedModelID: params.ValidatedModelID, ValidatedModelVersion: params.ValidatedModelVersion, } err = svc.inner.CreateAuditEventChangeValidatedModel( ctx, model.ValidatorUserID, model.Resource{ ID: SubmissionID, Type: model.ResourceSubmission, }, event_data, ) if err != nil { return nil, err } return &validator.NullResponse{}, nil } // ActionSubmissionSubmitted invokes actionSubmissionSubmitted operation. // // Role Validator changes status from Submitting -> Submitted. // // POST /submissions/{SubmissionID}/status/validator-submitted func (svc *Submissions) SetStatusSubmitted(ctx context.Context, params *validator.SubmittedRequest) (*validator.NullResponse, error) { SubmissionID := int64(params.ID) // transaction target_status := model.SubmissionStatusSubmitted update := service.NewSubmissionUpdate() update.SetStatusID(target_status) update.SetAssetVersion(uint64(params.ModelVersion)) update.SetDisplayName(params.DisplayName) update.SetCreator(params.Creator) update.SetGameID(uint32(params.GameID)) allowed_statuses := []model.SubmissionStatus{model.SubmissionStatusSubmitting} err := svc.inner.UpdateSubmissionIfStatus(ctx, SubmissionID, allowed_statuses, update) if err != nil { return nil, err } event_data := model.AuditEventDataAction{ TargetStatus: uint32(target_status), } err = svc.inner.CreateAuditEventAction( ctx, model.ValidatorUserID, model.Resource{ ID: SubmissionID, Type: model.ResourceSubmission, }, event_data, ) if err != nil { return nil, err } return &validator.NullResponse{}, nil } // ActionSubmissionRequestChanges implements actionSubmissionRequestChanges operation. // // (Internal endpoint) Role Validator changes status from Submitting -> RequestChanges. // // POST /submissions/{SubmissionID}/status/validator-request-changes func (svc *Submissions) SetStatusRequestChanges(ctx context.Context, params *validator.SubmissionID) (*validator.NullResponse, error) { SubmissionID := int64(params.ID) // transaction target_status := model.SubmissionStatusChangesRequested update := service.NewSubmissionUpdate() update.SetStatusID(target_status) allowed_statuses :=[]model.SubmissionStatus{model.SubmissionStatusSubmitting} err := svc.inner.UpdateSubmissionIfStatus(ctx, SubmissionID, allowed_statuses, update) if err != nil { return nil, err } // push an action audit event event_data := model.AuditEventDataAction{ TargetStatus: uint32(target_status), } err = svc.inner.CreateAuditEventAction( ctx, model.ValidatorUserID, model.Resource{ ID: SubmissionID, Type: model.ResourceSubmission, }, event_data, ) if err != nil { return nil, err } return &validator.NullResponse{}, nil } // ActionSubmissionValidate invokes actionSubmissionValidate operation. // // Role Validator changes status from Validating -> Validated. // // POST /submissions/{SubmissionID}/status/validator-validated func (svc *Submissions) SetStatusValidated(ctx context.Context, params *validator.SubmissionID) (*validator.NullResponse, error) { SubmissionID := int64(params.ID) // transaction target_status := model.SubmissionStatusValidated update := service.NewSubmissionUpdate() update.SetStatusID(target_status) allowed_statuses :=[]model.SubmissionStatus{model.SubmissionStatusValidating} err := svc.inner.UpdateSubmissionIfStatus(ctx, SubmissionID, allowed_statuses, update) if err != nil { return nil, err } event_data := model.AuditEventDataAction{ TargetStatus: uint32(target_status), } err = svc.inner.CreateAuditEventAction( ctx, model.ValidatorUserID, model.Resource{ ID: SubmissionID, Type: model.ResourceSubmission, }, event_data, ) if err != nil { return nil, err } return &validator.NullResponse{}, nil } // ActionSubmissionAccepted implements actionSubmissionAccepted operation. // // (Internal endpoint) Role Validator changes status from Validating -> Accepted. // // POST /submissions/{SubmissionID}/status/validator-failed func (svc *Submissions) SetStatusNotValidated(ctx context.Context, params *validator.SubmissionID) (*validator.NullResponse, error) { SubmissionID := int64(params.ID) // transaction target_status := model.SubmissionStatusAcceptedUnvalidated update := service.NewSubmissionUpdate() update.SetStatusID(target_status) allowed_statuses :=[]model.SubmissionStatus{model.SubmissionStatusValidating} err := svc.inner.UpdateSubmissionIfStatus(ctx, SubmissionID, allowed_statuses, update) if err != nil { return nil, err } // push an action audit event event_data := model.AuditEventDataAction{ TargetStatus: uint32(target_status), } err = svc.inner.CreateAuditEventAction( ctx, model.ValidatorUserID, model.Resource{ ID: SubmissionID, Type: model.ResourceSubmission, }, event_data, ) if err != nil { return nil, err } return &validator.NullResponse{}, nil } // ActionSubmissionUploaded implements actionSubmissionUploaded operation. // // (Internal endpoint) Role Validator changes status from Uploading -> Uploaded. // // POST /submissions/{SubmissionID}/status/validator-uploaded func (svc *Submissions) SetStatusUploaded(ctx context.Context, params *validator.StatusUploadedRequest) (*validator.NullResponse, error) { SubmissionID := int64(params.ID) // transaction target_status := model.SubmissionStatusUploaded update := service.NewSubmissionUpdate() update.SetStatusID(target_status) update.SetUploadedAssetID(params.UploadedAssetID) allowed_statuses :=[]model.SubmissionStatus{model.SubmissionStatusUploading} err := svc.inner.UpdateSubmissionIfStatus(ctx, SubmissionID, allowed_statuses, update) if err != nil { return nil, err } event_data := model.AuditEventDataAction{ TargetStatus: uint32(target_status), } err = svc.inner.CreateAuditEventAction( ctx, model.ValidatorUserID, model.Resource{ ID: SubmissionID, Type: model.ResourceSubmission, }, event_data, ) if err != nil { return nil, err } return &validator.NullResponse{}, nil } func (svc *Submissions) SetStatusNotUploaded(ctx context.Context, params *validator.SubmissionID) (*validator.NullResponse, error) { SubmissionID := int64(params.ID) // transaction target_status := model.SubmissionStatusValidated update := service.NewSubmissionUpdate() update.SetStatusID(target_status) allowed_statuses :=[]model.SubmissionStatus{model.SubmissionStatusUploading} err := svc.inner.UpdateSubmissionIfStatus(ctx, SubmissionID, allowed_statuses, update) if err != nil { return nil, err } // push an action audit event event_data := model.AuditEventDataAction{ TargetStatus: uint32(target_status), } err = svc.inner.CreateAuditEventAction( ctx, model.ValidatorUserID, model.Resource{ ID: SubmissionID, Type: model.ResourceSubmission, }, event_data, ) if err != nil { return nil, err } return &validator.NullResponse{}, nil } func (svc *Submissions) SetStatusReleased(ctx context.Context, params *validator.SubmissionReleaseRequest) (*validator.NullResponse, error){ // create map with go-grpc _, err := svc.inner.CreateMap(ctx, model.Map{ ID: params.MapCreate.ID, DisplayName: params.MapCreate.DisplayName, Creator: params.MapCreate.Creator, GameID: params.MapCreate.GameID, Date: time.Unix(params.MapCreate.Date, 0), Submitter: params.MapCreate.Submitter, Thumbnail: 0, AssetVersion: params.MapCreate.AssetVersion, LoadCount: 0, Modes: params.MapCreate.Modes, }) if err != nil { return nil, err } // update status to Released update := service.NewSubmissionUpdate() update.SetStatusID(model.SubmissionStatusReleased) err = svc.inner.UpdateSubmissionIfStatus(ctx, int64(params.SubmissionID), []model.SubmissionStatus{model.SubmissionStatusUploaded}, update) if err != nil { return nil, err } return &validator.NullResponse{}, nil } // CreateSubmissionAuditError implements createSubmissionAuditError operation. // // Post an error to the audit log // // POST /submissions/{SubmissionID}/error func (svc *Submissions) CreateAuditError(ctx context.Context, params *validator.AuditErrorRequest) (*validator.NullResponse, error) { SubmissionID := int64(params.ID) event_data := model.AuditEventDataError{ Error: params.ErrorMessage, } err := svc.inner.CreateAuditEventError( ctx, model.ValidatorUserID, model.Resource{ ID: SubmissionID, Type: model.ResourceSubmission, }, event_data, ) if err != nil { return nil, err } return &validator.NullResponse{}, nil } // CreateSubmissionAuditCheckList implements createSubmissionAuditCheckList operation. // // Post a checklist to the audit log // // POST /submissions/{SubmissionID}/checklist func (svc *Submissions) CreateAuditChecklist(ctx context.Context, params *validator.AuditChecklistRequest) (*validator.NullResponse, error) { SubmissionID := int64(params.ID) check_list := make([]model.Check, len(params.CheckList)) for i, check := range params.CheckList { check_list[i] = model.Check{ Name: check.Name, Summary: check.Summary, Passed: check.Passed, } } event_data := model.AuditEventDataCheckList{ CheckList: check_list, } err := svc.inner.CreateAuditEventCheckList( ctx, model.ValidatorUserID, model.Resource{ ID: SubmissionID, Type: model.ResourceSubmission, }, event_data, ) if err != nil { return nil, err } return &validator.NullResponse{}, nil } // POST /submissions func (svc *Submissions) Create(ctx context.Context, request *validator.SubmissionCreate) (*validator.SubmissionID, error) { var Submitter=uint64(request.AssetOwner); var Status=model.SubmissionStatus(request.Status); var roles=model.Roles(request.Roles); // Check if an active submission with the same asset id exists { filter := service.NewSubmissionFilter() filter.SetAssetID(request.AssetID) filter.SetAssetVersion(request.AssetVersion) filter.SetStatuses(ActiveSubmissionStatuses) active_submissions, err := svc.inner.ListSubmissions(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_id := int32(request.OperationID) operation, err := svc.inner.GetOperation(ctx, operation_id) if err != nil { return nil, err } // check if user owns asset is_submitter := operation.Owner == Submitter // check if user is map admin has_submission_review := roles & model.RolesSubmissionReview == model.RolesSubmissionReview // if neither, u not allowed if !is_submitter && !has_submission_review { return nil, ErrNotAssetOwner } submission, err := svc.inner.CreateSubmission(ctx, model.Submission{ ID: 0, DisplayName: request.DisplayName, Creator: request.Creator, GameID: request.GameID, Submitter: Submitter, AssetID: request.AssetID, AssetVersion: request.AssetVersion, Completed: false, StatusID: Status, }) if err != nil { return nil, err } // mark the operation as completed and provide the path params := service.NewOperationCompleteParams(fmt.Sprintf("/submissions/%d", submission.ID)) err = svc.inner.CompleteOperation(ctx, operation_id, params) if err != nil { return nil, err } return &validator.SubmissionID{ ID: uint64(submission.ID), }, nil }