Skip to content

Commit 2fb744b

Browse files
committed
Allow prefabs to be deleted
1. Prefabs can be deleted if they are not in use by any project. 2. Added checks to prevent usage of prefab from clients with stale data if a prefab has already been deleted by other clients.
1 parent 73cd72c commit 2fb744b

File tree

17 files changed

+262
-27
lines changed

17 files changed

+262
-27
lines changed

src/Pixel.Automation.AppExplorer.ViewModels/Prefab/PrefabExplorerViewModel.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
using Pixel.Persistence.Services.Client.Interfaces;
1212
using Serilog;
1313
using System.ComponentModel;
14+
using System.Windows;
1415
using System.Windows.Data;
1516

1617
namespace Pixel.Automation.AppExplorer.ViewModels.Prefab
@@ -113,6 +114,10 @@ private List<PrefabProjectViewModel> LoadPrefabs(ApplicationDescriptionViewModel
113114
var prefabs = this.prefabDataManager.GetPrefabsForScreen(applicationDescriptionViewModel.Model, screenName).ToList();
114115
foreach (var prefab in prefabs)
115116
{
117+
if(prefab.IsDeleted)
118+
{
119+
continue;
120+
}
116121
var prefabProjectViewModel = new PrefabProjectViewModel(prefab);
117122
applicationDescriptionViewModel.AddPrefab(prefabProjectViewModel, screenName);
118123
prefabsList.Add(prefabProjectViewModel);
@@ -231,6 +236,26 @@ public async Task ManagePrefab(PrefabProjectViewModel targetPrefab)
231236
}
232237
}
233238

239+
/// <summary>
240+
/// Delete the prefab
241+
/// </summary>
242+
/// <param name="prefabToDelete"></param>
243+
public async Task DeletePrefabAsync(PrefabProjectViewModel prefabToDelete)
244+
{
245+
try
246+
{
247+
Guard.Argument(prefabToDelete, nameof(prefabToDelete)).NotNull();
248+
await this.prefabDataManager.DeletePrefbAsync(prefabToDelete.PrefabProject);
249+
this.Prefabs.Remove(prefabToDelete);
250+
}
251+
catch (Exception ex)
252+
{
253+
logger.Error(ex, "There was an error while trying to delete prefab : {0}", prefabToDelete.PrefabName);
254+
MessageBox.Show($"Error while deleting prefab : {prefabToDelete.PrefabName}", "Delete Error", MessageBoxButton.OK, MessageBoxImage.Error);
255+
}
256+
}
257+
258+
234259
/// <summary>
235260
/// Broadcast a FilterTestMessage which is processed by Test explorer view to filter and show only those test cases
236261
/// which uses this prefab

src/Pixel.Automation.AppExplorer.ViewModels/Prefab/PrefabVersionManagerViewModel.cs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using Pixel.Persistence.Services.Client.Interfaces;
99
using Pixel.Scripting.Editor.Core.Contracts;
1010
using Serilog;
11+
using System.Windows;
1112

1213
namespace Pixel.Automation.AppExplorer.ViewModels.Prefab
1314
{
@@ -61,7 +62,8 @@ public PrefabVersionManagerViewModel(PrefabProject prefabProject, IWorkspaceMana
6162
public async Task PublishAsync(PrefabVersionViewModel prefabVersionViewModel)
6263
{
6364
try
64-
{
65+
{
66+
Guard.Argument(prefabVersionViewModel, nameof(prefabVersionViewModel)).NotNull();
6567
if (!prefabVersionViewModel.IsPublished)
6668
{
6769
bool isLatestActieVersion = prefabProject.LatestActiveVersion.Version.Equals(prefabVersionViewModel.Version);
@@ -75,7 +77,8 @@ public async Task PublishAsync(PrefabVersionViewModel prefabVersionViewModel)
7577
}
7678
catch (Exception ex)
7779
{
78-
logger.Error(ex, ex.Message);
80+
logger.Error(ex, "There was an error while trying to publish verion : {0} of prefab : {1}", prefabVersionViewModel.Version, prefabVersionViewModel.PrefabName);
81+
MessageBox.Show($"Error while publishing version {prefabVersionViewModel.Version} of prefab {prefabVersionViewModel.PrefabName}", "Publish Error", MessageBoxButton.OK, MessageBoxImage.Error);
7982
}
8083
}
8184

@@ -87,6 +90,7 @@ public async Task CloneAsync(PrefabVersionViewModel prefabVersionViewModel)
8790
{
8891
try
8992
{
93+
Guard.Argument(prefabVersionViewModel, nameof(prefabVersionViewModel)).NotNull();
9094
if (prefabVersionViewModel.IsPublished)
9195
{
9296
PrefabVersion newVersion = await prefabVersionViewModel.CloneAsync(this.prefabDataManager);
@@ -98,7 +102,8 @@ public async Task CloneAsync(PrefabVersionViewModel prefabVersionViewModel)
98102
}
99103
catch (Exception ex)
100104
{
101-
logger.Error(ex, ex.Message);
105+
logger.Error(ex, "There was an error while trying to create a new verion of prefab : {0} from version : {1}", prefabVersionViewModel.PrefabName, prefabVersionViewModel.Version);
106+
MessageBox.Show($"Error while cloning version {prefabVersionViewModel.Version} of prefab {prefabVersionViewModel.PrefabName}", "Clone Error", MessageBoxButton.OK, MessageBoxImage.Error);
102107
}
103108
}
104109

src/Pixel.Automation.AppExplorer.ViewModels/Prefab/PrefabVersionViewModel.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ public class PrefabVersionViewModel : PropertyChangedBase
2020
private readonly IPrefabFileSystem fileSystem;
2121
private readonly Lazy<IReferenceManager> referenceManager;
2222

23+
public string PrefabName
24+
{
25+
get => prefabProject.PrefabName;
26+
}
27+
2328
public Version Version
2429
{
2530
get => prefabVersion.Version;

src/Pixel.Automation.AppExplorer.ViewModels/PrefabDropHandler/PrefabVersionSelectorViewModel.cs

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77
using Pixel.Automation.Editor.Core.ViewModels;
88
using Pixel.Automation.Reference.Manager;
99
using Pixel.Automation.Reference.Manager.Contracts;
10+
using Pixel.Persistence.Services.Client.Interfaces;
1011
using Serilog;
1112
using System.IO;
13+
using System.Windows;
1214

1315
namespace Pixel.Automation.AppExplorer.ViewModels.PrefabDropHandler
1416
{
@@ -86,13 +88,13 @@ public string OutputMappingScriptFile
8688
/// <param name="prefabProjectViewModel">Prefab which needs to be added to automation process</param>
8789
/// <param name="dropTarget">EntityComponentViewModel wrapping an Entity to which Prefab needs to be added</param>
8890
public PrefabVersionSelectorViewModel(IProjectFileSystem projectFileSystem, IPrefabFileSystem prefabFileSystem,
89-
IReferenceManager projectReferenceManager, PrefabEntity prefabEntity,
91+
IReferenceManager projectReferenceManager, PrefabEntity prefabEntity,
9092
PrefabProjectViewModel prefabProjectViewModel, EntityComponentViewModel dropTarget)
9193
{
9294
this.DisplayName = "(1/3) Select prefab version and mapping scripts";
9395
this.projectFileSystem = projectFileSystem;
9496
this.prefabFileSystem = prefabFileSystem;
95-
this.projectReferenceManager = projectReferenceManager;
97+
this.projectReferenceManager = projectReferenceManager;
9698
this.prefabEntity = prefabEntity;
9799
this.prefabProject = prefabProjectViewModel.PrefabProject;
98100
this.dropTarget = dropTarget;
@@ -142,16 +144,24 @@ public void PickOutputMappingScriptFile()
142144
/// <inheritdoc/>
143145
public override async Task<bool> TryProcessStage()
144146
{
145-
//TODO : Can we make this asyc ?
146-
if(this.SelectedVersion != null && !dropTarget.ComponentCollection.Any(a => a.Model.Equals(prefabEntity)))
147+
try
147148
{
148-
await UpdatePrefabReferencesAsync();
149-
await UpdateControlReferencesAsync();
150-
dropTarget.AddComponent(prefabEntity);
151-
this.CanChangeVersion = false;
152-
logger.Information("Added version {0} of {1} to {2}.", this.SelectedVersion, this.prefabProject, this.dropTarget);
153-
}
154-
return true;
149+
if (this.SelectedVersion != null && !dropTarget.ComponentCollection.Any(a => a.Model.Equals(prefabEntity)))
150+
{
151+
await UpdatePrefabReferencesAsync();
152+
await UpdateControlReferencesAsync();
153+
dropTarget.AddComponent(prefabEntity);
154+
this.CanChangeVersion = false;
155+
logger.Information("Added version {0} of {1} to {2}.", this.SelectedVersion, this.prefabProject, this.dropTarget);
156+
}
157+
return true;
158+
}
159+
catch (Exception ex)
160+
{
161+
logger.Error(ex, "There was an error while trying to process stage for the prefab version selector screen");
162+
MessageBox.Show("Error while trying to add prefab", "Add Prefab Error", MessageBoxButton.OK, MessageBoxImage.Error);
163+
return false;
164+
}
155165
}
156166

157167
/// <summary>

src/Pixel.Automation.AppExplorer.Views/Prefab/PrefabExplorerView.xaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
cal:Action.TargetWithoutContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type Grid}, AncestorLevel=2}}" cal:Message.Attach="[Event Click] = [Action ManagePrefab($dataContext)]"></MenuItem>
9494
<MenuItem x:Name="ShowUsage" Header="Show Usage" cal:Action.TargetWithoutContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type Grid}, AncestorLevel=2}}" cal:Message.Attach="[Event Click] = [Action ShowUsage($dataContext)]"></MenuItem>
9595
<MenuItem x:Name="MoveToScreen" Header="Move To Screen" cal:Action.TargetWithoutContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type Grid}, AncestorLevel=2}}" cal:Message.Attach="[Event Click] = [Action MoveToScreen($dataContext)]"></MenuItem>
96+
<MenuItem x:Name="Delete" Header="Delete" cal:Action.TargetWithoutContext="{Binding Path=DataContext, RelativeSource={RelativeSource AncestorType={x:Type Grid}, AncestorLevel=2}}" cal:Message.Attach="[Event Click] = [Action DeletePrefabAsync($dataContext)]" ></MenuItem>
9697
</StackPanel>
9798
</ControlTemplate>
9899
</ContextMenu.Template>

src/Pixel.Automation.Core/Models/PrefabProject.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,12 @@ public class PrefabProject : ICloneable
4949
[DataMember(Order = 70)]
5050
public string GroupName { get; set; } = "Default";
5151

52+
/// <summary>
53+
/// Indicates if Prefab project is marked deleted.
54+
/// </summary>
55+
[DataMember(Order = 1000)]
56+
public bool IsDeleted { get; set; }
57+
5258
/// <summary>
5359
/// Get all the versions that are active.
5460
/// </summary>

src/Pixel.Persistence.Respository/Interfaces/IControlRepository.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public interface IControlRepository
5252
Task<bool> IsControlDeleted(string applicationId, string controlId);
5353

5454
/// <summary>
55-
/// Set IsDeleted flag on all versionf of control to true.
55+
/// Set IsDeleted flag on all versions of control to true.
5656
/// </summary>
5757
/// <param name="applicationId"></param>
5858
/// <param name="controlId"></param>

src/Pixel.Persistence.Respository/Interfaces/IPrefabsRepository.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,5 +59,19 @@ public interface IPrefabsRepository
5959
/// <param name="cancellationToken"></param>
6060
/// <returns></returns>
6161
Task UpdatePrefabVersionAsync(string prefabId, PrefabVersion version, CancellationToken cancellationToken);
62+
63+
/// <summary>
64+
/// Check if a Prefab is marked deleted
65+
/// </summary>
66+
/// <param name="prefabId"></param>
67+
/// <returns></returns>
68+
Task<bool> IsPrefabDeleted(string prefabId);
69+
70+
/// <summary>
71+
/// Mark prefab as deleted.
72+
/// </summary>
73+
/// <param name="prefabId"></param>
74+
/// <returns></returns>
75+
Task DeletePrefabAsync(string prefabId);
6276
}
6377
}

src/Pixel.Persistence.Respository/Interfaces/IReferencesRepository.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,23 @@ public interface IReferencesRepository
5959
/// <returns></returns>
6060
Task UpdateControlReference(string projectId, string projectVersion, ControlReference controlReference);
6161

62+
63+
/// <summary>
64+
/// Check if any version of prefab is in use by any version of any project
65+
/// </summary>
66+
/// <param name="prefabId"></param>
67+
/// <returns></returns>
68+
Task<bool> IsPrefabInUse(string prefabId);
69+
70+
/// <summary>
71+
/// Check if any of the projets has a reference to any version of this prefab
72+
/// </summary>
73+
/// <param name="projectId"></param>
74+
/// <param name="projectVersion"></param>
75+
/// <param name="controlReference"></param>
76+
/// <returns></returns>
77+
//Task<bool> HasPrefabReference(string projectId, string projectVersion, PrefabReference prefabReference);
78+
6279
/// <summary>
6380
/// Add or update PrefabReferences for a given version of project
6481
/// </summary>

src/Pixel.Persistence.Respository/PrefabsRepository.cs

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ namespace Pixel.Persistence.Respository
1717
public class PrefabsRepository : IPrefabsRepository
1818
{
1919
private readonly ILogger logger;
20-
private readonly IMongoCollection<PrefabProject> prefabsCollection;
20+
private readonly IMongoCollection<PrefabProject> prefabsCollection;
2121
private readonly IPrefabFilesRepository prefabFilesRepossitory;
2222
private readonly IReferencesRepository referencesRepository;
2323
private static readonly InsertOneOptions InsertOneOptions = new InsertOneOptions();
@@ -105,7 +105,9 @@ public async Task AddPrefabVersionAsync(string prefabId, PrefabVersion newVersio
105105
}
106106

107107
filter = Builders<PrefabProject>.Filter.Eq(x => x.PrefabId, prefabId);
108-
var push = Builders<PrefabProject>.Update.Push(t => t.AvailableVersions, newVersion);
108+
var push = Builders<PrefabProject>.Update.Push(t => t.AvailableVersions, newVersion)
109+
.Set(t => t.LastUpdated, DateTime.UtcNow)
110+
.Inc(t => t.Revision, 1);
109111
await this.prefabsCollection.UpdateOneAsync(filter, push);
110112
}
111113

@@ -125,12 +127,39 @@ public async Task UpdatePrefabVersionAsync(string prefabId, PrefabVersion prefab
125127
{
126128
prefabVersion.PublishedOn = DateTime.UtcNow;
127129
}
128-
var update = Builders<PrefabProject>.Update.Set(x => x.AvailableVersions[-1], prefabVersion);
130+
var update = Builders<PrefabProject>.Update.Set(x => x.AvailableVersions[-1], prefabVersion)
131+
.Set(t => t.LastUpdated, DateTime.UtcNow)
132+
.Inc(t => t.Revision, 1); ;
129133
await this.prefabsCollection.UpdateOneAsync(filter, update);
130134
logger.LogInformation("Project version {0} was updated for project : {1}", prefabVersion, prefabId);
131135
return;
132136
}
133137
throw new InvalidOperationException($"Version {prefabVersion} doesn't exist on prefab {prefabId}");
134138
}
139+
140+
///<inheritdoc/>
141+
public async Task<bool> IsPrefabDeleted(string prefabId)
142+
{
143+
Guard.Argument(prefabId, nameof(prefabId)).NotNull().NotEmpty();
144+
var filter = Builders<PrefabProject>.Filter.Eq(x => x.PrefabId, prefabId) &
145+
Builders<PrefabProject>.Filter.Eq(x => x.IsDeleted, true);
146+
long count = await this.prefabsCollection.CountDocumentsAsync(filter, new CountOptions() { Limit = 1 });
147+
return count > 0;
148+
}
149+
150+
/// <inheritdoc/>
151+
public async Task DeletePrefabAsync(string prefabId)
152+
{
153+
Guard.Argument(prefabId, nameof(prefabId)).NotNull();
154+
if(await this.referencesRepository.IsPrefabInUse(prefabId))
155+
{
156+
throw new InvalidOperationException("Prefab is in use across one or more projects");
157+
}
158+
var updateFilter = Builders<PrefabProject>.Filter.Eq(x => x.PrefabId, prefabId);
159+
var updateDefinition = Builders<PrefabProject>.Update.Set(x => x.IsDeleted, true)
160+
.Set(x => x.LastUpdated, DateTime.UtcNow)
161+
.Inc(t => t.Revision, 1);
162+
await this.prefabsCollection.FindOneAndUpdateAsync<PrefabProject>(updateFilter, updateDefinition);
163+
}
135164
}
136165
}

0 commit comments

Comments
 (0)