Skip to content

Commit 98867bb

Browse files
authored
fix: avoid redundant session imports (#126)
Makes Sessionize imports idempotent by generating stable presenter block keys and skipping unchanged published content. It also uses Umbraco's single-scope SaveAndPublish operation and stops on persistence failures.
1 parent ed69573 commit 98867bb

3 files changed

Lines changed: 70 additions & 27 deletions

File tree

SgfDevs.Tests/ImportedPresenterBlockBuilderTests.cs

Lines changed: 43 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,16 @@ namespace SgfDevs.Tests;
88

99
public class ImportedPresenterBlockBuilderTests
1010
{
11+
private static readonly Guid PresentationKey = new("11111111-1111-1111-1111-111111111111");
12+
1113
private readonly ImportedPresenterBlockBuilder _builder = new(
1214
new Guid("1bdea08d-8393-4e70-85a9-2ca27bef54f1"),
1315
new Guid("5ff3a2c3-9dc3-4131-8f07-99c2c0a38be5"));
1416

1517
[Fact]
1618
public void Build_ReturnsEmptyStringWhenNoPresentersExist()
1719
{
18-
var result = _builder.Build([]);
20+
var result = _builder.Build(PresentationKey, []);
1921

2022
Assert.Equal(string.Empty, result);
2123
}
@@ -24,10 +26,11 @@ public void Build_ReturnsEmptyStringWhenNoPresentersExist()
2426
public void Build_CreatesBlockListPayloadForNonMemberPresenters()
2527
{
2628
var result = _builder.Build(
27-
[
28-
new ImportedPresenterPlan("speaker-1", "Bertram Gilfoyle", null, MatchedMemberKey: new Guid("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")),
29-
new ImportedPresenterPlan("speaker-2", "Dinesh Chugtai", null)
30-
]);
29+
PresentationKey,
30+
[
31+
new ImportedPresenterPlan("speaker-1", "Bertram Gilfoyle", null, MatchedMemberKey: new Guid("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")),
32+
new ImportedPresenterPlan("speaker-2", "Dinesh Chugtai", null)
33+
]);
3134

3235
using var document = JsonDocument.Parse(result);
3336
var root = document.RootElement;
@@ -45,4 +48,39 @@ public void Build_CreatesBlockListPayloadForNonMemberPresenters()
4548
Assert.Equal("Dinesh Chugtai", secondValues[0].GetProperty("value").GetString());
4649
Assert.Equal(string.Empty, secondValues[1].GetProperty("value").GetString());
4750
}
51+
52+
[Fact]
53+
public void Build_ReturnsSamePayloadForSamePresenters()
54+
{
55+
var presenters = new[]
56+
{
57+
new ImportedPresenterPlan("speaker-1", "Bertram Gilfoyle", null, MatchedMemberKey: new Guid("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")),
58+
new ImportedPresenterPlan("speaker-2", "Dinesh Chugtai", null)
59+
};
60+
61+
var first = _builder.Build(PresentationKey, presenters);
62+
var second = _builder.Build(PresentationKey, presenters);
63+
64+
Assert.Equal(first, second);
65+
}
66+
67+
[Fact]
68+
public void Build_ReturnsDifferentBlockKeysForDifferentPresentations()
69+
{
70+
var presenters = new[]
71+
{
72+
new ImportedPresenterPlan("speaker-1", "Bertram Gilfoyle", null)
73+
};
74+
75+
var first = GetFirstBlockKey(_builder.Build(PresentationKey, presenters));
76+
var second = GetFirstBlockKey(_builder.Build(new Guid("22222222-2222-2222-2222-222222222222"), presenters));
77+
78+
Assert.NotEqual(first, second);
79+
}
80+
81+
private static Guid GetFirstBlockKey(string payload)
82+
{
83+
using var document = JsonDocument.Parse(payload);
84+
return document.RootElement.GetProperty("contentData")[0].GetProperty("key").GetGuid();
85+
}
4886
}

SgfDevs/Dev/EventSync/ImportedPresenterBlockBuilder.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
using System;
33
using System.Collections.Generic;
44
using System.Linq;
5+
using System.Security.Cryptography;
6+
using System.Text;
57
using System.Text.Json;
68
using SgfDevs.Dev.EventSync.Sessionize;
79
using Umbraco.Cms.Core;
@@ -29,7 +31,7 @@ internal ImportedPresenterBlockBuilder(Guid presenterPickerTypeKey, Guid nonMemb
2931
_nonMemberPresenterTypeKey = nonMemberPresenterTypeKey;
3032
}
3133

32-
public string Build(IReadOnlyList<ImportedPresenterPlan> presenters)
34+
public string Build(Guid presentationKey, IReadOnlyList<ImportedPresenterPlan> presenters)
3335
{
3436
if (presenters.Count == 0)
3537
{
@@ -45,7 +47,7 @@ public string Build(IReadOnlyList<ImportedPresenterPlan> presenters)
4547

4648
var blocks = presenters.Select(presenter =>
4749
{
48-
var key = Guid.NewGuid();
50+
var key = BuildBlockKey(presentationKey, presenter);
4951
var isMatchedMember = presenter.MatchedMemberKey.HasValue;
5052

5153
return new
@@ -114,4 +116,13 @@ public string Build(IReadOnlyList<ImportedPresenterPlan> presenters)
114116

115117
return JsonSerializer.Serialize(payload);
116118
}
119+
120+
private static Guid BuildBlockKey(Guid presentationKey, ImportedPresenterPlan presenter)
121+
{
122+
var presenterIdentity = presenter.MatchedMemberKey?.ToString("N")
123+
?? (string.IsNullOrWhiteSpace(presenter.SessionizeSpeakerId) ? presenter.Name : presenter.SessionizeSpeakerId);
124+
var identity = $"{presentationKey:N}\n{presenterIdentity}";
125+
var hash = SHA256.HashData(Encoding.UTF8.GetBytes(identity));
126+
return new Guid(hash.AsSpan(0, 16));
127+
}
117128
}

SgfDevs/Dev/EventSync/SessionizeEventSyncService.cs

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -112,8 +112,7 @@ public async Task SyncAsync(CancellationToken cancellationToken = default)
112112

113113
var eventContent = GetOrCreateEvent(existingEvents, references.EventsContainerId, eventPlan);
114114
SaveEventContent(eventContent, eventPlan);
115-
_contentService.Save(eventContent, SystemUserId);
116-
var presentationsToPublish = new List<IContent>();
115+
SaveAndPublishContent(eventContent, "event", clearSchedule: true);
117116

118117
var existingPresentations = GetChildren(eventContent.Id)
119118
.Where(content => string.Equals(content.ContentType.Alias, PresentationAlias, StringComparison.Ordinal))
@@ -136,14 +135,7 @@ public async Task SyncAsync(CancellationToken cancellationToken = default)
136135

137136
var presentationContent = GetOrCreatePresentation(existingPresentations, eventContent.Id, presentationPlan);
138137
SavePresentationContent(presentationContent, references.SpringfieldDevsGroupKey, enrichedPresentationPlan, meetupMatch?.EventUrl);
139-
_contentService.Save(presentationContent, SystemUserId);
140-
presentationsToPublish.Add(presentationContent);
141-
}
142-
143-
PublishContent(eventContent, "event", clearSchedule: true);
144-
foreach (var presentationContent in presentationsToPublish)
145-
{
146-
PublishContent(presentationContent, "presentation");
138+
SaveAndPublishContent(presentationContent, "presentation");
147139
}
148140
}
149141
}
@@ -230,7 +222,7 @@ private void SavePresentationContent(
230222
presentationContent.SetValue(MeetupUrlPropertyAlias, meetupUrl);
231223
}
232224

233-
presentationContent.SetValue(PresentersPropertyAlias, _presenterBlockBuilder.Build(presentationPlan.Presenters));
225+
presentationContent.SetValue(PresentersPropertyAlias, _presenterBlockBuilder.Build(presentationContent.Key, presentationPlan.Presenters));
234226
}
235227

236228
private async Task<IReadOnlyList<ImportedPresenterPlan>> ImportPresenterImagesAsync(
@@ -262,29 +254,31 @@ private async Task<IReadOnlyList<ImportedPresenterPlan>> ImportPresenterImagesAs
262254
return enrichedPresenters;
263255
}
264256

265-
private void PublishContent(IContent content, string contentKind, bool clearSchedule = false)
257+
private void SaveAndPublishContent(IContent content, string contentKind, bool clearSchedule = false)
266258
{
267-
if (clearSchedule)
259+
if (content.IsDirty() == false && content.IsCultureEdited(null!) == false)
260+
{
261+
return;
262+
}
263+
264+
if (clearSchedule && content.HasIdentity)
268265
{
269266
_contentService.PersistContentSchedule(content, new ContentScheduleCollection());
270267
}
271268

272-
var publishResult = _contentService.Publish(content, ["*"], SystemUserId);
269+
var publishResult = _contentService.SaveAndPublish(content, [], SystemUserId);
273270
if (publishResult.Success)
274271
{
275272
_logger.LogInformation(
276-
"Published {ContentKind} {ContentName} with result {PublishResult}.",
273+
"Saved and published {ContentKind} {ContentName} with result {PublishResult}.",
277274
contentKind,
278275
content.Name,
279276
publishResult.Result);
280277
return;
281278
}
282279

283-
_logger.LogWarning(
284-
"Failed to publish {ContentKind} {ContentName}. Publish result was {PublishResult}.",
285-
contentKind,
286-
content.Name,
287-
publishResult.Result);
280+
throw new InvalidOperationException(
281+
$"Failed to save and publish {contentKind} {content.Name}. Publish result was {publishResult.Result}.");
288282
}
289283

290284
private IReadOnlyList<IContent> GetChildren(int parentId)

0 commit comments

Comments
 (0)