Skip to content

Implementation for the Open API validation#152

Merged
darrelmiller merged 12 commits into
microsoft:masterfrom
xuzhg:Validation
Jan 11, 2018
Merged

Implementation for the Open API validation#152
darrelmiller merged 12 commits into
microsoft:masterfrom
xuzhg:Validation

Conversation

@xuzhg

@xuzhg xuzhg commented Dec 15, 2017

Copy link
Copy Markdown
Contributor

See #125:

I want to implement the validation logic for the Open API element. Here's my design (it's not finished).
General design:
Can validation each part individually. for example, Developer create a object, he can call the validate api to validate the created object.
Validate rule can be customized. For example, developer can create their own validation rules.
Detail:
OpenApiElementValidator.cs has the public apis
.\Validations\Visitors has the visitor classes for each element.
.\Validations\Rules has the rule classes for each element.
Please have a look and give me some feedback.

@xuzhg xuzhg changed the title Validation Implementation for the Open API validation Dec 15, 2017

OpenApiExtensibleRules.ExtensionNameMustStartWithXDash,
// add more default rules.
};

@PerthCharern PerthCharern Dec 15, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will be difficult to track. People will forget to put the new rules in here since this list is in a different file than the rules themselves.

I am thinking of this:

  • Add DefaultRuleSet for every OpenApiXXXRules file
  • This main DefaultRuleSet is a concatenation of all DefaultRuleSet's

What do you think? #Closed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In fact, we might not need this concatenated list at all since it will be broken down in Add() below anyway.


In reply to: 157320269 [](ancestors = 157320269)

@xuzhg xuzhg Dec 15, 2017

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Basically, I want to use the attribute for each "DOM" classes same as Add the validator attribute, validation rule and validator #133.
    Let me think it more.

  2. Why do you think it will break the Add() method? That's in deed will call "Add()" method. #Resolved

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you wanted to create an OpenApiValidationRuleAttribute and annotate each rule with that attribute and then have a discovery mechanism that searched the assembly to find all classes with that attribute, then I would have no objection to that.


In reply to: 157321750 [](ancestors = 157321750)

@darrelmiller darrelmiller Dec 16, 2017

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall I like the direction of this PR. I think by adding a simple marker attribute to each rule, you can reflect over the rules and built a default ruleset. You should be able to determine the target type from the generic return type of the rule.

This should address the concern brought up by @PerthCharern that someone might forget to add the rule to the ruleset. #Resolved

@PerthCharern PerthCharern Dec 16, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xuzhg I didn't mean it will break the Add() method. I meant the list didn't have to be created because it would be enumerated in the Add() method anyway, so we can find a way to make it less complicated.

Anyway, I like @darrelmiller 's suggestion here. That would put the concern of when each rule should be used back to the rule itself. #Closed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed the design to use reflect.


In reply to: 157320436 [](ancestors = 157320436,157320269)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed.


In reply to: 157326026 [](ancestors = 157326026,157321750)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed.


In reply to: 157327739 [](ancestors = 157327739)

@darrelmiller

darrelmiller commented Dec 15, 2017

Copy link
Copy Markdown
Member

Can I make a suggestion for doing these kind of design reviews? It would be helpful to see the code changes for a couple of examples rather than a complete implementation. Trying to review changes to 60 files in the GitHub UI is harder than it should be. #WontFix


/// <summary>
/// Validate the Open API element.
/// </summary>

@darrelmiller darrelmiller Dec 16, 2017

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, so here's the deal! If I have to put up with these horrible comment blocks, I would like to see comments that actually justify their existence. So let's make the comment explain why I would pick one overload over the other so that I don't have to guess the reason based on what parameters are there. #Resolved

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great point. I will modify it.


In reply to: 157324617 [](ancestors = 157324617)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

modified.


In reply to: 157324939 [](ancestors = 157324939,157324617)

public ValidationContext(ValidationRuleSet ruleSet)
{
RuleSet = ruleSet ?? throw Error.ArgumentNull(nameof(ruleSet));
}

@darrelmiller darrelmiller Dec 16, 2017

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a personal preference, but I really don't like this syntax. I'd much rather see a guard clause and a separate assignment. Could we even use the Code Contracts library that would do static analysis? #WontFix

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will we have to enforce this though? It's a valid and acceptable C# syntax as far as I know, and I'm not sure if we should enforce it one way or another when someone submits a PR.


In reply to: 157325008 [](ancestors = 157325008)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's ok. And it's VS recommendation.


In reply to: 157626597 [](ancestors = 157626597,157325008)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's also VS recommendation to sort System. namespaces after Microsoft. namespaces. There be dragons with that argument ;-)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And nobody likes the idea of using Code Contracts so we actually get static analysis for these issues too?

/// The base visitor class for Open Api elements.
/// </summary>
internal abstract class VisitorBase<T> : IVisitor where T : IOpenApiElement
{

@darrelmiller darrelmiller Dec 16, 2017

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class has no state and we have an IVisitor interface. Is there any reason these two methods couldn't just be library functions? I don't think we need a base class for all the visitors. #Pending

@xuzhg xuzhg Dec 16, 2017

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each visitor has two parts:

  1. Visit itself's properties
  2. Go to visit it's Children.

Where:
a. Visit() method is for the #1, it has the same logic for all elements. So, put its at the base class.

b. Next() method is for the #2, it has different logic for all elements. So, each element visitor class should override Next() method to list the children.


In reply to: 157326815 [](ancestors = 157326815)

@darrelmiller darrelmiller Dec 19, 2017

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Part of the reason I initially used the visitor pattern was so that we could define the traversal of the OpenAPI DOM just once and then have different visitors for different purposes. Validation is the obvious Visitor, but code generation could be another. Either client code gen, or server code scaffolding.
The Visitor related classes in this PR are specifically tied to validation via the ValidationContext. Even the abstract base class and IVisitor interface.

If you look at the Visitor pattern https://en.wikipedia.org/wiki/Visitor_pattern here the Visitor implements all the visit methods for all the elements it is interested in. This allows the Visitor to hold shared state whilst doing the traversal. It also plays the role of the context object.

With the current approach, ignoring the fact that it is tied to validation, each new "visiting" capability needs to create classes for each and every element and then pass the context between them.

I'm going to see if I can use my walker and validationVisitor to execute your rules classes and we can compare the end result. #Pending

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure!


In reply to: 157663995 [](ancestors = 157663995)

@darrelmiller darrelmiller Dec 19, 2017

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was able to incorporate your Rules classes into the OpenApiValidator like this,

   public class OpenApiValidator : OpenApiVisitorBase
    {
        readonly ValidationRuleSet _ruleSet;
        readonly ValidationContext _context;

        /// <summary>
        /// Create a vistor that will validate an OpenAPIDocument
        /// </summary>
        /// <param name="ruleSet"></param>
        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<ValidationError> Errors => _context.Errors;

        private void Validate<T>(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);
            }
        }
    }
}

Obviously this doesn't have the Visit methods for all types yet, and some types may be more complex than just the simple expression. Also, the walker class would need to build the property path.
However, using this approach, we need one class for walking the DOM (we could create other walkers if other visitors need to traverse in a different way) and one class for dispatching the Visit methods to the rules. This makes implementing other types of Visitors easier by only requiring one new class.
My understanding is that this approach would remove the need for :

  • the Visitor class for each element
  • the OpenApiVisitorSet
  • the ValidationContextExtensions

I'm still not quite sure how the ValidationMap and ValidateCollection extensions fit into this approach.
#Pending

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's ok. Please share your thoughts and codes. We can discuss to select one or just merge together.


In reply to: 157841020 [](ancestors = 157841020)

@xuzhg

xuzhg commented Dec 18, 2017

Copy link
Copy Markdown
Contributor Author

@darrelmiller @PerthCharern I added the attribute for Rule classes. Would you please take a look at da5f90a #WontFix

@xuzhg

xuzhg commented Dec 18, 2017

Copy link
Copy Markdown
Contributor Author

@darrelmiller @PerthCharern I also added the attribute for visitor classes. Would you please take a look at :

4a6ca43 #WontFix

{
if (_visitors == null)
{
_visitors = new Lazy<IDictionary<Type, IVisitor>>(() => BuildVisitorSet(), isThreadSafe: false).Value;

@xuzhg xuzhg Dec 18, 2017

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe Lazy is unnecessary? #Closed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say the underlying operation is not that time consuming/complex, so I think it's unnecessary. But I don't really mind using it if you prefer.


In reply to: 157624690 [](ancestors = 157624690)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok.


In reply to: 157634816 [](ancestors = 157634816,157624690)

/// <summary>
/// The Validator attribute.
/// </summary>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]

@PerthCharern PerthCharern Dec 18, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Class [](start = 37, length = 5)

Why class attribute vs field/property attribute? #Resolved

@xuzhg xuzhg Dec 18, 2017

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's easy to target "Rule" Class.

If it's target on the Rule property, we should go through all the properties in all the types, it's time consuming. #Pending

@PerthCharern

Copy link
Copy Markdown
Contributor

You just meant the logic to traverse all the Visitors, not a C# attribute, right? I want to make sure I'm not missing something.


In reply to: 352585589 [](ancestors = 352585589)

@xuzhg

xuzhg commented Dec 19, 2017

Copy link
Copy Markdown
Contributor Author

@PerthCharern For the visitor, Yes. I have the interface IVisitor. That's enough. #WontFix


// Assert
var exception = Assert.Throws<OpenApiException>(test);
Assert.Equal("Can not find visitor type registered for type 'Microsoft.OpenApi.Validations.Visitors.Tests.OpenApiVisitorSetTests'.",

@PerthCharern PerthCharern Dec 19, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can not find visitor type registered for type [](start = 26, length = 45)

This should be a public const in the main project and referenced here. #Pending

@xuzhg xuzhg Dec 19, 2017

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@PerthCharern

Yes. I can use the
Assert.Equal(String.Format(SRResource.xxxxx, typeof(xxx).FullName), exception.Message);
However, it looks not good than the plain text in the test project. Because it can help us verify the error message thrown as our expected. What do you think?
#WontFix

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@PerthCharern

Yes. I can use the

Assert.Equal(String.Format(SRResource.xxxxx, typeof(xxx).FullName), exception.Message);

However, it looks not good than the plain text in the test project. Because it can help us verify the error message thrown as our expected. What do you think?


In reply to: 157649686 [](ancestors = 157649686)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should go with that instead of duplicating string literals. We are essentially checking that the exception thrown has the "right string", not that it is spelled correctly, etc.

I would say we get better verification from referring to the constant string in SRResource.


In reply to: 157832135 [](ancestors = 157832135)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed.


In reply to: 157882006 [](ancestors = 157882006,157832135)

public void VisitorsPropertyReturnsTheCorrectVisitorList()
{
for (int i = 0; i < 5; i++) // 5 is just a magic number
{

@PerthCharern PerthCharern Dec 19, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure what the loop helps with. Maybe remove? #Closed

@xuzhg xuzhg Dec 19, 2017

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just want to test multiple retrieving the property "Visitors" can work as expected. #Closed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just want to test multiple retrieving the property "Visitors" can work as expected.


In reply to: 157649800 [](ancestors = 157649800)

// Assert
Assert.NotNull(visitors);
Assert.NotEmpty(visitors);
Assert.Equal(29, visitors.Count);

@PerthCharern PerthCharern Dec 19, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assert.Equal(29, visitors.Count); [](start = 16, length = 33)

Provide comment #Closed

[InlineData(typeof(OpenApiDocument), typeof(DocumentVisitor))]
[InlineData(typeof(OpenApiInfo), typeof(InfoVisitor))]
[InlineData(typeof(OpenApiXml), typeof(XmlVisitor))]
[InlineData(typeof(OpenApiComponents), typeof(ComponentsVisitor))]

@PerthCharern PerthCharern Dec 19, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[InlineData(typeof(OpenApiComponents), typeof(ComponentsVisitor))] [](start = 8, length = 66)

Can we add all types here? It's a one time thing, and it will help ensure no mistakes in the real code. #Closed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added.


In reply to: 157649917 [](ancestors = 157649917)

ruleSet.Add(rule);
}
}
}

@PerthCharern PerthCharern Dec 19, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a little risky to me that we rely on the type and not an attribute assigned to each property. #Closed

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is okay for now though. I don't have a better way than to tag all properties, which is something we want to avoid.


In reply to: 157650194 [](ancestors = 157650194)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. Basically, that's convention. It's ok if we follow up this convention.


In reply to: 157650606 [](ancestors = 157650606,157650194)

{
rule.Evaluate(context, extensbile);
}
}

@PerthCharern PerthCharern Dec 19, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this not done in Visit above? #Pending

@xuzhg xuzhg Dec 19, 2017

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:) My design is

  1. Visit() method is used to verify the itself properities
  2. Next() method is used to verify the Children element.

So, I think Extension belongs to the Children scope. What do you think? #WontFix

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:) My design is

  1. Visit() method is used to verify the itself properities
  2. Next() method is used to verify the Children element.
    So, I think Extension belongs to the Children scope. What do you think?

In reply to: 157650373 [](ancestors = 157650373)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why does Extension fall into the Children scope? Isn't it one of the properties?


In reply to: 157838009 [](ancestors = 157838009,157650373)

/// </summary>
/// <param name="context">The validation context.</param>
/// <param name="item">The element.</param>
public void Visit(ValidationContext context, object item)

@PerthCharern PerthCharern Dec 19, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

object [](start = 53, length = 6)

Can this be T? #WontFix

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried, But i can't.


In reply to: 157650480 [](ancestors = 157650480)

/// <param name="element">The element.</param>
protected virtual void Next(ValidationContext context, T element)
{
IOpenApiExtensible extensbile = element as IOpenApiExtensible;

@PerthCharern PerthCharern Dec 19, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extensbile [](start = 31, length = 10)

typo, extensible #Closed

@xuzhg xuzhg Dec 19, 2017

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Resolved. #Resolved

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));

@PerthCharern PerthCharern Dec 19, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These strings in all test classes should come from the main project, so that we don't need to keep track of the strings in two places. #Pending

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as others:

Yes. I can use the

Assert.Equal(String.Format(SRResource.xxxxx, typeof(xxx).FullName), exception.Message);

However, it looks not good than the plain text in the test project. Because it can help us verify the error message thrown as expected. What do you think?


In reply to: 157650687 [](ancestors = 157650687)

/// <returns>The error string.</returns>
public override string ToString()
{
return "ErroCode: " + ErrorCode + ", " + ErrorPath + " | " + ErrorMessage;

@PerthCharern PerthCharern Dec 19, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ErroCode [](start = 20, length = 8)

typo, ErrorCode #Closed

Assert.False(result);
Assert.NotNull(errors);
ValidationError error = Assert.Single(errors);
Assert.Equal("The field 'description' in 'response' object is REQUIRED.", error.ErrorMessage);

@PerthCharern PerthCharern Dec 19, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we check that the path and the reason are also as expected? #Closed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. I added such here.

Such test cases is helping us to understand the design. I'd like to make sure we can finalize the design first.


In reply to: 157651079 [](ancestors = 157651079)

protected override void Next(ValidationContext context, OpenApiPathItem pathItem)
{
Debug.Assert(context != null);
Debug.Assert(pathItem != null);

@PerthCharern PerthCharern Dec 19, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We haven't been using this anywhere else. Can we be consistent and use the normal null checks? #Pending

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Perth,

Yes. However, it's a protected method. It's only be called from "Visit(...)" method. So, I think it's only to add such Debug assert.

And, yes. Maybe I should add the null check at the "Visit(...)" method.


In reply to: 157651170 [](ancestors = 157651170)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

protected class is practically a public class (since our customer can technically just create a class that inherits the PathItemVisitor and calls the method).

I think we should do the null check here.


In reply to: 157838274 [](ancestors = 157838274,157651170)

new ValidationRule<OpenApiExternalDocs>(
(context, item) =>
{
// title

@PerthCharern PerthCharern Dec 19, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

title [](start = 23, length = 5)

url #Closed

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thx.


In reply to: 157651258 [](ancestors = 157651258)

new ValidationRule<OpenApiResponse>(
(context, response) =>
{
// title

@PerthCharern PerthCharern Dec 19, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

title [](start = 23, length = 5)

description #Resolved

public static class OpenApiTagRules
{
/// <summary>
/// REQUIRED.

@PerthCharern PerthCharern Dec 19, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

REQUIRED [](start = 12, length = 8)

change comment #Resolved

/// Input string must be in the format of an email address
/// </summary>
/// <param name="input">The input string.</param>
/// <returns></returns>

@PerthCharern PerthCharern Dec 19, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove or fill in #Resolved

/// </summary>
/// <param name="input">The input string.</param>
/// <returns></returns>
public static bool IsEmailAddress(this string input)

@PerthCharern PerthCharern Dec 19, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IsEmailAddress [](start = 27, length = 14)

The accepted answer here looks reasonable to me:

https://stackoverflow.com/questions/1365407/c-sharp-code-to-validate-email-address

It is though a little more forgiving than it has to be, but I believe it is strict enough for our purpose. #WontFix

@PerthCharern PerthCharern left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🕐

@xuzhg

xuzhg commented Dec 19, 2017

Copy link
Copy Markdown
Contributor Author

For the visitor, Yes. I have the interface IVisitor. That's enough.


In reply to: 352597979 [](ancestors = 352597979,352585589)

/// <param name="item">The element.</param>
public void Visit(ValidationContext context, object item)
{
Debug.Assert(item is T, "item should be " + typeof(T));

@PerthCharern PerthCharern Dec 19, 2017

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug.Assert(item is T, "item should be " + typeof(T)); [](start = 12, length = 55)

Remove Debug.Assert and use a normal check #Resolved

@xuzhg

xuzhg commented Dec 20, 2017

Copy link
Copy Markdown
Contributor Author

@darrelmiller
darrelmiller merged commit b29d532 into microsoft:master Jan 11, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants