Skip to content

Commit

Permalink
[ODS-6390] Release v7.1-patch2 (#1077)
Browse files Browse the repository at this point in the history
Co-authored-by: Jesus Flores <[email protected]>
Co-authored-by: Geoffrey McElhanon <[email protected]>
  • Loading branch information
3 people authored Jun 21, 2024
1 parent f8257c1 commit 0200570
Show file tree
Hide file tree
Showing 13 changed files with 142 additions and 56 deletions.
11 changes: 11 additions & 0 deletions Application/EdFi.Ods.Api/Container/Modules/ApplicationModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
using EdFi.Ods.Api.Middleware;
using EdFi.Ods.Api.Providers;
using EdFi.Ods.Api.Security.Authentication;
using EdFi.Ods.Api.Serialization;
using EdFi.Ods.Api.Validation;
using EdFi.Ods.Common;
using EdFi.Ods.Common.Caching;
Expand Down Expand Up @@ -55,6 +56,7 @@
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Options;
using Newtonsoft.Json.Serialization;
using Module = Autofac.Module;

namespace EdFi.Ods.Api.Container.Modules
Expand Down Expand Up @@ -91,6 +93,15 @@ protected override void Load(ContainerBuilder builder)
.As<IConfigureOptions<MvcOptions>>()
.SingleInstance();

builder.RegisterType<ProfilesAwareContractResolver>()
.WithProperty("NamingStrategy",
new CamelCaseNamingStrategy
{
ProcessDictionaryKeys = true,
OverrideSpecifiedNames = true
})
.SingleInstance();

builder.RegisterType<VersionRouteConvention>()
.As<IApplicationModelConvention>()
.SingleInstance();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

namespace EdFi.Ods.Api.Filters;

public class EnforceAssignedProfileUsageFilter : IAsyncActionFilter
public class EnforceAssignedProfileUsageFilter : IAsyncResourceFilter
{
private readonly IApiClientContextProvider _apiClientContextProvider;
private readonly IContextProvider<DataManagementResourceContext> _dataManagementResourceContextProvider;
Expand All @@ -49,7 +49,7 @@ public EnforceAssignedProfileUsageFilter(
_isEnabled = apiSettings.IsFeatureEnabled("Profiles");
}

public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
public async Task OnResourceExecutionAsync(ResourceExecutingContext context, ResourceExecutionDelegate next)
{
// If Profiles feature is not enabled, don't do any processing.
if (!_isEnabled)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using EdFi.Ods.Common.Profiles;
using EdFi.Ods.Common.Security.Claims;
using EdFi.Ods.Common.Utils.Profiles;
using log4net;
using Microsoft.Extensions.Primitives;
using Newtonsoft.Json.Serialization;

Expand All @@ -40,6 +41,7 @@ public class ProfilesAwareContractResolver : DefaultContractResolver
private readonly string _resourcesNamespacePrefix = $"{Namespaces.Resources.BaseNamespace}.";
private readonly ISchemaNameMapProvider _schemaNameMapProvider;
private static readonly char[] _decimalAsCharArray = { '.' };
private readonly ILog _logger = LogManager.GetLogger(typeof(ProfilesAwareContractResolver));

public ProfilesAwareContractResolver(
IContextProvider<ProfileContentTypeContext> profileContentTypeContextProvider,
Expand Down Expand Up @@ -212,4 +214,14 @@ protected override List<MemberInfo> GetSerializableMembers(Type objectType)

return profileConstrainedMembers;
}

public void Clear()
{
if (_logger.IsDebugEnabled)
{
_logger.Debug("Clears profile contracts due to profile metadata cache expiration...");
}

_contractByKey.Clear();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,16 @@
// 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;
using EdFi.Ods.Api.Serialization;
using EdFi.Ods.Common;
using EdFi.Ods.Common.Context;
using EdFi.Ods.Common.Models;
using EdFi.Ods.Common.Profiles;
using EdFi.Ods.Common.Security.Claims;
using EdFi.Ods.Common.Serialization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
Expand All @@ -22,21 +25,11 @@ namespace EdFi.Ods.Api.Startup;
/// </summary>
public class NewtonsoftJsonOptionConfigurator : IConfigureOptions<MvcNewtonsoftJsonOptions>
{
private readonly IContextProvider<ProfileContentTypeContext> _profileContentTypeContextProvider;
private readonly IContextProvider<DataManagementResourceContext> _dataManagementResourceContextProvider;
private readonly IProfileResourceModelProvider _profileResourceModelProvider;
private readonly ISchemaNameMapProvider _schemaNameMapProvider;
private readonly IServiceProvider _serviceProvider;

public NewtonsoftJsonOptionConfigurator(
IContextProvider<ProfileContentTypeContext> profileContentTypeContextProvider,
IContextProvider<DataManagementResourceContext> dataManagementResourceContextProvider,
IProfileResourceModelProvider profileResourceModelProvider,
ISchemaNameMapProvider schemaNameMapProvider)
public NewtonsoftJsonOptionConfigurator(IServiceProvider serviceProvider)
{
_profileContentTypeContextProvider = profileContentTypeContextProvider;
_dataManagementResourceContextProvider = dataManagementResourceContextProvider;
_profileResourceModelProvider = profileResourceModelProvider;
_schemaNameMapProvider = schemaNameMapProvider;
_serviceProvider = serviceProvider;
}

public void Configure(MvcNewtonsoftJsonOptions options)
Expand All @@ -45,18 +38,9 @@ public void Configure(MvcNewtonsoftJsonOptions options)
options.SerializerSettings.DateTimeZoneHandling = DateTimeZoneHandling.Utc;
options.SerializerSettings.DateParseHandling = DateParseHandling.None;
options.SerializerSettings.Formatting = Formatting.Indented;
options.SerializerSettings.ContractResolver
= new ProfilesAwareContractResolver(
_profileContentTypeContextProvider,
_dataManagementResourceContextProvider,
_profileResourceModelProvider,
_schemaNameMapProvider)
{
NamingStrategy = new CamelCaseNamingStrategy
{
ProcessDictionaryKeys = true,
OverrideSpecifiedNames = true
}
};

var contactResolver = _serviceProvider.GetService<ProfilesAwareContractResolver>();

options.SerializerSettings.ContractResolver = contactResolver;
}
}
}
8 changes: 8 additions & 0 deletions Application/EdFi.Ods.Api/Startup/OdsStartupBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,14 @@ public void ConfigureServices(IServiceCollection services)
services.AddHealthCheck(Configuration, _apiSettings);
services.AddScheduledJobs();

// Identify all EdFi.Ods.* assemblies
var mediatorAssemblies = AppDomain.CurrentDomain.GetAssemblies()
.Where(a => a.GetName().Name.StartsWith("EdFi.Ods."))
.ToArray();

// Add all the MediatR services from the Ed-Fi assemblies
services.AddMediatR(configuration => configuration.RegisterServicesFromAssemblies(mediatorAssemblies));

ConfigurePluginsServices();

void ConfigurePluginsServices()
Expand Down
14 changes: 13 additions & 1 deletion Application/EdFi.Ods.Common/Caching/CachingInterceptor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

namespace EdFi.Ods.Common.Caching;

public class CachingInterceptor : IInterceptor
public class CachingInterceptor : IInterceptor, IClearable
{
private readonly ICacheProvider<ulong> _cacheProvider;

Expand Down Expand Up @@ -75,4 +75,16 @@ protected virtual ulong GenerateCacheKey(MethodInfo method, object[] arguments)
"Support for generating cache keys for more than 3 arguments has not been implemented.");
}
}

public void Clear()
{
if (_cacheProvider is IClearable clearable)
{
clearable.Clear();

return;
}

throw new NotSupportedException($"Unable to clear the underlying data associated with the {nameof(CachingInterceptor)}.");
}
}
11 changes: 11 additions & 0 deletions Application/EdFi.Ods.Common/Caching/InterceptorCacheKeys.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace EdFi.Ods.Common.Caching;

public static class InterceptorCacheKeys
{
public const string Security = "cache-security";
public const string Descriptors = "cache-descriptors";
public const string OdsInstances = "cache-ods-instances";
public const string ProfileMetadata = "cache-profile-metadata";
public const string ApiClientDetails = "cache-api-client-details";
public const string ModelStateKey = "cache-model-state-key";
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// 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 EdFi.Ods.Common.Caching;
using EdFi.Ods.Common.Models.Domain;

namespace EdFi.Ods.Common.Models;
Expand All @@ -11,7 +12,7 @@ namespace EdFi.Ods.Common.Models;
/// Defines a method for obtaining a mapping contract appropriate for a resource/entity in the current context that indicates
/// what should be mapped/synchronized between entities/resources.
/// </summary>
public interface IMappingContractProvider
public interface IMappingContractProvider : IClearable
{
/// <summary>
/// Gets a mapping contract appropriate for a resource/entity in the current context.
Expand Down
19 changes: 16 additions & 3 deletions Application/EdFi.Ods.Common/Models/MappingContractProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using EdFi.Ods.Common.Profiles;
using EdFi.Ods.Common.Security.Claims;
using EdFi.Ods.Common.Utils.Profiles;
using log4net;

namespace EdFi.Ods.Common.Models;

Expand All @@ -25,6 +26,7 @@ public class MappingContractProvider : IMappingContractProvider
private readonly IContextProvider<ProfileContentTypeContext> _profileContentTypeContextProvider;
private readonly IProfileResourceModelProvider _profileResourceModelProvider;
private readonly ISchemaNameMapProvider _schemaNameMapProvider;
private readonly ILog _logger = LogManager.GetLogger(typeof(MappingContractProvider));

private readonly ConcurrentDictionary<MappingContractKey, IMappingContract>
_mappingContractByKey = new();
Expand Down Expand Up @@ -65,7 +67,7 @@ public IMappingContract GetMappingContract(FullName resourceClassFullName)
throw new BadRequestException(
"The resource in the profile-based content type does not match the resource targeted by the request.");
}

var mappingContractKey = new MappingContractKey(
resourceClassFullName,
profileContentTypeContext.ProfileName,
Expand Down Expand Up @@ -177,7 +179,8 @@ private IMappingContract GetOrCreateMappingContract(MappingContractKey mappingCo

return profileResourceClass.AllPropertyByName.ContainsKey(memberName) ||
profileResourceClass.EmbeddedObjectByName.ContainsKey(memberName) ||
profileResourceClass.CollectionByName.ContainsKey(memberName);
profileResourceClass.CollectionByName.ContainsKey(memberName) ||
profileResourceClass.ReferenceByName.ContainsKey(memberName);
}

if (parameterInfo.Name.EndsWith("Included"))
Expand Down Expand Up @@ -211,6 +214,16 @@ private IMappingContract GetOrCreateMappingContract(MappingContractKey mappingCo

return mappingContract;
}

public void Clear()
{
if (_logger.IsDebugEnabled)
{
_logger.Debug("Clears mapping Contracts due to profile metadata cache expiration...");
}

_mappingContractByKey.Clear();
}
}

public class MappingContractKey : IEquatable<MappingContractKey>
Expand Down Expand Up @@ -314,4 +327,4 @@ public override int GetHashCode()
return hashCode;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public OpenApiMetadataModule(ApiSettings apiSettings)
public override void ApplyConfigurationSpecificRegistrations(ContainerBuilder builder)
{
builder.RegisterType<OpenApiMetadataCacheProvider>()
.AsImplementedInterfaces()
.As<IOpenApiMetadataCacheProvider>()
.SingleInstance();

builder.RegisterType<InitializeOpenApiMetadataCache>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public override void ApplyConfigurationSpecificRegistrations(ContainerBuilder bu

builder.RegisterType<ProfileMetadataCacheExpiredNotificationHandler>()
.AsImplementedInterfaces()
.SingleInstance();
.SingleInstance();

builder.RegisterType<AdminProfileNamesPublisherTask>()
.As<IExternalTask>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,26 +8,22 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using EdFi.Common.Extensions;
using EdFi.Ods.Api.Constants;
using EdFi.Ods.Api.Models;
using EdFi.Ods.Api.Providers;
using EdFi.Ods.Api.Routing;
using EdFi.Ods.Common.Models;
using EdFi.Ods.Common.Profiles;
using EdFi.Ods.Features.OpenApiMetadata.Dtos;
using EdFi.Ods.Features.OpenApiMetadata.Factories;
using EdFi.Ods.Features.OpenApiMetadata.Strategies.ResourceStrategies;
using log4net;
using MediatR;
using Microsoft.OpenApi;
using OpenApiMetadataSections = EdFi.Ods.Api.Constants.OpenApiMetadataSections;

namespace EdFi.Ods.Features.OpenApiMetadata.Providers
{
public class OpenApiMetadataCacheProvider : IOpenApiMetadataCacheProvider, INotificationHandler<ProfileMetadataCacheExpired>
public class OpenApiMetadataCacheProvider : IOpenApiMetadataCacheProvider
{
private const string Descriptors = "descriptors";
private const string Resources = "resources";
Expand Down Expand Up @@ -110,6 +106,11 @@ public OpenApiContent GetOpenApiContentByFeedName(string feedName)

public void ResetCacheInitialization()
{
if (_logger.IsDebugEnabled)
{
_logger.Debug("Resetting OpenApiMetadata initialization due to profile metadata cache expiration...");
}

// Reset the underlying cache
_openApiMetadataMetadataCache = new Lazy<ConcurrentDictionary<string, OpenApiContent>>(LazyInitializeCache);
}
Expand Down Expand Up @@ -224,17 +225,5 @@ void AddToCache(IEnumerable<OpenApiContent> openApiContents)
}
}
}

public Task Handle(ProfileMetadataCacheExpired notification, CancellationToken cancellationToken)
{
if (_logger.IsDebugEnabled)
{
_logger.Debug("Resetting OpenApiMetadata initialization due to profile metadata cache expiration...");
}

ResetCacheInitialization();

return Task.CompletedTask;
}
}
}
Loading

0 comments on commit 0200570

Please sign in to comment.