GeezerVison is showing github.com — Adding per-file exception handling for issues that occur during analyze · HighEncryption/SyncPro@7609e2c
File treeExpand file treeCollapse file tree Open diff view settings SyncPro.Adapters.WindowsFileSystem
Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: SyncPro.Adapters.WindowsFileSystem/WindowsFileSystemAdapter.cs +13Lines changed: 13 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -217,8 +217,21 @@ public override void DeleteItem(SyncEntry entry) | 217 | 217 | entry.EntryLastUpdatedDateTimeUtc = DateTime.UtcNow; | 218 | 218 | } | 219 | 219 |
| | 220 | + /// <summary> | | 221 | + /// Get the <see cref="FileSystemFolder"/> items that are children to the provided | | 222 | + /// <see cref="FileSystemFolder"/>. | | 223 | + /// </summary> | | 224 | + /// <param name="folder">The parent folder</param> | | 225 | + /// <returns>The list of child items under the parent folder.</returns> | | 226 | + /// <remarks> | | 227 | + /// An errors encountered when querying for the list of child should be thrown for the | | 228 | + /// caller to handle. However, any errors related to querying the children themselves | | 229 | + /// should be caught and returned as error information for that child (the original | | 230 | + /// call should not fail in this case). | | 231 | + /// </remarks> | 220 | 232 | public override IEnumerable<IAdapterItem> GetAdapterItems(IAdapterItem folder) | 221 | 233 | { | | 234 | + // If folder is null, return the list of top-level folders on the computer (aka drives). | 222 | 235 | if (folder == null) | 223 | 236 | { | 224 | 237 | DriveInfo[] allDrives = DriveInfo.GetDrives(); | |
Collapse file Copy file name to clipboardExpand all lines: SyncPro.Core/Adapters/SyncEntryChangedFlags.cs +5Lines changed: 5 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -73,6 +73,11 @@ public enum SyncEntryChangedFlags : uint | 73 | 73 | /// </summary> | 74 | 74 | Moved = 0x0800, | 75 | 75 |
| | 76 | + /// <summary> | | 77 | + /// Indicates that there was an exception while reading this item | | 78 | + /// </summary> | | 79 | + Exception = 0x1000, | | 80 | + | 76 | 81 | /// <summary> | 77 | 82 | /// Indicates that the change is a new file or directory | 78 | 83 | /// </summary> | |
Collapse file Copy file name to clipboardExpand all lines: SyncPro.Core/Data/SyncEntry.cs +6Lines changed: 6 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -33,6 +33,12 @@ public enum SyncEntryState : short | 33 | 33 | /// The entry has been deleted from the adapter, but the entry remains in the database | 34 | 34 | /// </summary> | 35 | 35 | IsDeleted = 0x02, | | 36 | + | | 37 | + /// <summary> | | 38 | + /// Indicates that an error occurred when reading this entry from the source adapter. | | 39 | + /// </summary> | | 40 | + Exception = 0x04, | | 41 | + | 36 | 42 | Undefined = 0xFF | 37 | 43 | } | 38 | 44 |
| |
Collapse file Copy file name to clipboardExpand all lines: SyncPro.Core/Runtime/AnalyzeJobWorker.cs +31-2Lines changed: 31 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -681,10 +681,39 @@ private void AnalyzeChangesWithoutChangeTracking( | 681 | 681 | // TODO: Query the adapter somehow to see if the file should be suppressed (sparse file, for example). | 682 | 682 | // TODO: Further note - this would be the right place to implement an inclusion/exclusion behavior. | 683 | 683 |
| 684 | | - // Check if there was an error reading the item from the adapter. If so, skip the item. | | 684 | + // Check if there was an error reading the item from the adapter. Create a new EntryUpdateInfo object | | 685 | + // for this item so that we know it was found, but don't continue processing the item beyond this. | 685 | 686 | if (!string.IsNullOrEmpty(sourceAdapterChild.ErrorMessage)) | 686 | 687 | { | 687 | | - Logger.Info("Skipping adapter child '{0}' with error.", sourceAdapterChild.FullName); | | 688 | + // Check if there is an entry in the database that matches the unique ID of the item | | 689 | + SyncEntry sourceLogicalChildEx = logicalChildren?.FirstOrDefault(c => c.HasUniqueId(sourceAdapterChild.UniqueId)); | | 690 | + | | 691 | + // There isn't a database entry for this item, so we will need to create one. It won't be added | | 692 | + // to the database, but we will need it since other code expects it to be there. | | 693 | + if (sourceLogicalChildEx == null) | | 694 | + { | | 695 | + sourceLogicalChildEx = new SyncEntry | | 696 | + { | | 697 | + Name = sourceAdapterChild.Name, | | 698 | + AdapterEntries = new List<SyncEntryAdapterData>(), | | 699 | + ParentEntry = logicalParent | | 700 | + }; | | 701 | + } | | 702 | + | | 703 | + sourceLogicalChildEx.State = SyncEntryState.NotSynchronized; | | 704 | + | | 705 | + // Create the update info for the new entry. The only flag we want to set if Exception so that | | 706 | + // it isn't processed in a sync job. | | 707 | + sourceLogicalChildEx.UpdateInfo = new EntryUpdateInfo( | | 708 | + sourceLogicalChildEx, | | 709 | + sourceAdapter, | | 710 | + SyncEntryChangedFlags.Exception, | | 711 | + Path.Combine(relativePath, sourceLogicalChildEx.Name)); | | 712 | + | | 713 | + this.RaiseChangeDetected(sourceAdapter.Configuration.Id, sourceLogicalChildEx.UpdateInfo); | | 714 | + | | 715 | + this.LogSyncAnalyzerChangeFound(sourceLogicalChildEx); | | 716 | + | 688 | 717 | continue; | 689 | 718 | } | 690 | 719 |
| |
Collapse file Copy file name to clipboardExpand all lines: SyncPro.Core/Runtime/FlagNameCache.cs +5Lines changed: 5 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -98,6 +98,11 @@ private static string BuildFlagString(SyncEntryChangedFlags flags) | 98 | 98 | sb.Append("Moved,"); | 99 | 99 | } | 100 | 100 |
| | 101 | + if ((flags & SyncEntryChangedFlags.Exception) != 0) | | 102 | + { | | 103 | + sb.Append("Exception,"); | | 104 | + } | | 105 | + | 101 | 106 | return sb.ToString(0, sb.Length - 1); | 102 | 107 | } | 103 | 108 | } | |
Collapse file Copy file name to clipboardExpand all lines: SyncPro.UI/Navigation/Content/AnalyzeJobView.xaml +31-3Lines changed: 31 additions & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 33 | 33 | </Style> | 34 | 34 |
| 35 | 35 | <DataTemplate x:Key="RelativePathCellTemplate" DataType="{x:Type viewModels:EntryUpdateInfoViewModel}"> | 36 | | - <TextBlock x:Name="TextBox" Text="{Binding Path=RelativePath}" Style="{StaticResource TreeListItemStyle}" /> | | 36 | + <StackPanel Orientation="Horizontal"> | | 37 | + <TextBlock x:Name="TextBox" Text="{Binding Path=RelativePath}" Style="{StaticResource TreeListItemStyle}" /> | | 38 | + <Image Source="/Resources/Graphics/icon_exclaim.png" Height="16" Margin="4,0,0,0" | | 39 | + Visibility="{Binding Path=Flags, Converter={StaticResource EnumToVisibility}, ConverterParameter=Exception}"/> | | 40 | + </StackPanel> | 37 | 41 | </DataTemplate> | 38 | 42 |
| 39 | 43 | <DataTemplate x:Key="ChangeTypeCellTemplate" DataType="{x:Type viewModels:EntryUpdateInfoViewModel}"> | | 62 | 66 | <DataTrigger Binding="{Binding Path=IsDirectory}" Value="True"> | 63 | 67 | <Setter Property="Visibility" Value="Collapsed" /> | 64 | 68 | </DataTrigger> | | 69 | + <DataTrigger Binding="{Binding Path=Flags, Converter={StaticResource EnumToBoolean}, ConverterParameter=Exception}" Value="True"> | | 70 | + <Setter Property="Visibility" Value="Collapsed" /> | | 71 | + </DataTrigger> | 65 | 72 | </Style.Triggers> | 66 | 73 | </Style> | 67 | 74 | </TextBlock.Style> | | 216 | 223 | <ItemsControl ItemsSource="{Binding Path=AnalyzeJobViewModel.ErrorMessages}"> | 217 | 224 | <ItemsControl.ItemTemplate> | 218 | 225 | <DataTemplate DataType="viewModels:ErrorViewModel"> | 219 | | - <TextBlock Text="{Binding Path=Header}" | 220 | | - ToolTip="{Binding Path=Message}" /> | | 226 | + <Grid> | | 227 | + <Grid.ColumnDefinitions> | | 228 | + <ColumnDefinition Width="Auto" /> | | 229 | + <ColumnDefinition /> | | 230 | + </Grid.ColumnDefinitions> | | 231 | + | | 232 | + <Image Width="16"> | | 233 | + <Image.Style> | | 234 | + <Style TargetType="Image"> | | 235 | + <Style.Setters> | | 236 | + <Setter Property="Source" Value="/Resources/Graphics/icon_exclaim.png" /> | | 237 | + </Style.Setters> | | 238 | + <Style.Triggers> | | 239 | + <DataTrigger Binding="{Binding Path=IsWarning}" Value="True"> | | 240 | + <Setter Property="Source" Value="/Resources/Graphics/warning_16.png" /> | | 241 | + </DataTrigger> | | 242 | + </Style.Triggers> | | 243 | + </Style> | | 244 | + </Image.Style> | | 245 | + </Image> | | 246 | + <TextBlock Grid.Column="1" Text="{Binding Path=Header}" | | 247 | + ToolTip="{Binding Path=Message}" /> | | 248 | + </Grid> | 221 | 249 | </DataTemplate> | 222 | 250 | </ItemsControl.ItemTemplate> | 223 | 251 | </ItemsControl> | |
Collapse file Copy file name to clipboard 478 Bytes
- Display the source diff
- Display the rich diff
Collapse file Copy file name to clipboardExpand all lines: SyncPro.UI/SyncPro.UI.csproj +3Lines changed: 3 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 657 | 657 | <ItemGroup> | 658 | 658 | <Resource Include="Resources\Graphics\help_20.png" /> | 659 | 659 | </ItemGroup> | | 660 | + <ItemGroup> | | 661 | + <Resource Include="Resources\Graphics\warning_16.png" /> | | 662 | + </ItemGroup> | 660 | 663 | <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> | 661 | 664 | <PropertyGroup> | 662 | 665 | <PreBuildEvent>git describe --always --dirty > $(ProjectDir)\version.txt</PreBuildEvent> | |
Collapse file Copy file name to clipboardExpand all lines: SyncPro.UI/ViewModels/AnalyzeJobViewModel.cs +43-10Lines changed: 43 additions & 10 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -118,25 +118,58 @@ private void OnJobFinished(object sender, EventArgs eventArgs) | 118 | 118 | this.ChangeMetricsList[1].Unchanged = this.AnalyzeJob.AnalyzeResult.UnchangedFolderCount; | 119 | 119 | this.ChangeMetricsList[2].Unchanged = this.AnalyzeJob.AnalyzeResult.UnchangedFileBytes; | 120 | 120 |
| 121 | | - List<Exception> exceptions = | 122 | | - this.AnalyzeJob.AnalyzeResult.AdapterResults | 123 | | - .Select(r => r.Value.Exception) | 124 | | - .ToList(); | 125 | | - | 126 | | - if (exceptions.Any()) | | 121 | + int entryErrorCount = 0; | | 122 | + foreach (KeyValuePair<int, AnalyzeAdapterResult> adapterResult in this.AnalyzeJob.AnalyzeResult.AdapterResults) | 127 | 123 | { | 128 | | - foreach (Exception exception in exceptions) | | 124 | + if (adapterResult.Value.Exception != null) | 129 | 125 | { | 130 | 126 | this.ErrorMessages.Add( | 131 | 127 | new ErrorViewModel( | 132 | 128 | false, | 133 | | - exception.Message, | 134 | | - exception.ToString())); | | 129 | + adapterResult.Value.Exception.Message, | | 130 | + adapterResult.Value.Exception.ToString())); | | 131 | + | | 132 | + this.ProgressState = ProgressState.Error; | 135 | 133 | } | 136 | 134 |
| 137 | | - this.ProgressState = ProgressState.Error; | | 135 | + entryErrorCount += | | 136 | + adapterResult.Value.EntryResults.Count(e => e.HasSyncEntryFlag(SyncEntryChangedFlags.Exception)); | | 137 | + } | | 138 | + | | 139 | + if (entryErrorCount > 0) | | 140 | + { | | 141 | + var errorViewModel = new ErrorViewModel( | | 142 | + false, | | 143 | + string.Format( | | 144 | + "Error were encountered while analyzing {0} items. These items will not be synchronized.", | | 145 | + entryErrorCount), | | 146 | + null); | | 147 | + | | 148 | + App.DispatcherInvoke(() => this.ErrorMessages.Add(errorViewModel)); | 138 | 149 | } | 139 | 150 |
| | 151 | + //// Performance of this call isn't great, but there shouldn't be a large number of exceptions | | 152 | + //// to process in this way. | | 153 | + //List<Exception> exceptions = | | 154 | + // this.AnalyzeJob.AnalyzeResult.AdapterResults | | 155 | + // .Where(r => r.Value.Exception != null) | | 156 | + // .Select(r => r.Value.Exception) | | 157 | + // .ToList(); | | 158 | + | | 159 | + //if (exceptions.Any()) | | 160 | + //{ | | 161 | + // foreach (Exception exception in exceptions) | | 162 | + // { | | 163 | + // this.ErrorMessages.Add( | | 164 | + // new ErrorViewModel( | | 165 | + // false, | | 166 | + // exception.Message, | | 167 | + // exception.ToString())); | | 168 | + // } | | 169 | + | | 170 | + // this.ProgressState = ProgressState.Error; | | 171 | + //} | | 172 | + | 140 | 173 | this.metadataUpdateCancellationToken.Cancel(); | 141 | 174 |
| 142 | 175 | App.DispatcherInvoke(CommandManager.InvalidateRequerySuggested); | |
Collapse file Copy file name to clipboardExpand all lines: SyncPro.UI/ViewModels/EntryUpdateInfoViewModel.cs +5Lines changed: 5 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -391,6 +391,11 @@ private static IList<string> GetChangeNamesFromFlags(SyncEntryChangedFlags flags | 391 | 391 | changes.Add("Destination Exists"); | 392 | 392 | } | 393 | 393 |
| | 394 | + if ((flags & SyncEntryChangedFlags.Exception) == SyncEntryChangedFlags.Exception) | | 395 | + { | | 396 | + changes.Add("[Exception]"); | | 397 | + } | | 398 | + | 394 | 399 | return changes; | 395 | 400 | } | 396 | 401 |
| |
|