From 7316184c87f117c61a1266e8c7ec660973d3b8b5 Mon Sep 17 00:00:00 2001 From: Sam Xu Date: Tue, 12 Dec 2017 10:10:52 -0800 Subject: [PATCH 01/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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/18] 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 bc530c3325a147e66cb6f7ed514de5298e490390 Mon Sep 17 00:00:00 2001 From: Darrel Miller Date: Tue, 19 Dec 2017 19:19:50 -0500 Subject: [PATCH 12/18] Validation using walker and single visitor class --- .../Services/OpenApiValidator.cs | 34 ++++++++++---- .../Services/OpenApiVisitorBase.cs | 47 +++++++++++-------- .../Services/OpenApiValidatorTests.cs | 20 +++++--- 3 files changed, 66 insertions(+), 35 deletions(-) diff --git a/src/Microsoft.OpenApi/Services/OpenApiValidator.cs b/src/Microsoft.OpenApi/Services/OpenApiValidator.cs index 25d9b4d1b..e432518c9 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiValidator.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiValidator.cs @@ -2,8 +2,10 @@ // Licensed under the MIT license. using System.Collections.Generic; +using System.Linq; using Microsoft.OpenApi.Exceptions; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Validations; namespace Microsoft.OpenApi.Services { @@ -12,20 +14,34 @@ namespace Microsoft.OpenApi.Services /// public class OpenApiValidator : OpenApiVisitorBase { - /// - /// Exceptions related to this validation. - /// - public List Exceptions { get; } = new List(); + readonly ValidationRuleSet _ruleSet; + readonly ValidationContext _context; /// - /// Visit Open API Response element. + /// Create a vistor that will validate an OpenAPIDocument /// - /// Response element. - public override void Visit(OpenApiResponse response) + /// + public OpenApiValidator(ValidationRuleSet ruleSet = null) + { + _ruleSet = ruleSet ?? ValidationRuleSet.DefaultRuleSet; + _context = new ValidationContext(_ruleSet); + } + + public override void Visit(OpenApiDocument item) => Validate(item); + public override void Visit(OpenApiInfo item) => Validate(item); + public override void Visit(OpenApiContact item) => Validate(item); + public override void Visit(OpenApiResponse item) => Validate(item); + + + public IEnumerable Errors => _context.Errors; + + private void Validate(T item) { - if (string.IsNullOrEmpty(response.Description)) + if (item == null) return; // Required fields should be checked by higher level objects + var rules = _ruleSet.Where(r => r.ElementType == typeof(T)); + foreach (var rule in rules) { - Exceptions.Add(new OpenApiException("Response must have a description")); + rule.Evaluate(_context, item); } } } diff --git a/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs b/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs index 1a20c67ca..094aa144c 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs @@ -13,140 +13,147 @@ namespace Microsoft.OpenApi.Services public abstract class OpenApiVisitorBase { /// - /// Validates + /// Visits /// public virtual void Visit(OpenApiDocument doc) { } /// - /// Validates + /// Visits /// public virtual void Visit(OpenApiInfo info) { } /// - /// Validates list of + /// Visits + /// + public virtual void Visit(OpenApiContact contact) + { + } + + /// + /// Visits list of /// public virtual void Visit(IList servers) { } /// - /// Validates + /// Visits /// public virtual void Visit(OpenApiServer server) { } /// - /// Validates + /// Visits /// public virtual void Visit(OpenApiPaths paths) { } /// - /// Validates + /// Visits /// public virtual void Visit(OpenApiPathItem pathItem) { } /// - /// Validates + /// Visits /// public virtual void Visit(OpenApiServerVariable serverVariable) { } /// - /// Validates the operations. + /// Visits the operations. /// public virtual void Visit(IDictionary operations) { } /// - /// Validates + /// Visits /// public virtual void Visit(OpenApiOperation operation) { } /// - /// Validates list of + /// Visits list of /// public virtual void Visit(IList parameters) { } /// - /// Validates + /// Visits /// public virtual void Visit(OpenApiParameter parameter) { } /// - /// Validates + /// Visits /// public virtual void Visit(OpenApiRequestBody requestBody) { } /// - /// Validates responses. + /// Visits responses. /// public virtual void Visit(IDictionary responses) { } /// - /// Validates + /// Visits /// public virtual void Visit(OpenApiResponse response) { } /// - /// Validates media type content. + /// Visits media type content. /// public virtual void Visit(IDictionary content) { } /// - /// Validates + /// Visits /// public virtual void Visit(OpenApiMediaType mediaType) { } /// - /// Validates the examples. + /// Visits the examples. /// public virtual void Visit(IDictionary examples) { } /// - /// Validates + /// Visits /// public virtual void Visit(OpenApiSchema schema) { } /// - /// Validates the links. + /// Visits the links. /// public virtual void Visit(IDictionary links) { } /// - /// Validates + /// Visits /// public virtual void Visit(OpenApiLink link) { diff --git a/test/Microsoft.OpenApi.Tests/Services/OpenApiValidatorTests.cs b/test/Microsoft.OpenApi.Tests/Services/OpenApiValidatorTests.cs index af5ec8a05..34d5ae1d2 100644 --- a/test/Microsoft.OpenApi.Tests/Services/OpenApiValidatorTests.cs +++ b/test/Microsoft.OpenApi.Tests/Services/OpenApiValidatorTests.cs @@ -1,11 +1,14 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT license. +using System; using System.Collections.Generic; using FluentAssertions; using Microsoft.OpenApi.Exceptions; using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Properties; using Microsoft.OpenApi.Services; +using Microsoft.OpenApi.Validations; using Xunit; namespace Microsoft.OpenApi.Tests.Services @@ -17,7 +20,11 @@ public class OpenApiValidatorTests public void ResponseMustHaveADescription() { var openApiDocument = new OpenApiDocument(); - + openApiDocument.Info = new OpenApiInfo() + { + Title = "foo", + Version = "1.2.2" + }; openApiDocument.Paths.Add( "/test", new OpenApiPathItem @@ -38,11 +45,12 @@ public void ResponseMustHaveADescription() var walker = new OpenApiWalker(validator); walker.Walk(openApiDocument); - validator.Exceptions.ShouldBeEquivalentTo( - new List - { - new OpenApiException("Response must have a description") - }); + validator.Errors.ShouldBeEquivalentTo( + new List + { + new ValidationError(ErrorReason.Required, "#/description", + String.Format(SRResource.Validation_FieldIsRequired, "description", "response")) + }); } } } \ No newline at end of file From b29d53227156834a2ff5fa80c14383c7d3ab8843 Mon Sep 17 00:00:00 2001 From: Sam Xu Date: Tue, 19 Dec 2017 16:55:04 -0800 Subject: [PATCH 13/18] 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); } } } From 01885659ebb992c6e5ce235475af336ccd0ef05f Mon Sep 17 00:00:00 2001 From: Darrel Miller Date: Tue, 19 Dec 2017 22:31:29 -0500 Subject: [PATCH 14/18] Remove visitor classes --- .../Services/OpenApiValidator.cs | 9 ++ .../Services/OpenApiVisitorBase.cs | 36 +++++ .../Validations/OpenApiElementValidator.cs | 73 --------- .../ValidationContextExtensions.cs | 81 ---------- .../Validations/Visitors/CallbackVisitor.cs | 38 ----- .../Validations/Visitors/ComponentsVisitor.cs | 51 ------ .../Validations/Visitors/ContactVisitor.cs | 14 -- .../Visitors/DiscriminatorVisitor.cs | 14 -- .../Validations/Visitors/DocumentVisitor.cs | 47 ------ .../Validations/Visitors/EncodingVisitor.cs | 35 ----- .../Validations/Visitors/ExampleVisitor.cs | 14 -- .../Visitors/ExternalDocsVisitor.cs | 14 -- .../Validations/Visitors/HeaderVisitor.cs | 39 ----- .../Validations/Visitors/IVisitor.cs | 18 --- .../Validations/Visitors/InfoVisitor.cs | 37 ----- .../Validations/Visitors/LicenseVisitor.cs | 14 -- .../Validations/Visitors/LinkVisitor.cs | 35 ----- .../Validations/Visitors/MediaTypeVisitor.cs | 37 ----- .../Validations/Visitors/OAuthFlowVisitor.cs | 14 -- .../Validations/Visitors/OAuthFlowsVisitor.cs | 38 ----- .../Validations/Visitors/OpenApiVisitorSet.cs | 90 ----------- .../Validations/Visitors/OperationVisitor.cs | 42 ----- .../Validations/Visitors/ParameterVisitor.cs | 37 ----- .../Validations/Visitors/PathItemVisitor.cs | 41 ----- .../Validations/Visitors/PathsVisitor.cs | 34 ---- .../Visitors/RequestBodyVisitor.cs | 35 ----- .../Validations/Visitors/ResponseVisitor.cs | 37 ----- .../Validations/Visitors/ResponsesVisitor.cs | 35 ----- .../Validations/Visitors/SchemaVisitor.cs | 53 ------- .../Visitors/SecurityRequirementVisitor.cs | 14 -- .../Visitors/SecuritySchemeVisitor.cs | 35 ----- .../Visitors/ServerVariableVisitor.cs | 14 -- .../Validations/Visitors/ServerVisitor.cs | 35 ----- .../Validations/Visitors/TagVistor.cs | 35 ----- .../Validations/Visitors/VisitorBase.cs | 67 -------- .../Validations/Visitors/XmlVistor.cs | 14 -- .../OpenApiComponentsValidationTests.cs | 8 +- .../OpenApiContactValidationTests.cs | 8 +- .../OpenApiExternalDocsValidationTests.cs | 8 +- .../Validations/OpenApiInfoValidationTests.cs | 8 +- .../OpenApiLicenseValidationTests.cs | 7 +- .../OpenApiOAuthFlowValidationTests.cs | 6 +- .../OpenApiResponseValidationTests.cs | 7 +- .../OpenApiServerValidationTests.cs | 7 +- .../Validations/OpenApiTagValidationTests.cs | 12 +- .../Visitors/OpenApiVisitorSetTests.cs | 146 +++++++++--------- 46 files changed, 179 insertions(+), 1314 deletions(-) delete mode 100644 src/Microsoft.OpenApi/Validations/OpenApiElementValidator.cs delete mode 100644 src/Microsoft.OpenApi/Validations/ValidationContextExtensions.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/CallbackVisitor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/ComponentsVisitor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/ContactVisitor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/DiscriminatorVisitor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/DocumentVisitor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/EncodingVisitor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/ExampleVisitor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/ExternalDocsVisitor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/HeaderVisitor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/IVisitor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/InfoVisitor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/LicenseVisitor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/LinkVisitor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/MediaTypeVisitor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/OAuthFlowVisitor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/OAuthFlowsVisitor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/OpenApiVisitorSet.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/OperationVisitor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/ParameterVisitor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/PathItemVisitor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/PathsVisitor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/RequestBodyVisitor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/ResponseVisitor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/ResponsesVisitor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/SchemaVisitor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/SecurityRequirementVisitor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/SecuritySchemeVisitor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/ServerVariableVisitor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/ServerVisitor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/TagVistor.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/VisitorBase.cs delete mode 100644 src/Microsoft.OpenApi/Validations/Visitors/XmlVistor.cs diff --git a/src/Microsoft.OpenApi/Services/OpenApiValidator.cs b/src/Microsoft.OpenApi/Services/OpenApiValidator.cs index e432518c9..70fa07715 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiValidator.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiValidator.cs @@ -30,8 +30,17 @@ public OpenApiValidator(ValidationRuleSet ruleSet = null) public override void Visit(OpenApiDocument item) => Validate(item); public override void Visit(OpenApiInfo item) => Validate(item); public override void Visit(OpenApiContact item) => Validate(item); + public override void Visit(OpenApiComponents item) => Validate(item); + public override void Visit(OpenApiResponse item) => Validate(item); + public override void Visit(OpenApiExternalDocs item) => Validate(item); + + public override void Visit(OpenApiLicense item) => Validate(item); + public override void Visit(OpenApiOAuthFlow item) => Validate(item); + public override void Visit(OpenApiTag item) => Validate(item); + public override void Visit(OpenApiServer item) => Validate(item); + public IEnumerable Errors => _context.Errors; diff --git a/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs b/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs index 094aa144c..6993047c5 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs @@ -33,6 +33,14 @@ public virtual void Visit(OpenApiContact contact) { } + + /// + /// Visits + /// + public virtual void Visit(OpenApiLicense license) + { + } + /// /// Visits list of /// @@ -138,6 +146,21 @@ public virtual void Visit(IDictionary examples) { } + /// + /// Visits + /// + public virtual void Visit(OpenApiComponents components) + { + } + + + /// + /// Visits + /// + public virtual void Visit(OpenApiExternalDocs externalDocs) + { + } + /// /// Visits /// @@ -158,5 +181,18 @@ public virtual void Visit(IDictionary links) public virtual void Visit(OpenApiLink link) { } + + /// + /// Visits + /// + public virtual void Visit(OpenApiTag tag) + { + } + /// + /// Visits + /// + public virtual void Visit(OpenApiOAuthFlow openApiOAuthFlow) + { + } } } \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Validations/OpenApiElementValidator.cs b/src/Microsoft.OpenApi/Validations/OpenApiElementValidator.cs deleted file mode 100644 index de38e15d5..000000000 --- a/src/Microsoft.OpenApi/Validations/OpenApiElementValidator.cs +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -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 and output the whole validation errors. - /// - /// 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 with the given rule set and output the whole validation errors. - /// - /// 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/ValidationContextExtensions.cs b/src/Microsoft.OpenApi/Validations/ValidationContextExtensions.cs deleted file mode 100644 index db6056592..000000000 --- a/src/Microsoft.OpenApi/Validations/ValidationContextExtensions.cs +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -using System.Collections.Generic; -using Microsoft.OpenApi.Interfaces; -using Microsoft.OpenApi.Validations.Visitors; - -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); - } - } - } - - /// - /// 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 deleted file mode 100644 index 94e2f95ce..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/CallbackVisitor.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -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) - { - if (context == null) - { - throw Error.ArgumentNull(nameof(context)); - } - - if (callback == null) - { - throw Error.ArgumentNull(nameof(callback)); - } - - 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 deleted file mode 100644 index 7bd6c4e3b..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/ComponentsVisitor.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -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) - { - 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/ContactVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/ContactVisitor.cs deleted file mode 100644 index bc22aab7b..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/ContactVisitor.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -using Microsoft.OpenApi.Models; - -namespace Microsoft.OpenApi.Validations.Visitors -{ - /// - /// Visit . - /// - internal class ContactVisitor : VisitorBase - { - } -} diff --git a/src/Microsoft.OpenApi/Validations/Visitors/DiscriminatorVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/DiscriminatorVisitor.cs deleted file mode 100644 index 24657ed94..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/DiscriminatorVisitor.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -using Microsoft.OpenApi.Models; - -namespace Microsoft.OpenApi.Validations.Visitors -{ - /// - /// Visit . - /// - internal class DiscriminatorVisitor : VisitorBase - { - } -} diff --git a/src/Microsoft.OpenApi/Validations/Visitors/DocumentVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/DocumentVisitor.cs deleted file mode 100644 index 79f20be21..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/DocumentVisitor.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -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 (context == null) - { - throw Error.ArgumentNull(nameof(context)); - } - - if (document == null) - { - 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); - - 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 deleted file mode 100644 index 47e2f8337..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/EncodingVisitor.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -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) - { - if (context == null) - { - throw Error.ArgumentNull(nameof(context)); - } - - if (encoding == null) - { - throw Error.ArgumentNull(nameof(encoding)); - } - - 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 deleted file mode 100644 index 4dbe97d89..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/ExampleVisitor.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -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 deleted file mode 100644 index 279fd9310..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/ExternalDocsVisitor.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -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 deleted file mode 100644 index ee0307aca..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/HeaderVisitor.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -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) - { - if (context == null) - { - throw Error.ArgumentNull(nameof(context)); - } - - if (header == null) - { - throw Error.ArgumentNull(nameof(header)); - } - - context.Validate(header.Schema); - - context.ValidateCollection(header.Examples); - - context.ValidateMap(header.Content); - - base.Next(context, header); - } - } -} diff --git a/src/Microsoft.OpenApi/Validations/Visitors/IVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/IVisitor.cs deleted file mode 100644 index df61d1563..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/IVisitor.cs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -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 deleted file mode 100644 index 9c5b167f2..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/InfoVisitor.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -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 (context == null) - { - throw Error.ArgumentNull(nameof(context)); - } - - if (info == null) - { - throw Error.ArgumentNull(nameof(info)); - } - - 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 deleted file mode 100644 index 2d9867830..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/LicenseVisitor.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -using Microsoft.OpenApi.Models; - -namespace Microsoft.OpenApi.Validations.Visitors -{ - /// - /// Visit . - /// - internal class LicenseVisitor : VisitorBase - { - } -} diff --git a/src/Microsoft.OpenApi/Validations/Visitors/LinkVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/LinkVisitor.cs deleted file mode 100644 index 3cead4c66..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/LinkVisitor.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -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) - { - if (context == null) - { - throw Error.ArgumentNull(nameof(context)); - } - - if (link == null) - { - throw Error.ArgumentNull(nameof(link)); - } - - 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 deleted file mode 100644 index 5b035e221..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/MediaTypeVisitor.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -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) - { - if (context == null) - { - throw Error.ArgumentNull(nameof(context)); - } - - if (mediaType == null) - { - throw Error.ArgumentNull(nameof(mediaType)); - } - - 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 deleted file mode 100644 index f2faa565e..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/OAuthFlowVisitor.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -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 deleted file mode 100644 index cf84b4311..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/OAuthFlowsVisitor.cs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -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) - { - if (context == null) - { - throw Error.ArgumentNull(nameof(context)); - } - - if (oAuthFlows == null) - { - throw Error.ArgumentNull(nameof(oAuthFlows)); - } - - 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 deleted file mode 100644 index 105dc3d9e..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/OpenApiVisitorSet.cs +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.OpenApi.Exceptions; -using Microsoft.OpenApi.Properties; -using Microsoft.OpenApi.Interfaces; - -namespace Microsoft.OpenApi.Validations.Visitors -{ - /// - /// Class to cache the . - /// - internal static class OpenApiVisitorSet - { - private static IDictionary _visitors; - - /// - /// Gets the visitors - /// - public static IDictionary Visitors - { - get - { - if (_visitors == null) - { - _visitors = new Lazy>(() => BuildVisitorSet(), isThreadSafe: false).Value; - } - - return _visitors; - } - } - /// - /// Get the element visitor. - /// - /// The element type. - /// The element visitor or null. - public static IVisitor GetVisitor(Type elementType) - { - IVisitor 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/src/Microsoft.OpenApi/Validations/Visitors/OperationVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/OperationVisitor.cs deleted file mode 100644 index 5095a740b..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/OperationVisitor.cs +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -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) - { - if (context == null) - { - throw Error.ArgumentNull(nameof(context)); - } - - if (operation == null) - { - throw Error.ArgumentNull(nameof(operation)); - } - - 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 deleted file mode 100644 index f4e42fcd1..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/ParameterVisitor.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -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) - { - if (context == null) - { - throw Error.ArgumentNull(nameof(context)); - } - - if (parameter == null) - { - throw Error.ArgumentNull(nameof(parameter)); - } - - 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 deleted file mode 100644 index 06154374f..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/PathItemVisitor.cs +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -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) - { - if (context == null) - { - throw Error.ArgumentNull(nameof(context)); - } - - if (pathItem == null) - { - throw Error.ArgumentNull(nameof(pathItem)); - } - - 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 deleted file mode 100644 index 6aac857c5..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/PathsVisitor.cs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -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) - { - 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 deleted file mode 100644 index eea6cb2cb..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/RequestBodyVisitor.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -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) - { - if (context == null) - { - throw Error.ArgumentNull(nameof(context)); - } - - if (requestBody == null) - { - throw Error.ArgumentNull(nameof(requestBody)); - } - - 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 deleted file mode 100644 index 2e653c836..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/ResponseVisitor.cs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -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) - { - if (context == null) - { - throw Error.ArgumentNull(nameof(context)); - } - - if (response == null) - { - throw Error.ArgumentNull(nameof(response)); - } - - 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 deleted file mode 100644 index c1c6461fc..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/ResponsesVisitor.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -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) - { - if (context == null) - { - throw Error.ArgumentNull(nameof(context)); - } - - if (responses == null) - { - throw Error.ArgumentNull(nameof(responses)); - } - - 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 deleted file mode 100644 index 76556d23b..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/SchemaVisitor.cs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -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) - { - if (context == null) - { - throw Error.ArgumentNull(nameof(context)); - } - - if (schema == null) - { - throw Error.ArgumentNull(nameof(schema)); - } - - 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 deleted file mode 100644 index 23cbb1a34..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/SecurityRequirementVisitor.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -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 deleted file mode 100644 index 1e19210e0..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/SecuritySchemeVisitor.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -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) - { - if (context == null) - { - throw Error.ArgumentNull(nameof(context)); - } - - if (securityScheme == null) - { - throw Error.ArgumentNull(nameof(securityScheme)); - } - - context.Validate(securityScheme.Flows); - - base.Next(context, securityScheme); - } - } -} diff --git a/src/Microsoft.OpenApi/Validations/Visitors/ServerVariableVisitor.cs b/src/Microsoft.OpenApi/Validations/Visitors/ServerVariableVisitor.cs deleted file mode 100644 index 5033f274d..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/ServerVariableVisitor.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -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 deleted file mode 100644 index 9068f1bfe..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/ServerVisitor.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -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) - { - if (context == null) - { - throw Error.ArgumentNull(nameof(context)); - } - - if (server == null) - { - throw Error.ArgumentNull(nameof(server)); - } - - context.ValidateMap(server.Variables); - - base.Next(context, server); - } - } -} diff --git a/src/Microsoft.OpenApi/Validations/Visitors/TagVistor.cs b/src/Microsoft.OpenApi/Validations/Visitors/TagVistor.cs deleted file mode 100644 index 5db0e4018..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/TagVistor.cs +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -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 (context == null) - { - throw Error.ArgumentNull(nameof(context)); - } - - if (tag == null) - { - throw Error.ArgumentNull(nameof(tag)); - } - - 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 deleted file mode 100644 index 378fcfb62..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/VisitorBase.cs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -using System.Linq; -using Microsoft.OpenApi.Interfaces; -using Microsoft.OpenApi.Properties; - -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) - { - 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) - { - 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; - Next(context, typedItem); - } - - /// - /// Visit the children. - /// - /// The validation context. - /// The element. - protected virtual void Next(ValidationContext context, T element) - { - } - } -} diff --git a/src/Microsoft.OpenApi/Validations/Visitors/XmlVistor.cs b/src/Microsoft.OpenApi/Validations/Visitors/XmlVistor.cs deleted file mode 100644 index 7a11486ac..000000000 --- a/src/Microsoft.OpenApi/Validations/Visitors/XmlVistor.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -using Microsoft.OpenApi.Models; - -namespace Microsoft.OpenApi.Validations.Visitors -{ - /// - /// Visit . - /// - internal class XmlVisitor : VisitorBase - { - } -} diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiComponentsValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiComponentsValidationTests.cs index 872f4a36a..c7e45495d 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiComponentsValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiComponentsValidationTests.cs @@ -3,8 +3,10 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Properties; +using Microsoft.OpenApi.Services; using Microsoft.OpenApi.Validations.Rules; using Xunit; @@ -26,8 +28,12 @@ public void ValidateKeyMustMatchRegularExpressionInComponents() } }; + var validator = new OpenApiValidator(); + validator.Visit(components); // Act - bool result = components.Validate(out errors); + errors = validator.Errors; + bool result = !errors.Any(); + // Assert Assert.False(result); diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs index 94cbb6920..1a582dca6 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs @@ -3,8 +3,10 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Properties; +using Microsoft.OpenApi.Services; using Xunit; namespace Microsoft.OpenApi.Validations.Tests @@ -23,7 +25,11 @@ public void ValidateEmailFieldIsEmailAddressInContact() }; // Act - bool result = Contact.Validate(out errors); + var validator = new OpenApiValidator(); + validator.Visit(Contact); + + errors = validator.Errors; + bool result = !errors.Any(); // Assert Assert.False(result); diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiExternalDocsValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiExternalDocsValidationTests.cs index 5989d7448..86ce31905 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiExternalDocsValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiExternalDocsValidationTests.cs @@ -3,8 +3,10 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Properties; +using Microsoft.OpenApi.Services; using Xunit; namespace Microsoft.OpenApi.Validations.Tests @@ -19,9 +21,13 @@ public void ValidateUrlIsRequiredInExternalDocs() OpenApiExternalDocs externalDocs = new OpenApiExternalDocs(); // Act - bool result = externalDocs.Validate(out errors); + var validator = new OpenApiValidator(); + validator.Visit(externalDocs); // Assert + errors = validator.Errors; + bool result = !errors.Any(); + Assert.False(result); Assert.NotNull(errors); ValidationError error = Assert.Single(errors); diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiInfoValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiInfoValidationTests.cs index 4945b84ab..41031ee3b 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiInfoValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiInfoValidationTests.cs @@ -6,6 +6,7 @@ using System.Linq; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Properties; +using Microsoft.OpenApi.Services; using Xunit; namespace Microsoft.OpenApi.Validations.Tests @@ -22,7 +23,12 @@ public void ValidateFieldIsRequiredInInfo() OpenApiInfo info = new OpenApiInfo(); // Act - bool result = info.Validate(out errors); + var validator = new OpenApiValidator(); + validator.Visit(info); + + // Assert + errors = validator.Errors; + bool result = !errors.Any(); // Assert Assert.False(result); diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiLicenseValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiLicenseValidationTests.cs index fb6fd9f35..163fb1307 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiLicenseValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiLicenseValidationTests.cs @@ -3,8 +3,10 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Properties; +using Microsoft.OpenApi.Services; using Xunit; namespace Microsoft.OpenApi.Validations.Tests @@ -19,7 +21,10 @@ public void ValidateFieldIsRequiredInLicense() OpenApiLicense license = new OpenApiLicense(); // Act - bool result = license.Validate(out errors); + var validator = new OpenApiValidator(); + validator.Visit(license); + errors = validator.Errors; + bool result = !errors.Any(); // Assert Assert.False(result); diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiOAuthFlowValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiOAuthFlowValidationTests.cs index 776ccbd45..e4a6fa586 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiOAuthFlowValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiOAuthFlowValidationTests.cs @@ -6,6 +6,7 @@ using System.Linq; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Properties; +using Microsoft.OpenApi.Services; using Xunit; namespace Microsoft.OpenApi.Validations.Tests @@ -22,7 +23,10 @@ public void ValidateFixedFieldsIsRequiredInResponse() OpenApiOAuthFlow oAuthFlow = new OpenApiOAuthFlow(); // Act - bool result = oAuthFlow.Validate(out errors); + var validator = new OpenApiValidator(); + validator.Visit(oAuthFlow); + errors = validator.Errors; + bool result = !errors.Any(); // Assert Assert.False(result); diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiResponseValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiResponseValidationTests.cs index eb0a339af..390e093e1 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiResponseValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiResponseValidationTests.cs @@ -3,8 +3,10 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Properties; +using Microsoft.OpenApi.Services; using Xunit; namespace Microsoft.OpenApi.Validations.Tests @@ -19,7 +21,10 @@ public void ValidateDescriptionIsRequiredInResponse() OpenApiResponse response = new OpenApiResponse(); // Act - bool result = response.Validate(out errors); + var validator = new OpenApiValidator(); + validator.Visit(response); + errors = validator.Errors; + bool result = !errors.Any(); // Assert Assert.False(result); diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiServerValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiServerValidationTests.cs index bbbb05588..f3f5ece46 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiServerValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiServerValidationTests.cs @@ -3,8 +3,10 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Properties; +using Microsoft.OpenApi.Services; using Xunit; namespace Microsoft.OpenApi.Validations.Tests @@ -19,7 +21,10 @@ public void ValidateFieldIsRequiredInServer() OpenApiServer server = new OpenApiServer(); // Act - bool result = server.Validate(out errors); + var validator = new OpenApiValidator(); + validator.Visit(server); + errors = validator.Errors; + bool result = !errors.Any(); // Assert Assert.False(result); diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiTagValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiTagValidationTests.cs index e34c125f1..0b8975113 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiTagValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiTagValidationTests.cs @@ -3,9 +3,11 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.OpenApi.Any; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Properties; +using Microsoft.OpenApi.Services; using Xunit; namespace Microsoft.OpenApi.Validations.Tests @@ -20,7 +22,10 @@ public void ValidateNameIsRequiredInTag() OpenApiTag tag = new OpenApiTag(); // Act - bool result = tag.Validate(out errors); + var validator = new OpenApiValidator(); + validator.Visit(tag); + errors = validator.Errors; + bool result = !errors.Any(); // Assert Assert.False(result); @@ -41,7 +46,10 @@ public void ValidateExtensionNameStartsWithXDashInTag() tag.Extensions.Add("tagExt", new OpenApiString("value")); // Act - bool result = tag.Validate(out errors); + var validator = new OpenApiValidator(); + validator.Visit(tag); + errors = validator.Errors; + bool result = errors.Any(); // Assert Assert.False(result); diff --git a/test/Microsoft.OpenApi.Tests/Validations/Visitors/OpenApiVisitorSetTests.cs b/test/Microsoft.OpenApi.Tests/Validations/Visitors/OpenApiVisitorSetTests.cs index 81ce4b451..460b1dba4 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/Visitors/OpenApiVisitorSetTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/Visitors/OpenApiVisitorSetTests.cs @@ -1,80 +1,80 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. +//// Copyright (c) Microsoft Corporation. All rights reserved. +//// Licensed under the MIT license. -using System; -using Microsoft.OpenApi.Exceptions; -using Microsoft.OpenApi.Models; -using Xunit; +//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; +//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); // Now, we have 29 DOM classes. - } - } +// // Assert +// Assert.NotNull(visitors); +// Assert.NotEmpty(visitors); +// Assert.Equal(29, visitors.Count); // Now, we have 29 DOM classes. +// } +// } - [Fact] - public void GetVisitorThrowsUnknowElementType() - { - // Arrange & Act - Action test = () => OpenApiVisitorSet.GetVisitor(typeof(OpenApiVisitorSetTests)); +// [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); - } +// // 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(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))] - public void GetVisitorReturnsTheCorrectVisitor(Type elementType, Type visitorType) - { - // Arrange & Act - var visitor = OpenApiVisitorSet.GetVisitor(elementType); +// [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))] +// public void GetVisitorReturnsTheCorrectVisitor(Type elementType, Type visitorType) +// { +// // Arrange & Act +// var visitor = OpenApiVisitorSet.GetVisitor(elementType); - // Assert - Assert.NotNull(visitor); - Assert.Same(visitorType, visitor.GetType()); - } - } -} +// // Assert +// Assert.NotNull(visitor); +// Assert.Same(visitorType, visitor.GetType()); +// } +// } +//} From 43ae5162394f03e419822fbf837e1010549fada3 Mon Sep 17 00:00:00 2001 From: Darrel Miller Date: Wed, 20 Dec 2017 14:04:44 -0500 Subject: [PATCH 15/18] Enhancements to walker --- .../Services/OpenApiValidator.cs | 57 ------- .../Services/OpenApiVisitorBase.cs | 21 +++ .../Services/OpenApiWalker.cs | 161 ++++++++++++------ .../Validations/OpenApiValidator.cs | 119 +++++++++++++ .../Validations/OpenApiTagValidationTests.cs | 5 +- 5 files changed, 251 insertions(+), 112 deletions(-) delete mode 100644 src/Microsoft.OpenApi/Services/OpenApiValidator.cs create mode 100644 src/Microsoft.OpenApi/Validations/OpenApiValidator.cs diff --git a/src/Microsoft.OpenApi/Services/OpenApiValidator.cs b/src/Microsoft.OpenApi/Services/OpenApiValidator.cs deleted file mode 100644 index 70fa07715..000000000 --- a/src/Microsoft.OpenApi/Services/OpenApiValidator.cs +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT license. - -using System.Collections.Generic; -using System.Linq; -using Microsoft.OpenApi.Exceptions; -using Microsoft.OpenApi.Models; -using Microsoft.OpenApi.Validations; - -namespace Microsoft.OpenApi.Services -{ - /// - /// Class containing logic to validate an Open API document object. - /// - public class OpenApiValidator : OpenApiVisitorBase - { - readonly ValidationRuleSet _ruleSet; - readonly ValidationContext _context; - - /// - /// Create a vistor that will validate an OpenAPIDocument - /// - /// - public OpenApiValidator(ValidationRuleSet ruleSet = null) - { - _ruleSet = ruleSet ?? ValidationRuleSet.DefaultRuleSet; - _context = new ValidationContext(_ruleSet); - } - - public override void Visit(OpenApiDocument item) => Validate(item); - public override void Visit(OpenApiInfo item) => Validate(item); - public override void Visit(OpenApiContact item) => Validate(item); - public override void Visit(OpenApiComponents item) => Validate(item); - - public override void Visit(OpenApiResponse item) => Validate(item); - - public override void Visit(OpenApiExternalDocs item) => Validate(item); - - public override void Visit(OpenApiLicense item) => Validate(item); - public override void Visit(OpenApiOAuthFlow item) => Validate(item); - public override void Visit(OpenApiTag item) => Validate(item); - public override void Visit(OpenApiServer item) => Validate(item); - - - public IEnumerable Errors => _context.Errors; - - private void Validate(T item) - { - if (item == null) return; // Required fields should be checked by higher level objects - var rules = _ruleSet.Where(r => r.ElementType == typeof(T)); - foreach (var rule in rules) - { - rule.Evaluate(_context, item); - } - } - } -} \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs b/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs index 6993047c5..c11901df0 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs @@ -125,6 +125,13 @@ public virtual void Visit(OpenApiResponse response) { } + /// + /// Visits + /// + public virtual void Visit(OpenApiResponses response) + { + } + /// /// Visits media type content. /// @@ -194,5 +201,19 @@ public virtual void Visit(OpenApiTag tag) public virtual void Visit(OpenApiOAuthFlow openApiOAuthFlow) { } + + /// + /// Visits + /// + public virtual void Visit(IList openApiTags) + { + } + + /// + /// Visits + /// + public virtual void Visit(IOpenApiExtensible openApiExtensible) + { + } } } \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Services/OpenApiWalker.cs b/src/Microsoft.OpenApi/Services/OpenApiWalker.cs index c99a3f368..a06d6a495 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiWalker.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiWalker.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.Interfaces; namespace Microsoft.OpenApi.Services { @@ -22,81 +24,134 @@ public OpenApiWalker(OpenApiVisitorBase visitor) } /// - /// Walks through the and validates each element. + /// Walks through the and visits each element. /// - /// + /// OpenApiDocument to be walked public void Walk(OpenApiDocument doc) { _visitor.Visit(doc); _visitor.Visit(doc.Info); _visitor.Visit(doc.Servers); + // Visit Servers if (doc.Servers != null) { foreach (var server in doc.Servers) { - _visitor.Visit(server); - foreach (var variable in server.Variables.Values) - { - _visitor.Visit(variable); - } + WalkServer(server); } } + // Visit Paths _visitor.Visit(doc.Paths); foreach (var pathItem in doc.Paths.Values) { - _visitor.Visit(pathItem); - _visitor.Visit(pathItem.Operations); - foreach (var operation in pathItem.Operations.Values) + WalkPathItem(pathItem); + } + + _visitor.Visit(doc.Components); + + _visitor.Visit(doc.ExternalDocs); + + _visitor.Visit(doc.Tags); + + foreach (var tag in doc.Tags) + { + WalkTag(tag); + } + + _visitor.Visit(doc as IOpenApiExtensible); + + } + + private void WalkTag(OpenApiTag tag) + { + _visitor.Visit(tag); + _visitor.Visit(tag.ExternalDocs); + _visitor.Visit(tag as IOpenApiExtensible); + } + + private void WalkServer(OpenApiServer server) + { + _visitor.Visit(server); + foreach (var variable in server.Variables.Values) + { + _visitor.Visit(variable); + } + _visitor.Visit(server as IOpenApiExtensible); + } + + private void WalkPathItem(OpenApiPathItem pathItem) + { + _visitor.Visit(pathItem); + _visitor.Visit(pathItem.Operations); + foreach (var operation in pathItem.Operations.Values) + { + WalkOperation(operation); + } + _visitor.Visit(pathItem as IOpenApiExtensible); + } + + private void WalkOperation(OpenApiOperation operation) + { + _visitor.Visit(operation); + if (operation.Parameters != null) + { + _visitor.Visit(operation.Parameters); + foreach (var parameter in operation.Parameters) { - _visitor.Visit(operation); - if (operation.Parameters != null) - { - _visitor.Visit(operation.Parameters); - foreach (var parameter in operation.Parameters) - { - _visitor.Visit(parameter); - } - } - - if (operation.RequestBody != null) - { - _visitor.Visit(operation.RequestBody); - - if (operation.RequestBody.Content != null) - { - WalkContent(operation.RequestBody.Content); - } - } - - if (operation.Responses != null) - { - _visitor.Visit(operation.Responses); - - foreach (var response in operation.Responses.Values) - { - _visitor.Visit(response); - WalkContent(response.Content); - - if (response.Links != null) - { - _visitor.Visit(response.Links); - foreach (var link in response.Links.Values) - { - _visitor.Visit(link); - } - } - } - } + _visitor.Visit(parameter); } } + + WalkRequestBody(operation.RequestBody); + + if (operation.Responses != null) + { + _visitor.Visit(operation.Responses); + + foreach (var response in operation.Responses.Values) + { + WalkResponse(response); + } + + _visitor.Visit(operation.Responses as IOpenApiExtensible); + } } - /// - /// Walks through each media type in content and validates. - /// - private void WalkContent(IDictionary content) + private void WalkResponse(OpenApiResponse response) + { + _visitor.Visit(response); + WalkContentMap(response.Content); + + if (response.Links != null) + { + _visitor.Visit(response.Links); + foreach (var link in response.Links.Values) + { + _visitor.Visit(link); + } + } + + _visitor.Visit(response as IOpenApiExtensible); + } + + private void WalkRequestBody(OpenApiRequestBody requestBody) + { + if (requestBody != null) + { + _visitor.Visit(requestBody); + + if (requestBody.Content != null) + { + WalkContentMap(requestBody.Content); + } + + _visitor.Visit(requestBody as IOpenApiExtensible); + } + } + + private void WalkContentMap(IDictionary content) { if (content == null) { diff --git a/src/Microsoft.OpenApi/Validations/OpenApiValidator.cs b/src/Microsoft.OpenApi/Validations/OpenApiValidator.cs new file mode 100644 index 000000000..28565be9a --- /dev/null +++ b/src/Microsoft.OpenApi/Validations/OpenApiValidator.cs @@ -0,0 +1,119 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System.Collections.Generic; +using System.Linq; +using Microsoft.OpenApi.Exceptions; +using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Services; +using Microsoft.OpenApi.Validations; + +namespace Microsoft.OpenApi.Validations +{ + /// + /// Class containing dispatchers to execute validation rules on for Open API document. + /// + public class OpenApiValidator : OpenApiVisitorBase + { + readonly ValidationRuleSet _ruleSet; + readonly ValidationContext _context; + + /// + /// Create a vistor that will validate an OpenAPIDocument + /// + /// + public OpenApiValidator(ValidationRuleSet ruleSet = null) + { + _ruleSet = ruleSet ?? ValidationRuleSet.DefaultRuleSet; + _context = new ValidationContext(_ruleSet); + } + + /// + /// Execute validation rules against an + /// + /// The object to be validated + public override void Visit(OpenApiDocument item) => Validate(item); + + /// + /// Execute validation rules against an + /// + /// The object to be validated + public override void Visit(OpenApiInfo item) => Validate(item); + + /// + /// Execute validation rules against an + /// + /// The object to be validated + public override void Visit(OpenApiContact item) => Validate(item); + + /// + /// Execute validation rules against an + /// + /// The object to be validated + public override void Visit(OpenApiComponents item) => Validate(item); + + /// + /// Execute validation rules against an + /// + /// The object to be validated + public override void Visit(OpenApiResponse item) => Validate(item); + + /// + /// Execute validation rules against an + /// + /// The object to be validated + public override void Visit(OpenApiResponses item) => Validate(item); + + /// + /// Execute validation rules against an + /// + /// The object to be validated + public override void Visit(OpenApiExternalDocs item) => Validate(item); + + /// + /// Execute validation rules against an + /// + /// The object to be validated + public override void Visit(OpenApiLicense item) => Validate(item); + + /// + /// Execute validation rules against an + /// + /// The object to be validated + public override void Visit(OpenApiOAuthFlow item) => Validate(item); + + /// + /// Execute validation rules against an + /// + /// The object to be validated + public override void Visit(OpenApiTag item) => Validate(item); + + /// + /// Execute validation rules against an + /// + /// The object to be validated + public override void Visit(OpenApiServer item) => Validate(item); + + /// + /// Execute validation rules against an + /// + /// The object to be validated + public override void Visit(IOpenApiExtensible item) => Validate(item); + + /// + /// Errors accumulated while validating OpenAPI elements + /// + public IEnumerable Errors => _context.Errors; + + private void Validate(T item) + { + if (item == null) return; // Required fields should be checked by higher level objects + var rules = _ruleSet.Where(r => r.ElementType == typeof(T)); + foreach (var rule in rules) + { + rule.Evaluate(_context, item); + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiTagValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiTagValidationTests.cs index 0b8975113..56696ffac 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiTagValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiTagValidationTests.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using Microsoft.OpenApi.Any; +using Microsoft.OpenApi.Interfaces; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Properties; using Microsoft.OpenApi.Services; @@ -47,9 +48,9 @@ public void ValidateExtensionNameStartsWithXDashInTag() // Act var validator = new OpenApiValidator(); - validator.Visit(tag); + validator.Visit(tag as IOpenApiExtensible); errors = validator.Errors; - bool result = errors.Any(); + bool result = !errors.Any(); // Assert Assert.False(result); From fc4c3341a931b1dc27039fd7829ea4ec8af2bc6c Mon Sep 17 00:00:00 2001 From: Darrel Miller Date: Wed, 20 Dec 2017 14:19:17 -0500 Subject: [PATCH 16/18] Removed unused test --- .../Visitors/OpenApiVisitorSetTests.cs | 80 ------------------- 1 file changed, 80 deletions(-) delete mode 100644 test/Microsoft.OpenApi.Tests/Validations/Visitors/OpenApiVisitorSetTests.cs diff --git a/test/Microsoft.OpenApi.Tests/Validations/Visitors/OpenApiVisitorSetTests.cs b/test/Microsoft.OpenApi.Tests/Validations/Visitors/OpenApiVisitorSetTests.cs deleted file mode 100644 index 460b1dba4..000000000 --- a/test/Microsoft.OpenApi.Tests/Validations/Visitors/OpenApiVisitorSetTests.cs +++ /dev/null @@ -1,80 +0,0 @@ -//// 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); // Now, we have 29 DOM classes. -// } -// } - -// [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(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))] -// public void GetVisitorReturnsTheCorrectVisitor(Type elementType, Type visitorType) -// { -// // Arrange & Act -// var visitor = OpenApiVisitorSet.GetVisitor(elementType); - -// // Assert -// Assert.NotNull(visitor); -// Assert.Same(visitorType, visitor.GetType()); -// } -// } -//} From 2fd11fa8f6098bf8d05b41dbd97a7cfcc1a44be2 Mon Sep 17 00:00:00 2001 From: Darrel Miller Date: Tue, 2 Jan 2018 15:50:25 -0500 Subject: [PATCH 17/18] Updated Walker to use separate methods for each element --- .../Services/OpenApiVisitorBase.cs | 7 + .../Services/OpenApiWalker.cs | 317 +++++++++++++++--- .../Validations/OpenApiValidator.cs | 14 + .../OpenApiComponentsValidationTests.cs | 4 +- .../OpenApiContactValidationTests.cs | 5 +- .../OpenApiExternalDocsValidationTests.cs | 3 +- .../Validations/OpenApiInfoValidationTests.cs | 4 +- .../OpenApiLicenseValidationTests.cs | 4 +- .../OpenApiOAuthFlowValidationTests.cs | 4 +- .../OpenApiResponseValidationTests.cs | 4 +- 10 files changed, 310 insertions(+), 56 deletions(-) diff --git a/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs b/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs index c11901df0..acb466d36 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs @@ -146,6 +146,13 @@ public virtual void Visit(OpenApiMediaType mediaType) { } + /// + /// Visits + /// + public virtual void Visit(OpenApiEncoding encoding) + { + } + /// /// Visits the examples. /// diff --git a/src/Microsoft.OpenApi/Services/OpenApiWalker.cs b/src/Microsoft.OpenApi/Services/OpenApiWalker.cs index a06d6a495..a0a75b2a2 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiWalker.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiWalker.cs @@ -30,48 +30,134 @@ public OpenApiWalker(OpenApiVisitorBase visitor) public void Walk(OpenApiDocument doc) { _visitor.Visit(doc); - _visitor.Visit(doc.Info); - _visitor.Visit(doc.Servers); - // Visit Servers - if (doc.Servers != null) - { - foreach (var server in doc.Servers) - { - WalkServer(server); - } - } + Walk(doc.Info); + Walk(doc.Servers); + Walk(doc.Paths); + Walk(doc.Components); + Walk(doc.ExternalDocs); + Walk(doc.Tags); + Walk(doc as IOpenApiExtensible); + } + + /// + /// Walk Tags and child objects + /// + /// + internal void Walk(IList tags) + { + _visitor.Visit(tags); - // Visit Paths - _visitor.Visit(doc.Paths); - foreach (var pathItem in doc.Paths.Values) + foreach (var tag in tags) { - WalkPathItem(pathItem); + Walk(tag); } + } + + /// + /// Walk External Docs + /// + /// + internal void Walk(OpenApiExternalDocs externalDocs) + { + _visitor.Visit(externalDocs); + } - _visitor.Visit(doc.Components); + /// + /// Walk Components + /// + /// + internal void Walk(OpenApiComponents components) + { + _visitor.Visit(components); + } - _visitor.Visit(doc.ExternalDocs); + /// + /// Walk Paths and all child objects + /// + /// + internal void Walk(OpenApiPaths paths) + { + _visitor.Visit(paths); + foreach (var pathItem in paths.Values) + { + Walk(pathItem); + } + } - _visitor.Visit(doc.Tags); + /// + /// Walk Servers object and all child objects + /// + /// + internal void Walk(IList servers) + { + _visitor.Visit(servers); - foreach (var tag in doc.Tags) + // Visit Servers + if (servers != null) { - WalkTag(tag); + foreach (var server in servers) + { + Walk(server); + } } + } + + /// + /// Walk the info object and all child objects + /// + /// + internal void Walk(OpenApiInfo info) + { + _visitor.Visit(info); + Walk(info.Contact); + Walk(info.License); + Walk(info as IOpenApiExtensible); + } + + /// + /// Walk Extensions + /// + /// + internal void Walk(IOpenApiExtensible openApiExtensible) + { + _visitor.Visit(openApiExtensible); + } - _visitor.Visit(doc as IOpenApiExtensible); + /// + /// Walk License object + /// + /// + internal void Walk(OpenApiLicense license) + { + _visitor.Visit(license); + } + /// + /// Walk Contact object + /// + /// + internal void Walk(OpenApiContact contact) + { + _visitor.Visit(contact); } - private void WalkTag(OpenApiTag tag) + /// + /// Walk Tag Object + /// + /// + internal void Walk(OpenApiTag tag) { _visitor.Visit(tag); _visitor.Visit(tag.ExternalDocs); _visitor.Visit(tag as IOpenApiExtensible); } - private void WalkServer(OpenApiServer server) + /// + /// Walk Server Object + /// + /// + internal void Walk(OpenApiServer server) { _visitor.Visit(server); foreach (var variable in server.Variables.Values) @@ -81,48 +167,97 @@ private void WalkServer(OpenApiServer server) _visitor.Visit(server as IOpenApiExtensible); } - private void WalkPathItem(OpenApiPathItem pathItem) + /// + /// Walk Path Item + /// + /// + internal void Walk(OpenApiPathItem pathItem) { _visitor.Visit(pathItem); - _visitor.Visit(pathItem.Operations); - foreach (var operation in pathItem.Operations.Values) + + Walk(pathItem.Operations); + _visitor.Visit(pathItem as IOpenApiExtensible); + } + + /// + /// Walk Operations + /// + /// + internal void Walk(IDictionary operations) + { + _visitor.Visit(operations); + foreach (var operation in operations.Values) { - WalkOperation(operation); + Walk(operation); } - _visitor.Visit(pathItem as IOpenApiExtensible); } - private void WalkOperation(OpenApiOperation operation) + /// + /// Walk Operation + /// + /// + internal void Walk(OpenApiOperation operation) { _visitor.Visit(operation); - if (operation.Parameters != null) + + Walk(operation.Parameters); + Walk(operation.RequestBody); + Walk(operation.Responses); + Walk(operation as IOpenApiExtensible); + } + + /// + /// Walk Parameters + /// + /// + internal void Walk(IList parameters) + { + if (parameters != null) { - _visitor.Visit(operation.Parameters); - foreach (var parameter in operation.Parameters) + _visitor.Visit(parameters); + foreach (var parameter in parameters) { - _visitor.Visit(parameter); + Walk(parameter); } } + } - WalkRequestBody(operation.RequestBody); + /// + /// Walk Parameter + /// + /// + internal void Walk(OpenApiParameter parameter) + { + _visitor.Visit(parameter); + } - if (operation.Responses != null) + /// + /// Walk Responses + /// + /// + internal void Walk(OpenApiResponses responses) + { + if (responses != null) { - _visitor.Visit(operation.Responses); + _visitor.Visit(responses); - foreach (var response in operation.Responses.Values) + foreach (var response in responses.Values) { - WalkResponse(response); + Walk(response); } - _visitor.Visit(operation.Responses as IOpenApiExtensible); + Walk(responses as IOpenApiExtensible); } } - private void WalkResponse(OpenApiResponse response) + /// + /// Walk Response + /// + /// + internal void Walk(OpenApiResponse response) { _visitor.Visit(response); - WalkContentMap(response.Content); + Walk(response.Content); if (response.Links != null) { @@ -136,7 +271,11 @@ private void WalkResponse(OpenApiResponse response) _visitor.Visit(response as IOpenApiExtensible); } - private void WalkRequestBody(OpenApiRequestBody requestBody) + /// + /// Walk RequestBody + /// + /// + internal void Walk(OpenApiRequestBody requestBody) { if (requestBody != null) { @@ -144,14 +283,18 @@ private void WalkRequestBody(OpenApiRequestBody requestBody) if (requestBody.Content != null) { - WalkContentMap(requestBody.Content); + Walk(requestBody.Content); } - _visitor.Visit(requestBody as IOpenApiExtensible); + Walk(requestBody as IOpenApiExtensible); } } - - private void WalkContentMap(IDictionary content) + + /// + /// Walk ContentMap + /// + /// + internal void Walk(IDictionary content) { if (content == null) { @@ -161,10 +304,88 @@ private void WalkContentMap(IDictionary content) _visitor.Visit(content); foreach (var mediaType in content.Values) { - _visitor.Visit(mediaType); - _visitor.Visit(mediaType.Examples); - _visitor.Visit(mediaType.Schema); + Walk(mediaType); } } + + /// + /// Walk MediaType + /// + /// + internal void Walk(OpenApiMediaType mediaType) + { + _visitor.Visit(mediaType); + + Walk(mediaType.Examples); + Walk(mediaType.Schema); + Walk(mediaType.Encoding); + Walk(mediaType as IOpenApiExtensible); + } + + /// + /// Walk Encodings + /// + /// + internal void Walk(IDictionary encoding) + { + foreach (var item in encoding.Values) + { + _visitor.Visit(item); + } + } + + /// + /// Walk Encoding + /// + /// + internal void Walk(OpenApiEncoding encoding) + { + _visitor.Visit(encoding); + Walk(encoding as IOpenApiExtensible); + } + + /// + /// Walk Schema + /// + /// + internal void Walk(OpenApiSchema schema) + { + _visitor.Visit(schema); + Walk(schema as IOpenApiExtensible); + } + + /// + /// Walk Examples + /// + /// + internal void Walk(IDictionary examples) + { + _visitor.Visit(examples); + foreach (var example in examples.Values) + { + Walk(example); + } + } + + /// + /// Walk Example + /// + /// + internal void Walk(OpenApiExample example) + { + _visitor.Visit(example); + Walk(example as IOpenApiExtensible); + } + + /// + /// Walk OAuthFlow + /// + /// + internal void Walk(OpenApiOAuthFlow oAuthFlow) + { + _visitor.Visit(oAuthFlow); + Walk(oAuthFlow as IOpenApiExtensible); + } + } } \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Validations/OpenApiValidator.cs b/src/Microsoft.OpenApi/Validations/OpenApiValidator.cs index 28565be9a..aa5428555 100644 --- a/src/Microsoft.OpenApi/Validations/OpenApiValidator.cs +++ b/src/Microsoft.OpenApi/Validations/OpenApiValidator.cs @@ -89,12 +89,26 @@ public OpenApiValidator(ValidationRuleSet ruleSet = null) /// The object to be validated public override void Visit(OpenApiTag item) => Validate(item); + /// + /// Execute validation rules against an + /// + /// The object to be validated + public override void Visit(OpenApiSchema item) => Validate(item); + + /// /// Execute validation rules against an /// /// The object to be validated public override void Visit(OpenApiServer item) => Validate(item); + /// + /// Execute validation rules against an + /// + /// The object to be validated + public override void Visit(OpenApiEncoding item) => Validate(item); + + /// /// Execute validation rules against an /// diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiComponentsValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiComponentsValidationTests.cs index c7e45495d..a8beb2468 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiComponentsValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiComponentsValidationTests.cs @@ -29,7 +29,9 @@ public void ValidateKeyMustMatchRegularExpressionInComponents() }; var validator = new OpenApiValidator(); - validator.Visit(components); + var walker = new OpenApiWalker(validator); + walker.Walk(components); + // Act errors = validator.Errors; bool result = !errors.Any(); diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs index 1a582dca6..8c803a944 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs @@ -19,14 +19,15 @@ public void ValidateEmailFieldIsEmailAddressInContact() // Arrange const string testEmail = "support/example.com"; IEnumerable errors; - OpenApiContact Contact = new OpenApiContact() + OpenApiContact contact = new OpenApiContact() { Email = testEmail }; // Act var validator = new OpenApiValidator(); - validator.Visit(Contact); + var walker = new OpenApiWalker(validator); + walker.Walk(contact); errors = validator.Errors; bool result = !errors.Any(); diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiExternalDocsValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiExternalDocsValidationTests.cs index 86ce31905..82c751722 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiExternalDocsValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiExternalDocsValidationTests.cs @@ -22,7 +22,8 @@ public void ValidateUrlIsRequiredInExternalDocs() // Act var validator = new OpenApiValidator(); - validator.Visit(externalDocs); + var walker = new OpenApiWalker(validator); + walker.Walk(externalDocs); // Assert errors = validator.Errors; diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiInfoValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiInfoValidationTests.cs index 41031ee3b..285d64be7 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiInfoValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiInfoValidationTests.cs @@ -24,7 +24,9 @@ public void ValidateFieldIsRequiredInInfo() // Act var validator = new OpenApiValidator(); - validator.Visit(info); + var walker = new OpenApiWalker(validator); + walker.Walk(info); + // Assert errors = validator.Errors; diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiLicenseValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiLicenseValidationTests.cs index 163fb1307..ad542ba5f 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiLicenseValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiLicenseValidationTests.cs @@ -22,7 +22,9 @@ public void ValidateFieldIsRequiredInLicense() // Act var validator = new OpenApiValidator(); - validator.Visit(license); + var walker = new OpenApiWalker(validator); + walker.Walk(license); + errors = validator.Errors; bool result = !errors.Any(); diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiOAuthFlowValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiOAuthFlowValidationTests.cs index e4a6fa586..3d38e60e8 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiOAuthFlowValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiOAuthFlowValidationTests.cs @@ -24,7 +24,9 @@ public void ValidateFixedFieldsIsRequiredInResponse() // Act var validator = new OpenApiValidator(); - validator.Visit(oAuthFlow); + var walker = new OpenApiWalker(validator); + walker.Walk(oAuthFlow); + errors = validator.Errors; bool result = !errors.Any(); diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiResponseValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiResponseValidationTests.cs index 390e093e1..494bc0274 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiResponseValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiResponseValidationTests.cs @@ -22,7 +22,9 @@ public void ValidateDescriptionIsRequiredInResponse() // Act var validator = new OpenApiValidator(); - validator.Visit(response); + var walker = new OpenApiWalker(validator); + walker.Walk(response); + errors = validator.Errors; bool result = !errors.Any(); From d6dba1067d705e05b8cad7193605469f089acba6 Mon Sep 17 00:00:00 2001 From: Darrel Miller Date: Wed, 3 Jan 2018 15:11:38 -0500 Subject: [PATCH 18/18] Updated walkers and added validate method --- .../Extensions/OpenApiElementExtensions.cs | 31 +++ .../Services/OpenApiVisitorBase.cs | 24 ++- .../Services/OpenApiWalker.cs | 191 +++++++++++++++--- .../Validations/OpenApiValidator.cs | 6 + .../OpenApiComponentsValidationTests.cs | 8 +- .../OpenApiContactValidationTests.cs | 9 +- .../OpenApiExternalDocsValidationTests.cs | 8 +- 7 files changed, 231 insertions(+), 46 deletions(-) create mode 100644 src/Microsoft.OpenApi/Extensions/OpenApiElementExtensions.cs diff --git a/src/Microsoft.OpenApi/Extensions/OpenApiElementExtensions.cs b/src/Microsoft.OpenApi/Extensions/OpenApiElementExtensions.cs new file mode 100644 index 000000000..757f419df --- /dev/null +++ b/src/Microsoft.OpenApi/Extensions/OpenApiElementExtensions.cs @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT license. + +using System; +using System.Collections.Generic; +using Microsoft.OpenApi.Interfaces; +using Microsoft.OpenApi.Models; +using Microsoft.OpenApi.Services; +using Microsoft.OpenApi.Validations; + +namespace Microsoft.OpenApi.Extensions +{ + /// + /// Extension methods that apply across all OpenAPIElements + /// + public static class OpenApiElementExtensions + { + /// + /// Validate element and all child elements + /// + /// + /// + /// + public static IEnumerable Validate(this IOpenApiElement element) { + var validator = new OpenApiValidator(); + var walker = new OpenApiWalker(validator); + walker.Walk(element); + return validator.Errors; + } + } +} diff --git a/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs b/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs index acb466d36..0a0bc2416 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiVisitorBase.cs @@ -196,12 +196,27 @@ public virtual void Visit(OpenApiLink link) { } + /// + /// Visits + /// + public virtual void Visit(OpenApiCallback callback) + { + } + /// /// Visits /// public virtual void Visit(OpenApiTag tag) { } + + /// + /// Visits + /// + public virtual void Visit(OpenApiHeader tag) + { + } + /// /// Visits /// @@ -210,7 +225,14 @@ public virtual void Visit(OpenApiOAuthFlow openApiOAuthFlow) } /// - /// Visits + /// Visits + /// + public virtual void Visit(OpenApiSecurityRequirement securityRequirement) + { + } + + /// + /// Visits list of /// public virtual void Visit(IList openApiTags) { diff --git a/src/Microsoft.OpenApi/Services/OpenApiWalker.cs b/src/Microsoft.OpenApi/Services/OpenApiWalker.cs index a0a75b2a2..23f898f85 100644 --- a/src/Microsoft.OpenApi/Services/OpenApiWalker.cs +++ b/src/Microsoft.OpenApi/Services/OpenApiWalker.cs @@ -24,7 +24,7 @@ public OpenApiWalker(OpenApiVisitorBase visitor) } /// - /// Walks through the and visits each element. + /// Visits list of and child objects /// /// OpenApiDocument to be walked public void Walk(OpenApiDocument doc) @@ -41,7 +41,7 @@ public void Walk(OpenApiDocument doc) } /// - /// Walk Tags and child objects + /// Visits list of and child objects /// /// internal void Walk(IList tags) @@ -55,7 +55,7 @@ internal void Walk(IList tags) } /// - /// Walk External Docs + /// Visits and child objects /// /// internal void Walk(OpenApiExternalDocs externalDocs) @@ -64,7 +64,7 @@ internal void Walk(OpenApiExternalDocs externalDocs) } /// - /// Walk Components + /// Visits and child objects /// /// internal void Walk(OpenApiComponents components) @@ -73,7 +73,7 @@ internal void Walk(OpenApiComponents components) } /// - /// Walk Paths and all child objects + /// Visits and child objects /// /// internal void Walk(OpenApiPaths paths) @@ -86,7 +86,7 @@ internal void Walk(OpenApiPaths paths) } /// - /// Walk Servers object and all child objects + /// Visits list of and child objects /// /// internal void Walk(IList servers) @@ -104,7 +104,7 @@ internal void Walk(IList servers) } /// - /// Walk the info object and all child objects + /// Visits and child objects /// /// internal void Walk(OpenApiInfo info) @@ -116,7 +116,7 @@ internal void Walk(OpenApiInfo info) } /// - /// Walk Extensions + /// Visits dictionary of extensions /// /// internal void Walk(IOpenApiExtensible openApiExtensible) @@ -125,7 +125,7 @@ internal void Walk(IOpenApiExtensible openApiExtensible) } /// - /// Walk License object + /// Visits and child objects /// /// internal void Walk(OpenApiLicense license) @@ -134,7 +134,7 @@ internal void Walk(OpenApiLicense license) } /// - /// Walk Contact object + /// Visits and child objects /// /// internal void Walk(OpenApiContact contact) @@ -143,7 +143,7 @@ internal void Walk(OpenApiContact contact) } /// - /// Walk Tag Object + /// Visits and child objects /// /// internal void Walk(OpenApiTag tag) @@ -154,7 +154,7 @@ internal void Walk(OpenApiTag tag) } /// - /// Walk Server Object + /// Visits and child objects /// /// internal void Walk(OpenApiServer server) @@ -162,13 +162,23 @@ internal void Walk(OpenApiServer server) _visitor.Visit(server); foreach (var variable in server.Variables.Values) { - _visitor.Visit(variable); + Walk(variable); } _visitor.Visit(server as IOpenApiExtensible); } /// - /// Walk Path Item + /// Visits and child objects + /// + /// + internal void Walk(OpenApiServerVariable serverVariable) + { + _visitor.Visit(serverVariable); + _visitor.Visit(serverVariable as IOpenApiExtensible); + } + + /// + /// Visits and child objects /// /// internal void Walk(OpenApiPathItem pathItem) @@ -180,7 +190,7 @@ internal void Walk(OpenApiPathItem pathItem) } /// - /// Walk Operations + /// Visits dictionary of /// /// internal void Walk(IDictionary operations) @@ -193,7 +203,7 @@ internal void Walk(IDictionary operations) } /// - /// Walk Operation + /// Visits and child objects /// /// internal void Walk(OpenApiOperation operation) @@ -207,7 +217,7 @@ internal void Walk(OpenApiOperation operation) } /// - /// Walk Parameters + /// Visits list of /// /// internal void Walk(IList parameters) @@ -223,16 +233,19 @@ internal void Walk(IList parameters) } /// - /// Walk Parameter + /// Visits and child objects /// /// internal void Walk(OpenApiParameter parameter) { _visitor.Visit(parameter); + Walk(parameter.Schema); + Walk(parameter.Content); + Walk(parameter as IOpenApiExtensible); } /// - /// Walk Responses + /// Visits and child objects /// /// internal void Walk(OpenApiResponses responses) @@ -251,7 +264,7 @@ internal void Walk(OpenApiResponses responses) } /// - /// Walk Response + /// Visits and child objects /// /// internal void Walk(OpenApiResponse response) @@ -272,7 +285,7 @@ internal void Walk(OpenApiResponse response) } /// - /// Walk RequestBody + /// Visits and child objects /// /// internal void Walk(OpenApiRequestBody requestBody) @@ -291,7 +304,7 @@ internal void Walk(OpenApiRequestBody requestBody) } /// - /// Walk ContentMap + /// Visits dictionary of /// /// internal void Walk(IDictionary content) @@ -309,7 +322,7 @@ internal void Walk(IDictionary content) } /// - /// Walk MediaType + /// Visits and child objects /// /// internal void Walk(OpenApiMediaType mediaType) @@ -323,7 +336,7 @@ internal void Walk(OpenApiMediaType mediaType) } /// - /// Walk Encodings + /// Visits dictionary of /// /// internal void Walk(IDictionary encoding) @@ -335,7 +348,7 @@ internal void Walk(IDictionary encoding) } /// - /// Walk Encoding + /// Visits and child objects /// /// internal void Walk(OpenApiEncoding encoding) @@ -345,17 +358,18 @@ internal void Walk(OpenApiEncoding encoding) } /// - /// Walk Schema + /// Visits and child objects /// /// internal void Walk(OpenApiSchema schema) { _visitor.Visit(schema); + Walk(schema.ExternalDocs); Walk(schema as IOpenApiExtensible); } /// - /// Walk Examples + /// Visits dictionary of /// /// internal void Walk(IDictionary examples) @@ -368,7 +382,7 @@ internal void Walk(IDictionary examples) } /// - /// Walk Example + /// Visits and child objects /// /// internal void Walk(OpenApiExample example) @@ -378,7 +392,29 @@ internal void Walk(OpenApiExample example) } /// - /// Walk OAuthFlow + /// Visits the list of and child objects + /// + /// + internal void Walk(IList examples) + { + foreach (var item in examples) + { + _visitor.Visit(item); + } + } + + /// + /// Visits and child objects + /// + /// + internal void Walk(OpenApiOAuthFlows flows) + { + _visitor.Visit(flows); + Walk(flows as IOpenApiExtensible); + } + + /// + /// Visits and child objects /// /// internal void Walk(OpenApiOAuthFlow oAuthFlow) @@ -387,5 +423,102 @@ internal void Walk(OpenApiOAuthFlow oAuthFlow) Walk(oAuthFlow as IOpenApiExtensible); } + /// + /// Visits dictionary of and child objects + /// + /// + internal void Walk(IDictionary links) + { + foreach (var item in links) + { + _visitor.Visit(item.Value); + } + } + + /// + /// Visits and child objects + /// + /// + internal void Walk(OpenApiLink link) + { + _visitor.Visit(link); + Walk(link.Server); + Walk(link as IOpenApiExtensible); + } + + /// + /// Visits and child objects + /// + /// + internal void Walk(OpenApiHeader header) + { + _visitor.Visit(header); + Walk(header.Content); + Walk(header.Example); + Walk(header.Examples); + Walk(header.Schema); + Walk(header as IOpenApiExtensible); + } + + /// + /// Visits and child objects + /// + /// + internal void Walk(OpenApiSecurityRequirement securityRequirement) + { + _visitor.Visit(securityRequirement); + Walk(securityRequirement as IOpenApiExtensible); + } + + /// + /// Visits and child objects + /// + /// + internal void Walk(OpenApiSecurityScheme securityScheme) + { + _visitor.Visit(securityScheme); + Walk(securityScheme as IOpenApiExtensible); + } + + /// + /// Walk IOpenApiElement + /// + /// + internal void Walk(IOpenApiElement element) + { + switch(element) + { + case OpenApiDocument e: Walk(e); break; + case OpenApiLicense e: Walk(e); break; + case OpenApiInfo e: Walk(e); break; + case OpenApiComponents e: Walk(e); break; + case OpenApiContact e: Walk(e); break; + case OpenApiCallback e: Walk(e); break; + case OpenApiEncoding e: Walk(e); break; + case OpenApiExample e: Walk(e); break; + case IDictionary e: Walk(e); break; + case OpenApiExternalDocs e: Walk(e); break; + case OpenApiHeader e: Walk(e); break; + case OpenApiLink e: Walk(e); break; + case IDictionary e: Walk(e); break; + case OpenApiMediaType e: Walk(e); break; + case OpenApiOAuthFlows e: Walk(e); break; + case OpenApiOAuthFlow e: Walk(e); break; + case OpenApiOperation e: Walk(e); break; + case OpenApiParameter e: Walk(e); break; + case OpenApiRequestBody e: Walk(e); break; + case OpenApiResponse e: Walk(e); break; + case OpenApiSchema e: Walk(e); break; + case OpenApiSecurityRequirement e: Walk(e); break; + case OpenApiSecurityScheme e: Walk(e); break; + case OpenApiServer e: Walk(e); break; + case OpenApiServerVariable e: Walk(e); break; + case OpenApiTag e: Walk(e); break; + case IList e: Walk(e); break; + case OpenApiXml e: Walk(e); break; + case IOpenApiExtensible e: Walk(e); break; + } + } + } } \ No newline at end of file diff --git a/src/Microsoft.OpenApi/Validations/OpenApiValidator.cs b/src/Microsoft.OpenApi/Validations/OpenApiValidator.cs index aa5428555..468544b94 100644 --- a/src/Microsoft.OpenApi/Validations/OpenApiValidator.cs +++ b/src/Microsoft.OpenApi/Validations/OpenApiValidator.cs @@ -108,6 +108,12 @@ public OpenApiValidator(ValidationRuleSet ruleSet = null) /// The object to be validated public override void Visit(OpenApiEncoding item) => Validate(item); + /// + /// Execute validation rules against an + /// + /// The object to be validated + public override void Visit(OpenApiCallback item) => Validate(item); + /// /// Execute validation rules against an diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiComponentsValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiComponentsValidationTests.cs index a8beb2468..43aaefe57 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiComponentsValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiComponentsValidationTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.OpenApi.Extensions; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Properties; using Microsoft.OpenApi.Services; @@ -19,7 +20,7 @@ public void ValidateKeyMustMatchRegularExpressionInComponents() { // Arrange const string key = "%@abc"; - IEnumerable errors; + OpenApiComponents components = new OpenApiComponents() { Responses = new Dictionary @@ -28,12 +29,9 @@ public void ValidateKeyMustMatchRegularExpressionInComponents() } }; - var validator = new OpenApiValidator(); - var walker = new OpenApiWalker(validator); - walker.Walk(components); + var errors = components.Validate(); // Act - errors = validator.Errors; bool result = !errors.Any(); diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs index 8c803a944..80f4743a7 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiContactValidationTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.OpenApi.Extensions; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Properties; using Microsoft.OpenApi.Services; @@ -18,18 +19,14 @@ public void ValidateEmailFieldIsEmailAddressInContact() { // Arrange const string testEmail = "support/example.com"; - IEnumerable errors; + OpenApiContact contact = new OpenApiContact() { Email = testEmail }; // Act - var validator = new OpenApiValidator(); - var walker = new OpenApiWalker(validator); - walker.Walk(contact); - - errors = validator.Errors; + var errors = contact.Validate(); bool result = !errors.Any(); // Assert diff --git a/test/Microsoft.OpenApi.Tests/Validations/OpenApiExternalDocsValidationTests.cs b/test/Microsoft.OpenApi.Tests/Validations/OpenApiExternalDocsValidationTests.cs index 82c751722..6f350d407 100644 --- a/test/Microsoft.OpenApi.Tests/Validations/OpenApiExternalDocsValidationTests.cs +++ b/test/Microsoft.OpenApi.Tests/Validations/OpenApiExternalDocsValidationTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Microsoft.OpenApi.Extensions; using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Properties; using Microsoft.OpenApi.Services; @@ -17,16 +18,13 @@ public class OpenApiExternalDocsValidationTests public void ValidateUrlIsRequiredInExternalDocs() { // Arrange - IEnumerable errors; OpenApiExternalDocs externalDocs = new OpenApiExternalDocs(); // Act - var validator = new OpenApiValidator(); - var walker = new OpenApiWalker(validator); - walker.Walk(externalDocs); + var errors = externalDocs.Validate(); // Assert - errors = validator.Errors; + bool result = !errors.Any(); Assert.False(result);