Skip to content

Commit

Permalink
Applications and Test data source can be deleted now
Browse files Browse the repository at this point in the history
  • Loading branch information
Nfactor26 committed Dec 19, 2022
1 parent 72cca5a commit bdf9007
Show file tree
Hide file tree
Showing 26 changed files with 315 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
using Pixel.Persistence.Services.Client;
using Serilog;
using System.ComponentModel;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;

Expand Down Expand Up @@ -105,7 +106,10 @@ public ApplicationExplorerViewModel(IEventAggregator eventAggregator, IApplicati
var applications = this.applicationDataManager.GetAllApplications();
foreach (var application in applications)
{
this.Applications.Add(new ApplicationDescriptionViewModel(application));
if(!application.IsDeleted)
{
this.Applications.Add(new ApplicationDescriptionViewModel(application));
}
}
Applications.OrderBy(a => a.ApplicationName);
}
Expand Down Expand Up @@ -225,6 +229,29 @@ public async Task SaveApplicationAsync(ApplicationDescriptionViewModel applicati
this.Applications.Add(applicationDescriptionViewModel);
}

/// <summary>
/// Mark the application as deleted and remove from application explorer views
/// </summary>
/// <param name="applicationDescriptionViewModel"></param>
/// <returns></returns>
public async Task DeleteApplicationAsync(ApplicationDescriptionViewModel applicationDescriptionViewModel)
{
Guard.Argument(applicationDescriptionViewModel, nameof(applicationDescriptionViewModel)).NotNull();
try
{
MessageBoxResult result = MessageBox.Show("Are you sure you want to delete this application?", "Confirm Delete", MessageBoxButton.OKCancel);
if (result == MessageBoxResult.OK)
{
await this.applicationDataManager.DeleteApplicationAsync(applicationDescriptionViewModel.Model);
this.Applications.Remove(applicationDescriptionViewModel);
}
}
catch (Exception ex)
{
logger.Error(ex, ex.Message);
}
}

/// <summary>
/// Create a new screen for the application. Screens are used to group application controls.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,10 +203,10 @@
<ContextMenu.Template>
<ControlTemplate>
<StackPanel>
<MenuItem x:Name="Open" Header="Open" cal:Action.TargetWithoutContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type Grid}}}" cal:Message.Attach="[Event Click] = [Action OpenApplication($dataContext)]" ></MenuItem>
<!--<MenuItem x:Name="Save" Header="Save" cal:Action.TargetWithoutContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type Grid}}}" cal:Message.Attach="[Event Click] = [Action SaveApplicationAsync($dataContext)]" ></MenuItem>-->
<MenuItem x:Name="Open" Header="Open" cal:Action.TargetWithoutContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type Grid}}}" cal:Message.Attach="[Event Click] = [Action OpenApplication($dataContext)]" ></MenuItem>
<MenuItem x:Name="Edit" Header="Edit" cal:Action.TargetWithoutContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type Grid}}}" cal:Message.Attach="[Event Click] = [Action EditApplicationAsync($dataContext)]" ></MenuItem>
<MenuItem x:Name="Rename" Header="Rename" cal:Action.TargetWithoutContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type Grid}}}" cal:Message.Attach="[Event Click] = [Action ToggleRename($dataContext)]" ></MenuItem>
<MenuItem x:Name="Rename" Header="Rename" cal:Action.TargetWithoutContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type Grid}}}" cal:Message.Attach="[Event Click] = [Action ToggleRename($dataContext)]" ></MenuItem>
<MenuItem x:Name="Delete" Header="Delete" cal:Action.TargetWithoutContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type Grid}}}" cal:Message.Attach="[Event Click] = [Action DeleteApplicationAsync($dataContext)]" ></MenuItem>
</StackPanel>
</ControlTemplate>
</ContextMenu.Template>
Expand Down
6 changes: 6 additions & 0 deletions src/Pixel.Automation.Core/Models/ApplicationDescription.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ public string ApplicationName
[DataMember(Order = 50)]
public Dictionary<string, List<string>> AvailablePrefabs { get; private set; } = new();

/// <summary>
/// Indicates if the TestCase is deleted. Deleted test cases are not loaded in explorer.
/// </summary>
[DataMember(IsRequired = false, Order = 1000)]
public bool IsDeleted { get; set; }

/// <summary>
/// constructor
/// </summary>
Expand Down
1 change: 0 additions & 1 deletion src/Pixel.Automation.Core/TestData/TestCase.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using Pixel.Automation.Core.Attributes;
using Pixel.Automation.Core.Enums;
using System;
using System.Diagnostics.SymbolStore;
using System.Runtime.Serialization;
using System.Text.Json.Serialization;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Pixel.Automation.Core.TestData;
using System.Threading.Tasks;

namespace Pixel.Automation.Editor.Core.Interfaces
{
Expand All @@ -10,17 +11,17 @@ public interface ITestDataRepository
/// <summary>
/// Show the wizard for creating a coded test data source
/// </summary>
void CreateCodedTestDataSource();
Task CreateCodedTestDataSource();

/// <summary>
/// Show the wizard for creating a csv backed test data source
/// </summary>
void CreateCsvTestDataSource();
Task CreateCsvTestDataSource();

/// <summary>
/// Open an existing TestData source for edit
/// </summary>
/// <param name="testDataSource"></param>
void EditDataSource(TestDataSource testDataSource);
Task EditDataSource(TestDataSource testDataSource);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Data;

namespace Pixel.Automation.TestData.Repository.ViewModels
Expand Down Expand Up @@ -87,7 +88,10 @@ private async Task LoadDataSourcesAsync()
await this.testDataManager.DownloadAllTestDataSourcesAsync();
foreach (var testDataSource in this.projectFileSystem.GetTestDataSources())
{
this.TestDataSourceCollection.Add(testDataSource);
if(!testDataSource.IsDeleted)
{
this.TestDataSourceCollection.Add(testDataSource);
}
}
}

Expand Down Expand Up @@ -137,15 +141,15 @@ private void CreateCollectionView()
#region Create Test Data Source

///<inheritdoc/>
public void CreateCodedTestDataSource()
public async Task CreateCodedTestDataSource()
{
_ = CreateDataSource(DataSource.Code);
await CreateDataSource(DataSource.Code);
}

///<inheritdoc/>
public void CreateCsvTestDataSource()
public async Task CreateCsvTestDataSource()
{
_ = CreateDataSource(DataSource.CsvFile);
await CreateDataSource(DataSource.CsvFile);
}

private async Task CreateDataSource(DataSource dataSourceType)
Expand Down Expand Up @@ -222,17 +226,17 @@ private async Task CreateEmptyDataSourceAsync()
#region Edit Test Data Source

///<inheritdoc/>
public void EditDataSource(TestDataSource testDataSource)
public async Task EditDataSource(TestDataSource testDataSource)
{
try
{
switch (testDataSource.DataSource)
{
case DataSource.Code:
EditCodedDataSource(testDataSource);
await EditCodedDataSource(testDataSource);
break;
case DataSource.CsvFile:
EditCsvDataSource(testDataSource);
await EditCsvDataSource(testDataSource);
break;
}
}
Expand All @@ -247,7 +251,7 @@ public void EditDataSource(TestDataSource testDataSource)
/// show script editor screen to edit the script for Code data source
/// </summary>
/// <param name="testDataSource"></param>
private async void EditCodedDataSource(TestDataSource testDataSource)
private async Task EditCodedDataSource(TestDataSource testDataSource)
{
string projectName = testDataSource.DataSourceId;
this.scriptEditorFactory.AddProject(projectName, Array.Empty<string>(), typeof(EmptyModel));
Expand All @@ -265,7 +269,7 @@ private async void EditCodedDataSource(TestDataSource testDataSource)
/// Show the TestDataSource screen to edit the details for CSV data source
/// </summary>
/// <param name="testDataSource"></param>
private async void EditCsvDataSource(TestDataSource testDataSource)
private async Task EditCsvDataSource(TestDataSource testDataSource)
{
TestDataSourceViewModel dataSourceViewModel = new TestDataSourceViewModel(this.windowManager, this.projectFileSystem, testDataSource);
TestDataSourceBuilderViewModel testDataSourceBuilder = new TestDataSourceBuilderViewModel(new IStagedScreen[] { dataSourceViewModel });
Expand All @@ -277,10 +281,34 @@ private async void EditCsvDataSource(TestDataSource testDataSource)
}
}


/// <summary>
/// Mark the test data source as deleted
/// </summary>
/// <param name="testDataSource"></param>
public async Task DeleteDataSource(TestDataSource testDataSource)
{
Guard.Argument(testDataSource, nameof(testDataSource)).NotNull();
try
{
MessageBoxResult result = MessageBox.Show("Are you sure you want to delete data source", "Confirm Delete", MessageBoxButton.OKCancel);
if (result == MessageBoxResult.OK)
{
await this.testDataManager.DeleteTestDataSourceAsync(testDataSource);
this.TestDataSourceCollection.Remove(testDataSource);
logger.Information("TestDataSource : '{0}' was deleted ", testDataSource.Name);
}
}
catch (Exception ex)
{
logger.Error(ex, ex.Message);
}
}

#endregion Edit Data Source

#region life cycle

/// <summary>
/// Called just before view is activiated for the first time.
/// Available TestDataSource are loaded from local storage during initialization.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@
<ContextMenu.Template>
<ControlTemplate>
<StackPanel>
<MenuItem x:Name="Edit" Header="Edit" cal:Action.TargetWithoutContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type Grid}}}" cal:Message.Attach="[Event Click] = [Action EditDataSource($dataContext)]" ></MenuItem>
<MenuItem x:Name="Edit" Header="Edit" cal:Action.TargetWithoutContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type Grid}}}" cal:Message.Attach="[Event Click] = [Action EditDataSource($dataContext)]"></MenuItem>
<MenuItem x:Name="Delete" Header="Delete" cal:Action.TargetWithoutContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type Grid}}}" cal:Message.Attach="[Event Click] = [Action DeleteDataSource($dataContext)]"></MenuItem>
</StackPanel>
</ControlTemplate>
</ContextMenu.Template>
Expand Down
52 changes: 47 additions & 5 deletions src/Pixel.Persistence.Respository/ApplicationRepository.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using Dawn;
using Microsoft.Extensions.Logging;
using MongoDB.Bson;
using MongoDB.Driver;
using Pixel.Persistence.Core.Models;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
Expand All @@ -9,17 +11,27 @@ namespace Pixel.Persistence.Respository
{
public class ApplicationRepository : IApplicationRepository
{
private readonly ILogger logger;
private readonly IMongoCollection<BsonDocument> applicationsCollection;
private readonly IMongoCollection<ProjectReferences> referencesCollection;
private readonly IControlRepository controlRepository;
private readonly IPrefabsRepository prefabsRepository;

public ApplicationRepository(IMongoDbSettings dbSettings)
public ApplicationRepository(ILogger<ApplicationRepository> logger, IMongoDbSettings dbSettings,
IControlRepository controlRepository, IPrefabsRepository prefabsRepository)
{
this.logger = Guard.Argument(logger, nameof(logger)).NotNull().Value;
this.controlRepository = Guard.Argument(controlRepository, nameof(controlRepository)).NotNull().Value;
this.prefabsRepository = Guard.Argument(prefabsRepository, nameof(prefabsRepository)).NotNull().Value;

var client = new MongoClient(dbSettings.ConnectionString);
var database = client.GetDatabase(dbSettings.DatabaseName);
applicationsCollection = database.GetCollection<BsonDocument>(dbSettings.ApplicationsCollectionName);
referencesCollection = database.GetCollection<ProjectReferences>(dbSettings.ProjectReferencesCollectionName);
}

///<inheritdoc/>
public async Task<object> GetApplication(string applicationId)
public async Task<object> FindByIdAsync(string applicationId)
{
Guard.Argument(applicationId).NotNull().NotEmpty();

Expand Down Expand Up @@ -54,24 +66,54 @@ public async Task AddOrUpdate(string applicationDescriptionJson)

//Add these extra fields while storing. However, these will be dropped off in response while retrieving
document.Add("ApplicationId", applicationId);
document.Add("LastUpdated", DateTime.Now.ToUniversalTime());
document.Add("LastUpdated", DateTime.UtcNow);

var filter = Builders<BsonDocument>.Filter.Eq(x => x["ApplicationDetails.ApplicationId"], applicationId);

//if document with application id already exists, replace it
if (applicationsCollection.CountDocuments<BsonDocument>(x => x["ApplicationDetails.ApplicationId"].Equals(applicationId),
new CountOptions { Limit = 1 }) > 0)
{
await applicationsCollection.FindOneAndReplaceAsync<BsonDocument>(filter, document);
await applicationsCollection.FindOneAndReplaceAsync<BsonDocument>(filter, document);
logger.LogInformation("Application : '{0}' was updated", document["ApplicationDetails"]["ApplicationName"]);
}
else
{
await applicationsCollection.InsertOneAsync(document);
await applicationsCollection.InsertOneAsync(document);
logger.LogInformation("Application : '{0}' was added", document["ApplicationDetails"]["ApplicationName"]);
}
return;

}
throw new ArgumentException("Failed to parse application data in to BsonDocument");
}

///<inheritdoc/>
public async Task DeleteAsync(string applicationId)
{
Guard.Argument(applicationId, nameof(applicationId)).NotNull().NotEmpty();

var applicationFilter = Builders<BsonDocument>.Filter.Eq(x => x["ApplicationId"], applicationId);
var application = await (applicationsCollection.Find<BsonDocument>(applicationFilter)).SingleOrDefaultAsync();

//Check if application is used by any of the projects
var referenceFilter = Builders<ProjectReferences>.Filter.ElemMatch(x => x.ControlReferences, Builders<ControlReference>.Filter.Eq(c => c.ApplicationId, applicationId))
| Builders<ProjectReferences>.Filter.ElemMatch(x => x.PrefabReferences, Builders<PrefabReference>.Filter.Eq(p => p.ApplicationId, applicationId));

long count = await this.referencesCollection.CountDocumentsAsync(referenceFilter);
if (count > 0)
{
throw new InvalidOperationException($"Application : '{application["ApplicationDetails"]["ApplicationName"]}' is in use by one or more projects");
}

var updateDefinition = Builders<BsonDocument>.Update
.Set("ApplicationDetails.IsDeleted", true)
.Set("ApplicationDetails.LastUpdated", DateTime.UtcNow);
await applicationsCollection.FindOneAndUpdateAsync(applicationFilter, updateDefinition);
logger.LogInformation("Application : '{0}' was marked deleted", application["ApplicationDetails"]["ApplicationName"]);

await this.controlRepository.DeleteAllControlsForApplicationAsync(applicationId);
await this.prefabsRepository.DeleteAllPrefabsForApplicationAsync(applicationId);
}
}
}
Loading

0 comments on commit bdf9007

Please sign in to comment.