diff --git a/src/Api/SecretsManager/Models/Response/BaseSecretResponseModel.cs b/src/Api/SecretsManager/Models/Response/BaseSecretResponseModel.cs index 59ba5ac72..622499016 100644 --- a/src/Api/SecretsManager/Models/Response/BaseSecretResponseModel.cs +++ b/src/Api/SecretsManager/Models/Response/BaseSecretResponseModel.cs @@ -1,6 +1,5 @@ #nullable enable -using System.ComponentModel.DataAnnotations; using Bit.Core.Models.Api; using Bit.Core.SecretsManager.Entities; @@ -27,10 +26,8 @@ public class BaseSecretResponseModel : ResponseModel Projects = secret.Projects?.Select(p => new SecretResponseInnerProject(p)); } - [Required] public Guid Id { get; } - [Required] public Guid OrganizationId { get; } public string? Key { get; } @@ -39,10 +36,8 @@ public class BaseSecretResponseModel : ResponseModel public string? Note { get; } - [Required] public DateTime CreationDate { get; } - [Required] public DateTime RevisionDate { get; } public IEnumerable? Projects { get; init; } diff --git a/src/Api/Utilities/ServiceCollectionExtensions.cs b/src/Api/Utilities/ServiceCollectionExtensions.cs index 310a77589..150f9c6d0 100644 --- a/src/Api/Utilities/ServiceCollectionExtensions.cs +++ b/src/Api/Utilities/ServiceCollectionExtensions.cs @@ -71,6 +71,7 @@ public static class ServiceCollectionExtensions // config.UseReferencedDefinitionsForEnums(); config.SchemaFilter(); + config.SchemaFilter(); var apiFilePath = Path.Combine(AppContext.BaseDirectory, "Api.xml"); config.IncludeXmlComments(apiFilePath, true); diff --git a/src/SharedWeb/Swagger/RequireNotNullableSchemaFilter.cs b/src/SharedWeb/Swagger/RequireNotNullableSchemaFilter.cs new file mode 100644 index 000000000..93de13c17 --- /dev/null +++ b/src/SharedWeb/Swagger/RequireNotNullableSchemaFilter.cs @@ -0,0 +1,66 @@ +using System.Reflection; +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace Bit.SharedWeb.Swagger; + +/// +/// +/// +/// +/// Credits: https://github.com/domaindrivendev/Swashbuckle.AspNetCore/issues/2036#issuecomment-894015122 +/// +public class RequireNotNullableSchemaFilter : ISchemaFilter +{ + public void Apply(OpenApiSchema schema, SchemaFilterContext context) + { + if (schema.Properties == null) + { + return; + } + + FixNullableProperties(schema, context); + + var notNullableProperties = schema + .Properties + .Where(x => !x.Value.Nullable && x.Value.Default == default && !schema.Required.Contains(x.Key)) + .ToList(); + + foreach (var property in notNullableProperties) + { + schema.Required.Add(property.Key); + } + } + + /// + /// Option "SupportNonNullableReferenceTypes" not working with complex types ({ "type": "object" }), + /// so they always have "Nullable = false", + /// see method "SchemaGenerator.GenerateSchemaForMember" + /// + private static void FixNullableProperties(OpenApiSchema schema, SchemaFilterContext context) + { + foreach (var property in schema.Properties) + { + var field = context.Type + .GetMembers(BindingFlags.Public | BindingFlags.Instance) + .FirstOrDefault(x => + string.Equals(x.Name, property.Key, StringComparison.InvariantCultureIgnoreCase)); + + if (field == null) + { + continue; + } + + var fieldType = field switch + { + FieldInfo fieldInfo => fieldInfo.FieldType, + PropertyInfo propertyInfo => propertyInfo.PropertyType, + _ => throw new NotSupportedException(), + }; + + property.Value.Nullable = fieldType.IsValueType + ? Nullable.GetUnderlyingType(fieldType) != null + : !field.IsNonNullableReferenceType(); + } + } +}