Skip to content

Commit

Permalink
[ADMINAPI-1117] - Adds /v2/resourceClaimActions and /v2/resourceClaim…
Browse files Browse the repository at this point in the history
…ActionAuthStrategies endpoints. (#221)
  • Loading branch information
DavidJGapCR authored Feb 5, 2025
1 parent 4e720ff commit 3d6d5ad
Show file tree
Hide file tree
Showing 9 changed files with 369 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -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<ResourceClaimAction> SetupResourceClaimActions(int[] actions, int resourceClaimId)
{
var resourceClaimActions = new List<ResourceClaimAction>();
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<object>().ToArray());
return resourceClaimActions;
}

private IReadOnlyCollection<ResourceClaim> SetupResourceClaims(int resourceClaimCount = 1)
{
var resourceClaims = new List<ResourceClaim>();
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<object>().ToArray());

return resourceClaims;
}

private IReadOnlyCollection<Security.DataAccess.Models.Action> SetupActions(int resourceClaimCount = 5)
{
var actions = new List<Security.DataAccess.Models.Action>();
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<object>().ToArray());

return actions;
}
}
4 changes: 2 additions & 2 deletions Application/EdFi.Ods.AdminApi.DBTests/Testing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<int>("DefaultPageSizeOffset");

public static int DefaultPageSizeLimit => (int)Configuration().GetValue(typeof(int), "DefaultPageSizeLimit");
public static int DefaultPageSizeLimit => Configuration().GetSection("AppSettings").GetValue<int>("DefaultPageSizeLimit");

public static DbContextOptions GetDbContextOptions(string connectionString)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -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<List<ResourceClaimActionAuthStrategyModel>>(200))
.BuildForVersions(AdminApiVersions.V2);
}

internal Task<IResult> GetResourceClaimActionAuthorizationStrategies(IGetResourceClaimActionAuthorizationStrategiesQuery getResourceClaimActionAuthorizationStrategiesQuery, [AsParameters] CommonQueryParams commonQueryParams)
{
var resourceClaims = getResourceClaimActionAuthorizationStrategiesQuery.Execute(commonQueryParams);
return Task.FromResult(Results.Ok(resourceClaims));
}
}
Original file line number Diff line number Diff line change
@@ -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<ActionWithAuthorizationStrategy> AuthorizationStrategiesForActions { get; set; } = new List<ActionWithAuthorizationStrategy>();
}

public class ActionWithAuthorizationStrategy
{
public int ActionId { get; set; }
public string ActionName { get; set; } = string.Empty;
public IReadOnlyList<AuthorizationStrategyModelForAction> AuthorizationStrategies { get; set; } = new List<AuthorizationStrategyModelForAction>();

}

public class AuthorizationStrategyModelForAction
{
public int AuthStrategyId { get; set; }
public string AuthStrategyName { get; set; } = string.Empty;
}
}
Original file line number Diff line number Diff line change
@@ -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<List<ResourceClaimActionModel>>(200))
.BuildForVersions(AdminApiVersions.V2);
}

internal Task<IResult> GetResourceClaimsActions(IGetResourceClaimActionsQuery getResourceClaimActionsQuery, IMapper mapper, [AsParameters] CommonQueryParams commonQueryParams)
{
var resourceClaimActions = getResourceClaimActionsQuery.Execute(commonQueryParams);
return Task.FromResult(Results.Ok(resourceClaimActions));
}
}
Original file line number Diff line number Diff line change
@@ -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<ActionForResourceClaimModel> Actions { get; set; } = new List<ActionForResourceClaimModel>();
}

public class ActionForResourceClaimModel
{
public string Name { get; set; } = string.Empty;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<EdFi.Security.DataAccess.Models.ResourceClaimAction, ResourceClaimActionModel>()
.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
}
}
Original file line number Diff line number Diff line change
@@ -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<ResourceClaimActionAuthStrategyModel> Execute(CommonQueryParams commonQueryParams);
}

public class GetResourceClaimActionAuthorizationStrategiesQuery : IGetResourceClaimActionAuthorizationStrategiesQuery
{
private readonly ISecurityContext _securityContext;
private readonly IOptions<AppSettings> _options;
private readonly Dictionary<string, Expression<Func<ResourceClaimActionAuthStrategyModel, object>>> _orderByColumns;

public GetResourceClaimActionAuthorizationStrategiesQuery(ISecurityContext securityContext, IOptions<AppSettings> options)
{
_securityContext = securityContext;
_options = options;
_orderByColumns = new Dictionary<string, Expression<Func<ResourceClaimActionAuthStrategyModel, object>>>
(StringComparer.OrdinalIgnoreCase)
{
{ SortingColumns.DefaultIdColumn, x => x.ResourceClaimId },
{ nameof(ResourceClaimActionAuthStrategyModel.ResourceClaimName), x => x.ResourceClaimName }
};
}

public IReadOnlyList<ResourceClaimActionAuthStrategyModel> Execute(CommonQueryParams commonQueryParams)
{
Expression<Func<ResourceClaimActionAuthStrategyModel, object>> 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();
}
}
Original file line number Diff line number Diff line change
@@ -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<ResourceClaimActionModel> Execute(CommonQueryParams commonQueryParams);
}

public class GetResourceClaimActionsQuery : IGetResourceClaimActionsQuery
{
private readonly ISecurityContext _securityContext;
private readonly IOptions<AppSettings> _options;
private readonly Dictionary<string, Expression<Func<ResourceClaimActionModel, object>>> _orderByColumns;

public GetResourceClaimActionsQuery(ISecurityContext securityContext, IOptions<AppSettings> options)
{
_securityContext = securityContext;
_options = options;
var isSQLServerEngine = _options.Value.DatabaseEngine?.ToLowerInvariant() == DatabaseEngineEnum.SqlServer.ToLowerInvariant();
_orderByColumns = new Dictionary<string, Expression<Func<ResourceClaimActionModel, object>>>
(StringComparer.OrdinalIgnoreCase)
{
{ nameof(ResourceClaimActionModel.ResourceClaimId), x => x.ResourceClaimId},
{ nameof(ResourceClaimActionModel.ResourceName), x => isSQLServerEngine ? EF.Functions.Collate(x.ResourceName, DatabaseEngineEnum.SqlServerCollation) : x.ResourceName},
};
}

public IReadOnlyList<ResourceClaimActionModel> Execute(CommonQueryParams commonQueryParams)
{
Expression<Func<ResourceClaimActionModel, object>> 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();
}
}

0 comments on commit 3d6d5ad

Please sign in to comment.