1007 lines
25 KiB
Go
1007 lines
25 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
"git.itzana.me/strafesnet/go-grpc/maps"
|
|
"git.itzana.me/strafesnet/maps-service/pkg/api"
|
|
"git.itzana.me/strafesnet/maps-service/pkg/datastore"
|
|
"git.itzana.me/strafesnet/maps-service/pkg/model"
|
|
)
|
|
|
|
var(
|
|
CreationPhaseMapfixesLimit = 20
|
|
CreationPhaseMapfixStatuses = []model.MapfixStatus{
|
|
model.MapfixStatusChangesRequested,
|
|
model.MapfixStatusSubmitted,
|
|
model.MapfixStatusUnderConstruction,
|
|
}
|
|
// limit mapfixes in the pipeline to one per target map
|
|
ActiveAcceptedMapfixStatuses = []model.MapfixStatus{
|
|
model.MapfixStatusUploading,
|
|
model.MapfixStatusValidated,
|
|
model.MapfixStatusValidating,
|
|
model.MapfixStatusAcceptedUnvalidated,
|
|
}
|
|
// Allow 5 mapfixes every 10 minutes
|
|
CreateMapfixRateLimit int64 = 5
|
|
CreateMapfixRecencyWindow = time.Second*600
|
|
)
|
|
|
|
var (
|
|
ErrCreationPhaseMapfixesLimit = errors.New("Active mapfixes limited to 20")
|
|
ErrActiveMapfixSameTargetAssetID = errors.New("There is an active mapfix with the same TargetAssetID")
|
|
ErrAcceptOwnMapfix = fmt.Errorf("%w: You cannot accept your own mapfix as the submitter", ErrPermissionDenied)
|
|
ErrCreateMapfixRateLimit = errors.New("You must not create more than 5 mapfixes every 10 minutes")
|
|
)
|
|
|
|
// POST /mapfixes
|
|
func (svc *Service) CreateMapfix(ctx context.Context, request *api.MapfixTriggerCreate) (*api.OperationID, error) {
|
|
// sanitization
|
|
if request.AssetID<0 || request.TargetAssetID<0{
|
|
return nil, ErrNegativeID
|
|
}
|
|
var ModelID=uint64(request.AssetID);
|
|
var TargetAssetID=uint64(request.TargetAssetID);
|
|
|
|
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
|
|
if !ok {
|
|
return nil, ErrUserInfo
|
|
}
|
|
|
|
userId, err := userInfo.GetUserID()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Check if user's mapfixes in the creation phase exceeds the limit
|
|
{
|
|
filter := datastore.Optional()
|
|
filter.Add("submitter", int64(userId))
|
|
filter.Add("status_id", CreationPhaseMapfixStatuses)
|
|
creation_mapfixes, err := svc.DB.Mapfixes().List(ctx, filter, model.Page{
|
|
Number: 1,
|
|
Size: int32(CreationPhaseMapfixesLimit),
|
|
},datastore.ListSortDisabled)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if CreationPhaseMapfixesLimit <= len(creation_mapfixes) {
|
|
return nil, ErrCreationPhaseMapfixesLimit
|
|
}
|
|
}
|
|
|
|
// Check if TargetAssetID actually exists
|
|
{
|
|
_, err := svc.Client.Get(ctx, &maps.IdMessage{
|
|
ID: request.TargetAssetID,
|
|
})
|
|
if err != nil {
|
|
// TODO: match specific does not exist grpc error
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
// Check if too many operations have been created recently
|
|
{
|
|
count, err := svc.DB.Operations().CountSince(ctx,
|
|
int64(userId),
|
|
time.Now().Add(-CreateMapfixRecencyWindow),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if CreateMapfixRateLimit < count {
|
|
return nil, ErrCreateMapfixRateLimit
|
|
}
|
|
}
|
|
|
|
operation, err := svc.DB.Operations().Create(ctx, model.Operation{
|
|
Owner: userId,
|
|
StatusID: model.OperationStatusCreated,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
create_request := model.CreateMapfixRequest{
|
|
OperationID: operation.ID,
|
|
ModelID: ModelID,
|
|
TargetAssetID: TargetAssetID,
|
|
Description: request.Description,
|
|
}
|
|
|
|
j, err := json.Marshal(create_request)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
_, err = svc.Nats.Publish("maptest.mapfixes.create", []byte(j))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &api.OperationID{
|
|
OperationID: operation.ID,
|
|
}, nil
|
|
}
|
|
|
|
// GetMapfix implements getMapfix operation.
|
|
//
|
|
// Retrieve map with ID.
|
|
//
|
|
// GET /mapfixes/{MapfixID}
|
|
func (svc *Service) GetMapfix(ctx context.Context, params api.GetMapfixParams) (*api.Mapfix, error) {
|
|
mapfix, err := svc.DB.Mapfixes().Get(ctx, params.MapfixID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &api.Mapfix{
|
|
ID: mapfix.ID,
|
|
DisplayName: mapfix.DisplayName,
|
|
Creator: mapfix.Creator,
|
|
GameID: int32(mapfix.GameID),
|
|
CreatedAt: mapfix.CreatedAt.Unix(),
|
|
UpdatedAt: mapfix.UpdatedAt.Unix(),
|
|
Submitter: int64(mapfix.Submitter),
|
|
AssetID: int64(mapfix.AssetID),
|
|
AssetVersion: int64(mapfix.AssetVersion),
|
|
Completed: mapfix.Completed,
|
|
TargetAssetID: int64(mapfix.TargetAssetID),
|
|
StatusID: int32(mapfix.StatusID),
|
|
Description: mapfix.Description,
|
|
}, nil
|
|
}
|
|
|
|
// ListMapfixes implements listMapfixes operation.
|
|
//
|
|
// Get list of mapfixes.
|
|
//
|
|
// GET /mapfixes
|
|
func (svc *Service) ListMapfixes(ctx context.Context, params api.ListMapfixesParams) (*api.Mapfixes, error) {
|
|
filter := datastore.Optional()
|
|
|
|
if params.DisplayName.IsSet(){
|
|
filter.Add("display_name", params.DisplayName.Value)
|
|
}
|
|
if params.Creator.IsSet(){
|
|
filter.Add("creator", params.Creator.Value)
|
|
}
|
|
if params.GameID.IsSet(){
|
|
filter.Add("game_id", params.GameID.Value)
|
|
}
|
|
if params.Submitter.IsSet(){
|
|
filter.Add("submitter", params.Submitter.Value)
|
|
}
|
|
if params.AssetID.IsSet(){
|
|
filter.Add("asset_id", params.AssetID.Value)
|
|
}
|
|
if params.TargetAssetID.IsSet(){
|
|
filter.Add("target_asset_id", params.TargetAssetID.Value)
|
|
}
|
|
if params.StatusID.IsSet(){
|
|
filter.Add("status_id", params.StatusID.Value)
|
|
}
|
|
|
|
sort := datastore.ListSort(params.Sort.Or(int32(datastore.ListSortDisabled)))
|
|
|
|
total, items, err := svc.DB.Mapfixes().ListWithTotal(ctx, filter, model.Page{
|
|
Number: params.Page,
|
|
Size: params.Limit,
|
|
},sort)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var resp api.Mapfixes
|
|
resp.Total=total
|
|
for _, item := range items {
|
|
resp.Mapfixes = append(resp.Mapfixes, api.Mapfix{
|
|
ID: item.ID,
|
|
DisplayName: item.DisplayName,
|
|
Creator: item.Creator,
|
|
GameID: int32(item.GameID),
|
|
CreatedAt: item.CreatedAt.Unix(),
|
|
UpdatedAt: item.UpdatedAt.Unix(),
|
|
Submitter: int64(item.Submitter),
|
|
AssetID: int64(item.AssetID),
|
|
AssetVersion: int64(item.AssetVersion),
|
|
Completed: item.Completed,
|
|
TargetAssetID: int64(item.TargetAssetID),
|
|
StatusID: int32(item.StatusID),
|
|
Description: item.Description,
|
|
})
|
|
}
|
|
|
|
return &resp, nil
|
|
}
|
|
|
|
// PatchMapfixCompleted implements patchMapfixCompleted operation.
|
|
//
|
|
// Retrieve map with ID.
|
|
//
|
|
// POST /mapfixes/{MapfixID}/completed
|
|
func (svc *Service) SetMapfixCompleted(ctx context.Context, params api.SetMapfixCompletedParams) error {
|
|
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
|
|
if !ok {
|
|
return ErrUserInfo
|
|
}
|
|
|
|
has_role, err := userInfo.HasRoleMaptest()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// check if caller has MaptestGame role (request must originate from a maptest roblox game)
|
|
if !has_role {
|
|
return ErrPermissionDeniedNeedRoleMaptest
|
|
}
|
|
|
|
pmap := datastore.Optional()
|
|
pmap.Add("completed", true)
|
|
return svc.DB.Mapfixes().Update(ctx, params.MapfixID, pmap)
|
|
}
|
|
|
|
// UpdateMapfixModel implements patchMapfixModel operation.
|
|
//
|
|
// Update model following role restrictions.
|
|
//
|
|
// POST /mapfixes/{MapfixID}/model
|
|
func (svc *Service) UpdateMapfixModel(ctx context.Context, params api.UpdateMapfixModelParams) error {
|
|
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
|
|
if !ok {
|
|
return ErrUserInfo
|
|
}
|
|
|
|
// read mapfix (this could be done with a transaction WHERE clause)
|
|
mapfix, err := svc.DB.Mapfixes().Get(ctx, params.MapfixID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
userId, err := userInfo.GetUserID()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// check if caller is the submitter
|
|
has_role := userId == mapfix.Submitter
|
|
if !has_role {
|
|
return ErrPermissionDeniedNotSubmitter
|
|
}
|
|
|
|
OldModelID := mapfix.AssetID
|
|
OldModelVersion := mapfix.AssetVersion
|
|
NewModelID := uint64(params.ModelID)
|
|
NewModelVersion := uint64(params.ModelVersion)
|
|
|
|
// check if Status is ChangesRequested|Submitted|UnderConstruction
|
|
pmap := datastore.Optional()
|
|
pmap.Add("asset_id", NewModelID)
|
|
pmap.Add("asset_version", NewModelVersion)
|
|
//always reset completed when model changes
|
|
pmap.Add("completed", false)
|
|
err = svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusChangesRequested, model.MapfixStatusSubmitted, model.MapfixStatusUnderConstruction}, pmap)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
event_data := model.AuditEventDataChangeModel{
|
|
OldModelID: OldModelID,
|
|
OldModelVersion: OldModelVersion,
|
|
NewModelID: NewModelID,
|
|
NewModelVersion: NewModelVersion,
|
|
}
|
|
|
|
EventData, err := json.Marshal(event_data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = svc.DB.AuditEvents().Create(ctx, model.AuditEvent{
|
|
ID: 0,
|
|
User: userId,
|
|
ResourceType: model.ResourceMapfix,
|
|
ResourceID: params.MapfixID,
|
|
EventType: model.AuditEventTypeChangeModel,
|
|
EventData: EventData,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ActionMapfixReject invokes actionMapfixReject operation.
|
|
//
|
|
// Role Reviewer changes status from Submitted -> Rejected.
|
|
//
|
|
// POST /mapfixes/{MapfixID}/status/reject
|
|
func (svc *Service) ActionMapfixReject(ctx context.Context, params api.ActionMapfixRejectParams) error {
|
|
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
|
|
if !ok {
|
|
return ErrUserInfo
|
|
}
|
|
|
|
has_role, err := userInfo.HasRoleMapfixReview()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// check if caller has required role
|
|
if !has_role {
|
|
return ErrPermissionDeniedNeedRoleMapfixReview
|
|
}
|
|
|
|
userId, err := userInfo.GetUserID()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// transaction
|
|
target_status := model.MapfixStatusRejected
|
|
smap := datastore.Optional()
|
|
smap.Add("status_id", target_status)
|
|
err = svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusSubmitted}, 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: userId,
|
|
ResourceType: model.ResourceMapfix,
|
|
ResourceID: params.MapfixID,
|
|
EventType: model.AuditEventTypeAction,
|
|
EventData: EventData,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ActionMapfixRequestChanges invokes actionMapfixRequestChanges operation.
|
|
//
|
|
// Role Reviewer changes status from Validated|Accepted|Submitted -> ChangesRequested.
|
|
//
|
|
// POST /mapfixes/{MapfixID}/status/request-changes
|
|
func (svc *Service) ActionMapfixRequestChanges(ctx context.Context, params api.ActionMapfixRequestChangesParams) error {
|
|
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
|
|
if !ok {
|
|
return ErrUserInfo
|
|
}
|
|
|
|
has_role, err := userInfo.HasRoleMapfixReview()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// check if caller has required role
|
|
if !has_role {
|
|
return ErrPermissionDeniedNeedRoleMapfixReview
|
|
}
|
|
|
|
// transaction
|
|
smap := datastore.Optional()
|
|
smap.Add("status_id", model.MapfixStatusChangesRequested)
|
|
return svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidated, model.MapfixStatusAcceptedUnvalidated, model.MapfixStatusSubmitted}, smap)
|
|
}
|
|
|
|
// ActionMapfixRevoke invokes actionMapfixRevoke operation.
|
|
//
|
|
// Role Submitter changes status from Submitted|ChangesRequested -> UnderConstruction.
|
|
//
|
|
// POST /mapfixes/{MapfixID}/status/revoke
|
|
func (svc *Service) ActionMapfixRevoke(ctx context.Context, params api.ActionMapfixRevokeParams) error {
|
|
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
|
|
if !ok {
|
|
return ErrUserInfo
|
|
}
|
|
|
|
// read mapfix (this could be done with a transaction WHERE clause)
|
|
mapfix, err := svc.DB.Mapfixes().Get(ctx, params.MapfixID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
userId, err := userInfo.GetUserID()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// check if caller is the submitter
|
|
has_role := userId == mapfix.Submitter
|
|
if !has_role {
|
|
return ErrPermissionDeniedNotSubmitter
|
|
}
|
|
|
|
// transaction
|
|
target_status := model.MapfixStatusUnderConstruction
|
|
smap := datastore.Optional()
|
|
smap.Add("status_id", target_status)
|
|
err = svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusSubmitted, model.MapfixStatusChangesRequested}, 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: userId,
|
|
ResourceType: model.ResourceMapfix,
|
|
ResourceID: params.MapfixID,
|
|
EventType: model.AuditEventTypeAction,
|
|
EventData: EventData,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ActionMapfixTriggerSubmit invokes actionMapfixTriggerSubmit operation.
|
|
//
|
|
// Role Submitter changes status from UnderConstruction|ChangesRequested -> Submitting.
|
|
//
|
|
// POST /mapfixes/{MapfixID}/status/trigger-submit
|
|
func (svc *Service) ActionMapfixTriggerSubmit(ctx context.Context, params api.ActionMapfixTriggerSubmitParams) error {
|
|
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
|
|
if !ok {
|
|
return ErrUserInfo
|
|
}
|
|
|
|
// read mapfix (this could be done with a transaction WHERE clause)
|
|
mapfix, err := svc.DB.Mapfixes().Get(ctx, params.MapfixID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
userId, err := userInfo.GetUserID()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// check if caller is the submitter
|
|
has_role := userId == mapfix.Submitter
|
|
if !has_role {
|
|
return ErrPermissionDeniedNotSubmitter
|
|
}
|
|
|
|
// transaction
|
|
target_status := model.MapfixStatusSubmitting
|
|
smap := datastore.Optional()
|
|
smap.Add("status_id", target_status)
|
|
err = svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusUnderConstruction, model.MapfixStatusChangesRequested}, smap)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
validate_request := model.CheckMapfixRequest{
|
|
MapfixID: mapfix.ID,
|
|
ModelID: mapfix.AssetID,
|
|
}
|
|
|
|
j, err := json.Marshal(validate_request)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = svc.Nats.Publish("maptest.mapfixes.check", []byte(j))
|
|
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: userId,
|
|
ResourceType: model.ResourceMapfix,
|
|
ResourceID: params.MapfixID,
|
|
EventType: model.AuditEventTypeAction,
|
|
EventData: EventData,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ActionMapfixResetSubmitting implements actionMapfixResetSubmitting operation.
|
|
//
|
|
// Role MapfixReview changes status from Submitting -> UnderConstruction.
|
|
//
|
|
// POST /mapfixes/{MapfixID}/status/reset-submitting
|
|
func (svc *Service) ActionMapfixResetSubmitting(ctx context.Context, params api.ActionMapfixResetSubmittingParams) error {
|
|
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
|
|
if !ok {
|
|
return ErrUserInfo
|
|
}
|
|
|
|
userId, err := userInfo.GetUserID()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// check when mapfix was updated
|
|
mapfix, err := svc.DB.Mapfixes().Get(ctx, params.MapfixID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if time.Now().Before(mapfix.UpdatedAt.Add(time.Second*10)) {
|
|
// the last time the mapfix was updated must be longer than 10 seconds ago
|
|
return ErrDelayReset
|
|
}
|
|
|
|
// check if caller has required role
|
|
has_role := userId == mapfix.Submitter
|
|
if !has_role {
|
|
return ErrPermissionDeniedNotSubmitter
|
|
}
|
|
|
|
// transaction
|
|
target_status := model.MapfixStatusUnderConstruction
|
|
smap := datastore.Optional()
|
|
smap.Add("status_id", target_status)
|
|
err = svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusSubmitting}, 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: userId,
|
|
ResourceType: model.ResourceMapfix,
|
|
ResourceID: params.MapfixID,
|
|
EventType: model.AuditEventTypeAction,
|
|
EventData: EventData,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ActionMapfixTriggerUpload invokes actionMapfixTriggerUpload operation.
|
|
//
|
|
// Role Admin changes status from Validated -> Uploading.
|
|
//
|
|
// POST /mapfixes/{MapfixID}/status/trigger-upload
|
|
func (svc *Service) ActionMapfixTriggerUpload(ctx context.Context, params api.ActionMapfixTriggerUploadParams) error {
|
|
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
|
|
if !ok {
|
|
return ErrUserInfo
|
|
}
|
|
|
|
has_role, err := userInfo.HasRoleMapfixUpload()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// check if caller has required role
|
|
if !has_role {
|
|
return ErrPermissionDeniedNeedRoleMapfixUpload
|
|
}
|
|
|
|
userId, err := userInfo.GetUserID()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// transaction
|
|
target_status := model.MapfixStatusUploading
|
|
smap := datastore.Optional()
|
|
smap.Add("status_id", target_status)
|
|
mapfix, err := svc.DB.Mapfixes().IfStatusThenUpdateAndGet(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidated}, smap)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// this is a map fix
|
|
upload_fix_request := model.UploadMapfixRequest{
|
|
MapfixID: mapfix.ID,
|
|
ModelID: mapfix.ValidatedAssetID,
|
|
ModelVersion: mapfix.ValidatedAssetVersion,
|
|
TargetAssetID: mapfix.TargetAssetID,
|
|
}
|
|
|
|
j, err := json.Marshal(upload_fix_request)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = svc.Nats.Publish("maptest.mapfixes.upload", []byte(j))
|
|
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: userId,
|
|
ResourceType: model.ResourceMapfix,
|
|
ResourceID: params.MapfixID,
|
|
EventType: model.AuditEventTypeAction,
|
|
EventData: EventData,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ActionMapfixValidate invokes actionMapfixValidate operation.
|
|
//
|
|
// Role MapfixRelease changes status from Uploading -> Validated.
|
|
//
|
|
// POST /mapfixes/{MapfixID}/status/reset-uploading
|
|
func (svc *Service) ActionMapfixValidated(ctx context.Context, params api.ActionMapfixValidatedParams) error {
|
|
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
|
|
if !ok {
|
|
return ErrUserInfo
|
|
}
|
|
|
|
has_role, err := userInfo.HasRoleMapfixUpload()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// check if caller has required role
|
|
if !has_role {
|
|
return ErrPermissionDeniedNeedRoleMapfixUpload
|
|
}
|
|
|
|
userId, err := userInfo.GetUserID()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// check when mapfix was updated
|
|
mapfix, err := svc.DB.Mapfixes().Get(ctx, params.MapfixID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if time.Now().Before(mapfix.UpdatedAt.Add(time.Second*10)) {
|
|
// the last time the mapfix was updated must be longer than 10 seconds ago
|
|
return ErrDelayReset
|
|
}
|
|
|
|
// transaction
|
|
target_status := model.MapfixStatusValidated
|
|
smap := datastore.Optional()
|
|
smap.Add("status_id", target_status)
|
|
err = svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusUploading}, 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: userId,
|
|
ResourceType: model.ResourceMapfix,
|
|
ResourceID: params.MapfixID,
|
|
EventType: model.AuditEventTypeAction,
|
|
EventData: EventData,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ActionMapfixTriggerValidate invokes actionMapfixTriggerValidate operation.
|
|
//
|
|
// Role Reviewer triggers validation and changes status from Submitted -> Validating.
|
|
//
|
|
// POST /mapfixes/{MapfixID}/status/trigger-validate
|
|
func (svc *Service) ActionMapfixTriggerValidate(ctx context.Context, params api.ActionMapfixTriggerValidateParams) error {
|
|
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
|
|
if !ok {
|
|
return ErrUserInfo
|
|
}
|
|
|
|
has_role, err := userInfo.HasRoleMapfixReview()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// check if caller has required role
|
|
if !has_role {
|
|
return ErrPermissionDeniedNeedRoleMapfixReview
|
|
}
|
|
|
|
// read mapfix (this could be done with a transaction WHERE clause)
|
|
mapfix, err := svc.DB.Mapfixes().Get(ctx, params.MapfixID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
userId, err := userInfo.GetUserID()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// check if caller is NOT the submitter
|
|
has_role = userId == mapfix.Submitter
|
|
if has_role {
|
|
return ErrAcceptOwnMapfix
|
|
}
|
|
|
|
// Check if an active mapfix with the same target asset id exists
|
|
if mapfix.TargetAssetID != 0 {
|
|
filter := datastore.Optional()
|
|
filter.Add("target_asset_id", mapfix.TargetAssetID)
|
|
filter.Add("status_id", ActiveAcceptedMapfixStatuses)
|
|
active_mapfixes, err := svc.DB.Mapfixes().List(ctx, filter, model.Page{
|
|
Number: 1,
|
|
Size: 1,
|
|
},datastore.ListSortDisabled)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(active_mapfixes) != 0{
|
|
return ErrActiveMapfixSameTargetAssetID
|
|
}
|
|
}
|
|
|
|
// transaction
|
|
target_status := model.MapfixStatusValidating
|
|
smap := datastore.Optional()
|
|
smap.Add("status_id", target_status)
|
|
mapfix, err = svc.DB.Mapfixes().IfStatusThenUpdateAndGet(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusSubmitted}, smap)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
validate_request := model.ValidateMapfixRequest{
|
|
MapfixID: mapfix.ID,
|
|
ModelID: mapfix.AssetID,
|
|
ModelVersion: mapfix.AssetVersion,
|
|
ValidatedModelID: nil,
|
|
}
|
|
|
|
// sentinel values because we're not using rust
|
|
if mapfix.ValidatedAssetID != 0 {
|
|
validate_request.ValidatedModelID = &mapfix.ValidatedAssetID
|
|
}
|
|
|
|
j, err := json.Marshal(validate_request)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = svc.Nats.Publish("maptest.mapfixes.validate", []byte(j))
|
|
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: userId,
|
|
ResourceType: model.ResourceMapfix,
|
|
ResourceID: params.MapfixID,
|
|
EventType: model.AuditEventTypeAction,
|
|
EventData: EventData,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ActionMapfixRetryValidate invokes actionMapfixRetryValidate operation.
|
|
//
|
|
// Role Reviewer re-runs validation and changes status from Accepted -> Validating.
|
|
//
|
|
// POST /mapfixes/{MapfixID}/status/retry-validate
|
|
func (svc *Service) ActionMapfixRetryValidate(ctx context.Context, params api.ActionMapfixRetryValidateParams) error {
|
|
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
|
|
if !ok {
|
|
return ErrUserInfo
|
|
}
|
|
|
|
has_role, err := userInfo.HasRoleMapfixReview()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// check if caller has required role
|
|
if !has_role {
|
|
return ErrPermissionDeniedNeedRoleMapfixReview
|
|
}
|
|
|
|
userId, err := userInfo.GetUserID()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// transaction
|
|
target_status := model.MapfixStatusValidating
|
|
smap := datastore.Optional()
|
|
smap.Add("status_id", target_status)
|
|
mapfix, err := svc.DB.Mapfixes().IfStatusThenUpdateAndGet(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusAcceptedUnvalidated}, smap)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
validate_request := model.ValidateMapfixRequest{
|
|
MapfixID: mapfix.ID,
|
|
ModelID: mapfix.AssetID,
|
|
ModelVersion: mapfix.AssetVersion,
|
|
ValidatedModelID: nil,
|
|
}
|
|
|
|
// sentinel values because we're not using rust
|
|
if mapfix.ValidatedAssetID != 0 {
|
|
validate_request.ValidatedModelID = &mapfix.ValidatedAssetID
|
|
}
|
|
|
|
j, err := json.Marshal(validate_request)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = svc.Nats.Publish("maptest.mapfixes.validate", []byte(j))
|
|
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: userId,
|
|
ResourceType: model.ResourceMapfix,
|
|
ResourceID: params.MapfixID,
|
|
EventType: model.AuditEventTypeAction,
|
|
EventData: EventData,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ActionMapfixAccepted implements actionMapfixAccepted operation.
|
|
//
|
|
// Role MapfixReview changes status from Validating -> Accepted.
|
|
//
|
|
// POST /mapfixes/{MapfixID}/status/reset-validating
|
|
func (svc *Service) ActionMapfixAccepted(ctx context.Context, params api.ActionMapfixAcceptedParams) error {
|
|
userInfo, ok := ctx.Value("UserInfo").(UserInfoHandle)
|
|
if !ok {
|
|
return ErrUserInfo
|
|
}
|
|
|
|
has_role, err := userInfo.HasRoleMapfixReview()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
// check if caller has required role
|
|
if !has_role {
|
|
return ErrPermissionDeniedNeedRoleMapfixReview
|
|
}
|
|
|
|
userId, err := userInfo.GetUserID()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// check when mapfix was updated
|
|
mapfix, err := svc.DB.Mapfixes().Get(ctx, params.MapfixID)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if time.Now().Before(mapfix.UpdatedAt.Add(time.Second*10)) {
|
|
// the last time the mapfix was updated must be longer than 10 seconds ago
|
|
return ErrDelayReset
|
|
}
|
|
|
|
// transaction
|
|
target_status := model.MapfixStatusAcceptedUnvalidated
|
|
smap := datastore.Optional()
|
|
smap.Add("status_id", target_status)
|
|
err = svc.DB.Mapfixes().IfStatusThenUpdate(ctx, params.MapfixID, []model.MapfixStatus{model.MapfixStatusValidating}, 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: userId,
|
|
ResourceType: model.ResourceMapfix,
|
|
ResourceID: params.MapfixID,
|
|
EventType: model.AuditEventTypeAction,
|
|
EventData: EventData,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|