public async Task <SimulationApiModel> PatchAsync( string id, [FromBody] SimulationPatchApiModel patch) { if (patch == null) { this.log.Warn("NULL patch provided", () => new { id }); throw new BadRequestException("No data or invalid data provided"); } SimulationPatch patchServiceModel = patch.ToServiceModel(id); if (patchServiceModel.Enabled == false) { patchServiceModel.Statistics = new SimulationStatistics { AverageMessagesPerSecond = this.rateReporter.GetThroughputForMessages(), TotalMessagesSent = this.simulationRunner.TotalMessagesCount }; } var simulation = await this.simulationsService.MergeAsync(patchServiceModel); return(SimulationApiModel.FromServiceModel( simulation, this.servicesConfig, this.deploymentConfig, this.connectionStringManager, this.simulationRunner, this.rateReporter)); }
public async Task <SimulationApiModel> PatchAsync(string id, [FromBody] SimulationPatchApiModel patch) { if (patch == null) { this.log.Warn("NULL patch provided", () => new { id }); throw new BadRequestException("No data or invalid data provided"); } SimulationPatch patchServiceModel = patch.ToServiceModel(id); var simulation = await this.simulationsService.MergeAsync(patchServiceModel); return(SimulationApiModel.FromServiceModel(simulation)); }
public async Task <Models.Simulation> MergeAsync(SimulationPatch patch) { if (patch.Id != SIMULATION_ID) { this.log.Warn("Invalid simulation ID.", () => new { patch.Id }); throw new InvalidInputException("Invalid simulation ID. Use ID '" + SIMULATION_ID + "'."); } var item = await this.storage.GetAsync(STORAGE_COLLECTION, patch.Id); var simulation = JsonConvert.DeserializeObject <Models.Simulation>(item.Data); simulation.Etag = item.ETag; // Even when there's nothing to do, verify the etag mismatch if (patch.Etag != simulation.Etag) { this.log.Warn("Etag mismatch", () => new { Current = simulation.Etag, Provided = patch.Etag }); throw new ConflictingResourceException( $"The ETag provided doesn't match the current resource ETag ({simulation.Etag})."); } if (!patch.Enabled.HasValue || patch.Enabled.Value == simulation.Enabled) { // Nothing to do return(simulation); } simulation.Enabled = patch.Enabled.Value; simulation.Modified = DateTimeOffset.UtcNow; simulation.Version += 1; item = await this.storage.UpdateAsync( STORAGE_COLLECTION, SIMULATION_ID, JsonConvert.SerializeObject(simulation), patch.Etag); simulation.Etag = item.ETag; return(simulation); }
/// <summary> /// Modify a simulation. /// </summary> public async Task <Models.Simulation> MergeAsync(SimulationPatch patch) { if (string.IsNullOrEmpty(patch.Id)) { this.log.Warn("Invalid simulation ID.", () => new { patch.Id }); throw new InvalidInputException("Invalid simulation ID."); } var item = await this.simulationsStorage.GetAsync(patch.Id); var simulation = JsonConvert.DeserializeObject <Models.Simulation>(item.Data); simulation.ETag = item.ETag; simulation.Id = item.Id; // Even when there's nothing to do, verify the ETag mismatch if (patch.ETag != simulation.ETag) { this.log.Warn("ETag mismatch", () => new { Current = simulation.ETag, Provided = patch.ETag }); throw new ConflictingResourceException( $"The ETag provided doesn't match the current resource ETag ({simulation.ETag})."); } if (!patch.Enabled.HasValue || patch.Enabled.Value == simulation.Enabled) { // Nothing to do return(simulation); } simulation.Enabled = patch.Enabled.Value; if (patch.Enabled == false) { simulation.StoppedTime = simulation.Modified; simulation.Statistics = new Models.Simulation.StatisticsRef { AverageMessagesPerSecond = patch.Statistics.AverageMessagesPerSecond, TotalMessagesSent = patch.Statistics.TotalMessagesSent }; // When a simulation is disabled, its partitions are deleted - this triggers the deletion if (!simulation.Enabled) { simulation.PartitioningComplete = false; } } item = await this.simulationsStorage.UpsertAsync( new StorageRecord { Id = simulation.Id, Data = JsonConvert.SerializeObject(simulation) }, patch.ETag ); simulation.ETag = item.ETag; return(simulation); }
/// <summary> /// Modify some simulation details /// </summary> public async Task <Models.Simulation> MergeAsync(SimulationPatch patch) { if (string.IsNullOrEmpty(patch.Id)) { this.log.Warn("Invalid simulation ID.", () => new { patch.Id }); throw new InvalidInputException("Invalid simulation ID."); } var item = await this.simulationsStorage.GetAsync(patch.Id); var simulation = JsonConvert.DeserializeObject <Models.Simulation>(item.Data); simulation.ETag = item.ETag; simulation.Id = item.Id; // Even when there's nothing to do, verify the ETag mismatch if (patch.ETag != simulation.ETag) { this.log.Warn("ETag mismatch", () => new { Current = simulation.ETag, Provided = patch.ETag }); throw new ConflictingResourceException( $"The ETag provided doesn't match the current resource ETag ({simulation.ETag})."); } // Define the user intent looking at the simulation status and the patch content var simulationIsRestarting = !simulation.Enabled && patch.Enabled.HasValue && patch.Enabled.Value; var simulationIsStopping = simulation.Enabled && patch.Enabled.HasValue && !patch.Enabled.Value; // Note: this code is also in UpsertAsync // (consider refactoring it out unless the code becomes harder to follow) if (simulationIsRestarting) { simulation = await this.ResetSimulationStatisticsAsync(simulation); } else if (simulationIsStopping) { simulation.StoppedTime = DateTimeOffset.UtcNow; // When a simulation is disabled, its partitions are deleted. // This boolean triggers the deletion of partitions from the storage // in the partitioning agent. simulation.PartitioningComplete = false; } // The Enabled field is optional, e.g. in case PATCH is extended to // modify other fields, so we need to check for null if (patch.Enabled != null) { simulation.Enabled = patch.Enabled.Value; } // TODO: can we use this.SaveAsync() here too and avoid the duplication? item = await this.simulationsStorage.UpsertAsync( new StorageRecord { Id = simulation.Id, Data = JsonConvert.SerializeObject(simulation) }, patch.ETag ); simulation.ETag = item.ETag; return(simulation); }