From 3d6d5ad714c4f107c4f5877c02102581b6dde35e Mon Sep 17 00:00:00 2001 From: David Jimenez Barrantes Date: Wed, 5 Feb 2025 09:06:58 -0600 Subject: [PATCH] [ADMINAPI-1117] - Adds /v2/resourceClaimActions and /v2/resourceClaimActionAuthStrategies endpoints. (#221) --- .../GetResourceClaimActionsQueryTests.cs | 117 ++++++++++++++++++ .../EdFi.Ods.AdminApi.DBTests/Testing.cs | 4 +- .../ReadResourceClaimActionAuthStrategies.cs | 27 ++++ .../ResourceClaimActionAuthStrategyModel.cs | 29 +++++ .../ReadResourceClaimActions.cs | 27 ++++ .../ResourceClaimActionModel.cs | 19 +++ .../AutoMapper/AdminApiMappingProfile.cs | 7 ++ ...ClaimActionAuthorizationStrategiesQuery.cs | 80 ++++++++++++ .../Queries/GetResourceClaimActionsQuery.cs | 61 +++++++++ 9 files changed, 369 insertions(+), 2 deletions(-) create mode 100644 Application/EdFi.Ods.AdminApi.DBTests/Database/QueryTests/GetResourceClaimActionsQueryTests.cs create mode 100644 Application/EdFi.Ods.AdminApi/Features/ResourceClaimActionAuthStrategies/ReadResourceClaimActionAuthStrategies.cs create mode 100644 Application/EdFi.Ods.AdminApi/Features/ResourceClaimActionAuthStrategies/ResourceClaimActionAuthStrategyModel.cs create mode 100644 Application/EdFi.Ods.AdminApi/Features/ResourceClaimActions/ReadResourceClaimActions.cs create mode 100644 Application/EdFi.Ods.AdminApi/Features/ResourceClaimActions/ResourceClaimActionModel.cs create mode 100644 Application/EdFi.Ods.AdminApi/Infrastructure/Database/Queries/GetResourceClaimActionAuthorizationStrategiesQuery.cs create mode 100644 Application/EdFi.Ods.AdminApi/Infrastructure/Database/Queries/GetResourceClaimActionsQuery.cs diff --git a/Application/EdFi.Ods.AdminApi.DBTests/Database/QueryTests/GetResourceClaimActionsQueryTests.cs b/Application/EdFi.Ods.AdminApi.DBTests/Database/QueryTests/GetResourceClaimActionsQueryTests.cs new file mode 100644 index 000000000..cbc00096f --- /dev/null +++ b/Application/EdFi.Ods.AdminApi.DBTests/Database/QueryTests/GetResourceClaimActionsQueryTests.cs @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: Apache-2.0 +// Licensed to the Ed-Fi Alliance under one or more agreements. +// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0. +// See the LICENSE and NOTICES files in the project root for more information. + +using System.Collections.Generic; +using System.Linq; +using EdFi.Ods.AdminApi.Features.ResourceClaimActions; +using EdFi.Ods.AdminApi.Infrastructure; +using EdFi.Ods.AdminApi.Infrastructure.Database.Queries; +using EdFi.Security.DataAccess.Models; +using NUnit.Framework; +using Shouldly; + +namespace EdFi.Ods.AdminApi.DBTests.Database.QueryTests; + +[TestFixture] +public class GetResourceClaimActionsQueryTests : SecurityDataTestBase +{ + [Test] + public void ShouldGetResourceClaimActions() + { + var skip = 0; + ResourceClaimActionModel[] results = null; + using var securityContext = TestContext; + var actions = SetupActions().Select(s => s.ActionId).ToArray(); + var resourceClaimId = SetupResourceClaims().FirstOrDefault().ResourceClaimId; + var testResourceClaimActions = SetupResourceClaimActions(actions, resourceClaimId); + var query = new GetResourceClaimActionsQuery(securityContext, Testing.GetAppSettings()); + results = query.Execute(new CommonQueryParams(skip, Testing.DefaultPageSizeLimit)).ToArray(); + results.SelectMany(x => x.Actions).Count().ShouldBe(testResourceClaimActions.Count); + results.Select(x => x.ResourceClaimId).ShouldBe(testResourceClaimActions.Select(s => s.ResourceClaimId).Distinct(), true); + results.Select(x => x.ResourceName).ShouldBe(testResourceClaimActions.Select(x => x.ResourceClaim.ResourceName).Distinct(), true); + } + + [Test] + public void ShouldGetAllResourceClaimActions_With_Offset_and_Limit() + { + var offset = 1; + var limit = 2; + + ResourceClaimActionModel[] results = null; + using var securityContext = TestContext; + //Set actions + var actions = SetupActions().Select(s => s.ActionId).ToArray(); + //Set resourceClaims + var resourceClaims = SetupResourceClaims(4); + + foreach (var resourceClaim in resourceClaims) + { + var testResourceClaimActions = SetupResourceClaimActions(actions, resourceClaim.ResourceClaimId); + } + //Add ResourceClaimActions + var query = new GetResourceClaimActionsQuery(securityContext, Testing.GetAppSettings()); + results = query.Execute(new CommonQueryParams(offset, limit)).ToArray(); + + results.Length.ShouldBe(2); + results[0].ResourceName.ShouldBe("TestResourceClaim2.00"); + results[1].ResourceName.ShouldBe("TestResourceClaim3.00"); + results[0].Actions.Any().ShouldBe(true); + results[1].Actions.Any().ShouldBe(true); + } + + private IReadOnlyCollection SetupResourceClaimActions(int[] actions, int resourceClaimId) + { + var resourceClaimActions = new List(); + var resourceClaimCount = actions.Length; + foreach (var index in Enumerable.Range(1, resourceClaimCount)) + { + var resourceClaim = new ResourceClaimAction + { + ActionId = actions[index - 1], + ResourceClaimId = resourceClaimId, + ValidationRuleSetName = $"Test{index}" + }; + resourceClaimActions.Add(resourceClaim); + } + Save(resourceClaimActions.Cast().ToArray()); + return resourceClaimActions; + } + + private IReadOnlyCollection SetupResourceClaims(int resourceClaimCount = 1) + { + var resourceClaims = new List(); + foreach (var index in Enumerable.Range(1, resourceClaimCount)) + { + var resourceClaim = new ResourceClaim + { + ClaimName = $"TestResourceClaim{index:N}", + ResourceName = $"TestResourceClaim{index:N}", + }; + resourceClaims.Add(resourceClaim); + } + + Save(resourceClaims.Cast().ToArray()); + + return resourceClaims; + } + + private IReadOnlyCollection SetupActions(int resourceClaimCount = 5) + { + var actions = new List(); + foreach (var index in Enumerable.Range(1, resourceClaimCount)) + { + var action = new Security.DataAccess.Models.Action + { + ActionName = $"TestResourceClaim{index:N}", + ActionUri = $"http://ed-fi.org/odsapi/actions/TestResourceClaim{index:N}" + }; + actions.Add(action); + } + + Save(actions.Cast().ToArray()); + + return actions; + } +} diff --git a/Application/EdFi.Ods.AdminApi.DBTests/Testing.cs b/Application/EdFi.Ods.AdminApi.DBTests/Testing.cs index 628203028..a945bcb75 100644 --- a/Application/EdFi.Ods.AdminApi.DBTests/Testing.cs +++ b/Application/EdFi.Ods.AdminApi.DBTests/Testing.cs @@ -28,9 +28,9 @@ public static IConfiguration Configuration() public static string SecurityConnectionString { get { return Configuration().GetConnectionString("EdFi_Security"); } } - public static int DefaultPageSizeOffset => (int)Configuration().GetValue(typeof(int), "DefaultPageSizeOffset"); + public static int DefaultPageSizeOffset => Configuration().GetSection("AppSettings").GetValue("DefaultPageSizeOffset"); - public static int DefaultPageSizeLimit => (int)Configuration().GetValue(typeof(int), "DefaultPageSizeLimit"); + public static int DefaultPageSizeLimit => Configuration().GetSection("AppSettings").GetValue("DefaultPageSizeLimit"); public static DbContextOptions GetDbContextOptions(string connectionString) { diff --git a/Application/EdFi.Ods.AdminApi/Features/ResourceClaimActionAuthStrategies/ReadResourceClaimActionAuthStrategies.cs b/Application/EdFi.Ods.AdminApi/Features/ResourceClaimActionAuthStrategies/ReadResourceClaimActionAuthStrategies.cs new file mode 100644 index 000000000..699557a35 --- /dev/null +++ b/Application/EdFi.Ods.AdminApi/Features/ResourceClaimActionAuthStrategies/ReadResourceClaimActionAuthStrategies.cs @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: Apache-2.0 +// Licensed to the Ed-Fi Alliance under one or more agreements. +// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0. +// See the LICENSE and NOTICES files in the project root for more information. + +using AutoMapper; +using EdFi.Ods.AdminApi.Infrastructure; +using EdFi.Ods.AdminApi.Infrastructure.Database.Queries; + +namespace EdFi.Ods.AdminApi.Features.ResourceClaimActionAuthStrategies; + +public class ReadResourceClaimActionAuthStrategies : IFeature +{ + public void MapEndpoints(IEndpointRouteBuilder endpoints) + { + AdminApiEndpointBuilder.MapGet(endpoints, "/resourceClaimActionAuthStrategies", GetResourceClaimActionAuthorizationStrategies) + .WithDefaultSummaryAndDescription() + .WithRouteOptions(b => b.WithResponse>(200)) + .BuildForVersions(AdminApiVersions.V2); + } + + internal Task GetResourceClaimActionAuthorizationStrategies(IGetResourceClaimActionAuthorizationStrategiesQuery getResourceClaimActionAuthorizationStrategiesQuery, [AsParameters] CommonQueryParams commonQueryParams) + { + var resourceClaims = getResourceClaimActionAuthorizationStrategiesQuery.Execute(commonQueryParams); + return Task.FromResult(Results.Ok(resourceClaims)); + } +} diff --git a/Application/EdFi.Ods.AdminApi/Features/ResourceClaimActionAuthStrategies/ResourceClaimActionAuthStrategyModel.cs b/Application/EdFi.Ods.AdminApi/Features/ResourceClaimActionAuthStrategies/ResourceClaimActionAuthStrategyModel.cs new file mode 100644 index 000000000..c50e763a4 --- /dev/null +++ b/Application/EdFi.Ods.AdminApi/Features/ResourceClaimActionAuthStrategies/ResourceClaimActionAuthStrategyModel.cs @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: Apache-2.0 +// Licensed to the Ed-Fi Alliance under one or more agreements. +// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0. +// See the LICENSE and NOTICES files in the project root for more information. + +namespace EdFi.Ods.AdminApi.Features.ResourceClaimActionAuthStrategies +{ + public class ResourceClaimActionAuthStrategyModel + { + public int ResourceClaimId { get; set; } + public string ResourceClaimName { get; set; } = string.Empty; + + public IReadOnlyList AuthorizationStrategiesForActions { get; set; } = new List(); + } + + public class ActionWithAuthorizationStrategy + { + public int ActionId { get; set; } + public string ActionName { get; set; } = string.Empty; + public IReadOnlyList AuthorizationStrategies { get; set; } = new List(); + + } + + public class AuthorizationStrategyModelForAction + { + public int AuthStrategyId { get; set; } + public string AuthStrategyName { get; set; } = string.Empty; + } +} diff --git a/Application/EdFi.Ods.AdminApi/Features/ResourceClaimActions/ReadResourceClaimActions.cs b/Application/EdFi.Ods.AdminApi/Features/ResourceClaimActions/ReadResourceClaimActions.cs new file mode 100644 index 000000000..2a2de6017 --- /dev/null +++ b/Application/EdFi.Ods.AdminApi/Features/ResourceClaimActions/ReadResourceClaimActions.cs @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: Apache-2.0 +// Licensed to the Ed-Fi Alliance under one or more agreements. +// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0. +// See the LICENSE and NOTICES files in the project root for more information. + +using AutoMapper; +using EdFi.Ods.AdminApi.Infrastructure; +using EdFi.Ods.AdminApi.Infrastructure.Database.Queries; + +namespace EdFi.Ods.AdminApi.Features.ResourceClaimActions; + +public class ReadResourceClaimActions : IFeature +{ + public void MapEndpoints(IEndpointRouteBuilder endpoints) + { + AdminApiEndpointBuilder.MapGet(endpoints, "/resourceClaimActions", GetResourceClaimsActions) + .WithDefaultSummaryAndDescription() + .WithRouteOptions(b => b.WithResponse>(200)) + .BuildForVersions(AdminApiVersions.V2); + } + + internal Task GetResourceClaimsActions(IGetResourceClaimActionsQuery getResourceClaimActionsQuery, IMapper mapper, [AsParameters] CommonQueryParams commonQueryParams) + { + var resourceClaimActions = getResourceClaimActionsQuery.Execute(commonQueryParams); + return Task.FromResult(Results.Ok(resourceClaimActions)); + } +} diff --git a/Application/EdFi.Ods.AdminApi/Features/ResourceClaimActions/ResourceClaimActionModel.cs b/Application/EdFi.Ods.AdminApi/Features/ResourceClaimActions/ResourceClaimActionModel.cs new file mode 100644 index 000000000..a7f5ff627 --- /dev/null +++ b/Application/EdFi.Ods.AdminApi/Features/ResourceClaimActions/ResourceClaimActionModel.cs @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: Apache-2.0 +// Licensed to the Ed-Fi Alliance under one or more agreements. +// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0. +// See the LICENSE and NOTICES files in the project root for more information. + +namespace EdFi.Ods.AdminApi.Features.ResourceClaimActions +{ + public class ResourceClaimActionModel + { + public int ResourceClaimId { get; set; } + public string ResourceName { get; set; } = string.Empty; + public List Actions { get; set; } = new List(); + } + + public class ActionForResourceClaimModel + { + public string Name { get; set; } = string.Empty; + } +} diff --git a/Application/EdFi.Ods.AdminApi/Infrastructure/AutoMapper/AdminApiMappingProfile.cs b/Application/EdFi.Ods.AdminApi/Infrastructure/AutoMapper/AdminApiMappingProfile.cs index d0d4976ef..10fa6902a 100644 --- a/Application/EdFi.Ods.AdminApi/Infrastructure/AutoMapper/AdminApiMappingProfile.cs +++ b/Application/EdFi.Ods.AdminApi/Infrastructure/AutoMapper/AdminApiMappingProfile.cs @@ -10,6 +10,8 @@ using EdFi.Ods.AdminApi.Features.ClaimSets; using EdFi.Ods.AdminApi.Features.ODSInstances; using EdFi.Ods.AdminApi.Features.Profiles; +using EdFi.Ods.AdminApi.Features.ResourceClaimActionAuthStrategies; +using EdFi.Ods.AdminApi.Features.ResourceClaimActions; using EdFi.Ods.AdminApi.Features.Vendors; using EdFi.Ods.AdminApi.Infrastructure.AutoMapper; using EdFi.Ods.AdminApi.Infrastructure.ClaimSetEditor; @@ -155,5 +157,10 @@ public AdminApiMappingProfile() .ForMember(dst => dst.Name, opt => opt.MapFrom(src => src.Name)) .ForMember(dst => dst.OdsInstanceDerivatives, opt => opt.MapFrom(src => src.OdsInstanceDerivatives)) .ForMember(dst => dst.OdsInstanceContexts, opt => opt.MapFrom(src => src.OdsInstanceContexts)); + + CreateMap() + .ForMember(dest => dest.ResourceClaimId, opt => opt.MapFrom(src => src.ResourceClaim.ResourceClaimId)) + .ForMember(dest => dest.ResourceName, opt => opt.MapFrom(src => src.ResourceClaim.ResourceName)) + .ForMember(dest => dest.Actions, opt => opt.Ignore());//Action is ignore as we build it manually } } diff --git a/Application/EdFi.Ods.AdminApi/Infrastructure/Database/Queries/GetResourceClaimActionAuthorizationStrategiesQuery.cs b/Application/EdFi.Ods.AdminApi/Infrastructure/Database/Queries/GetResourceClaimActionAuthorizationStrategiesQuery.cs new file mode 100644 index 000000000..1008b6009 --- /dev/null +++ b/Application/EdFi.Ods.AdminApi/Infrastructure/Database/Queries/GetResourceClaimActionAuthorizationStrategiesQuery.cs @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: Apache-2.0 +// Licensed to the Ed-Fi Alliance under one or more agreements. +// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0. +// See the LICENSE and NOTICES files in the project root for more information. + +using System.Linq.Expressions; +using EdFi.Ods.AdminApi.Features.ResourceClaimActionAuthStrategies; +using EdFi.Ods.AdminApi.Helpers; +using EdFi.Ods.AdminApi.Infrastructure.Extensions; +using EdFi.Ods.AdminApi.Infrastructure.Helpers; +using EdFi.Security.DataAccess.Contexts; +using EdFi.Security.DataAccess.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Polly; + +namespace EdFi.Ods.AdminApi.Infrastructure.Database.Queries; + +public interface IGetResourceClaimActionAuthorizationStrategiesQuery +{ + public IReadOnlyList Execute(CommonQueryParams commonQueryParams); +} + +public class GetResourceClaimActionAuthorizationStrategiesQuery : IGetResourceClaimActionAuthorizationStrategiesQuery +{ + private readonly ISecurityContext _securityContext; + private readonly IOptions _options; + private readonly Dictionary>> _orderByColumns; + + public GetResourceClaimActionAuthorizationStrategiesQuery(ISecurityContext securityContext, IOptions options) + { + _securityContext = securityContext; + _options = options; + _orderByColumns = new Dictionary>> + (StringComparer.OrdinalIgnoreCase) + { + { SortingColumns.DefaultIdColumn, x => x.ResourceClaimId }, + { nameof(ResourceClaimActionAuthStrategyModel.ResourceClaimName), x => x.ResourceClaimName } + }; + } + + public IReadOnlyList Execute(CommonQueryParams commonQueryParams) + { + Expression> columnToOrderBy = _orderByColumns.GetColumnToOrderBy(commonQueryParams.OrderBy); + + return _securityContext.ResourceClaimActionAuthorizationStrategies + // Group by ResourceClaimId and ResourceName to structure the JSON correctly + .GroupBy(gb => new + { + gb.ResourceClaimAction.ResourceClaimId, + gb.ResourceClaimAction.ResourceClaim.ResourceName + }) + .Select(group => new ResourceClaimActionAuthStrategyModel + { + ResourceClaimId = group.Key.ResourceClaimId, + ResourceClaimName = group.Key.ResourceName, + // Group by ActionId and ActionName to create a list of actions within the resource + AuthorizationStrategiesForActions = group.GroupBy(gb => new + { + gb.ResourceClaimAction.Action.ActionId, + gb.ResourceClaimAction.Action.ActionName + }) + .Select(groupedActions => new ActionWithAuthorizationStrategy + { + ActionId = groupedActions.Key.ActionId, + ActionName = groupedActions.Key.ActionName, + // For each action, get the associated authorization strategies + AuthorizationStrategies = groupedActions.Select(resourceClaimActionAuthorizationStrategies => + new AuthorizationStrategyModelForAction + { + AuthStrategyId = resourceClaimActionAuthorizationStrategies.AuthorizationStrategy.AuthorizationStrategyId, + AuthStrategyName = resourceClaimActionAuthorizationStrategies.AuthorizationStrategy.AuthorizationStrategyName, + }).ToList() + }).ToList() + }) + .OrderByColumn(columnToOrderBy, commonQueryParams.IsDescending) + .Paginate(commonQueryParams.Offset, commonQueryParams.Limit, _options) + .ToList(); + } +} diff --git a/Application/EdFi.Ods.AdminApi/Infrastructure/Database/Queries/GetResourceClaimActionsQuery.cs b/Application/EdFi.Ods.AdminApi/Infrastructure/Database/Queries/GetResourceClaimActionsQuery.cs new file mode 100644 index 000000000..c29b4b9d5 --- /dev/null +++ b/Application/EdFi.Ods.AdminApi/Infrastructure/Database/Queries/GetResourceClaimActionsQuery.cs @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: Apache-2.0 +// Licensed to the Ed-Fi Alliance under one or more agreements. +// The Ed-Fi Alliance licenses this file to you under the Apache License, Version 2.0. +// See the LICENSE and NOTICES files in the project root for more information. + +using System.Linq.Expressions; +using EdFi.Ods.AdminApi.Features.ResourceClaimActions; +using EdFi.Ods.AdminApi.Helpers; +using EdFi.Ods.AdminApi.Infrastructure.Extensions; +using EdFi.Ods.AdminApi.Infrastructure.Helpers; +using EdFi.Security.DataAccess.Contexts; +using EdFi.Security.DataAccess.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Polly; + +namespace EdFi.Ods.AdminApi.Infrastructure.Database.Queries; + +public interface IGetResourceClaimActionsQuery +{ + public IReadOnlyList Execute(CommonQueryParams commonQueryParams); +} + +public class GetResourceClaimActionsQuery : IGetResourceClaimActionsQuery +{ + private readonly ISecurityContext _securityContext; + private readonly IOptions _options; + private readonly Dictionary>> _orderByColumns; + + public GetResourceClaimActionsQuery(ISecurityContext securityContext, IOptions options) + { + _securityContext = securityContext; + _options = options; + var isSQLServerEngine = _options.Value.DatabaseEngine?.ToLowerInvariant() == DatabaseEngineEnum.SqlServer.ToLowerInvariant(); + _orderByColumns = new Dictionary>> + (StringComparer.OrdinalIgnoreCase) + { + { nameof(ResourceClaimActionModel.ResourceClaimId), x => x.ResourceClaimId}, + { nameof(ResourceClaimActionModel.ResourceName), x => isSQLServerEngine ? EF.Functions.Collate(x.ResourceName, DatabaseEngineEnum.SqlServerCollation) : x.ResourceName}, + }; + } + + public IReadOnlyList Execute(CommonQueryParams commonQueryParams) + { + Expression> columnToOrderBy = _orderByColumns.GetColumnToOrderBy(commonQueryParams.OrderBy); + + return _securityContext.ResourceClaimActions + .Include(i => i.ResourceClaim) + .Include(i => i.Action) + .GroupBy(r => new { r.ResourceClaim.ResourceClaimId, r.ResourceClaim.ResourceName }) + .Select(group => new ResourceClaimActionModel + { + ResourceClaimId = group.Key.ResourceClaimId, + ResourceName = group.Key.ResourceName, + Actions = group.Select(g => new ActionForResourceClaimModel { Name = g.Action.ActionName }).Distinct().ToList() + }) + .OrderByColumn(columnToOrderBy, commonQueryParams.IsDescending) + .Paginate(commonQueryParams.Offset, commonQueryParams.Limit, _options) + .ToList(); + } +}