From 34ca9203bace7e7ef6e3f26e25e808a57080221f Mon Sep 17 00:00:00 2001 From: Dwi Siswanto Date: Fri, 16 May 2025 19:41:13 +0700 Subject: [PATCH] fix(openapi): handles nil schema & schema values Signed-off-by: Dwi Siswanto --- pkg/input/formats/openapi/examples.go | 30 ++++++++++++++++++++++++++ pkg/input/formats/openapi/generator.go | 28 +++++++++++++++--------- 2 files changed, 48 insertions(+), 10 deletions(-) diff --git a/pkg/input/formats/openapi/examples.go b/pkg/input/formats/openapi/examples.go index 9e7224ab73..235f940c9b 100644 --- a/pkg/input/formats/openapi/examples.go +++ b/pkg/input/formats/openapi/examples.go @@ -288,3 +288,33 @@ func openAPIExample(schema *openapi3.Schema, cache map[*openapi3.Schema]*cachedS func generateExampleFromSchema(schema *openapi3.Schema) (interface{}, error) { return openAPIExample(schema, make(map[*openapi3.Schema]*cachedSchema)) // TODO: Use caching } + +func generateEmptySchemaValue(contentType string) *openapi3.Schema { + schema := &openapi3.Schema{} + objectType := &openapi3.Types{"object"} + stringType := &openapi3.Types{"string"} + + switch contentType { + case "application/json": + schema.Type = objectType + schema.Properties = make(map[string]*openapi3.SchemaRef) + case "application/xml": + schema.Type = stringType + schema.Format = "xml" + schema.Example = "" + case "text/plain": + schema.Type = stringType + case "application/x-www-form-urlencoded": + schema.Type = objectType + schema.Properties = make(map[string]*openapi3.SchemaRef) + case "multipart/form-data": + schema.Type = objectType + schema.Properties = make(map[string]*openapi3.SchemaRef) + case "application/octet-stream": + default: + schema.Type = stringType + schema.Format = "binary" + } + + return schema +} diff --git a/pkg/input/formats/openapi/generator.go b/pkg/input/formats/openapi/generator.go index 9c44797dc5..1906858cba 100644 --- a/pkg/input/formats/openapi/generator.go +++ b/pkg/input/formats/openapi/generator.go @@ -268,24 +268,32 @@ func generateRequestsFromOp(opts *generateReqOptions) error { for content, value := range opts.op.RequestBody.Value.Content { cloned := req.Clone(req.Context()) - example, err := generateExampleFromSchema(value.Schema.Value) - if err != nil { - continue + var val interface{} + + if value.Schema == nil || value.Schema.Value == nil { + val = generateEmptySchemaValue(content) + } else { + var err error + + val, err = generateExampleFromSchema(value.Schema.Value) + if err != nil { + continue + } } // var body string switch content { case "application/json": - if marshalled, err := json.Marshal(example); err == nil { + if marshalled, err := json.Marshal(val); err == nil { // body = string(marshalled) cloned.Body = io.NopCloser(bytes.NewReader(marshalled)) cloned.ContentLength = int64(len(marshalled)) cloned.Header.Set("Content-Type", "application/json") } case "application/xml": - exampleVal := mxj.Map(example.(map[string]interface{})) + values := mxj.Map(val.(map[string]interface{})) - if marshalled, err := exampleVal.Xml(); err == nil { + if marshalled, err := values.Xml(); err == nil { // body = string(marshalled) cloned.Body = io.NopCloser(bytes.NewReader(marshalled)) cloned.ContentLength = int64(len(marshalled)) @@ -294,7 +302,7 @@ func generateRequestsFromOp(opts *generateReqOptions) error { gologger.Warning().Msgf("openapi: could not encode xml") } case "application/x-www-form-urlencoded": - if values, ok := example.(map[string]interface{}); ok { + if values, ok := val.(map[string]interface{}); ok { cloned.Form = url.Values{} for k, v := range values { cloned.Form.Set(k, types.ToString(v)) @@ -306,7 +314,7 @@ func generateRequestsFromOp(opts *generateReqOptions) error { cloned.Header.Set("Content-Type", "application/x-www-form-urlencoded") } case "multipart/form-data": - if values, ok := example.(map[string]interface{}); ok { + if values, ok := val.(map[string]interface{}); ok { buffer := &bytes.Buffer{} multipartWriter := multipart.NewWriter(buffer) for k, v := range values { @@ -326,13 +334,13 @@ func generateRequestsFromOp(opts *generateReqOptions) error { cloned.Header.Set("Content-Type", multipartWriter.FormDataContentType()) } case "text/plain": - str := types.ToString(example) + str := types.ToString(val) // body = str cloned.Body = io.NopCloser(strings.NewReader(str)) cloned.ContentLength = int64(len(str)) cloned.Header.Set("Content-Type", "text/plain") case "application/octet-stream": - str := types.ToString(example) + str := types.ToString(val) if str == "" { // use two strings str = "string1\nstring2"