diff --git a/design/design.go b/design/design.go index 7fb32d206..b690a40e4 100644 --- a/design/design.go +++ b/design/design.go @@ -24,5 +24,7 @@ var _ = API("arduino-create-agent", func() { such as detecting which boards are connected and upload sketches on them.`) HTTP(func() { Path("/v2") + Consumes("application/json") + Consumes("plain/text") }) }) diff --git a/gen/http/openapi.json b/gen/http/openapi.json index a315391ad..e6a796113 100644 --- a/gen/http/openapi.json +++ b/gen/http/openapi.json @@ -1 +1 @@ -{"swagger":"2.0","info":{"title":"Arduino Create Agent","description":"A companion of Arduino Create. \n\tAllows the website to perform operations on the user computer, \n\tsuch as detecting which boards are connected and upload sketches on them.","version":""},"host":"localhost:80","basePath":"/v2","consumes":["application/json","application/xml","application/gob"],"produces":["application/json","application/xml","application/gob"],"paths":{"/pkgs/indexes":{"get":{"tags":["indexes"],"summary":"list indexes","operationId":"indexes#list","responses":{"200":{"description":"OK response.","schema":{"type":"array","items":{"type":"string","example":"Repudiandae dignissimos consectetur eos molestiae culpa soluta."}}},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/IndexesListInvalidURLResponseBody"}}},"schemes":["http"]}},"/pkgs/indexes/add":{"post":{"tags":["indexes"],"summary":"add indexes","operationId":"indexes#add","parameters":[{"name":"AddRequestBody","in":"body","required":true,"schema":{"$ref":"#/definitions/IndexesAddRequestBody","required":["url"]}}],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/IndexesAddResponseBody"}},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/IndexesAddInvalidURLResponseBody"}}},"schemes":["http"]}},"/pkgs/indexes/delete":{"post":{"tags":["indexes"],"summary":"remove indexes","operationId":"indexes#remove","parameters":[{"name":"RemoveRequestBody","in":"body","required":true,"schema":{"$ref":"#/definitions/IndexesRemoveRequestBody","required":["url"]}}],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/IndexesRemoveResponseBody"}},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/IndexesRemoveInvalidURLResponseBody"}}},"schemes":["http"]}},"/pkgs/tools/available":{"get":{"tags":["tools"],"summary":"available tools","operationId":"tools#available","responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/ToolsToolResponseCollection"}}},"schemes":["http"]}},"/pkgs/tools/installed":{"get":{"tags":["tools"],"summary":"installed tools","operationId":"tools#installed","responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/ToolsToolResponseCollection"}}},"schemes":["http"]},"post":{"tags":["tools"],"summary":"install tools","operationId":"tools#install","parameters":[{"name":"InstallRequestBody","in":"body","required":true,"schema":{"$ref":"#/definitions/ToolsInstallRequestBody","required":["name","version","packager"]}}],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/ToolsInstallResponseBody"}}},"schemes":["http"]}},"/pkgs/tools/installed/{packager}/{name}/{version}":{"delete":{"tags":["tools"],"summary":"remove tools","operationId":"tools#remove","parameters":[{"name":"packager","in":"path","description":"The packager of the tool","required":true,"type":"string"},{"name":"name","in":"path","description":"The name of the tool","required":true,"type":"string"},{"name":"version","in":"path","description":"The version of the tool","required":true,"type":"string"},{"name":"RemoveRequestBody","in":"body","required":true,"schema":{"$ref":"#/definitions/ToolsRemoveRequestBody"}}],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/ToolsRemoveResponseBody"}}},"schemes":["http"]}}},"definitions":{"IndexesAddInvalidURLResponseBody":{"title":"Mediatype identifier: application/vnd.goa.error; view=default","type":"object","properties":{"fault":{"type":"boolean","description":"Is the error a server-side fault?","example":true},"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":true},"timeout":{"type":"boolean","description":"Is the error a timeout?","example":false}},"description":"url invalid (default view)","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"]},"IndexesAddRequestBody":{"title":"IndexesAddRequestBody","type":"object","properties":{"url":{"type":"string","description":"The url of the index file","example":"https://downloads.arduino.cc/packages/package_index.json"}},"example":{"url":"https://downloads.arduino.cc/packages/package_index.json"},"required":["url"]},"IndexesAddResponseBody":{"title":"Mediatype identifier: application/vnd.arduino.operation; view=default","type":"object","properties":{"status":{"type":"string","description":"The status of the operation","example":"ok"}},"description":"AddResponseBody result type (default view)","example":{"status":"ok"},"required":["status"]},"IndexesListInvalidURLResponseBody":{"title":"Mediatype identifier: application/vnd.goa.error; view=default","type":"object","properties":{"fault":{"type":"boolean","description":"Is the error a server-side fault?","example":true},"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":"url invalid (default view)","example":{"fault":true,"id":"123abc","message":"parameter 'p' must be an integer","name":"bad_request","temporary":true,"timeout":true},"required":["name","id","message","temporary","timeout","fault"]},"IndexesRemoveInvalidURLResponseBody":{"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":true},"timeout":{"type":"boolean","description":"Is the error a timeout?","example":true}},"description":"url invalid (default view)","example":{"fault":false,"id":"123abc","message":"parameter 'p' must be an integer","name":"bad_request","temporary":true,"timeout":false},"required":["name","id","message","temporary","timeout","fault"]},"IndexesRemoveRequestBody":{"title":"IndexesRemoveRequestBody","type":"object","properties":{"url":{"type":"string","description":"The url of the index file","example":"https://downloads.arduino.cc/packages/package_index.json"}},"example":{"url":"https://downloads.arduino.cc/packages/package_index.json"},"required":["url"]},"IndexesRemoveResponseBody":{"title":"Mediatype identifier: application/vnd.arduino.operation; view=default","type":"object","properties":{"status":{"type":"string","description":"The status of the operation","example":"ok"}},"description":"RemoveResponseBody result type (default view)","example":{"status":"ok"},"required":["status"]},"ToolResponse":{"title":"Mediatype identifier: application/vnd.arduino.tool; view=default","type":"object","properties":{"name":{"type":"string","description":"The name of the tool","example":"avrdude"},"packager":{"type":"string","description":"The packager of the tool","example":"arduino"},"version":{"type":"string","description":"The version of the tool","example":"6.3.0-arduino9"}},"description":"A tool is an executable program that can upload sketches. (default view)","example":{"name":"avrdude","packager":"arduino","version":"6.3.0-arduino9"},"required":["name","version","packager"]},"ToolsInstallRequestBody":{"title":"ToolsInstallRequestBody","type":"object","properties":{"checksum":{"type":"string","description":"A checksum of the archive. Mandatory when url is present. \n\tThis ensures that the package is downloaded correcly.","example":"Totam cum inventore exercitationem in."},"name":{"type":"string","description":"The name of the tool","example":"avrdude"},"packager":{"type":"string","description":"The packager of the tool","example":"arduino"},"url":{"type":"string","description":"The url where the package can be found. Optional. \n\tIf present checksum must also be present.","example":"Totam vero ipsum corporis nihil voluptatem id."},"version":{"type":"string","description":"The version of the tool","example":"6.3.0-arduino9"}},"example":{"checksum":"Modi dolorem reprehenderit perspiciatis illo aspernatur.","name":"avrdude","packager":"arduino","url":"Officia optio inventore atque in voluptatibus qui.","version":"6.3.0-arduino9"},"required":["name","version","packager"]},"ToolsInstallResponseBody":{"title":"Mediatype identifier: application/vnd.arduino.operation; view=default","type":"object","properties":{"status":{"type":"string","description":"The status of the operation","example":"ok"}},"description":"InstallResponseBody result type (default view)","example":{"status":"ok"},"required":["status"]},"ToolsRemoveRequestBody":{"title":"ToolsRemoveRequestBody","type":"object","properties":{"checksum":{"type":"string","description":"A checksum of the archive. Mandatory when url is present. \n\tThis ensures that the package is downloaded correcly.","example":"Et qui id et cumque illo."},"url":{"type":"string","description":"The url where the package can be found. Optional. \n\tIf present checksum must also be present.","example":"Officia maiores reiciendis est nemo."}},"example":{"checksum":"Corporis eum et numquam sapiente.","url":"Est voluptatem eos reprehenderit quo sint quod."}},"ToolsRemoveResponseBody":{"title":"Mediatype identifier: application/vnd.arduino.operation; view=default","type":"object","properties":{"status":{"type":"string","description":"The status of the operation","example":"ok"}},"description":"RemoveResponseBody result type (default view)","example":{"status":"ok"},"required":["status"]},"ToolsToolResponseCollection":{"title":"Mediatype identifier: application/vnd.arduino.tool; type=collection; view=default","type":"array","items":{"$ref":"#/definitions/ToolResponse"},"description":"AvailableResponseBody is the result type for an array of ToolResponse (default view)","example":[{"name":"avrdude","packager":"arduino","version":"6.3.0-arduino9"},{"name":"avrdude","packager":"arduino","version":"6.3.0-arduino9"}]}}} \ No newline at end of file +{"swagger":"2.0","info":{"title":"Arduino Create Agent","description":"A companion of Arduino Create. \n\tAllows the website to perform operations on the user computer, \n\tsuch as detecting which boards are connected and upload sketches on them.","version":""},"host":"localhost:80","basePath":"/v2","consumes":["application/json","plain/text"],"produces":["application/json","application/xml","application/gob"],"paths":{"/pkgs/indexes":{"get":{"tags":["indexes"],"summary":"list indexes","operationId":"indexes#list","responses":{"200":{"description":"OK response.","schema":{"type":"array","items":{"type":"string","example":"Repudiandae dignissimos consectetur eos molestiae culpa soluta."}}},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/IndexesListInvalidURLResponseBody"}}},"schemes":["http"]}},"/pkgs/indexes/add":{"post":{"tags":["indexes"],"summary":"add indexes","operationId":"indexes#add","parameters":[{"name":"AddRequestBody","in":"body","required":true,"schema":{"$ref":"#/definitions/IndexesAddRequestBody","required":["url"]}}],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/IndexesAddResponseBody"}},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/IndexesAddInvalidURLResponseBody"}}},"schemes":["http"]}},"/pkgs/indexes/delete":{"post":{"tags":["indexes"],"summary":"remove indexes","operationId":"indexes#remove","parameters":[{"name":"RemoveRequestBody","in":"body","required":true,"schema":{"$ref":"#/definitions/IndexesRemoveRequestBody","required":["url"]}}],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/IndexesRemoveResponseBody"}},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/IndexesRemoveInvalidURLResponseBody"}}},"schemes":["http"]}},"/pkgs/tools/available":{"get":{"tags":["tools"],"summary":"available tools","operationId":"tools#available","responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/ToolsToolResponseCollection"}}},"schemes":["http"]}},"/pkgs/tools/installed":{"get":{"tags":["tools"],"summary":"installed tools","operationId":"tools#installed","responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/ToolsToolResponseCollection"}}},"schemes":["http"]},"post":{"tags":["tools"],"summary":"install tools","operationId":"tools#install","parameters":[{"name":"InstallRequestBody","in":"body","required":true,"schema":{"$ref":"#/definitions/ToolsInstallRequestBody","required":["name","version","packager"]}}],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/ToolsInstallResponseBody"}}},"schemes":["http"]}},"/pkgs/tools/installed/{packager}/{name}/{version}":{"delete":{"tags":["tools"],"summary":"remove tools","operationId":"tools#remove","parameters":[{"name":"packager","in":"path","description":"The packager of the tool","required":true,"type":"string"},{"name":"name","in":"path","description":"The name of the tool","required":true,"type":"string"},{"name":"version","in":"path","description":"The version of the tool","required":true,"type":"string"},{"name":"RemoveRequestBody","in":"body","required":true,"schema":{"$ref":"#/definitions/ToolsRemoveRequestBody"}}],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/ToolsRemoveResponseBody"}}},"schemes":["http"]}}},"definitions":{"IndexesAddInvalidURLResponseBody":{"title":"Mediatype identifier: application/vnd.goa.error; view=default","type":"object","properties":{"fault":{"type":"boolean","description":"Is the error a server-side fault?","example":true},"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":true},"timeout":{"type":"boolean","description":"Is the error a timeout?","example":false}},"description":"url invalid (default view)","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"]},"IndexesAddRequestBody":{"title":"IndexesAddRequestBody","type":"object","properties":{"url":{"type":"string","description":"The url of the index file","example":"https://downloads.arduino.cc/packages/package_index.json"}},"example":{"url":"https://downloads.arduino.cc/packages/package_index.json"},"required":["url"]},"IndexesAddResponseBody":{"title":"Mediatype identifier: application/vnd.arduino.operation; view=default","type":"object","properties":{"status":{"type":"string","description":"The status of the operation","example":"ok"}},"description":"AddResponseBody result type (default view)","example":{"status":"ok"},"required":["status"]},"IndexesListInvalidURLResponseBody":{"title":"Mediatype identifier: application/vnd.goa.error; view=default","type":"object","properties":{"fault":{"type":"boolean","description":"Is the error a server-side fault?","example":true},"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":"url invalid (default view)","example":{"fault":true,"id":"123abc","message":"parameter 'p' must be an integer","name":"bad_request","temporary":true,"timeout":true},"required":["name","id","message","temporary","timeout","fault"]},"IndexesRemoveInvalidURLResponseBody":{"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":true},"timeout":{"type":"boolean","description":"Is the error a timeout?","example":true}},"description":"url invalid (default view)","example":{"fault":false,"id":"123abc","message":"parameter 'p' must be an integer","name":"bad_request","temporary":true,"timeout":false},"required":["name","id","message","temporary","timeout","fault"]},"IndexesRemoveRequestBody":{"title":"IndexesRemoveRequestBody","type":"object","properties":{"url":{"type":"string","description":"The url of the index file","example":"https://downloads.arduino.cc/packages/package_index.json"}},"example":{"url":"https://downloads.arduino.cc/packages/package_index.json"},"required":["url"]},"IndexesRemoveResponseBody":{"title":"Mediatype identifier: application/vnd.arduino.operation; view=default","type":"object","properties":{"status":{"type":"string","description":"The status of the operation","example":"ok"}},"description":"RemoveResponseBody result type (default view)","example":{"status":"ok"},"required":["status"]},"ToolResponse":{"title":"Mediatype identifier: application/vnd.arduino.tool; view=default","type":"object","properties":{"name":{"type":"string","description":"The name of the tool","example":"avrdude"},"packager":{"type":"string","description":"The packager of the tool","example":"arduino"},"version":{"type":"string","description":"The version of the tool","example":"6.3.0-arduino9"}},"description":"A tool is an executable program that can upload sketches. (default view)","example":{"name":"avrdude","packager":"arduino","version":"6.3.0-arduino9"},"required":["name","version","packager"]},"ToolsInstallRequestBody":{"title":"ToolsInstallRequestBody","type":"object","properties":{"checksum":{"type":"string","description":"A checksum of the archive. Mandatory when url is present. \n\tThis ensures that the package is downloaded correcly.","example":"Totam cum inventore exercitationem in."},"name":{"type":"string","description":"The name of the tool","example":"avrdude"},"packager":{"type":"string","description":"The packager of the tool","example":"arduino"},"url":{"type":"string","description":"The url where the package can be found. Optional. \n\tIf present checksum must also be present.","example":"Totam vero ipsum corporis nihil voluptatem id."},"version":{"type":"string","description":"The version of the tool","example":"6.3.0-arduino9"}},"example":{"checksum":"Modi dolorem reprehenderit perspiciatis illo aspernatur.","name":"avrdude","packager":"arduino","url":"Officia optio inventore atque in voluptatibus qui.","version":"6.3.0-arduino9"},"required":["name","version","packager"]},"ToolsInstallResponseBody":{"title":"Mediatype identifier: application/vnd.arduino.operation; view=default","type":"object","properties":{"status":{"type":"string","description":"The status of the operation","example":"ok"}},"description":"InstallResponseBody result type (default view)","example":{"status":"ok"},"required":["status"]},"ToolsRemoveRequestBody":{"title":"ToolsRemoveRequestBody","type":"object","properties":{"checksum":{"type":"string","description":"A checksum of the archive. Mandatory when url is present. \n\tThis ensures that the package is downloaded correcly.","example":"Et qui id et cumque illo."},"url":{"type":"string","description":"The url where the package can be found. Optional. \n\tIf present checksum must also be present.","example":"Officia maiores reiciendis est nemo."}},"example":{"checksum":"Corporis eum et numquam sapiente.","url":"Est voluptatem eos reprehenderit quo sint quod."}},"ToolsRemoveResponseBody":{"title":"Mediatype identifier: application/vnd.arduino.operation; view=default","type":"object","properties":{"status":{"type":"string","description":"The status of the operation","example":"ok"}},"description":"RemoveResponseBody result type (default view)","example":{"status":"ok"},"required":["status"]},"ToolsToolResponseCollection":{"title":"Mediatype identifier: application/vnd.arduino.tool; type=collection; view=default","type":"array","items":{"$ref":"#/definitions/ToolResponse"},"description":"AvailableResponseBody is the result type for an array of ToolResponse (default view)","example":[{"name":"avrdude","packager":"arduino","version":"6.3.0-arduino9"},{"name":"avrdude","packager":"arduino","version":"6.3.0-arduino9"}]}}} \ No newline at end of file diff --git a/gen/http/openapi.yaml b/gen/http/openapi.yaml index 30aaaa8d7..4079b44c9 100644 --- a/gen/http/openapi.yaml +++ b/gen/http/openapi.yaml @@ -7,8 +7,7 @@ host: localhost:80 basePath: /v2 consumes: - application/json - - application/xml - - application/gob + - plain/text produces: - application/json - application/xml diff --git a/main_test.go b/main_test.go index 0f84ff42a..238ae2a2d 100644 --- a/main_test.go +++ b/main_test.go @@ -16,11 +16,20 @@ package main import ( + "bytes" "crypto/x509" + "encoding/json" "encoding/pem" + "io" + "net/http" + "net/http/httptest" "path/filepath" "testing" + "github.com/arduino/arduino-create-agent/config" + "github.com/arduino/arduino-create-agent/gen/tools" + v2 "github.com/arduino/arduino-create-agent/v2" + "github.com/gin-gonic/gin" "github.com/stretchr/testify/require" ) @@ -38,3 +47,35 @@ func TestValidSignatureKey(t *testing.T) { require.NoError(t, err) require.NotNil(t, key) } + +func TestInstallToolDifferentContentType(t *testing.T) { + r := gin.New() + goa := v2.Server(config.GetDataDir().String()) + r.Any("/v2/*path", gin.WrapH(goa)) + ts := httptest.NewServer(r) + + URL := "http://downloads.arduino.cc/tools/bossac-1.7.0-arduino3-linux64.tar.gz" + Checksum := "SHA-256:1ae54999c1f97234a5c603eb99ad39313b11746a4ca517269a9285afa05f9100" + request := tools.ToolPayload{ + Name: "bossac", + Version: "1.7.0-arduino3", + Packager: "arduino", + URL: &URL, + Checksum: &Checksum, + } + + payload, err := json.Marshal(request) + require.NoError(t, err) + + // for some reason the fronted sends requests with "text/plain" content type. + // Even if the request body contains a json object. + // With this test we verify is parsed correctly. + for _, encoding := range []string{"encoding/json", "text/plain"} { + resp, err := http.Post(ts.URL+"/v2/pkgs/tools/installed", encoding, bytes.NewBuffer(payload)) + require.NoError(t, err) + body, err := io.ReadAll(resp.Body) + require.NoError(t, err) + require.Contains(t, string(body), "ok") + require.Equal(t, http.StatusOK, resp.StatusCode) + } +} diff --git a/v2/http.go b/v2/http.go index efafbfd7e..3e9c00965 100644 --- a/v2/http.go +++ b/v2/http.go @@ -17,6 +17,7 @@ package v2 import ( "context" + "encoding/json" "net/http" "path/filepath" @@ -56,7 +57,7 @@ func Server(home string) http.Handler { Indexes: &indexesSvc, } toolsEndpoints := toolssvc.NewEndpoints(&toolsSvc) - toolsServer := toolssvr.New(toolsEndpoints, mux, goahttp.RequestDecoder, goahttp.ResponseEncoder, errorHandler(logger), nil) + toolsServer := toolssvr.New(toolsEndpoints, mux, CustomRequestDecoder, goahttp.ResponseEncoder, errorHandler(logger), nil) toolssvr.Mount(mux, toolsServer) // Mount middlewares @@ -76,3 +77,14 @@ func errorHandler(logger *logrus.Logger) func(context.Context, http.ResponseWrit logger.Printf("[%s] ERROR: %s", id, err.Error()) } } + +// CustomRequestDecoder overrides the RequestDecoder provided by goahttp package +// It returns always a json.NewDecoder for legacy reasons: +// The web editor sends always request to the agent setting "Content-Type: text/plain" +// even when it should set "Content-Type: application/json". This breaks the parsing with: +// "can't decode text/plain to *server.InstallRequestBody" error message. +// This was working before the bump to goa v3 only because a "text/plain" decoder was not implemented +// and it was fallbacking to the json decoder. (https://github.com/goadesign/goa/pull/2310) +func CustomRequestDecoder(r *http.Request) goahttp.Decoder { + return json.NewDecoder(r.Body) +}