diff --git a/Microsoft.OpenApi.sln b/Microsoft.OpenApi.sln index a8c85bdb4..c4745c45c 100644 --- a/Microsoft.OpenApi.sln +++ b/Microsoft.OpenApi.sln @@ -15,9 +15,9 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.OpenApi.Workbench EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.OpenApi.Readers", "src\Microsoft.OpenApi.Readers\Microsoft.OpenApi.Readers.csproj", "{79933258-0126-4382-8755-D50820ECC483}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.OpenApi.Tests", "test\Microsoft.OpenApi.Tests\Microsoft.OpenApi.Tests.csproj", "{AD83F991-DBF3-4251-8613-9CC54C826964}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.OpenApi.Tests", "test\Microsoft.OpenApi.Tests\Microsoft.OpenApi.Tests.csproj", "{AD83F991-DBF3-4251-8613-9CC54C826964}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.OpenApi.Readers.Tests", "test\Microsoft.OpenApi.Readers.Tests\Microsoft.OpenApi.Readers.Tests.csproj", "{1ED3C2C1-E1E7-4925-B4E6-2D969C3F5237}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.OpenApi.Readers.Tests", "test\Microsoft.OpenApi.Readers.Tests\Microsoft.OpenApi.Readers.Tests.csproj", "{1ED3C2C1-E1E7-4925-B4E6-2D969C3F5237}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E546B92F-20A8-49C3-8323-4B25BB78F3E1}" EndProject diff --git a/src/Microsoft.OpenApi/Models/OpenApiCallback.cs b/src/Microsoft.OpenApi/Models/OpenApiCallback.cs index a80282c28..14489c394 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiCallback.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiCallback.cs @@ -5,6 +5,8 @@ using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Expressions; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; using Microsoft.OpenApi.Writers; namespace Microsoft.OpenApi.Models @@ -12,6 +14,7 @@ namespace Microsoft.OpenApi.Models /// /// Callback Object: A map of possible out-of band callbacks related to the parent operation. /// + [OpenApiValidator(typeof(CallbackValidator))] public class OpenApiCallback : IOpenApiSerializable, IOpenApiReferenceable, IOpenApiExtensible { /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiComponents.cs b/src/Microsoft.OpenApi/Models/OpenApiComponents.cs index 2e719e1ff..7232eee78 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiComponents.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiComponents.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; using Microsoft.OpenApi.Writers; namespace Microsoft.OpenApi.Models @@ -11,6 +13,7 @@ namespace Microsoft.OpenApi.Models /// /// Components Object. /// + [OpenApiValidator(typeof(ComponentsValidator))] public class OpenApiComponents : IOpenApiSerializable, IOpenApiExtensible { /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiContact.cs b/src/Microsoft.OpenApi/Models/OpenApiContact.cs index 01fca2aa8..cb2b1aeeb 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiContact.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiContact.cs @@ -5,6 +5,8 @@ using System.Collections.Generic; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; using Microsoft.OpenApi.Writers; namespace Microsoft.OpenApi.Models @@ -12,6 +14,7 @@ namespace Microsoft.OpenApi.Models /// /// Contact Object. /// + [OpenApiValidator(typeof(ContactValidator))] public class OpenApiContact : IOpenApiSerializable, IOpenApiExtensible { /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiDiscriminator.cs b/src/Microsoft.OpenApi/Models/OpenApiDiscriminator.cs index cee77aadb..be9259121 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiDiscriminator.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiDiscriminator.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; using Microsoft.OpenApi.Writers; namespace Microsoft.OpenApi.Models @@ -10,6 +12,7 @@ namespace Microsoft.OpenApi.Models /// /// Discriminator object. /// + [OpenApiValidator(typeof(DiscriminatorValidator))] public class OpenApiDiscriminator : IOpenApiSerializable { /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs index 630f2f86d..3163cf7bd 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiDocument.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiDocument.cs @@ -6,6 +6,8 @@ using System.Linq; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; using Microsoft.OpenApi.Writers; namespace Microsoft.OpenApi.Models @@ -13,6 +15,7 @@ namespace Microsoft.OpenApi.Models /// /// Describes an Open API Document. See: https://swagger.io/specification /// + [OpenApiValidator(typeof(DocumentValidator))] public class OpenApiDocument : IOpenApiSerializable, IOpenApiExtensible { /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiEncoding.cs b/src/Microsoft.OpenApi/Models/OpenApiEncoding.cs index 716d626d5..d98c3c8f1 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiEncoding.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiEncoding.cs @@ -5,6 +5,8 @@ using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Extensions; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; using Microsoft.OpenApi.Writers; namespace Microsoft.OpenApi.Models @@ -12,6 +14,7 @@ namespace Microsoft.OpenApi.Models /// /// ExternalDocs object. /// + [OpenApiValidator(typeof(EncodingValidator))] public class OpenApiEncoding : IOpenApiSerializable, IOpenApiExtensible { /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiExample.cs b/src/Microsoft.OpenApi/Models/OpenApiExample.cs index b6baf18c7..28d1dc0e2 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiExample.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiExample.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; using Microsoft.OpenApi.Writers; namespace Microsoft.OpenApi.Models @@ -11,6 +13,7 @@ namespace Microsoft.OpenApi.Models /// /// Example Object. /// + [OpenApiValidator(typeof(ExampleValidator))] public class OpenApiExample : IOpenApiSerializable, IOpenApiReferenceable, IOpenApiExtensible { /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiExternalDocs.cs b/src/Microsoft.OpenApi/Models/OpenApiExternalDocs.cs index 8e5789bba..d49026f27 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiExternalDocs.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiExternalDocs.cs @@ -5,6 +5,8 @@ using System.Collections.Generic; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; using Microsoft.OpenApi.Writers; namespace Microsoft.OpenApi.Models @@ -12,6 +14,7 @@ namespace Microsoft.OpenApi.Models /// /// ExternalDocs object. /// + [OpenApiValidator(typeof(ExampleValidator))] public class OpenApiExternalDocs : IOpenApiSerializable, IOpenApiExtensible { /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiHeader.cs b/src/Microsoft.OpenApi/Models/OpenApiHeader.cs index d87b2f73e..43ce9bed2 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiHeader.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiHeader.cs @@ -5,6 +5,8 @@ using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Extensions; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; using Microsoft.OpenApi.Writers; namespace Microsoft.OpenApi.Models @@ -13,6 +15,7 @@ namespace Microsoft.OpenApi.Models /// Header Object. /// The Header Object follows the structure of the Parameter Object. /// + [OpenApiValidator(typeof(HeaderValidator))] public class OpenApiHeader : IOpenApiSerializable, IOpenApiReferenceable, IOpenApiExtensible { /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiInfo.cs b/src/Microsoft.OpenApi/Models/OpenApiInfo.cs index 922ae0818..8d490405f 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiInfo.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiInfo.cs @@ -5,6 +5,8 @@ using System.Collections.Generic; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; using Microsoft.OpenApi.Writers; namespace Microsoft.OpenApi.Models @@ -12,6 +14,7 @@ namespace Microsoft.OpenApi.Models /// /// Open API Info Object, it provides the metadata about the Open API. /// + [OpenApiValidator(typeof(InfoValidator))] public class OpenApiInfo : IOpenApiSerializable, IOpenApiExtensible { /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiLicense.cs b/src/Microsoft.OpenApi/Models/OpenApiLicense.cs index fc29afba9..192b48386 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiLicense.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiLicense.cs @@ -5,6 +5,8 @@ using System.Collections.Generic; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; using Microsoft.OpenApi.Writers; namespace Microsoft.OpenApi.Models @@ -12,6 +14,7 @@ namespace Microsoft.OpenApi.Models /// /// License Object. /// + [OpenApiValidator(typeof(LicenseValidator))] public class OpenApiLicense : IOpenApiSerializable, IOpenApiExtensible { /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiLink.cs b/src/Microsoft.OpenApi/Models/OpenApiLink.cs index 83241fd44..ca539f2ad 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiLink.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiLink.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; using Microsoft.OpenApi.Writers; namespace Microsoft.OpenApi.Models @@ -11,6 +13,7 @@ namespace Microsoft.OpenApi.Models /// /// Link Object. /// + [OpenApiValidator(typeof(LinkValidator))] public class OpenApiLink : IOpenApiSerializable, IOpenApiReferenceable, IOpenApiExtensible { /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiMediaType.cs b/src/Microsoft.OpenApi/Models/OpenApiMediaType.cs index 86ab12239..2f076453c 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiMediaType.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiMediaType.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; using Microsoft.OpenApi.Writers; namespace Microsoft.OpenApi.Models @@ -11,6 +13,7 @@ namespace Microsoft.OpenApi.Models /// /// Media Type Object. /// + [OpenApiValidator(typeof(MediaTypeValidator))] public class OpenApiMediaType : IOpenApiSerializable, IOpenApiExtensible { /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiOAuthFlow.cs b/src/Microsoft.OpenApi/Models/OpenApiOAuthFlow.cs index 54d500128..f873f85fe 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiOAuthFlow.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiOAuthFlow.cs @@ -5,6 +5,8 @@ using System.Collections.Generic; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; using Microsoft.OpenApi.Writers; namespace Microsoft.OpenApi.Models @@ -12,6 +14,7 @@ namespace Microsoft.OpenApi.Models /// /// OAuth Flow Object. /// + [OpenApiValidator(typeof(OAuthFlowValidator))] public class OpenApiOAuthFlow : IOpenApiSerializable, IOpenApiExtensible { /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiOAuthFlows.cs b/src/Microsoft.OpenApi/Models/OpenApiOAuthFlows.cs index 9fe47e316..bfb43188e 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiOAuthFlows.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiOAuthFlows.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; using Microsoft.OpenApi.Writers; namespace Microsoft.OpenApi.Models @@ -11,6 +13,7 @@ namespace Microsoft.OpenApi.Models /// /// OAuth Flows Object. /// + [OpenApiValidator(typeof(OAuthFlowsValidator))] public class OpenApiOAuthFlows : IOpenApiSerializable, IOpenApiExtensible { /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiOperation.cs b/src/Microsoft.OpenApi/Models/OpenApiOperation.cs index 0548a447c..7205bfb52 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiOperation.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiOperation.cs @@ -6,6 +6,8 @@ using System.Linq; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; using Microsoft.OpenApi.Writers; namespace Microsoft.OpenApi.Models @@ -13,6 +15,7 @@ namespace Microsoft.OpenApi.Models /// /// Operation Object. /// + [OpenApiValidator(typeof(OperationValidator))] public class OpenApiOperation : IOpenApiSerializable, IOpenApiExtensible { /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs b/src/Microsoft.OpenApi/Models/OpenApiParameter.cs index 45272a60c..d3c467c58 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiParameter.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiParameter.cs @@ -5,6 +5,8 @@ using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Extensions; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; using Microsoft.OpenApi.Writers; namespace Microsoft.OpenApi.Models @@ -12,6 +14,7 @@ namespace Microsoft.OpenApi.Models /// /// Parameter Object. /// + [OpenApiValidator(typeof(ParameterValidator))] public class OpenApiParameter : IOpenApiSerializable, IOpenApiReferenceable, IOpenApiExtensible { /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiPathItem.cs b/src/Microsoft.OpenApi/Models/OpenApiPathItem.cs index 257d7db30..e5c42c255 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiPathItem.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiPathItem.cs @@ -5,6 +5,8 @@ using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Extensions; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; using Microsoft.OpenApi.Writers; namespace Microsoft.OpenApi.Models @@ -12,6 +14,7 @@ namespace Microsoft.OpenApi.Models /// /// Path Item Object: to describe the operations available on a single path. /// + [OpenApiValidator(typeof(PathItemValidator))] public class OpenApiPathItem : IOpenApiSerializable, IOpenApiExtensible { /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiPaths.cs b/src/Microsoft.OpenApi/Models/OpenApiPaths.cs index c998a101a..66405349e 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiPaths.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiPaths.cs @@ -1,11 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; + namespace Microsoft.OpenApi.Models { /// /// Paths object. /// + [OpenApiValidator(typeof(PathsValidator))] public class OpenApiPaths : OpenApiExtensibleDictionary { } diff --git a/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs b/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs index ab1b0c20e..b4e84e524 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiRequestBody.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; using Microsoft.OpenApi.Writers; namespace Microsoft.OpenApi.Models @@ -11,6 +13,7 @@ namespace Microsoft.OpenApi.Models /// /// Request Body Object /// + [OpenApiValidator(typeof(RequestBodyValidator))] public class OpenApiRequestBody : IOpenApiSerializable, IOpenApiReferenceable, IOpenApiExtensible { /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiResponse.cs b/src/Microsoft.OpenApi/Models/OpenApiResponse.cs index 591317554..709317970 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiResponse.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiResponse.cs @@ -5,6 +5,8 @@ using System.Linq; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; using Microsoft.OpenApi.Writers; namespace Microsoft.OpenApi.Models @@ -12,6 +14,7 @@ namespace Microsoft.OpenApi.Models /// /// Response object. /// + [OpenApiValidator(typeof(ResponseValidator))] public class OpenApiResponse : IOpenApiSerializable, IOpenApiReferenceable, IOpenApiExtensible { /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiResponses.cs b/src/Microsoft.OpenApi/Models/OpenApiResponses.cs index 0244d99cf..084f9bbd5 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiResponses.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiResponses.cs @@ -1,11 +1,15 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; + namespace Microsoft.OpenApi.Models { /// /// Responses object. /// + [OpenApiValidator(typeof(ResponsesValidator))] public class OpenApiResponses : OpenApiExtensibleDictionary { } diff --git a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs index af586e650..2ea4396c6 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiSchema.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiSchema.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; using Microsoft.OpenApi.Writers; namespace Microsoft.OpenApi.Models @@ -11,6 +13,7 @@ namespace Microsoft.OpenApi.Models /// /// Schema Object. /// + [OpenApiValidator(typeof(SchemaValidator))] public class OpenApiSchema : IOpenApiSerializable, IOpenApiReferenceable, IOpenApiExtensible { /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiSecurityRequirement.cs b/src/Microsoft.OpenApi/Models/OpenApiSecurityRequirement.cs index a94797b6f..eb8a41a21 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiSecurityRequirement.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiSecurityRequirement.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; using Microsoft.OpenApi.Writers; namespace Microsoft.OpenApi.Models @@ -15,6 +17,7 @@ namespace Microsoft.OpenApi.Models /// then the value is a list of scope names required for the execution. /// For other security scheme types, the array MUST be empty. /// + [OpenApiValidator(typeof(SecurityRequirementValidator))] public class OpenApiSecurityRequirement : Dictionary>, IOpenApiSerializable { diff --git a/src/Microsoft.OpenApi/Models/OpenApiSecurityScheme.cs b/src/Microsoft.OpenApi/Models/OpenApiSecurityScheme.cs index b9ba59c4d..950421901 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiSecurityScheme.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiSecurityScheme.cs @@ -6,6 +6,8 @@ using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Extensions; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; using Microsoft.OpenApi.Writers; namespace Microsoft.OpenApi.Models @@ -13,6 +15,7 @@ namespace Microsoft.OpenApi.Models /// /// Security Scheme Object. /// + [OpenApiValidator(typeof(SecuritySchemeValidator))] public class OpenApiSecurityScheme : IOpenApiSerializable, IOpenApiReferenceable, IOpenApiExtensible { /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiServer.cs b/src/Microsoft.OpenApi/Models/OpenApiServer.cs index 3f9ac6c11..e2095fb6c 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiServer.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiServer.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; using Microsoft.OpenApi.Writers; namespace Microsoft.OpenApi.Models @@ -11,6 +13,7 @@ namespace Microsoft.OpenApi.Models /// /// Server Object: an object representing a Server. /// + [OpenApiValidator(typeof(ServerValidator))] public class OpenApiServer : IOpenApiSerializable, IOpenApiExtensible { /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiServerVariable.cs b/src/Microsoft.OpenApi/Models/OpenApiServerVariable.cs index 51c44576d..5725f215b 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiServerVariable.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiServerVariable.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; using Microsoft.OpenApi.Writers; namespace Microsoft.OpenApi.Models @@ -11,6 +13,7 @@ namespace Microsoft.OpenApi.Models /// /// Server Variable Object. /// + [OpenApiValidator(typeof(ServerVariableValidator))] public class OpenApiServerVariable : IOpenApiSerializable, IOpenApiExtensible { /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiTag.cs b/src/Microsoft.OpenApi/Models/OpenApiTag.cs index fa84da6cb..769ccdf26 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiTag.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiTag.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; using Microsoft.OpenApi.Writers; namespace Microsoft.OpenApi.Models @@ -11,6 +13,7 @@ namespace Microsoft.OpenApi.Models /// /// Tag Object. /// + [OpenApiValidator(typeof(TagValidator))] public class OpenApiTag : IOpenApiSerializable, IOpenApiReferenceable, IOpenApiExtensible { /// diff --git a/src/Microsoft.OpenApi/Models/OpenApiXml.cs b/src/Microsoft.OpenApi/Models/OpenApiXml.cs index 1dbd8e310..c34aabff1 100644 --- a/src/Microsoft.OpenApi/Models/OpenApiXml.cs +++ b/src/Microsoft.OpenApi/Models/OpenApiXml.cs @@ -5,6 +5,8 @@ using System.Collections.Generic; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Validations.Validators; using Microsoft.OpenApi.Writers; namespace Microsoft.OpenApi.Models @@ -12,6 +14,7 @@ namespace Microsoft.OpenApi.Models /// /// XML Object. /// + [OpenApiValidator(typeof(XmlValidator))] public class OpenApiXml : IOpenApiSerializable, IOpenApiExtensible { /// diff --git a/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs b/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs index 2b77e680a..e8f4df444 100644 --- a/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs +++ b/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs @@ -239,5 +239,50 @@ internal static string SourceExpressionHasInvalidFormat { return ResourceManager.GetString("SourceExpressionHasInvalidFormat", resourceCulture); } } + + /// + /// Looks up a localized string similar to The key '{0}' in '{1}' of components MUST match the regular expression '{2}'.. + /// + internal static string Validataion_ComponentsKeyMustMatchRegularExpr { + get { + return ResourceManager.GetString("Validataion_ComponentsKeyMustMatchRegularExpr", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The extension name '{0}' in '{1}' object MUST begin with 'x-'.. + /// + internal static string Validation_ExtensionNameMustBeginWithXDash { + get { + return ResourceManager.GetString("Validation_ExtensionNameMustBeginWithXDash", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The field '{0}' in '{1}' object is REQUIRED.. + /// + internal static string Validation_FieldIsRequired { + get { + return ResourceManager.GetString("Validation_FieldIsRequired", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The path item name '{0}' MUST begin with a slash.. + /// + internal static string Validation_PathItemMustBeginWithSlash { + get { + return ResourceManager.GetString("Validation_PathItemMustBeginWithSlash", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The string '{0}' MUST be in the format of an email address.. + /// + internal static string Validation_StringMustBeEmailAddress { + get { + return ResourceManager.GetString("Validation_StringMustBeEmailAddress", resourceCulture); + } + } } } diff --git a/src/Microsoft.OpenApi/Properties/SRResource.resx b/src/Microsoft.OpenApi/Properties/SRResource.resx index bfffc638c..3f5c2bfbe 100644 --- a/src/Microsoft.OpenApi/Properties/SRResource.resx +++ b/src/Microsoft.OpenApi/Properties/SRResource.resx @@ -177,4 +177,19 @@ The source expression '{0}' has invalid format. + + The key '{0}' in '{1}' of components MUST match the regular expression '{2}'. + + + The extension name '{0}' in '{1}' object MUST begin with 'x-'. + + + The field '{0}' in '{1}' object is REQUIRED. + + + The path item name '{0}' MUST begin with a slash. + + + The string '{0}' MUST be in the format of an email address. + \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Validations/OpenApiElementValidator.cs b/src/Microsoft.OpenApi/Validations/OpenApiElementValidator.cs new file mode 100644 index 000000000..dcc8b4dcd --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/OpenApiElementValidator.cs @@ -0,0 +1,74 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System.Collections.Generic; +using System.Linq; +using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations.Rules; + +namespace Microsoft.OpenApi.Validations +{ + /// + /// The public APIs to validate the Open API element. + /// + public static class OpenApiElementValidator + { + /// + /// Validate the Open API element. + /// + /// The Open API element type. + /// The Open API element. + /// True means no errors, otherwise with errors. + public static bool Validate(this T element) where T : IOpenApiElement + { + IEnumerable errors; + return element.Validate(out errors); + } + + /// + /// Validate the Open API element. + /// + /// The Open API element type. + /// The Open API element. + /// The output errors. + /// True means no errors, otherwise with errors. + public static bool Validate(this T element, out IEnumerable errors) + where T : IOpenApiElement + { + return element.Validate(null, out errors); + } + + /// + /// Validate the Open API element. + /// + /// The Open API element type. + /// The Open API element. + /// The input rule set. + /// The output errors. + /// True means no errors, otherwise with errors. + public static bool Validate(this T element, ValidationRuleSet ruleSet, out IEnumerable errors) + where T : IOpenApiElement + { + errors = null; + if (element == null) + { + return true; + } + + if (ruleSet == null) + { + ruleSet = ValidationRuleSet.CreateDefaultRuleSet(); + } + + ValidationContext context = new ValidationContext(ruleSet); + + context.Validate(element); + + errors = context.Errors; + + return !errors.Any(); + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/OpenApiValidatorAttribute.cs b/src/Microsoft.OpenApi/Validations/OpenApiValidatorAttribute.cs new file mode 100644 index 000000000..004755107 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/OpenApiValidatorAttribute.cs @@ -0,0 +1,30 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System; + +namespace Microsoft.OpenApi.Validations +{ + /// + /// The Validator attribute. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + public class OpenApiValidatorAttribute : Attribute + { + /// + /// Initializes the class. + /// + /// The validator type. + public OpenApiValidatorAttribute(Type validatorType) + { + ValidatorType = validatorType ?? throw Error.ArgumentNull(nameof(validatorType)); + } + + /// + /// Gets the validator type. + /// + public Type ValidatorType { get; } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiComponentsRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiComponentsRules.cs new file mode 100644 index 000000000..d396fc9fe --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiComponentsRules.cs @@ -0,0 +1,68 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System.Collections.Generic; +using System.Text.RegularExpressions; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; + +namespace Microsoft.OpenApi.Validations.Rules +{ + /// + /// The validation rules for . + /// + public static class OpenApiComponentsRules + { + /// + /// The key regex. + /// + public static Regex KeyRegex = new Regex(@"^[a-zA-Z0-9\.\-_]+$"); + + /// + /// All the fixed fields declared above are objects + /// that MUST use keys that match the regular expression: ^[a-zA-Z0-9\.\-_]+$. + /// + public static readonly ValidationRule KeyMustBeRegularExpression = + new ValidationRule( + (context, components) => + { + ValidateKeys(context, components.Schemas?.Keys, "schemas"); + + ValidateKeys(context, components.Responses?.Keys, "responses"); + + ValidateKeys(context, components.Parameters?.Keys, "parameters"); + + ValidateKeys(context, components.Examples?.Keys, "examples"); + + ValidateKeys(context, components.RequestBodies?.Keys, "requestBodies"); + + ValidateKeys(context, components.Headers?.Keys, "headers"); + + ValidateKeys(context, components.SecuritySchemes?.Keys, "securitySchemes"); + + ValidateKeys(context, components.Links?.Keys, "links"); + + ValidateKeys(context, components.Callbacks?.Keys, "callbacks"); + }); + + private static void ValidateKeys(ValidationContext context, IEnumerable keys, string component) + { + if (keys == null) + { + return; + } + + foreach(var key in keys) + { + if (!KeyRegex.IsMatch(key)) + { + ValidationError error = new ValidationError(ErrorReason.Format, context.PathString, + string.Format(SRResource.Validataion_ComponentsKeyMustMatchRegularExpr, key, component, KeyRegex.ToString())); + context.AddError(error); + } + } + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiContactRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiContactRules.cs new file mode 100644 index 000000000..f64ac46c2 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiContactRules.cs @@ -0,0 +1,52 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; + +namespace Microsoft.OpenApi.Validations.Rules +{ + /// + /// The validation rules for . + /// + internal static class OpenApiContactRules + { + /// + /// Email field MUST be email address. + /// + public static readonly ValidationRule EmailMustBeEmailFormat = + new ValidationRule( + (context, item) => + { + context.Push("email"); + if (item != null && item.Email != null) + { + if (!item.Email.IsEmailAddress()) + { + ValidationError error = new ValidationError(ErrorReason.Format, context.PathString, + String.Format(SRResource.Validation_StringMustBeEmailAddress, item.Email)); + context.AddError(error); + } + } + context.Pop(); + }); + + /// + /// Url field MUST be url format. + /// + public static readonly ValidationRule UrlMustBeUrlFormat = + new ValidationRule( + (context, item) => + { + context.Push("url"); + if (item != null && item.Url != null) + { + // TODO: + } + context.Pop(); + }); + } +} diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs new file mode 100644 index 000000000..3dd445ee8 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs @@ -0,0 +1,46 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; + + +namespace Microsoft.OpenApi.Validations.Rules +{ + /// + /// The validation rules for . + /// + internal static class OpenApiDocumentRules + { + /// + /// The field is required. + /// + public static readonly ValidationRule FieldIsRequired = + new ValidationRule( + (context, item) => + { + // info + context.Push("info"); + if (item.Info == null) + { + ValidationError error = new ValidationError(ErrorReason.Required, context.PathString, + String.Format(SRResource.Validation_FieldIsRequired, "info", "document")); + context.AddError(error); + } + context.Pop(); + + // paths + context.Push("paths"); + if (item.Paths == null) + { + ValidationError error = new ValidationError(ErrorReason.Required, context.PathString, + String.Format(SRResource.Validation_FieldIsRequired, "paths", "document")); + context.AddError(error); + } + context.Pop(); + }); + } +} diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiExtensionRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiExtensionRules.cs new file mode 100644 index 000000000..e74dbe226 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiExtensionRules.cs @@ -0,0 +1,37 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System; +using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Properties; + +namespace Microsoft.OpenApi.Validations.Rules +{ + /// + /// The validation rules for . + /// + public static class OpenApiExtensibleRules + { + /// + /// Extension name MUST start with "x-". + /// + public static readonly ValidationRule ExtensionNameMustStartWithXDash = + new ValidationRule( + (context, item) => + { + context.Push("extensions"); + foreach (var extensible in item.Extensions) + { + if (!extensible.Key.StartsWith("x-")) + { + ValidationError error = new ValidationError(ErrorReason.Format, context.PathString, + String.Format(SRResource.Validation_ExtensionNameMustBeginWithXDash, extensible.Key, context.PathString)); + context.AddError(error); + } + } + context.Pop(); + }); + } +} \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiExternalDocsRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiExternalDocsRules.cs new file mode 100644 index 000000000..7bf9803db --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiExternalDocsRules.cs @@ -0,0 +1,34 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; + +namespace Microsoft.OpenApi.Validations.Rules +{ + /// + /// The validation rules for . + /// + public static class OpenApiExternalDocsRules + { + /// + /// REQUIRED. The url of the External Documentation Object. + /// + public static readonly ValidationRule UrlIsRequired = + new ValidationRule( + (context, docs) => + { + context.Push("url"); + if (docs.Url == null) + { + ValidationError error = new ValidationError(ErrorReason.Required, context.PathString, + String.Format(SRResource.Validation_FieldIsRequired, "url", "External Documentation")); + context.AddError(error); + } + context.Pop(); + }); + } +} \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiInfoRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiInfoRules.cs new file mode 100644 index 000000000..a908e0a81 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiInfoRules.cs @@ -0,0 +1,47 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; + +namespace Microsoft.OpenApi.Validations.Rules +{ + /// + /// The validation rules for . + /// + internal static class OpenApiInfoRules + { + /// + /// Validate the field is required. + /// + public static readonly ValidationRule FieldIsRequired = + new ValidationRule( + (context, item) => + { + // title + context.Push("title"); + if (String.IsNullOrEmpty(item.Title)) + { + ValidationError error = new ValidationError(ErrorReason.Required, context.PathString, + String.Format(SRResource.Validation_FieldIsRequired, "url", "info")); + context.AddError(error); + } + context.Pop(); + + // version + context.Push("version"); + if (String.IsNullOrEmpty(item.Version)) + { + ValidationError error = new ValidationError(ErrorReason.Required, context.PathString, + String.Format(SRResource.Validation_FieldIsRequired, "version", "info")); + context.AddError(error); + } + context.Pop(); + }); + + // add more rule. + } +} \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiLicenseRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiLicenseRules.cs new file mode 100644 index 000000000..157c32ab5 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiLicenseRules.cs @@ -0,0 +1,34 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; + +namespace Microsoft.OpenApi.Validations.Rules +{ + /// + /// The validation rules for . + /// + public static class OpenApiLicenseRules + { + /// + /// REQUIRED. REQUIRED. The license name used for the API. + /// + public static readonly ValidationRule FieldIsRequired = + new ValidationRule( + (context, license) => + { + context.Push("name"); + if (String.IsNullOrEmpty(license.Name)) + { + ValidationError error = new ValidationError(ErrorReason.Required, context.PathString, + String.Format(SRResource.Validation_FieldIsRequired, "name", "license")); + context.AddError(error); + } + context.Pop(); + }); + } +} \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs new file mode 100644 index 000000000..437071c13 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs @@ -0,0 +1,46 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; + +namespace Microsoft.OpenApi.Validations.Rules +{ + /// + /// The validation rules for . + /// + public static class OpenApiPathsRules + { + /// + /// A relative path to an individual endpoint. The field name MUST begin with a slash. + /// + public static readonly ValidationRule PathNameMustBeginWithSlash = + new ValidationRule( + (context, item) => + { + foreach (var pathName in item.Keys) + { + context.Push(pathName); + + if (string.IsNullOrEmpty(pathName)) + { + // Add the error message + // context.Add(...); + } + + if (!pathName.StartsWith("/")) + { + ValidationError error = new ValidationError(ErrorReason.Format, context.PathString, + string.Format(SRResource.Validation_PathItemMustBeginWithSlash, pathName)); + context.AddError(error); + } + + context.Pop(); + } + }); + + // add more rules + } +} \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiServerRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiServerRules.cs new file mode 100644 index 000000000..f701b0fad --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiServerRules.cs @@ -0,0 +1,34 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; + +namespace Microsoft.OpenApi.Validations.Rules +{ + /// + /// The validation rules for . + /// + public static class OpenApiServerRules + { + /// + /// REQUIRED. The name of the tag. + /// + public static readonly ValidationRule FieldIsRequired = + new ValidationRule( + (context, server) => + { + context.Push("url"); + if (String.IsNullOrEmpty(server.Url)) + { + ValidationError error = new ValidationError(ErrorReason.Required, context.PathString, + String.Format(SRResource.Validation_FieldIsRequired, "url", "Server")); + context.AddError(error); + } + context.Pop(); + }); + } +} \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiTagRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiTagRules.cs new file mode 100644 index 000000000..c09436a73 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiTagRules.cs @@ -0,0 +1,34 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; + +namespace Microsoft.OpenApi.Validations.Rules +{ + /// + /// The validation rules for . + /// + public static class OpenApiTagRules + { + /// + /// REQUIRED. The name of the tag. + /// + public static readonly ValidationRule NameIsRequired = + new ValidationRule( + (context, tag) => + { + context.Push("name"); + if (String.IsNullOrEmpty(tag.Name)) + { + ValidationError error = new ValidationError(ErrorReason.Required, context.PathString, + String.Format(SRResource.Validation_FieldIsRequired, "name", "Tag")); + context.AddError(error); + } + context.Pop(); + }); + } +} \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs b/src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs new file mode 100644 index 000000000..780503158 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs @@ -0,0 +1,40 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System; + +namespace Microsoft.OpenApi.Validations.Rules +{ + internal static class RuleHelpers + { + /// + /// Input string must be in the format of an email address + /// + /// The input string. + /// True is email address, otherwise false. + public static bool IsEmailAddress(this string input) + { + if (String.IsNullOrEmpty(input)) + { + return false; + } + + var splits = input.Split('@'); + if (splits.Length != 2) + { + return false; + } + + if (String.IsNullOrEmpty(splits[0]) || String.IsNullOrEmpty(splits[1])) + { + return false; + } + + // Add more rules. + + return true; + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Rules/ValidationRule.cs b/src/Microsoft.OpenApi/Validations/Rules/ValidationRule.cs new file mode 100644 index 000000000..d9575969e --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Rules/ValidationRule.cs @@ -0,0 +1,48 @@ + +using System; +using System.Diagnostics; +using Microsoft.OpenApi.Interfaces; + +namespace Microsoft.OpenApi.Validations.Rules +{ + /// + /// Class containing validation rule logic. + /// + public abstract class ValidationRule + { + internal abstract Type ValidatedType { get; } + + internal abstract void Evaluate(ValidationContext context, object item); + } + + /// + /// Class containing validation rule logic for . + /// + /// + public class ValidationRule : ValidationRule + where T: IOpenApiElement + { + private readonly Action _validate; + + /// + /// Initializes a new instance of the class. + /// + /// Action to perform the validation. + public ValidationRule(Action validate) + { + this._validate = validate; + } + + internal override Type ValidatedType + { + get { return typeof(T); } + } + + internal override void Evaluate(ValidationContext context, object item) + { + Debug.Assert(item is T, "item should be " + typeof(T)); + T typedItem = (T)item; + this._validate(context, typedItem); + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Rules/ValidationRuleSet.cs b/src/Microsoft.OpenApi/Validations/Rules/ValidationRuleSet.cs new file mode 100644 index 000000000..8cfdcb650 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Rules/ValidationRuleSet.cs @@ -0,0 +1,138 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System; +using System.Linq; +using System.Collections; +using System.Collections.Generic; +using Microsoft.OpenApi.Exceptions; + +namespace Microsoft.OpenApi.Validations.Rules +{ + /// + /// + /// + public sealed class ValidationRuleSet : IEnumerable + { + private IDictionary> _rules = new Dictionary>(); + + private static ValidationRuleSet DefaultRuleSet; + + /// + /// The default rule set. + /// + public static ValidationRuleSet CreateDefaultRuleSet() + { + if (DefaultRuleSet != null) + { + return DefaultRuleSet; + } + + DefaultRuleSet = new ValidationRuleSet + { + OpenApiComponentsRules.KeyMustBeRegularExpression, + + OpenApiDocumentRules.FieldIsRequired, + + OpenApiInfoRules.FieldIsRequired, + + OpenApiContactRules.EmailMustBeEmailFormat, + + OpenApiTagRules.NameIsRequired, + + OpenApiExternalDocsRules.UrlIsRequired, + + OpenApiExtensibleRules.ExtensionNameMustStartWithXDash, + + OpenApiLicenseRules.FieldIsRequired, + + OpenApiServerRules.FieldIsRequired, + + OpenApiPathsRules.PathNameMustBeginWithSlash, + // add more default rules. + }; + + return DefaultRuleSet; + } + + /// + /// Initializes a new instance of the class. + /// + public ValidationRuleSet() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// Rules to be contained in this ruleset. + public ValidationRuleSet(IEnumerable rules) + { + if (rules != null) + { + foreach (ValidationRule rule in rules) + { + Add(rule); + } + } + } + + /// + /// Gets the rules in this rule set. + /// + public IEnumerable Rules + { + get + { + return _rules.Values.SelectMany(v => v); + } + } + + /// + /// Add the new rule into rule set. + /// + /// The rule. + public void Add(ValidationRule rule) + { + IList typeRules; + if (!_rules.TryGetValue(rule.ValidatedType, out typeRules)) + { + typeRules = new List(); + _rules[rule.ValidatedType] = typeRules; + } + + if (typeRules.Contains(rule)) + { + throw new OpenApiException("The same rule cannot be in the same rule set twice."); + } + + typeRules.Add(rule); + } + + /// + /// Get the enumerator. + /// + /// The enumerator. + public IEnumerator GetEnumerator() + { + foreach (List ruleList in _rules.Values) + { + foreach (var rule in ruleList) + { + yield return rule; + } + } + } + + /// + /// Get the enumerator. + /// + /// The enumerator. + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/ValidationContext.cs b/src/Microsoft.OpenApi/Validations/ValidationContext.cs new file mode 100644 index 000000000..f5f8cae64 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/ValidationContext.cs @@ -0,0 +1,81 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System; +using System.Linq; +using System.Collections.Generic; +using Microsoft.OpenApi.Validations.Rules; + +namespace Microsoft.OpenApi.Validations +{ + /// + /// The validation context. + /// + public class ValidationContext + { + private readonly IList _errors = new List(); + + /// + /// Initializes the class. + /// + /// + public ValidationContext(ValidationRuleSet ruleSet) + { + RuleSet = ruleSet ?? throw Error.ArgumentNull(nameof(ruleSet)); + } + + /// + /// Gets the rule set. + /// + public ValidationRuleSet RuleSet { get; } + + /// + /// Gets the validation errors. + /// + public IEnumerable Errors + { + get + { + return _errors; + } + } + + /// + /// Register an error with the validation context. + /// + /// Error to register. + public void AddError(ValidationError error) + { + if (error == null) + { + throw Error.ArgumentNull(nameof(error)); + } + + _errors.Add(error); + } + + #region Visit Path + private readonly Stack _path = new Stack(); + + internal void Push(string segment) + { + this._path.Push(segment); + } + + internal void Pop() + { + this._path.Pop(); + } + + internal string PathString + { + get + { + return "#/" + String.Join("/", _path.Reverse()); + } + } + #endregion + } +} diff --git a/src/Microsoft.OpenApi/Validations/ValidationContextExtensions.cs b/src/Microsoft.OpenApi/Validations/ValidationContextExtensions.cs new file mode 100644 index 000000000..c76d0b426 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/ValidationContextExtensions.cs @@ -0,0 +1,83 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Validations.Validators; +using System.Collections.Generic; + +namespace Microsoft.OpenApi.Validations +{ + internal static class ValidationContextExtensions + { + /// + /// Validate the Open API element. + /// + /// The Open API element. + /// The validation cotext. + /// The Open API element. + public static void Validate(this ValidationContext context, T element) where T : IOpenApiElement + { + if (context == null) + { + throw Error.ArgumentNull(nameof(context)); + } + + if (element != null) + { + IValidator visitor = OpenApiValidatorCache.GetValidator(typeof(T)); + if (visitor != null) + { + visitor.Validate(context, element); + } + } + } + + /// + /// Validate the collection of Open API element. + /// + /// The Open API element. + /// The validation cotext. + /// The collection of Open API element. + public static void ValidateCollection(this ValidationContext context, IEnumerable collection) + where T : IOpenApiElement + { + if (context == null) + { + throw Error.ArgumentNull(nameof(context)); + } + + if (collection != null) + { + foreach (var element in collection) + { + context.Validate(element); + } + } + } + + /// + /// Validate the map of Open API element. + /// + /// The Open API element. + /// The validation cotext. + /// The map of Open API element. + public static void ValidateMap(this ValidationContext context, IDictionary elements) + where T : IOpenApiElement + { + if (context == null) + { + throw Error.ArgumentNull(nameof(context)); + } + + if (elements != null) + { + foreach (var element in elements) + { + context.Validate(element.Value); + } + } + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/ValidationError.cs b/src/Microsoft.OpenApi/Validations/ValidationError.cs new file mode 100644 index 000000000..19a4ac193 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/ValidationError.cs @@ -0,0 +1,66 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +namespace Microsoft.OpenApi.Validations +{ + /// + /// Error reason. + /// + public enum ErrorReason + { + /// + /// Field is required. + /// + Required, + + /// + /// Format error. + /// + Format, + } + + /// + /// The validation error class. + /// + public sealed class ValidationError + { + /// + /// Initializes the class. + /// + /// The error reason. + /// The visit path. + /// The error message. + public ValidationError(ErrorReason reason, string path, string message) + { + ErrorCode = reason; + ErrorPath = path; + ErrorMessage = message; + } + + /// + /// Gets the path of the error in the Open API in which it occurred. + /// + public string ErrorPath { get; private set; } + + /// + /// Gets an integer code representing the error. + /// + public ErrorReason ErrorCode { get; private set; } + + /// + /// Gets a human readable string describing the error. + /// + public string ErrorMessage { get; private set; } + + /// + /// Returns the whole error message. + /// + /// The error string. + public override string ToString() + { + return "ErroCode: " + ErrorCode + ", " + ErrorPath + " | " + ErrorMessage; + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/CallbackValidator.cs b/src/Microsoft.OpenApi/Validations/Validators/CallbackValidator.cs new file mode 100644 index 000000000..4a3ebdf03 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/CallbackValidator.cs @@ -0,0 +1,34 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System.Diagnostics; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Visit . + /// + internal class CallbackValidator : ValidatorBase + { + /// + /// Visit the children of the . + /// + /// The validation context. + /// The . + protected override void Next(ValidationContext context, OpenApiCallback callback) + { + Debug.Assert(context != null); + Debug.Assert(callback != null); + + foreach (var pathItem in callback.PathItems) + { + context.Validate(pathItem.Value); + } + + base.Next(context, callback); + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/ComponentsValidator.cs b/src/Microsoft.OpenApi/Validations/Validators/ComponentsValidator.cs new file mode 100644 index 000000000..7bdab6419 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/ComponentsValidator.cs @@ -0,0 +1,38 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System.Diagnostics; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Visit . + /// + internal class ComponentsValidator : ValidatorBase + { + /// + /// Visit the children of the . + /// + /// The validation context. + /// The . + protected override void Next(ValidationContext context, OpenApiComponents components) + { + Debug.Assert(context != null); + Debug.Assert(components != null); + + context.ValidateMap(components.Schemas); + context.ValidateMap(components.Responses); + context.ValidateMap(components.Parameters); + context.ValidateMap(components.Examples); + context.ValidateMap(components.RequestBodies); + context.ValidateMap(components.Headers); + context.ValidateMap(components.SecuritySchemes); + context.ValidateMap(components.Links); + context.ValidateMap(components.Callbacks); + base.Next(context, components); + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/ContactValidator.cs b/src/Microsoft.OpenApi/Validations/Validators/ContactValidator.cs new file mode 100644 index 000000000..15ac45bfa --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/ContactValidator.cs @@ -0,0 +1,16 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Visit . + /// + internal class ContactValidator : ValidatorBase + { + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/DiscriminatorValidator.cs b/src/Microsoft.OpenApi/Validations/Validators/DiscriminatorValidator.cs new file mode 100644 index 000000000..0c0919574 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/DiscriminatorValidator.cs @@ -0,0 +1,16 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Visit . + /// + internal class DiscriminatorValidator : ValidatorBase + { + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/DocumentVisitor.cs b/src/Microsoft.OpenApi/Validations/Validators/DocumentVisitor.cs new file mode 100644 index 000000000..222caca01 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/DocumentVisitor.cs @@ -0,0 +1,35 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System.Diagnostics; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Vistor for + /// + internal class DocumentValidator : ValidatorBase + { + /// + /// Visit the children of the . + /// + /// The validation context. + /// The . + protected override void Next(ValidationContext context, OpenApiDocument document) + { + Debug.Assert(context != null); + Debug.Assert(document != null); + + context.Validate(document.Info); + context.ValidateCollection(document.Servers); + context.Validate(document.Paths); + context.Validate(document.Components); + context.ValidateCollection(document.SecurityRequirements); + context.ValidateCollection(document.Tags); + base.Next(context, document); + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/EncodingValidator.cs b/src/Microsoft.OpenApi/Validations/Validators/EncodingValidator.cs new file mode 100644 index 000000000..e3c8efec0 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/EncodingValidator.cs @@ -0,0 +1,31 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System.Diagnostics; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Visit . + /// + internal class EncodingValidator : ValidatorBase + { + /// + /// Visit the children in . + /// + /// The validation context. + /// The . + protected override void Next(ValidationContext context, OpenApiEncoding encoding) + { + Debug.Assert(context != null); + Debug.Assert(encoding != null); + + context.ValidateMap(encoding.Headers); + + base.Next(context, encoding); + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/ExampleValidator.cs b/src/Microsoft.OpenApi/Validations/Validators/ExampleValidator.cs new file mode 100644 index 000000000..47eab7a53 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/ExampleValidator.cs @@ -0,0 +1,16 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Visit . + /// + internal class ExampleValidator : ValidatorBase + { + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/ExternalDocsValidator.cs b/src/Microsoft.OpenApi/Validations/Validators/ExternalDocsValidator.cs new file mode 100644 index 000000000..13f574b04 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/ExternalDocsValidator.cs @@ -0,0 +1,16 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Visit . + /// + internal class ExternalDocsValidator : ValidatorBase + { + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/HeaderValidator.cs b/src/Microsoft.OpenApi/Validations/Validators/HeaderValidator.cs new file mode 100644 index 000000000..5df8088de --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/HeaderValidator.cs @@ -0,0 +1,35 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System.Diagnostics; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Visit . + /// + internal class HeaderValidator : ValidatorBase + { + /// + /// Visit the children in . + /// + /// The validation context. + /// The . + protected override void Next(ValidationContext context, OpenApiHeader header) + { + Debug.Assert(context != null); + Debug.Assert(header != null); + + context.Validate(header.Schema); + + context.ValidateCollection(header.Examples); + + context.ValidateMap(header.Content); + + base.Next(context, header); + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/IVisitor.cs b/src/Microsoft.OpenApi/Validations/Validators/IVisitor.cs new file mode 100644 index 000000000..2236d1868 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/IVisitor.cs @@ -0,0 +1,20 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// The interface for visitor. + /// + internal interface IValidator + { + /// + /// Visit the object. + /// + /// The validation context. + /// The validation object. + void Validate(ValidationContext context, object item); + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/InfoValidator.cs b/src/Microsoft.OpenApi/Validations/Validators/InfoValidator.cs new file mode 100644 index 000000000..bddc9ae0e --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/InfoValidator.cs @@ -0,0 +1,33 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System.Diagnostics; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Visit . + /// + internal class InfoValidator : ValidatorBase + { + /// + /// Visit the children in . + /// + /// The validation context. + /// The . + protected override void Next(ValidationContext context, OpenApiInfo info) + { + Debug.Assert(context != null); + Debug.Assert(info != null); + + context.Validate(info.Contact); + + context.Validate(info.License); + + base.Next(context, info); + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/LicenseValidator.cs b/src/Microsoft.OpenApi/Validations/Validators/LicenseValidator.cs new file mode 100644 index 000000000..60880df68 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/LicenseValidator.cs @@ -0,0 +1,16 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Visit . + /// + internal class LicenseValidator : ValidatorBase + { + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/LinkValidator.cs b/src/Microsoft.OpenApi/Validations/Validators/LinkValidator.cs new file mode 100644 index 000000000..4be2b80de --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/LinkValidator.cs @@ -0,0 +1,31 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System.Diagnostics; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Visit . + /// + internal class LinkValidator : ValidatorBase + { + /// + /// Visit the children of the . + /// + /// The validation context. + /// The . + protected override void Next(ValidationContext context, OpenApiLink link) + { + Debug.Assert(context != null); + Debug.Assert(link != null); + + context.Validate(link.Server); + + base.Next(context, link); + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/MediaTypeValidator.cs b/src/Microsoft.OpenApi/Validations/Validators/MediaTypeValidator.cs new file mode 100644 index 000000000..b2fb92b99 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/MediaTypeValidator.cs @@ -0,0 +1,33 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System.Diagnostics; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Visit . + /// + internal class MediaTypeValidator : ValidatorBase + { + /// + /// Visit the children of the . + /// + /// The validation context. + /// The . + protected override void Next(ValidationContext context, OpenApiMediaType mediaType) + { + Debug.Assert(context != null); + Debug.Assert(mediaType != null); + + context.Validate(mediaType.Schema); + context.ValidateMap(mediaType.Examples); + context.ValidateMap(mediaType.Encoding); + + base.Next(context, mediaType); + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/OAuthFlowValidator.cs b/src/Microsoft.OpenApi/Validations/Validators/OAuthFlowValidator.cs new file mode 100644 index 000000000..a36c4bb79 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/OAuthFlowValidator.cs @@ -0,0 +1,16 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Visit . + /// + internal class OAuthFlowValidator : ValidatorBase + { + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/OAuthFlowsValidator.cs b/src/Microsoft.OpenApi/Validations/Validators/OAuthFlowsValidator.cs new file mode 100644 index 000000000..45a8561f8 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/OAuthFlowsValidator.cs @@ -0,0 +1,34 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System.Diagnostics; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Visit . + /// + internal class OAuthFlowsValidator : ValidatorBase + { + /// + /// Visit the children of the . + /// + /// The validation context. + /// The . + protected override void Next(ValidationContext context, OpenApiOAuthFlows oAuthFlows) + { + Debug.Assert(context != null); + Debug.Assert(oAuthFlows != null); + + context.Validate(oAuthFlows.Implicit); + context.Validate(oAuthFlows.Password); + context.Validate(oAuthFlows.ClientCredentials); + context.Validate(oAuthFlows.AuthorizationCode); + + base.Next(context, oAuthFlows); + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/OpenApiValidatorCache.cs b/src/Microsoft.OpenApi/Validations/Validators/OpenApiValidatorCache.cs new file mode 100644 index 000000000..c4e94872b --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/OpenApiValidatorCache.cs @@ -0,0 +1,46 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.OpenApi.Validations.Validators +{ + internal static class OpenApiValidatorCache + { + private static IDictionary _elementVisitor = new Dictionary(); + + public static IValidator GetValidator(Type elementType) + { + IValidator validator; + if (_elementVisitor.TryGetValue(elementType, out validator)) + { + return validator; + } + + var attribute = elementType.GetCustomAttributes(typeof(OpenApiValidatorAttribute), false).FirstOrDefault(); + if (attribute == null) + { + return null; + } + + OpenApiValidatorAttribute validateAttr = attribute as OpenApiValidatorAttribute; + if (validateAttr == null) + { + return null; + } + + object instance = Activator.CreateInstance(validateAttr.ValidatorType); + validator = instance as IValidator; + if (validator != null) + { + _elementVisitor[validateAttr.ValidatorType] = validator; + } + + return validator; + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/OperationValidator.cs b/src/Microsoft.OpenApi/Validations/Validators/OperationValidator.cs new file mode 100644 index 000000000..c0e34fb2a --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/OperationValidator.cs @@ -0,0 +1,38 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System.Diagnostics; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Visit . + /// + internal class OperationValidator : ValidatorBase + { + /// + /// Visit the children of the . + /// + /// The validation context. + /// The . + protected override void Next(ValidationContext context, OpenApiOperation operation) + { + Debug.Assert(context != null); + Debug.Assert(operation != null); + + context.ValidateCollection(operation.Tags); + context.Validate(operation.ExternalDocs); + context.ValidateCollection(operation.Parameters); + context.Validate(operation.RequestBody); + context.Validate(operation.Responses); + context.ValidateMap(operation.Callbacks); + context.ValidateCollection(operation.Security); + context.ValidateCollection(operation.Servers); + + base.Next(context, operation); + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/ParameterValidator.cs b/src/Microsoft.OpenApi/Validations/Validators/ParameterValidator.cs new file mode 100644 index 000000000..ae845307f --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/ParameterValidator.cs @@ -0,0 +1,33 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System.Diagnostics; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Visit . + /// + internal class ParameterValidator : ValidatorBase + { + /// + /// Visit the children of the . + /// + /// The validation context. + /// The . + protected override void Next(ValidationContext context, OpenApiParameter parameter) + { + Debug.Assert(context != null); + Debug.Assert(parameter != null); + + context.Validate(parameter.Schema); + context.ValidateCollection(parameter.Examples); + context.ValidateMap(parameter.Content); + + base.Next(context, parameter); + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/PathItemValidator.cs b/src/Microsoft.OpenApi/Validations/Validators/PathItemValidator.cs new file mode 100644 index 000000000..b837fa846 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/PathItemValidator.cs @@ -0,0 +1,37 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System.Diagnostics; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Visit . + /// + internal class PathItemValidator : ValidatorBase + { + /// + /// Visit the children of the . + /// + /// The validation context. + /// The . + protected override void Next(ValidationContext context, OpenApiPathItem pathItem) + { + Debug.Assert(context != null); + Debug.Assert(pathItem != null); + + foreach (var operation in pathItem.Operations) + { + context.Validate(operation.Value); + } + + context.ValidateCollection(pathItem.Servers); + context.ValidateCollection(pathItem.Parameters); + + base.Next(context, pathItem); + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/PathsValidator.cs b/src/Microsoft.OpenApi/Validations/Validators/PathsValidator.cs new file mode 100644 index 000000000..2f6ea1423 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/PathsValidator.cs @@ -0,0 +1,30 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System.Diagnostics; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Visit . + /// + internal class PathsValidator : ValidatorBase + { + /// + /// Visit the children of the . + /// + /// The validation context. + /// The . + protected override void Next(ValidationContext context, OpenApiPaths paths) + { + Debug.Assert(context != null); + Debug.Assert(paths != null); + + context.ValidateMap(paths); + base.Next(context, paths); + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/RequestBodyValidator.cs b/src/Microsoft.OpenApi/Validations/Validators/RequestBodyValidator.cs new file mode 100644 index 000000000..f198efb53 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/RequestBodyValidator.cs @@ -0,0 +1,31 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System.Diagnostics; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Visit . + /// + internal class RequestBodyValidator : ValidatorBase + { + /// + /// Visit the children of the . + /// + /// The validation context. + /// The . + protected override void Next(ValidationContext context, OpenApiRequestBody requestBody) + { + Debug.Assert(context != null); + Debug.Assert(requestBody != null); + + context.ValidateMap(requestBody.Content); + + base.Next(context, requestBody); + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/ResponseValidator.cs b/src/Microsoft.OpenApi/Validations/Validators/ResponseValidator.cs new file mode 100644 index 000000000..c11674c39 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/ResponseValidator.cs @@ -0,0 +1,33 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System.Diagnostics; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Visit . + /// + internal class ResponseValidator : ValidatorBase + { + /// + /// Visit the children of the . + /// + /// The validation context. + /// The . + protected override void Next(ValidationContext context, OpenApiResponse response) + { + Debug.Assert(context != null); + Debug.Assert(response != null); + + context.ValidateMap(response.Headers); + context.ValidateMap(response.Content); + context.ValidateMap(response.Links); + + base.Next(context, response); + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/ResponsesValidator.cs b/src/Microsoft.OpenApi/Validations/Validators/ResponsesValidator.cs new file mode 100644 index 000000000..9b32af4a3 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/ResponsesValidator.cs @@ -0,0 +1,31 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System.Diagnostics; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Visit . + /// + internal class ResponsesValidator : ValidatorBase + { + /// + /// Visit the children of the . + /// + /// The validation context. + /// The . + protected override void Next(ValidationContext context, OpenApiResponses responses) + { + Debug.Assert(context != null); + Debug.Assert(responses != null); + + context.ValidateMap(responses); + + base.Next(context, responses); + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/SchemaValidator.cs b/src/Microsoft.OpenApi/Validations/Validators/SchemaValidator.cs new file mode 100644 index 000000000..b77a3aab9 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/SchemaValidator.cs @@ -0,0 +1,49 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System.Diagnostics; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Visit . + /// + internal class SchemaValidator : ValidatorBase + { + /// + /// Visit the children of the . + /// + /// The validation context. + /// The . + protected override void Next(ValidationContext context, OpenApiSchema schema) + { + Debug.Assert(context != null); + Debug.Assert(schema != null); + + context.ValidateCollection(schema.AllOf); + + context.ValidateCollection(schema.OneOf); + + context.ValidateCollection(schema.AnyOf); + + context.Validate(schema.Not); + + context.Validate(schema.Items); + + context.ValidateMap(schema.Properties); + + context.Validate(schema.AdditionalProperties); + + context.Validate(schema.Discriminator); + + context.Validate(schema.ExternalDocs); + + context.Validate(schema.Xml); + + base.Next(context, schema); + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/SecurityRequirementValidator.cs b/src/Microsoft.OpenApi/Validations/Validators/SecurityRequirementValidator.cs new file mode 100644 index 000000000..9bffd2d5a --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/SecurityRequirementValidator.cs @@ -0,0 +1,16 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Visit . + /// + internal class SecurityRequirementValidator : ValidatorBase + { + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/SecuritySchemeValidator.cs b/src/Microsoft.OpenApi/Validations/Validators/SecuritySchemeValidator.cs new file mode 100644 index 000000000..0b17dd52c --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/SecuritySchemeValidator.cs @@ -0,0 +1,32 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System.Diagnostics; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Visit . + /// + internal class SecuritySchemeValidator : ValidatorBase + { + /// + /// Visit the children of the . + /// + /// The validation context. + /// The . + protected override void Next(ValidationContext context, OpenApiSecurityScheme securityScheme) + { + Debug.Assert(context != null); + Debug.Assert(securityScheme != null); + + context.Validate(securityScheme.Flows); + + // add more. + base.Next(context, securityScheme); + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/ServerValidator.cs b/src/Microsoft.OpenApi/Validations/Validators/ServerValidator.cs new file mode 100644 index 000000000..89d125dd2 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/ServerValidator.cs @@ -0,0 +1,32 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System.Diagnostics; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Visit . + /// + internal class ServerValidator : ValidatorBase + { + /// + /// Visit the children of the . + /// + /// The validation context. + /// The . + protected override void Next(ValidationContext context, OpenApiServer server) + { + Debug.Assert(context != null); + Debug.Assert(server != null); + + context.ValidateMap(server.Variables); + + // add more. + base.Next(context, server); + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/ServerVariableValidator.cs b/src/Microsoft.OpenApi/Validations/Validators/ServerVariableValidator.cs new file mode 100644 index 000000000..89494829d --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/ServerVariableValidator.cs @@ -0,0 +1,16 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Visit . + /// + internal class ServerVariableValidator : ValidatorBase + { + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/TagValidator.cs b/src/Microsoft.OpenApi/Validations/Validators/TagValidator.cs new file mode 100644 index 000000000..e21e93e73 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/TagValidator.cs @@ -0,0 +1,31 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System.Diagnostics; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Visit . + /// + internal class TagValidator : ValidatorBase + { + /// + /// Visit the children in . + /// + /// The validation context. + /// The . + protected override void Next(ValidationContext context, OpenApiTag tag) + { + Debug.Assert(context != null); + Debug.Assert(tag != null); + + context.Validate(tag.ExternalDocs); + + base.Next(context, tag); + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/ValidatorBase.cs b/src/Microsoft.OpenApi/Validations/Validators/ValidatorBase.cs new file mode 100644 index 000000000..2938bcc0f --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/ValidatorBase.cs @@ -0,0 +1,56 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System.Diagnostics; +using System.Linq; +using Microsoft.OpenApi.Interfaces; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// The base visitor class for Open Api elements. + /// + internal abstract class ValidatorBase : IValidator where T : IOpenApiElement + { + /// + /// Visit the element itself. + /// + /// The validation context. + /// The element. + public void Validate(ValidationContext context, object item) + { + Debug.Assert(item is T, "item should be " + typeof(T)); + + // validate itself + var rules = context.RuleSet.Where(r => r.ValidatedType == typeof(T)); + foreach (var rule in rules) + { + rule.Evaluate(context, item); + } + + // validate it's children + T typedItem = (T)item; + this.Next(context, typedItem); + } + + /// + /// Visit the children. + /// + /// The validation context. + /// The element. + protected virtual void Next(ValidationContext context, T element) + { + IOpenApiExtensible extensbile = element as IOpenApiExtensible; + if (extensbile != null) + { + var rules = context.RuleSet.Where(r => r.ValidatedType == typeof(IOpenApiExtensible)); + foreach (var rule in rules) + { + rule.Evaluate(context, extensbile); + } + } + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Validators/XmlValidator.cs b/src/Microsoft.OpenApi/Validations/Validators/XmlValidator.cs new file mode 100644 index 000000000..f0cc70bb3 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Validators/XmlValidator.cs @@ -0,0 +1,16 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Validators +{ + /// + /// Visit . + /// + internal class XmlValidator : ValidatorBase + { + } +} diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs new file mode 100644 index 000000000..f66e4fe07 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs @@ -0,0 +1,35 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System.Collections.Generic; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Validations; +using Xunit; + +namespace Microsoft.OpenApi.Tests.Models +{ + public class OpenApiContactValidationTests + { + [Fact] + public void ValidateEmailFieldIsEmailAddressInContact() + { + // Arrange + IEnumerable errors; + OpenApiContact Contact = new OpenApiContact() + { + Email = "support/example.com", + }; + + // Act + bool result = Contact.Validate(out errors); + + // Assert + Assert.False(result); + Assert.NotNull(errors); + ValidationError error = Assert.Single(errors); + Assert.Equal("The string 'support/example.com' MUST be in the format of an email address.", error.ErrorMessage); + } + } +} diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiInfoValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiInfoValidationTests.cs new file mode 100644 index 000000000..2f89e2d62 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiInfoValidationTests.cs @@ -0,0 +1,34 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System.Collections.Generic; +using System.Linq; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Validations; +using Xunit; + +namespace Microsoft.OpenApi.Tests.Models +{ + public class OpenApiInfoValidationTests + { + [Fact] + public void ValidateFieldIsRequiredInInfo() + { + // Arrange + IEnumerable errors; + OpenApiInfo info = new OpenApiInfo(); + + // Act + bool result = info.Validate(out errors); + + // Assert + Assert.False(result); + Assert.NotNull(errors); + + Assert.Equal(new[] { "The field 'url' in 'info' object is REQUIRED.", + "The field 'version' in 'info' object is REQUIRED."}, errors.Select(e => e.ErrorMessage)); + } + } +} diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiLicenseValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiLicenseValidationTests.cs new file mode 100644 index 000000000..b6e5225f7 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiLicenseValidationTests.cs @@ -0,0 +1,32 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System.Collections.Generic; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Validations; +using Xunit; + +namespace Microsoft.OpenApi.Tests.Models +{ + public class OpenApiLicenseValidationTests + { + [Fact] + public void ValidateFieldIsRequiredInLicense() + { + // Arrange + IEnumerable errors; + OpenApiLicense license = new OpenApiLicense(); + + // Act + bool result = license.Validate(out errors); + + // Assert + Assert.False(result); + Assert.NotNull(errors); + ValidationError error = Assert.Single(errors); + Assert.Equal("The field 'name' in 'license' object is REQUIRED.", error.ErrorMessage); + } + } +} diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiServerValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiServerValidationTests.cs new file mode 100644 index 000000000..9bbf86516 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiServerValidationTests.cs @@ -0,0 +1,32 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System.Collections.Generic; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Validations; +using Xunit; + +namespace Microsoft.OpenApi.Tests.Models +{ + public class OpenApiServerValidationTests + { + [Fact] + public void ValidateFieldIsRequiredInServer() + { + // Arrange + IEnumerable errors; + OpenApiServer server = new OpenApiServer(); + + // Act + bool result = server.Validate(out errors); + + // Assert + Assert.False(result); + Assert.NotNull(errors); + ValidationError error = Assert.Single(errors); + Assert.Equal("The field 'url' in 'Server' object is REQUIRED.", error.ErrorMessage); + } + } +} diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiTagValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiTagValidationTests.cs new file mode 100644 index 000000000..2fd1b16bd --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiTagValidationTests.cs @@ -0,0 +1,54 @@ +// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. +// ------------------------------------------------------------ + +using System.Collections.Generic; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Validations; +using Xunit; +using Microsoft.OpenApi.Any; + +namespace Microsoft.OpenApi.Tests.Models +{ + public class OpenApiTagValidationTests + { + [Fact] + public void ValidateNameIsRequiredInTag() + { + // Arrange + IEnumerable errors; + OpenApiTag tag = new OpenApiTag(); + + // Act + bool result = tag.Validate(out errors); + + // Assert + Assert.False(result); + Assert.NotNull(errors); + ValidationError error = Assert.Single(errors); + Assert.Equal("The field 'name' in 'Tag' object is REQUIRED.", error.ErrorMessage); + } + + [Fact] + public void ValidateExtensionNameStartsWithXDashInTag() + { + // Arrange + IEnumerable errors; + OpenApiTag tag = new OpenApiTag + { + Name = "tag" + }; + tag.Extensions.Add("tagExt", new OpenApiString("value")); + + // Act + bool result = tag.Validate(out errors); + + // Assert + Assert.False(result); + Assert.NotNull(errors); + ValidationError error = Assert.Single(errors); + Assert.Equal("The extension name 'tagExt' in '#/extensions' object MUST begin with 'x-'.", error.ErrorMessage); + } + } +}