Skip to content

Commit cad2d69

Browse files
KevinJumpCopilot
andauthored
V16/multiple nodes per file (#829)
* Production 'mode' where we merge all the files together. * ajust the interface, so we can have nice defaults for new properties. * clean up the merge function * make sure individual empty files process the 'current' way (we don't want to slow down existing imports). * cache the loading of xml files in the clean loop, to stop multiple file loads. * clean up the import/report methods to make them more readable. * add incremental update messages so we can smooth out progress even when using a single import file. * fix typo on exportOnSave settings localization string. * XML Comments. * more xml comments * Update uSync.BackOffice/Hubs/uSyncCallbacks.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update uSync.BackOffice/Configuration/uSyncSettings.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update uSync.BackOffice/SyncHandlers/SyncHandlerRoot.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update uSync.BackOffice/Configuration/uSyncSettings.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update uSync.BackOffice/Services/SyncService_Files.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update uSync.BackOffice/Services/SyncFileService.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update uSync.BackOffice/Hubs/uSyncCallbacks.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * make sure we process all single actions (Co-piolt fix) * Update uSync.BackOffice/SyncHandlers/Interfaces/ISyncHandler.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix serializer interface typo. * remove bool clean from merge public api * add using alias for core constants. * Replace File.Exists with call to SyncFileService.FileExists * Update uSync.BackOffice/Configuration/SyncConfigService.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * typo fixes in the generated schema file. * Update uSync.BackOffice/Hubs/HubClientService.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update uSync.BackOffice/Services/ISyncFileService.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent b5e9f0f commit cad2d69

File tree

21 files changed

+382
-85
lines changed

21 files changed

+382
-85
lines changed

uSync.BackOffice.Targets/appsettings-schema.usync.json

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@
205205
},
206206
"DisableNotificationSuppression": {
207207
"type": "boolean",
208-
"description": "turns of use of the Notifications.Supress method, so notifications\nfire after every item is imported.\n ",
208+
"description": "turns off use of the Notifications.Suppress method, so notifications\nfire after every item is imported.\n ",
209209
"default": "true"
210210
},
211211
"BackgroundNotifications": {
@@ -217,9 +217,37 @@
217217
"type": "boolean",
218218
"description": "Move the uSync tree to it's own section in the back office. \n(requires a restart to take effect).\n ",
219219
"default": false
220+
},
221+
"FolderMode": {
222+
"description": "What type of mode the folder should work in (default, root, or production)\n ",
223+
"default": "Normal",
224+
"oneOf": [
225+
{
226+
"$ref": "#/definitions/USyncBackOfficeConfigurationSyncFolderMode"
227+
}
228+
]
229+
},
230+
"ProductionFolder": {
231+
"type": "string",
232+
"description": "location of the 'production' folder to use when in production mode, \nor when creating the production mode files.\n ",
233+
"default": "uSync/production"
220234
}
221235
}
222236
},
237+
"USyncBackOfficeConfigurationSyncFolderMode": {
238+
"type": "string",
239+
"description": "uSync's folder mode - normal, root or production\n ",
240+
"x-enumNames": [
241+
"Normal",
242+
"Root",
243+
"Production"
244+
],
245+
"enum": [
246+
"Normal",
247+
"Root",
248+
"Production"
249+
]
250+
},
223251
"USyncuSyncSetsDefinition": {
224252
"type": "object",
225253
"properties": {

uSync.BackOffice/Configuration/SyncConfigService.cs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,21 +66,24 @@ private class SyncFolderItem
6666
/// <inheritdoc/>
6767
public string GetWorkingFolder()
6868
{
69-
var folders = FetchFolders();
70-
71-
return Settings.IsRootSite
72-
? folders[0].TrimStart('/')
73-
: folders.Last().TrimStart('/');
69+
var folders = GetFolders();
70+
return folders.Last().TrimStart('/');
7471
}
7572

7673
/// <inheritdoc/>
7774
public string[] GetFolders()
7875
{
7976
var folders = FetchFolders();
8077

81-
return Settings.IsRootSite
82-
? [folders[0].TrimStart('/')]
83-
: [.. folders.Select(x => x.TrimStart('/'))];
78+
switch(Settings.FolderMode)
79+
{
80+
case SyncFolderMode.Root:
81+
return [folders[0].TrimStart('/')];
82+
case SyncFolderMode.Production:
83+
return [Settings.ProductionFolder.TrimStart('/')];
84+
default:
85+
return [.. folders.Select(x => x.TrimStart('/'))];
86+
}
8487
}
8588

8689
/// <inheritdoc/>

uSync.BackOffice/Configuration/uSyncSettings.cs

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -197,26 +197,22 @@ public class uSyncSettings
197197
public string HideAddOns { get; set; } = "licence";
198198

199199
/// <summary>
200-
/// turns of use of the Notifications.Supress method, so notifications
200+
/// turns off use of the Notifications.Suppress method, so notifications
201201
/// fire after every item is imported.
202202
/// </summary>
203203
/// <remarks>
204-
/// I am not sure this does what i think it does, it doesn't suppress
205-
/// then fire at the end , it just suppresses them all.
204+
/// this disables the internal uSync scope provider that delays all
205+
/// non cancellable notifications until after the import is complete.
206206
///
207-
/// until we have had time to look at this , we will leave this as
208-
/// disabled by default so all notification messages fire.
207+
/// on v13 this is false, the import happens and then the notifications fire.
209208
///
210-
/// for v13 thius is fine, but for v14, grouping the notifications
211-
/// can causes issues if something fails.
212-
///
213-
/// So if a single content import fails then the whole batch doesn't
214-
/// get published properly (so no content for you :( ) .
209+
/// on v16 the default is true, because some of the notifications appear to
210+
/// be closely coupled to the save/publish process, and if something goes
211+
/// wrong in one item's import it can cause a cascade of failures across
212+
/// everything that might have been imported along with it.
215213
///
216-
/// there might be something downlever we can do, but it likey means
217-
/// lots of core investigation to find that, for now 'old' school
218-
/// non suppressed notifications should be fine (if a little slower).
219-
///
214+
/// if the notifications are not suppressed, then if an item fails to import
215+
/// it doesn't stop other items from being imported.
220216
/// </remarks>
221217
[DefaultValue("true")]
222218
public bool DisableNotificationSuppression { get; set; } = true;
@@ -238,4 +234,39 @@ public class uSyncSettings
238234
/// </summary>
239235
[DefaultValue(false)]
240236
public bool MoveToSection { get; set; } = false;
237+
238+
/// <summary>
239+
/// What type of mode the folder should work in (default, root, or production)
240+
/// </summary>
241+
[DefaultValue(SyncFolderMode.Normal)]
242+
public SyncFolderMode FolderMode { get; set; } = SyncFolderMode.Normal;
243+
244+
/// <summary>
245+
/// location of the 'production' folder to use when in production mode,
246+
/// or when creating the production mode files.
247+
/// </summary>
248+
[DefaultValue("uSync/production")]
249+
public string ProductionFolder { get; set; } = "uSync/production";
241250
}
251+
252+
/// <summary>
253+
/// uSync's folder mode - normal, root or production
254+
/// </summary>
255+
public enum SyncFolderMode
256+
{
257+
/// <summary>
258+
/// normal - expects individual files in the uSync folder(s)
259+
/// </summary>
260+
Normal,
261+
262+
/// <summary>
263+
/// root - will read and write things to the root folder,
264+
/// </summary>
265+
Root,
266+
267+
/// <summary>
268+
/// production - looks in a 'production' folder, expects a single file per handler.
269+
/// </summary>
270+
Production,
271+
};
272+

uSync.BackOffice/Hubs/HubClientService.cs

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,5 +80,27 @@ public void PostUpdate(string message, int count, int total)
8080
/// get the uSync callbacks for this connection
8181
/// </summary>
8282
/// <returns></returns>
83-
public uSyncCallbacks Callbacks() => new(this.PostSummary, this.PostUpdate);
83+
public uSyncCallbacks Callbacks() => new(this.PostSummary, this.PostUpdate, this.SetCountRange, this.PostIncrementalUpdate);
84+
85+
private int _start = 0;
86+
private int _end = 0;
87+
88+
/// <summary>
89+
/// set a range (start to end) that we expect the next set of updates to be bound within.
90+
/// </summary>
91+
public void SetCountRange(int start, int end)
92+
{
93+
_start = start;
94+
_end = end;
95+
}
96+
97+
/// <summary>
98+
/// post an update and increment the counter by one.
99+
/// </summary>
100+
public void PostIncrementalUpdate(string message)
101+
{
102+
_start++;
103+
if (_start > _end) _end = _start;
104+
this.PostUpdate(message, _start, _end);
105+
}
84106
}

uSync.BackOffice/Hubs/uSyncCallbacks.cs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,30 @@
1-
using uSync.BackOffice.SyncHandlers.Interfaces;
1+
using uSync.BackOffice.Models;
2+
using uSync.BackOffice.SyncHandlers.Interfaces;
23

34
namespace uSync.BackOffice;
45

6+
/// <summary>
7+
/// Callback event for SignalR hub
8+
/// </summary>
9+
public delegate void SyncEventCallback(SyncProgressSummary summary);
10+
11+
/// <summary>
12+
/// callback delegate for SignalR messaging
13+
/// </summary>
14+
public delegate void SyncUpdateCallback(string message, int count, int total);
15+
16+
/// <summary>
17+
/// callback delegate to set the start and end range for the update counters.
18+
/// </summary>
19+
public delegate void SyncSetUpdateRange(int start, int end);
20+
21+
/// <summary>
22+
/// callback to send a update message and increment the counter by one so moving the progress bar.
23+
/// </summary>
24+
/// <param name="message"></param>
25+
public delegate void SyncIncrementalUpdateCallback(string message);
26+
27+
528
/// <summary>
629
/// Callback objects used to communicate via SignalR
730
/// </summary>
@@ -17,6 +40,16 @@ public class uSyncCallbacks
1740
/// </summary>
1841
public SyncUpdateCallback? Update { get; private set; }
1942

43+
/// <summary>
44+
/// set a start and end range for the counter.
45+
/// </summary>
46+
public SyncSetUpdateRange? SetRange { get; private set; }
47+
48+
/// <summary>
49+
/// update and increment callback.
50+
/// </summary>
51+
public SyncIncrementalUpdateCallback? IncrementalUpdate { get; private set; }
52+
2053
/// <summary>
2154
/// generate a new callback object
2255
/// </summary>
@@ -25,4 +58,14 @@ public uSyncCallbacks(SyncEventCallback? callback, SyncUpdateCallback? update)
2558
this.Callback = callback;
2659
this.Update = update;
2760
}
61+
62+
/// <summary>
63+
/// generate a callback object with range and incremental update
64+
/// </summary>
65+
public uSyncCallbacks(SyncEventCallback? callback, SyncUpdateCallback? update, SyncSetUpdateRange? updateRange, SyncIncrementalUpdateCallback incrementalUpdate)
66+
: this(callback, update)
67+
{
68+
this.SetRange = updateRange;
69+
this.IncrementalUpdate = incrementalUpdate;
70+
}
2871
}

uSync.BackOffice/Services/ISyncFileService.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,11 @@ public interface ISyncFileService
135135
/// <returns></returns>
136136
Task<XElement> LoadXElementAsync(string file);
137137

138+
/// <summary>
139+
/// merge all the files in the given folders into a single xml node, that can be bulk imported
140+
/// </summary>
141+
Task<int> MakeSingleExportFromFolders(string[] folders, string itemType, ISyncTrackerBase? trackerBase, string fileName, string extension);
142+
138143
/// <summary>
139144
/// merge a list of files into a single XElement
140145
/// </summary>

uSync.BackOffice/Services/ISyncService.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,4 +156,10 @@ public interface ISyncService
156156
/// trigger the end of the bulk process
157157
/// </summary>
158158
Task FinishBulkProcessAsync(HandlerActions action, IEnumerable<uSyncAction> actions);
159+
160+
/// <summary>
161+
/// merge the given folders in single 'production' files for each handler.
162+
/// </summary>
163+
Task<int> MergeExportFolder(string[] paths, IEnumerable<HandlerConfigPair> handlers);
164+
159165
}

uSync.BackOffice/Services/SyncFileService.cs

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ public async Task SaveFileAsync(string filename, string content)
242242
{
243243
CheckCharacters = false,
244244
Async = true,
245+
IgnoreWhitespace = true,
245246
};
246247

247248
private static XmlWriterSettings _writerSettings = new XmlWriterSettings
@@ -251,8 +252,6 @@ public async Task SaveFileAsync(string filename, string content)
251252
Async = true,
252253
CloseOutput= false,
253254
Indent = true,
254-
255-
256255
};
257256

258257
/// <inheritdoc/>
@@ -323,7 +322,26 @@ public void CopyFolder(string source, string target)
323322
{
324323
File.Copy(file, file.Replace(resolvedSource, resolvedTarget), true);
325324
}
325+
}
326+
327+
public async Task<int> MakeSingleExportFromFolders(string[] folders, string itemType, ISyncTrackerBase? trackerBase, string filename, string extension)
328+
{
329+
var merged = await MergeFoldersAsync(folders, extension, trackerBase);
330+
331+
var megaNode = new XElement(itemType + "s");
332+
int count = 0;
333+
foreach(var item in merged)
334+
{
335+
count++;
336+
megaNode.Add(new XElement(item.Node));
337+
}
338+
339+
var resolvedTargetFile = GetAbsPath(filename);
340+
CreateFoldersForFile(resolvedTargetFile);
341+
342+
await SaveXElementAsync(megaNode, resolvedTargetFile);
326343

344+
return count;
327345
}
328346

329347
/// <inheritdoc/>

uSync.BackOffice/Services/SyncService.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,6 @@
2727

2828
namespace uSync.BackOffice;
2929

30-
31-
/// <summary>
32-
/// Callback event for SignalR hub
33-
/// </summary>
34-
public delegate void SyncEventCallback(SyncProgressSummary summary);
35-
3630
/// <summary>
3731
/// the service that does all the processing,
3832
/// this forms the entry point as an API to

uSync.BackOffice/Services/SyncService_Files.cs

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1-
using System;
1+
using Microsoft.Extensions.Logging;
2+
3+
using System;
4+
using System.Collections.Generic;
25
using System.IO;
36
using System.IO.Compression;
47
using System.Linq;
8+
using System.Threading.Tasks;
9+
10+
using uSync.BackOffice.SyncHandlers.Models;
511

612
namespace uSync.BackOffice;
713

@@ -104,4 +110,29 @@ private static string CleanPathForZip(string path)
104110
=> Path.GetFullPath(
105111
path.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar))
106112
.TrimEnd(Path.DirectorySeparatorChar);
113+
114+
/// <inheritdoc />
115+
public async Task<int> MergeExportFolder(string[] paths, IEnumerable<HandlerConfigPair> handlers)
116+
{
117+
var totalMerged = 0;
118+
119+
foreach (var handler in handlers)
120+
{
121+
var serializerType = handler.Handler.GetSerializerType();
122+
var baseTracker = handler.Handler.GetBaseTracker();
123+
if (serializerType is null || baseTracker is null)
124+
{
125+
_logger.LogWarning("Handler {Handler} does not support file merging", handler.Handler.Alias);
126+
continue;
127+
}
128+
129+
var folders = paths.Select(x => Path.Combine(x, handler.Handler.DefaultFolder)).ToArray();
130+
var targetFileName = Path.Combine(_uSyncConfig.Settings.ProductionFolder,
131+
handler.Handler.DefaultFolder + "." + _uSyncConfig.Settings.DefaultExtension);
132+
133+
totalMerged += await _syncFileService.MakeSingleExportFromFolders(folders, serializerType, baseTracker, targetFileName, _uSyncConfig.Settings.DefaultExtension);
134+
}
135+
136+
return totalMerged;
137+
}
107138
}

0 commit comments

Comments
 (0)