Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 0 additions & 12 deletions build.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,18 +75,6 @@ const (
Password Format = "password"
) */

// common media types
const (
Json MIMEType = "application/json"
Xml MIMEType = "application/xml"
Text MIMEType = "text/plain"
General MIMEType = "application/octet-stream"
Html MIMEType = "text/html"
XForm MIMEType = "application/x-www-form-urlencoded"
Jscript MIMEType = "application/javascript"
Form MIMEType = "multipart/form-data"
)

func (o *OpenAPI) AddTags(t ...Tag) {
o.Tags = append(o.Tags, t...)
}
Expand Down
13 changes: 13 additions & 0 deletions openapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,19 @@ func (c *Code) UnmarshalText(b []byte) error {
}

type MIMEType string

// common media types
const (
Json MIMEType = "application/json"
Xml MIMEType = "application/xml"
Text MIMEType = "text/plain"
General MIMEType = "application/octet-stream"
Html MIMEType = "text/html"
XForm MIMEType = "application/x-www-form-urlencoded"
Jscript MIMEType = "application/javascript"
Form MIMEType = "multipart/form-data"
)

type Content map[MIMEType]Media

type Media struct {
Expand Down
57 changes: 53 additions & 4 deletions paths.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,10 @@ func (r Response) WithNamedExample(name string, i any) Response {
if r.Content == nil {
r.Content = make(Content)
}
m := r.Content[Json]
c := getContentType(i)
m := r.Content[c]
m.AddExample(name, i)
r.Content[Json] = m
r.Content[c] = m
return r
}

Expand Down Expand Up @@ -261,6 +262,53 @@ func (r RequestBody) WithNamedJsonString(name string, s string) RequestBody {
return r.WithExample(m)
}

// getContentType attempts to determine the MIME type based on the data provided.
func getContentType(i any) MIMEType {
switch v := i.(type) {
case string:
// Try to detect content type from string content
s := strings.TrimSpace(v)
if len(s) == 0 {
return Text
}

// Check for XML/HTML content first (both start with <)
if strings.HasPrefix(s, "<") {
// Check for XML declaration
if strings.HasPrefix(s, "<?xml") {
return Xml
}

// Check for HTML content by looking for common HTML tags
htmlTags := []string{"<html", "<head", "<body", "<div", "<span", "<p>", "<h1", "<h2", "<h3", "<h4", "<h5", "<h6", "<ul>", "<ol>", "<li>", "<table", "<form", "<input", "<button", "<a ", "<img", "<script", "<style", "<link"}
for _, tag := range htmlTags {
if strings.Contains(s, tag) {
return Html
}
}

// If it starts with < but doesn't match HTML patterns, assume XML
return Xml
}

// Check for JavaScript content
jsPatterns := []string{"function", "var ", "let ", "const ", "=>", "console.log", "document.", "window."}
for _, pattern := range jsPatterns {
if strings.Contains(s, pattern) {
return Jscript
}
}

// Default to plain text for other strings
return Text
case []byte:
return General
case map[string]any, []any, JSONString:
}
// default to JSON for any other type
return Json
}

// Deprecated: use WithExample(JSONString(s)) instead
func (r RequestBody) WithJSONString(s string) RequestBody {
return r.WithNamedJsonString("", s)
Expand All @@ -274,9 +322,10 @@ func (r RequestBody) WithNamedExample(name string, i any) RequestBody {
if r.Content == nil {
r.Content = make(Content)
}
m := r.Content[Json]
c := getContentType(i)
m := r.Content[c]
m.AddExample(name, i)
r.Content[Json] = m
r.Content[c] = m
return r
}

Expand Down
95 changes: 95 additions & 0 deletions paths_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,102 @@ func TestMarshalRoute(t *testing.T) {
},
}
trial.New(fn, cases).SubTest(t)
}

func TestGetContentType(t *testing.T) {
fn := func(i any) (MIMEType, error) {
return getContentType(i), nil
}
cases := trial.Cases[any, MIMEType]{
"jsonString": {
Input: JSONString(`{"key":"value"}`),
Expected: Json,
},
"csv": {
Input: "data1,data2,data3",
Expected: Text,
},
"xml": {
Input: "<root><key>value</key></root>",
Expected: Xml,
},
"xmlWithDeclaration": {
Input: `<?xml version="1.0" encoding="UTF-8"?><root><key>value</key></root>`,
Expected: Xml,
},
"html": {
Input: "<html><body>Hello World</body></html>",
Expected: Html,
},
"htmlWithHead": {
Input: "<head><title>Test</title></head><body>Content</body>",
Expected: Html,
},
"htmlWithDiv": {
Input: "<div>Hello World</div>",
Expected: Html,
},
"htmlWithParagraph": {
Input: "<p>This is a paragraph</p>",
Expected: Html,
},
"htmlWithHeading": {
Input: "<h1>Main Heading</h1>",
Expected: Html,
},
"htmlWithForm": {
Input: "<form><input type='text'><button>Submit</button></form>",
Expected: Html,
},
"javascript": {
Input: "function hello() { console.log('Hello World'); }",
Expected: Jscript,
},
"javascriptWithVar": {
Input: "var name = 'John'; let age = 30; const city = 'NYC';",
Expected: Jscript,
},
"javascriptWithArrow": {
Input: "const greet = () => 'Hello';",
Expected: Jscript,
},
"javascriptWithConsole": {
Input: "console.log('Debug info');",
Expected: Jscript,
},
"javascriptWithDocument": {
Input: "document.getElementById('myElement');",
Expected: Jscript,
},
"javascriptWithWindow": {
Input: "window.location.href = '/new-page';",
Expected: Jscript,
},
"plainText": {
Input: "This is just plain text without any special formatting",
Expected: Text,
},
"emptyString": {
Input: "",
Expected: Text,
},
"whitespaceString": {
Input: " \n\t ",
Expected: Text,
},
"struct": {
Input: struct{ Name string }{Name: "example"},
Expected: Json,
},
"bytes": {Input: []byte(`902kn219jsk`),
Expected: General,
},
"map": {
Input: map[string]int{"name": 1},
Expected: Json,
},
}
trial.New(fn, cases).SubTest(t)
}

func TestRouteAddSecurityRequirement(t *testing.T) {
Expand Down