diff --git a/_examples/advanced-generic-openapi31/_testdata/openapi.json b/_examples/advanced-generic-openapi31/_testdata/openapi.json index b539ff3..53628a7 100644 --- a/_examples/advanced-generic-openapi31/_testdata/openapi.json +++ b/_examples/advanced-generic-openapi31/_testdata/openapi.json @@ -305,7 +305,7 @@ }, { "name":"identity","in":"query","description":"JSON value in query", - "content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryAdvancedJSONPayload"}}} + "content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedJSONPayload"}}} }, { "name":"in-path","in":"path","description":"Simple scalar value in path","required":true, @@ -313,7 +313,7 @@ }, { "name":"in_cookie","in":"cookie","description":"UUID in cookie.", - "schema":{"$ref":"#/components/schemas/CookieUuidUUID","description":"UUID in cookie."} + "schema":{"$ref":"#/components/schemas/UuidUUID","description":"UUID in cookie."} }, { "name":"X-Header","in":"header","description":"Simple scalar value in header.", @@ -485,13 +485,12 @@ }, { "name":"json_filter","in":"query","description":"JSON object value in query.", - "content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryAdvancedJsonFilter"}}} + "content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedJsonFilter"}}} }, { "name":"deep_object_filter","in":"query","description":"Deep object value in query params.", "schema":{ - "$ref":"#/components/schemas/QueryAdvancedDeepObjectFilter", - "description":"Deep object value in query params." + "$ref":"#/components/schemas/AdvancedDeepObjectFilter","description":"Deep object value in query params." }, "style":"deepObject","explode":true } @@ -692,6 +691,7 @@ "properties":{"id":{"minimum":100,"type":"integer"},"name":{"minLength":3,"type":"string"}},"type":"object" }, "AdvancedJSONMapPayload":{"additionalProperties":{"type":"number"},"type":"object"}, + "AdvancedJSONPayload":{"additionalProperties":false,"properties":{"id":{"type":"integer"},"name":{"type":"string"}},"type":"object"}, "AdvancedJSONPayloadType2":{"additionalProperties":false,"properties":{"id":{"type":"integer"},"name":{"type":"string"}},"type":"object"}, "AdvancedJSONPayloadType3":{"additionalProperties":false,"properties":{"id":{"type":"integer"},"name":{"type":"string"}},"type":"object"}, "AdvancedJSONSlicePayload":{"items":{"type":"integer"},"type":["array","null"]}, @@ -758,7 +758,6 @@ }, "type":"object" }, - "CookieUuidUUID":{"examples":["248df4b7-aa70-47b8-a036-33ac447e668d"],"format":"uuid","type":"string"}, "FormDataAdvancedForm":{"additionalProperties":false,"properties":{"id":{"type":"integer"},"name":{"type":"string"}},"type":"object"}, "FormDataAdvancedInputPort":{ "additionalProperties":false, @@ -794,13 +793,6 @@ }, "FormDataMultipartFile":{"format":"binary","type":["null","string"]}, "FormDataMultipartFileHeader":{"format":"binary","type":["null","string"]}, - "QueryAdvancedDeepObjectFilter":{ - "additionalProperties":false, - "properties":{"bar":{"minLength":3,"type":"string"},"baz":{"minLength":3,"type":["null","string"]}}, - "type":"object" - }, - "QueryAdvancedJSONPayload":{"additionalProperties":false,"properties":{"id":{"type":"integer"},"name":{"type":"string"}},"type":"object"}, - "QueryAdvancedJsonFilter":{"additionalProperties":false,"properties":{"foo":{"maxLength":5,"type":"string"}},"type":"object"}, "RestErrResponse":{ "properties":{ "code":{"description":"Application-specific error code.","type":"integer"}, @@ -810,7 +802,8 @@ }, "type":"object" }, - "TextprotoMIMEHeader":{"additionalProperties":{"items":{"type":"string"},"type":"array"},"type":"object"} + "TextprotoMIMEHeader":{"additionalProperties":{"items":{"type":"string"},"type":"array"},"type":"object"}, + "UuidUUID":{"examples":["248df4b7-aa70-47b8-a036-33ac447e668d"],"format":"uuid","type":"string"} }, "securitySchemes":{"User":{"description":"Session cookie.","type":"apiKey","name":"sessid","in":"cookie"}} } diff --git a/_examples/advanced-generic/_testdata/openapi.json b/_examples/advanced-generic/_testdata/openapi.json deleted file mode 100644 index b40ed84..0000000 --- a/_examples/advanced-generic/_testdata/openapi.json +++ /dev/null @@ -1,807 +0,0 @@ -{ - "openapi":"3.0.3", - "info":{"title":"Advanced Example","description":"This app showcases a variety of features.","version":"v1.2.3"}, - "paths":{ - "/deeper-with-session/one":{ - "get":{ - "tags":["Other"],"summary":"Dummy","operationId":"_examples/advanced-generic.dummy2", - "responses":{ - "204":{"description":"No Content"}, - "401":{ - "description":"Unauthorized", - "content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestErrResponse"}}} - } - }, - "security":[{"User":[]}] - } - }, - "/deeper-with-session/two":{ - "get":{ - "tags":["Other"],"summary":"Dummy","operationId":"_examples/advanced-generic.dummy3", - "responses":{ - "204":{"description":"No Content"}, - "401":{ - "description":"Unauthorized", - "content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestErrResponse"}}} - } - }, - "security":[{"User":[]}] - } - }, - "/error-response":{ - "get":{ - "tags":["Response"],"summary":"Declare Expected Errors", - "description":"This use case demonstrates documentation of expected errors.", - "operationId":"_examples/advanced-generic.errorResponse", - "parameters":[ - { - "name":"type","in":"query","required":true, - "schema":{"enum":["ok","invalid_argument","conflict"],"type":"string"} - } - ], - "responses":{ - "200":{ - "description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedOkResp"}}} - }, - "400":{ - "description":"Bad Request", - "content":{ - "application/json":{ - "schema":{ - "anyOf":[ - {"$ref":"#/components/schemas/AdvancedCustomErr"}, - {"$ref":"#/components/schemas/AdvancedAnotherErr"} - ] - } - } - } - }, - "409":{ - "description":"Conflict", - "content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedCustomErr"}}} - }, - "412":{ - "description":"Precondition Failed", - "content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedCustomErr"}}} - } - }, - "x-forbid-unknown-query":true - } - }, - "/file-multi-upload":{ - "post":{ - "tags":["Request"],"summary":"Files Uploads With 'multipart/form-data'", - "operationId":"_examples/advanced-generic.fileMultiUploader", - "parameters":[ - { - "name":"in_query","in":"query","description":"Simple scalar value in query.", - "schema":{"type":"integer","description":"Simple scalar value in query."} - } - ], - "requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/FormDataAdvancedUploadType2"}}}}, - "responses":{ - "200":{ - "description":"OK", - "content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedInfoType2"}}} - } - }, - "x-forbid-unknown-query":true - } - }, - "/file-upload":{ - "post":{ - "tags":["Request"],"summary":"File Upload With 'multipart/form-data'", - "operationId":"_examples/advanced-generic.fileUploader", - "parameters":[ - { - "name":"in_query","in":"query","description":"Simple scalar value in query.", - "schema":{"type":"integer","description":"Simple scalar value in query."} - } - ], - "requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/FormDataAdvancedUpload"}}}}, - "responses":{ - "200":{"description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedInfo"}}}} - }, - "x-forbid-unknown-query":true - } - }, - "/form":{ - "post":{ - "tags":["Request"],"summary":"Request With Form", - "description":"The `form` field tag acts as `query` and `formData`, with priority on `formData`.\n\nIt is decoded with `http.Request.Form` values.", - "operationId":"_examples/advanced-generic.form", - "parameters":[ - {"name":"id","in":"query","schema":{"type":"integer"}}, - {"name":"name","in":"query","schema":{"type":"string"}} - ], - "requestBody":{ - "content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/FormDataAdvancedForm"}}} - }, - "responses":{ - "200":{ - "description":"OK","content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedOutput"}}} - } - }, - "x-forbid-unknown-query":true - } - }, - "/gzip-pass-through":{ - "get":{ - "tags":["Response"],"summary":"Direct Gzip","operationId":"_examples/advanced-generic.directGzip", - "parameters":[ - { - "name":"plainStruct","in":"query","description":"Output plain structure instead of gzip container.", - "schema":{"type":"boolean","description":"Output plain structure instead of gzip container."} - }, - { - "name":"countItems","in":"query","description":"Invokes internal decoding of compressed data.", - "schema":{"type":"boolean","description":"Invokes internal decoding of compressed data."} - } - ], - "responses":{ - "200":{ - "description":"OK","headers":{"X-Header":{"style":"simple","schema":{"type":"string"}}}, - "content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedGzipPassThroughStruct"}}} - } - }, - "x-forbid-unknown-query":true - }, - "head":{ - "tags":["Response"],"summary":"Direct Gzip","operationId":"_examples/advanced-generic.directGzip2", - "parameters":[ - { - "name":"plainStruct","in":"query","description":"Output plain structure instead of gzip container.", - "schema":{"type":"boolean","description":"Output plain structure instead of gzip container."} - }, - { - "name":"countItems","in":"query","description":"Invokes internal decoding of compressed data.", - "schema":{"type":"boolean","description":"Invokes internal decoding of compressed data."} - } - ], - "responses":{"200":{"description":"OK","headers":{"X-Header":{"style":"simple","schema":{"type":"string"}}}}}, - "x-forbid-unknown-query":true - } - }, - "/html-response/{id}":{ - "get":{ - "tags":["Response"],"summary":"Request With HTML Response", - "description":"Request with templated HTML response.","operationId":"_examples/advanced-generic.htmlResponse", - "parameters":[ - {"name":"filter","in":"query","schema":{"type":"string"}}, - {"name":"id","in":"path","required":true,"schema":{"type":"integer"}}, - {"name":"X-Header","in":"header","schema":{"type":"boolean"}} - ], - "responses":{ - "200":{ - "description":"OK","headers":{"X-Anti-Header":{"style":"simple","schema":{"type":"boolean"}}}, - "content":{"text/html":{"schema":{"type":"string"}}} - } - }, - "x-forbid-unknown-path":true,"x-forbid-unknown-query":true - } - }, - "/json-body-manual/{in-path}":{ - "post":{ - "tags":["Request"],"summary":"Request With JSON Body and manual decoder", - "description":"Request with JSON body and query/header/path params, response with JSON body and data from request.", - "operationId":"_examples/advanced-generic.jsonBodyManual", - "parameters":[ - { - "name":"in_query","in":"query","description":"Simple scalar value in query.", - "schema":{"type":"string","description":"Simple scalar value in query.","format":"date"} - }, - { - "name":"in-path","in":"path","description":"Simple scalar value in path","required":true, - "schema":{"type":"string","description":"Simple scalar value in path"} - }, - { - "name":"X-Header","in":"header","description":"Simple scalar value in header.", - "schema":{"type":"string","description":"Simple scalar value in header."} - } - ], - "requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedInputWithJSONType3"}}}}, - "responses":{ - "201":{ - "description":"Created", - "content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedOutputWithJSONType3"}}} - } - }, - "x-forbid-unknown-path":true,"x-forbid-unknown-query":true - } - }, - "/json-body-validation/{in-path}":{ - "post":{ - "tags":["Request","Response","Validation"],"summary":"Request With JSON Body and non-trivial validation", - "description":"Request with JSON body and query/header/path params, response with JSON body and data from request.", - "operationId":"_examples/advanced-generic.jsonBodyValidation", - "parameters":[ - { - "name":"in_query","in":"query","description":"Simple scalar value in query.", - "schema":{"minimum":100,"type":"integer","description":"Simple scalar value in query."} - }, - { - "name":"in-path","in":"path","description":"Simple scalar value in path","required":true, - "schema":{"minLength":3,"type":"string","description":"Simple scalar value in path"} - }, - { - "name":"X-Header","in":"header","description":"Simple scalar value in header.", - "schema":{"minLength":3,"type":"string","description":"Simple scalar value in header."} - } - ], - "requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedInputWithJSONType4"}}}}, - "responses":{ - "200":{ - "description":"OK", - "content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedOutputWithJSONType4"}}} - } - }, - "x-forbid-unknown-path":true,"x-forbid-unknown-query":true - } - }, - "/json-body/{in-path}":{ - "post":{ - "tags":["Request"],"summary":"Request With JSON Body", - "description":"Request with JSON body and query/header/path params, response with JSON body and data from request.", - "operationId":"_examples/advanced-generic.jsonBody", - "parameters":[ - { - "name":"in_query","in":"query","description":"Simple scalar value in query.", - "schema":{"type":"string","description":"Simple scalar value in query.","format":"date"} - }, - { - "name":"in-path","in":"path","description":"Simple scalar value in path","required":true, - "schema":{"type":"string","description":"Simple scalar value in path"} - }, - { - "name":"X-Header","in":"header","description":"Simple scalar value in header.", - "schema":{"type":"string","description":"Simple scalar value in header."} - } - ], - "requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedInputWithJSONType2"}}}}, - "responses":{ - "201":{ - "description":"Created", - "content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedOutputWithJSONType2"}}} - } - }, - "x-forbid-unknown-path":true,"x-forbid-unknown-query":true - } - }, - "/json-map-body":{ - "post":{ - "tags":["Request"],"summary":"Request With JSON Map In Body", - "description":"Request with JSON object (map) body.","operationId":"_examples/advanced-generic.jsonMapBody", - "parameters":[ - { - "name":"in_query","in":"query","description":"Simple scalar value in query.", - "schema":{"type":"integer","description":"Simple scalar value in query."} - }, - { - "name":"X-Header","in":"header","description":"Simple scalar value in header.", - "schema":{"type":"string","description":"Simple scalar value in header."} - } - ], - "requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedJsonMapReq"}}}}, - "responses":{ - "200":{ - "description":"OK", - "content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedJsonOutputType2"}}} - } - }, - "x-forbid-unknown-query":true - } - }, - "/json-param/{in-path}":{ - "get":{ - "tags":["Request"],"summary":"Request With JSON Query Parameter", - "description":"Request with JSON body and query/header/path params, response with JSON body and data from request.", - "operationId":"_examples/advanced-generic.jsonParam", - "parameters":[ - { - "name":"in_query","in":"query","description":"Simple scalar value in query.", - "schema":{"type":"integer","description":"Simple scalar value in query."} - }, - { - "name":"identity","in":"query","description":"JSON value in query", - "content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryAdvancedJSONPayload"}}} - }, - { - "name":"in-path","in":"path","description":"Simple scalar value in path","required":true, - "schema":{"type":"string","description":"Simple scalar value in path"} - }, - { - "name":"in_cookie","in":"cookie","description":"UUID in cookie.", - "schema":{"$ref":"#/components/schemas/CookieUuidUUID"} - }, - { - "name":"X-Header","in":"header","description":"Simple scalar value in header.", - "schema":{"type":"string","description":"Simple scalar value in header."} - } - ], - "responses":{ - "200":{ - "description":"OK", - "content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedOutputWithJSON"}}} - } - }, - "x-forbid-unknown-cookie":true,"x-forbid-unknown-path":true,"x-forbid-unknown-query":true - } - }, - "/json-slice-body":{ - "post":{ - "tags":["Request"],"summary":"Request With JSON Array In Body", - "operationId":"_examples/advanced-generic.jsonSliceBody", - "parameters":[ - { - "name":"in_query","in":"query","description":"Simple scalar value in query.", - "schema":{"type":"integer","description":"Simple scalar value in query."} - }, - { - "name":"X-Header","in":"header","description":"Simple scalar value in header.", - "schema":{"type":"string","description":"Simple scalar value in header."} - } - ], - "requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedJsonSliceReq"}}}}, - "responses":{ - "200":{ - "description":"OK", - "content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedJsonOutput"}}} - } - }, - "x-forbid-unknown-query":true - } - }, - "/no-validation":{ - "post":{ - "tags":["Request","Response"],"summary":"No Validation","description":"Input/Output without validation.", - "operationId":"_examples/advanced-generic.noValidation", - "parameters":[ - {"name":"q","in":"query","schema":{"type":"boolean"}}, - {"name":"X-Input","in":"header","schema":{"type":"integer"}} - ], - "requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedInputPortType3"}}}}, - "responses":{ - "200":{ - "description":"OK", - "headers":{ - "X-Output":{"style":"simple","schema":{"type":"integer"}}, - "X-Query":{"style":"simple","schema":{"type":"boolean"}} - }, - "content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedOutputPortType3"}}} - } - }, - "x-forbid-unknown-query":true - } - }, - "/output-csv-writer":{ - "get":{ - "tags":["Response"],"summary":"Output With Stream Writer","description":"Output with stream writer.", - "operationId":"_examples/advanced-generic.outputCSVWriter", - "parameters":[ - { - "name":"If-None-Match","in":"header","description":"Content hash.", - "schema":{"type":"string","description":"Content hash."} - } - ], - "responses":{ - "200":{ - "description":"OK", - "headers":{ - "ETag":{ - "style":"simple","description":"Content hash.","schema":{"type":"string","description":"Content hash."} - }, - "X-Header":{ - "style":"simple","description":"Sample response header.", - "schema":{"type":"string","description":"Sample response header."} - } - }, - "content":{"text/csv":{"schema":{"type":"string"}}} - }, - "304":{"description":"Not Modified"}, - "500":{ - "description":"Internal Server Error", - "content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedCustomErr"}}} - } - } - } - }, - "/output-headers":{ - "get":{ - "tags":["Response"],"summary":"Output With Headers","description":"Output with headers.", - "operationId":"_examples/advanced-generic.outputHeaders", - "parameters":[ - { - "name":"X-foO","in":"header","description":"Reduced by 20 in response.","required":true, - "schema":{"minimum":10,"type":"integer","description":"Reduced by 20 in response."} - } - ], - "responses":{ - "200":{ - "description":"OK", - "headers":{ - "X-foO":{ - "style":"simple","description":"Reduced by 20 in response.","required":true, - "schema":{"minimum":10,"type":"integer","description":"Reduced by 20 in response."} - }, - "x-HeAdEr":{ - "style":"simple","description":"Sample response header.", - "schema":{"type":"string","description":"Sample response header."} - }, - "x-omit-empty":{ - "style":"simple","description":"Receives req value of X-Foo reduced by 30.", - "schema":{"type":"integer","description":"Receives req value of X-Foo reduced by 30."} - } - }, - "content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedHeaderOutput"}}} - }, - "500":{ - "description":"Internal Server Error", - "content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedCustomErr"}}} - } - } - }, - "head":{ - "tags":["Response"],"summary":"Output With Headers","description":"Output with headers.", - "operationId":"_examples/advanced-generic.outputHeaders2", - "parameters":[ - { - "name":"X-foO","in":"header","description":"Reduced by 20 in response.","required":true, - "schema":{"minimum":10,"type":"integer","description":"Reduced by 20 in response."} - } - ], - "responses":{ - "200":{ - "description":"OK", - "headers":{ - "X-foO":{ - "style":"simple","description":"Reduced by 20 in response.","required":true, - "schema":{"minimum":10,"type":"integer","description":"Reduced by 20 in response."} - }, - "x-HeAdEr":{ - "style":"simple","description":"Sample response header.", - "schema":{"type":"string","description":"Sample response header."} - }, - "x-omit-empty":{ - "style":"simple","description":"Receives req value of X-Foo reduced by 30.", - "schema":{"type":"integer","description":"Receives req value of X-Foo reduced by 30."} - } - } - }, - "500":{"description":"Internal Server Error"} - } - } - }, - "/query-object":{ - "get":{ - "tags":["Request"],"summary":"Request With Object As Query Parameter", - "operationId":"_examples/advanced-generic.queryObject", - "parameters":[ - { - "name":"in_query","in":"query","description":"Object value in query.","style":"deepObject","explode":true, - "schema":{"type":"object","additionalProperties":{"type":"number"},"description":"Object value in query."} - }, - { - "name":"json_filter","in":"query","description":"JSON object value in query.", - "content":{"application/json":{"schema":{"$ref":"#/components/schemas/QueryAdvancedJsonFilter"}}} - }, - { - "name":"deep_object_filter","in":"query","description":"Deep object value in query params.", - "style":"deepObject","explode":true,"schema":{"$ref":"#/components/schemas/QueryAdvancedDeepObjectFilter"} - } - ], - "responses":{ - "200":{ - "description":"OK", - "content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedOutputQueryObject"}}} - } - }, - "x-forbid-unknown-query":true - } - }, - "/req-resp-mapping":{ - "post":{ - "tags":["Request","Response"],"summary":"Request Response Mapping", - "description":"This use case has transport concerns fully decoupled with external req/resp mapping.", - "operationId":"reqRespMapping", - "parameters":[ - { - "name":"X-Header","in":"header","description":"Simple scalar value with sample validation.","required":true, - "schema":{"minLength":3,"type":"string","description":"Simple scalar value with sample validation."} - } - ], - "requestBody":{ - "content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/FormDataAdvancedInputPort"}}} - }, - "responses":{ - "204":{ - "description":"No Content", - "headers":{ - "X-Value-1":{ - "style":"simple","description":"Simple scalar value with sample validation.","required":true, - "schema":{"minLength":3,"type":"string","description":"Simple scalar value with sample validation."} - }, - "X-Value-2":{ - "style":"simple","description":"Simple scalar value with sample validation.","required":true, - "schema":{"minimum":3,"type":"integer","description":"Simple scalar value with sample validation."} - } - } - } - } - } - }, - "/root-with-session":{ - "get":{ - "tags":["Other"],"summary":"Dummy","operationId":"_examples/advanced-generic.dummy", - "responses":{ - "204":{"description":"No Content"}, - "401":{ - "description":"Unauthorized", - "content":{"application/json":{"schema":{"$ref":"#/components/schemas/RestErrResponse"}}} - } - }, - "security":[{"User":[]}] - } - }, - "/text-req-body-ptr/{path}":{ - "post":{ - "tags":["Request"],"summary":"Request With Text Body (ptr input)", - "description":"This usecase allows direct access to original `*http.Request` while keeping automated decoding of parameters.", - "operationId":"_examples/advanced-generic.textReqBodyPtr", - "parameters":[ - {"name":"query","in":"query","schema":{"type":"integer"}}, - {"name":"path","in":"path","required":true,"schema":{"type":"string"}} - ], - "requestBody":{"content":{"text/csv":{"schema":{"type":"string"}}}}, - "responses":{ - "200":{ - "description":"OK", - "content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedOutputType3"}}} - } - }, - "x-forbid-unknown-path":true,"x-forbid-unknown-query":true - } - }, - "/text-req-body/{path}":{ - "post":{ - "tags":["Request"],"summary":"Request With Text Body", - "description":"This usecase allows direct access to original `*http.Request` while keeping automated decoding of parameters.", - "operationId":"_examples/advanced-generic.textReqBody", - "parameters":[ - {"name":"query","in":"query","schema":{"type":"integer"}}, - {"name":"path","in":"path","required":true,"schema":{"type":"string"}} - ], - "requestBody":{"content":{"text/csv":{"schema":{"type":"string"}}}}, - "responses":{ - "200":{ - "description":"OK", - "content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedOutputType2"}}} - } - }, - "x-forbid-unknown-path":true,"x-forbid-unknown-query":true - } - }, - "/validation":{ - "post":{ - "tags":["Request","Response","Validation"],"summary":"Validation", - "description":"Input/Output with validation. Custom annotation.", - "operationId":"_examples/advanced-generic.validation", - "parameters":[ - { - "name":"q","in":"query", - "description":"This parameter will bypass explicit validation as it does not have constraints.", - "schema":{ - "type":"boolean", - "description":"This parameter will bypass explicit validation as it does not have constraints." - } - }, - { - "name":"X-Input","in":"header","description":"Request minimum: 10, response maximum: 20.", - "schema":{"minimum":10,"type":"integer","description":"Request minimum: 10, response maximum: 20."} - } - ], - "requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedInputPortType2"}}}}, - "responses":{ - "200":{ - "description":"OK", - "headers":{ - "X-Output":{"style":"simple","schema":{"maximum":20,"type":"integer"}}, - "X-Query":{ - "style":"simple","description":"This header bypasses validation as it does not have constraints.", - "schema":{"type":"boolean","description":"This header bypasses validation as it does not have constraints."} - } - }, - "content":{"application/json":{"schema":{"$ref":"#/components/schemas/AdvancedOutputPortType2"}}} - } - }, - "x-forbid-unknown-query":true - } - } - }, - "components":{ - "schemas":{ - "AdvancedAnotherErr":{"type":"object","properties":{"foo":{"type":"integer"}}}, - "AdvancedCustomErr":{"type":"object","properties":{"details":{"type":"object","additionalProperties":{}},"msg":{"type":"string"}}}, - "AdvancedDeepObjectFilter":{ - "type":"object", - "properties":{"bar":{"minLength":3,"type":"string"},"baz":{"minLength":3,"type":"string","nullable":true}} - }, - "AdvancedGzipPassThroughStruct":{ - "type":"object", - "properties":{"id":{"type":"integer"},"text":{"type":"array","items":{"type":"string"},"nullable":true}} - }, - "AdvancedHeaderOutput":{"type":"object","properties":{"inBody":{"type":"string","deprecated":true}}}, - "AdvancedInfo":{ - "type":"object", - "properties":{ - "filename":{"type":"string"},"header":{"$ref":"#/components/schemas/TextprotoMIMEHeader"}, - "inQuery":{"type":"integer"},"peek1":{"type":"string"},"peek2":{"type":"string"},"simple":{"type":"string"}, - "size":{"type":"integer"} - } - }, - "AdvancedInfoType2":{ - "type":"object", - "properties":{ - "filenames":{"type":"array","items":{"type":"string"},"nullable":true}, - "headers":{"type":"array","items":{"$ref":"#/components/schemas/TextprotoMIMEHeader"},"nullable":true}, - "inQuery":{"type":"integer"},"peeks1":{"type":"array","items":{"type":"string"},"nullable":true}, - "peeks2":{"type":"array","items":{"type":"string"},"nullable":true},"simple":{"type":"string"}, - "sizes":{"type":"array","items":{"type":"integer"},"nullable":true} - } - }, - "AdvancedInputPortType2":{ - "required":["data"],"type":"object", - "properties":{ - "data":{ - "type":"object", - "properties":{"value":{"minLength":3,"type":"string","description":"Request minLength: 3, response maxLength: 7"}}, - "additionalProperties":false - } - }, - "additionalProperties":false - }, - "AdvancedInputPortType3":{ - "type":"object", - "properties":{"data":{"type":"object","properties":{"value":{"type":"string"}},"additionalProperties":false}}, - "additionalProperties":false - }, - "AdvancedInputWithJSONType2":{ - "type":"object", - "properties":{ - "id":{"type":"integer"},"name":{"type":"string"}, - "namedStruct":{"allOf":[{"deprecated":true},{"$ref":"#/components/schemas/AdvancedJSONPayloadType2"}]} - }, - "additionalProperties":false - }, - "AdvancedInputWithJSONType3":{ - "type":"object", - "properties":{ - "id":{"type":"integer"},"name":{"type":"string"}, - "namedStruct":{"allOf":[{"deprecated":true},{"$ref":"#/components/schemas/AdvancedJSONPayloadType3"}]} - }, - "additionalProperties":false - }, - "AdvancedInputWithJSONType4":{ - "type":"object","properties":{"id":{"minimum":100,"type":"integer"},"name":{"minLength":3,"type":"string"}}, - "additionalProperties":false - }, - "AdvancedJSONMapPayload":{"type":"object","additionalProperties":{"type":"number"}}, - "AdvancedJSONPayloadType2":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"}},"additionalProperties":false}, - "AdvancedJSONPayloadType3":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"}},"additionalProperties":false}, - "AdvancedJSONSlicePayload":{"type":"array","items":{"type":"integer"},"nullable":true}, - "AdvancedJsonFilter":{"type":"object","properties":{"foo":{"maxLength":5,"type":"string"}}}, - "AdvancedJsonMapReq":{"type":"object","additionalProperties":{"type":"number"}}, - "AdvancedJsonOutput":{ - "type":"object", - "properties":{ - "data":{"$ref":"#/components/schemas/AdvancedJSONSlicePayload"},"inHeader":{"type":"string"}, - "inQuery":{"type":"integer"} - } - }, - "AdvancedJsonOutputType2":{ - "type":"object", - "properties":{ - "data":{"$ref":"#/components/schemas/AdvancedJSONMapPayload"},"inHeader":{"type":"string"}, - "inQuery":{"type":"integer"} - } - }, - "AdvancedJsonSliceReq":{"type":"array","items":{"type":"integer"}}, - "AdvancedOkResp":{"type":"object","properties":{"status":{"type":"string"}}}, - "AdvancedOutput":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"}}}, - "AdvancedOutputPortType2":{ - "required":["data"],"type":"object", - "properties":{"data":{"type":"object","properties":{"value":{"maxLength":7,"type":"string"}}}} - }, - "AdvancedOutputPortType3":{"type":"object","properties":{"data":{"type":"object","properties":{"value":{"type":"string"}}}}}, - "AdvancedOutputQueryObject":{ - "type":"object", - "properties":{ - "deepObjectFilter":{"$ref":"#/components/schemas/AdvancedDeepObjectFilter"}, - "inQuery":{"type":"object","additionalProperties":{"type":"number"},"nullable":true}, - "jsonFilter":{"$ref":"#/components/schemas/AdvancedJsonFilter"} - } - }, - "AdvancedOutputType2":{"type":"object","properties":{"path":{"type":"string"},"query":{"type":"integer"},"text":{"type":"string"}}}, - "AdvancedOutputType3":{"type":"object","properties":{"path":{"type":"string"},"query":{"type":"integer"},"text":{"type":"string"}}}, - "AdvancedOutputWithJSON":{ - "type":"object", - "properties":{ - "id":{"type":"integer"},"inHeader":{"type":"string"},"inPath":{"type":"string"},"inQuery":{"type":"integer"}, - "name":{"type":"string"} - } - }, - "AdvancedOutputWithJSONType2":{ - "type":"object", - "properties":{ - "id":{"type":"integer"},"inHeader":{"type":"string"},"inPath":{"type":"string"}, - "inQuery":{"type":"string","format":"date","deprecated":true},"name":{"type":"string"} - } - }, - "AdvancedOutputWithJSONType3":{ - "type":"object", - "properties":{ - "id":{"type":"integer"},"inHeader":{"type":"string"},"inPath":{"type":"string"}, - "inQuery":{"type":"string","format":"date","deprecated":true},"name":{"type":"string"} - } - }, - "AdvancedOutputWithJSONType4":{ - "type":"object", - "properties":{ - "id":{"minimum":100,"type":"integer"},"inHeader":{"minLength":3,"type":"string"}, - "inPath":{"minLength":3,"type":"string"},"inQuery":{"minimum":3,"type":"integer"}, - "name":{"minLength":3,"type":"string"} - } - }, - "CookieUuidUUID":{"type":"string","format":"uuid","example":"248df4b7-aa70-47b8-a036-33ac447e668d"}, - "FormDataAdvancedForm":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"}},"additionalProperties":false}, - "FormDataAdvancedInputPort":{ - "required":["val2"],"type":"object", - "properties":{"val2":{"minimum":3,"type":"integer","description":"Simple scalar value with sample validation."}}, - "additionalProperties":false - }, - "FormDataAdvancedUpload":{ - "type":"object", - "properties":{ - "simple":{"type":"string","description":"Simple scalar value in body."}, - "upload1":{"$ref":"#/components/schemas/FormDataMultipartFileHeader"}, - "upload2":{"$ref":"#/components/schemas/FormDataMultipartFile"} - }, - "additionalProperties":false - }, - "FormDataAdvancedUploadType2":{ - "type":"object", - "properties":{ - "simple":{"type":"string","description":"Simple scalar value in body."}, - "uploads1":{ - "type":"array","items":{"$ref":"#/components/schemas/FormDataMultipartFileHeader"}, - "description":"Uploads with *multipart.FileHeader.","nullable":true - }, - "uploads2":{ - "type":"array","items":{"$ref":"#/components/schemas/FormDataMultipartFile"}, - "description":"Uploads with multipart.File.","nullable":true - } - }, - "additionalProperties":false - }, - "FormDataMultipartFile":{"type":"string","format":"binary","nullable":true}, - "FormDataMultipartFileHeader":{"type":"string","format":"binary","nullable":true}, - "QueryAdvancedDeepObjectFilter":{ - "type":"object", - "properties":{"bar":{"minLength":3,"type":"string"},"baz":{"minLength":3,"type":"string","nullable":true}}, - "additionalProperties":false - }, - "QueryAdvancedJSONPayload":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"}},"additionalProperties":false}, - "QueryAdvancedJsonFilter":{"type":"object","properties":{"foo":{"maxLength":5,"type":"string"}},"additionalProperties":false}, - "RestErrResponse":{ - "type":"object", - "properties":{ - "code":{"type":"integer","description":"Application-specific error code."}, - "context":{"type":"object","additionalProperties":{},"description":"Application context."}, - "error":{"type":"string","description":"Error message."}, - "status":{"type":"string","description":"Status text."} - } - }, - "TextprotoMIMEHeader":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}} - }, - "securitySchemes":{"User":{"type":"apiKey","name":"sessid","in":"cookie","description":"Session cookie."}} - } -} diff --git a/_examples/advanced-generic/dummy.go b/_examples/advanced-generic/dummy.go deleted file mode 100644 index f9d2db5..0000000 --- a/_examples/advanced-generic/dummy.go +++ /dev/null @@ -1,16 +0,0 @@ -package main - -import ( - "context" - - "github.com/swaggest/usecase" -) - -func dummy() usecase.Interactor { - u := usecase.NewInteractor(func(ctx context.Context, input struct{}, output *struct{}) error { - return nil - }) - u.SetTags("Other") - - return u -} diff --git a/_examples/advanced-generic/error_response.go b/_examples/advanced-generic/error_response.go deleted file mode 100644 index 2fa2352..0000000 --- a/_examples/advanced-generic/error_response.go +++ /dev/null @@ -1,57 +0,0 @@ -//go:build go1.18 - -package main - -import ( - "context" - "errors" - - "github.com/bool64/ctxd" - "github.com/swaggest/usecase" - "github.com/swaggest/usecase/status" -) - -type customErr struct { - Message string `json:"msg"` - Details map[string]interface{} `json:"details,omitempty"` -} - -func errorResponse() usecase.Interactor { - type errType struct { - Type string `query:"type" enum:"ok,invalid_argument,conflict" required:"true"` - } - - type okResp struct { - Status string `json:"status"` - } - - u := usecase.NewInteractor(func(ctx context.Context, in errType, out *okResp) (err error) { - switch in.Type { - case "ok": - out.Status = "ok" - case "invalid_argument": - return status.Wrap(errors.New("bad value for foo"), status.InvalidArgument) - case "conflict": - return status.Wrap(ctxd.NewError(ctx, "conflict", "foo", "bar"), - status.AlreadyExists) - } - - return nil - }) - - u.SetTitle("Declare Expected Errors") - u.SetDescription("This use case demonstrates documentation of expected errors.") - u.SetExpectedErrors(status.InvalidArgument, anotherErr{}, status.FailedPrecondition, status.AlreadyExists) - u.SetTags("Response") - - return u -} - -// anotherErr is another custom error. -type anotherErr struct { - Foo int `json:"foo"` -} - -func (anotherErr) Error() string { - return "foo happened" -} diff --git a/_examples/advanced-generic/file_multi_upload.go b/_examples/advanced-generic/file_multi_upload.go deleted file mode 100644 index 006d5c0..0000000 --- a/_examples/advanced-generic/file_multi_upload.go +++ /dev/null @@ -1,78 +0,0 @@ -//go:build go1.18 - -package main - -import ( - "context" - "mime/multipart" - "net/textproto" - - "github.com/swaggest/usecase" -) - -func fileMultiUploader() usecase.Interactor { - type upload struct { - Simple string `formData:"simple" description:"Simple scalar value in body."` - Query int `query:"in_query" description:"Simple scalar value in query."` - Uploads1 []*multipart.FileHeader `formData:"uploads1" description:"Uploads with *multipart.FileHeader."` - Uploads2 []multipart.File `formData:"uploads2" description:"Uploads with multipart.File."` - } - - type info struct { - Filenames []string `json:"filenames"` - Headers []textproto.MIMEHeader `json:"headers"` - Sizes []int64 `json:"sizes"` - Upload1Peeks []string `json:"peeks1"` - Upload2Peeks []string `json:"peeks2"` - Simple string `json:"simple"` - Query int `json:"inQuery"` - } - - u := usecase.NewInteractor(func(ctx context.Context, in upload, out *info) (err error) { - out.Query = in.Query - out.Simple = in.Simple - for _, o := range in.Uploads1 { - out.Filenames = append(out.Filenames, o.Filename) - out.Headers = append(out.Headers, o.Header) - out.Sizes = append(out.Sizes, o.Size) - - f, err := o.Open() - if err != nil { - return err - } - p := make([]byte, 100) - _, err = f.Read(p) - if err != nil { - return err - } - - out.Upload1Peeks = append(out.Upload1Peeks, string(p)) - - err = f.Close() - if err != nil { - return err - } - } - - for _, o := range in.Uploads2 { - p := make([]byte, 100) - _, err = o.Read(p) - if err != nil { - return err - } - - out.Upload2Peeks = append(out.Upload2Peeks, string(p)) - err = o.Close() - if err != nil { - return err - } - } - - return nil - }) - - u.SetTitle("Files Uploads With 'multipart/form-data'") - u.SetTags("Request") - - return u -} diff --git a/_examples/advanced-generic/file_upload.go b/_examples/advanced-generic/file_upload.go deleted file mode 100644 index be820a2..0000000 --- a/_examples/advanced-generic/file_upload.go +++ /dev/null @@ -1,82 +0,0 @@ -//go:build go1.18 - -package main - -import ( - "context" - "mime/multipart" - "net/textproto" - - "github.com/swaggest/usecase" -) - -func fileUploader() usecase.Interactor { - type upload struct { - Simple string `formData:"simple" description:"Simple scalar value in body."` - Query int `query:"in_query" description:"Simple scalar value in query."` - Upload1 *multipart.FileHeader `formData:"upload1" description:"Upload with *multipart.FileHeader."` - Upload2 multipart.File `formData:"upload2" description:"Upload with multipart.File."` - } - - type info struct { - Filename string `json:"filename"` - Header textproto.MIMEHeader `json:"header"` - Size int64 `json:"size"` - Upload1Peek string `json:"peek1"` - Upload2Peek string `json:"peek2"` - Simple string `json:"simple"` - Query int `json:"inQuery"` - } - - u := usecase.NewInteractor(func(ctx context.Context, in upload, out *info) (err error) { - out.Query = in.Query - out.Simple = in.Simple - if in.Upload1 == nil { - return nil - } - - out.Filename = in.Upload1.Filename - out.Header = in.Upload1.Header - out.Size = in.Upload1.Size - - f, err := in.Upload1.Open() - if err != nil { - return err - } - - defer func() { - clErr := f.Close() - if clErr != nil && err == nil { - err = clErr - } - - clErr = in.Upload2.Close() - if clErr != nil && err == nil { - err = clErr - } - }() - - p := make([]byte, 100) - _, err = f.Read(p) - if err != nil { - return err - } - - out.Upload1Peek = string(p) - - p = make([]byte, 100) - _, err = in.Upload2.Read(p) - if err != nil { - return err - } - - out.Upload2Peek = string(p) - - return nil - }) - - u.SetTitle("File Upload With 'multipart/form-data'") - u.SetTags("Request") - - return u -} diff --git a/_examples/advanced-generic/form.go b/_examples/advanced-generic/form.go deleted file mode 100644 index ffdfa28..0000000 --- a/_examples/advanced-generic/form.go +++ /dev/null @@ -1,35 +0,0 @@ -//go:build go1.18 - -package main - -import ( - "context" - - "github.com/swaggest/usecase" -) - -func form() usecase.Interactor { - type form struct { - ID int `form:"id"` - Name string `form:"name"` - } - - type output struct { - ID int `json:"id"` - Name string `json:"name"` - } - - u := usecase.NewInteractor(func(ctx context.Context, in form, out *output) error { - out.ID = in.ID - out.Name = in.Name - - return nil - }) - - u.SetTitle("Request With Form") - u.SetDescription("The `form` field tag acts as `query` and `formData`, with priority on `formData`.\n\n" + - "It is decoded with `http.Request.Form` values.") - u.SetTags("Request") - - return u -} diff --git a/_examples/advanced-generic/gzip_pass_through.go b/_examples/advanced-generic/gzip_pass_through.go deleted file mode 100644 index f09d3b7..0000000 --- a/_examples/advanced-generic/gzip_pass_through.go +++ /dev/null @@ -1,92 +0,0 @@ -//go:build go1.18 - -package main - -import ( - "context" - - "github.com/swaggest/rest/gzip" - "github.com/swaggest/usecase" -) - -type gzipPassThroughInput struct { - PlainStruct bool `query:"plainStruct" description:"Output plain structure instead of gzip container."` - CountItems bool `query:"countItems" description:"Invokes internal decoding of compressed data."` -} - -// gzipPassThroughOutput defers data to an accessor function instead of using struct directly. -// This is necessary to allow containers that can data in binary wire-friendly format. -type gzipPassThroughOutput interface { - // Data should be accessed though an accessor to allow container interface. - gzipPassThroughStruct() gzipPassThroughStruct -} - -// gzipPassThroughStruct represents the actual structure that is held in the container -// and implements gzipPassThroughOutput to be directly useful in output. -type gzipPassThroughStruct struct { - Header string `header:"X-Header" json:"-"` - ID int `json:"id"` - Text []string `json:"text"` -} - -func (d gzipPassThroughStruct) gzipPassThroughStruct() gzipPassThroughStruct { - return d -} - -// gzipPassThroughContainer is wrapping gzip.JSONContainer and implements gzipPassThroughOutput. -type gzipPassThroughContainer struct { - Header string `header:"X-Header" json:"-"` - gzip.JSONContainer -} - -func (dc gzipPassThroughContainer) gzipPassThroughStruct() gzipPassThroughStruct { - var p gzipPassThroughStruct - - err := dc.UnpackJSON(&p) - if err != nil { - panic(err) - } - - return p -} - -func directGzip() usecase.Interactor { - // Prepare moderately big JSON, resulting JSON payload is ~67KB. - rawData := gzipPassThroughStruct{ - ID: 123, - } - for i := 0; i < 400; i++ { - rawData.Text = append(rawData.Text, "Quis autem vel eum iure reprehenderit, qui in ea voluptate velit esse, "+ - "quam nihil molestiae consequatur, vel illum, qui dolorem eum fugiat, quo voluptas nulla pariatur?") - } - - // Precompute compressed data container. Generally this step should be owned by a caching storage of data. - dataFromCache := gzipPassThroughContainer{} - - err := dataFromCache.PackJSON(rawData) - if err != nil { - panic(err) - } - - u := usecase.NewInteractor(func(ctx context.Context, in gzipPassThroughInput, out *gzipPassThroughOutput) error { - if in.PlainStruct { - o := rawData - o.Header = "cba" - *out = o - } else { - o := dataFromCache - o.Header = "abc" - *out = o - } - - // Imitating an internal read operation on data in container. - if in.CountItems { - _ = len((*out).gzipPassThroughStruct().Text) - } - - return nil - }) - u.SetTags("Response") - - return u -} diff --git a/_examples/advanced-generic/gzip_pass_through_test.go b/_examples/advanced-generic/gzip_pass_through_test.go deleted file mode 100644 index c50bb04..0000000 --- a/_examples/advanced-generic/gzip_pass_through_test.go +++ /dev/null @@ -1,154 +0,0 @@ -//go:build go1.18 - -package main - -import ( - "net/http" - "net/http/httptest" - "testing" - - "github.com/bool64/httptestbench" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "github.com/valyala/fasthttp" -) - -func Test_directGzip(t *testing.T) { - r := NewRouter() - - req, err := http.NewRequest(http.MethodGet, "/gzip-pass-through", nil) - require.NoError(t, err) - - req.Header.Set("Accept-Encoding", "gzip") - - rw := httptest.NewRecorder() - - r.ServeHTTP(rw, req) - assert.Equal(t, http.StatusOK, rw.Code) - assert.Equal(t, "330epditz19z", rw.Header().Get("Etag")) - assert.Equal(t, "gzip", rw.Header().Get("Content-Encoding")) - assert.Equal(t, "abc", rw.Header().Get("X-Header")) - assert.Less(t, len(rw.Body.Bytes()), 500) -} - -func Test_noDirectGzip(t *testing.T) { - r := NewRouter() - - req, err := http.NewRequest(http.MethodGet, "/gzip-pass-through?plainStruct=1", nil) - require.NoError(t, err) - - req.Header.Set("Accept-Encoding", "gzip") - - rw := httptest.NewRecorder() - - r.ServeHTTP(rw, req) - assert.Equal(t, http.StatusOK, rw.Code) - assert.Equal(t, "", rw.Header().Get("Etag")) // No ETag for dynamic compression. - assert.Equal(t, "gzip", rw.Header().Get("Content-Encoding")) - assert.Equal(t, "cba", rw.Header().Get("X-Header")) - assert.Less(t, len(rw.Body.Bytes()), 1000) // Worse compression for better speed. -} - -func Test_directGzip_perf(t *testing.T) { - res := testing.Benchmark(Benchmark_directGzip) - - if httptestbench.RaceDetectorEnabled { - assert.Less(t, res.Extra["B:rcvd/op"], 700.0) - assert.Less(t, res.Extra["B:sent/op"], 104.0) - assert.Less(t, res.AllocsPerOp(), int64(60)) - assert.Less(t, res.AllocedBytesPerOp(), int64(8500)) - } else { - assert.Less(t, res.Extra["B:rcvd/op"], 700.0) - assert.Less(t, res.Extra["B:sent/op"], 104.0) - assert.Less(t, res.AllocsPerOp(), int64(45)) - assert.Less(t, res.AllocedBytesPerOp(), int64(4100)) - } -} - -// Direct gzip enabled. -// Benchmark_directGzip-4 48037 24474 ns/op 624 B:rcvd/op 103 B:sent/op 40860 rps 3499 B/op 36 allocs/op. -// Benchmark_directGzip-4 45792 26102 ns/op 624 B:rcvd/op 103 B:sent/op 38278 rps 3063 B/op 33 allocs/op. -func Benchmark_directGzip(b *testing.B) { - r := NewRouter() - - srv := httptest.NewServer(r) - defer srv.Close() - - httptestbench.RoundTrip(b, 50, func(i int, req *fasthttp.Request) { - req.Header.Set("Accept-Encoding", "gzip") - req.SetRequestURI(srv.URL + "/gzip-pass-through") - }, func(i int, resp *fasthttp.Response) bool { - return resp.StatusCode() == http.StatusOK - }) -} - -// Direct gzip enabled. -// Benchmark_directGzipHead-4 43804 26481 ns/op 168 B:rcvd/op 104 B:sent/op 37730 rps 3507 B/op 36 allocs/op. -// Benchmark_directGzipHead-4 45580 32286 ns/op 168 B:rcvd/op 104 B:sent/op 30963 rps 3093 B/op 33 allocs/op. -func Benchmark_directGzipHead(b *testing.B) { - r := NewRouter() - - srv := httptest.NewServer(r) - defer srv.Close() - - httptestbench.RoundTrip(b, 50, func(i int, req *fasthttp.Request) { - req.Header.SetMethod(http.MethodHead) - req.Header.Set("Accept-Encoding", "gzip") - req.SetRequestURI(srv.URL + "/gzip-pass-through") - }, func(i int, resp *fasthttp.Response) bool { - return resp.StatusCode() == http.StatusOK - }) -} - -// Direct gzip disabled, payload is marshaled and compressed for every request. -// Benchmark_noDirectGzip-4 8031 136836 ns/op 1029 B:rcvd/op 117 B:sent/op 7308 rps 5382 B/op 41 allocs/op. -// Benchmark_noDirectGzip-4 7587 143294 ns/op 1029 B:rcvd/op 117 B:sent/op 6974 rps 4619 B/op 38 allocs/op. -// Benchmark_noDirectGzip-4 7825 157317 ns/op 1029 B:rcvd/op 117 B:sent/op 6357 rps 4655 B/op 40 allocs/op. -func Benchmark_noDirectGzip(b *testing.B) { - r := NewRouter() - - srv := httptest.NewServer(r) - defer srv.Close() - - httptestbench.RoundTrip(b, 50, func(i int, req *fasthttp.Request) { - req.Header.Set("Accept-Encoding", "gzip") - req.SetRequestURI(srv.URL + "/gzip-pass-through?plainStruct=1") - }, func(i int, resp *fasthttp.Response) bool { - return resp.StatusCode() == http.StatusOK - }) -} - -// Direct gzip enabled, payload is unmarshaled and decompressed for every request in usecase body. -// Unmarshaling large JSON payloads can be much more expensive than explicitly creating them from Go values. -// Benchmark_directGzip_decode-4 2018 499755 ns/op 624 B:rcvd/op 116 B:sent/op 2001 rps 403967 B/op 496 allocs/op. -// Benchmark_directGzip_decode-4 2085 526586 ns/op 624 B:rcvd/op 116 B:sent/op 1899 rps 403600 B/op 493 allocs/op. -func Benchmark_directGzip_decode(b *testing.B) { - r := NewRouter() - - srv := httptest.NewServer(r) - defer srv.Close() - - httptestbench.RoundTrip(b, 50, func(i int, req *fasthttp.Request) { - req.Header.Set("Accept-Encoding", "gzip") - req.SetRequestURI(srv.URL + "/gzip-pass-through?countItems=1") - }, func(i int, resp *fasthttp.Response) bool { - return resp.StatusCode() == http.StatusOK - }) -} - -// Direct gzip disabled. -// Benchmark_noDirectGzip_decode-4 7603 142173 ns/op 1029 B:rcvd/op 130 B:sent/op 7034 rps 5122 B/op 43 allocs/op. -// Benchmark_noDirectGzip_decode-4 5836 198000 ns/op 1029 B:rcvd/op 130 B:sent/op 5051 rps 5371 B/op 42 allocs/op. -func Benchmark_noDirectGzip_decode(b *testing.B) { - r := NewRouter() - - srv := httptest.NewServer(r) - defer srv.Close() - - httptestbench.RoundTrip(b, 50, func(i int, req *fasthttp.Request) { - req.Header.Set("Accept-Encoding", "gzip") - req.SetRequestURI(srv.URL + "/gzip-pass-through?plainStruct=1&countItems=1") - }, func(i int, resp *fasthttp.Response) bool { - return resp.StatusCode() == http.StatusOK - }) -} diff --git a/_examples/advanced-generic/html_response.go b/_examples/advanced-generic/html_response.go deleted file mode 100644 index bdf6242..0000000 --- a/_examples/advanced-generic/html_response.go +++ /dev/null @@ -1,70 +0,0 @@ -//go:build go1.18 - -package main - -import ( - "context" - "html/template" - "io" - - "github.com/swaggest/usecase" -) - -type htmlResponseOutput struct { - ID int - Filter string - Title string - Items []string - AntiHeader bool `header:"X-Anti-Header"` - - writer io.Writer -} - -func (o *htmlResponseOutput) SetWriter(w io.Writer) { - o.writer = w -} - -func (o *htmlResponseOutput) Render(tmpl *template.Template) error { - return tmpl.Execute(o.writer, o) -} - -func htmlResponse() usecase.Interactor { - type htmlResponseInput struct { - ID int `path:"id"` - Filter string `query:"filter"` - Header bool `header:"X-Header"` - } - - const tpl = ` - -
- -