diff --git a/datamodel/spec_info.go b/datamodel/spec_info.go index f65440f0..a86c37ee 100644 --- a/datamodel/spec_info.go +++ b/datamodel/spec_info.go @@ -104,18 +104,22 @@ func ExtractSpecInfoWithDocumentCheck(spec []byte, bypass bool) (*SpecInfo, erro _, openAPI2 := utils.FindKeyNode(utils.OpenApi2, parsedSpec.Content) _, asyncAPI := utils.FindKeyNode(utils.AsyncApi, parsedSpec.Content) - parseJSON := func(bytes []byte, spec *SpecInfo, parsedNode *yaml.Node) { + parseJSON := func(bytes []byte, spec *SpecInfo, parsedNode *yaml.Node) error { var jsonSpec map[string]interface{} - if utils.IsYAML(string(bytes)) { + switch { + case utils.IsYAML(string(bytes)): _ = parsedNode.Decode(&jsonSpec) b, _ := json.Marshal(&jsonSpec) spec.SpecJSONBytes = &b spec.SpecJSON = &jsonSpec - } else { - _ = json.Unmarshal(bytes, &jsonSpec) + case utils.IsJSON(string(bytes)): + if err := json.Unmarshal(bytes, &jsonSpec); err != nil { + return err + } spec.SpecJSONBytes = &bytes spec.SpecJSON = &jsonSpec } + return nil } // if !bypass { @@ -149,7 +153,10 @@ func ExtractSpecInfoWithDocumentCheck(spec []byte, bypass bool) (*SpecInfo, erro } // parse JSON - parseJSON(spec, specInfo, &parsedSpec) + if err := parseJSON(spec, specInfo, &parsedSpec); err != nil { + specInfo.Error = fmt.Errorf("unable to parse specification: %w", err) + return specInfo, specInfo.Error + } parsed = true // double check for the right version, people mix this up. @@ -176,7 +183,10 @@ func ExtractSpecInfoWithDocumentCheck(spec []byte, bypass bool) (*SpecInfo, erro specInfo.APISchema = OpenAPI2SchemaData // parse JSON - parseJSON(spec, specInfo, &parsedSpec) + if err := parseJSON(spec, specInfo, &parsedSpec); err != nil { + specInfo.Error = fmt.Errorf("unable to parse specification: %w", err) + return specInfo, specInfo.Error + } parsed = true // I am not certain this edge-case is very frequent, but let's make sure we handle it anyway. @@ -200,7 +210,10 @@ func ExtractSpecInfoWithDocumentCheck(spec []byte, bypass bool) (*SpecInfo, erro // TODO: format for AsyncAPI. // parse JSON - parseJSON(spec, specInfo, &parsedSpec) + if err := parseJSON(spec, specInfo, &parsedSpec); err != nil { + specInfo.Error = fmt.Errorf("unable to parse specification: %w", err) + return specInfo, nil + } parsed = true // so far there is only 2 as a major release of AsyncAPI @@ -215,7 +228,10 @@ func ExtractSpecInfoWithDocumentCheck(spec []byte, bypass bool) (*SpecInfo, erro if specInfo.SpecType == "" { // parse JSON if !bypass { - parseJSON(spec, specInfo, &parsedSpec) + if err := parseJSON(spec, specInfo, &parsedSpec); err != nil { + specInfo.Error = errors.New("spec type not supported by libopenapi, sorry") + return specInfo, specInfo.Error + } parsed = true specInfo.Error = errors.New("spec type not supported by libopenapi, sorry") return specInfo, specInfo.Error @@ -227,7 +243,10 @@ func ExtractSpecInfoWithDocumentCheck(spec []byte, bypass bool) (*SpecInfo, erro //} if !parsed { - parseJSON(spec, specInfo, &parsedSpec) + if err := parseJSON(spec, specInfo, &parsedSpec); err != nil { + specInfo.Error = fmt.Errorf("unable to parse specification: %w", err) + return specInfo, specInfo.Error + } } // detect the original whitespace indentation diff --git a/datamodel/spec_info_test.go b/datamodel/spec_info_test.go index 661732ed..d83ee495 100644 --- a/datamodel/spec_info_test.go +++ b/datamodel/spec_info_test.go @@ -24,9 +24,10 @@ const ( ) var ( - goodJSON = `{"name":"kitty", "noises":["meow","purrrr","gggrrraaaaaooooww"]}` - badJSON = `{"name":"kitty, "noises":[{"meow","purrrr","gggrrraaaaaooooww"]}}` - goodYAML = `name: kitty + goodJSON = `{"name":"kitty", "noises":["meow","purrrr","gggrrraaaaaooooww"]}` + badJSON = `{"name":"kitty, "noises":[{"meow","purrrr","gggrrraaaaaooooww"]}}` + badJSONContainingComma = `{"openapi":"3.0.3","info":{"title":"Broken API Spec","version":"1.0.0"},"paths":{"/ping":{"get":{"summary":"Ping endpoint","responses":{"200":{"description":"OK",}}}}}}` + goodYAML = `name: kitty noises: - meow - purrr @@ -118,6 +119,11 @@ func TestExtractSpecInfo_InvalidJSON(t *testing.T) { assert.Error(t, e) } +func TestExtractSpecInfo_InvalidJSONContainingExtraComma(t *testing.T) { + _, e := ExtractSpecInfo([]byte(badJSONContainingComma)) + assert.EqualError(t, e, "unable to parse specification: invalid character '}' looking for beginning of object key string") +} + func TestExtractSpecInfo_Nothing(t *testing.T) { _, e := ExtractSpecInfo([]byte("")) assert.Error(t, e) diff --git a/document_examples_test.go b/document_examples_test.go index aab8f5da..e0580895 100644 --- a/document_examples_test.go +++ b/document_examples_test.go @@ -378,12 +378,12 @@ func TestExampleCompareDocuments_swagger(t *testing.T) { func TestDocument_Paths_As_Array(t *testing.T) { // paths can now be wrapped in an array. spec := `{ - "openapi": "3.1.0", - "paths": [ - "/": { - "get": {} - } - ] + "openapi": "3.1.0", + "paths": { + "/": { + "get": {} + } + } } ` // create a new document from specification bytes diff --git a/document_test.go b/document_test.go index a13cfd93..084a4c18 100644 --- a/document_test.go +++ b/document_test.go @@ -544,9 +544,9 @@ func TestDocument_BuildModelBad(t *testing.T) { } func TestDocument_Serialize_JSON_Modified(t *testing.T) { - json := `{ 'openapi': '3.0', - 'info': { - 'title': 'The magic API' + json := `{ "openapi": "3.0", + "info": { + "title": "The magic API" } } `