Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

OpenAPI spec validation in Golang

I want to validate an openapi spec in a similar fashion as its done here : http://bigstickcarpet.com/swagger-parser/www/index.html but the difference is that Ill use GO to code the tool and its only a CLI.

I am trying to use this :

https://github.com/go-openapi/validate

But the main problem is that the documentation is almost non existent. I came here looking for help of someone that might previously used this library and can give me a MINIMAL example of sending a file containing a spec like such and having this library to throw all the errors or warnings in a similar fashion to the online Swagger validator.

I already can read a file and do some manual validation of the fields on it but of course thats not what I need to do and its just a sample.

Additionally, as a secondary question, I want to post this same question on their GitHub repo but I get this :

enter image description here

and I have no idea how to "review" these guidelines so I can post my question.

What I have :

func validate_spec(spec string) []validator_error {
    // RULES HERE. Now I am hardcoding since this is just a dummy app. On the real app we will need to use goapenapi plus a schema validator
    var errors []validator_error
    name_regex, _ := regexp.Compile("^[a-zA-Z]+[ ][a-zA-Z]+")

    // Validate _.name field
        if ( ! gjson.Get(spec, "name").Exists() ) {
            n := validator_error{Path: "_.name", Message: "Does not exist!"}
            errors = append(errors,n)
        }

        if gjson.Get(spec, "name").Exists() {
            if _, ok := gjson.Get(spec, "name").Value().(string); !ok {
                n := validator_error{Path: "_.name", Message: "should be a string"}
                errors = append(errors,n)
            }
            if ( ! name_regex.MatchString(gjson.Get(spec, "name").String() ) ) {
                n := validator_error{Path: "_.name", Message: "should match " + name_regex.String()}
                errors = append(errors,n)
            }
        }
    // ***************************

    // Validate _.age field
        if ( ! gjson.Get(spec, "age").Exists() ) {
            n := validator_error{Path: "_.age", Message: "Does not exist!"}
            errors = append(errors,n)
        }
        if gjson.Get(spec, "age").Exists() {
            if _, ok := gjson.Get(spec, "age").Value().(float64); !ok {
                n := validator_error{Path: "_.age", Message: "should be an int"}
                errors = append(errors,n)
            }

        }
    // ***************************
    return errors
}

What I need :

func validate_spec(spec string) []validator_error {
        // Something like this is what I am looking for. On the above example I am just hard-coding some dummy rules. I need to use the library here to get the validity of the spec being passed.
        return goopenapi.validate(spec )
    }
like image 822
Matias Barrios Avatar asked Feb 12 '18 18:02

Matias Barrios


2 Answers

I use https://github.com/go-openapi quite a lot and find that packages very useful to work with OpenAPI spec, validations and other related stuff.

Validate the spec itself

Take a look at the following code:

document, err = loads.Spec(fpath)
if err != nil {
    return nil, errors.Wrap(err, "Failed to load spec")
}

document, err = document.Expanded(&spec.ExpandOptions{RelativeBase: fpath})
if err != nil {
    return nil, errors.Wrap(err, "Failed to expand spec")
}

if err := validate.Spec(document, strfmt.Default); err != nil {
    return nil, errors.Wrap(err, "Spec is invalid")
}

First of all, it loads spec. Then it expands all references ($ref-s) in that spec. After that it validates the spec itself.

Validate by spec

So the spec itself is correct. Now we want to validate, for example, a request body by that spec.

// sch here is the schema object that can be extracted from
// the spec that you created above.

// data is just an interface{} that represents your data
// structure that you need to validate. data is a struct
// you decoded a json body into like json.Unmarshal(b, &data)

err := validate.AgainstSchema(sch, data, strfmt.Default)
ve, ok := err.(*errors.CompositeError)

// now you can extract errors from ve.Errors

I've build some wrappers around it for easy request validations, for example:

// op here is the OpenAPI operation object that can also be extracted
// from the spec loaded above.
if errs := validate.Body(op.Parameters, body); len(errs) > 0 {
    // work with errs
}

Disclaimer: Some links above lead to the repository oas2 where I am an author and a maintainer. That repository is build on top of go-openapi where I am not the author.

like image 105
hypnoglow Avatar answered Oct 23 '22 06:10

hypnoglow


I've found that kin-openapi provided the closest thing to the level of abstraction I was looking for. That is, I just want validate my http request and response against the spec, without breaking it into parts (query params, path params, body, etc). Its not quite to this level of abstraction, but I have an open PR. Not sure if it'll be accepted, but its a good starting point for your own adventures, nonetheless!

like image 37
Cody A. Ray Avatar answered Oct 23 '22 08:10

Cody A. Ray