From 7316184c87f117c61a1266e8c7ec660973d3b8b5 Mon Sep 17 00:00:00 2001 From: Sam Xu Date: Tue, 12 Dec 2017 10:10:52 -0800 Subject: [PATCH 01/12] Init implementation for the Open API validation --- .../Properties/SRResource.Designer.cs | 18 +++ .../Properties/SRResource.resx | 6 + .../Validations/OpenApiElementValidator.cs | 75 ++++++++++++ .../Rules/OpenApiComponentsRules.cs | 16 +++ .../Validations/Rules/OpenApiContactRules.cs | 52 ++++++++ .../Validations/Rules/OpenApiDocumentRules.cs | 41 +++++++ .../Rules/OpenApiExtensionRules.cs | 28 +++++ .../Validations/Rules/OpenApiInfoRules.cs | 31 +++++ .../Validations/Rules/OpenApiPathsRules.cs | 46 +++++++ .../Validations/Rules/RuleHelpers.cs | 40 ++++++ .../Validations/Rules/ValidationRule.cs | 48 ++++++++ .../Validations/Rules/ValidationRuleSet.cs | 114 ++++++++++++++++++ .../Validations/ValidationContext.cs | 80 ++++++++++++ .../ValidationContextExtensions.cs | 60 +++++++++ .../Validations/ValidationError.cs | 78 ++++++++++++ .../Validations/Visitors/ContactVisitor.cs | 24 ++++ .../Validations/Visitors/DocumentVisitor.cs | 34 ++++++ .../Validations/Visitors/IVisitor.cs | 20 +++ .../Validations/Visitors/InfoVisitor.cs | 32 +++++ .../Validations/Visitors/LicenseVisitor.cs | 28 +++++ .../Validations/Visitors/OpenApiVisitorSet.cs | 45 +++++++ .../Validations/Visitors/TagVisitor.cs | 30 +++++ .../Validations/Visitors/VisitorBase.cs | 43 +++++++ .../OpenApiContactValidationTests.cs | 36 ++++++ 24 files changed, 1025 insertions(+) create mode 100644 src/Microsoft.OpenApi/Validations/OpenApiElementValidator.cs create mode 100644 src/Microsoft.OpenApi/Validations/Rules/OpenApiComponentsRules.cs create mode 100644 src/Microsoft.OpenApi/Validations/Rules/OpenApiContactRules.cs create mode 100644 src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs create mode 100644 src/Microsoft.OpenApi/Validations/Rules/OpenApiExtensionRules.cs create mode 100644 src/Microsoft.OpenApi/Validations/Rules/OpenApiInfoRules.cs create mode 100644 src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs create mode 100644 src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs create mode 100644 src/Microsoft.OpenApi/Validations/Rules/ValidationRule.cs create mode 100644 src/Microsoft.OpenApi/Validations/Rules/ValidationRuleSet.cs create mode 100644 src/Microsoft.OpenApi/Validations/ValidationContext.cs create mode 100644 src/Microsoft.OpenApi/Validations/ValidationContextExtensions.cs create mode 100644 src/Microsoft.OpenApi/Validations/ValidationError.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/ContactVisitor.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/DocumentVisitor.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/IVisitor.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/InfoVisitor.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/LicenseVisitor.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/OpenApiVisitorSet.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/TagVisitor.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/VisitorBase.cs create mode 100644 test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs diff --git a/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs b/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs index 2b77e680a..7ad783f28 100644 --- a/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs +++ b/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs @@ -239,5 +239,23 @@ internal static string SourceExpressionHasInvalidFormat { return ResourceManager.GetString("SourceExpressionHasInvalidFormat", 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..6eab4a17e 100644 --- a/src/Microsoft.OpenApi/Properties/SRResource.resx +++ b/src/Microsoft.OpenApi/Properties/SRResource.resx @@ -177,4 +177,10 @@ The source expression '{0}' has invalid format. + + 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..e3613f5c5 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/OpenApiElementValidator.cs @@ -0,0 +1,75 @@ +// ------------------------------------------------------------ +// 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 + { + ValidationRuleSet ruleSet = ValidationRuleSet.DefaultRuleSet; + return element.Validate(ruleSet, 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.DefaultRuleSet; + } + + ValidationContext context = new ValidationContext(ruleSet); + + context.Validate(element); + + errors = context.Errors; + + return !errors.Any(); + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiComponentsRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiComponentsRules.cs new file mode 100644 index 000000000..96b7e2735 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiComponentsRules.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.Rules +{ + /// + /// The validation rules for . + /// + public static class OpenApiComponentsRules + { + } +} diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiContactRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiContactRules.cs new file mode 100644 index 000000000..2ebb37596 --- /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.EmailFormat, 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..511eccbea --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs @@ -0,0 +1,41 @@ +// ------------------------------------------------------------ +// 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.Rules +{ + /// + /// The validation rules for . + /// + internal static class OpenApiDocumentRules + { + /// + /// The Info field is required. + /// + public static readonly ValidationRule InfoIsRequired = + new ValidationRule( + (context, item) => + { + if (item.Info == null) + { + //context.AddError(); + } + }); + + /// + /// The Paths field is required. + /// + public static readonly ValidationRule PathsIsRequired = + new ValidationRule( + (context, item) => + { + if (item.Paths == null) + { + //context.AddError(); + } + }); + } +} diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiExtensionRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiExtensionRules.cs new file mode 100644 index 000000000..5bdf0f5cf --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiExtensionRules.cs @@ -0,0 +1,28 @@ +// ------------------------------------------------------------ +// 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; + +namespace Microsoft.OpenApi.Validations.Rules +{ + /// + /// The validation rules for . + /// + public static class OpenApiExtensibleRules + { + /// + /// Extension name MUST start with "x-". + /// + private static readonly ValidationRule ExtensionNameMustStartWithXDollar = + new ValidationRule( + (context, item) => + { + foreach (var extensible in item.Extensions) + { + + } + }); + } +} \ 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..b03f8ad8e --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiInfoRules.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; +using Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Rules +{ + /// + /// The validation rules for . + /// + internal static class OpenApiInfoRules + { + /// + /// The title of the application is required. + /// + public static readonly ValidationRule TitleIsRequired = + new ValidationRule( + (context, item) => + { + if (String.IsNullOrEmpty(item.Title)) + { + // add error. + } + }); + + // add more rule. + } +} \ 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/RuleHelpers.cs b/src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs new file mode 100644 index 000000000..67a52f809 --- /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. + /// + 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..d85c0c201 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Rules/ValidationRuleSet.cs @@ -0,0 +1,114 @@ +// ------------------------------------------------------------ +// 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>(); + + /// + /// The default rule set. + /// + public static ValidationRuleSet DefaultRuleSet = new ValidationRuleSet + { + OpenApiDocumentRules.InfoIsRequired, + OpenApiDocumentRules.PathsIsRequired, + + OpenApiInfoRules.TitleIsRequired, + + OpenApiContactRules.EmailMustBeEmailFormat, + + // add more default rules. + }; + + /// + /// 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..0adcee72c --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/ValidationContext.cs @@ -0,0 +1,80 @@ +// ------------------------------------------------------------ +// 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 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); + } + } + #endregion + } +} diff --git a/src/Microsoft.OpenApi/Validations/ValidationContextExtensions.cs b/src/Microsoft.OpenApi/Validations/ValidationContextExtensions.cs new file mode 100644 index 000000000..0cb8325c2 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/ValidationContextExtensions.cs @@ -0,0 +1,60 @@ +// ------------------------------------------------------------ +// 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.Visitors; +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) + { + IVisitor visitor = OpenApiVisitorSet.GetVisitor(typeof(T)); + if (visitor != null) + { + visitor.Visit(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); + } + } + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/ValidationError.cs b/src/Microsoft.OpenApi/Validations/ValidationError.cs new file mode 100644 index 000000000..2356e7720 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/ValidationError.cs @@ -0,0 +1,78 @@ +// ------------------------------------------------------------ +// 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. + /// + FieldIsRequired, + + /// + /// Format error. + /// + Format, + + /// + /// Must be email format. + /// + EmailFormat, + + /// + /// Must be Url format. + /// + UrlFormat, + + // Add more. + } + + /// + /// 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/Visitors/ContactVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/ContactVisitor.cs new file mode 100644 index 000000000..9b2420f9c --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/ContactVisitor.cs @@ -0,0 +1,24 @@ +// ------------------------------------------------------------ +// 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.Visitors +{ + /// + /// Visit . + /// + internal class ContactVisitor : VisitorBase + { + /// + /// Visit the children in . + /// + /// The validation context. + /// The . + protected override void Next(ValidationContext context, OpenApiContact contact) + { + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Visitors/DocumentVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/DocumentVisitor.cs new file mode 100644 index 000000000..032617ea5 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/DocumentVisitor.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 Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Visitors +{ + /// + /// Vistor for + /// + internal class DocumentVisitor : VisitorBase + { + /// + /// Visit the children of the . + /// + /// The validation context. + /// The . + protected override void Next(ValidationContext context, OpenApiDocument document) + { + if (document == null) + { + return; + } + + context.Validate(document.Info); + + context.ValidateCollection(document.Tags); + + // add more. + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Visitors/IVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/IVisitor.cs new file mode 100644 index 000000000..60374d19c --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/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.Visitors +{ + /// + /// The interface for visitor. + /// + internal interface IVisitor + { + /// + /// Visit the object. + /// + /// The validation context. + /// The validation object. + void Visit(ValidationContext context, object item); + } +} diff --git a/src/Microsoft.OpenApi/Validations/Visitors/InfoVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/InfoVisitor.cs new file mode 100644 index 000000000..23e708eec --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/InfoVisitor.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 Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Visitors +{ + /// + /// Visit . + /// + internal class InfoVisitor : VisitorBase + { + /// + /// Visit the children in . + /// + /// The validation context. + /// The . + protected override void Next(ValidationContext context, OpenApiInfo info) + { + if (info == null) + { + return; + } + + context.Validate(info.Contact); + + context.Validate(info.License); + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Visitors/LicenseVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/LicenseVisitor.cs new file mode 100644 index 000000000..30361834f --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/LicenseVisitor.cs @@ -0,0 +1,28 @@ +// ------------------------------------------------------------ +// 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.Visitors +{ + /// + /// Visit . + /// + internal class LicenseVisitor : VisitorBase + { + /// + /// Visit the children in . + /// + /// The validation context. + /// The . + protected override void Next(ValidationContext context, OpenApiLicense license) + { + if (license == null) + { + return; + } + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Visitors/OpenApiVisitorSet.cs b/src/Microsoft.OpenApi/Validations/Visitors/OpenApiVisitorSet.cs new file mode 100644 index 000000000..d48f77120 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/OpenApiVisitorSet.cs @@ -0,0 +1,45 @@ +// ------------------------------------------------------------ +// 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 System; +using System.Collections.Generic; +using System.Linq; + +namespace Microsoft.OpenApi.Validations.Visitors +{ + /// + /// + /// + internal static class OpenApiVisitorSet + { + private static IDictionary _elementVisitor = new Dictionary + { + { typeof(OpenApiDocument), new DocumentVisitor() }, + { typeof(OpenApiInfo), new InfoVisitor() }, + { typeof(OpenApiTag), new TagVisitor() }, + { typeof(OpenApiLicense), new LicenseVisitor() }, + { typeof(OpenApiContact), new ContactVisitor() }, + + // add more + }; + + /// + /// Get the element visitor. + /// + /// The element type. + /// The element visitor or null. + public static IVisitor GetVisitor(Type elementType) + { + var visitor = _elementVisitor.FirstOrDefault(c => c.Key == elementType).Value; + if (visitor != null) + { + return visitor; + } + + return null; + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Visitors/TagVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/TagVisitor.cs new file mode 100644 index 000000000..a7b193367 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/TagVisitor.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 Microsoft.OpenApi.Models; + +namespace Microsoft.OpenApi.Validations.Visitors +{ + /// + /// Visit . + /// + internal class TagVisitor : VisitorBase + { + /// + /// Visit the children in . + /// + /// The validation context. + /// The . + protected override void Next(ValidationContext context, OpenApiTag tag) + { + if (tag == null) + { + return; + } + + context.Validate(tag.ExternalDocs); + } + } +} diff --git a/src/Microsoft.OpenApi/Validations/Visitors/VisitorBase.cs b/src/Microsoft.OpenApi/Validations/Visitors/VisitorBase.cs new file mode 100644 index 000000000..8a9075283 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/VisitorBase.cs @@ -0,0 +1,43 @@ +// ------------------------------------------------------------ +// 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.Visitors +{ + /// + /// The base visitor class for Open Api elements. + /// + internal abstract class VisitorBase : IVisitor where T : IOpenApiElement + { + /// + /// Visit the element itself. + /// + /// The validation context. + /// The element. + public void Visit(ValidationContext context, object item) + { + Debug.Assert(item is T, "item should be " + typeof(T)); + + var rules = context.RuleSet.Where(r => r.ValidatedType == typeof(T)); + foreach (var rule in rules) + { + rule.Evaluate(context, item); + } + + T typedItem = (T)item; + this.Next(context, typedItem); + } + + /// + /// Visit the children. + /// + /// The validation context. + /// The element. + protected abstract void Next(ValidationContext context, T element); + } +} diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs new file mode 100644 index 000000000..ad2406d86 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs @@ -0,0 +1,36 @@ +// ------------------------------------------------------------ +// 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.Validations; +using System.Collections.Generic; +using Xunit; + +namespace Microsoft.OpenApi.Tests.Models +{ + public class OpenApiContactValidationTests + { + public static OpenApiContact Contact = new OpenApiContact() + { + Email = "support/example.com", + }; + + [Fact] + public void SerializeAdvanceContactAsJsonWorks() + { + // Arrange + IEnumerable errors; + + // 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); + } + } +} From 0988b1b63f38b9f40be2f40697356009a34ad5ba Mon Sep 17 00:00:00 2001 From: Sam Xu Date: Fri, 15 Dec 2017 13:37:06 -0800 Subject: [PATCH 02/12] add the whole visitor classes for validation --- .../Properties/SRResource.Designer.cs | 9 ++++ .../Properties/SRResource.resx | 3 ++ .../ValidationContextExtensions.cs | 25 +++++++++- .../Validations/Visitors/CallbackVisitor.cs | 34 +++++++++++++ .../Validations/Visitors/ComponentsVisitor.cs | 38 ++++++++++++++ .../Validations/Visitors/ContactVisitor.cs | 8 --- .../Visitors/DiscriminatorVisitor.cs | 16 ++++++ .../Validations/Visitors/EncodingVisitor.cs | 31 ++++++++++++ .../Validations/Visitors/ExampleVisitor.cs | 16 ++++++ .../Visitors/ExternalDocsVisitor.cs | 16 ++++++ .../Validations/Visitors/HeaderVisitor.cs | 35 +++++++++++++ .../Validations/Visitors/InfoVisitor.cs | 9 ++-- .../Validations/Visitors/LicenseVisitor.cs | 14 +----- .../Validations/Visitors/LinkVisitor.cs | 31 ++++++++++++ .../Validations/Visitors/MediaTypeVisitor.cs | 33 +++++++++++++ .../Validations/Visitors/OAuthFlowVisitor.cs | 16 ++++++ .../Validations/Visitors/OAuthFlowsVisitor.cs | 34 +++++++++++++ .../Validations/Visitors/OpenApiVisitorSet.cs | 45 ++++++++++++----- .../Validations/Visitors/OperationVisitor.cs | 38 ++++++++++++++ .../Validations/Visitors/ParameterVisitor.cs | 33 +++++++++++++ .../Validations/Visitors/PathItemVisitor.cs | 37 ++++++++++++++ .../Validations/Visitors/PathsVisitor.cs | 30 ++++++++++++ .../Visitors/RequestBodyVisitor.cs | 31 ++++++++++++ .../Validations/Visitors/ResponseVisitor.cs | 33 +++++++++++++ .../Validations/Visitors/ResponsesVisitor.cs | 31 ++++++++++++ .../Validations/Visitors/SchemaVisitor.cs | 49 +++++++++++++++++++ .../Visitors/SecurityRequirementVisitor.cs | 16 ++++++ .../Visitors/SecuritySchemeVisitor.cs | 32 ++++++++++++ .../Visitors/ServerVariableVisitor.cs | 16 ++++++ .../Validations/Visitors/ServerVisitor.cs | 32 ++++++++++++ .../Visitors/{TagVisitor.cs => TagVistor.cs} | 9 ++-- .../Validations/Visitors/VisitorBase.cs | 13 ++++- .../Validations/Visitors/XmlVistor.cs | 16 ++++++ 33 files changed, 787 insertions(+), 42 deletions(-) create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/CallbackVisitor.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/ComponentsVisitor.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/DiscriminatorVisitor.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/EncodingVisitor.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/ExampleVisitor.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/ExternalDocsVisitor.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/HeaderVisitor.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/LinkVisitor.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/MediaTypeVisitor.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/OAuthFlowVisitor.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/OAuthFlowsVisitor.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/OperationVisitor.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/ParameterVisitor.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/PathItemVisitor.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/PathsVisitor.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/RequestBodyVisitor.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/ResponseVisitor.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/ResponsesVisitor.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/SchemaVisitor.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/SecurityRequirementVisitor.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/SecuritySchemeVisitor.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/ServerVariableVisitor.cs create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/ServerVisitor.cs rename src/Microsoft.OpenApi/Validations/Visitors/{TagVisitor.cs => TagVistor.cs} (86%) create mode 100644 src/Microsoft.OpenApi/Validations/Visitors/XmlVistor.cs diff --git a/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs b/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs index 7ad783f28..6ac662cdd 100644 --- a/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs +++ b/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs @@ -240,6 +240,15 @@ internal static string SourceExpressionHasInvalidFormat { } } + /// + /// Looks up a localized string similar to Can not find visitor type registered for type '{0}'.. + /// + internal static string UnknownVisitorType { + get { + return ResourceManager.GetString("UnknownVisitorType", resourceCulture); + } + } + /// /// Looks up a localized string similar to The path item name '{0}' MUST begin with a slash.. /// diff --git a/src/Microsoft.OpenApi/Properties/SRResource.resx b/src/Microsoft.OpenApi/Properties/SRResource.resx index 6eab4a17e..c7a24d0b6 100644 --- a/src/Microsoft.OpenApi/Properties/SRResource.resx +++ b/src/Microsoft.OpenApi/Properties/SRResource.resx @@ -177,6 +177,9 @@ The source expression '{0}' has invalid format. + + Can not find visitor type registered for type '{0}'. + The path item name '{0}' MUST begin with a slash. diff --git a/src/Microsoft.OpenApi/Validations/ValidationContextExtensions.cs b/src/Microsoft.OpenApi/Validations/ValidationContextExtensions.cs index 0cb8325c2..52b9c5d2c 100644 --- a/src/Microsoft.OpenApi/Validations/ValidationContextExtensions.cs +++ b/src/Microsoft.OpenApi/Validations/ValidationContextExtensions.cs @@ -3,9 +3,9 @@ // Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. // ------------------------------------------------------------ +using System.Collections.Generic; using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Validations.Visitors; -using System.Collections.Generic; namespace Microsoft.OpenApi.Validations { @@ -56,5 +56,28 @@ public static void ValidateCollection(this ValidationContext context, IEnumer } } } + + /// + /// 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/Visitors/CallbackVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/CallbackVisitor.cs new file mode 100644 index 000000000..cbe023917 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/CallbackVisitor.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.Visitors +{ + /// + /// Visit . + /// + internal class CallbackVisitor : VisitorBase + { + /// + /// 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/Visitors/ComponentsVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/ComponentsVisitor.cs new file mode 100644 index 000000000..3af0a6016 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/ComponentsVisitor.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.Visitors +{ + /// + /// Visit . + /// + internal class ComponentsVisitor : VisitorBase + { + /// + /// 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/Visitors/ContactVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/ContactVisitor.cs index 9b2420f9c..d53dba4c2 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/ContactVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/ContactVisitor.cs @@ -12,13 +12,5 @@ namespace Microsoft.OpenApi.Validations.Visitors /// internal class ContactVisitor : VisitorBase { - /// - /// Visit the children in . - /// - /// The validation context. - /// The . - protected override void Next(ValidationContext context, OpenApiContact contact) - { - } } } diff --git a/src/Microsoft.OpenApi/Validations/Visitors/DiscriminatorVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/DiscriminatorVisitor.cs new file mode 100644 index 000000000..71e00f478 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/DiscriminatorVisitor.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.Visitors +{ + /// + /// Visit . + /// + internal class DiscriminatorVisitor : VisitorBase + { + } +} diff --git a/src/Microsoft.OpenApi/Validations/Visitors/EncodingVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/EncodingVisitor.cs new file mode 100644 index 000000000..cb6ec2db2 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/EncodingVisitor.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.Visitors +{ + /// + /// Visit . + /// + internal class EncodingVisitor : VisitorBase + { + /// + /// 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/Visitors/ExampleVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/ExampleVisitor.cs new file mode 100644 index 000000000..8c9f19565 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/ExampleVisitor.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.Visitors +{ + /// + /// Visit . + /// + internal class ExampleVisitor : VisitorBase + { + } +} diff --git a/src/Microsoft.OpenApi/Validations/Visitors/ExternalDocsVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/ExternalDocsVisitor.cs new file mode 100644 index 000000000..15b24e57d --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/ExternalDocsVisitor.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.Visitors +{ + /// + /// Visit . + /// + internal class ExternalDocsVisitor : VisitorBase + { + } +} diff --git a/src/Microsoft.OpenApi/Validations/Visitors/HeaderVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/HeaderVisitor.cs new file mode 100644 index 000000000..2b2222237 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/HeaderVisitor.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.Visitors +{ + /// + /// Visit . + /// + internal class HeaderVisitor : VisitorBase + { + /// + /// 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/Visitors/InfoVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/InfoVisitor.cs index 23e708eec..2c2ca8e5c 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/InfoVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/InfoVisitor.cs @@ -3,6 +3,7 @@ // 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.Visitors @@ -19,14 +20,14 @@ internal class InfoVisitor : VisitorBase /// The . protected override void Next(ValidationContext context, OpenApiInfo info) { - if (info == null) - { - return; - } + 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/Visitors/LicenseVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/LicenseVisitor.cs index 30361834f..cdb926107 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/LicenseVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/LicenseVisitor.cs @@ -8,21 +8,9 @@ namespace Microsoft.OpenApi.Validations.Visitors { /// - /// Visit . + /// Visit . /// internal class LicenseVisitor : VisitorBase { - /// - /// Visit the children in . - /// - /// The validation context. - /// The . - protected override void Next(ValidationContext context, OpenApiLicense license) - { - if (license == null) - { - return; - } - } } } diff --git a/src/Microsoft.OpenApi/Validations/Visitors/LinkVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/LinkVisitor.cs new file mode 100644 index 000000000..eb92c3483 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/LinkVisitor.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.Visitors +{ + /// + /// Visit . + /// + internal class LinkVisitor : VisitorBase + { + /// + /// 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/Visitors/MediaTypeVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/MediaTypeVisitor.cs new file mode 100644 index 000000000..6fb2897de --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/MediaTypeVisitor.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.Visitors +{ + /// + /// Visit . + /// + internal class MediaTypeVisitor : VisitorBase + { + /// + /// 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/Visitors/OAuthFlowVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/OAuthFlowVisitor.cs new file mode 100644 index 000000000..e96364e20 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/OAuthFlowVisitor.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.Visitors +{ + /// + /// Visit . + /// + internal class OAuthFlowVisitor : VisitorBase + { + } +} diff --git a/src/Microsoft.OpenApi/Validations/Visitors/OAuthFlowsVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/OAuthFlowsVisitor.cs new file mode 100644 index 000000000..a5baacd25 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/OAuthFlowsVisitor.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.Visitors +{ + /// + /// Visit . + /// + internal class OAuthFlowsVisitor : VisitorBase + { + /// + /// 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/Visitors/OpenApiVisitorSet.cs b/src/Microsoft.OpenApi/Validations/Visitors/OpenApiVisitorSet.cs index d48f77120..e79af1058 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/OpenApiVisitorSet.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/OpenApiVisitorSet.cs @@ -3,27 +3,50 @@ // Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. // ------------------------------------------------------------ -using Microsoft.OpenApi.Models; using System; using System.Collections.Generic; -using System.Linq; +using Microsoft.OpenApi.Exceptions; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; namespace Microsoft.OpenApi.Validations.Visitors { /// - /// + /// Class to cache the . /// internal static class OpenApiVisitorSet { - private static IDictionary _elementVisitor = new Dictionary + private static IDictionary _visitorCache = new Dictionary { + { typeof(OpenApiCallback), new CallbackVisitor() }, + { typeof(OpenApiComponents), new ComponentsVisitor() }, + { typeof(OpenApiContact), new ContactVisitor() }, + { typeof(OpenApiDiscriminator), new DiscriminatorVisitor() }, { typeof(OpenApiDocument), new DocumentVisitor() }, + { typeof(OpenApiEncoding), new EncodingVisitor() }, + { typeof(OpenApiExample), new ExampleVisitor() }, + { typeof(OpenApiExternalDocs), new ExternalDocsVisitor() }, + { typeof(OpenApiHeader), new HeaderVisitor() }, { typeof(OpenApiInfo), new InfoVisitor() }, - { typeof(OpenApiTag), new TagVisitor() }, { typeof(OpenApiLicense), new LicenseVisitor() }, - { typeof(OpenApiContact), new ContactVisitor() }, - - // add more + { typeof(OpenApiLink), new LinkVisitor() }, + { typeof(OpenApiMediaType), new MediaTypeVisitor() }, + { typeof(OpenApiOAuthFlows), new OAuthFlowsVisitor() }, + { typeof(OpenApiOAuthFlow), new OAuthFlowVisitor() }, + { typeof(OpenApiOperation), new OperationVisitor() }, + { typeof(OpenApiParameter), new ParameterVisitor() }, + { typeof(OpenApiPathItem), new PathItemVisitor() }, + { typeof(OpenApiPaths), new PathsVisitor() }, + { typeof(OpenApiRequestBody), new RequestBodyVisitor() }, + { typeof(OpenApiResponses), new ResponsesVisitor() }, + { typeof(OpenApiResponse), new ResponseVisitor() }, + { typeof(OpenApiSchema), new SchemaVisitor() }, + { typeof(OpenApiSecurityRequirement), new SecurityRequirementVisitor() }, + { typeof(OpenApiSecurityScheme), new SecuritySchemeVisitor() }, + { typeof(OpenApiServerVariable), new ServerVariableVisitor() }, + { typeof(OpenApiServer), new ServerVisitor() }, + { typeof(OpenApiTag), new TagVisitor() }, + { typeof(OpenApiXml), new XmlVisitor() } }; /// @@ -33,13 +56,13 @@ internal static class OpenApiVisitorSet /// The element visitor or null. public static IVisitor GetVisitor(Type elementType) { - var visitor = _elementVisitor.FirstOrDefault(c => c.Key == elementType).Value; - if (visitor != null) + IVisitor visitor; + if (_visitorCache.TryGetValue(elementType, out visitor)) { return visitor; } - return null; + throw new OpenApiException(String.Format(SRResource.UnknownVisitorType, elementType.FullName)); } } } diff --git a/src/Microsoft.OpenApi/Validations/Visitors/OperationVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/OperationVisitor.cs new file mode 100644 index 000000000..f0a897d86 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/OperationVisitor.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.Visitors +{ + /// + /// Visit . + /// + internal class OperationVisitor : VisitorBase + { + /// + /// 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/Visitors/ParameterVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/ParameterVisitor.cs new file mode 100644 index 000000000..cf9d1d9f0 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/ParameterVisitor.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.Visitors +{ + /// + /// Visit . + /// + internal class ParameterVisitor : VisitorBase + { + /// + /// 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/Visitors/PathItemVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/PathItemVisitor.cs new file mode 100644 index 000000000..f1f6c97a0 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/PathItemVisitor.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.Visitors +{ + /// + /// Visit . + /// + internal class PathItemVisitor : VisitorBase + { + /// + /// 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/Visitors/PathsVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/PathsVisitor.cs new file mode 100644 index 000000000..a31d1c7f8 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/PathsVisitor.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.Visitors +{ + /// + /// Visit . + /// + internal class PathsVisitor : VisitorBase + { + /// + /// 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/Visitors/RequestBodyVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/RequestBodyVisitor.cs new file mode 100644 index 000000000..4653a42e7 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/RequestBodyVisitor.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.Visitors +{ + /// + /// Visit . + /// + internal class RequestBodyVisitor : VisitorBase + { + /// + /// 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/Visitors/ResponseVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/ResponseVisitor.cs new file mode 100644 index 000000000..630d1a1ee --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/ResponseVisitor.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.Visitors +{ + /// + /// Visit . + /// + internal class ResponseVisitor : VisitorBase + { + /// + /// 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/Visitors/ResponsesVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/ResponsesVisitor.cs new file mode 100644 index 000000000..8a933d9de --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/ResponsesVisitor.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.Visitors +{ + /// + /// Visit . + /// + internal class ResponsesVisitor : VisitorBase + { + /// + /// 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/Visitors/SchemaVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/SchemaVisitor.cs new file mode 100644 index 000000000..d536d55f4 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/SchemaVisitor.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.Visitors +{ + /// + /// Visit . + /// + internal class SchemaVisitor : VisitorBase + { + /// + /// 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/Visitors/SecurityRequirementVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/SecurityRequirementVisitor.cs new file mode 100644 index 000000000..c2d8670cc --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/SecurityRequirementVisitor.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.Visitors +{ + /// + /// Visit . + /// + internal class SecurityRequirementVisitor : VisitorBase + { + } +} diff --git a/src/Microsoft.OpenApi/Validations/Visitors/SecuritySchemeVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/SecuritySchemeVisitor.cs new file mode 100644 index 000000000..377bd11db --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/SecuritySchemeVisitor.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.Visitors +{ + /// + /// Visit . + /// + internal class SecuritySchemeVisitor : VisitorBase + { + /// + /// 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/Visitors/ServerVariableVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/ServerVariableVisitor.cs new file mode 100644 index 000000000..67948085f --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/ServerVariableVisitor.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.Visitors +{ + /// + /// Visit . + /// + internal class ServerVariableVisitor : VisitorBase + { + } +} diff --git a/src/Microsoft.OpenApi/Validations/Visitors/ServerVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/ServerVisitor.cs new file mode 100644 index 000000000..6e3538ef3 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/ServerVisitor.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.Visitors +{ + /// + /// Visit . + /// + internal class ServerVisitor : VisitorBase + { + /// + /// 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/Visitors/TagVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/TagVistor.cs similarity index 86% rename from src/Microsoft.OpenApi/Validations/Visitors/TagVisitor.cs rename to src/Microsoft.OpenApi/Validations/Visitors/TagVistor.cs index a7b193367..e011462ce 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/TagVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/TagVistor.cs @@ -3,6 +3,7 @@ // 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.Visitors @@ -19,12 +20,12 @@ internal class TagVisitor : VisitorBase /// The . protected override void Next(ValidationContext context, OpenApiTag tag) { - if (tag == null) - { - return; - } + Debug.Assert(context != null); + Debug.Assert(tag != null); context.Validate(tag.ExternalDocs); + + base.Next(context, tag); } } } diff --git a/src/Microsoft.OpenApi/Validations/Visitors/VisitorBase.cs b/src/Microsoft.OpenApi/Validations/Visitors/VisitorBase.cs index 8a9075283..d22ee7854 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/VisitorBase.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/VisitorBase.cs @@ -38,6 +38,17 @@ public void Visit(ValidationContext context, object item) /// /// The validation context. /// The element. - protected abstract void Next(ValidationContext context, T 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/Visitors/XmlVistor.cs b/src/Microsoft.OpenApi/Validations/Visitors/XmlVistor.cs new file mode 100644 index 000000000..a915cd015 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Visitors/XmlVistor.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.Visitors +{ + /// + /// Visit . + /// + internal class XmlVisitor : VisitorBase + { + } +} From 620b0f21114cb2cdd0c68c70a2cf37d0976f8e3c Mon Sep 17 00:00:00 2001 From: Sam Xu Date: Fri, 15 Dec 2017 14:25:55 -0800 Subject: [PATCH 03/12] add the rules and modify the header copyright --- .../Properties/SRResource.Designer.cs | 36 ++++++++++++ .../Properties/SRResource.resx | 12 ++++ .../Validations/OpenApiElementValidator.cs | 6 +- .../Rules/OpenApiComponentsRules.cs | 58 +++++++++++++++++-- .../Validations/Rules/OpenApiContactRules.cs | 8 +-- .../Validations/Rules/OpenApiDocumentRules.cs | 32 +++++----- .../Rules/OpenApiExtensionRules.cs | 21 ++++--- .../Rules/OpenApiExternalDocsRules.cs | 35 +++++++++++ .../Validations/Rules/OpenApiInfoRules.cs | 30 +++++++--- .../Validations/Rules/OpenApiLicenseRules.cs | 34 +++++++++++ .../Validations/Rules/OpenApiPathsRules.cs | 6 +- .../Validations/Rules/OpenApiResponseRules.cs | 35 +++++++++++ .../Validations/Rules/OpenApiServerRules.cs | 34 +++++++++++ .../Validations/Rules/OpenApiTagRules.cs | 34 +++++++++++ .../Validations/Rules/RuleHelpers.cs | 6 +- .../Validations/ValidationContext.cs | 8 +-- .../ValidationContextExtensions.cs | 6 +- .../Validations/ValidationError.cs | 20 +------ .../Validations/{Rules => }/ValidationRule.cs | 21 +++++-- .../{Rules => }/ValidationRuleSet.cs | 33 +++++++---- .../Validations/Visitors/CallbackVisitor.cs | 6 +- .../Validations/Visitors/ComponentsVisitor.cs | 6 +- .../Validations/Visitors/ContactVisitor.cs | 6 +- .../Visitors/DiscriminatorVisitor.cs | 6 +- .../Validations/Visitors/DocumentVisitor.cs | 6 +- .../Validations/Visitors/EncodingVisitor.cs | 6 +- .../Validations/Visitors/ExampleVisitor.cs | 6 +- .../Visitors/ExternalDocsVisitor.cs | 6 +- .../Validations/Visitors/HeaderVisitor.cs | 6 +- .../Validations/Visitors/IVisitor.cs | 6 +- .../Validations/Visitors/InfoVisitor.cs | 6 +- .../Validations/Visitors/LicenseVisitor.cs | 6 +- .../Validations/Visitors/LinkVisitor.cs | 6 +- .../Validations/Visitors/MediaTypeVisitor.cs | 6 +- .../Validations/Visitors/OAuthFlowVisitor.cs | 6 +- .../Validations/Visitors/OAuthFlowsVisitor.cs | 6 +- .../Validations/Visitors/OpenApiVisitorSet.cs | 6 +- .../Validations/Visitors/OperationVisitor.cs | 6 +- .../Validations/Visitors/ParameterVisitor.cs | 6 +- .../Validations/Visitors/PathItemVisitor.cs | 6 +- .../Validations/Visitors/PathsVisitor.cs | 6 +- .../Visitors/RequestBodyVisitor.cs | 6 +- .../Validations/Visitors/ResponseVisitor.cs | 6 +- .../Validations/Visitors/ResponsesVisitor.cs | 6 +- .../Validations/Visitors/SchemaVisitor.cs | 6 +- .../Visitors/SecurityRequirementVisitor.cs | 6 +- .../Visitors/SecuritySchemeVisitor.cs | 6 +- .../Visitors/ServerVariableVisitor.cs | 6 +- .../Validations/Visitors/ServerVisitor.cs | 6 +- .../Validations/Visitors/TagVistor.cs | 6 +- .../Validations/Visitors/VisitorBase.cs | 10 ++-- .../Validations/Visitors/XmlVistor.cs | 6 +- .../OpenApiComponentsValidationTests.cs | 37 ++++++++++++ .../OpenApiContactValidationTests.cs | 17 +++--- .../Validations/OpenApiInfoValidationTests.cs | 32 ++++++++++ .../OpenApiLicenseValidationTests.cs | 30 ++++++++++ .../OpenApiServerValidationTests.cs | 30 ++++++++++ .../Validations/OpenApiTagValidationTests.cs | 52 +++++++++++++++++ 58 files changed, 635 insertions(+), 234 deletions(-) create mode 100644 src/Microsoft.OpenApi/Validations/Rules/OpenApiExternalDocsRules.cs create mode 100644 src/Microsoft.OpenApi/Validations/Rules/OpenApiLicenseRules.cs create mode 100644 src/Microsoft.OpenApi/Validations/Rules/OpenApiResponseRules.cs create mode 100644 src/Microsoft.OpenApi/Validations/Rules/OpenApiServerRules.cs create mode 100644 src/Microsoft.OpenApi/Validations/Rules/OpenApiTagRules.cs rename src/Microsoft.OpenApi/Validations/{Rules => }/ValidationRule.cs (66%) rename src/Microsoft.OpenApi/Validations/{Rules => }/ValidationRuleSet.cs (75%) create mode 100644 test/Microsoft.OpenApi.Tests/Validations/OpenApiComponentsValidationTests.cs create mode 100644 test/Microsoft.OpenApi.Tests/Validations/OpenApiInfoValidationTests.cs create mode 100644 test/Microsoft.OpenApi.Tests/Validations/OpenApiLicenseValidationTests.cs create mode 100644 test/Microsoft.OpenApi.Tests/Validations/OpenApiServerValidationTests.cs create mode 100644 test/Microsoft.OpenApi.Tests/Validations/OpenApiTagValidationTests.cs diff --git a/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs b/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs index 6ac662cdd..3f0fcb101 100644 --- a/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs +++ b/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs @@ -249,6 +249,33 @@ internal static string UnknownVisitorType { } } + /// + /// Looks up a localized string similar to The key '{0}' in '{1}' of components MUST match the regular expression '{2}'.. + /// + internal static string Validation_ComponentsKeyMustMatchRegularExpr { + get { + return ResourceManager.GetString("Validation_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.. /// @@ -258,6 +285,15 @@ internal static string Validation_PathItemMustBeginWithSlash { } } + /// + /// Looks up a localized string similar to The same rule cannot be in the same rule set twice.. + /// + internal static string Validation_RuleAddTwice { + get { + return ResourceManager.GetString("Validation_RuleAddTwice", resourceCulture); + } + } + /// /// Looks up a localized string similar to The string '{0}' MUST be in the format of an email address.. /// diff --git a/src/Microsoft.OpenApi/Properties/SRResource.resx b/src/Microsoft.OpenApi/Properties/SRResource.resx index c7a24d0b6..d32042422 100644 --- a/src/Microsoft.OpenApi/Properties/SRResource.resx +++ b/src/Microsoft.OpenApi/Properties/SRResource.resx @@ -180,9 +180,21 @@ Can not find visitor type registered for type '{0}'. + + 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 same rule cannot be in the same rule set twice. + The string '{0}' MUST be in the format of an email address. diff --git a/src/Microsoft.OpenApi/Validations/OpenApiElementValidator.cs b/src/Microsoft.OpenApi/Validations/OpenApiElementValidator.cs index e3613f5c5..917f1edce 100644 --- a/src/Microsoft.OpenApi/Validations/OpenApiElementValidator.cs +++ b/src/Microsoft.OpenApi/Validations/OpenApiElementValidator.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using System.Collections.Generic; using System.Linq; diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiComponentsRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiComponentsRules.cs index 96b7e2735..84d3bba1e 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiComponentsRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiComponentsRules.cs @@ -1,9 +1,10 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +using System.Collections.Generic; +using System.Text.RegularExpressions; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; namespace Microsoft.OpenApi.Validations.Rules { @@ -12,5 +13,54 @@ namespace Microsoft.OpenApi.Validations.Rules /// 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.Validation_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 index 2ebb37596..ed79b13ac 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiContactRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiContactRules.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using System; using Microsoft.OpenApi.Models; @@ -26,7 +24,7 @@ internal static class OpenApiContactRules { if (!item.Email.IsEmailAddress()) { - ValidationError error = new ValidationError(ErrorReason.EmailFormat, context.PathString, + ValidationError error = new ValidationError(ErrorReason.Format, context.PathString, String.Format(SRResource.Validation_StringMustBeEmailAddress, item.Email)); context.AddError(error); } diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs index 511eccbea..1b2314b39 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs @@ -1,9 +1,9 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +using System; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; namespace Microsoft.OpenApi.Validations.Rules { @@ -15,27 +15,29 @@ internal static class OpenApiDocumentRules /// /// The Info field is required. /// - public static readonly ValidationRule InfoIsRequired = + public static readonly ValidationRule FieldIsRequired = new ValidationRule( (context, item) => { + // info + context.Push("info"); if (item.Info == null) { - //context.AddError(); + ValidationError error = new ValidationError(ErrorReason.Required, context.PathString, + String.Format(SRResource.Validation_FieldIsRequired, "info", "document")); + context.AddError(error); } - }); + context.Pop(); - /// - /// The Paths field is required. - /// - public static readonly ValidationRule PathsIsRequired = - new ValidationRule( - (context, item) => - { + // paths + context.Push("paths"); if (item.Paths == null) { - //context.AddError(); + 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 index 5bdf0f5cf..7888e35c0 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiExtensionRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiExtensionRules.cs @@ -1,9 +1,9 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. +using System; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Properties; namespace Microsoft.OpenApi.Validations.Rules { @@ -15,14 +15,21 @@ public static class OpenApiExtensibleRules /// /// Extension name MUST start with "x-". /// - private static readonly ValidationRule ExtensionNameMustStartWithXDollar = + 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..e30f31ade --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiExternalDocsRules.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; + +namespace Microsoft.OpenApi.Validations.Rules +{ + /// + /// The validation rules for . + /// + internal static class OpenApiExternalDocsRules + { + /// + /// Validate the field is required. + /// + public static readonly ValidationRule FieldIsRequired = + new ValidationRule( + (context, item) => + { + // title + context.Push("url"); + if (item.Url == null) + { + ValidationError error = new ValidationError(ErrorReason.Required, context.PathString, + String.Format(SRResource.Validation_FieldIsRequired, "url", "External Documentation")); + context.AddError(error); + } + context.Pop(); + }); + + // add more rule. + } +} diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiInfoRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiInfoRules.cs index b03f8ad8e..d35af1a86 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiInfoRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiInfoRules.cs @@ -1,10 +1,9 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using System; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; namespace Microsoft.OpenApi.Validations.Rules { @@ -14,18 +13,33 @@ namespace Microsoft.OpenApi.Validations.Rules internal static class OpenApiInfoRules { /// - /// The title of the application is required. + /// Validate the field is required. /// - public static readonly ValidationRule TitleIsRequired = + public static readonly ValidationRule FieldIsRequired = new ValidationRule( (context, item) => { + // title + context.Push("title"); if (String.IsNullOrEmpty(item.Title)) { - // add error. + 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..ec11e6145 --- /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. + +using System; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; + +namespace Microsoft.OpenApi.Validations.Rules +{ + /// + /// The validation rules for . + /// + public static class OpenApiLicenseRules + { + /// + /// REQUIRED. + /// + 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(); + }); + + // add more rules + } +} diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs index 437071c13..90223d21c 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Properties; diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiResponseRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiResponseRules.cs new file mode 100644 index 000000000..18a1155d4 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiResponseRules.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; + +namespace Microsoft.OpenApi.Validations.Rules +{ + /// + /// The validation rules for . + /// + internal static class OpenApiResponseRules + { + /// + /// Validate the field is required. + /// + public static readonly ValidationRule FieldIsRequired = + new ValidationRule( + (context, response) => + { + // title + context.Push("description"); + if (String.IsNullOrEmpty(response.Description)) + { + ValidationError error = new ValidationError(ErrorReason.Required, context.PathString, + String.Format(SRResource.Validation_FieldIsRequired, "description", "response")); + context.AddError(error); + } + context.Pop(); + }); + + // add more rule. + } +} diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiServerRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiServerRules.cs new file mode 100644 index 000000000..be3328255 --- /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. + +using System; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; + +namespace Microsoft.OpenApi.Validations.Rules +{ + /// + /// The validation rules for . + /// + public static class OpenApiServerRules + { + /// + /// REQUIRED. + /// + 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(); + }); + + // add more rules + } +} diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiTagRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiTagRules.cs new file mode 100644 index 000000000..d1af4ab5c --- /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. + +using System; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; + +namespace Microsoft.OpenApi.Validations.Rules +{ + /// + /// The validation rules for . + /// + public static class OpenApiTagRules + { + /// + /// REQUIRED. + /// + public static readonly ValidationRule FieldIsRequired = + 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(); + }); + + // add more rules + } +} diff --git a/src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs b/src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs index 67a52f809..8f7f1b046 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using System; diff --git a/src/Microsoft.OpenApi/Validations/ValidationContext.cs b/src/Microsoft.OpenApi/Validations/ValidationContext.cs index 0adcee72c..866e21e6e 100644 --- a/src/Microsoft.OpenApi/Validations/ValidationContext.cs +++ b/src/Microsoft.OpenApi/Validations/ValidationContext.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using System; using System.Collections.Generic; @@ -72,7 +70,7 @@ internal string PathString { get { - return String.Join("/", _path); + return "#/" + String.Join("/", _path); } } #endregion diff --git a/src/Microsoft.OpenApi/Validations/ValidationContextExtensions.cs b/src/Microsoft.OpenApi/Validations/ValidationContextExtensions.cs index 52b9c5d2c..db6056592 100644 --- a/src/Microsoft.OpenApi/Validations/ValidationContextExtensions.cs +++ b/src/Microsoft.OpenApi/Validations/ValidationContextExtensions.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using System.Collections.Generic; using Microsoft.OpenApi.Interfaces; diff --git a/src/Microsoft.OpenApi/Validations/ValidationError.cs b/src/Microsoft.OpenApi/Validations/ValidationError.cs index 2356e7720..8fe51cb68 100644 --- a/src/Microsoft.OpenApi/Validations/ValidationError.cs +++ b/src/Microsoft.OpenApi/Validations/ValidationError.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. namespace Microsoft.OpenApi.Validations { @@ -13,24 +11,12 @@ public enum ErrorReason /// /// Field is required. /// - FieldIsRequired, + Required, /// /// Format error. /// Format, - - /// - /// Must be email format. - /// - EmailFormat, - - /// - /// Must be Url format. - /// - UrlFormat, - - // Add more. } /// diff --git a/src/Microsoft.OpenApi/Validations/Rules/ValidationRule.cs b/src/Microsoft.OpenApi/Validations/ValidationRule.cs similarity index 66% rename from src/Microsoft.OpenApi/Validations/Rules/ValidationRule.cs rename to src/Microsoft.OpenApi/Validations/ValidationRule.cs index d9575969e..363a1b433 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/ValidationRule.cs +++ b/src/Microsoft.OpenApi/Validations/ValidationRule.cs @@ -1,4 +1,6 @@ - +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + using System; using System.Diagnostics; using Microsoft.OpenApi.Interfaces; @@ -10,8 +12,16 @@ namespace Microsoft.OpenApi.Validations.Rules /// public abstract class ValidationRule { - internal abstract Type ValidatedType { get; } + /// + /// Element Type. + /// + internal abstract Type ElementType { get; } + /// + /// Validate the object. + /// + /// The context. + /// The object item. internal abstract void Evaluate(ValidationContext context, object item); } @@ -19,8 +29,7 @@ public abstract class ValidationRule /// Class containing validation rule logic for . /// /// - public class ValidationRule : ValidationRule - where T: IOpenApiElement + public class ValidationRule : ValidationRule where T: IOpenApiElement { private readonly Action _validate; @@ -30,10 +39,10 @@ public class ValidationRule : ValidationRule /// Action to perform the validation. public ValidationRule(Action validate) { - this._validate = validate; + _validate = validate ?? throw Error.ArgumentNull(nameof(validate)); } - internal override Type ValidatedType + internal override Type ElementType { get { return typeof(T); } } diff --git a/src/Microsoft.OpenApi/Validations/Rules/ValidationRuleSet.cs b/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs similarity index 75% rename from src/Microsoft.OpenApi/Validations/Rules/ValidationRuleSet.cs rename to src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs index d85c0c201..15ccaf81a 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/ValidationRuleSet.cs +++ b/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs @@ -1,18 +1,17 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using System; using System.Linq; using System.Collections; using System.Collections.Generic; using Microsoft.OpenApi.Exceptions; +using Microsoft.OpenApi.Properties; namespace Microsoft.OpenApi.Validations.Rules { /// - /// + /// The rule set of the validation. /// public sealed class ValidationRuleSet : IEnumerable { @@ -23,13 +22,25 @@ public sealed class ValidationRuleSet : IEnumerable /// public static ValidationRuleSet DefaultRuleSet = new ValidationRuleSet { - OpenApiDocumentRules.InfoIsRequired, - OpenApiDocumentRules.PathsIsRequired, + OpenApiComponentsRules.KeyMustBeRegularExpression, - OpenApiInfoRules.TitleIsRequired, + OpenApiDocumentRules.FieldIsRequired, + + OpenApiInfoRules.FieldIsRequired, + + OpenApiLicenseRules.FieldIsRequired, OpenApiContactRules.EmailMustBeEmailFormat, + OpenApiExternalDocsRules.FieldIsRequired, + + OpenApiServerRules.FieldIsRequired, + + OpenApiTagRules.FieldIsRequired, + + OpenApiResponseRules.FieldIsRequired, + + OpenApiExtensibleRules.ExtensionNameMustStartWithXDash, // add more default rules. }; @@ -73,15 +84,15 @@ public IEnumerable Rules public void Add(ValidationRule rule) { IList typeRules; - if (!_rules.TryGetValue(rule.ValidatedType, out typeRules)) + if (!_rules.TryGetValue(rule.ElementType, out typeRules)) { typeRules = new List(); - _rules[rule.ValidatedType] = typeRules; + _rules[rule.ElementType] = typeRules; } if (typeRules.Contains(rule)) { - throw new OpenApiException("The same rule cannot be in the same rule set twice."); + throw new OpenApiException(SRResource.Validation_RuleAddTwice); } typeRules.Add(rule); diff --git a/src/Microsoft.OpenApi/Validations/Visitors/CallbackVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/CallbackVisitor.cs index cbe023917..dc368d427 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/CallbackVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/CallbackVisitor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using System.Diagnostics; using Microsoft.OpenApi.Models; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/ComponentsVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/ComponentsVisitor.cs index 3af0a6016..d3b8f995c 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/ComponentsVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/ComponentsVisitor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using System.Diagnostics; using Microsoft.OpenApi.Models; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/ContactVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/ContactVisitor.cs index d53dba4c2..bc22aab7b 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/ContactVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/ContactVisitor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using Microsoft.OpenApi.Models; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/DiscriminatorVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/DiscriminatorVisitor.cs index 71e00f478..24657ed94 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/DiscriminatorVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/DiscriminatorVisitor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using Microsoft.OpenApi.Models; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/DocumentVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/DocumentVisitor.cs index 032617ea5..0e56ecd98 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/DocumentVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/DocumentVisitor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using Microsoft.OpenApi.Models; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/EncodingVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/EncodingVisitor.cs index cb6ec2db2..5ecd8e7f1 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/EncodingVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/EncodingVisitor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using System.Diagnostics; using Microsoft.OpenApi.Models; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/ExampleVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/ExampleVisitor.cs index 8c9f19565..4dbe97d89 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/ExampleVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/ExampleVisitor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using Microsoft.OpenApi.Models; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/ExternalDocsVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/ExternalDocsVisitor.cs index 15b24e57d..279fd9310 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/ExternalDocsVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/ExternalDocsVisitor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using Microsoft.OpenApi.Models; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/HeaderVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/HeaderVisitor.cs index 2b2222237..f601d69bb 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/HeaderVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/HeaderVisitor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using System.Diagnostics; using Microsoft.OpenApi.Models; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/IVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/IVisitor.cs index 60374d19c..df61d1563 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/IVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/IVisitor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. namespace Microsoft.OpenApi.Validations.Visitors { diff --git a/src/Microsoft.OpenApi/Validations/Visitors/InfoVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/InfoVisitor.cs index 2c2ca8e5c..2075dd68b 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/InfoVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/InfoVisitor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using System.Diagnostics; using Microsoft.OpenApi.Models; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/LicenseVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/LicenseVisitor.cs index cdb926107..2d9867830 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/LicenseVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/LicenseVisitor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using Microsoft.OpenApi.Models; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/LinkVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/LinkVisitor.cs index eb92c3483..f3659fb8f 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/LinkVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/LinkVisitor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using System.Diagnostics; using Microsoft.OpenApi.Models; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/MediaTypeVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/MediaTypeVisitor.cs index 6fb2897de..50a4507be 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/MediaTypeVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/MediaTypeVisitor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using System.Diagnostics; using Microsoft.OpenApi.Models; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/OAuthFlowVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/OAuthFlowVisitor.cs index e96364e20..f2faa565e 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/OAuthFlowVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/OAuthFlowVisitor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using Microsoft.OpenApi.Models; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/OAuthFlowsVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/OAuthFlowsVisitor.cs index a5baacd25..80cf8be13 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/OAuthFlowsVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/OAuthFlowsVisitor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using System.Diagnostics; using Microsoft.OpenApi.Models; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/OpenApiVisitorSet.cs b/src/Microsoft.OpenApi/Validations/Visitors/OpenApiVisitorSet.cs index e79af1058..17afe37b2 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/OpenApiVisitorSet.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/OpenApiVisitorSet.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using System; using System.Collections.Generic; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/OperationVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/OperationVisitor.cs index f0a897d86..3b07a3d25 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/OperationVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/OperationVisitor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using System.Diagnostics; using Microsoft.OpenApi.Models; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/ParameterVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/ParameterVisitor.cs index cf9d1d9f0..b8bbbb330 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/ParameterVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/ParameterVisitor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using System.Diagnostics; using Microsoft.OpenApi.Models; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/PathItemVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/PathItemVisitor.cs index f1f6c97a0..93d359b19 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/PathItemVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/PathItemVisitor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using System.Diagnostics; using Microsoft.OpenApi.Models; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/PathsVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/PathsVisitor.cs index a31d1c7f8..7849e37ec 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/PathsVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/PathsVisitor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using System.Diagnostics; using Microsoft.OpenApi.Models; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/RequestBodyVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/RequestBodyVisitor.cs index 4653a42e7..abf3ba34a 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/RequestBodyVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/RequestBodyVisitor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using System.Diagnostics; using Microsoft.OpenApi.Models; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/ResponseVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/ResponseVisitor.cs index 630d1a1ee..3849f44ef 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/ResponseVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/ResponseVisitor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using System.Diagnostics; using Microsoft.OpenApi.Models; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/ResponsesVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/ResponsesVisitor.cs index 8a933d9de..f71cfecc3 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/ResponsesVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/ResponsesVisitor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using System.Diagnostics; using Microsoft.OpenApi.Models; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/SchemaVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/SchemaVisitor.cs index d536d55f4..56111c95e 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/SchemaVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/SchemaVisitor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using System.Diagnostics; using Microsoft.OpenApi.Models; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/SecurityRequirementVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/SecurityRequirementVisitor.cs index c2d8670cc..23cbb1a34 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/SecurityRequirementVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/SecurityRequirementVisitor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using Microsoft.OpenApi.Models; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/SecuritySchemeVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/SecuritySchemeVisitor.cs index 377bd11db..f51ff473f 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/SecuritySchemeVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/SecuritySchemeVisitor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using System.Diagnostics; using Microsoft.OpenApi.Models; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/ServerVariableVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/ServerVariableVisitor.cs index 67948085f..5033f274d 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/ServerVariableVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/ServerVariableVisitor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using Microsoft.OpenApi.Models; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/ServerVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/ServerVisitor.cs index 6e3538ef3..62b67a77c 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/ServerVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/ServerVisitor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using System.Diagnostics; using Microsoft.OpenApi.Models; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/TagVistor.cs b/src/Microsoft.OpenApi/Validations/Visitors/TagVistor.cs index e011462ce..80ccaa523 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/TagVistor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/TagVistor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using System.Diagnostics; using Microsoft.OpenApi.Models; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/VisitorBase.cs b/src/Microsoft.OpenApi/Validations/Visitors/VisitorBase.cs index d22ee7854..ec9f880da 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/VisitorBase.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/VisitorBase.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using System.Diagnostics; using System.Linq; @@ -23,7 +21,7 @@ public void Visit(ValidationContext context, object item) { Debug.Assert(item is T, "item should be " + typeof(T)); - var rules = context.RuleSet.Where(r => r.ValidatedType == typeof(T)); + var rules = context.RuleSet.Where(r => r.ElementType == typeof(T)); foreach (var rule in rules) { rule.Evaluate(context, item); @@ -43,7 +41,7 @@ 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)); + var rules = context.RuleSet.Where(r => r.ElementType == typeof(IOpenApiExtensible)); foreach (var rule in rules) { rule.Evaluate(context, extensbile); diff --git a/src/Microsoft.OpenApi/Validations/Visitors/XmlVistor.cs b/src/Microsoft.OpenApi/Validations/Visitors/XmlVistor.cs index a915cd015..7a11486ac 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/XmlVistor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/XmlVistor.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using Microsoft.OpenApi.Models; diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiComponentsValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiComponentsValidationTests.cs new file mode 100644 index 000000000..6c82554fc --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiComponentsValidationTests.cs @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System.Collections.Generic; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Validations; +using Xunit; + +namespace Microsoft.OpenApi.Tests.Models +{ + public class OpenApiComponentsValidationTests + { + [Fact] + public void ValidateKeyMustMatchRegularExpressionInComponents() + { + // Arrange + IEnumerable errors; + OpenApiComponents components = new OpenApiComponents() + { + Responses = new Dictionary + { + { "%@abc", new OpenApiResponse { Description = "any" } } + } + }; + + // Act + bool result = components.Validate(out errors); + + // Assert + Assert.False(result); + Assert.NotNull(errors); + ValidationError error = Assert.Single(errors); + Assert.Equal(@"The key '%@abc' in 'responses' of components MUST match the regular expression '^[a-zA-Z0-9\.\-_]+$'.", + error.ErrorMessage); + } + } +} diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs index ad2406d86..00b12a1e6 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs @@ -1,7 +1,5 @@ -// ------------------------------------------------------------ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License (MIT). See LICENSE in the repo root for license information. -// ------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Validations; @@ -12,16 +10,15 @@ namespace Microsoft.OpenApi.Tests.Models { public class OpenApiContactValidationTests { - public static OpenApiContact Contact = new OpenApiContact() - { - Email = "support/example.com", - }; - [Fact] - public void SerializeAdvanceContactAsJsonWorks() + public void ValidateEmailFieldIsEmailAddressInContact() { // Arrange IEnumerable errors; + OpenApiContact Contact = new OpenApiContact() + { + Email = "support/example.com", + }; // Act bool result = Contact.Validate(out errors); diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiInfoValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiInfoValidationTests.cs new file mode 100644 index 000000000..7a6bbbac6 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiInfoValidationTests.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +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..b7bfc8dd9 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiLicenseValidationTests.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +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..0dc0ba900 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiServerValidationTests.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +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..cdcf3f2f0 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiTagValidationTests.cs @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System.Collections.Generic; +using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Validations; +using Xunit; + +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); + } + } +} From 31a3cd9b71e3407d46842ea1a8376d04961ef8ba Mon Sep 17 00:00:00 2001 From: Sam Xu Date: Fri, 15 Dec 2017 15:09:06 -0800 Subject: [PATCH 04/12] Add test cases for external docs and response --- .../OpenApiExternalDocsValidationTests.cs | 30 +++++++++++++++++++ .../OpenApiResponseValidationTests.cs | 30 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 test/Microsoft.OpenApi.Tests/Validations/OpenApiExternalDocsValidationTests.cs create mode 100644 test/Microsoft.OpenApi.Tests/Validations/OpenApiResponseValidationTests.cs diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiExternalDocsValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiExternalDocsValidationTests.cs new file mode 100644 index 000000000..0a285571c --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiExternalDocsValidationTests.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System.Collections.Generic; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Validations; +using Xunit; + +namespace Microsoft.OpenApi.Tests.Models +{ + public class OpenApiExternalDocsValidationTests + { + [Fact] + public void ValidateUrlIsRequiredInExternalDocs() + { + // Arrange + IEnumerable errors; + OpenApiExternalDocs externalDocs = new OpenApiExternalDocs(); + + // Act + bool result = externalDocs.Validate(out errors); + + // Assert + Assert.False(result); + Assert.NotNull(errors); + ValidationError error = Assert.Single(errors); + Assert.Equal("The field 'url' in 'External Documentation' object is REQUIRED.", error.ErrorMessage); + } + } +} diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiResponseValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiResponseValidationTests.cs new file mode 100644 index 000000000..404bba49b --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiResponseValidationTests.cs @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System.Collections.Generic; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Validations; +using Xunit; + +namespace Microsoft.OpenApi.Tests.Models +{ + public class OpenApiResponseValidationTests + { + [Fact] + public void ValidateDescriptionIsRequiredInResponse() + { + // Arrange + IEnumerable errors; + OpenApiResponse response = new OpenApiResponse(); + + // Act + bool result = response.Validate(out errors); + + // Assert + Assert.False(result); + Assert.NotNull(errors); + ValidationError error = Assert.Single(errors); + Assert.Equal("The field 'description' in 'response' object is REQUIRED.", error.ErrorMessage); + } + } +} From 401072cda11eff90ae1f9c06577cf32d5c02904d Mon Sep 17 00:00:00 2001 From: Sam Xu Date: Fri, 15 Dec 2017 15:19:35 -0800 Subject: [PATCH 05/12] add a validation rule for the OAuthFlow object --- .../Rules/OpenApiOAuthFlowRules.cs | 55 +++++++++++++++++++ .../Validations/ValidationRuleSet.cs | 2 + .../OpenApiOAuthFlowValidationTests.cs | 32 +++++++++++ 3 files changed, 89 insertions(+) create mode 100644 src/Microsoft.OpenApi/Validations/Rules/OpenApiOAuthFlowRules.cs create mode 100644 test/Microsoft.OpenApi.Tests/Validations/OpenApiOAuthFlowValidationTests.cs diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiOAuthFlowRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiOAuthFlowRules.cs new file mode 100644 index 000000000..009610d65 --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiOAuthFlowRules.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; + +namespace Microsoft.OpenApi.Validations.Rules +{ + /// + /// The validation rules for . + /// + internal static class OpenApiOAuthFlowRules + { + /// + /// Validate the field is required. + /// + public static readonly ValidationRule FieldIsRequired = + new ValidationRule( + (context, flow) => + { + // authorizationUrl + context.Push("authorizationUrl"); + if (flow.AuthorizationUrl == null) + { + ValidationError error = new ValidationError(ErrorReason.Required, context.PathString, + String.Format(SRResource.Validation_FieldIsRequired, "authorizationUrl", "OAuth Flow")); + context.AddError(error); + } + context.Pop(); + + // tokenUrl + context.Push("tokenUrl"); + if (flow.TokenUrl == null) + { + ValidationError error = new ValidationError(ErrorReason.Required, context.PathString, + String.Format(SRResource.Validation_FieldIsRequired, "tokenUrl", "OAuth Flow")); + context.AddError(error); + } + context.Pop(); + + // scopes + context.Push("scopes"); + if (flow.Scopes == null) + { + ValidationError error = new ValidationError(ErrorReason.Required, context.PathString, + String.Format(SRResource.Validation_FieldIsRequired, "scopes", "OAuth Flow")); + context.AddError(error); + } + context.Pop(); + }); + + // add more rule. + } +} diff --git a/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs b/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs index 15ccaf81a..5287552c7 100644 --- a/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs +++ b/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs @@ -40,6 +40,8 @@ public sealed class ValidationRuleSet : IEnumerable OpenApiResponseRules.FieldIsRequired, + OpenApiOAuthFlowRules.FieldIsRequired, + OpenApiExtensibleRules.ExtensionNameMustStartWithXDash, // add more default rules. }; diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiOAuthFlowValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiOAuthFlowValidationTests.cs new file mode 100644 index 000000000..52ca36ee7 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiOAuthFlowValidationTests.cs @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System.Collections.Generic; +using System.Linq; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Validations; +using Xunit; + +namespace Microsoft.OpenApi.Tests.Models +{ + public class OpenApiOAuthFlowValidationTests + { + [Fact] + public void ValidateFixedFieldsIsRequiredInResponse() + { + // Arrange + IEnumerable errors; + OpenApiOAuthFlow oAuthFlow = new OpenApiOAuthFlow(); + + // Act + bool result = oAuthFlow.Validate(out errors); + + // Assert + Assert.False(result); + Assert.NotNull(errors); + Assert.Equal(2, errors.Count()); + Assert.Equal(new[] { "The field 'authorizationUrl' in 'OAuth Flow' object is REQUIRED.", + "The field 'tokenUrl' in 'OAuth Flow' object is REQUIRED."}, errors.Select(e => e.ErrorMessage)); + } + } +} From bd39460f63368d68e8a567a4bd5792f14812d8ae Mon Sep 17 00:00:00 2001 From: Sam Xu Date: Fri, 15 Dec 2017 17:18:35 -0800 Subject: [PATCH 06/12] modify to give more meaningful comments --- src/Microsoft.OpenApi/Validations/OpenApiElementValidator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Microsoft.OpenApi/Validations/OpenApiElementValidator.cs b/src/Microsoft.OpenApi/Validations/OpenApiElementValidator.cs index 917f1edce..de38e15d5 100644 --- a/src/Microsoft.OpenApi/Validations/OpenApiElementValidator.cs +++ b/src/Microsoft.OpenApi/Validations/OpenApiElementValidator.cs @@ -26,7 +26,7 @@ public static bool Validate(this T element) where T : IOpenApiElement } /// - /// Validate the Open API element. + /// Validate the Open API element and output the whole validation errors. /// /// The Open API element type. /// The Open API element. @@ -40,7 +40,7 @@ public static bool Validate(this T element, out IEnumerable } /// - /// Validate the Open API element. + /// Validate the Open API element with the given rule set and output the whole validation errors. /// /// The Open API element type. /// The Open API element. From da5f90a05a0c3c931cb8ad214c2dda9b0191aee3 Mon Sep 17 00:00:00 2001 From: Sam Xu Date: Mon, 18 Dec 2017 14:17:41 -0800 Subject: [PATCH 07/12] add the OpenApiRuleAttribute for each rule classes --- .../Rules/OpenApiComponentsRules.cs | 3 +- .../Validations/Rules/OpenApiContactRules.cs | 5 +- .../Validations/Rules/OpenApiDocumentRules.cs | 3 +- .../Rules/OpenApiExtensionRules.cs | 3 +- .../Rules/OpenApiExternalDocsRules.cs | 3 +- .../Validations/Rules/OpenApiInfoRules.cs | 3 +- .../Validations/Rules/OpenApiLicenseRules.cs | 3 +- .../Rules/OpenApiOAuthFlowRules.cs | 3 +- .../Validations/Rules/OpenApiPathsRules.cs | 3 +- .../Validations/Rules/OpenApiResponseRules.cs | 3 +- .../Validations/Rules/OpenApiRuleAttribute.cs | 15 ++++ .../Validations/Rules/OpenApiServerRules.cs | 3 +- .../Validations/Rules/OpenApiTagRules.cs | 3 +- .../Validations/ValidationRuleSet.cs | 69 ++++++++++++------- 14 files changed, 85 insertions(+), 37 deletions(-) create mode 100644 src/Microsoft.OpenApi/Validations/Rules/OpenApiRuleAttribute.cs diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiComponentsRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiComponentsRules.cs index 84d3bba1e..11f0f7ada 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiComponentsRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiComponentsRules.cs @@ -11,6 +11,7 @@ namespace Microsoft.OpenApi.Validations.Rules /// /// The validation rules for . /// + [OpenApiRule] public static class OpenApiComponentsRules { /// @@ -22,7 +23,7 @@ public static class OpenApiComponentsRules /// 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 = + public static ValidationRule KeyMustBeRegularExpression => new ValidationRule( (context, components) => { diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiContactRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiContactRules.cs index ed79b13ac..e4a239198 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiContactRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiContactRules.cs @@ -10,12 +10,13 @@ namespace Microsoft.OpenApi.Validations.Rules /// /// The validation rules for . /// + [OpenApiRule] internal static class OpenApiContactRules { /// /// Email field MUST be email address. /// - public static readonly ValidationRule EmailMustBeEmailFormat = + public static ValidationRule EmailMustBeEmailFormat => new ValidationRule( (context, item) => { @@ -35,7 +36,7 @@ internal static class OpenApiContactRules /// /// Url field MUST be url format. /// - public static readonly ValidationRule UrlMustBeUrlFormat = + public static ValidationRule UrlMustBeUrlFormat => new ValidationRule( (context, item) => { diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs index 1b2314b39..030410123 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiDocumentRules.cs @@ -10,12 +10,13 @@ namespace Microsoft.OpenApi.Validations.Rules /// /// The validation rules for . /// + [OpenApiRule] internal static class OpenApiDocumentRules { /// /// The Info field is required. /// - public static readonly ValidationRule FieldIsRequired = + public static ValidationRule FieldIsRequired => new ValidationRule( (context, item) => { diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiExtensionRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiExtensionRules.cs index 7888e35c0..db2a9711a 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiExtensionRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiExtensionRules.cs @@ -10,12 +10,13 @@ namespace Microsoft.OpenApi.Validations.Rules /// /// The validation rules for . /// + [OpenApiRule] public static class OpenApiExtensibleRules { /// /// Extension name MUST start with "x-". /// - public static readonly ValidationRule ExtensionNameMustStartWithXDash = + public static ValidationRule ExtensionNameMustStartWithXDash => new ValidationRule( (context, item) => { diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiExternalDocsRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiExternalDocsRules.cs index e30f31ade..fe88a8e0d 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiExternalDocsRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiExternalDocsRules.cs @@ -10,12 +10,13 @@ namespace Microsoft.OpenApi.Validations.Rules /// /// The validation rules for . /// + [OpenApiRule] internal static class OpenApiExternalDocsRules { /// /// Validate the field is required. /// - public static readonly ValidationRule FieldIsRequired = + public static ValidationRule FieldIsRequired => new ValidationRule( (context, item) => { diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiInfoRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiInfoRules.cs index d35af1a86..d77f28898 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiInfoRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiInfoRules.cs @@ -10,12 +10,13 @@ namespace Microsoft.OpenApi.Validations.Rules /// /// The validation rules for . /// + [OpenApiRule] internal static class OpenApiInfoRules { /// /// Validate the field is required. /// - public static readonly ValidationRule FieldIsRequired = + public static ValidationRule FieldIsRequired => new ValidationRule( (context, item) => { diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiLicenseRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiLicenseRules.cs index ec11e6145..4f17b4753 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiLicenseRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiLicenseRules.cs @@ -10,12 +10,13 @@ namespace Microsoft.OpenApi.Validations.Rules /// /// The validation rules for . /// + [OpenApiRule] public static class OpenApiLicenseRules { /// /// REQUIRED. /// - public static readonly ValidationRule FieldIsRequired = + public static ValidationRule FieldIsRequired => new ValidationRule( (context, license) => { diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiOAuthFlowRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiOAuthFlowRules.cs index 009610d65..dca45f890 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiOAuthFlowRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiOAuthFlowRules.cs @@ -10,12 +10,13 @@ namespace Microsoft.OpenApi.Validations.Rules /// /// The validation rules for . /// + [OpenApiRule] internal static class OpenApiOAuthFlowRules { /// /// Validate the field is required. /// - public static readonly ValidationRule FieldIsRequired = + public static ValidationRule FieldIsRequired => new ValidationRule( (context, flow) => { diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs index 90223d21c..79c81191a 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs @@ -9,12 +9,13 @@ namespace Microsoft.OpenApi.Validations.Rules /// /// The validation rules for . /// + [OpenApiRule] public static class OpenApiPathsRules { /// /// A relative path to an individual endpoint. The field name MUST begin with a slash. /// - public static readonly ValidationRule PathNameMustBeginWithSlash = + public static ValidationRule PathNameMustBeginWithSlash => new ValidationRule( (context, item) => { diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiResponseRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiResponseRules.cs index 18a1155d4..34587dcfd 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiResponseRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiResponseRules.cs @@ -10,12 +10,13 @@ namespace Microsoft.OpenApi.Validations.Rules /// /// The validation rules for . /// + [OpenApiRule] internal static class OpenApiResponseRules { /// /// Validate the field is required. /// - public static readonly ValidationRule FieldIsRequired = + public static ValidationRule FieldIsRequired => new ValidationRule( (context, response) => { diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiRuleAttribute.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiRuleAttribute.cs new file mode 100644 index 000000000..a9bc65bad --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiRuleAttribute.cs @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System; + +namespace Microsoft.OpenApi.Validations.Rules +{ + /// + /// The Validator attribute. + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)] + internal class OpenApiRuleAttribute : Attribute + { + } +} diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiServerRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiServerRules.cs index be3328255..1d6cee524 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiServerRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiServerRules.cs @@ -10,12 +10,13 @@ namespace Microsoft.OpenApi.Validations.Rules /// /// The validation rules for . /// + [OpenApiRule] public static class OpenApiServerRules { /// /// REQUIRED. /// - public static readonly ValidationRule FieldIsRequired = + public static ValidationRule FieldIsRequired => new ValidationRule( (context, server) => { diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiTagRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiTagRules.cs index d1af4ab5c..1ef4e3289 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiTagRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiTagRules.cs @@ -10,12 +10,13 @@ namespace Microsoft.OpenApi.Validations.Rules /// /// The validation rules for . /// + [OpenApiRule] public static class OpenApiTagRules { /// /// REQUIRED. /// - public static readonly ValidationRule FieldIsRequired = + public static ValidationRule FieldIsRequired => new ValidationRule( (context, tag) => { diff --git a/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs b/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs index 5287552c7..81ca56af4 100644 --- a/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs +++ b/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using System.Reflection; using System.Collections; using System.Collections.Generic; using Microsoft.OpenApi.Exceptions; @@ -17,34 +18,23 @@ public sealed class ValidationRuleSet : IEnumerable { private IDictionary> _rules = new Dictionary>(); + private static ValidationRuleSet _defaultRuleSet; + /// - /// The default rule set. + /// Gets the default validation rule sets. /// - public static ValidationRuleSet DefaultRuleSet = new ValidationRuleSet + public static ValidationRuleSet DefaultRuleSet { - OpenApiComponentsRules.KeyMustBeRegularExpression, - - OpenApiDocumentRules.FieldIsRequired, - - OpenApiInfoRules.FieldIsRequired, - - OpenApiLicenseRules.FieldIsRequired, - - OpenApiContactRules.EmailMustBeEmailFormat, - - OpenApiExternalDocsRules.FieldIsRequired, - - OpenApiServerRules.FieldIsRequired, - - OpenApiTagRules.FieldIsRequired, - - OpenApiResponseRules.FieldIsRequired, - - OpenApiOAuthFlowRules.FieldIsRequired, + get + { + if (_defaultRuleSet == null) + { + _defaultRuleSet = new Lazy(() => BuildDefaultRuleSet(), isThreadSafe: false).Value; + } - OpenApiExtensibleRules.ExtensionNameMustStartWithXDash, - // add more default rules. - }; + return _defaultRuleSet; + } + } /// /// Initializes a new instance of the class. @@ -123,5 +113,36 @@ IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } + + private static ValidationRuleSet BuildDefaultRuleSet() + { + ValidationRuleSet ruleSet = new ValidationRuleSet(); + + IEnumerable allTypes = typeof(ValidationRuleSet).Assembly.GetTypes().Where(t => t.IsClass && t != typeof(object)); + Type validationRuleType = typeof(ValidationRule); + foreach (Type type in allTypes) + { + if (!type.GetCustomAttributes(typeof(OpenApiRuleAttribute), false).Any()) + { + continue; + } + + var properties = type.GetProperties(BindingFlags.Static | BindingFlags.Public); + foreach (var property in properties) + { + if (validationRuleType.IsAssignableFrom(property.PropertyType)) + { + var propertyValue = property.GetValue(null); // static property + ValidationRule rule = propertyValue as ValidationRule; + if (rule != null) + { + ruleSet.Add(rule); + } + } + } + } + + return ruleSet; + } } } From 4a6ca43701b589a359794a9fa99e3ff6e1c0d33a Mon Sep 17 00:00:00 2001 From: Sam Xu Date: Mon, 18 Dec 2017 15:00:22 -0800 Subject: [PATCH 08/12] change to lazy loading for the validation visitors --- .../Validations/Visitors/OpenApiVisitorSet.cs | 89 ++++++++++++------- .../Visitors/OpenApiVisitorSetTests.cs | 55 ++++++++++++ 2 files changed, 112 insertions(+), 32 deletions(-) create mode 100644 test/Microsoft.OpenApi.Tests/Validations/Visitors/OpenApiVisitorSetTests.cs diff --git a/src/Microsoft.OpenApi/Validations/Visitors/OpenApiVisitorSet.cs b/src/Microsoft.OpenApi/Validations/Visitors/OpenApiVisitorSet.cs index 17afe37b2..2cde35fef 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/OpenApiVisitorSet.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/OpenApiVisitorSet.cs @@ -3,9 +3,11 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.OpenApi.Exceptions; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Properties; +using Microsoft.OpenApi.Interfaces; namespace Microsoft.OpenApi.Validations.Visitors { @@ -14,39 +16,23 @@ namespace Microsoft.OpenApi.Validations.Visitors /// internal static class OpenApiVisitorSet { - private static IDictionary _visitorCache = new Dictionary + private static IDictionary _visitors; + + /// + /// Gets the visitors + /// + public static IDictionary Visitors { - { typeof(OpenApiCallback), new CallbackVisitor() }, - { typeof(OpenApiComponents), new ComponentsVisitor() }, - { typeof(OpenApiContact), new ContactVisitor() }, - { typeof(OpenApiDiscriminator), new DiscriminatorVisitor() }, - { typeof(OpenApiDocument), new DocumentVisitor() }, - { typeof(OpenApiEncoding), new EncodingVisitor() }, - { typeof(OpenApiExample), new ExampleVisitor() }, - { typeof(OpenApiExternalDocs), new ExternalDocsVisitor() }, - { typeof(OpenApiHeader), new HeaderVisitor() }, - { typeof(OpenApiInfo), new InfoVisitor() }, - { typeof(OpenApiLicense), new LicenseVisitor() }, - { typeof(OpenApiLink), new LinkVisitor() }, - { typeof(OpenApiMediaType), new MediaTypeVisitor() }, - { typeof(OpenApiOAuthFlows), new OAuthFlowsVisitor() }, - { typeof(OpenApiOAuthFlow), new OAuthFlowVisitor() }, - { typeof(OpenApiOperation), new OperationVisitor() }, - { typeof(OpenApiParameter), new ParameterVisitor() }, - { typeof(OpenApiPathItem), new PathItemVisitor() }, - { typeof(OpenApiPaths), new PathsVisitor() }, - { typeof(OpenApiRequestBody), new RequestBodyVisitor() }, - { typeof(OpenApiResponses), new ResponsesVisitor() }, - { typeof(OpenApiResponse), new ResponseVisitor() }, - { typeof(OpenApiSchema), new SchemaVisitor() }, - { typeof(OpenApiSecurityRequirement), new SecurityRequirementVisitor() }, - { typeof(OpenApiSecurityScheme), new SecuritySchemeVisitor() }, - { typeof(OpenApiServerVariable), new ServerVariableVisitor() }, - { typeof(OpenApiServer), new ServerVisitor() }, - { typeof(OpenApiTag), new TagVisitor() }, - { typeof(OpenApiXml), new XmlVisitor() } - }; + get + { + if (_visitors == null) + { + _visitors = new Lazy>(() => BuildVisitorSet(), isThreadSafe: false).Value; + } + return _visitors; + } + } /// /// Get the element visitor. /// @@ -55,12 +41,51 @@ internal static class OpenApiVisitorSet public static IVisitor GetVisitor(Type elementType) { IVisitor visitor; - if (_visitorCache.TryGetValue(elementType, out visitor)) + if (Visitors.TryGetValue(elementType, out visitor)) { return visitor; } throw new OpenApiException(String.Format(SRResource.UnknownVisitorType, elementType.FullName)); } + + private static IDictionary BuildVisitorSet() + { + IDictionary visitors = new Dictionary(); + + IEnumerable allTypes = typeof(OpenApiVisitorSet).Assembly.GetTypes().Where(t => t.IsClass && !t.IsAbstract && t != typeof(object)); + + Type visitorInterfaceType = typeof(IVisitor); + Type elementType = typeof(IOpenApiElement); + foreach (Type type in allTypes) + { + if (!visitorInterfaceType.IsAssignableFrom(type)) + { + continue; + } + + Type baseType = type.BaseType; + if (baseType == null || !baseType.IsGenericType || + baseType.GetGenericTypeDefinition() != typeof(VisitorBase<>)) + { + continue; + } + + Type validationType = baseType.GetGenericArguments()[0]; + if (!elementType.IsAssignableFrom(validationType)) + { + continue; + } + + object instance = Activator.CreateInstance(type); + IVisitor visitor = instance as IVisitor; + if (visitor != null) + { + visitors[validationType] = visitor; + } + } + + return visitors; + } } } diff --git a/test/Microsoft.OpenApi.Tests/Validations/Visitors/OpenApiVisitorSetTests.cs b/test/Microsoft.OpenApi.Tests/Validations/Visitors/OpenApiVisitorSetTests.cs new file mode 100644 index 000000000..d7705b5c6 --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Validations/Visitors/OpenApiVisitorSetTests.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System; +using Microsoft.OpenApi.Exceptions; +using Microsoft.OpenApi.Models; +using Xunit; + +namespace Microsoft.OpenApi.Validations.Visitors.Tests +{ + public class OpenApiVisitorSetTests + { + [Fact] + public void VisitorsPropertyReturnsTheCorrectVisitorList() + { + for (int i = 0; i < 5; i++) // 5 is just a magic number + { + // Arrange & Act + var visitors = OpenApiVisitorSet.Visitors; + + // Assert + Assert.NotNull(visitors); + Assert.NotEmpty(visitors); + Assert.Equal(29, visitors.Count); + } + } + + [Fact] + public void GetVisitorThrowsUnknowElementType() + { + // Arrange & Act + Action test = () => OpenApiVisitorSet.GetVisitor(typeof(OpenApiVisitorSetTests)); + + // Assert + var exception = Assert.Throws(test); + Assert.Equal("Can not find visitor type registered for type 'Microsoft.OpenApi.Validations.Visitors.Tests.OpenApiVisitorSetTests'.", + exception.Message); + } + + [Theory] + [InlineData(typeof(OpenApiDocument), typeof(DocumentVisitor))] + [InlineData(typeof(OpenApiInfo), typeof(InfoVisitor))] + [InlineData(typeof(OpenApiXml), typeof(XmlVisitor))] + [InlineData(typeof(OpenApiComponents), typeof(ComponentsVisitor))] + public void GetVisitorReturnsTheCorrectVisitor(Type elementType, Type visitorType) + { + // Arrange & Act + var visitor = OpenApiVisitorSet.GetVisitor(elementType); + + // Assert + Assert.NotNull(visitor); + Assert.Same(visitorType, visitor.GetType()); + } + } +} From 2e7e2dd62c9b81bfd813ce46d817fd0219af7826 Mon Sep 17 00:00:00 2001 From: Sam Xu Date: Mon, 18 Dec 2017 15:06:36 -0800 Subject: [PATCH 09/12] Change the namespace for the Validation test files --- .../Validations/OpenApiComponentsValidationTests.cs | 2 +- .../Validations/OpenApiContactValidationTests.cs | 3 +-- .../Validations/OpenApiExternalDocsValidationTests.cs | 3 +-- .../Validations/OpenApiInfoValidationTests.cs | 3 +-- .../Validations/OpenApiLicenseValidationTests.cs | 3 +-- .../Validations/OpenApiOAuthFlowValidationTests.cs | 3 +-- .../Validations/OpenApiResponseValidationTests.cs | 3 +-- .../Validations/OpenApiServerValidationTests.cs | 3 +-- .../Validations/OpenApiTagValidationTests.cs | 3 +-- 9 files changed, 9 insertions(+), 17 deletions(-) diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiComponentsValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiComponentsValidationTests.cs index 6c82554fc..668b8530c 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiComponentsValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiComponentsValidationTests.cs @@ -6,7 +6,7 @@ using Microsoft.OpenApi.Validations; using Xunit; -namespace Microsoft.OpenApi.Tests.Models +namespace Microsoft.OpenApi.Validations.Tests { public class OpenApiComponentsValidationTests { diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs index 00b12a1e6..5e7c59cd0 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs @@ -2,11 +2,10 @@ // Licensed under the MIT license. using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Validations; using System.Collections.Generic; using Xunit; -namespace Microsoft.OpenApi.Tests.Models +namespace Microsoft.OpenApi.Validations.Tests { public class OpenApiContactValidationTests { diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiExternalDocsValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiExternalDocsValidationTests.cs index 0a285571c..1f3b69a49 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiExternalDocsValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiExternalDocsValidationTests.cs @@ -3,10 +3,9 @@ using System.Collections.Generic; using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Validations; using Xunit; -namespace Microsoft.OpenApi.Tests.Models +namespace Microsoft.OpenApi.Validations.Tests { public class OpenApiExternalDocsValidationTests { diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiInfoValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiInfoValidationTests.cs index 7a6bbbac6..578d5d200 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiInfoValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiInfoValidationTests.cs @@ -4,10 +4,9 @@ using System.Collections.Generic; using System.Linq; using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Validations; using Xunit; -namespace Microsoft.OpenApi.Tests.Models +namespace Microsoft.OpenApi.Validations.Tests { public class OpenApiInfoValidationTests { diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiLicenseValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiLicenseValidationTests.cs index b7bfc8dd9..998da2900 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiLicenseValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiLicenseValidationTests.cs @@ -3,10 +3,9 @@ using System.Collections.Generic; using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Validations; using Xunit; -namespace Microsoft.OpenApi.Tests.Models +namespace Microsoft.OpenApi.Validations.Tests { public class OpenApiLicenseValidationTests { diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiOAuthFlowValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiOAuthFlowValidationTests.cs index 52ca36ee7..b6f5c2b4d 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiOAuthFlowValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiOAuthFlowValidationTests.cs @@ -4,10 +4,9 @@ using System.Collections.Generic; using System.Linq; using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Validations; using Xunit; -namespace Microsoft.OpenApi.Tests.Models +namespace Microsoft.OpenApi.Validations.Tests { public class OpenApiOAuthFlowValidationTests { diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiResponseValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiResponseValidationTests.cs index 404bba49b..4aa2d467e 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiResponseValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiResponseValidationTests.cs @@ -3,10 +3,9 @@ using System.Collections.Generic; using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Validations; using Xunit; -namespace Microsoft.OpenApi.Tests.Models +namespace Microsoft.OpenApi.Validations.Tests { public class OpenApiResponseValidationTests { diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiServerValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiServerValidationTests.cs index 0dc0ba900..a829f3637 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiServerValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiServerValidationTests.cs @@ -3,10 +3,9 @@ using System.Collections.Generic; using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Validations; using Xunit; -namespace Microsoft.OpenApi.Tests.Models +namespace Microsoft.OpenApi.Validations.Tests { public class OpenApiServerValidationTests { diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiTagValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiTagValidationTests.cs index cdcf3f2f0..492e97c26 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiTagValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiTagValidationTests.cs @@ -4,10 +4,9 @@ using System.Collections.Generic; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Validations; using Xunit; -namespace Microsoft.OpenApi.Tests.Models +namespace Microsoft.OpenApi.Validations.Tests { public class OpenApiTagValidationTests { From aebee6fa58e05330dad7aca2ef11898c4933a40f Mon Sep 17 00:00:00 2001 From: Sam Xu Date: Mon, 18 Dec 2017 15:16:27 -0800 Subject: [PATCH 10/12] Add the test cases for ValidationRuleSet --- .../Validations/ValidationRule.cs | 2 +- .../Validations/ValidationRuleSet.cs | 3 +- .../Validations/ValidationRuleSetTests.cs | 40 +++++++++++++++++++ 3 files changed, 43 insertions(+), 2 deletions(-) create mode 100644 test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs diff --git a/src/Microsoft.OpenApi/Validations/ValidationRule.cs b/src/Microsoft.OpenApi/Validations/ValidationRule.cs index 363a1b433..81bbd8cc4 100644 --- a/src/Microsoft.OpenApi/Validations/ValidationRule.cs +++ b/src/Microsoft.OpenApi/Validations/ValidationRule.cs @@ -5,7 +5,7 @@ using System.Diagnostics; using Microsoft.OpenApi.Interfaces; -namespace Microsoft.OpenApi.Validations.Rules +namespace Microsoft.OpenApi.Validations { /// /// Class containing validation rule logic. diff --git a/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs b/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs index 81ca56af4..8cc9a78b0 100644 --- a/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs +++ b/src/Microsoft.OpenApi/Validations/ValidationRuleSet.cs @@ -8,8 +8,9 @@ using System.Collections.Generic; using Microsoft.OpenApi.Exceptions; using Microsoft.OpenApi.Properties; +using Microsoft.OpenApi.Validations.Rules; -namespace Microsoft.OpenApi.Validations.Rules +namespace Microsoft.OpenApi.Validations { /// /// The rule set of the validation. diff --git a/test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs b/test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs new file mode 100644 index 000000000..9e4bfc80c --- /dev/null +++ b/test/Microsoft.OpenApi.Tests/Validations/ValidationRuleSetTests.cs @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System.Linq; +using Xunit; + +namespace Microsoft.OpenApi.Validations.Tests +{ + public class ValidationRuleSetTests + { + [Fact] + public void DefaultRuleSetReturnsTheCorrectRules() + { + // Arrange + var ruleSet = new ValidationRuleSet(); + + // Act + var rules = ruleSet.Rules; + + // Assert + Assert.NotNull(rules); + Assert.Empty(rules); + } + + [Fact] + public void DefaultRuleSetPropertyReturnsTheCorrectRules() + { + // Arrange & Act + var ruleSet = ValidationRuleSet.DefaultRuleSet; + Assert.NotNull(ruleSet); // guard + + var rules = ruleSet.Rules; + + // Assert + Assert.NotNull(rules); + Assert.NotEmpty(rules); + Assert.Equal(13, rules.ToList().Count); // please update the number if you add new rule. + } + } +} From df86936a55e2788a68f940184df166dd4284051a Mon Sep 17 00:00:00 2001 From: Sam Xu Date: Tue, 19 Dec 2017 11:11:55 -0800 Subject: [PATCH 11/12] resolve the comments, typos and some test codes --- .../Rules/OpenApiExternalDocsRules.cs | 2 +- .../Validations/Rules/OpenApiPathsRules.cs | 2 +- .../Validations/Rules/OpenApiResponseRules.cs | 2 +- .../Validations/Rules/OpenApiServerRules.cs | 2 +- .../Validations/Rules/OpenApiTagRules.cs | 2 +- .../Validations/Rules/RuleHelpers.cs | 2 +- .../Validations/ValidationError.cs | 2 +- .../Validations/Visitors/VisitorBase.cs | 6 ++-- .../OpenApiResponseValidationTests.cs | 2 ++ .../Visitors/OpenApiVisitorSetTests.cs | 29 +++++++++++++++++-- 10 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiExternalDocsRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiExternalDocsRules.cs index fe88a8e0d..2c42cbb47 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiExternalDocsRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiExternalDocsRules.cs @@ -20,7 +20,7 @@ internal static class OpenApiExternalDocsRules new ValidationRule( (context, item) => { - // title + // url context.Push("url"); if (item.Url == null) { diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs index 79c81191a..dbc6eb383 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiPathsRules.cs @@ -13,7 +13,7 @@ namespace Microsoft.OpenApi.Validations.Rules public static class OpenApiPathsRules { /// - /// A relative path to an individual endpoint. The field name MUST begin with a slash. + /// A relative path to an individual endpoint. The field name MUST begin with a slash. /// public static ValidationRule PathNameMustBeginWithSlash => new ValidationRule( diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiResponseRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiResponseRules.cs index 34587dcfd..1c6f57b16 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiResponseRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiResponseRules.cs @@ -20,7 +20,7 @@ internal static class OpenApiResponseRules new ValidationRule( (context, response) => { - // title + // description context.Push("description"); if (String.IsNullOrEmpty(response.Description)) { diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiServerRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiServerRules.cs index 1d6cee524..e12ebdf87 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiServerRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiServerRules.cs @@ -14,7 +14,7 @@ namespace Microsoft.OpenApi.Validations.Rules public static class OpenApiServerRules { /// - /// REQUIRED. + /// Validate the field is required. /// public static ValidationRule FieldIsRequired => new ValidationRule( diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiTagRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiTagRules.cs index 1ef4e3289..1aa8328c7 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiTagRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiTagRules.cs @@ -14,7 +14,7 @@ namespace Microsoft.OpenApi.Validations.Rules public static class OpenApiTagRules { /// - /// REQUIRED. + /// Validate the field is required. /// public static ValidationRule FieldIsRequired => new ValidationRule( diff --git a/src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs b/src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs index 8f7f1b046..5f369fd9f 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/RuleHelpers.cs @@ -11,7 +11,7 @@ internal static class RuleHelpers /// Input string must be in the format of an email address /// /// The input string. - /// + /// True if it's an email address. Otherwise False. public static bool IsEmailAddress(this string input) { if (String.IsNullOrEmpty(input)) diff --git a/src/Microsoft.OpenApi/Validations/ValidationError.cs b/src/Microsoft.OpenApi/Validations/ValidationError.cs index 8fe51cb68..8ea695fa0 100644 --- a/src/Microsoft.OpenApi/Validations/ValidationError.cs +++ b/src/Microsoft.OpenApi/Validations/ValidationError.cs @@ -58,7 +58,7 @@ public ValidationError(ErrorReason reason, string path, string message) /// The error string. public override string ToString() { - return "ErroCode: " + ErrorCode + ", " + ErrorPath + " | " + ErrorMessage; + return "ErrorCode: " + ErrorCode + ", " + ErrorPath + " | " + ErrorMessage; } } } diff --git a/src/Microsoft.OpenApi/Validations/Visitors/VisitorBase.cs b/src/Microsoft.OpenApi/Validations/Visitors/VisitorBase.cs index ec9f880da..abeada05f 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/VisitorBase.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/VisitorBase.cs @@ -38,13 +38,13 @@ public void Visit(ValidationContext context, object item) /// The element. protected virtual void Next(ValidationContext context, T element) { - IOpenApiExtensible extensbile = element as IOpenApiExtensible; - if (extensbile != null) + IOpenApiExtensible extensible = element as IOpenApiExtensible; + if (extensible != null) { var rules = context.RuleSet.Where(r => r.ElementType == typeof(IOpenApiExtensible)); foreach (var rule in rules) { - rule.Evaluate(context, extensbile); + rule.Evaluate(context, extensible); } } } diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiResponseValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiResponseValidationTests.cs index 4aa2d467e..a69ec8a59 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiResponseValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiResponseValidationTests.cs @@ -24,6 +24,8 @@ public void ValidateDescriptionIsRequiredInResponse() Assert.NotNull(errors); ValidationError error = Assert.Single(errors); Assert.Equal("The field 'description' in 'response' object is REQUIRED.", error.ErrorMessage); + Assert.Equal(ErrorReason.Required, error.ErrorCode); + Assert.Equal("#/description", error.ErrorPath); } } } diff --git a/test/Microsoft.OpenApi.Tests/Validations/Visitors/OpenApiVisitorSetTests.cs b/test/Microsoft.OpenApi.Tests/Validations/Visitors/OpenApiVisitorSetTests.cs index d7705b5c6..81ce4b451 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/Visitors/OpenApiVisitorSetTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/Visitors/OpenApiVisitorSetTests.cs @@ -21,7 +21,7 @@ public void VisitorsPropertyReturnsTheCorrectVisitorList() // Assert Assert.NotNull(visitors); Assert.NotEmpty(visitors); - Assert.Equal(29, visitors.Count); + Assert.Equal(29, visitors.Count); // Now, we have 29 DOM classes. } } @@ -38,10 +38,35 @@ public void GetVisitorThrowsUnknowElementType() } [Theory] + [InlineData(typeof(OpenApiCallback), typeof(CallbackVisitor))] + [InlineData(typeof(OpenApiComponents), typeof(ComponentsVisitor))] + [InlineData(typeof(OpenApiContact), typeof(ContactVisitor))] + [InlineData(typeof(OpenApiDiscriminator), typeof(DiscriminatorVisitor))] [InlineData(typeof(OpenApiDocument), typeof(DocumentVisitor))] + [InlineData(typeof(OpenApiEncoding), typeof(EncodingVisitor))] + [InlineData(typeof(OpenApiExample), typeof(ExampleVisitor))] + [InlineData(typeof(OpenApiExternalDocs), typeof(ExternalDocsVisitor))] + [InlineData(typeof(OpenApiHeader), typeof(HeaderVisitor))] [InlineData(typeof(OpenApiInfo), typeof(InfoVisitor))] + [InlineData(typeof(OpenApiLicense), typeof(LicenseVisitor))] + [InlineData(typeof(OpenApiLink), typeof(LinkVisitor))] + [InlineData(typeof(OpenApiMediaType), typeof(MediaTypeVisitor))] + [InlineData(typeof(OpenApiOAuthFlows), typeof(OAuthFlowsVisitor))] + [InlineData(typeof(OpenApiOAuthFlow), typeof(OAuthFlowVisitor))] + [InlineData(typeof(OpenApiOperation), typeof(OperationVisitor))] + [InlineData(typeof(OpenApiParameter), typeof(ParameterVisitor))] + [InlineData(typeof(OpenApiPathItem), typeof(PathItemVisitor))] + [InlineData(typeof(OpenApiPaths), typeof(PathsVisitor))] + [InlineData(typeof(OpenApiRequestBody), typeof(RequestBodyVisitor))] + [InlineData(typeof(OpenApiResponses), typeof(ResponsesVisitor))] + [InlineData(typeof(OpenApiResponse), typeof(ResponseVisitor))] + [InlineData(typeof(OpenApiSchema), typeof(SchemaVisitor))] + [InlineData(typeof(OpenApiSecurityRequirement), typeof(SecurityRequirementVisitor))] + [InlineData(typeof(OpenApiSecurityScheme), typeof(SecuritySchemeVisitor))] + [InlineData(typeof(OpenApiServerVariable), typeof(ServerVariableVisitor))] + [InlineData(typeof(OpenApiServer), typeof(ServerVisitor))] + [InlineData(typeof(OpenApiTag), typeof(TagVisitor))] [InlineData(typeof(OpenApiXml), typeof(XmlVisitor))] - [InlineData(typeof(OpenApiComponents), typeof(ComponentsVisitor))] public void GetVisitorReturnsTheCorrectVisitor(Type elementType, Type visitorType) { // Arrange & Act From b29d53227156834a2ff5fa80c14383c7d3ab8843 Mon Sep 17 00:00:00 2001 From: Sam Xu Date: Tue, 19 Dec 2017 16:55:04 -0800 Subject: [PATCH 12/12] Change debug.assert to throw arugment null and use the resource from the product codes --- .../Properties/SRResource.Designer.cs | 9 +++++ .../Properties/SRResource.resx | 3 ++ .../Validations/Rules/OpenApiTagRules.cs | 2 +- .../Validations/ValidationRule.cs | 18 ++++++++- .../Validations/Visitors/CallbackVisitor.cs | 12 ++++-- .../Validations/Visitors/ComponentsVisitor.cs | 21 ++++++++-- .../Validations/Visitors/DocumentVisitor.cs | 19 ++++++++- .../Validations/Visitors/EncodingVisitor.cs | 12 ++++-- .../Validations/Visitors/HeaderVisitor.cs | 12 ++++-- .../Validations/Visitors/InfoVisitor.cs | 12 ++++-- .../Validations/Visitors/LinkVisitor.cs | 12 ++++-- .../Validations/Visitors/MediaTypeVisitor.cs | 12 ++++-- .../Validations/Visitors/OAuthFlowsVisitor.cs | 12 ++++-- .../Validations/Visitors/OpenApiVisitorSet.cs | 1 - .../Validations/Visitors/OperationVisitor.cs | 12 ++++-- .../Validations/Visitors/ParameterVisitor.cs | 12 ++++-- .../Validations/Visitors/PathItemVisitor.cs | 12 ++++-- .../Validations/Visitors/PathsVisitor.cs | 12 ++++-- .../Visitors/RequestBodyVisitor.cs | 12 ++++-- .../Validations/Visitors/ResponseVisitor.cs | 12 ++++-- .../Validations/Visitors/ResponsesVisitor.cs | 12 ++++-- .../Validations/Visitors/SchemaVisitor.cs | 12 ++++-- .../Visitors/SecuritySchemeVisitor.cs | 13 +++++-- .../Validations/Visitors/ServerVisitor.cs | 13 +++++-- .../Validations/Visitors/TagVistor.cs | 12 ++++-- .../Validations/Visitors/VisitorBase.cs | 39 +++++++++++++------ .../OpenApiComponentsValidationTests.cs | 9 +++-- .../OpenApiContactValidationTests.cs | 9 +++-- .../OpenApiExternalDocsValidationTests.cs | 4 +- .../Validations/OpenApiInfoValidationTests.cs | 7 +++- .../OpenApiLicenseValidationTests.cs | 4 +- .../OpenApiOAuthFlowValidationTests.cs | 7 +++- .../OpenApiResponseValidationTests.cs | 4 +- .../OpenApiServerValidationTests.cs | 4 +- .../Validations/OpenApiTagValidationTests.cs | 6 ++- 35 files changed, 291 insertions(+), 93 deletions(-) diff --git a/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs b/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs index 3f0fcb101..4d0dd1fd5 100644 --- a/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs +++ b/src/Microsoft.OpenApi/Properties/SRResource.Designer.cs @@ -96,6 +96,15 @@ internal static string IndentationLevelInvalid { } } + /// + /// Looks up a localized string similar to The input item should be in type of '{0}'.. + /// + internal static string InputItemShouldBeType { + get { + return ResourceManager.GetString("InputItemShouldBeType", resourceCulture); + } + } + /// /// Looks up a localized string similar to The active scope must be an object scope for property name '{0}' to be written.. /// diff --git a/src/Microsoft.OpenApi/Properties/SRResource.resx b/src/Microsoft.OpenApi/Properties/SRResource.resx index d32042422..e3f232c76 100644 --- a/src/Microsoft.OpenApi/Properties/SRResource.resx +++ b/src/Microsoft.OpenApi/Properties/SRResource.resx @@ -129,6 +129,9 @@ Indentation level cannot be lower than 0. + + The input item should be in type of '{0}'. + The active scope must be an object scope for property name '{0}' to be written. diff --git a/src/Microsoft.OpenApi/Validations/Rules/OpenApiTagRules.cs b/src/Microsoft.OpenApi/Validations/Rules/OpenApiTagRules.cs index 1aa8328c7..78c2c972d 100644 --- a/src/Microsoft.OpenApi/Validations/Rules/OpenApiTagRules.cs +++ b/src/Microsoft.OpenApi/Validations/Rules/OpenApiTagRules.cs @@ -24,7 +24,7 @@ public static class OpenApiTagRules if (String.IsNullOrEmpty(tag.Name)) { ValidationError error = new ValidationError(ErrorReason.Required, context.PathString, - String.Format(SRResource.Validation_FieldIsRequired, "name", "Tag")); + String.Format(SRResource.Validation_FieldIsRequired, "name", "tag")); context.AddError(error); } context.Pop(); diff --git a/src/Microsoft.OpenApi/Validations/ValidationRule.cs b/src/Microsoft.OpenApi/Validations/ValidationRule.cs index 81bbd8cc4..beb3f519b 100644 --- a/src/Microsoft.OpenApi/Validations/ValidationRule.cs +++ b/src/Microsoft.OpenApi/Validations/ValidationRule.cs @@ -2,8 +2,8 @@ // Licensed under the MIT license. using System; -using System.Diagnostics; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Properties; namespace Microsoft.OpenApi.Validations { @@ -49,7 +49,21 @@ internal override Type ElementType internal override void Evaluate(ValidationContext context, object item) { - Debug.Assert(item is T, "item should be " + typeof(T)); + if (context == null) + { + throw Error.ArgumentNull(nameof(context)); + } + + if (item == null) + { + return; + } + + if (!(item is T)) + { + throw Error.Argument(string.Format(SRResource.InputItemShouldBeType, typeof(T).FullName)); + } + T typedItem = (T)item; this._validate(context, typedItem); } diff --git a/src/Microsoft.OpenApi/Validations/Visitors/CallbackVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/CallbackVisitor.cs index dc368d427..94e2f95ce 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/CallbackVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/CallbackVisitor.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -using System.Diagnostics; using Microsoft.OpenApi.Models; namespace Microsoft.OpenApi.Validations.Visitors @@ -18,8 +17,15 @@ internal class CallbackVisitor : VisitorBase /// The . protected override void Next(ValidationContext context, OpenApiCallback callback) { - Debug.Assert(context != null); - Debug.Assert(callback != null); + if (context == null) + { + throw Error.ArgumentNull(nameof(context)); + } + + if (callback == null) + { + throw Error.ArgumentNull(nameof(callback)); + } foreach (var pathItem in callback.PathItems) { diff --git a/src/Microsoft.OpenApi/Validations/Visitors/ComponentsVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/ComponentsVisitor.cs index d3b8f995c..7bd6c4e3b 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/ComponentsVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/ComponentsVisitor.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -using System.Diagnostics; using Microsoft.OpenApi.Models; namespace Microsoft.OpenApi.Validations.Visitors @@ -18,18 +17,34 @@ internal class ComponentsVisitor : VisitorBase /// The . protected override void Next(ValidationContext context, OpenApiComponents components) { - Debug.Assert(context != null); - Debug.Assert(components != null); + if (context == null) + { + throw Error.ArgumentNull(nameof(context)); + } + + if (components == null) + { + throw Error.ArgumentNull(nameof(components)); + } 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/Visitors/DocumentVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/DocumentVisitor.cs index 0e56ecd98..79f20be21 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/DocumentVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/DocumentVisitor.cs @@ -17,16 +17,31 @@ internal class DocumentVisitor : VisitorBase /// The . protected override void Next(ValidationContext context, OpenApiDocument document) { + if (context == null) + { + throw Error.ArgumentNull(nameof(context)); + } + if (document == null) { - return; + throw Error.ArgumentNull(nameof(document)); } context.Validate(document.Info); + context.ValidateCollection(document.Servers); + + context.Validate(document.Paths); + + context.Validate(document.Components); + + context.ValidateCollection(document.SecurityRequirements); + context.ValidateCollection(document.Tags); - // add more. + context.Validate(document.ExternalDocs); + + base.Next(context, document); } } } diff --git a/src/Microsoft.OpenApi/Validations/Visitors/EncodingVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/EncodingVisitor.cs index 5ecd8e7f1..47e2f8337 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/EncodingVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/EncodingVisitor.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -using System.Diagnostics; using Microsoft.OpenApi.Models; namespace Microsoft.OpenApi.Validations.Visitors @@ -18,8 +17,15 @@ internal class EncodingVisitor : VisitorBase /// The . protected override void Next(ValidationContext context, OpenApiEncoding encoding) { - Debug.Assert(context != null); - Debug.Assert(encoding != null); + if (context == null) + { + throw Error.ArgumentNull(nameof(context)); + } + + if (encoding == null) + { + throw Error.ArgumentNull(nameof(encoding)); + } context.ValidateMap(encoding.Headers); diff --git a/src/Microsoft.OpenApi/Validations/Visitors/HeaderVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/HeaderVisitor.cs index f601d69bb..ee0307aca 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/HeaderVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/HeaderVisitor.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -using System.Diagnostics; using Microsoft.OpenApi.Models; namespace Microsoft.OpenApi.Validations.Visitors @@ -18,8 +17,15 @@ internal class HeaderVisitor : VisitorBase /// The . protected override void Next(ValidationContext context, OpenApiHeader header) { - Debug.Assert(context != null); - Debug.Assert(header != null); + if (context == null) + { + throw Error.ArgumentNull(nameof(context)); + } + + if (header == null) + { + throw Error.ArgumentNull(nameof(header)); + } context.Validate(header.Schema); diff --git a/src/Microsoft.OpenApi/Validations/Visitors/InfoVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/InfoVisitor.cs index 2075dd68b..9c5b167f2 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/InfoVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/InfoVisitor.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -using System.Diagnostics; using Microsoft.OpenApi.Models; namespace Microsoft.OpenApi.Validations.Visitors @@ -18,8 +17,15 @@ internal class InfoVisitor : VisitorBase /// The . protected override void Next(ValidationContext context, OpenApiInfo info) { - Debug.Assert(context != null); - Debug.Assert(info != null); + if (context == null) + { + throw Error.ArgumentNull(nameof(context)); + } + + if (info == null) + { + throw Error.ArgumentNull(nameof(info)); + } context.Validate(info.Contact); diff --git a/src/Microsoft.OpenApi/Validations/Visitors/LinkVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/LinkVisitor.cs index f3659fb8f..3cead4c66 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/LinkVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/LinkVisitor.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -using System.Diagnostics; using Microsoft.OpenApi.Models; namespace Microsoft.OpenApi.Validations.Visitors @@ -18,8 +17,15 @@ internal class LinkVisitor : VisitorBase /// The . protected override void Next(ValidationContext context, OpenApiLink link) { - Debug.Assert(context != null); - Debug.Assert(link != null); + if (context == null) + { + throw Error.ArgumentNull(nameof(context)); + } + + if (link == null) + { + throw Error.ArgumentNull(nameof(link)); + } context.Validate(link.Server); diff --git a/src/Microsoft.OpenApi/Validations/Visitors/MediaTypeVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/MediaTypeVisitor.cs index 50a4507be..5b035e221 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/MediaTypeVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/MediaTypeVisitor.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -using System.Diagnostics; using Microsoft.OpenApi.Models; namespace Microsoft.OpenApi.Validations.Visitors @@ -18,8 +17,15 @@ internal class MediaTypeVisitor : VisitorBase /// The . protected override void Next(ValidationContext context, OpenApiMediaType mediaType) { - Debug.Assert(context != null); - Debug.Assert(mediaType != null); + if (context == null) + { + throw Error.ArgumentNull(nameof(context)); + } + + if (mediaType == null) + { + throw Error.ArgumentNull(nameof(mediaType)); + } context.Validate(mediaType.Schema); context.ValidateMap(mediaType.Examples); diff --git a/src/Microsoft.OpenApi/Validations/Visitors/OAuthFlowsVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/OAuthFlowsVisitor.cs index 80cf8be13..cf84b4311 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/OAuthFlowsVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/OAuthFlowsVisitor.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -using System.Diagnostics; using Microsoft.OpenApi.Models; namespace Microsoft.OpenApi.Validations.Visitors @@ -18,8 +17,15 @@ internal class OAuthFlowsVisitor : VisitorBase /// The . protected override void Next(ValidationContext context, OpenApiOAuthFlows oAuthFlows) { - Debug.Assert(context != null); - Debug.Assert(oAuthFlows != null); + if (context == null) + { + throw Error.ArgumentNull(nameof(context)); + } + + if (oAuthFlows == null) + { + throw Error.ArgumentNull(nameof(oAuthFlows)); + } context.Validate(oAuthFlows.Implicit); context.Validate(oAuthFlows.Password); diff --git a/src/Microsoft.OpenApi/Validations/Visitors/OpenApiVisitorSet.cs b/src/Microsoft.OpenApi/Validations/Visitors/OpenApiVisitorSet.cs index 2cde35fef..105dc3d9e 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/OpenApiVisitorSet.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/OpenApiVisitorSet.cs @@ -5,7 +5,6 @@ using System.Collections.Generic; using System.Linq; using Microsoft.OpenApi.Exceptions; -using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Properties; using Microsoft.OpenApi.Interfaces; diff --git a/src/Microsoft.OpenApi/Validations/Visitors/OperationVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/OperationVisitor.cs index 3b07a3d25..5095a740b 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/OperationVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/OperationVisitor.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -using System.Diagnostics; using Microsoft.OpenApi.Models; namespace Microsoft.OpenApi.Validations.Visitors @@ -18,8 +17,15 @@ internal class OperationVisitor : VisitorBase /// The . protected override void Next(ValidationContext context, OpenApiOperation operation) { - Debug.Assert(context != null); - Debug.Assert(operation != null); + if (context == null) + { + throw Error.ArgumentNull(nameof(context)); + } + + if (operation == null) + { + throw Error.ArgumentNull(nameof(operation)); + } context.ValidateCollection(operation.Tags); context.Validate(operation.ExternalDocs); diff --git a/src/Microsoft.OpenApi/Validations/Visitors/ParameterVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/ParameterVisitor.cs index b8bbbb330..f4e42fcd1 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/ParameterVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/ParameterVisitor.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -using System.Diagnostics; using Microsoft.OpenApi.Models; namespace Microsoft.OpenApi.Validations.Visitors @@ -18,8 +17,15 @@ internal class ParameterVisitor : VisitorBase /// The . protected override void Next(ValidationContext context, OpenApiParameter parameter) { - Debug.Assert(context != null); - Debug.Assert(parameter != null); + if (context == null) + { + throw Error.ArgumentNull(nameof(context)); + } + + if (parameter == null) + { + throw Error.ArgumentNull(nameof(parameter)); + } context.Validate(parameter.Schema); context.ValidateCollection(parameter.Examples); diff --git a/src/Microsoft.OpenApi/Validations/Visitors/PathItemVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/PathItemVisitor.cs index 93d359b19..06154374f 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/PathItemVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/PathItemVisitor.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -using System.Diagnostics; using Microsoft.OpenApi.Models; namespace Microsoft.OpenApi.Validations.Visitors @@ -18,8 +17,15 @@ internal class PathItemVisitor : VisitorBase /// The . protected override void Next(ValidationContext context, OpenApiPathItem pathItem) { - Debug.Assert(context != null); - Debug.Assert(pathItem != null); + if (context == null) + { + throw Error.ArgumentNull(nameof(context)); + } + + if (pathItem == null) + { + throw Error.ArgumentNull(nameof(pathItem)); + } foreach (var operation in pathItem.Operations) { diff --git a/src/Microsoft.OpenApi/Validations/Visitors/PathsVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/PathsVisitor.cs index 7849e37ec..6aac857c5 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/PathsVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/PathsVisitor.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -using System.Diagnostics; using Microsoft.OpenApi.Models; namespace Microsoft.OpenApi.Validations.Visitors @@ -18,8 +17,15 @@ internal class PathsVisitor : VisitorBase /// The . protected override void Next(ValidationContext context, OpenApiPaths paths) { - Debug.Assert(context != null); - Debug.Assert(paths != null); + if (context == null) + { + throw Error.ArgumentNull(nameof(context)); + } + + if (paths == null) + { + throw Error.ArgumentNull(nameof(paths)); + } context.ValidateMap(paths); base.Next(context, paths); diff --git a/src/Microsoft.OpenApi/Validations/Visitors/RequestBodyVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/RequestBodyVisitor.cs index abf3ba34a..eea6cb2cb 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/RequestBodyVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/RequestBodyVisitor.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -using System.Diagnostics; using Microsoft.OpenApi.Models; namespace Microsoft.OpenApi.Validations.Visitors @@ -18,8 +17,15 @@ internal class RequestBodyVisitor : VisitorBase /// The . protected override void Next(ValidationContext context, OpenApiRequestBody requestBody) { - Debug.Assert(context != null); - Debug.Assert(requestBody != null); + if (context == null) + { + throw Error.ArgumentNull(nameof(context)); + } + + if (requestBody == null) + { + throw Error.ArgumentNull(nameof(requestBody)); + } context.ValidateMap(requestBody.Content); diff --git a/src/Microsoft.OpenApi/Validations/Visitors/ResponseVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/ResponseVisitor.cs index 3849f44ef..2e653c836 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/ResponseVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/ResponseVisitor.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -using System.Diagnostics; using Microsoft.OpenApi.Models; namespace Microsoft.OpenApi.Validations.Visitors @@ -18,8 +17,15 @@ internal class ResponseVisitor : VisitorBase /// The . protected override void Next(ValidationContext context, OpenApiResponse response) { - Debug.Assert(context != null); - Debug.Assert(response != null); + if (context == null) + { + throw Error.ArgumentNull(nameof(context)); + } + + if (response == null) + { + throw Error.ArgumentNull(nameof(response)); + } context.ValidateMap(response.Headers); context.ValidateMap(response.Content); diff --git a/src/Microsoft.OpenApi/Validations/Visitors/ResponsesVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/ResponsesVisitor.cs index f71cfecc3..c1c6461fc 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/ResponsesVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/ResponsesVisitor.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -using System.Diagnostics; using Microsoft.OpenApi.Models; namespace Microsoft.OpenApi.Validations.Visitors @@ -18,8 +17,15 @@ internal class ResponsesVisitor : VisitorBase /// The . protected override void Next(ValidationContext context, OpenApiResponses responses) { - Debug.Assert(context != null); - Debug.Assert(responses != null); + if (context == null) + { + throw Error.ArgumentNull(nameof(context)); + } + + if (responses == null) + { + throw Error.ArgumentNull(nameof(responses)); + } context.ValidateMap(responses); diff --git a/src/Microsoft.OpenApi/Validations/Visitors/SchemaVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/SchemaVisitor.cs index 56111c95e..76556d23b 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/SchemaVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/SchemaVisitor.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -using System.Diagnostics; using Microsoft.OpenApi.Models; namespace Microsoft.OpenApi.Validations.Visitors @@ -18,8 +17,15 @@ internal class SchemaVisitor : VisitorBase /// The . protected override void Next(ValidationContext context, OpenApiSchema schema) { - Debug.Assert(context != null); - Debug.Assert(schema != null); + if (context == null) + { + throw Error.ArgumentNull(nameof(context)); + } + + if (schema == null) + { + throw Error.ArgumentNull(nameof(schema)); + } context.ValidateCollection(schema.AllOf); diff --git a/src/Microsoft.OpenApi/Validations/Visitors/SecuritySchemeVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/SecuritySchemeVisitor.cs index f51ff473f..1e19210e0 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/SecuritySchemeVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/SecuritySchemeVisitor.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -using System.Diagnostics; using Microsoft.OpenApi.Models; namespace Microsoft.OpenApi.Validations.Visitors @@ -18,12 +17,18 @@ internal class SecuritySchemeVisitor : VisitorBase /// The . protected override void Next(ValidationContext context, OpenApiSecurityScheme securityScheme) { - Debug.Assert(context != null); - Debug.Assert(securityScheme != null); + if (context == null) + { + throw Error.ArgumentNull(nameof(context)); + } + + if (securityScheme == null) + { + throw Error.ArgumentNull(nameof(securityScheme)); + } context.Validate(securityScheme.Flows); - // add more. base.Next(context, securityScheme); } } diff --git a/src/Microsoft.OpenApi/Validations/Visitors/ServerVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/ServerVisitor.cs index 62b67a77c..9068f1bfe 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/ServerVisitor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/ServerVisitor.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -using System.Diagnostics; using Microsoft.OpenApi.Models; namespace Microsoft.OpenApi.Validations.Visitors @@ -18,12 +17,18 @@ internal class ServerVisitor : VisitorBase /// The . protected override void Next(ValidationContext context, OpenApiServer server) { - Debug.Assert(context != null); - Debug.Assert(server != null); + if (context == null) + { + throw Error.ArgumentNull(nameof(context)); + } + + if (server == null) + { + throw Error.ArgumentNull(nameof(server)); + } context.ValidateMap(server.Variables); - // add more. base.Next(context, server); } } diff --git a/src/Microsoft.OpenApi/Validations/Visitors/TagVistor.cs b/src/Microsoft.OpenApi/Validations/Visitors/TagVistor.cs index 80ccaa523..5db0e4018 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/TagVistor.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/TagVistor.cs @@ -1,7 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -using System.Diagnostics; using Microsoft.OpenApi.Models; namespace Microsoft.OpenApi.Validations.Visitors @@ -18,8 +17,15 @@ internal class TagVisitor : VisitorBase /// The . protected override void Next(ValidationContext context, OpenApiTag tag) { - Debug.Assert(context != null); - Debug.Assert(tag != null); + if (context == null) + { + throw Error.ArgumentNull(nameof(context)); + } + + if (tag == null) + { + throw Error.ArgumentNull(nameof(tag)); + } context.Validate(tag.ExternalDocs); diff --git a/src/Microsoft.OpenApi/Validations/Visitors/VisitorBase.cs b/src/Microsoft.OpenApi/Validations/Visitors/VisitorBase.cs index abeada05f..378fcfb62 100644 --- a/src/Microsoft.OpenApi/Validations/Visitors/VisitorBase.cs +++ b/src/Microsoft.OpenApi/Validations/Visitors/VisitorBase.cs @@ -1,9 +1,9 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -using System.Diagnostics; using System.Linq; using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Properties; namespace Microsoft.OpenApi.Validations.Visitors { @@ -19,7 +19,20 @@ internal abstract class VisitorBase : IVisitor where T : IOpenApiElement /// The element. public void Visit(ValidationContext context, object item) { - Debug.Assert(item is T, "item should be " + typeof(T)); + if (context == null) + { + throw Error.ArgumentNull(nameof(context)); + } + + if (item == null) + { + return; // uplevel should verify + } + + if (!(item is T)) + { + throw Error.Argument(string.Format(SRResource.InputItemShouldBeType, typeof(T).FullName)); + } var rules = context.RuleSet.Where(r => r.ElementType == typeof(T)); foreach (var rule in rules) @@ -27,8 +40,19 @@ public void Visit(ValidationContext context, object item) rule.Evaluate(context, item); } + // verify its extension if the input item is an extensible element. + IOpenApiExtensible extensible = item as IOpenApiExtensible; + if (extensible != null) + { + rules = context.RuleSet.Where(r => r.ElementType == typeof(IOpenApiExtensible)); + foreach (var rule in rules) + { + rule.Evaluate(context, extensible); + } + } + T typedItem = (T)item; - this.Next(context, typedItem); + Next(context, typedItem); } /// @@ -38,15 +62,6 @@ public void Visit(ValidationContext context, object item) /// The element. protected virtual void Next(ValidationContext context, T element) { - IOpenApiExtensible extensible = element as IOpenApiExtensible; - if (extensible != null) - { - var rules = context.RuleSet.Where(r => r.ElementType == typeof(IOpenApiExtensible)); - foreach (var rule in rules) - { - rule.Evaluate(context, extensible); - } - } } } } diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiComponentsValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiComponentsValidationTests.cs index 668b8530c..872f4a36a 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiComponentsValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiComponentsValidationTests.cs @@ -1,9 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using System; using System.Collections.Generic; using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Validations; +using Microsoft.OpenApi.Properties; +using Microsoft.OpenApi.Validations.Rules; using Xunit; namespace Microsoft.OpenApi.Validations.Tests @@ -14,12 +16,13 @@ public class OpenApiComponentsValidationTests public void ValidateKeyMustMatchRegularExpressionInComponents() { // Arrange + const string key = "%@abc"; IEnumerable errors; OpenApiComponents components = new OpenApiComponents() { Responses = new Dictionary { - { "%@abc", new OpenApiResponse { Description = "any" } } + { key, new OpenApiResponse { Description = "any" } } } }; @@ -30,7 +33,7 @@ public void ValidateKeyMustMatchRegularExpressionInComponents() Assert.False(result); Assert.NotNull(errors); ValidationError error = Assert.Single(errors); - Assert.Equal(@"The key '%@abc' in 'responses' of components MUST match the regular expression '^[a-zA-Z0-9\.\-_]+$'.", + Assert.Equal(String.Format(SRResource.Validation_ComponentsKeyMustMatchRegularExpr, key, "responses", OpenApiComponentsRules.KeyRegex.ToString()), error.ErrorMessage); } } diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs index 5e7c59cd0..94cbb6920 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs @@ -1,8 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. -using Microsoft.OpenApi.Models; +using System; using System.Collections.Generic; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; using Xunit; namespace Microsoft.OpenApi.Validations.Tests @@ -13,10 +15,11 @@ public class OpenApiContactValidationTests public void ValidateEmailFieldIsEmailAddressInContact() { // Arrange + const string testEmail = "support/example.com"; IEnumerable errors; OpenApiContact Contact = new OpenApiContact() { - Email = "support/example.com", + Email = testEmail }; // Act @@ -26,7 +29,7 @@ public void ValidateEmailFieldIsEmailAddressInContact() 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); + Assert.Equal(String.Format(SRResource.Validation_StringMustBeEmailAddress, testEmail), error.ErrorMessage); } } } diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiExternalDocsValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiExternalDocsValidationTests.cs index 1f3b69a49..5989d7448 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiExternalDocsValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiExternalDocsValidationTests.cs @@ -1,8 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using System; using System.Collections.Generic; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; using Xunit; namespace Microsoft.OpenApi.Validations.Tests @@ -23,7 +25,7 @@ public void ValidateUrlIsRequiredInExternalDocs() Assert.False(result); Assert.NotNull(errors); ValidationError error = Assert.Single(errors); - Assert.Equal("The field 'url' in 'External Documentation' object is REQUIRED.", error.ErrorMessage); + Assert.Equal(String.Format(SRResource.Validation_FieldIsRequired, "url", "External Documentation"), error.ErrorMessage); } } } diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiInfoValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiInfoValidationTests.cs index 578d5d200..4945b84ab 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiInfoValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiInfoValidationTests.cs @@ -1,9 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using System; using System.Collections.Generic; using System.Linq; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; using Xunit; namespace Microsoft.OpenApi.Validations.Tests @@ -14,6 +16,8 @@ public class OpenApiInfoValidationTests public void ValidateFieldIsRequiredInInfo() { // Arrange + string urlError = String.Format(SRResource.Validation_FieldIsRequired, "url", "info"); + string versionError = String.Format(SRResource.Validation_FieldIsRequired, "version", "info"); IEnumerable errors; OpenApiInfo info = new OpenApiInfo(); @@ -24,8 +28,7 @@ public void ValidateFieldIsRequiredInInfo() 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)); + Assert.Equal(new[] { urlError, versionError }, errors.Select(e => e.ErrorMessage)); } } } diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiLicenseValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiLicenseValidationTests.cs index 998da2900..fb6fd9f35 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiLicenseValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiLicenseValidationTests.cs @@ -1,8 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using System; using System.Collections.Generic; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; using Xunit; namespace Microsoft.OpenApi.Validations.Tests @@ -23,7 +25,7 @@ public void ValidateFieldIsRequiredInLicense() Assert.False(result); Assert.NotNull(errors); ValidationError error = Assert.Single(errors); - Assert.Equal("The field 'name' in 'license' object is REQUIRED.", error.ErrorMessage); + Assert.Equal(String.Format(SRResource.Validation_FieldIsRequired, "name", "license"), error.ErrorMessage); } } } diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiOAuthFlowValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiOAuthFlowValidationTests.cs index b6f5c2b4d..776ccbd45 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiOAuthFlowValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiOAuthFlowValidationTests.cs @@ -1,9 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using System; using System.Collections.Generic; using System.Linq; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; using Xunit; namespace Microsoft.OpenApi.Validations.Tests @@ -14,6 +16,8 @@ public class OpenApiOAuthFlowValidationTests public void ValidateFixedFieldsIsRequiredInResponse() { // Arrange + string authorizationUrlError = String.Format(SRResource.Validation_FieldIsRequired, "authorizationUrl", "OAuth Flow"); + string tokenUrlError = String.Format(SRResource.Validation_FieldIsRequired, "tokenUrl", "OAuth Flow"); IEnumerable errors; OpenApiOAuthFlow oAuthFlow = new OpenApiOAuthFlow(); @@ -24,8 +28,7 @@ public void ValidateFixedFieldsIsRequiredInResponse() Assert.False(result); Assert.NotNull(errors); Assert.Equal(2, errors.Count()); - Assert.Equal(new[] { "The field 'authorizationUrl' in 'OAuth Flow' object is REQUIRED.", - "The field 'tokenUrl' in 'OAuth Flow' object is REQUIRED."}, errors.Select(e => e.ErrorMessage)); + Assert.Equal(new[] { authorizationUrlError, tokenUrlError }, errors.Select(e => e.ErrorMessage)); } } } diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiResponseValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiResponseValidationTests.cs index a69ec8a59..eb0a339af 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiResponseValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiResponseValidationTests.cs @@ -1,8 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using System; using System.Collections.Generic; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; using Xunit; namespace Microsoft.OpenApi.Validations.Tests @@ -23,7 +25,7 @@ public void ValidateDescriptionIsRequiredInResponse() Assert.False(result); Assert.NotNull(errors); ValidationError error = Assert.Single(errors); - Assert.Equal("The field 'description' in 'response' object is REQUIRED.", error.ErrorMessage); + Assert.Equal(String.Format(SRResource.Validation_FieldIsRequired, "description", "response"), error.ErrorMessage); Assert.Equal(ErrorReason.Required, error.ErrorCode); Assert.Equal("#/description", error.ErrorPath); } diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiServerValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiServerValidationTests.cs index a829f3637..bbbb05588 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiServerValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiServerValidationTests.cs @@ -1,8 +1,10 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using System; using System.Collections.Generic; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; using Xunit; namespace Microsoft.OpenApi.Validations.Tests @@ -23,7 +25,7 @@ public void ValidateFieldIsRequiredInServer() Assert.False(result); Assert.NotNull(errors); ValidationError error = Assert.Single(errors); - Assert.Equal("The field 'url' in 'server' object is REQUIRED.", error.ErrorMessage); + Assert.Equal(String.Format(SRResource.Validation_FieldIsRequired, "url", "server"), error.ErrorMessage); } } } diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiTagValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiTagValidationTests.cs index 492e97c26..e34c125f1 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiTagValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiTagValidationTests.cs @@ -1,9 +1,11 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using System; using System.Collections.Generic; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; using Xunit; namespace Microsoft.OpenApi.Validations.Tests @@ -24,7 +26,7 @@ public void ValidateNameIsRequiredInTag() Assert.False(result); Assert.NotNull(errors); ValidationError error = Assert.Single(errors); - Assert.Equal("The field 'name' in 'Tag' object is REQUIRED.", error.ErrorMessage); + Assert.Equal(String.Format(SRResource.Validation_FieldIsRequired, "name", "tag"), error.ErrorMessage); } [Fact] @@ -45,7 +47,7 @@ public void ValidateExtensionNameStartsWithXDashInTag() 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); + Assert.Equal(String.Format(SRResource.Validation_ExtensionNameMustBeginWithXDash, "tagExt", "#/extensions"), error.ErrorMessage); } } }