feat(12.2-02): add file removal, caption, reorder and protected downloads
- DELETE, PUT and POST reorder under .../{id}/files/{field}, each scoped by one parent query (404 for a foreign file)
- protected download and thumb routes: is_public=false only, nosniff, private no-store, sandbox CSP, inline only for jpeg/png/gif/webp
- the save applies deferred removals, replaces attachOne files and rechecks maxFiles and required
- blobs of deleted files are removed after commit
- swagger2openapi emits binary content for file responses
- admin OpenAPI, TS types, conformance, README and attachments docs
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -322,8 +323,17 @@ func convertResponses(res map[string]any, produces []string) map[string]any {
|
||||
converted[k] = v
|
||||
}
|
||||
if schema != nil {
|
||||
// A Swagger 2.0 file response is binary under the operation's
|
||||
// media types; any other schema (an error envelope next to a
|
||||
// binary success) is JSON.
|
||||
types := produces
|
||||
if m, ok := schema.(map[string]any); ok && m["type"] == "file" {
|
||||
schema = map[string]any{"type": "string", "format": "binary"}
|
||||
} else {
|
||||
types = jsonTypes(produces)
|
||||
}
|
||||
content := map[string]any{}
|
||||
for _, ct := range produces {
|
||||
for _, ct := range types {
|
||||
content[ct] = map[string]any{"schema": schema}
|
||||
}
|
||||
converted["content"] = content
|
||||
@@ -333,6 +343,21 @@ func convertResponses(res map[string]any, produces []string) map[string]any {
|
||||
return out
|
||||
}
|
||||
|
||||
// jsonTypes keeps the JSON media types of produces, or application/json
|
||||
// when there are none.
|
||||
func jsonTypes(produces []string) []string {
|
||||
var out []string
|
||||
for _, ct := range produces {
|
||||
if strings.Contains(ct, "json") {
|
||||
out = append(out, ct)
|
||||
}
|
||||
}
|
||||
if len(out) == 0 {
|
||||
return []string{"application/json"}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func convertSecurity(sec map[string]any) map[string]any {
|
||||
out := make(map[string]any, len(sec))
|
||||
for name, raw := range sec {
|
||||
|
||||
@@ -234,3 +234,18 @@ func TestConvertFormData(t *testing.T) {
|
||||
t.Fatalf("form schema = %#v", schema)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertFileResponse(t *testing.T) {
|
||||
res := convertResponses(decode(t, `{
|
||||
"200": {"description": "OK", "schema": {"type": "file"}},
|
||||
"404": {"description": "Not Found", "schema": {"$ref": "#/definitions/acme.Error"}}
|
||||
}`), []string{"application/octet-stream"})
|
||||
ok := res["200"].(map[string]any)["content"].(map[string]any)
|
||||
if !reflect.DeepEqual(ok, map[string]any{"application/octet-stream": map[string]any{"schema": map[string]any{"type": "string", "format": "binary"}}}) {
|
||||
t.Fatalf("file response = %#v", ok)
|
||||
}
|
||||
missing := res["404"].(map[string]any)["content"].(map[string]any)
|
||||
if _, ok := missing["application/json"]; !ok || len(missing) != 1 {
|
||||
t.Fatalf("error response next to a binary success = %#v", missing)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user