diff --git a/SgfDevs.Tests/ImportedPresenterBlockBuilderTests.cs b/SgfDevs.Tests/ImportedPresenterBlockBuilderTests.cs index b293ebd..f027ffc 100644 --- a/SgfDevs.Tests/ImportedPresenterBlockBuilderTests.cs +++ b/SgfDevs.Tests/ImportedPresenterBlockBuilderTests.cs @@ -8,6 +8,8 @@ namespace SgfDevs.Tests; public class ImportedPresenterBlockBuilderTests { + private static readonly Guid PresentationKey = new("11111111-1111-1111-1111-111111111111"); + private readonly ImportedPresenterBlockBuilder _builder = new( new Guid("1bdea08d-8393-4e70-85a9-2ca27bef54f1"), new Guid("5ff3a2c3-9dc3-4131-8f07-99c2c0a38be5")); @@ -15,7 +17,7 @@ public class ImportedPresenterBlockBuilderTests [Fact] public void Build_ReturnsEmptyStringWhenNoPresentersExist() { - var result = _builder.Build([]); + var result = _builder.Build(PresentationKey, []); Assert.Equal(string.Empty, result); } @@ -24,10 +26,11 @@ public void Build_ReturnsEmptyStringWhenNoPresentersExist() public void Build_CreatesBlockListPayloadForNonMemberPresenters() { var result = _builder.Build( - [ - new ImportedPresenterPlan("speaker-1", "Bertram Gilfoyle", null, MatchedMemberKey: new Guid("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")), - new ImportedPresenterPlan("speaker-2", "Dinesh Chugtai", null) - ]); + PresentationKey, + [ + new ImportedPresenterPlan("speaker-1", "Bertram Gilfoyle", null, MatchedMemberKey: new Guid("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")), + new ImportedPresenterPlan("speaker-2", "Dinesh Chugtai", null) + ]); using var document = JsonDocument.Parse(result); var root = document.RootElement; @@ -45,4 +48,39 @@ public void Build_CreatesBlockListPayloadForNonMemberPresenters() Assert.Equal("Dinesh Chugtai", secondValues[0].GetProperty("value").GetString()); Assert.Equal(string.Empty, secondValues[1].GetProperty("value").GetString()); } + + [Fact] + public void Build_ReturnsSamePayloadForSamePresenters() + { + var presenters = new[] + { + new ImportedPresenterPlan("speaker-1", "Bertram Gilfoyle", null, MatchedMemberKey: new Guid("aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa")), + new ImportedPresenterPlan("speaker-2", "Dinesh Chugtai", null) + }; + + var first = _builder.Build(PresentationKey, presenters); + var second = _builder.Build(PresentationKey, presenters); + + Assert.Equal(first, second); + } + + [Fact] + public void Build_ReturnsDifferentBlockKeysForDifferentPresentations() + { + var presenters = new[] + { + new ImportedPresenterPlan("speaker-1", "Bertram Gilfoyle", null) + }; + + var first = GetFirstBlockKey(_builder.Build(PresentationKey, presenters)); + var second = GetFirstBlockKey(_builder.Build(new Guid("22222222-2222-2222-2222-222222222222"), presenters)); + + Assert.NotEqual(first, second); + } + + private static Guid GetFirstBlockKey(string payload) + { + using var document = JsonDocument.Parse(payload); + return document.RootElement.GetProperty("contentData")[0].GetProperty("key").GetGuid(); + } } diff --git a/SgfDevs/Dev/EventSync/ImportedPresenterBlockBuilder.cs b/SgfDevs/Dev/EventSync/ImportedPresenterBlockBuilder.cs index 6b0ac72..414ab8f 100644 --- a/SgfDevs/Dev/EventSync/ImportedPresenterBlockBuilder.cs +++ b/SgfDevs/Dev/EventSync/ImportedPresenterBlockBuilder.cs @@ -2,6 +2,8 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Security.Cryptography; +using System.Text; using System.Text.Json; using SgfDevs.Dev.EventSync.Sessionize; using Umbraco.Cms.Core; @@ -29,7 +31,7 @@ internal ImportedPresenterBlockBuilder(Guid presenterPickerTypeKey, Guid nonMemb _nonMemberPresenterTypeKey = nonMemberPresenterTypeKey; } - public string Build(IReadOnlyList presenters) + public string Build(Guid presentationKey, IReadOnlyList presenters) { if (presenters.Count == 0) { @@ -45,7 +47,7 @@ public string Build(IReadOnlyList presenters) var blocks = presenters.Select(presenter => { - var key = Guid.NewGuid(); + var key = BuildBlockKey(presentationKey, presenter); var isMatchedMember = presenter.MatchedMemberKey.HasValue; return new @@ -114,4 +116,13 @@ public string Build(IReadOnlyList presenters) return JsonSerializer.Serialize(payload); } + + private static Guid BuildBlockKey(Guid presentationKey, ImportedPresenterPlan presenter) + { + var presenterIdentity = presenter.MatchedMemberKey?.ToString("N") + ?? (string.IsNullOrWhiteSpace(presenter.SessionizeSpeakerId) ? presenter.Name : presenter.SessionizeSpeakerId); + var identity = $"{presentationKey:N}\n{presenterIdentity}"; + var hash = SHA256.HashData(Encoding.UTF8.GetBytes(identity)); + return new Guid(hash.AsSpan(0, 16)); + } } diff --git a/SgfDevs/Dev/EventSync/SessionizeEventSyncService.cs b/SgfDevs/Dev/EventSync/SessionizeEventSyncService.cs index 95e72b5..1a4676c 100644 --- a/SgfDevs/Dev/EventSync/SessionizeEventSyncService.cs +++ b/SgfDevs/Dev/EventSync/SessionizeEventSyncService.cs @@ -112,8 +112,7 @@ public async Task SyncAsync(CancellationToken cancellationToken = default) var eventContent = GetOrCreateEvent(existingEvents, references.EventsContainerId, eventPlan); SaveEventContent(eventContent, eventPlan); - _contentService.Save(eventContent, SystemUserId); - var presentationsToPublish = new List(); + SaveAndPublishContent(eventContent, "event", clearSchedule: true); var existingPresentations = GetChildren(eventContent.Id) .Where(content => string.Equals(content.ContentType.Alias, PresentationAlias, StringComparison.Ordinal)) @@ -136,14 +135,7 @@ public async Task SyncAsync(CancellationToken cancellationToken = default) var presentationContent = GetOrCreatePresentation(existingPresentations, eventContent.Id, presentationPlan); SavePresentationContent(presentationContent, references.SpringfieldDevsGroupKey, enrichedPresentationPlan, meetupMatch?.EventUrl); - _contentService.Save(presentationContent, SystemUserId); - presentationsToPublish.Add(presentationContent); - } - - PublishContent(eventContent, "event", clearSchedule: true); - foreach (var presentationContent in presentationsToPublish) - { - PublishContent(presentationContent, "presentation"); + SaveAndPublishContent(presentationContent, "presentation"); } } } @@ -230,7 +222,7 @@ private void SavePresentationContent( presentationContent.SetValue(MeetupUrlPropertyAlias, meetupUrl); } - presentationContent.SetValue(PresentersPropertyAlias, _presenterBlockBuilder.Build(presentationPlan.Presenters)); + presentationContent.SetValue(PresentersPropertyAlias, _presenterBlockBuilder.Build(presentationContent.Key, presentationPlan.Presenters)); } private async Task> ImportPresenterImagesAsync( @@ -262,29 +254,31 @@ private async Task> ImportPresenterImagesAs return enrichedPresenters; } - private void PublishContent(IContent content, string contentKind, bool clearSchedule = false) + private void SaveAndPublishContent(IContent content, string contentKind, bool clearSchedule = false) { - if (clearSchedule) + if (content.IsDirty() == false && content.IsCultureEdited(null!) == false) + { + return; + } + + if (clearSchedule && content.HasIdentity) { _contentService.PersistContentSchedule(content, new ContentScheduleCollection()); } - var publishResult = _contentService.Publish(content, ["*"], SystemUserId); + var publishResult = _contentService.SaveAndPublish(content, [], SystemUserId); if (publishResult.Success) { _logger.LogInformation( - "Published {ContentKind} {ContentName} with result {PublishResult}.", + "Saved and published {ContentKind} {ContentName} with result {PublishResult}.", contentKind, content.Name, publishResult.Result); return; } - _logger.LogWarning( - "Failed to publish {ContentKind} {ContentName}. Publish result was {PublishResult}.", - contentKind, - content.Name, - publishResult.Result); + throw new InvalidOperationException( + $"Failed to save and publish {contentKind} {content.Name}. Publish result was {publishResult.Result}."); } private IReadOnlyList GetChildren(int parentId)