protected override void ConfigImpl() { CommandPhase1 <CreateAppCommand>("CreateApp-Phase 1", obs => obs.Do(m => m.Reporter.Send(DeploymentMessages.RegisterRepository)) .Select(m => Msg.New(new RegisterRepository(m.Event.TargetRepo, true), m)), (command, reporter, op) => new ContinueCreateApp(op, command, reporter)); CommandPhase2 <ContinueCreateApp, CreateAppCommand, AppInfo>("CreateApp-Phase 2", obs => obs.ConditionalSelect() .ToResult <AppInfo?>(b => { b.When(m => !m.Result.Ok, o => o.ApplyWhen(m => !m.Reporter.IsCompled, data => data.Reporter.Compled(OperationResult.Failure(data.Result.Error ?? BuildErrorCodes.CommandErrorRegisterRepository))) .Select(_ => default(AppInfo))); b.When(m => m.AppData != null, o => o.Do(m => m.Reporter.Compled(OperationResult.Failure(BuildErrorCodes.CommandDuplicateApp))) .Select(_ => default(AppInfo))); b.When(m => m.AppData == null, o => o.Select(m => { var data = new AppData( ImmutableList <AppFileInfo> .Empty, m.Command.AppName, -1, DateTime.UtcNow, DateTime.MinValue, m.Command.TargetRepo, m.Command.ProjectName); m.State.Apps.Add(data); return(Data: data.ToInfo(), Tracker: m.State.ChangeTracker); }) .Do(i => i.Tracker.Tell(i.Data)) .Select(i => i.Data)); })); DirectCommandPhase1 <PushVersionCommand>("PushVersion-Phase 1", obs => obs.Select(m => (App: m.State.Apps.Get(m.Event.AppName), Data: m)) .ConditionalSelect() .ToResult <Unit>( b => { b.When(m => m.App == null, o => o.ToUnit(d => d.Data.Reporter.Compled(OperationResult.Failure(BuildErrorCodes.CommandAppNotFound)))); b.When(m => m.App != null, o => o.ToUnit(d => BuildRequest.SendWork(d.Data.State.WorkDistributor, d.Data.Reporter, d.App, d.Data.State.Repository, BuildEnv.TempFiles.CreateFile()) .PipeTo(Self, success: c => new ContinuePushNewVersion(OperationResult.Success(c), d.Data.Event, d.Data.Reporter), failure: e => new ContinuePushNewVersion(OperationResult.Failure(e), d.Data.Event, d.Data.Reporter)))); })); CommandPhase2 <ContinuePushNewVersion, PushVersionCommand, AppBinary>("PushVersion-Phase 2", obs => obs.ConditionalSelect() .ToResult <AppBinary?>( b => { b.When(m => m.AppData == null, o => o.Do(m => m.Reporter.Compled(OperationResult.Failure(BuildErrorCodes.CommandAppNotFound))) .Select(_ => default(AppBinary))); b.When(m => m.AppData != null, o => o.SelectMany(UpdateAppData));
public AppCommandProcessor(IMongoCollection <AppData> apps, GridFSBucket files, RepositoryApi repository, DataTransferManager dataTransfer, IMongoCollection <ToDeleteRevision> toDelete, WorkDistributor <BuildRequest, BuildCompled> workDistributor, IActorRef changeTracker) { _apps = apps; CommandPhase1 <CreateAppCommand>("CreateApp", repository, (command, reporter) => { reporter.Send(DeploymentMessages.RegisterRepository); return(new RegisterRepository(command.TargetRepo) { IgnoreDuplicate = true }); }, (command, reporter, op) => new ContinueCreateApp(op, command, reporter)); CommandPhase2 <ContinueCreateApp, CreateAppCommand, AppInfo>("CreateApp2", (command, result, reporter, data) => { if (!result.Ok) { if (reporter.IsCompled) { return(null); } reporter.Compled(OperationResult.Failure(result.Error ?? BuildErrorCodes.CommandErrorRegisterRepository)); return(null); } if (data != null) { reporter.Compled(OperationResult.Failure(BuildErrorCodes.CommandDuplicateApp)); return(null); } var newData = new AppData(command.AppName, -1, DateTime.UtcNow, DateTime.MinValue, command.TargetRepo, command.ProjectName, ImmutableList <AppFileInfo> .Empty); apps.InsertOne(newData); var info = newData.ToInfo(); changeTracker.Tell(info); return(info); }); CommandPhase1 <PushVersionCommand>("PushVersion", (command, reporter) => { var data = apps.AsQueryable().FirstOrDefault(ad => ad.Name == command.AppName); if (data == null) { reporter.Compled(OperationResult.Failure(BuildErrorCodes.CommandAppNotFound)); } else { BuildRequest.SendWork(workDistributor, reporter, data, repository, BuildEnv.TempFiles.CreateFile()) .PipeTo(Self, success: c => new ContinuePushNewVersion(OperationResult.Success(c), command, reporter), failure: e => new ContinuePushNewVersion(OperationResult.Failure(e.Unwrap()?.Message ?? "Cancel"), command, reporter)); } }); CommandPhase2 <ContinuePushNewVersion, PushVersionCommand, AppBinary>("PushVersion2", (command, result, reporter, data) => { if (data == null) { if (!reporter.IsCompled) { reporter.Compled(OperationResult.Failure(BuildErrorCodes.CommandAppNotFound)); } return(null); } if (!result.Ok) { return(null); } using var transaction = apps.Database.Client.StartSession(new ClientSessionOptions { DefaultTransactionOptions = new TransactionOptions(writeConcern: WriteConcern.Acknowledged) }); var dataFilter = Builders <AppData> .Filter.Eq(ad => ad.Name, data.Name); var(commit, fileName) = ((string, ITempFile))result.Outcome !; using var targetStream = fileName; var newId = files.UploadFromStream(data.Name + ".zip", targetStream.Stream); var newBinary = new AppFileInfo(newId, data.Last + 1, DateTime.UtcNow, false, commit); var newBinarys = data.Versions.Add(newBinary); var definition = Builders <AppData> .Update; var updates = new List <UpdateDefinition <AppData> > { definition.Set(ad => ad.Last, newBinary.Version), definition.Set(ad => ad.Versions, newBinarys) }; var deleteUpdates = new List <ToDeleteRevision>(); if (data.Versions.Count(s => !s.Deleted) > 5) { foreach (var info in newBinarys.OrderByDescending(i => i.CreationTime).Skip(5)) { if (info.Deleted) { continue; } info.Deleted = true; deleteUpdates.Add(new ToDeleteRevision(info.File.ToString())); } } transaction.StartTransaction(); if (deleteUpdates.Count != 0) { toDelete.InsertMany(transaction, deleteUpdates); } if (!apps.UpdateOne(transaction, dataFilter, definition.Combine(updates)).IsAcknowledged) { transaction.AbortTransaction(); reporter.Compled(OperationResult.Failure(BuildErrorCodes.DatabaseError)); return(null); } transaction.CommitTransaction(); changeTracker.Tell(_apps.AsQueryable().FirstOrDefault(ad => ad.Name == command.AppName)); return(new AppBinary(command.AppName, newBinary.Version, newBinary.CreationTime, false, newBinary.Commit, data.Repository)); });