From 521c03893da925a4f3284f9f7e7dcd861ae88221 Mon Sep 17 00:00:00 2001 From: Chase Hanson Date: Fri, 24 Jan 2025 22:59:08 -0800 Subject: [PATCH] async loops --- NP.Lti13Platform.Core/README.md | 2 +- NP.Lti13Platform.Core/Startup.cs | 2 +- .../README.md | 2 +- .../Startup.cs | 37 +++++++++++-------- 4 files changed, 24 insertions(+), 19 deletions(-) diff --git a/NP.Lti13Platform.Core/README.md b/NP.Lti13Platform.Core/README.md index b4c7c41..9af3e57 100644 --- a/NP.Lti13Platform.Core/README.md +++ b/NP.Lti13Platform.Core/README.md @@ -204,7 +204,7 @@ The expiration time of the access tokens handed out to the tools. The LTI specs allow for messages to be extended with custom data. This is handled by adding `Populators` in the setup of the platform. To extend the message, create an interface with the properties that will be used to extend the message, and create a `Populator` to fill those properties when the request for that message is generated. -Multiple populators can be added to the same interface. Multiple interfaces can be added to the same message type. The populator interface properties support the System.Text.Json attributes for serialization. +Multiple populators can be added to the same interface. Multiple interfaces can be added to the same message type. The populator interface properties support the System.Text.Json attributes for serialization. Populators must be thread safe or have a Transient Dependency Injection strategy. ```csharp interface ICustomMessage diff --git a/NP.Lti13Platform.Core/Startup.cs b/NP.Lti13Platform.Core/Startup.cs index 641c7f3..88f29e8 100644 --- a/NP.Lti13Platform.Core/Startup.cs +++ b/NP.Lti13Platform.Core/Startup.cs @@ -287,7 +287,7 @@ public static IEndpointRouteBuilder UseLti13PlatformCore(this IEndpointRouteBuil messageHintString); var services = serviceProvider.GetKeyedServices(messageTypeString); - foreach (var service in services) // TODO: await in list + foreach (var service in services) { await service.PopulateAsync(ltiMessage, scope, cancellationToken); } diff --git a/NP.Lti13Platform.NameRoleProvisioningServices/README.md b/NP.Lti13Platform.NameRoleProvisioningServices/README.md index 671e608..223737d 100644 --- a/NP.Lti13Platform.NameRoleProvisioningServices/README.md +++ b/NP.Lti13Platform.NameRoleProvisioningServices/README.md @@ -97,7 +97,7 @@ The base url used to tell tools where the service is located. ## Member Message -The IMS [Name and Role Provisioning Services](https://www.imsglobal.org/spec/lti-nrps/v2p0#message-section) spec defines a way to give tools access to the parts of LTI messages that are specific to members. This project includes the specifics for the core message and known properties defined within the spec. Additional message can be added by calling `ExtendNameRoleProvisioningMessage` on startup. This follows the same pattern as [Populators](../NP.Lti13Platform.Core/README.md#populators) from the core spec. These messages should only contain the user specific message properties of the given message. Multiple populators may be added for the same interface and multiple interfaces may be added for the same . +The IMS [Name and Role Provisioning Services](https://www.imsglobal.org/spec/lti-nrps/v2p0#message-section) spec defines a way to give tools access to the parts of LTI messages that are specific to members. This project includes the specifics for the core message and known properties defined within the spec. Additional message can be added by calling `ExtendNameRoleProvisioningMessage` on startup. This follows the same pattern as [Populators](../NP.Lti13Platform.Core/README.md#populators) from the core spec. These messages should only contain the user specific message properties of the given message. Multiple populators may be added for the same interface and multiple interfaces may be added for the same . Populators must be thread safe or have a Transient Dependency Injection strategy. ```csharp builder.Services diff --git a/NP.Lti13Platform.NameRoleProvisioningServices/Startup.cs b/NP.Lti13Platform.NameRoleProvisioningServices/Startup.cs index 677d627..2a3c82d 100644 --- a/NP.Lti13Platform.NameRoleProvisioningServices/Startup.cs +++ b/NP.Lti13Platform.NameRoleProvisioningServices/Startup.cs @@ -164,7 +164,7 @@ public static IEndpointRouteBuilder UseLti13PlatformNameRoleProvisioningServices .Select(x => x.OrderByDescending(y => y.IsCurrent).First()); } - var messages = new Dictionary>(); + var messages = new Dictionary>(); if (!string.IsNullOrWhiteSpace(rlid)) { var resourceLink = await coreDataService.GetResourceLinkAsync(rlid, cancellationToken); @@ -173,38 +173,43 @@ public static IEndpointRouteBuilder UseLti13PlatformNameRoleProvisioningServices return Results.BadRequest(new { Error = "resource link unavailable", Error_Description = "resource link does not exist in the context", Error_Uri = "https://www.imsglobal.org/spec/lti-nrps/v2p0#access-restriction" }); } - var messageTypes = LtiMessageTypes.ToDictionary(mt => mt.Key, mt => serviceProvider.GetKeyedServices(mt.Key)); - - foreach (var currentUser in currentUsers) // TODO: await in list + IEnumerable<(MessageType MessageType, NameRoleProvisioningMessage Message, MessageScope Scope)> GetUserMessages(User user) { - ICollection userMessages = []; - messages.Add(currentUser.User.Id, userMessages); - var scope = new MessageScope( - new UserScope(currentUser.User, ActualUser: null, IsAnonymous: false), + new UserScope(user, ActualUser: null, IsAnonymous: false), tool, deployment, context, resourceLink, MessageHint: null); - foreach (var messageType in messageTypes) // TODO: await in list + foreach (var messageType in LtiMessageTypes) { var message = serviceProvider.GetKeyedService(messageType.Key); if (message != null) { message.MessageType = messageType.Key.Name; - - foreach (var populator in messageType.Value) // TODO: await in list - { - await populator.PopulateAsync(message, scope, cancellationToken); - } - - userMessages.Add(message); + yield return (messageType.Key, message, scope); } } } + + var userMessages = currentUsers.SelectMany(currentUser => GetUserMessages(currentUser.User)); + + var tasks = userMessages.Select(async userMessage => + { + var populators = serviceProvider.GetKeyedServices(userMessage.MessageType.Name); + + foreach (var populator in populators) + { + await populator.PopulateAsync(userMessage.Message, userMessage.Scope, cancellationToken); + } + + return (userMessage.Scope.UserScope.User.Id, userMessage.Message); + }); + + messages = (await Task.WhenAll(tasks)).GroupBy(x => x.Id).ToDictionary(x => x.Key, x => x.Select(y => y.Message)); } var usersWithRoles = currentUsers.Where(u => u.Membership.Roles.Any());