From 29ad671c3793163d1b058b5b082a6e2e4c4441c3 Mon Sep 17 00:00:00 2001 From: Robert Brands Date: Thu, 14 Sep 2023 15:07:10 +0200 Subject: [PATCH] Federation (#126) * Libraries updated * Setting federation fiel * Interim version * Working version * Working version * Working federation * NuGet packages updated * NuGet Package updated --------- Co-authored-by: Robert Brands (RiwaAdmin) --- .../AddParticipantToCalendarItem.cs | 1 + MeetUpFunctions/Constants.cs | 2 +- MeetUpFunctions/CosmosDBRepository.cs | 42 +++++++++ MeetUpFunctions/GetExtendedCalendarItems.cs | 16 +++- .../MeetUpPlanner.Functions.csproj | 6 +- MeetUpFunctions/RemoveFederation.cs | 86 +++++++++++++++++++ MeetUpPlanner/Client/BackendApiRepository.cs | 10 +++ .../Client/MeetUpPlanner.Client.csproj | 16 ++-- MeetUpPlanner/Client/Pages/About.razor | 2 +- MeetUpPlanner/Client/Pages/Calendar.razor | 21 ++++- MeetUpPlanner/Client/Pages/NewMeetUp.razor | 33 ++++++- MeetUpPlanner/Client/Shared/MeetUpCard.razor | 12 +++ .../Client/Shared/ParticipantsList.razor | 19 +++- .../Server/Controllers/CalendarController.cs | 7 ++ .../Server/Controllers/UtilController.cs | 2 +- .../Server/MeetUpPlanner.Server.csproj | 4 +- .../Server/Repositories/MeetUpFunctions.cs | 10 +++ MeetUpPlanner/Shared/CalendarItem.cs | 8 ++ MeetUpPlanner/Shared/ClientSettings.cs | 2 + MeetUpPlanner/Shared/ExtendedCalendarItem.cs | 2 + .../Shared/MeetUpPlanner.Shared.csproj | 2 +- MeetUpPlanner/Shared/Participant.cs | 2 + MeetUpPlanner/Shared/ServerSettings.cs | 2 + MeetUpPlanner/Shared/TenantSettings.cs | 2 + 24 files changed, 286 insertions(+), 23 deletions(-) create mode 100644 MeetUpFunctions/RemoveFederation.cs diff --git a/MeetUpFunctions/AddParticipantToCalendarItem.cs b/MeetUpFunctions/AddParticipantToCalendarItem.cs index 8b62d9c..1efeeef 100644 --- a/MeetUpFunctions/AddParticipantToCalendarItem.cs +++ b/MeetUpFunctions/AddParticipantToCalendarItem.cs @@ -112,6 +112,7 @@ public async Task Run( { participant.Tenant = tenant; } + participant.Federation = serverSettings.Federation; participant = await _cosmosRepository.UpsertItem(participant); BackendResult result = new BackendResult(true); diff --git a/MeetUpFunctions/Constants.cs b/MeetUpFunctions/Constants.cs index 800a5a7..29aa392 100644 --- a/MeetUpFunctions/Constants.cs +++ b/MeetUpFunctions/Constants.cs @@ -22,7 +22,7 @@ public static class Constants public const string DEFAULT_DISCLAIMER = "Disclaimer"; public const string DEFAULT_GUEST_DISCLAIMER = "Guest Disclaimer"; - public const string VERSION = "2023-05-09"; + public const string VERSION = "2023-09-10"; public const int ADMINOVERBOOKFACTOR = 1; // no overbooking any more, because not needed public const int LOG_TTL = 30 * 24 * 3600; // 30 days TTL for Log items diff --git a/MeetUpFunctions/CosmosDBRepository.cs b/MeetUpFunctions/CosmosDBRepository.cs index b85f700..376a8bb 100644 --- a/MeetUpFunctions/CosmosDBRepository.cs +++ b/MeetUpFunctions/CosmosDBRepository.cs @@ -18,6 +18,8 @@ namespace MeetUpPlanner.Functions private CosmosClient _cosmosClient; string _cosmosDbDatabase; string _cosmosDbContainer; + const int MAX_PATCH_OPERATIONS = 10; + /// /// Create repository, typically as singleton. Create CosmosClient before. @@ -167,7 +169,47 @@ public async Task UpsertItem(T item) return response.Resource; } + public async Task PatchItem(string id, IReadOnlyList patchOperations, string timestamp = null) + { + Container container = _cosmosClient.GetDatabase(_cosmosDbDatabase).GetContainer(_cosmosDbContainer); + PartitionKey partitionKey = new PartitionKey(typeof(T).Name); + + if (String.IsNullOrEmpty(id)) + { + throw new ArgumentNullException("id"); + } + PatchItemRequestOptions patchOption = new PatchItemRequestOptions(); + if (null != timestamp) + { + patchOption.FilterPredicate = $"from c where c._ts = {timestamp}"; + } + // PatchItem only supports up to 10 operations. If there are more than that given, create more batches and + // patch the document several times. + ItemResponse response = null; + do + { + List po = new List(patchOperations.Take(MAX_PATCH_OPERATIONS)); + response = await container.PatchItemAsync( + id: id, + partitionKey: partitionKey, + patchOperations: po, + requestOptions: patchOption + ); + patchOperations = new List(patchOperations.Skip(MAX_PATCH_OPERATIONS)); + } + while (patchOperations.Count > 0); + + return response.Resource; + } + public async Task PatchField(string id, string fieldName, object value, string timestamp = null) + { + List patchOperations = new() + { + PatchOperation.Add($"/{fieldName}", value) + }; + return await PatchItem(id, patchOperations, timestamp); + } } } diff --git a/MeetUpFunctions/GetExtendedCalendarItems.cs b/MeetUpFunctions/GetExtendedCalendarItems.cs index cf978d8..1d46e1d 100644 --- a/MeetUpFunctions/GetExtendedCalendarItems.cs +++ b/MeetUpFunctions/GetExtendedCalendarItems.cs @@ -76,8 +76,22 @@ public async Task Run( { rawListOfCalendarItems = await _cosmosRepository.GetItems(d => d.StartDate > compareDate && d.Tenant.Equals(tenant)); } + IEnumerable rawCalendarItemsWithFederatedOnes = rawListOfCalendarItems; + if (!String.IsNullOrEmpty(serverSettings.Federation)) + { + IEnumerable rawListOfFederatedCalendarItems; + if (null == tenant) + { + rawListOfFederatedCalendarItems = await _cosmosRepository.GetItems(d => d.StartDate > compareDate && (d.Tenant ?? String.Empty) == String.Empty && d.Federation == serverSettings.Federation); + } + else + { + rawListOfFederatedCalendarItems = await _cosmosRepository.GetItems(d => d.StartDate > compareDate && d.Tenant.Equals(tenant) && d.Federation == serverSettings.Federation); + } + rawCalendarItemsWithFederatedOnes = rawListOfCalendarItems.Concat(rawListOfFederatedCalendarItems); + } List resultCalendarItems = new List(10); - foreach (CalendarItem item in rawListOfCalendarItems) + foreach (CalendarItem item in rawCalendarItemsWithFederatedOnes) { // Create ExtendedCalendarItem and get comments and participants ExtendedCalendarItem extendedItem = new ExtendedCalendarItem(item); diff --git a/MeetUpFunctions/MeetUpPlanner.Functions.csproj b/MeetUpFunctions/MeetUpPlanner.Functions.csproj index 2c6baa0..d202630 100644 --- a/MeetUpFunctions/MeetUpPlanner.Functions.csproj +++ b/MeetUpFunctions/MeetUpPlanner.Functions.csproj @@ -6,10 +6,10 @@ - - + + - + diff --git a/MeetUpFunctions/RemoveFederation.cs b/MeetUpFunctions/RemoveFederation.cs new file mode 100644 index 0000000..61a0982 --- /dev/null +++ b/MeetUpFunctions/RemoveFederation.cs @@ -0,0 +1,86 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Azure.WebJobs; +using Microsoft.Azure.WebJobs.Extensions.Http; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using MeetUpPlanner.Shared; +using System.Web.Http; +using Aliencube.AzureFunctions.Extensions.OpenApi.Core.Attributes; + + +namespace MeetUpPlanner.Functions +{ + public class RemoveFederation + { + private readonly ILogger _logger; + private ServerSettingsRepository _serverSettingsRepository; + private CosmosDBRepository _cosmosRepository; + private CosmosDBRepository _participantRepository; + private NotificationSubscriptionRepository _subscriptionRepository; + + public RemoveFederation(ILogger logger, + ServerSettingsRepository serverSettingsRepository, + NotificationSubscriptionRepository subscriptionRepository, + CosmosDBRepository participantRepository, + CosmosDBRepository cosmosRepository) + { + _logger = logger; + _serverSettingsRepository = serverSettingsRepository; + _cosmosRepository = cosmosRepository; + _participantRepository = participantRepository; + _subscriptionRepository = subscriptionRepository; + } + + /// + /// Removes the federation from CalendarItem to the database. x-meetup-keyword must be set to admin keyword. + /// + /// + /// + [FunctionName("RemoveFederation")] + public async Task Run( + [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req) + { + _logger.LogInformation("C# HTTP trigger function RemoveFederation processed a request."); + string tenant = req.Headers[Constants.HEADER_TENANT]; + if (String.IsNullOrWhiteSpace(tenant)) + { + tenant = null; + } + ServerSettings serverSettings; + if (null == tenant) + { + serverSettings = await _serverSettingsRepository.GetServerSettings(); + } + else + { + serverSettings = await _serverSettingsRepository.GetServerSettings(tenant); + } + + string keyWord = req.Headers[Constants.HEADER_KEYWORD]; + if (String.IsNullOrEmpty(keyWord) || !serverSettings.IsAdmin(keyWord)) + { + return new BadRequestErrorMessageResult("Keyword is missing or wrong."); + } + string requestBody = await new StreamReader(req.Body).ReadToEndAsync(); + CalendarItem calendarItem = JsonConvert.DeserializeObject(requestBody); + if (null != tenant) + { + calendarItem.Tenant = tenant; + } + CalendarItem oldCalendarItem = null; + if (!String.IsNullOrEmpty(calendarItem.Id)) + { + oldCalendarItem = await _cosmosRepository.GetItem(calendarItem.Id); + // Patching/removing federation makes only sense if the CalendarItem still lives in the database + await _cosmosRepository.PatchField(calendarItem.Id, "federation", String.Empty); + calendarItem = await _cosmosRepository.PatchField(calendarItem.Id, "federatedFrom", String.Empty); + } + + return new OkObjectResult(calendarItem); + } + } +} diff --git a/MeetUpPlanner/Client/BackendApiRepository.cs b/MeetUpPlanner/Client/BackendApiRepository.cs index ad1b905..4c40c64 100644 --- a/MeetUpPlanner/Client/BackendApiRepository.cs +++ b/MeetUpPlanner/Client/BackendApiRepository.cs @@ -241,5 +241,15 @@ public static string GetUrlFriendlyTitle(string title) return urlFriendlyTitle; } + public async Task RemoveFederation(CalendarItem calendarItem) + { + this.PrepareHttpClient(); + HttpResponseMessage response = await _http.PostAsJsonAsync($"Calendar/removefederation", calendarItem); + response.EnsureSuccessStatusCode(); + _http.DefaultRequestHeaders.Remove(HEADER_TENANT); + return; + + } + } } diff --git a/MeetUpPlanner/Client/MeetUpPlanner.Client.csproj b/MeetUpPlanner/Client/MeetUpPlanner.Client.csproj index 3d16ac1..d9907bf 100644 --- a/MeetUpPlanner/Client/MeetUpPlanner.Client.csproj +++ b/MeetUpPlanner/Client/MeetUpPlanner.Client.csproj @@ -6,16 +6,16 @@ - + - - + + - - - - - + + + + + diff --git a/MeetUpPlanner/Client/Pages/About.razor b/MeetUpPlanner/Client/Pages/About.razor index 72e770e..28a5ccd 100644 --- a/MeetUpPlanner/Client/Pages/About.razor +++ b/MeetUpPlanner/Client/Pages/About.razor @@ -51,7 +51,7 @@ @code { - private const string clientVersion = "2023-05-09"; + private const string clientVersion = "2023-09-10"; private string serverVersion = "tbd"; private string functionsVersion = "tbd"; diff --git a/MeetUpPlanner/Client/Pages/Calendar.razor b/MeetUpPlanner/Client/Pages/Calendar.razor index b0e7bc5..53b3b18 100644 --- a/MeetUpPlanner/Client/Pages/Calendar.razor +++ b/MeetUpPlanner/Client/Pages/Calendar.razor @@ -4,6 +4,7 @@ @inject AppState AppStateStore @inject KeywordCheck KeywordCheck @inject HttpClient Http +@inject BackendApiRepository Api @inject NavigationManager NavigationManager @inject NotificationService notificationService @inject DialogService dialogService @@ -70,6 +71,7 @@ } + @{ string badge = GetCalendarBadge(item); if (!String.IsNullOrEmpty(badge)) @@ -180,7 +182,14 @@