From bacbeb5c5df747a0d0ca70a5bdb8b8a477f04058 Mon Sep 17 00:00:00 2001 From: "deepsource-autofix[bot]" <62050782+deepsource-autofix[bot]@users.noreply.github.com> Date: Wed, 10 Jan 2024 20:14:54 +0000 Subject: [PATCH] style: format code with dotnet-format This commit fixes the style issues introduced in 649b602 according to the output from dotnet-format. Details: None --- .../IMailSendResult.cs | 3 +- src/Communication/Email/EmailSenderOptions.cs | 82 ++-- src/Configuration/CompositeChangeToken.cs | 20 +- .../JsonFileConfigurationExtensions.cs | 4 +- src/Controllers/PingController.cs | 6 +- src/Conventions/ApiConventions.cs | 3 +- .../DownstreamApiOptionsConfigurator.cs | 14 +- .../HttpResponseMessageOutputFormatter.cs | 2 +- src/Formatters/PlainTextOutputFormatter.cs | 4 +- src/Http/Mime/ApplicationMediaTypeNames.cs | 6 +- src/Http/Mime/TempMediaType.cs | 128 +++--- src/Http/ResponseCodes/ResponseCode.cs | 12 +- .../Services/AddHttpServicesExtensions.cs | 6 +- src/Http/Services/CorsOptions.cs | 2 +- src/Http/Services/CorsOptionsConfigurator.cs | 16 +- src/Http/Services/HttpServicesOptions.cs | 2 +- .../Services/UseHttpServicesExtensions.cs | 28 +- src/MicrosoftGraph/ApplicationService.cs | 48 +-- .../Models/ExtensionPropertyKey.cs | 32 +- src/MicrosoftGraph/PassphraseGenerator.cs | 144 +++---- src/MicrosoftGraph/UsersService.cs | 408 +++++++++--------- .../RequestLoggingMiddleware.cs | 12 +- .../IHostApplicationBuilderMvcExtensions.cs | 10 +- src/Mvc/OKAttributes.cs | 30 +- .../Produces400ErrorAttribute.cs | 3 +- .../Produces401ErrorAttribute.cs | 3 +- .../Produces403ErrorAttribute.cs | 3 +- .../Produces404ErrorAttribute.cs | 3 +- .../Produces418ErrorAttribute.cs | 3 +- .../Produces500ErrorAttribute.cs | 3 +- .../Produces501ErrorAttribute.cs | 3 +- .../Infrastructure/ResponsePayloadExecutor.cs | 4 +- src/Payloads/Pagers/SingleItemPager.cs | 20 +- src/Payloads/Pagers/SingleItemPager{T}.cs | 150 +++---- .../BooleanResponsePayload.cs | 16 +- .../ResponsePayloads/IntResponsePayload.cs | 10 +- .../NumericResponsePayload.cs | 10 +- .../ResponsePayloads/UriResponsePayload.cs | 42 +- src/Payloads/Scalars/BooleanPayload.cs | 6 +- .../Scalars/StringWithRegexPayload.cs | 14 +- src/Startup/Startup.cs | 12 +- .../Microsoft.OpenApi/OpenApiAnyType.cs | 72 ++-- ...SwaggerGenExtension.ThisAssemblyProject.cs | 184 ++++---- src/Swagger/SwaggerExtensions/UseSwaggerUI.cs | 303 ++++++------- .../Controllers/SwaggerUIController.cs | 320 +++++++------- 45 files changed, 1111 insertions(+), 1095 deletions(-) diff --git a/src/Communication.Abstractions/IMailSendResult.cs b/src/Communication.Abstractions/IMailSendResult.cs index f9266067..a53a5c49 100644 --- a/src/Communication.Abstractions/IMailSendResult.cs +++ b/src/Communication.Abstractions/IMailSendResult.cs @@ -1,4 +1,5 @@ namespace Dgmjr.AspNetCore.Communication.Email; public interface IMailSendResult - : Dgmjr.AspNetCore.Communication.IMessageSendResult { } + : Dgmjr.AspNetCore.Communication.IMessageSendResult +{ } diff --git a/src/Communication/Email/EmailSenderOptions.cs b/src/Communication/Email/EmailSenderOptions.cs index d9d6c255..b42a0186 100644 --- a/src/Communication/Email/EmailSenderOptions.cs +++ b/src/Communication/Email/EmailSenderOptions.cs @@ -18,45 +18,45 @@ namespace Dgmjr.AspNetCore.Communication.Email; public record class EmailSenderOptions : AzureCommunicationServicesOptions { - public override required EmailAddress DefaultFrom { get; set; } - - public static new EmailSenderOptions Parse(string connectionString) - { - var options = AzureCommunicationServicesOptions.Parse(connectionString); - return new EmailSenderOptions(options); - } - - [SetsRequiredMembers] - public EmailSenderOptions( - AzureCommunicationServicesOptionsBase options, - EmailAddress? defaultFrom = null - ) - { - DefaultFrom = defaultFrom ?? EmailAddress.Empty; - Endpoint = options.Endpoint; - AccessKey = options.AccessKey; - } - - [SetsRequiredMembers] - public EmailSenderOptions(AzureCommunicationServicesOptions options) - : base(options) - { - DefaultFrom = options.DefaultFrom; - } - - [SetsRequiredMembers] - public EmailSenderOptions(string connectionString, EmailAddress? defaultFrom = null) - : this(Parse(connectionString) with { DefaultFrom = defaultFrom ?? EmailAddress.Empty }) { } - - [SetsRequiredMembers] - public EmailSenderOptions(string endpoint, string accessKey, EmailAddress? defaultFrom = null) - { - DefaultFrom = defaultFrom ?? EmailAddress.Empty; - Endpoint = endpoint; - AccessKey = accessKey; - } - - [SetsRequiredMembers] - public EmailSenderOptions() - : this(string.Empty) { } + public override required EmailAddress DefaultFrom { get; set; } + +public static new EmailSenderOptions Parse(string connectionString) +{ + var options = AzureCommunicationServicesOptions.Parse(connectionString); + return new EmailSenderOptions(options); +} + +[SetsRequiredMembers] +public EmailSenderOptions( + AzureCommunicationServicesOptionsBase options, + EmailAddress? defaultFrom = null +) +{ + DefaultFrom = defaultFrom ?? EmailAddress.Empty; + Endpoint = options.Endpoint; + AccessKey = options.AccessKey; +} + +[SetsRequiredMembers] +public EmailSenderOptions(AzureCommunicationServicesOptions options) + : base(options) +{ + DefaultFrom = options.DefaultFrom; +} + +[SetsRequiredMembers] +public EmailSenderOptions(string connectionString, EmailAddress? defaultFrom = null) + : this(Parse(connectionString) with { DefaultFrom = defaultFrom ?? EmailAddress.Empty }) { } + +[SetsRequiredMembers] +public EmailSenderOptions(string endpoint, string accessKey, EmailAddress? defaultFrom = null) +{ + DefaultFrom = defaultFrom ?? EmailAddress.Empty; + Endpoint = endpoint; + AccessKey = accessKey; +} + +[SetsRequiredMembers] +public EmailSenderOptions() + : this(string.Empty) { } } diff --git a/src/Configuration/CompositeChangeToken.cs b/src/Configuration/CompositeChangeToken.cs index 039570a7..5e05406f 100644 --- a/src/Configuration/CompositeChangeToken.cs +++ b/src/Configuration/CompositeChangeToken.cs @@ -3,14 +3,14 @@ namespace Microsoft.Extensions.DependencyInjection; public class CompositeChangeToken(params IChangeToken[] changeTokens) : IChangeToken { - private readonly IChangeToken[] _changeTokens = changeTokens; - - public bool ActiveChangeCallbacks => Exists(_changeTokens, t => t.ActiveChangeCallbacks); - - public bool HasChanged => Exists(_changeTokens, t => t.HasChanged); - - public IDisposable RegisterChangeCallback(Action callback, object? state) => - new CompositeDisposable( - _changeTokens.Select(t => t.RegisterChangeCallback(callback, state)) - ); + private readonly IChangeToken[] _changeTokens = changeTokens; + +public bool ActiveChangeCallbacks => Exists(_changeTokens, t => t.ActiveChangeCallbacks); + +public bool HasChanged => Exists(_changeTokens, t => t.HasChanged); + +public IDisposable RegisterChangeCallback(Action callback, object? state) => + new CompositeDisposable( + _changeTokens.Select(t => t.RegisterChangeCallback(callback, state)) + ); } diff --git a/src/Configuration/JsonFileConfigurationExtensions.cs b/src/Configuration/JsonFileConfigurationExtensions.cs index 920280f8..96aa27fc 100644 --- a/src/Configuration/JsonFileConfigurationExtensions.cs +++ b/src/Configuration/JsonFileConfigurationExtensions.cs @@ -104,8 +104,8 @@ bool recursive .EnumerateFiles( "*.json", recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly - ) - // .Where(IsForThisEnvironment) + ) + // .Where(IsForThisEnvironment) .OrderBy(jsonFile => jsonFile.Name); internal static string RemoveEnvironmentIfIsForThisEnvironment(this string jsonFileName) => diff --git a/src/Controllers/PingController.cs b/src/Controllers/PingController.cs index bdf73671..966267b9 100644 --- a/src/Controllers/PingController.cs +++ b/src/Controllers/PingController.cs @@ -60,8 +60,8 @@ public static WebApplication MapPing(this WebApplication app) Predicate = _ => true } ) - .WithDisplayName("Health Check").WithName("Health Check").AllowAnonymous()//.Produces(200, contentType: Dgmjr.Mime.Application.Json.DisplayName) - #if NET8_0_OR_GREATER + .WithDisplayName("Health Check").WithName("Health Check").AllowAnonymous()//.Produces(200, contentType: Dgmjr.Mime.Application.Json.DisplayName) +#if NET8_0_OR_GREATER .WithOpenApi(op => { op.Responses["200"] = new() @@ -84,7 +84,7 @@ public static WebApplication MapPing(this WebApplication app) }; return op; }) - #endif +#endif ; return app; diff --git a/src/Conventions/ApiConventions.cs b/src/Conventions/ApiConventions.cs index 39449ee1..e0eccdb4 100644 --- a/src/Conventions/ApiConventions.cs +++ b/src/Conventions/ApiConventions.cs @@ -17,5 +17,6 @@ public static void Get( [ApiConventionNameMatch(ApiConventionNameMatchBehavior.Suffix)] [ApiConventionTypeMatch(ApiConventionTypeMatchBehavior.Any)] object id - ) { } + ) + { } } diff --git a/src/DownstreamApis/DownstreamApiOptionsConfigurator.cs b/src/DownstreamApis/DownstreamApiOptionsConfigurator.cs index 328946aa..4c7ed109 100644 --- a/src/DownstreamApis/DownstreamApiOptionsConfigurator.cs +++ b/src/DownstreamApis/DownstreamApiOptionsConfigurator.cs @@ -5,11 +5,11 @@ namespace Dgmjr.Web.DownstreamApis; public class DownstreamApiOptionsConfigurator(IOptions jsonOptions) : IConfigureOptions { - private readonly Jso _jso = jsonOptions?.Value?.JsonSerializerOptions; - - public void Configure(DownstreamApiOptions options) - { - options.Serializer = requestObject => - new StringContent(Serialize(requestObject, _jso), UTF8, Application.Json.DisplayName); - } + private readonly Jso _jso = jsonOptions?.Value?.JsonSerializerOptions; + +public void Configure(DownstreamApiOptions options) +{ + options.Serializer = requestObject => + new StringContent(Serialize(requestObject, _jso), UTF8, Application.Json.DisplayName); +} } diff --git a/src/Formatters/HttpResponseMessageOutputFormatter.cs b/src/Formatters/HttpResponseMessageOutputFormatter.cs index d3a4168a..d7134267 100644 --- a/src/Formatters/HttpResponseMessageOutputFormatter.cs +++ b/src/Formatters/HttpResponseMessageOutputFormatter.cs @@ -21,7 +21,7 @@ public override bool CanWriteResult(OutputFormatterCanWriteContext context) public override async Task WriteResponseBodyAsync(OutputFormatterWriteContext context) { var response = context.Object is HttpResponseMessage resp ? resp : null; - if(response != null) + if (response != null) { context.HttpContext.Response.StatusCode = (int)response.StatusCode; foreach (var header in response.Headers) diff --git a/src/Formatters/PlainTextOutputFormatter.cs b/src/Formatters/PlainTextOutputFormatter.cs index 1bd6ffe0..bfcdfb83 100644 --- a/src/Formatters/PlainTextOutputFormatter.cs +++ b/src/Formatters/PlainTextOutputFormatter.cs @@ -21,8 +21,8 @@ protected virtual string GetContentType(OutputFormatterWriteContext context) => || context.HttpContext.Request .GetTypedHeaders() .Accept.Any(a => a.MediaType.Value.ToLower().Contains(TextMediaTypeNames.Css)) - ? TextMediaTypeNames.Css - // : context.HttpContext.Request.Path.Value.EndsWith(".js") ? + ? TextMediaTypeNames.Css + // : context.HttpContext.Request.Path.Value.EndsWith(".js") ? : TextMediaTypeNames.Plain; public override void WriteResponseHeaders(OutputFormatterWriteContext context) diff --git a/src/Http/Mime/ApplicationMediaTypeNames.cs b/src/Http/Mime/ApplicationMediaTypeNames.cs index a13d00b3..0fac6481 100644 --- a/src/Http/Mime/ApplicationMediaTypeNames.cs +++ b/src/Http/Mime/ApplicationMediaTypeNames.cs @@ -80,8 +80,8 @@ public static class ApplicationMediaTypeNames public const string OpenApiV2Yaml = $"{OpenApiYaml}; version=2.x"; /// ; version=3.x - public const string OpenApiV3Json = $"{OpenApiJson}; version=3.x"; - - /// ; version=3.x + public const string OpenApiV3Json = $"{OpenApiJson}; version=3.x"; + + /// ; version=3.x public const string OpenApiV3Yaml = $"{OpenApiYaml}; version=3.x"; } diff --git a/src/Http/Mime/TempMediaType.cs b/src/Http/Mime/TempMediaType.cs index fcfc0352..874edb01 100644 --- a/src/Http/Mime/TempMediaType.cs +++ b/src/Http/Mime/TempMediaType.cs @@ -21,68 +21,68 @@ namespace Dgmjr.Mime; public readonly record struct TempMediaType(string Name) : IMediaType { - public string DisplayName { get; } = Name; - private static readonly MD5 MD5 = MD5.Create(); - public string[] Synonyms { get; init; } = Empty(); - Uri IHaveAUri.Uri => new(UriString); - object IIdentifiable.Id => Id; - public int Id => 0; - public int Value => Id; - public string UriString => "urn:publicid:temp:media-type:" + DisplayName.ToKebabCase(); - public string GuidString => MD5.ComputeHash(UriString.ToUTF8Bytes()).ToHexString(); - public guid Guid => new(GuidString); - public string Description => Name; - public string GroupName => Name; - public string ShortName => Name; - public string Name => DisplayName; - public int Order => 0; - public string Prompt => ""; - - uri IMediaType.Uri => UriString; - uri IHaveAuri.Uri => UriString; - - object IHaveAValue.Value => Value; - - int IHaveAValue.Value => Id; - - MediaTypes IHaveAValue.Value => MediaTypes.Any; - - TypeCode IConvertible.GetTypeCode() => TypeCode.Object; - - bool IConvertible.ToBoolean(IFormatProvider? provider) => true; - - byte IConvertible.ToByte(IFormatProvider? provider) => Convert.ToByte(Value); - - char IConvertible.ToChar(IFormatProvider? provider) => Convert.ToChar(Value); - - datetime IConvertible.ToDateTime(IFormatProvider? provider) => Convert.ToDateTime(Value); - - decimal IConvertible.ToDecimal(IFormatProvider? provider) => Convert.ToDecimal(Value); - - double IConvertible.ToDouble(IFormatProvider? provider) => Convert.ToDouble(Value); - - short IConvertible.ToInt16(IFormatProvider? provider) => Convert.ToInt16(Value); - - int IConvertible.ToInt32(IFormatProvider? provider) => Convert.ToInt32(Value); - - long IConvertible.ToInt64(IFormatProvider? provider) => Convert.ToInt64(Value); - - sbyte IConvertible.ToSByte(IFormatProvider? provider) => Convert.ToSByte(Value); - - float IConvertible.ToSingle(IFormatProvider? provider) => Convert.ToSingle(Value); - - string IConvertible.ToString(IFormatProvider? provider) => Convert.ToString(Value); - - object IConvertible.ToType(type conversionType, IFormatProvider? provider) => - Convert.ChangeType(Value, conversionType); - - ushort IConvertible.ToUInt16(IFormatProvider? provider) => Convert.ToUInt16(Value); - - uint IConvertible.ToUInt32(IFormatProvider? provider) => Convert.ToUInt32(Value); - - ulong IConvertible.ToUInt64(IFormatProvider? provider) => Convert.ToUInt64(Value); - - bool IEquatable.Equals(int other) => Id == other; - - bool IEquatable.Equals(IMediaType other) => this.Matches(other); + public string DisplayName { get; } = Name; +private static readonly MD5 MD5 = MD5.Create(); +public string[] Synonyms { get; init; } = Empty(); +Uri IHaveAUri.Uri => new(UriString); +object IIdentifiable.Id => Id; +public int Id => 0; +public int Value => Id; +public string UriString => "urn:publicid:temp:media-type:" + DisplayName.ToKebabCase(); +public string GuidString => MD5.ComputeHash(UriString.ToUTF8Bytes()).ToHexString(); +public guid Guid => new(GuidString); +public string Description => Name; +public string GroupName => Name; +public string ShortName => Name; +public string Name => DisplayName; +public int Order => 0; +public string Prompt => ""; + +uri IMediaType.Uri => UriString; +uri IHaveAuri.Uri => UriString; + +object IHaveAValue.Value => Value; + +int IHaveAValue.Value => Id; + +MediaTypes IHaveAValue.Value => MediaTypes.Any; + +TypeCode IConvertible.GetTypeCode() => TypeCode.Object; + +bool IConvertible.ToBoolean(IFormatProvider? provider) => true; + +byte IConvertible.ToByte(IFormatProvider? provider) => Convert.ToByte(Value); + +char IConvertible.ToChar(IFormatProvider? provider) => Convert.ToChar(Value); + +datetime IConvertible.ToDateTime(IFormatProvider? provider) => Convert.ToDateTime(Value); + +decimal IConvertible.ToDecimal(IFormatProvider? provider) => Convert.ToDecimal(Value); + +double IConvertible.ToDouble(IFormatProvider? provider) => Convert.ToDouble(Value); + +short IConvertible.ToInt16(IFormatProvider? provider) => Convert.ToInt16(Value); + +int IConvertible.ToInt32(IFormatProvider? provider) => Convert.ToInt32(Value); + +long IConvertible.ToInt64(IFormatProvider? provider) => Convert.ToInt64(Value); + +sbyte IConvertible.ToSByte(IFormatProvider? provider) => Convert.ToSByte(Value); + +float IConvertible.ToSingle(IFormatProvider? provider) => Convert.ToSingle(Value); + +string IConvertible.ToString(IFormatProvider? provider) => Convert.ToString(Value); + +object IConvertible.ToType(type conversionType, IFormatProvider? provider) => + Convert.ChangeType(Value, conversionType); + +ushort IConvertible.ToUInt16(IFormatProvider? provider) => Convert.ToUInt16(Value); + +uint IConvertible.ToUInt32(IFormatProvider? provider) => Convert.ToUInt32(Value); + +ulong IConvertible.ToUInt64(IFormatProvider? provider) => Convert.ToUInt64(Value); + +bool IEquatable.Equals(int other) => Id == other; + +bool IEquatable.Equals(IMediaType other) => this.Matches(other); } diff --git a/src/Http/ResponseCodes/ResponseCode.cs b/src/Http/ResponseCodes/ResponseCode.cs index 5f8480e0..aef9cb85 100644 --- a/src/Http/ResponseCodes/ResponseCode.cs +++ b/src/Http/ResponseCodes/ResponseCode.cs @@ -51,8 +51,8 @@ public enum StatusCode : ushort /// 200 /// [Display( - Name = "OK", - // Description = "The request has succeeded. The meaning of the success depends on the HTTP method:" + Name = "OK", + // Description = "The request has succeeded. The meaning of the success depends on the HTTP method:" Description = "Yay! You didn't fuck up!" )] [Uri($"{UriBase}200")] @@ -63,8 +63,8 @@ public enum StatusCode : ushort /// 201 /// [Display( - Name = "Created", - // Description = "The request has succeeded and a new resource has been created as a result. This is typically the response sent after a PUT request." + Name = "Created", + // Description = "The request has succeeded and a new resource has been created as a result. This is typically the response sent after a PUT request." Description = "I created the thing for you." )] [Uri($"{UriBase}201")] @@ -75,8 +75,8 @@ public enum StatusCode : ushort /// 202 /// [Display( - Name = "Accepted", - // Description = "The request has been received but not yet acted upon. It is non-committal, meaning that there is no way in HTTP to later send an asynchronous response indicating the outcome of processing the request. It is intended for cases where another process or server handles the request, or for batch processing." + Name = "Accepted", + // Description = "The request has been received but not yet acted upon. It is non-committal, meaning that there is no way in HTTP to later send an asynchronous response indicating the outcome of processing the request. It is intended for cases where another process or server handles the request, or for batch processing." Description = "I got it and you can go do other stuff while I work on it for you." )] [Uri($"{UriBase}202")] diff --git a/src/Http/Services/AddHttpServicesExtensions.cs b/src/Http/Services/AddHttpServicesExtensions.cs index 01d6cecc..2869e94b 100644 --- a/src/Http/Services/AddHttpServicesExtensions.cs +++ b/src/Http/Services/AddHttpServicesExtensions.cs @@ -125,7 +125,7 @@ public static IHostApplicationBuilder AddHttpServices( ); } - if(options.IIS != null) + if (options.IIS != null) { builder.Services.Configure( options => @@ -133,7 +133,7 @@ public static IHostApplicationBuilder AddHttpServices( ); } - if(options.Kestrel != null) + if (options.Kestrel != null) { builder.Services.Configure( options => @@ -141,7 +141,7 @@ public static IHostApplicationBuilder AddHttpServices( ); } - if(options.ExceptionHandling != null) + if (options.ExceptionHandling != null) { builder.Services.Configure( options => diff --git a/src/Http/Services/CorsOptions.cs b/src/Http/Services/CorsOptions.cs index 23d3733d..0731c908 100644 --- a/src/Http/Services/CorsOptions.cs +++ b/src/Http/Services/CorsOptions.cs @@ -6,7 +6,7 @@ namespace Dgmjr.AspNetCore.Http.Services; string, Microsoft.AspNetCore.Cors.Infrastructure.CorsPolicy >; -using ICorsPolicyCollection = ICollection>; +using ICorsPolicyCollection = ICollection>; using ICorsPolicyEnumerable = IEnumerable>; public class CorsOptions diff --git a/src/Http/Services/CorsOptionsConfigurator.cs b/src/Http/Services/CorsOptionsConfigurator.cs index d787cba8..d6b97ee0 100644 --- a/src/Http/Services/CorsOptionsConfigurator.cs +++ b/src/Http/Services/CorsOptionsConfigurator.cs @@ -7,12 +7,12 @@ namespace Dgmjr.AspNetCore.Http.Services; public class CorsOptionsConfigurator(IConfiguration configuration) : IConfigureOptions { - public const string CorsOptionsSection = $"{Http}:{Cors}"; - - private readonly IConfiguration _configuration = configuration; - - public void Configure(CorsOptions options) - { - var corsPolicyDictionary = _configuration.GetSection(CorsOptionsSection).Get>(); - } + public const string CorsOptionsSection = $"{Http}:{Cors}"; + +private readonly IConfiguration _configuration = configuration; + +public void Configure(CorsOptions options) +{ + var corsPolicyDictionary = _configuration.GetSection(CorsOptionsSection).Get>(); +} } diff --git a/src/Http/Services/HttpServicesOptions.cs b/src/Http/Services/HttpServicesOptions.cs index fe480500..825fe699 100644 --- a/src/Http/Services/HttpServicesOptions.cs +++ b/src/Http/Services/HttpServicesOptions.cs @@ -20,7 +20,7 @@ namespace Dgmjr.AspNetCore.Http; public class HttpServicesOptions { public bool UseWelcomePage { get; set; } = true; - public WelcomePageOptions WelcomePage { get; set; } = new () { Path = "/welcome" }; + public WelcomePageOptions WelcomePage { get; set; } = new() { Path = "/welcome" }; public bool UseCookiePolicy { get; set; } = true; public CookiePolicyOptions CookiePolicy { get; set; } = new(); diff --git a/src/Http/Services/UseHttpServicesExtensions.cs b/src/Http/Services/UseHttpServicesExtensions.cs index e8338093..370ed6d7 100644 --- a/src/Http/Services/UseHttpServicesExtensions.cs +++ b/src/Http/Services/UseHttpServicesExtensions.cs @@ -11,7 +11,7 @@ public static IApplicationBuilder UseHttpServices(this IApplicationBuilder app) { var options = app.ApplicationServices.GetRequiredService>().Value; - if(options.UseRequestDecompression) + if (options.UseRequestDecompression) { app.UseRequestDecompression(); } @@ -21,37 +21,37 @@ public static IApplicationBuilder UseHttpServices(this IApplicationBuilder app) app.UseResponseCompression(); } - if(options.UseFileServer) + if (options.UseFileServer) { app.UseFileServer(options.FileServer); - if(options.FileServer.EnableDefaultFiles) + if (options.FileServer.EnableDefaultFiles) { app.UseDefaultFiles(options.FileServer.DefaultFilesOptions); } - if(options.FileServer.EnableDirectoryBrowsing) + if (options.FileServer.EnableDirectoryBrowsing) { app.UseDirectoryBrowser(options.FileServer.DirectoryBrowserOptions); } - if(options.FileServer.StaticFileOptions != null || options.UseStaticFiles) + if (options.FileServer.StaticFileOptions != null || options.UseStaticFiles) { app.UseStaticFiles(options.FileServer.StaticFileOptions); } } - if(options.UseResponseCaching) + if (options.UseResponseCaching) { app.UseResponseCaching(); } - if(options.UseForwardedHeaders) + if (options.UseForwardedHeaders) { app.UseForwardedHeaders(options.ForwardedHeaders); } - if(options.UseCors) + if (options.UseCors) { app.UseCors(); // _ = app.UseCors(corsOptions => corsOptions @@ -63,32 +63,32 @@ public static IApplicationBuilder UseHttpServices(this IApplicationBuilder app) // ); } - if(options.UseCookiePolicy) + if (options.UseCookiePolicy) { app.UseCookiePolicy(options.CookiePolicy); } - if(options.UseSession) + if (options.UseSession) { app.UseSession(options.Session); } - if(options.UseHsts) + if (options.UseHsts) { app.UseHsts(); } - if(options.UseHttpsRedirection) + if (options.UseHttpsRedirection) { app.UseHttpsRedirection(); } - if(options.UseExceptionHandler) + if (options.UseExceptionHandler) { app.UseExceptionHandler(options.ExceptionHandling); } - if(options.UseWelcomePage) + if (options.UseWelcomePage) { app.UseWelcomePage(options.WelcomePage); } diff --git a/src/MicrosoftGraph/ApplicationService.cs b/src/MicrosoftGraph/ApplicationService.cs index 656d4430..f216a6cc 100644 --- a/src/MicrosoftGraph/ApplicationService.cs +++ b/src/MicrosoftGraph/ApplicationService.cs @@ -3,28 +3,28 @@ namespace Dgmjr.MicrosoftGraph; public class ApplicationService(GraphServiceClient graph, ILogger logger, IConfiguration configuration) : ILog { - public ILogger Logger => logger; - private readonly IConfiguration _configuration = configuration; - private MicrosoftB2CGraphOptions GraphOptions => _configuration.GetSection(Constants.AzureAdB2C).Get(); - private MicrosoftIdentityOptions IdentityOptions => _configuration.GetSection(Constants.AzureAdB2C).Get(); - protected virtual GraphServiceClient Graph => graph; - public guid ExtensionsAppClientId => GraphOptions.AzureAdB2CExtensionsApplicationId; - - /// Retrieves the client ID from "AzureAdB2C:ClientIf" - public string ClientId => - IdentityOptions?.ClientId ?? - throw new InvalidOperationException("ClientId is required"); - - public async Task GetApplication() - => await Graph.Applications[ClientId].Request().GetAsync(); - - public async Task GetExtensionsApplication() - => await Graph.Applications[GraphOptions.AzureAdB2CExtensionsApplicationId.ToString()].Request().GetAsync(); - - public async Task GetExtensionPropertiesAsync(CancellationToken cancellationToken = default) - { - - var extensionProperties = await Graph.Applications[ExtensionsAppClientId.ToString()].ExtensionProperties.Request().GetAsync(); - return extensionProperties.AsEnumerable().ToArray(); - } + public ILogger Logger => logger; +private readonly IConfiguration _configuration = configuration; +private MicrosoftB2CGraphOptions GraphOptions => _configuration.GetSection(Constants.AzureAdB2C).Get(); +private MicrosoftIdentityOptions IdentityOptions => _configuration.GetSection(Constants.AzureAdB2C).Get(); +protected virtual GraphServiceClient Graph => graph; +public guid ExtensionsAppClientId => GraphOptions.AzureAdB2CExtensionsApplicationId; + +/// Retrieves the client ID from "AzureAdB2C:ClientIf" +public string ClientId => + IdentityOptions?.ClientId ?? + throw new InvalidOperationException("ClientId is required"); + +public async Task GetApplication() + => await Graph.Applications[ClientId].Request().GetAsync(); + +public async Task GetExtensionsApplication() + => await Graph.Applications[GraphOptions.AzureAdB2CExtensionsApplicationId.ToString()].Request().GetAsync(); + +public async Task GetExtensionPropertiesAsync(CancellationToken cancellationToken = default) +{ + + var extensionProperties = await Graph.Applications[ExtensionsAppClientId.ToString()].ExtensionProperties.Request().GetAsync(); + return extensionProperties.AsEnumerable().ToArray(); +} } diff --git a/src/MicrosoftGraph/Models/ExtensionPropertyKey.cs b/src/MicrosoftGraph/Models/ExtensionPropertyKey.cs index e7009fe7..f5d7228a 100644 --- a/src/MicrosoftGraph/Models/ExtensionPropertyKey.cs +++ b/src/MicrosoftGraph/Models/ExtensionPropertyKey.cs @@ -38,22 +38,22 @@ public ExtensionProperty(string extensionPropertyName, guid extensionsAppId) : t public int CompareTo(object obj) => obj is ExtensionProperty other ? CompareTo(other) : -1; - public static implicit operator ExtensionProperty(MgExtensionProperty extensionProperty) => new(extensionProperty.Name); - - // public static implicit operator DgmjrExtensionProperty(string key) => new (key, guid.Empty); - - // public static implicit operator DgmjrExtensionProperty((string key, guid appId) key) => new (key.key, key.appId); - -// #if NET6_0_OR_GREATER -// public static Regx IRegexValueObject.Regex => Regex(); -// #else -// Regx IRegexValueObject.Regex() => Regex(); - -// bool IEquatable.Equals(ExtensionProperty other) -// { -// throw new NotImplementedException(); -// } -// #endif + public static implicit operator ExtensionProperty(MgExtensionProperty extensionProperty) => new(extensionProperty.Name); + + // public static implicit operator DgmjrExtensionProperty(string key) => new (key, guid.Empty); + + // public static implicit operator DgmjrExtensionProperty((string key, guid appId) key) => new (key.key, key.appId); + + // #if NET6_0_OR_GREATER + // public static Regx IRegexValueObject.Regex => Regex(); + // #else + // Regx IRegexValueObject.Regex() => Regex(); + + // bool IEquatable.Equals(ExtensionProperty other) + // { + // throw new NotImplementedException(); + // } + // #endif } public static class Helpers diff --git a/src/MicrosoftGraph/PassphraseGenerator.cs b/src/MicrosoftGraph/PassphraseGenerator.cs index 3447157a..25c5c3c5 100644 --- a/src/MicrosoftGraph/PassphraseGenerator.cs +++ b/src/MicrosoftGraph/PassphraseGenerator.cs @@ -39,76 +39,76 @@ public class PassphraseGeneratorOptions public class PassphraseGenerator(IOptions options) : IPassphraseGenerator { - private static readonly RandomNumberGenerator Random = RandomNumberGenerator.Create(); - private readonly PassphraseGeneratorOptions _options = - options?.Value ?? throw new ArgumentNullException(nameof(options)); - private string[] WordList => _options.WordList; - private char[] SpecialCharacters => _options.SpecialCharacters; - private int WordCount => _options.WordCount; - private int EmojiCount => _options.EmojiCount; - private int SpecialCharacterCount => _options.SpecialCharacterCount; - private int LowercaseCharacterCount => _options.LowercaseCharacterCount; - private int UppercaseCharacterCount => _options.UppercaseCharacterCount; - private int CharacterCount => _options.CharacterCount; - private char[] Emoji => _options.Emoji; - - public string Generate() - { - var wordsToGo = WordCount; - var emojiToGo = EmojiCount; - var specialCharactersToGo = SpecialCharacterCount; - var lowercaseCharactersToGo = LowercaseCharacterCount; - var uppercaseCharactersToGo = UppercaseCharacterCount; - var charactersToGo = CharacterCount; - - var passphrase = new StringBuilder(); - - while ( - wordsToGo > 0 - || emojiToGo > 0 - || specialCharactersToGo > 0 - || lowercaseCharactersToGo > 0 - || uppercaseCharactersToGo > 0 - || charactersToGo > 0 - ) - { - var word = GetRandomWord(); - passphrase.Append(word); - wordsToGo--; - charactersToGo -= word.Length; - lowercaseCharactersToGo -= word.Count(char.IsLower); - uppercaseCharactersToGo -= word.Count(char.IsUpper); - - if (emojiToGo > 0) - { - var emoji = PickRandomElement(Emoji); - passphrase.Append(emoji); - emojiToGo--; - charactersToGo--; - } - - if (lowercaseCharactersToGo > 0) - { - var emoji = PickRandomElement(Emoji); - passphrase.Append(emoji); - emojiToGo--; - charactersToGo--; - } - - if (specialCharactersToGo > 0) - { - var specialCharacter = PickRandomElement(SpecialCharacters); - passphrase.Append(specialCharacter); - specialCharactersToGo--; - charactersToGo--; - } - } - - return passphrase.ToString(); - } - - private string GetRandomWord() => PickRandomElement(WordList); - - private static T PickRandomElement(T[] elements) => - elements.Skip(Random.NextInt32(elements.Length - 1)).First(); + private static readonly RandomNumberGenerator Random = RandomNumberGenerator.Create(); +private readonly PassphraseGeneratorOptions _options = + options?.Value ?? throw new ArgumentNullException(nameof(options)); +private string[] WordList => _options.WordList; +private char[] SpecialCharacters => _options.SpecialCharacters; +private int WordCount => _options.WordCount; +private int EmojiCount => _options.EmojiCount; +private int SpecialCharacterCount => _options.SpecialCharacterCount; +private int LowercaseCharacterCount => _options.LowercaseCharacterCount; +private int UppercaseCharacterCount => _options.UppercaseCharacterCount; +private int CharacterCount => _options.CharacterCount; +private char[] Emoji => _options.Emoji; + +public string Generate() +{ + var wordsToGo = WordCount; + var emojiToGo = EmojiCount; + var specialCharactersToGo = SpecialCharacterCount; + var lowercaseCharactersToGo = LowercaseCharacterCount; + var uppercaseCharactersToGo = UppercaseCharacterCount; + var charactersToGo = CharacterCount; + + var passphrase = new StringBuilder(); + + while ( + wordsToGo > 0 + || emojiToGo > 0 + || specialCharactersToGo > 0 + || lowercaseCharactersToGo > 0 + || uppercaseCharactersToGo > 0 + || charactersToGo > 0 + ) + { + var word = GetRandomWord(); + passphrase.Append(word); + wordsToGo--; + charactersToGo -= word.Length; + lowercaseCharactersToGo -= word.Count(char.IsLower); + uppercaseCharactersToGo -= word.Count(char.IsUpper); + + if (emojiToGo > 0) + { + var emoji = PickRandomElement(Emoji); + passphrase.Append(emoji); + emojiToGo--; + charactersToGo--; + } + + if (lowercaseCharactersToGo > 0) + { + var emoji = PickRandomElement(Emoji); + passphrase.Append(emoji); + emojiToGo--; + charactersToGo--; + } + + if (specialCharactersToGo > 0) + { + var specialCharacter = PickRandomElement(SpecialCharacters); + passphrase.Append(specialCharacter); + specialCharactersToGo--; + charactersToGo--; + } + } + + return passphrase.ToString(); +} + +private string GetRandomWord() => PickRandomElement(WordList); + +private static T PickRandomElement(T[] elements) => + elements.Skip(Random.NextInt32(elements.Length - 1)).First(); } diff --git a/src/MicrosoftGraph/UsersService.cs b/src/MicrosoftGraph/UsersService.cs index b3f5e146..5c1bbd7b 100644 --- a/src/MicrosoftGraph/UsersService.cs +++ b/src/MicrosoftGraph/UsersService.cs @@ -7,208 +7,208 @@ namespace Dgmjr.MicrosoftGraph; public class UsersService(GraphServiceClient graph, ILogger logger, IOptionsMonitor options, CancellationToken cancellationToken = default) : IUsersService { - public ILogger Logger => logger; - private MicrosoftB2CGraphOptions _options => options.CurrentValue; - public guid ExtensionsAppClientId => _options.AzureAdB2CExtensionsApplicationId; - - protected virtual GraphServiceClient Graph => graph; - - public async Task GetMeAsync(CancellationToken cancellationToken = default) - { - return await Graph.Me.Request().GetAsync(); - } - - public async Task UpdateAsync(User user, CancellationToken cancellationToken = default) - { - return await Graph.Me.Request().UpdateAsync(user); - } - - public async Task UpdateAsync(string id, User user, CancellationToken cancellationToken = default) - { - return await Graph.Users[id].Request().UpdateAsync(user); - } - - public async Task UpdateAsync(string id, string property, string value, CancellationToken cancellationToken = default) - { - var user = await Graph.Users[id].Request().GetAsync(); - user.AdditionalData[property] = value; - return await Graph.Users[id].Request().UpdateAsync(user); - } - - public async Task GetAsync(string id, CancellationToken cancellationToken = default) - { - return await Graph.Users[id].Request().GetAsync(); - } - - public async Task GetAsync(string id, string property, CancellationToken cancellationToken = default) - { - return await Graph.Users[id].Request().Select(property).GetAsync(); - } - - public async Task CreateAsync(User user, CancellationToken cancellationToken = default) - { - return await Graph.Users.Request().AddAsync(user); - } - - public async Task DeleteAsync(string id, CancellationToken cancellationToken = default) - { - await Graph.Users[id].Request().DeleteAsync(); - } - - public async Task IsInAppRoleAsync(string id, string appId, string appRoleId, CancellationToken cancellationToken = default) - => (await Graph.Users[id].Request().GetAsync()).AppRoleAssignments.Any(a => a.AppRoleId == new guid(appRoleId) && a.ResourceId == new guid(appId)); - - public async Task AssignToAppRoleAsync(string userId, string appId, string appRoleId, CancellationToken cancellationToken = default) - => await AssignToAppRoleAsync(new guid(userId), new guid(appId), new guid(appRoleId)); - - public async Task AssignToAppRoleAsync(guid userId, guid appId, guid appRoleId, CancellationToken cancellationToken = default) - { - return await Graph.Users[userId.ToString()].AppRoleAssignments.Request().AddAsync(new AppRoleAssignment - { - PrincipalId = userId, - ResourceId = appId, - AppRoleId = appRoleId - }); - } - - public async Task UnassignAppRoleAsync(string userId, string appId, string appRoleId, CancellationToken cancellationToken = default) - => await UnassignAppRoleAsync(new guid(userId), new(appId), new(appRoleId)); - - public async Task UnassignAppRoleAsync(guid userId, guid appId, guid appRoleId, CancellationToken cancellationToken = default) - { - var assignments = await Graph.Users[userId.ToString()].AppRoleAssignments.Request().GetAsync(); - var assignment = assignments.FirstOrDefault(a => a.AppRoleId == appRoleId && a.ResourceId == appId); - if (assignment is null) - { - throw new KeyNotFoundException($"No app role assignment found for user {userId} with app {appId} and role {appRoleId}"); - } - await Graph.Users[userId.ToString()].AppRoleAssignments[assignment.Id].Request().DeleteAsync(); - } - - public async Task FindByEmailAsync( - string normalizedEmail, - CancellationToken cancellationToken = default - ) - { - return ( - await Graph.Users - .Request() - .Filter($"mail eq '{normalizedEmail}'") - .GetAsync(cancellationToken) - ).FirstOrDefault(); - } - - public async Task FindBySignInNameAsync(string name, CancellationToken cancellationToken = default) - { - try - { - // Get user by sign-in name - var result = (await Graph.Users - .Request() - .Filter($"identities/any(c:c/issuerAssignedId eq '{name}')") - .Select(e => new - { - e.DisplayName, - e.Id, - e.Identities - }) - .GetAsync(cancellationToken)) - .SingleOrDefault(); - - if (result != null) - { - Logger.LogInformation(Serialize(result)); - return result; - } - - Logger.LogError($"User with login name {name} not found."); - return null; - } - catch (Exception ex) - { - Logger.LogError(ex, "Error retrieving user by sign-in name."); - } - - return null; - } - - public async Task SetPasswordByUserId(string userId, string password, bool forceChangePasswordNextSignIn = false, CancellationToken cancellationToken = default) - { - Logger.LogInformation($"Looking for user with object ID '{userId}'..."); - - var user = new User - { - PasswordPolicies = "DisablePasswordExpiration,DisableStrongPassword", - PasswordProfile = new PasswordProfile - { - ForceChangePasswordNextSignIn = forceChangePasswordNextSignIn, - Password = password, - } - }; - - try - { - // Update user by object ID - await Graph.Users[userId] - .Request() - .UpdateAsync(user); - - Logger.LogInformation($"User with object ID '{userId}' successfully updated."); - } - catch (ClientException ex) - { - Logger.LogError(ex, $"Error updating user with object ID '{userId}'."); - throw; - } - } - - public async Task> GetUsersWithCustomAttribute(GraphServiceClient graphClient, string attributeName, CancellationToken cancellationToken = default) - { - var extensionAttribute = new DgmjrExtensionProperty(attributeName, ExtensionsAppClientId); - - Logger.LogInformation($"Getting list of users with the custom attributes '{attributeName}' (string)"); - - // Get all users (one page) - var result = await graphClient.Users - .Request() - .Select($"id,displayName,identities,{extensionAttribute}") - .GetAsync(); - - return [.. result]; - - // foreach (var user in result.CurrentPage) - // { - // Console.WriteLine(Serialize(user)); - - // // Only output the custom attributes... - // //Console.WriteLine(JsonSerializer.Serialize(user.AdditionalData)); - // } - } - - public async Task GetExtensionPropertiesAsync(CancellationToken cancellationToken = default) - { - - var extensionProperties = await Graph.Applications[ExtensionsAppClientId.ToString()].ExtensionProperties.Request().GetAsync(); - return extensionProperties.AsEnumerable().ToArray(); - } - - // public async Task RemoveAppRoleByNameAsync(string userId, string appId, string appRoleName, CancellationToken cancellationToken = default) - // => await RemoveAppRoleByNameAsync(new guid(userId), new (appId), appRoleName); - - // public async Task RemoveAppRoleByNameAsync(guid userId, guid appId, string appRoleName, CancellationToken cancellationToken = default) - // { - // var assignments = await Graph.Users[userId.ToString()].AppRoleAssignments.Request().GetAsync(); - // graph..AppRoles.Request().Filter($"displayName eq '{appRoleName}'").GetAsync(); - // var assignment = assignments.FirstOrDefault(a => a..AppRoleId == appRoleId && a.ResourceId == appId); - // if (assignment is null) - // { - // throw new KeyNotFoundException($"No app role assignment found for user {userId} with app {appId} and role {appRoleId}"); - // } - // await Graph.Users[userId.ToString()].AppRoleAssignments[assignment.Id].Request().DeleteAsync(); - // } - - // public async Task GetAsync(string id, string property, string value, CancellationToken cancellationToken = default) - // { - // return await Graph.Users[id].Request().Filter($"{property} eq '{value}'").GetAsync(); - // } + public ILogger Logger => logger; +private MicrosoftB2CGraphOptions _options => options.CurrentValue; +public guid ExtensionsAppClientId => _options.AzureAdB2CExtensionsApplicationId; + +protected virtual GraphServiceClient Graph => graph; + +public async Task GetMeAsync(CancellationToken cancellationToken = default) +{ + return await Graph.Me.Request().GetAsync(); +} + +public async Task UpdateAsync(User user, CancellationToken cancellationToken = default) +{ + return await Graph.Me.Request().UpdateAsync(user); +} + +public async Task UpdateAsync(string id, User user, CancellationToken cancellationToken = default) +{ + return await Graph.Users[id].Request().UpdateAsync(user); +} + +public async Task UpdateAsync(string id, string property, string value, CancellationToken cancellationToken = default) +{ + var user = await Graph.Users[id].Request().GetAsync(); + user.AdditionalData[property] = value; + return await Graph.Users[id].Request().UpdateAsync(user); +} + +public async Task GetAsync(string id, CancellationToken cancellationToken = default) +{ + return await Graph.Users[id].Request().GetAsync(); +} + +public async Task GetAsync(string id, string property, CancellationToken cancellationToken = default) +{ + return await Graph.Users[id].Request().Select(property).GetAsync(); +} + +public async Task CreateAsync(User user, CancellationToken cancellationToken = default) +{ + return await Graph.Users.Request().AddAsync(user); +} + +public async Task DeleteAsync(string id, CancellationToken cancellationToken = default) +{ + await Graph.Users[id].Request().DeleteAsync(); +} + +public async Task IsInAppRoleAsync(string id, string appId, string appRoleId, CancellationToken cancellationToken = default) + => (await Graph.Users[id].Request().GetAsync()).AppRoleAssignments.Any(a => a.AppRoleId == new guid(appRoleId) && a.ResourceId == new guid(appId)); + +public async Task AssignToAppRoleAsync(string userId, string appId, string appRoleId, CancellationToken cancellationToken = default) + => await AssignToAppRoleAsync(new guid(userId), new guid(appId), new guid(appRoleId)); + +public async Task AssignToAppRoleAsync(guid userId, guid appId, guid appRoleId, CancellationToken cancellationToken = default) +{ + return await Graph.Users[userId.ToString()].AppRoleAssignments.Request().AddAsync(new AppRoleAssignment + { + PrincipalId = userId, + ResourceId = appId, + AppRoleId = appRoleId + }); +} + +public async Task UnassignAppRoleAsync(string userId, string appId, string appRoleId, CancellationToken cancellationToken = default) + => await UnassignAppRoleAsync(new guid(userId), new(appId), new(appRoleId)); + +public async Task UnassignAppRoleAsync(guid userId, guid appId, guid appRoleId, CancellationToken cancellationToken = default) +{ + var assignments = await Graph.Users[userId.ToString()].AppRoleAssignments.Request().GetAsync(); + var assignment = assignments.FirstOrDefault(a => a.AppRoleId == appRoleId && a.ResourceId == appId); + if (assignment is null) + { + throw new KeyNotFoundException($"No app role assignment found for user {userId} with app {appId} and role {appRoleId}"); + } + await Graph.Users[userId.ToString()].AppRoleAssignments[assignment.Id].Request().DeleteAsync(); +} + +public async Task FindByEmailAsync( + string normalizedEmail, + CancellationToken cancellationToken = default +) +{ + return ( + await Graph.Users + .Request() + .Filter($"mail eq '{normalizedEmail}'") + .GetAsync(cancellationToken) + ).FirstOrDefault(); +} + +public async Task FindBySignInNameAsync(string name, CancellationToken cancellationToken = default) +{ + try + { + // Get user by sign-in name + var result = (await Graph.Users + .Request() + .Filter($"identities/any(c:c/issuerAssignedId eq '{name}')") + .Select(e => new + { + e.DisplayName, + e.Id, + e.Identities + }) + .GetAsync(cancellationToken)) + .SingleOrDefault(); + + if (result != null) + { + Logger.LogInformation(Serialize(result)); + return result; + } + + Logger.LogError($"User with login name {name} not found."); + return null; + } + catch (Exception ex) + { + Logger.LogError(ex, "Error retrieving user by sign-in name."); + } + + return null; +} + +public async Task SetPasswordByUserId(string userId, string password, bool forceChangePasswordNextSignIn = false, CancellationToken cancellationToken = default) +{ + Logger.LogInformation($"Looking for user with object ID '{userId}'..."); + + var user = new User + { + PasswordPolicies = "DisablePasswordExpiration,DisableStrongPassword", + PasswordProfile = new PasswordProfile + { + ForceChangePasswordNextSignIn = forceChangePasswordNextSignIn, + Password = password, + } + }; + + try + { + // Update user by object ID + await Graph.Users[userId] + .Request() + .UpdateAsync(user); + + Logger.LogInformation($"User with object ID '{userId}' successfully updated."); + } + catch (ClientException ex) + { + Logger.LogError(ex, $"Error updating user with object ID '{userId}'."); + throw; + } +} + +public async Task> GetUsersWithCustomAttribute(GraphServiceClient graphClient, string attributeName, CancellationToken cancellationToken = default) +{ + var extensionAttribute = new DgmjrExtensionProperty(attributeName, ExtensionsAppClientId); + + Logger.LogInformation($"Getting list of users with the custom attributes '{attributeName}' (string)"); + + // Get all users (one page) + var result = await graphClient.Users + .Request() + .Select($"id,displayName,identities,{extensionAttribute}") + .GetAsync(); + + return [..result]; + + // foreach (var user in result.CurrentPage) + // { + // Console.WriteLine(Serialize(user)); + + // // Only output the custom attributes... + // //Console.WriteLine(JsonSerializer.Serialize(user.AdditionalData)); + // } +} + +public async Task GetExtensionPropertiesAsync(CancellationToken cancellationToken = default) +{ + + var extensionProperties = await Graph.Applications[ExtensionsAppClientId.ToString()].ExtensionProperties.Request().GetAsync(); + return extensionProperties.AsEnumerable().ToArray(); +} + +// public async Task RemoveAppRoleByNameAsync(string userId, string appId, string appRoleName, CancellationToken cancellationToken = default) +// => await RemoveAppRoleByNameAsync(new guid(userId), new (appId), appRoleName); + +// public async Task RemoveAppRoleByNameAsync(guid userId, guid appId, string appRoleName, CancellationToken cancellationToken = default) +// { +// var assignments = await Graph.Users[userId.ToString()].AppRoleAssignments.Request().GetAsync(); +// graph..AppRoles.Request().Filter($"displayName eq '{appRoleName}'").GetAsync(); +// var assignment = assignments.FirstOrDefault(a => a..AppRoleId == appRoleId && a.ResourceId == appId); +// if (assignment is null) +// { +// throw new KeyNotFoundException($"No app role assignment found for user {userId} with app {appId} and role {appRoleId}"); +// } +// await Graph.Users[userId.ToString()].AppRoleAssignments[assignment.Id].Request().DeleteAsync(); +// } + +// public async Task GetAsync(string id, string property, string value, CancellationToken cancellationToken = default) +// { +// return await Graph.Users[id].Request().Filter($"{property} eq '{value}'").GetAsync(); +// } } diff --git a/src/Middleware/RequestLogging/RequestLoggingMiddleware.cs b/src/Middleware/RequestLogging/RequestLoggingMiddleware.cs index 4141f3fa..73cb6f75 100644 --- a/src/Middleware/RequestLogging/RequestLoggingMiddleware.cs +++ b/src/Middleware/RequestLogging/RequestLoggingMiddleware.cs @@ -6,10 +6,10 @@ namespace Dgmjr.AspNetCore.Middleware.RequestLogging; public class RequestLoggingMiddleware(ILogger logger) : IMiddleware { - public ILogger Logger => logger; - - public Task InvokeAsync(HttpContext context, RequestDelegate next) - { - throw new NotImplementedException(); - } + public ILogger Logger => logger; + +public Task InvokeAsync(HttpContext context, RequestDelegate next) +{ + throw new NotImplementedException(); +} } diff --git a/src/Mvc/IHostApplicationBuilderMvcExtensions.cs b/src/Mvc/IHostApplicationBuilderMvcExtensions.cs index f39baaaf..226d9777 100644 --- a/src/Mvc/IHostApplicationBuilderMvcExtensions.cs +++ b/src/Mvc/IHostApplicationBuilderMvcExtensions.cs @@ -21,7 +21,7 @@ public static IHostApplicationBuilder AddMvc(this IHostApplicationBuilder builde builder.Services.Configure(mvcOptionsSection); var mvcOptions = mvcOptionsSection.Get(); - if(mvcOptions is not null) + if (mvcOptions is not null) { #if NET5_0_OR_GREATER if(mvcOptions.EnableEndpointRouting) @@ -44,22 +44,22 @@ public static IHostApplicationBuilder AddMvc(this IHostApplicationBuilder builde } #endif - if(mvcOptions.AddControllersAsServices) + if (mvcOptions.AddControllersAsServices) { mvcBuilder.AddControllersAsServices(); } - if(mvcOptions.AddXmlSerializerFormatters) + if (mvcOptions.AddXmlSerializerFormatters) { mvcBuilder.AddXmlSerializerFormatters(); } - if(mvcOptions.AddXmlDataContractSerializerFormatters) + if (mvcOptions.AddXmlDataContractSerializerFormatters) { mvcBuilder.AddXmlDataContractSerializerFormatters(); } - if(mvcOptions.AddMvcConventions) + if (mvcOptions.AddMvcConventions) { // mvcBuilder.AddMvcOptions(options => builder.Configuration.Bind(configurationSectionKey, options)); } diff --git a/src/Mvc/OKAttributes.cs b/src/Mvc/OKAttributes.cs index 80487ae5..f962357e 100644 --- a/src/Mvc/OKAttributes.cs +++ b/src/Mvc/OKAttributes.cs @@ -34,7 +34,8 @@ public class ProducesOKResponseAttribute( Application.MessagePack.DisplayName, Application.Bson.DisplayName, Text.Plain.DisplayName - ) { } + ) +{ } /// Notes that the method can produce a 200 OK response /// The type of the model to be returned. @@ -55,7 +56,8 @@ public sealed class ProducesNoContentResponseAttribute( Application.MessagePack.DisplayName, Application.Bson.DisplayName, TextMediaTypeNames.Plain - ) { } + ) +{ } public class ProducesCreatedResponseAttribute( type modelType, @@ -70,7 +72,8 @@ public class ProducesCreatedResponseAttribute( Application.MessagePack.DisplayName, Application.Bson.DisplayName, Text.Plain.DisplayName - ) { } + ) +{ } public class ProducesCreatedResponseAttribute( string description = "The shit you were try'n'a create was created successfully." @@ -89,7 +92,8 @@ public class ProducesPartialContentResponseAttribute( Application.MessagePack.DisplayName, Application.Bson.DisplayName, TextMediaTypeNames.Plain - ) { } + ) +{ } public sealed class ProducesPartialContentResponseAttribute( string description = "Here's some of the shit you requested." @@ -110,9 +114,9 @@ public sealed class CreateOperationAttribute( public sealed class UpdateOperationAttribute( string? operationId, - string? summary = "Update an existing resource from a complete model object", - string? description = "Update an existing resource from a complete model object", - string[]? tags = null + string ? summary = "Update an existing resource from a complete model object", + string ? description = "Update an existing resource from a complete model object", + string[] ? tags = null ) : DgmjrOperationAttribute( operationId, @@ -123,9 +127,9 @@ public sealed class UpdateOperationAttribute( public sealed class DeleteOperationAttribute( string? operationId, - string? summary = "Delete an existing resource", - string? description = "Delete an existing resource", - string[]? tags = null + string ? summary = "Delete an existing resource", + string ? description = "Delete an existing resource", + string[] ? tags = null ) : DgmjrOperationAttribute( operationId, @@ -136,9 +140,9 @@ public sealed class DeleteOperationAttribute( public sealed class PatchOperationAttribute( string? operationId, - string? summary = "Update an existing resource from a partial model object", - string? description = "Update an existing resource from a partial model object", - string[]? tags = null + string ? summary = "Update an existing resource from a partial model object", + string ? description = "Update an existing resource from a partial model object", + string[] ? tags = null ) : DgmjrOperationAttribute( operationId, diff --git a/src/Mvc/ProducesErrorAttributes/Produces400ErrorAttribute.cs b/src/Mvc/ProducesErrorAttributes/Produces400ErrorAttribute.cs index 33427666..74d88ce7 100644 --- a/src/Mvc/ProducesErrorAttributes/Produces400ErrorAttribute.cs +++ b/src/Mvc/ProducesErrorAttributes/Produces400ErrorAttribute.cs @@ -25,5 +25,6 @@ public Produces400ErrorAttribute() "You done fucked up!", typeof(BadRequestProblemDetails), ApplicationMediaTypeNames.ProblemJson - ) { } + ) + { } } diff --git a/src/Mvc/ProducesErrorAttributes/Produces401ErrorAttribute.cs b/src/Mvc/ProducesErrorAttributes/Produces401ErrorAttribute.cs index e66a09a9..b19d3a00 100644 --- a/src/Mvc/ProducesErrorAttributes/Produces401ErrorAttribute.cs +++ b/src/Mvc/ProducesErrorAttributes/Produces401ErrorAttribute.cs @@ -25,5 +25,6 @@ public Produces401ErrorAttribute() "You're not allowed to fucking do that!", typeof(UnauthorizedProblemDetails), ApplicationMediaTypeNames.ProblemJson - ) { } + ) + { } } diff --git a/src/Mvc/ProducesErrorAttributes/Produces403ErrorAttribute.cs b/src/Mvc/ProducesErrorAttributes/Produces403ErrorAttribute.cs index 568b0a0a..2f970984 100644 --- a/src/Mvc/ProducesErrorAttributes/Produces403ErrorAttribute.cs +++ b/src/Mvc/ProducesErrorAttributes/Produces403ErrorAttribute.cs @@ -25,5 +25,6 @@ public Produces403ErrorAttribute() "You're not allowed to fucking do that!", typeof(ForbiddenProblemDetails), ApplicationMediaTypeNames.ProblemJson - ) { } + ) + { } } diff --git a/src/Mvc/ProducesErrorAttributes/Produces404ErrorAttribute.cs b/src/Mvc/ProducesErrorAttributes/Produces404ErrorAttribute.cs index ecfb62f0..d36c24b3 100644 --- a/src/Mvc/ProducesErrorAttributes/Produces404ErrorAttribute.cs +++ b/src/Mvc/ProducesErrorAttributes/Produces404ErrorAttribute.cs @@ -25,5 +25,6 @@ public Produces404ErrorAttribute() "The shit you're looking for doesn't fucking exist!", typeof(NotFoundProblemDetails), ApplicationMediaTypeNames.ProblemJson - ) { } + ) + { } } diff --git a/src/Mvc/ProducesErrorAttributes/Produces418ErrorAttribute.cs b/src/Mvc/ProducesErrorAttributes/Produces418ErrorAttribute.cs index 0fdf6b67..e9580db1 100644 --- a/src/Mvc/ProducesErrorAttributes/Produces418ErrorAttribute.cs +++ b/src/Mvc/ProducesErrorAttributes/Produces418ErrorAttribute.cs @@ -25,5 +25,6 @@ public Produces418ErrorAttribute() "I'm a fucking teapot, short and stout. Here's my handle; here's my spout. If you've reached this error code, you must shout, \"I'm a fuckin' idiot so kick me out!\"", typeof(ImATeapotProblemDetailsExample), ApplicationMediaTypeNames.ProblemJson - ) { } + ) + { } } diff --git a/src/Mvc/ProducesErrorAttributes/Produces500ErrorAttribute.cs b/src/Mvc/ProducesErrorAttributes/Produces500ErrorAttribute.cs index a91cc24a..65e8a73d 100644 --- a/src/Mvc/ProducesErrorAttributes/Produces500ErrorAttribute.cs +++ b/src/Mvc/ProducesErrorAttributes/Produces500ErrorAttribute.cs @@ -25,5 +25,6 @@ public Produces500ErrorAttribute() "Shit hit the fucking fan!", typeof(InternalServerErrorProblemDetails), ApplicationMediaTypeNames.ProblemJson - ) { } + ) + { } } diff --git a/src/Mvc/ProducesErrorAttributes/Produces501ErrorAttribute.cs b/src/Mvc/ProducesErrorAttributes/Produces501ErrorAttribute.cs index 73dd1850..b90ba7e9 100644 --- a/src/Mvc/ProducesErrorAttributes/Produces501ErrorAttribute.cs +++ b/src/Mvc/ProducesErrorAttributes/Produces501ErrorAttribute.cs @@ -25,5 +25,6 @@ public Produces501ErrorAttribute() "I'm not fucking programmed to do that shit!", typeof(NotImplementedProblemDetails), ApplicationMediaTypeNames.ProblemJson - ) { } + ) + { } } diff --git a/src/Payloads/Infrastructure/ResponsePayloadExecutor.cs b/src/Payloads/Infrastructure/ResponsePayloadExecutor.cs index 89882729..c5ae6062 100644 --- a/src/Payloads/Infrastructure/ResponsePayloadExecutor.cs +++ b/src/Payloads/Infrastructure/ResponsePayloadExecutor.cs @@ -34,8 +34,8 @@ public partial class ResponsePayloadExecutor : IActionResultExecutor _outputFormatters; public ResponsePayloadExecutor( - OutputFormatterSelector formatterSelector, - // IHttpResponseStreamWriterFactory writerFactory, + OutputFormatterSelector formatterSelector, + // IHttpResponseStreamWriterFactory writerFactory, ILogger> logger, IOptions mvcOptions ) diff --git a/src/Payloads/Pagers/SingleItemPager.cs b/src/Payloads/Pagers/SingleItemPager.cs index 1f885460..9a197b07 100644 --- a/src/Payloads/Pagers/SingleItemPager.cs +++ b/src/Payloads/Pagers/SingleItemPager.cs @@ -24,14 +24,14 @@ public class SingleItemPager(object? value, int pageNumber, int totalRecords) : SingleItemPager(value, pageNumber, totalRecords) { public SingleItemPager() - : this(default, 0, 0) { } - - public static new SingleItemPager NotFound() => - new() { StatusCode = (int)HttpStatusCode.NotFound }; - - public static new SingleItemPager BadRequest() => - new() { StatusCode = (int)HttpStatusCode.BadRequest }; - - public static new SingleItemPager NoContent() => - new() { StatusCode = (int)HttpStatusCode.NoContent }; + : this(default, 0, 0) { } + +public static new SingleItemPager NotFound() => + new() { StatusCode = (int)HttpStatusCode.NotFound }; + +public static new SingleItemPager BadRequest() => + new() { StatusCode = (int)HttpStatusCode.BadRequest }; + +public static new SingleItemPager NoContent() => + new() { StatusCode = (int)HttpStatusCode.NoContent }; } diff --git a/src/Payloads/Pagers/SingleItemPager{T}.cs b/src/Payloads/Pagers/SingleItemPager{T}.cs index 021a9f2a..a6e8933d 100644 --- a/src/Payloads/Pagers/SingleItemPager{T}.cs +++ b/src/Payloads/Pagers/SingleItemPager{T}.cs @@ -33,79 +33,79 @@ public SingleItemPager(T value, int pageNumber, int totalRecords) TotalRecords = totalRecords; Message = string.Empty; StringValue = value.ToString(); - } - - public SingleItemPager(IQueryable items, int itemNumber) - : base() - { - TotalRecords = items.Count(); - Page = itemNumber; - PageSize = 1; - Message = string.Empty; - Item = items.Skip(itemNumber - 1).FirstOrDefault(); - StringValue = Item.ToString(); - } - - [JProp("item")] - public virtual T? Item - { - get => (Items ?? new[] { default(T) }).FirstOrDefault(); - set => Items = new[] { value }!; - } - - [JIgnore] - public override T[]? Items - { - get => base.Items; - set => base.Items = value; - } - - [JIgnore] - public override T[]? Value - { - get => base.Value; - set => base.Value = value; - } - - [JIgnore] - T? IPayload.Value - { - get => Item; - set => Item = value; - } - object[]? IPager.Items - { - get => (Items ?? new[] { default(T) }).OfType().ToArray(); - set => Items = (value ?? new object[] { default(T) }).OfType().ToArray(); - } - - public static new OpenApiSchema GetOpenApiSchema() - { - var schema = Pager.GetOpenApiSchema(); - schema.Properties.Remove("items"); - // schema.Properties.Add("page", new OpenApiSchema { Type = "integer", Format = "int32" }); - // schema.Properties.Add("pageSize", new OpenApiSchema { Type = "integer", Format = "int32" }); - // schema.Properties.Add("totalRecords", new OpenApiSchema { Type = "integer", Format = "int32" }); - schema.Properties.Add( - "item", - new OpenApiSchema - { - Reference = new OpenApiReference - { - Id = typeof(T).Name, - Type = ReferenceType.Schema - } - } - ); - return schema; - } - - public static new SingleItemPager NotFound() => - new() { StatusCode = (int)HttpStatusCode.NotFound }; - - public static new SingleItemPager BadRequest() => - new() { StatusCode = (int)HttpStatusCode.BadRequest }; - - public static new SingleItemPager NoContent() => - new() { StatusCode = (int)HttpStatusCode.NoContent }; + } + +public SingleItemPager(IQueryable items, int itemNumber) + : base() +{ + TotalRecords = items.Count(); + Page = itemNumber; + PageSize = 1; + Message = string.Empty; + Item = items.Skip(itemNumber - 1).FirstOrDefault(); + StringValue = Item.ToString(); +} + +[JProp("item")] +public virtual T? Item +{ + get => (Items ?? new[] { default(T) }).FirstOrDefault(); + set => Items = new[] { value }!; +} + +[JIgnore] +public override T[]? Items +{ + get => base.Items; + set => base.Items = value; +} + +[JIgnore] +public override T[]? Value +{ + get => base.Value; + set => base.Value = value; +} + +[JIgnore] +T? IPayload.Value +{ + get => Item; + set => Item = value; +} +object[]? IPager.Items +{ + get => (Items ?? new[] { default(T) }).OfType().ToArray(); + set => Items = (value ?? new object[] { default(T) }).OfType().ToArray(); +} + +public static new OpenApiSchema GetOpenApiSchema() +{ + var schema = Pager.GetOpenApiSchema(); + schema.Properties.Remove("items"); + // schema.Properties.Add("page", new OpenApiSchema { Type = "integer", Format = "int32" }); + // schema.Properties.Add("pageSize", new OpenApiSchema { Type = "integer", Format = "int32" }); + // schema.Properties.Add("totalRecords", new OpenApiSchema { Type = "integer", Format = "int32" }); + schema.Properties.Add( + "item", + new OpenApiSchema + { + Reference = new OpenApiReference + { + Id = typeof(T).Name, + Type = ReferenceType.Schema + } + } + ); + return schema; +} + +public static new SingleItemPager NotFound() => + new() { StatusCode = (int)HttpStatusCode.NotFound }; + +public static new SingleItemPager BadRequest() => + new() { StatusCode = (int)HttpStatusCode.BadRequest }; + +public static new SingleItemPager NoContent() => + new() { StatusCode = (int)HttpStatusCode.NoContent }; } diff --git a/src/Payloads/ResponsePayloads/BooleanResponsePayload.cs b/src/Payloads/ResponsePayloads/BooleanResponsePayload.cs index f7e7d9fb..7b05fdd0 100644 --- a/src/Payloads/ResponsePayloads/BooleanResponsePayload.cs +++ b/src/Payloads/ResponsePayloads/BooleanResponsePayload.cs @@ -20,12 +20,12 @@ public class BooleanResponsePayload(bool value, string? message = default!) : ResponsePayload(value, message) { /// - [JProp("stringValue")] - [JIgnore(Condition = JIgnore.WhenWritingNull)] - [XmlAttribute("stringValue")] - public override string? StringValue - { - get => Value.ToString(); - set => Value = bool.Parse(value); - } + [JProp("stringValue")] +[JIgnore(Condition = JIgnore.WhenWritingNull)] +[XmlAttribute("stringValue")] +public override string? StringValue +{ + get => Value.ToString(); + set => Value = bool.Parse(value); +} } diff --git a/src/Payloads/ResponsePayloads/IntResponsePayload.cs b/src/Payloads/ResponsePayloads/IntResponsePayload.cs index d2c80c1c..3b28a52f 100644 --- a/src/Payloads/ResponsePayloads/IntResponsePayload.cs +++ b/src/Payloads/ResponsePayloads/IntResponsePayload.cs @@ -17,9 +17,9 @@ public class IntResponsePayload(int value, string? message = default!) : ResponsePayload(value, message ?? string.Empty) { /// - public override string? StringValue - { - get => Value.ToString(); - set => Value = int.Parse(value); - } + public override string? StringValue +{ + get => Value.ToString(); + set => Value = int.Parse(value); +} } diff --git a/src/Payloads/ResponsePayloads/NumericResponsePayload.cs b/src/Payloads/ResponsePayloads/NumericResponsePayload.cs index 131d891a..17af9cd3 100644 --- a/src/Payloads/ResponsePayloads/NumericResponsePayload.cs +++ b/src/Payloads/ResponsePayloads/NumericResponsePayload.cs @@ -21,9 +21,9 @@ public class NumericResponsePayload( ) : ResponsePayload(value, message) { /// - public override string? StringValue - { - get => Value.ToString(); - set => base.StringValue = stringValue ?? value; - } + public override string? StringValue +{ + get => Value.ToString(); + set => base.StringValue = stringValue ?? value; +} } diff --git a/src/Payloads/ResponsePayloads/UriResponsePayload.cs b/src/Payloads/ResponsePayloads/UriResponsePayload.cs index bb409056..2f1a126e 100644 --- a/src/Payloads/ResponsePayloads/UriResponsePayload.cs +++ b/src/Payloads/ResponsePayloads/UriResponsePayload.cs @@ -20,25 +20,25 @@ public class UriResponsePayload(uri value, string? message = default!) : ResponsePayload(value, message) { /// - [JProp("stringValue")] - [JIgnore(Condition = JIgnore.WhenWritingNull)] - [XAttribute("stringValue")] - public override string? StringValue - { - get => base.StringValue ?? Value.ToString(); - set - { - if (uri.TryParse(value, out var u)) - { - base.StringValue = u.ToString(); - Value = u; - } - else - { - throw new InvalidCastException( - $"Cannot cast {value} to {nameof(UriResponsePayload)}" - ); - } - } - } + [JProp("stringValue")] +[JIgnore(Condition = JIgnore.WhenWritingNull)] +[XAttribute("stringValue")] +public override string? StringValue +{ + get => base.StringValue ?? Value.ToString(); + set + { + if (uri.TryParse(value, out var u)) + { + base.StringValue = u.ToString(); + Value = u; + } + else + { + throw new InvalidCastException( + $"Cannot cast {value} to {nameof(UriResponsePayload)}" + ); + } + } +} } diff --git a/src/Payloads/Scalars/BooleanPayload.cs b/src/Payloads/Scalars/BooleanPayload.cs index 5399dba3..3c9fd554 100644 --- a/src/Payloads/Scalars/BooleanPayload.cs +++ b/src/Payloads/Scalars/BooleanPayload.cs @@ -15,7 +15,7 @@ namespace Dgmjr.Payloads; public class BooleanPayload(bool value) : Payload(value, value.ToString()) { public BooleanPayload() - : this(true) { } - - public override string ToString() => Value.ToString(); + : this(true) { } + +public override string ToString() => Value.ToString(); } diff --git a/src/Payloads/Scalars/StringWithRegexPayload.cs b/src/Payloads/Scalars/StringWithRegexPayload.cs index 19777878..6e561406 100644 --- a/src/Payloads/Scalars/StringWithRegexPayload.cs +++ b/src/Payloads/Scalars/StringWithRegexPayload.cs @@ -15,11 +15,11 @@ namespace Dgmjr.Payloads; public class StringWithRegexPayload(string? value, string? regex = default) : Payload(value) { public StringWithRegexPayload() - : this(default, default) { } - - [JProp("value")] - public override string? Value { get; set; } - - [JProp("regex")] - public virtual string Regex { get; set; } = regex ?? string.Empty; + : this(default, default) { } + +[JProp("value")] +public override string? Value { get; set; } + +[JProp("regex")] +public virtual string Regex { get; set; } = regex ?? string.Empty; } diff --git a/src/Startup/Startup.cs b/src/Startup/Startup.cs index cd68b0aa..ce34413c 100644 --- a/src/Startup/Startup.cs +++ b/src/Startup/Startup.cs @@ -7,10 +7,10 @@ namespace Dgmjr.AspNetCore; public abstract class Startup(IConfiguration configuration, IWebHostEnvironment env) { - public IConfiguration Configuration { get; } = configuration; - public IWebHostEnvironment Environment { get; } = env; - - public abstract void ConfigureServices(IServiceCollection services); - - public abstract void Configure(IApplicationBuilder app); + public IConfiguration Configuration { get; } = configuration; +public IWebHostEnvironment Environment { get; } = env; + +public abstract void ConfigureServices(IServiceCollection services); + +public abstract void Configure(IApplicationBuilder app); } diff --git a/src/Swagger/Microsoft.OpenApi/OpenApiAnyType.cs b/src/Swagger/Microsoft.OpenApi/OpenApiAnyType.cs index d694df71..ad2e8614 100644 --- a/src/Swagger/Microsoft.OpenApi/OpenApiAnyType.cs +++ b/src/Swagger/Microsoft.OpenApi/OpenApiAnyType.cs @@ -37,42 +37,42 @@ or Version => AnyType.Primitive, IEnumerable => AnyType.Array, _ => AnyType.Object - }; - - private readonly IOpenApiAny? _value = value switch - { - null => new OpenApiNull(), - bool => new OpenApiBoolean((bool)value), - byte - or sbyte => new OpenApiByte((byte)value), - byte[] => new OpenApiBinary((byte[])value), - short - or ushort - or int - or uint - or long - or ulong => new OpenApiInteger((short)value), - float => new OpenApiFloat((float)value), - double - or decimal => new OpenApiDouble((double)value), - char - or string => new OpenApiString((string)value), - datetime => new OpenApiDateTime((DateTime)value), - date => new OpenApiDate(((date)value).ToDateTime(new time(0, 0, 0, 0))), - time => new OpenApiString(((time)value).ToTimeSpan().ToString()), - DateTimeOffset => new OpenApiDateTime(DateTime.FromFileTimeUtc(((DateTimeOffset)value).ToUniversalTime().ToFileTime())), - Uri => new OpenApiString(((Uri)value).ToString()), - duration => new OpenApiString(((TimeSpan)value).ToString()), - guid => new OpenApiString(((Guid)value).ToString()), - Version => new OpenApiString(((Version)value).ToString()), - IEnumerable => (value as IEnumerable).ToOpenApiArray(), - _ => value.ToOpenApiObject() - }; - - public void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion) - { - _value.Write(writer, specVersion); - } + }; + +private readonly IOpenApiAny? _value = value switch +{ + null => new OpenApiNull(), + bool => new OpenApiBoolean((bool)value), + byte + or sbyte => new OpenApiByte((byte)value), + byte[] => new OpenApiBinary((byte[])value), + short + or ushort + or int + or uint + or long + or ulong => new OpenApiInteger((short)value), + float => new OpenApiFloat((float)value), + double + or decimal => new OpenApiDouble((double)value), + char + or string => new OpenApiString((string)value), + datetime => new OpenApiDateTime((DateTime)value), + date => new OpenApiDate(((date)value).ToDateTime(new time(0, 0, 0, 0))), + time => new OpenApiString(((time)value).ToTimeSpan().ToString()), + DateTimeOffset => new OpenApiDateTime(DateTime.FromFileTimeUtc(((DateTimeOffset)value).ToUniversalTime().ToFileTime())), + Uri => new OpenApiString(((Uri)value).ToString()), + duration => new OpenApiString(((TimeSpan)value).ToString()), + guid => new OpenApiString(((Guid)value).ToString()), + Version => new OpenApiString(((Version)value).ToString()), + IEnumerable => (value as IEnumerable).ToOpenApiArray(), + _ => value.ToOpenApiObject() +}; + +public void Write(IOpenApiWriter writer, OpenApiSpecVersion specVersion) +{ + _value.Write(writer, specVersion); +} } public static class ToOpenApiObjectExtensions diff --git a/src/Swagger/SwaggerExtensions/AddSwaggerGenExtension.ThisAssemblyProject.cs b/src/Swagger/SwaggerExtensions/AddSwaggerGenExtension.ThisAssemblyProject.cs index 78344712..22967125 100644 --- a/src/Swagger/SwaggerExtensions/AddSwaggerGenExtension.ThisAssemblyProject.cs +++ b/src/Swagger/SwaggerExtensions/AddSwaggerGenExtension.ThisAssemblyProject.cs @@ -18,96 +18,96 @@ namespace Microsoft.Extensions.DependencyInjection; internal record struct TThisAssemblyStaticProxy(type ThisAssemblyStaticProxy) { - public const string ThisAssembly = nameof(ThisAssembly); - - public static TThisAssemblyStaticProxy From(Assembly asm) => - new TThisAssemblyStaticProxy(Find(asm.GetTypes(), t => t.Name is nameof(ThisAssembly))); - - public type? Project => - Find(ThisAssemblyStaticProxy.GetNestedTypes(), t => t.Name == nameof(Project)); - public type? Info => - Find(ThisAssemblyStaticProxy.GetNestedTypes(), t => t.Name == nameof(Info)); - public type? Git => Find(ThisAssemblyStaticProxy.GetNestedTypes(), t => t.Name == nameof(Git)); - public type? Metadata => - Find(ThisAssemblyStaticProxy.GetNestedTypes(), t => t.Name == nameof(Metadata)); - public type? Strings => - Find(ThisAssemblyStaticProxy.GetNestedTypes(), t => t.Name == nameof(Strings)); - public Assembly Assembly => ThisAssemblyStaticProxy.Assembly; - - public string? AssemblyVersion => - Project?.GetRuntimeField(nameof(AssemblyVersion))?.GetValue(null) as string - ?? Info?.GetRuntimeField(nameof(AssemblyVersion))?.GetValue(null) as string - ?? Assembly.GetCustomAttributes()?.FirstOrDefault()?.Version; - public string? Authors => Project?.GetRuntimeField(nameof(Authors))?.GetValue(null) as string; - public string? Company => - Project?.GetRuntimeField(nameof(Company))?.GetValue(null) as string - ?? Assembly.GetCustomAttributes()?.FirstOrDefault()?.Company; - public string? ContactEmail => - Project?.GetRuntimeField(nameof(ContactEmail))?.GetValue(null) as string; - public string? Copyright => - Project?.GetRuntimeField(nameof(Copyright))?.GetValue(null) as string - ?? Assembly.GetCustomAttributes()?.FirstOrDefault()?.Copyright; - public string? Description => - Assembly.GetCustomAttributes()?.FirstOrDefault()?.Description - ?? this.ThisAssemblyStaticProxy - ?.GetRuntimeField(nameof(Description)) - ?.GetValue(null) - ?.ToString() - ?? Project?.GetRuntimeField(nameof(Description))?.GetValue(null) as string; - public string? FileVersion => - Project?.GetRuntimeField(nameof(FileVersion))?.GetValue(null) as string - ?? Assembly.GetCustomAttributes()?.FirstOrDefault()?.Version; - public string? InformationalVersion => - Project?.GetRuntimeField(nameof(InformationalVersion))?.GetValue(null) as string; - public string? LicenseExpression => - PackageLicenseExpression - ?? Project?.GetRuntimeField(nameof(LicenseExpression))?.GetValue(null)?.ToString() - ?? Project?.GetRuntimeField(nameof(LicenseExpression))?.GetValue(null)?.ToString() - ?? "None"; - public string? Owners => Project?.GetRuntimeField(nameof(Owners))?.GetValue(null) as string; - public string? PackageLicenseExpression => - Project?.GetRuntimeField(nameof(PackageLicenseExpression))?.GetValue(null) as string; - public string? PackageTags => - Project?.GetRuntimeField(nameof(PackageTags))?.GetValue(null) as string; - public string? PackageVersion => - Project?.GetRuntimeField(nameof(PackageVersion))?.GetValue(null) as string; - public string? Product => Project?.GetRuntimeField(nameof(Product))?.GetValue(null) as string; - public string? SwaggerTheme => - Project?.GetRuntimeField(nameof(SwaggerTheme))?.GetValue(null) as string; - public string? Title => - Project?.GetRuntimeField(nameof(Title))?.GetValue(null) as string - ?? Assembly.GetCustomAttributes()?.FirstOrDefault()?.Title; - public string? Version => Project?.GetRuntimeField(nameof(Version))?.GetValue(null) as string; - public uri? LicenseUrl => $"https://opensource.org/licenses/{LicenseExpression}"; - public uri? PackageProjectUrl => - IsNullOrWhiteSpace(PackageProjectUrlString) - ? "https://example.com/contact" - : PackageProjectUrlString; - - public uri? RepositoryUrl => - IsNullOrWhiteSpace(RepositoryUrlString) ? "about:blank" : RepositoryUrlString; - public uri? TermsOfServiceUrl => - IsNullOrWhiteSpace(TermsOfServiceUrlString) - ? "https://example.com/terms" - : TermsOfServiceUrlString; - - public string? RepositoryUrlString => - Project?.GetRuntimeField(nameof(RepositoryUrl))?.GetValue(null) as string; - public string? PackageProjectUrlString => - Project?.GetRuntimeField(nameof(PackageProjectUrl))?.GetValue(null) as string; - public string? TermsOfServiceUrlString => - Project?.GetRuntimeField(nameof(TermsOfServiceUrl))?.GetValue(null) as string; - public string? ApiVersion => - "v" - + ( - IsNullOrEmpty(PackageVersion) - ? IsNullOrEmpty(FileVersion) - ? IsNullOrEmpty(Version) - ? IsNullOrEmpty(AssemblyVersion) - ? "0.0.1-Preview" - : AssemblyVersion - : Version - : FileVersion - : PackageVersion - ); + public const string ThisAssembly = nameof(ThisAssembly); + +public static TThisAssemblyStaticProxy From(Assembly asm) => + new TThisAssemblyStaticProxy(Find(asm.GetTypes(), t => t.Name is nameof(ThisAssembly))); + +public type? Project => + Find(ThisAssemblyStaticProxy.GetNestedTypes(), t => t.Name == nameof(Project)); +public type? Info => + Find(ThisAssemblyStaticProxy.GetNestedTypes(), t => t.Name == nameof(Info)); +public type? Git => Find(ThisAssemblyStaticProxy.GetNestedTypes(), t => t.Name == nameof(Git)); +public type? Metadata => + Find(ThisAssemblyStaticProxy.GetNestedTypes(), t => t.Name == nameof(Metadata)); +public type? Strings => + Find(ThisAssemblyStaticProxy.GetNestedTypes(), t => t.Name == nameof(Strings)); +public Assembly Assembly => ThisAssemblyStaticProxy.Assembly; + +public string? AssemblyVersion => + Project?.GetRuntimeField(nameof(AssemblyVersion))?.GetValue(null) as string + ?? Info?.GetRuntimeField(nameof(AssemblyVersion))?.GetValue(null) as string + ?? Assembly.GetCustomAttributes()?.FirstOrDefault()?.Version; +public string? Authors => Project?.GetRuntimeField(nameof(Authors))?.GetValue(null) as string; +public string? Company => + Project?.GetRuntimeField(nameof(Company))?.GetValue(null) as string + ?? Assembly.GetCustomAttributes()?.FirstOrDefault()?.Company; +public string? ContactEmail => + Project?.GetRuntimeField(nameof(ContactEmail))?.GetValue(null) as string; +public string? Copyright => + Project?.GetRuntimeField(nameof(Copyright))?.GetValue(null) as string + ?? Assembly.GetCustomAttributes()?.FirstOrDefault()?.Copyright; +public string? Description => + Assembly.GetCustomAttributes()?.FirstOrDefault()?.Description + ?? this.ThisAssemblyStaticProxy + ?.GetRuntimeField(nameof(Description)) + ?.GetValue(null) + ?.ToString() + ?? Project?.GetRuntimeField(nameof(Description))?.GetValue(null) as string; +public string? FileVersion => + Project?.GetRuntimeField(nameof(FileVersion))?.GetValue(null) as string + ?? Assembly.GetCustomAttributes()?.FirstOrDefault()?.Version; +public string? InformationalVersion => + Project?.GetRuntimeField(nameof(InformationalVersion))?.GetValue(null) as string; +public string? LicenseExpression => + PackageLicenseExpression + ?? Project?.GetRuntimeField(nameof(LicenseExpression))?.GetValue(null)?.ToString() + ?? Project?.GetRuntimeField(nameof(LicenseExpression))?.GetValue(null)?.ToString() + ?? "None"; +public string? Owners => Project?.GetRuntimeField(nameof(Owners))?.GetValue(null) as string; +public string? PackageLicenseExpression => + Project?.GetRuntimeField(nameof(PackageLicenseExpression))?.GetValue(null) as string; +public string? PackageTags => + Project?.GetRuntimeField(nameof(PackageTags))?.GetValue(null) as string; +public string? PackageVersion => + Project?.GetRuntimeField(nameof(PackageVersion))?.GetValue(null) as string; +public string? Product => Project?.GetRuntimeField(nameof(Product))?.GetValue(null) as string; +public string? SwaggerTheme => + Project?.GetRuntimeField(nameof(SwaggerTheme))?.GetValue(null) as string; +public string? Title => + Project?.GetRuntimeField(nameof(Title))?.GetValue(null) as string + ?? Assembly.GetCustomAttributes()?.FirstOrDefault()?.Title; +public string? Version => Project?.GetRuntimeField(nameof(Version))?.GetValue(null) as string; +public uri? LicenseUrl => $"https://opensource.org/licenses/{LicenseExpression}"; +public uri? PackageProjectUrl => + IsNullOrWhiteSpace(PackageProjectUrlString) + ? "https://example.com/contact" + : PackageProjectUrlString; + +public uri? RepositoryUrl => + IsNullOrWhiteSpace(RepositoryUrlString) ? "about:blank" : RepositoryUrlString; +public uri? TermsOfServiceUrl => + IsNullOrWhiteSpace(TermsOfServiceUrlString) + ? "https://example.com/terms" + : TermsOfServiceUrlString; + +public string? RepositoryUrlString => + Project?.GetRuntimeField(nameof(RepositoryUrl))?.GetValue(null) as string; +public string? PackageProjectUrlString => + Project?.GetRuntimeField(nameof(PackageProjectUrl))?.GetValue(null) as string; +public string? TermsOfServiceUrlString => + Project?.GetRuntimeField(nameof(TermsOfServiceUrl))?.GetValue(null) as string; +public string? ApiVersion => + "v" + + ( + IsNullOrEmpty(PackageVersion) + ? IsNullOrEmpty(FileVersion) + ? IsNullOrEmpty(Version) + ? IsNullOrEmpty(AssemblyVersion) + ? "0.0.1-Preview" + : AssemblyVersion + : Version + : FileVersion + : PackageVersion + ); } diff --git a/src/Swagger/SwaggerExtensions/UseSwaggerUI.cs b/src/Swagger/SwaggerExtensions/UseSwaggerUI.cs index 9d512022..bb84610f 100644 --- a/src/Swagger/SwaggerExtensions/UseSwaggerUI.cs +++ b/src/Swagger/SwaggerExtensions/UseSwaggerUI.cs @@ -52,67 +52,67 @@ private static string GetManifestResourceString(string resourceName) => } .swagger-ui*, .swagger-ui :after, .swagger-ui :before { - box-sizing: inherit - } - - etc.etc.etc... + box-sizing: inherit +} + +etc.etc.etc... """"; - static readonly OpenApiString _exampleCssOpenApiString = new(ExampleCss); - - public static WebApplication UseCustomizedSwaggerUI( - this WebApplication app, - type tThisAssemblyProject, - string? indexDocumentAssemblyResourceName = "swaggerui.index.html", - string? swaggerTheme = null - ) - { - var thisAssemblyProject = new TThisAssemblyStaticProxy(tThisAssemblyProject); - var logger = app.Logger; - var swaggerPath = $"/swagger/{thisAssemblyProject.ApiVersion}/swagger.json"; - app.UseSwagger(options => - { - options.RouteTemplate = swaggerPath; - }); - app.UseSwaggerUI(options => - { - options.RoutePrefix = string.Empty; - options.DefaultModelExpandDepth(0); - options.DefaultModelRendering(ModelRendering.Model); - options.DocExpansion(DocExpansion.List); - options.DisplayOperationId(); - options.DisplayRequestDuration(); - options.EnableDeepLinking(); - options.EnableFilter(); - options.EnableTryItOutByDefault(); - options.EnablePersistAuthorization(); - options.ShowExtensions(); - options.ShowCommonExtensions(); - options.EnableValidator(); - options.SupportedSubmitMethods( - SubmitMethod.Get, - SubmitMethod.Post, - SubmitMethod.Put, - SubmitMethod.Delete, - SubmitMethod.Patch, - SubmitMethod.Head, - SubmitMethod.Options, - SubmitMethod.Trace - ); - - options.SwaggerEndpoint( - swaggerPath, - $"{thisAssemblyProject.Title} {thisAssemblyProject.ApiVersion}" - ); - }); - - app.MapGet( - "swagger-ui/SwaggerUI.custom.css", - () => new CssResult(CustomCss(swaggerTheme + ".css")) - ) - .WithGroupName("Style") - .WithTags("Style", "UI") - .Produces(Status200OK, Text.Css.DisplayName) + static readonly OpenApiString _exampleCssOpenApiString = new(ExampleCss); + +public static WebApplication UseCustomizedSwaggerUI( + this WebApplication app, + type tThisAssemblyProject, + string? indexDocumentAssemblyResourceName = "swaggerui.index.html", + string? swaggerTheme = null +) +{ + var thisAssemblyProject = new TThisAssemblyStaticProxy(tThisAssemblyProject); + var logger = app.Logger; + var swaggerPath = $"/swagger/{thisAssemblyProject.ApiVersion}/swagger.json"; + app.UseSwagger(options => + { + options.RouteTemplate = swaggerPath; + }); + app.UseSwaggerUI(options => + { + options.RoutePrefix = string.Empty; + options.DefaultModelExpandDepth(0); + options.DefaultModelRendering(ModelRendering.Model); + options.DocExpansion(DocExpansion.List); + options.DisplayOperationId(); + options.DisplayRequestDuration(); + options.EnableDeepLinking(); + options.EnableFilter(); + options.EnableTryItOutByDefault(); + options.EnablePersistAuthorization(); + options.ShowExtensions(); + options.ShowCommonExtensions(); + options.EnableValidator(); + options.SupportedSubmitMethods( + SubmitMethod.Get, + SubmitMethod.Post, + SubmitMethod.Put, + SubmitMethod.Delete, + SubmitMethod.Patch, + SubmitMethod.Head, + SubmitMethod.Options, + SubmitMethod.Trace + ); + + options.SwaggerEndpoint( + swaggerPath, + $"{thisAssemblyProject.Title} {thisAssemblyProject.ApiVersion}" + ); + }); + + app.MapGet( + "swagger-ui/SwaggerUI.custom.css", + () => new CssResult(CustomCss(swaggerTheme + ".css")) + ) + .WithGroupName("Style") + .WithTags("Style", "UI") + .Produces(Status200OK, Text.Css.DisplayName) #if NET8_0_OR_GREATER .WithOpenApi(op => { @@ -136,20 +136,20 @@ public static WebApplication UseCustomizedSwaggerUI( }) .WithSummary("Custom CSS for Swagger UI") #endif - ; - - foreach ( - var cssFile in Assembly - .GetManifestResourceNames() - .Where(x => x.EndsWith(".css")) - .Distinct() - ) - { - app.MapGet($"/swagger-ui/{cssFile}", () => new CssResult(CustomCss(cssFile))) - .WithName($"/swagger-ui/{cssFile}") - .WithGroupName("Style") - .WithTags("style", "ui") - .Produces(Status200OK, Text.Css.DisplayName) + ; + + foreach ( + var cssFile in Assembly + .GetManifestResourceNames() + .Where(x => x.EndsWith(".css")) + .Distinct() + ) + { + app.MapGet($"/swagger-ui/{cssFile}", () => new CssResult(CustomCss(cssFile))) + .WithName($"/swagger-ui/{cssFile}") + .WithGroupName("Style") + .WithTags("style", "ui") + .Produces(Status200OK, Text.Css.DisplayName) #if NET8_0_OR_GREATER .WithOpenApi(op => { @@ -173,82 +173,85 @@ var cssFile in Assembly return op; }) #endif - ; - } - - logger.LogApiVersion(thisAssemblyProject.ApiVersion); - - app.MapGet( - swaggerPath, - ctx => - { - ctx.Response.Redirect($"/swagger/v1/swagger.json"); - return Task.CompletedTask; - } - ); - - // app.UseRewriter(new RewriteOptions().AddRedirect("^$", "swagger-ui/SwaggerUI.custom.css")) - // .UseRewriter(new RewriteOptions().AddRedirect("swagger-ui.css", "swagger-ui")); - - app.UseReDoc(opts => - { - opts.DocumentTitle = thisAssemblyProject.Title; - opts.SpecUrl = swaggerPath; - opts.OnlyRequiredInSamples(); - opts.HeadContent += $$$"""" - - {{{opts.HeadContent}}} - - """"; - }); - - return app; - } - - [LoggerMessage( - EventId = 0, - Level = LogLevel.Warning, - Message = "Cannot load index document from assembly resource '{indexDocumentAssemblyResourceName}'." - )] - internal static partial void CannotLoadIndexDocument( - this ILogger logger, - string indexDocumentAssemblyResourceName - ); - - [LoggerMessage( - EventId = 0, - Level = LogLevel.Warning, - Message = "thisAssemblyProject.Version: {apiVersion}'." - )] - internal static partial void LogApiVersion(this ILogger logger, string apiVersion); - - public static WebApplication UseCustomizedSwaggerUI( - this WebApplication app, - type tThisAssemblyProject, - string indexDocumentAssemblyResourceName = "SwaggerUI.index.html", - string? swaggerTheme = null - ) => - app.UseCustomizedSwaggerUI( - tThisAssemblyProject, - indexDocumentAssemblyResourceName, - swaggerTheme - ); - - internal sealed class CssResult(string css) : IResult + ; + } + + logger.LogApiVersion(thisAssemblyProject.ApiVersion); + + app.MapGet( + swaggerPath, + ctx => + { + ctx.Response.Redirect($"/swagger/v1/swagger.json"); + return Task.CompletedTask; + } + ); + + // app.UseRewriter(new RewriteOptions().AddRedirect("^$", "swagger-ui/SwaggerUI.custom.css")) + // .UseRewriter(new RewriteOptions().AddRedirect("swagger-ui.css", "swagger-ui")); + + app.UseReDoc(opts => + { + opts.DocumentTitle = thisAssemblyProject.Title; + opts.SpecUrl = swaggerPath; + opts.OnlyRequiredInSamples(); + opts.HeadContent += $$$"""" + < script type = "application/javascript" src = "https://cdn.jsdelivr.net/npm/redoc-try-it-out/dist/try-it-out.min.js" > + + { { { opts.HeadContent} } } + + < script > + var redoc_container = document.createElement("div"); + document.body.appendChild(redoc_container); + RedocTryItOut.init( + "{{{opts.SpecUrl}}}", + + { title: ""{ { { thisAssemblyProject.Title} } } "" }, + redoc_container + + """"; + }); + + return app; +} + +[LoggerMessage( + EventId = 0, + Level = LogLevel.Warning, + Message = "Cannot load index document from assembly resource '{indexDocumentAssemblyResourceName}'." +)] +internal static partial void CannotLoadIndexDocument( + this ILogger logger, + string indexDocumentAssemblyResourceName +); + +[LoggerMessage( + EventId = 0, + Level = LogLevel.Warning, + Message = "thisAssemblyProject.Version: {apiVersion}'." +)] +internal static partial void LogApiVersion(this ILogger logger, string apiVersion); + +public static WebApplication UseCustomizedSwaggerUI( + this WebApplication app, + type tThisAssemblyProject, + string indexDocumentAssemblyResourceName = "SwaggerUI.index.html", + string? swaggerTheme = null +) => + app.UseCustomizedSwaggerUI( + tThisAssemblyProject, + indexDocumentAssemblyResourceName, + swaggerTheme + ); + +internal sealed class CssResult(string css) : IResult { - private readonly string _css = css; - - public Task ExecuteAsync(HttpContext httpContext) - { - httpContext.Response.ContentType = Text.Css.DisplayName; - return httpContext.Response.WriteAsync(_css); - } - } + private readonly string _css = css; + +public Task ExecuteAsync(HttpContext httpContext) +{ + httpContext.Response.ContentType = Text.Css.DisplayName; + return httpContext.Response.WriteAsync(_css); +} +} } diff --git a/src/Swagger/swaggerui/Controllers/SwaggerUIController.cs b/src/Swagger/swaggerui/Controllers/SwaggerUIController.cs index 1168e088..cc45d7cb 100644 --- a/src/Swagger/swaggerui/Controllers/SwaggerUIController.cs +++ b/src/Swagger/swaggerui/Controllers/SwaggerUIController.cs @@ -15,164 +15,164 @@ namespace Dgmjr.AspNetCore.Swagger.UI; [Route("/swagger-ui/{documentName}")] public partial class SwaggerUIController(ILogger logger, HttpClient httpClient, IDistributedCache cache, IOptionsMonitor options, IOptionsMonitor swaggerOptions, IOptionsMonitor swaggerGenOptions) : ControllerBase, ILog { - private const string Swagger_UI = "Swagger UI"; - private string SwaggerUIIndex_Html => $"swaggerui.{Options.SwaggerUI}.index.html"; - private string SwaggerUIInit_js => $"swaggerui.{Options.SwaggerUI}.swagger-ui-init.js"; - private string SwaggerUI => $"swaggerui.{Options.SwaggerUI}"; - private string BaseUrl => Options.ReverseProxyEndpoint; - public ILogger Logger => logger; - private SwaggerUIOptions Options => options.CurrentValue; - private SwaggerOptions SwaggerOptions => swaggerOptions.CurrentValue; - private SwaggerGenOptions SwaggerGenOptions => swaggerGenOptions.CurrentValue; - - private readonly Jso _jso = new () { WriteIndented = false, DefaultIgnoreCondition = JIgnore.Never, IgnoreReadOnlyProperties = true, PropertyNamingPolicy = JNaming.CamelCase, DictionaryKeyPolicy = JNaming.CamelCase }; - private ConfigObject ConfigObject => Options.ConfigObject; - private OAuthConfigObject OAuthConfigObject => Options.OAuthConfigObject; - private InterceptorFunctions Interceptors => Options.Interceptors; - private string ConfigObjectJson => Serialize(ConfigObject, _jso).Replace("\n", " ").Replace("\r", " "); - private string OAuthConfigObjectJson => Serialize(OAuthConfigObject, _jso).Replace("\n", " ").Replace("\r", " "); - private string InterceptorsJson => Serialize(Interceptors, _jso).Replace("\n", " ").Replace("\r", " "); - - private readonly IDistributedCache _cache = cache; - private readonly HttpClient _httpClient = httpClient; - - [HttpGet("{**swaggerUiFileName}")] - [SwaggerOperation(Summary = "Swagger UI Documents", Description = "Retrieves Swagger UI Documents", Tags = [ Swagger_UI ], OperationId = "SwaggerUIDocuments")] - [Produces(Text.Html.DisplayName, Application.Json.DisplayName, Application.JavaScript.DisplayName, Application.Xml.DisplayName, Application.Zip.DisplayName, Application.Pdf.DisplayName, Application.Rtf.DisplayName)] - public async Task GetSwaggerUIFileAsync(string swaggerUiFileName) - { - if(Options.UseReverseProxy) - { - var cacheKey = $"{SwaggerUI}-{swaggerUiFileName}"; - var document = Options.UseCache ? await _cache.GetAsync(cacheKey) : null; - var extension = Path.GetExtension(swaggerUiFileName); - if (document is not null) - { - Logger.LogCacheHit(cacheKey); - return File(document, MimeTypes.GetMimeType(extension)); - } - Logger.LogCacheMiss(cacheKey); - Logger.LogTrace($"Swagger UI file request URI: {BaseUrl}{swaggerUiFileName}"); - var content = await _httpClient.GetByteArrayAsync($"{BaseUrl}{swaggerUiFileName}"); - await _cache.SetAsync(cacheKey, content); - return File(content, MimeTypes.GetMimeType(extension)); - } - else - { - var document = await GetType().Assembly.GetManifestResourceStream(SwaggerUIIndex_Html).ReadAllBytesAsync(); - var extension = Path.GetExtension(swaggerUiFileName); - return File(document, MimeTypes.GetMimeType(extension), fileDownloadName: swaggerUiFileName); - } - } - - [HttpGet("index.html")] - [SwaggerOperation(Summary = "Swagger UI index.html", Description = "Retrieves Swagger UI index.html", Tags = [ Swagger_UI ], OperationId = "SwaggerUIIndexHtml")] - [Produces(Text.Html.DisplayName)] - public async Task GetSwaggerUIIndexAsync() - { - if(Options.UseReverseProxy && Options.SwaggerUI is not UI.SwaggerUI.Bootstrap) - { - return await GetSwaggerUIFileAsync("index.html"); - } - - var cacheKey = SwaggerUIIndex_Html; - var document = Options.UseCache ? await _cache.GetAsync(cacheKey) : null; - if (document is not null) - { - Logger.LogCacheHit(cacheKey); - return File(document, Text.Html.DisplayName); - } - Logger.LogCacheMiss(cacheKey); - var content = await GetType().Assembly.GetManifestResourceStream(SwaggerUIIndex_Html).ReadAllBytesAsync(); - await _cache.SetAsync(cacheKey, content); - return File(content, Text.Html.DisplayName); - } - - [HttpGet("assets/swagger-ui.js")] - [SwaggerOperation(Summary = "Swagger UI swagger-ui.js", Description = "Retrieves Swagger UI swagger-ui.js", Tags = [ Swagger_UI ], OperationId = "SwaggerUISwaggerUIJs")] - [Produces(Application.JavaScript.DisplayName)] - public async Task GetSwaggerUIJsAsync() - { - if(Options.UseReverseProxy && Options.SwaggerUI is not UI.SwaggerUI.Bootstrap) - { - return await GetSwaggerUIFileAsync("swagger-ui.js"); - } - - var cacheKey = $"{SwaggerUI}-swagger-ui.js"; - var contentBytes = Options.UseCache ? await _cache.GetAsync(cacheKey) : null; - if (contentBytes is not null) - { - Logger.LogCacheHit(cacheKey); - } - else - { - Logger.LogCacheMiss(cacheKey); - contentBytes = await _httpClient.GetByteArrayAsync($"{BaseUrl}/assets/swagger-ui.js"); - } - var stringContent = contentBytes.ToUTF8String(); - - stringContent = stringContent.Replace("definitions", "schemas"); - contentBytes = stringContent.ToUTF8Bytes(); - - await _cache.SetAsync(cacheKey, contentBytes); - return File(contentBytes, Text.Html.DisplayName); - } - - [HttpGet("swagger-ui-init.js")] - [SwaggerOperation(Summary = "Swagger UI Init", Description = "Swagger UI Init", Tags = [ Swagger_UI ], OperationId = "SwaggerUIInit")] - [Produces(Application.JavaScript.DisplayName)] - public async Task GetSwaggerUIIInitJsAsync() - { - var cacheKey = $"{SwaggerUI}-swagger-ui-init.js"; - var document = Options.UseCache ? await _cache.GetAsync(cacheKey) : null; - if (document is not null) - { - Logger.LogCacheHit(cacheKey); - return File(document, Text.Html.DisplayName); - } - Logger.LogCacheMiss(cacheKey); - var content = await GetType().Assembly.ReadAssemblyResourceAllTextAsync(SwaggerUIInit_js); - content = content.Replace("${ConfigObject}", ConfigObjectJson); - content = content.Replace("${OAuthConfigObject}", OAuthConfigObjectJson); - content = content.Replace("${Interceptors}", InterceptorsJson); - var contentBytes = content.ToUTF8Bytes(); - await _cache.SetAsync(cacheKey, contentBytes); - return File(contentBytes, Text.Html.DisplayName); - } - - [HttpGet("swagger.json")] - public async Task GetSwaggerJsonAsync([FromRoute] string documentName) - { - var cacheKey = $"{SwaggerUI}-{documentName}"; - var documentBytes = Options.UseCache ? await _cache.GetAsync(cacheKey) : null; - if (documentBytes is not null) - { - Logger.LogCacheHit(cacheKey); - } - else - { - Logger.LogCacheMiss(cacheKey); - var baseServerUri = GetBaseServerUri(); - var requestUri = $"{baseServerUri}/{SwaggerOptions.RouteTemplate.Replace("{documentName}", documentName)}"; - Logger.LogTrace($"Swagger document request URI: {requestUri}"); - documentBytes = await _httpClient.GetByteArrayAsync(requestUri); - await _cache.SetAsync(cacheKey, documentBytes); - } - return File(documentBytes, ApplicationMediaTypeNames.OpenApiJson); - } - - private string GetBaseServerUri() - { - var request = HttpContext.Request; - var scheme = request.Scheme; - var host = request.Host; - // var pathBase = request.PathBase; - var port = request.Host.Port; - var baseUri = $"{scheme}://{host}"; - Logger.LogTrace($"scheme: {scheme}"); - Logger.LogTrace($"host: {host}"); - Logger.LogTrace($"port: {port}"); - Logger.LogTrace($"baseUri: {baseUri}"); - return baseUri; - } + private const string Swagger_UI = "Swagger UI"; +private string SwaggerUIIndex_Html => $"swaggerui.{Options.SwaggerUI}.index.html"; +private string SwaggerUIInit_js => $"swaggerui.{Options.SwaggerUI}.swagger-ui-init.js"; +private string SwaggerUI => $"swaggerui.{Options.SwaggerUI}"; +private string BaseUrl => Options.ReverseProxyEndpoint; +public ILogger Logger => logger; +private SwaggerUIOptions Options => options.CurrentValue; +private SwaggerOptions SwaggerOptions => swaggerOptions.CurrentValue; +private SwaggerGenOptions SwaggerGenOptions => swaggerGenOptions.CurrentValue; + +private readonly Jso _jso = new() { WriteIndented = false, DefaultIgnoreCondition = JIgnore.Never, IgnoreReadOnlyProperties = true, PropertyNamingPolicy = JNaming.CamelCase, DictionaryKeyPolicy = JNaming.CamelCase }; +private ConfigObject ConfigObject => Options.ConfigObject; +private OAuthConfigObject OAuthConfigObject => Options.OAuthConfigObject; +private InterceptorFunctions Interceptors => Options.Interceptors; +private string ConfigObjectJson => Serialize(ConfigObject, _jso).Replace("\n", " ").Replace("\r", " "); +private string OAuthConfigObjectJson => Serialize(OAuthConfigObject, _jso).Replace("\n", " ").Replace("\r", " "); +private string InterceptorsJson => Serialize(Interceptors, _jso).Replace("\n", " ").Replace("\r", " "); + +private readonly IDistributedCache _cache = cache; +private readonly HttpClient _httpClient = httpClient; + +[HttpGet("{**swaggerUiFileName}")] +[SwaggerOperation(Summary = "Swagger UI Documents", Description = "Retrieves Swagger UI Documents", Tags = [Swagger_UI], OperationId = "SwaggerUIDocuments")] +[Produces(Text.Html.DisplayName, Application.Json.DisplayName, Application.JavaScript.DisplayName, Application.Xml.DisplayName, Application.Zip.DisplayName, Application.Pdf.DisplayName, Application.Rtf.DisplayName)] +public async Task GetSwaggerUIFileAsync(string swaggerUiFileName) +{ + if (Options.UseReverseProxy) + { + var cacheKey = $"{SwaggerUI}-{swaggerUiFileName}"; + var document = Options.UseCache ? await _cache.GetAsync(cacheKey) : null; + var extension = Path.GetExtension(swaggerUiFileName); + if (document is not null) + { + Logger.LogCacheHit(cacheKey); + return File(document, MimeTypes.GetMimeType(extension)); + } + Logger.LogCacheMiss(cacheKey); + Logger.LogTrace($"Swagger UI file request URI: {BaseUrl}{swaggerUiFileName}"); + var content = await _httpClient.GetByteArrayAsync($"{BaseUrl}{swaggerUiFileName}"); + await _cache.SetAsync(cacheKey, content); + return File(content, MimeTypes.GetMimeType(extension)); + } + else + { + var document = await GetType().Assembly.GetManifestResourceStream(SwaggerUIIndex_Html).ReadAllBytesAsync(); + var extension = Path.GetExtension(swaggerUiFileName); + return File(document, MimeTypes.GetMimeType(extension), fileDownloadName: swaggerUiFileName); + } +} + +[HttpGet("index.html")] +[SwaggerOperation(Summary = "Swagger UI index.html", Description = "Retrieves Swagger UI index.html", Tags = [Swagger_UI], OperationId = "SwaggerUIIndexHtml")] +[Produces(Text.Html.DisplayName)] +public async Task GetSwaggerUIIndexAsync() +{ + if (Options.UseReverseProxy && Options.SwaggerUI is not UI.SwaggerUI.Bootstrap) + { + return await GetSwaggerUIFileAsync("index.html"); + } + + var cacheKey = SwaggerUIIndex_Html; + var document = Options.UseCache ? await _cache.GetAsync(cacheKey) : null; + if (document is not null) + { + Logger.LogCacheHit(cacheKey); + return File(document, Text.Html.DisplayName); + } + Logger.LogCacheMiss(cacheKey); + var content = await GetType().Assembly.GetManifestResourceStream(SwaggerUIIndex_Html).ReadAllBytesAsync(); + await _cache.SetAsync(cacheKey, content); + return File(content, Text.Html.DisplayName); +} + +[HttpGet("assets/swagger-ui.js")] +[SwaggerOperation(Summary = "Swagger UI swagger-ui.js", Description = "Retrieves Swagger UI swagger-ui.js", Tags = [Swagger_UI], OperationId = "SwaggerUISwaggerUIJs")] +[Produces(Application.JavaScript.DisplayName)] +public async Task GetSwaggerUIJsAsync() +{ + if (Options.UseReverseProxy && Options.SwaggerUI is not UI.SwaggerUI.Bootstrap) + { + return await GetSwaggerUIFileAsync("swagger-ui.js"); + } + + var cacheKey = $"{SwaggerUI}-swagger-ui.js"; + var contentBytes = Options.UseCache ? await _cache.GetAsync(cacheKey) : null; + if (contentBytes is not null) + { + Logger.LogCacheHit(cacheKey); + } + else + { + Logger.LogCacheMiss(cacheKey); + contentBytes = await _httpClient.GetByteArrayAsync($"{BaseUrl}/assets/swagger-ui.js"); + } + var stringContent = contentBytes.ToUTF8String(); + + stringContent = stringContent.Replace("definitions", "schemas"); + contentBytes = stringContent.ToUTF8Bytes(); + + await _cache.SetAsync(cacheKey, contentBytes); + return File(contentBytes, Text.Html.DisplayName); +} + +[HttpGet("swagger-ui-init.js")] +[SwaggerOperation(Summary = "Swagger UI Init", Description = "Swagger UI Init", Tags = [Swagger_UI], OperationId = "SwaggerUIInit")] +[Produces(Application.JavaScript.DisplayName)] +public async Task GetSwaggerUIIInitJsAsync() +{ + var cacheKey = $"{SwaggerUI}-swagger-ui-init.js"; + var document = Options.UseCache ? await _cache.GetAsync(cacheKey) : null; + if (document is not null) + { + Logger.LogCacheHit(cacheKey); + return File(document, Text.Html.DisplayName); + } + Logger.LogCacheMiss(cacheKey); + var content = await GetType().Assembly.ReadAssemblyResourceAllTextAsync(SwaggerUIInit_js); + content = content.Replace("${ConfigObject}", ConfigObjectJson); + content = content.Replace("${OAuthConfigObject}", OAuthConfigObjectJson); + content = content.Replace("${Interceptors}", InterceptorsJson); + var contentBytes = content.ToUTF8Bytes(); + await _cache.SetAsync(cacheKey, contentBytes); + return File(contentBytes, Text.Html.DisplayName); +} + +[HttpGet("swagger.json")] +public async Task GetSwaggerJsonAsync([FromRoute] string documentName) +{ + var cacheKey = $"{SwaggerUI}-{documentName}"; + var documentBytes = Options.UseCache ? await _cache.GetAsync(cacheKey) : null; + if (documentBytes is not null) + { + Logger.LogCacheHit(cacheKey); + } + else + { + Logger.LogCacheMiss(cacheKey); + var baseServerUri = GetBaseServerUri(); + var requestUri = $"{baseServerUri}/{SwaggerOptions.RouteTemplate.Replace("{documentName}", documentName)}"; + Logger.LogTrace($"Swagger document request URI: {requestUri}"); + documentBytes = await _httpClient.GetByteArrayAsync(requestUri); + await _cache.SetAsync(cacheKey, documentBytes); + } + return File(documentBytes, ApplicationMediaTypeNames.OpenApiJson); +} + +private string GetBaseServerUri() +{ + var request = HttpContext.Request; + var scheme = request.Scheme; + var host = request.Host; + // var pathBase = request.PathBase; + var port = request.Host.Port; + var baseUri = $"{scheme}://{host}"; + Logger.LogTrace($"scheme: {scheme}"); + Logger.LogTrace($"host: {host}"); + Logger.LogTrace($"port: {port}"); + Logger.LogTrace($"baseUri: {baseUri}"); + return baseUri; +} }