Skip to content
Merged
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
18 changes: 13 additions & 5 deletions upload_download/cmd/upload_download/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"syscall"

uploaddownload "goa.design/examples/upload_download"
"goa.design/examples/upload_download/gen/updown"
genupdown "goa.design/examples/upload_download/gen/updown"
)

func main() {
Expand All @@ -36,19 +36,27 @@ func main() {
logger = log.New(os.Stderr, "[upload-download] ", log.Ltime)
}

// Initialize the services.
updownSvc, err := uploaddownload.NewUpdown(*dirF, logger)
// Open the root directory used to confine uploaded and downloaded files.
root, err := os.OpenRoot(*dirF)
if err != nil {
logger.Fatal(err.Error())
}
defer func() {
if closeErr := root.Close(); closeErr != nil {
logger.Printf("failed to close root directory: %v", closeErr)
}
}()

// Initialize the services.
updownSvc := uploaddownload.NewUpdown(root, logger)

// Wrap the services in endpoints that can be invoked from other services
// potentially running in different processes.
var (
updownEndpoints *updown.Endpoints
updownEndpoints *genupdown.Endpoints
)
{
updownEndpoints = updown.NewEndpoints(updownSvc)
updownEndpoints = genupdown.NewEndpoints(updownSvc)
}

// Create channel used by both the signal handler and server goroutines
Expand Down
2 changes: 2 additions & 0 deletions upload_download/design/design.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ incompatible with gRPC.`)

Error("invalid_media_type", ErrorResult, "Error returned when the Content-Type header does not define a multipart request.")
Error("invalid_multipart_request", ErrorResult, "Error returned when the request body is not a valid multipart content.")
Error("invalid_file_path", ErrorResult, "Error returned when the upload directory path is invalid.")
Error("internal_error", ErrorResult, "Fault while processing upload.")

HTTP(func() {
Expand All @@ -53,6 +54,7 @@ incompatible with gRPC.`)
// Define error HTTP statuses.
Response("invalid_media_type", StatusBadRequest)
Response("invalid_multipart_request", StatusBadRequest)
Response("invalid_file_path", StatusBadRequest)
Response("internal_error", StatusInternalServerError)
})
})
Expand Down
2 changes: 1 addition & 1 deletion upload_download/gen/http/openapi.json

Large diffs are not rendered by default.

49 changes: 46 additions & 3 deletions upload_download/gen/http/openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -83,15 +83,15 @@ paths:
"204":
description: No Content response.
"400":
description: 'invalid_multipart_request: Error returned when the request body is not a valid multipart content.'
description: 'invalid_file_path: Error returned when the upload directory path is invalid.'
schema:
$ref: '#/definitions/UpdownUploadInvalidMultipartRequestResponseBody'
$ref: '#/definitions/UpdownUploadInvalidFilePathResponseBody'
examples:
application/vnd.goa.error:
fault: false
id: 123abc
message: parameter 'p' must be an integer
name: invalid_multipart_request
name: invalid_file_path
temporary: false
timeout: false
"500":
Expand Down Expand Up @@ -238,6 +238,49 @@ definitions:
- temporary
- timeout
- fault
UpdownUploadInvalidFilePathResponseBody:
title: 'Mediatype identifier: application/vnd.goa.error; view=default'
type: object
properties:
fault:
type: boolean
description: Is the error a server-side fault?
example: false
id:
type: string
description: ID is a unique identifier for this particular occurrence of the problem.
example: 123abc
message:
type: string
description: Message is a human-readable explanation specific to this occurrence of the problem.
example: parameter 'p' must be an integer
name:
type: string
description: Name is the name of this class of errors.
example: bad_request
temporary:
type: boolean
description: Is the error temporary?
example: false
timeout:
type: boolean
description: Is the error a timeout?
example: true
description: Error response result type (default view)
example:
fault: false
id: 123abc
message: parameter 'p' must be an integer
name: bad_request
temporary: false
timeout: true
required:
- name
- id
- message
- temporary
- timeout
- fault
UpdownUploadInvalidMediaTypeResponseBody:
title: 'Mediatype identifier: application/vnd.goa.error; view=default'
type: object
Expand Down
2 changes: 1 addition & 1 deletion upload_download/gen/http/openapi3.2.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"openapi":"3.2.0","info":{"title":"Goa API","description":"Simple file upload and download example.","version":"0.0.1"},"servers":[{"url":"http://localhost:80","name":"upload_download","description":"Default server for upload_download"}],"paths":{"/download/{filename}":{"get":{"tags":["updown"],"summary":"download updown","operationId":"updown#download","parameters":[{"name":"filename","in":"path","description":"Path to downloaded file.","required":true,"schema":{"type":"string","description":"Path to downloaded file.","example":"Ut rerum est minus natus voluptas ex."},"example":"Ut rerum est minus natus voluptas ex."}],"responses":{"200":{"description":"OK response.","headers":{"Content-Length":{"description":"Length is the downloaded content length in bytes.","schema":{"type":"integer","description":"Length is the downloaded content length in bytes.","example":4194304,"format":"int64"},"example":4194304}},"content":{"application/json":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"invalid_file_path: Could not locate file for download","content":{"application/vnd.goa.error":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"fault":false,"id":"123abc","message":"parameter 'p' must be an integer","name":"invalid_file_path","temporary":false,"timeout":false}}}},"500":{"description":"internal_error: Fault while processing download.","content":{"application/vnd.goa.error":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"fault":false,"id":"123abc","message":"parameter 'p' must be an integer","name":"internal_error","temporary":false,"timeout":false}}}}}}},"/upload/{dir}":{"post":{"tags":["updown"],"summary":"upload updown","operationId":"updown#upload","parameters":[{"name":"dir","in":"path","description":"Dir is the relative path to the file directory where the uploaded content is saved.","required":true,"schema":{"type":"string","description":"Dir is the relative path to the file directory where the uploaded content is saved.","default":"upload","example":"upload"},"example":"upload"},{"name":"Content-Type","in":"header","description":"Content-Type header, must define value for multipart boundary.","schema":{"type":"string","description":"Content-Type header, must define value for multipart boundary.","default":"multipart/form-data; boundary=goa","example":"multipart/form-data; boundary=goa","pattern":"multipart/[^;]+; boundary=.+"},"example":"multipart/form-data; boundary=goa"}],"responses":{"204":{"description":"No Content response."},"400":{"description":"invalid_multipart_request: Error returned when the request body is not a valid multipart content.","content":{"application/vnd.goa.error":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"fault":false,"id":"123abc","message":"parameter 'p' must be an integer","name":"invalid_multipart_request","temporary":false,"timeout":false}}}},"500":{"description":"internal_error: Fault while processing upload.","content":{"application/vnd.goa.error":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"fault":false,"id":"123abc","message":"parameter 'p' must be an integer","name":"internal_error","temporary":false,"timeout":false}}}}}}}},"components":{"schemas":{"Error":{"type":"object","properties":{"fault":{"type":"boolean","description":"Is the error a server-side fault?","example":false},"id":{"type":"string","description":"ID is a unique identifier for this particular occurrence of the problem.","example":"123abc"},"message":{"type":"string","description":"Message is a human-readable explanation specific to this occurrence of the problem.","example":"parameter 'p' must be an integer"},"name":{"type":"string","description":"Name is the name of this class of errors.","example":"bad_request"},"temporary":{"type":"boolean","description":"Is the error temporary?","example":false},"timeout":{"type":"boolean","description":"Is the error a timeout?","example":false}},"description":"Error response result type","example":{"fault":false,"id":"123abc","message":"parameter 'p' must be an integer","name":"bad_request","temporary":false,"timeout":false},"required":["name","id","message","temporary","timeout","fault"]}}},"tags":[{"name":"updown","description":"Service updown demonstrates how to implement upload and download of files in\nGoa without having to load the entire content in memory first.\nThe upload method uses SkipRequestBodyEncodeDecode to delegate reading the HTTP\nrequest body to the service logic. This alleviates the need for loading the\nfull body content in memory first to decode it into a data structure. Note that\nusing SkipRequestBodyDecode is incompatible with gRPC and can only be used on\nmethods that only define a HTTP transport mapping. This example implementation\nleverages package \"mime/multipart\" to read the request body.\nSimilarly the download method uses SkipResponseBodyEncodeDecode to stream the \nfile to the client without requiring to load the complete content in memory\nfirst. As with SkipRequestBodyDecode using SkipResponseBodyEncodeDecode is\nincompatible with gRPC."}]}
{"openapi":"3.2.0","info":{"title":"Goa API","description":"Simple file upload and download example.","version":"0.0.1"},"servers":[{"url":"http://localhost:80","name":"upload_download","description":"Default server for upload_download"}],"paths":{"/download/{filename}":{"get":{"tags":["updown"],"summary":"download updown","operationId":"updown#download","parameters":[{"name":"filename","in":"path","description":"Path to downloaded file.","required":true,"schema":{"type":"string","description":"Path to downloaded file.","example":"Ut rerum est minus natus voluptas ex."},"example":"Ut rerum est minus natus voluptas ex."}],"responses":{"200":{"description":"OK response.","headers":{"Content-Length":{"description":"Length is the downloaded content length in bytes.","schema":{"type":"integer","description":"Length is the downloaded content length in bytes.","example":4194304,"format":"int64"},"example":4194304}},"content":{"application/json":{"schema":{"type":"string","format":"binary"}}}},"404":{"description":"invalid_file_path: Could not locate file for download","content":{"application/vnd.goa.error":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"fault":false,"id":"123abc","message":"parameter 'p' must be an integer","name":"invalid_file_path","temporary":false,"timeout":false}}}},"500":{"description":"internal_error: Fault while processing download.","content":{"application/vnd.goa.error":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"fault":false,"id":"123abc","message":"parameter 'p' must be an integer","name":"internal_error","temporary":false,"timeout":false}}}}}}},"/upload/{dir}":{"post":{"tags":["updown"],"summary":"upload updown","operationId":"updown#upload","parameters":[{"name":"dir","in":"path","description":"Dir is the relative path to the file directory where the uploaded content is saved.","required":true,"schema":{"type":"string","description":"Dir is the relative path to the file directory where the uploaded content is saved.","default":"upload","example":"upload"},"example":"upload"},{"name":"Content-Type","in":"header","description":"Content-Type header, must define value for multipart boundary.","schema":{"type":"string","description":"Content-Type header, must define value for multipart boundary.","default":"multipart/form-data; boundary=goa","example":"multipart/form-data; boundary=goa","pattern":"multipart/[^;]+; boundary=.+"},"example":"multipart/form-data; boundary=goa"}],"responses":{"204":{"description":"No Content response."},"400":{"description":"invalid_file_path: Error returned when the upload directory path is invalid.","content":{"application/vnd.goa.error":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"fault":false,"id":"123abc","message":"parameter 'p' must be an integer","name":"invalid_file_path","temporary":false,"timeout":false}}}},"500":{"description":"internal_error: Fault while processing upload.","content":{"application/vnd.goa.error":{"schema":{"$ref":"#/components/schemas/Error"},"example":{"fault":false,"id":"123abc","message":"parameter 'p' must be an integer","name":"internal_error","temporary":false,"timeout":false}}}}}}}},"components":{"schemas":{"Error":{"type":"object","properties":{"fault":{"type":"boolean","description":"Is the error a server-side fault?","example":false},"id":{"type":"string","description":"ID is a unique identifier for this particular occurrence of the problem.","example":"123abc"},"message":{"type":"string","description":"Message is a human-readable explanation specific to this occurrence of the problem.","example":"parameter 'p' must be an integer"},"name":{"type":"string","description":"Name is the name of this class of errors.","example":"bad_request"},"temporary":{"type":"boolean","description":"Is the error temporary?","example":false},"timeout":{"type":"boolean","description":"Is the error a timeout?","example":false}},"description":"Error response result type","example":{"fault":false,"id":"123abc","message":"parameter 'p' must be an integer","name":"bad_request","temporary":false,"timeout":false},"required":["name","id","message","temporary","timeout","fault"]}}},"tags":[{"name":"updown","description":"Service updown demonstrates how to implement upload and download of files in\nGoa without having to load the entire content in memory first.\nThe upload method uses SkipRequestBodyEncodeDecode to delegate reading the HTTP\nrequest body to the service logic. This alleviates the need for loading the\nfull body content in memory first to decode it into a data structure. Note that\nusing SkipRequestBodyDecode is incompatible with gRPC and can only be used on\nmethods that only define a HTTP transport mapping. This example implementation\nleverages package \"mime/multipart\" to read the request body.\nSimilarly the download method uses SkipResponseBodyEncodeDecode to stream the \nfile to the client without requiring to load the complete content in memory\nfirst. As with SkipRequestBodyDecode using SkipResponseBodyEncodeDecode is\nincompatible with gRPC."}]}
4 changes: 2 additions & 2 deletions upload_download/gen/http/openapi3.2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ paths:
"204":
description: No Content response.
"400":
description: 'invalid_multipart_request: Error returned when the request body is not a valid multipart content.'
description: 'invalid_file_path: Error returned when the upload directory path is invalid.'
content:
application/vnd.goa.error:
schema:
Expand All @@ -107,7 +107,7 @@ paths:
fault: false
id: 123abc
message: parameter 'p' must be an integer
name: invalid_multipart_request
name: invalid_file_path
temporary: false
timeout: false
"500":
Expand Down
Loading
Loading