Repository: osbuild/osbuild-composer
Pull request #159 (2019-12-11 17:53:53)

Add toml support to /source routes


Patch:
diff --git a/internal/weldr/api.go b/internal/weldr/api.go
index 55dd816bd..d9034269d 100644
--- a/internal/weldr/api.go
+++ b/internal/weldr/api.go
@@ -301,10 +301,34 @@ func (api *API) sourceInfoHandler(writer http.ResponseWriter, request *http.Requ
 		}
 	}
 
-	json.NewEncoder(writer).Encode(reply{
-		Sources: sources,
-		Errors:  errors,
-	})
+	q, err := url.ParseQuery(request.URL.RawQuery)
+	if err != nil {
+		errors := responseError{
+			ID:  "InvalidChars",
+			Msg: fmt.Sprintf("invalid query string: %v", err),
+		}
+		statusResponseError(writer, http.StatusBadRequest, errors)
+		return
+	}
+
+	format := q.Get("format")
+	if format == "json" || format == "" {
+		json.NewEncoder(writer).Encode(reply{
+			Sources: sources,
+			Errors:  errors,
+		})
+	} else if format == "toml" {
+		encoder := toml.NewEncoder(writer)
+		encoder.Indent = ""
+		encoder.Encode(sources)
+	} else {
+		errors := responseError{
+			ID:  "InvalidChars",
+			Msg: fmt.Sprintf("invalid format parameter: %s", format),
+		}
+		statusResponseError(writer, http.StatusBadRequest, errors)
+		return
+	}
 }
 
 func (api *API) sourceNewHandler(writer http.ResponseWriter, request *http.Request, params httprouter.Params) {
@@ -313,17 +337,25 @@ func (api *API) sourceNewHandler(writer http.ResponseWriter, request *http.Reque
 	}
 
 	contentType := request.Header["Content-Type"]
-	if len(contentType) != 1 || contentType[0] != "application/json" {
+	if len(contentType) == 0 {
 		errors := responseError{
 			ID:  "HTTPError",
-			Msg: "Internal Server Error",
+			Msg: "malformed Content-Type header",
 		}
-		statusResponseError(writer, http.StatusInternalServerError, errors)
+		statusResponseError(writer, http.StatusBadRequest, errors)
 		return
 	}
 
 	var source store.SourceConfig
-	err := json.NewDecoder(request.Body).Decode(&source)
+	var err error
+	if contentType[0] == "application/json" {
+		err = json.NewDecoder(request.Body).Decode(&source)
+	} else if contentType[0] == "text/x-toml" {
+		_, err = toml.DecodeReader(request.Body, &source)
+	} else {
+		err = errors.New("blueprint must be in json or toml format")
+	}
+
 	if err != nil {
 		errors := responseError{
 			Code: http.StatusBadRequest,
@@ -746,7 +778,7 @@ func (api *API) blueprintsInfoHandler(writer http.ResponseWriter, request *http.
 	} else {
 		errors := responseError{
 			ID:  "InvalidChars",
-			Msg: fmt.Sprintf("invalid `format` parameter: %s", format),
+			Msg: fmt.Sprintf("invalid format parameter: %s", format),
 		}
 		statusResponseError(writer, http.StatusBadRequest, errors)
 		return
@@ -1052,7 +1084,7 @@ func (api *API) blueprintsNewHandler(writer http.ResponseWriter, request *http.R
 	}
 
 	contentType := request.Header["Content-Type"]
-	if len(contentType) != 1 {
+	if len(contentType) == 0 {
 		errors := responseError{
 			ID:  "BlueprintsError",
 			Msg: "missing Content-Type header",


Test patch:
diff --git a/internal/weldr/api_test.go b/internal/weldr/api_test.go
index 8c0e1020b..89a41a169 100644
--- a/internal/weldr/api_test.go
+++ b/internal/weldr/api_test.go
@@ -594,6 +594,69 @@ func TestSourcesNew(t *testing.T) {
 	}
 }
 
+func TestSourcesNewToml(t *testing.T) {
+	source := `
+name = "fish"
+url = "https://download.opensuse.org/repositories/shells:/fish:/release:/3/Fedora_29/"
+type = "yum-baseurl"
+check_ssl = false
+check_gpg = false
+`
+	req := httptest.NewRequest("POST", "/api/v0/projects/source/new", bytes.NewReader([]byte(source)))
+	req.Header.Set("Content-Type", "text/x-toml")
+	recorder := httptest.NewRecorder()
+
+	api, _ := createWeldrAPI(rpmmd_mock.BaseFixture)
+	api.ServeHTTP(recorder, req)
+
+	r := recorder.Result()
+	if r.StatusCode != http.StatusOK {
+		t.Fatalf("unexpected status %v", r.StatusCode)
+	}
+
+	test.SendHTTP(api, true, "DELETE", "/api/v0/projects/source/delete/fish", ``)
+}
+
+func TestSourcesInfo(t *testing.T) {
+	sourceStr := `{"name":"fish","type":"yum-baseurl","url":"https://download.opensuse.org/repositories/shells:/fish:/release:/3/Fedora_29/","check_gpg":false,"check_ssl":false,"system":false}`
+
+	api, _ := createWeldrAPI(rpmmd_mock.BaseFixture)
+	test.SendHTTP(api, true, "POST", "/api/v0/projects/source/new", sourceStr)
+	test.TestRoute(t, api, true, "GET", "/api/v0/projects/source/info/fish", ``, 200, `{"sources":{"fish":` + sourceStr + `},"errors":[]}`)
+	test.TestRoute(t, api, true, "GET", "/api/v0/projects/source/info/fish?format=json", ``, 200, `{"sources":{"fish":` + sourceStr + `},"errors":[]}`)
+	test.TestRoute(t, api, true, "GET", "/api/v0/projects/source/info/fish?format=son", ``, 400, `{"status":false,"errors":[{"id":"InvalidChars","msg":"invalid format parameter: son"}]}`)
+}
+
+func TestSourcesInfoToml(t *testing.T) {
+	sourceStr := `{"name":"fish","type":"yum-baseurl","url":"https://download.opensuse.org/repositories/shells:/fish:/release:/3/Fedora_29/","check_gpg":false,"check_ssl":false,"system":false}`
+
+	api, _ := createWeldrAPI(rpmmd_mock.BaseFixture)
+	test.SendHTTP(api, true, "POST", "/api/v0/projects/source/new", sourceStr)
+
+	req := httptest.NewRequest("GET", "/api/v0/projects/source/info/fish?format=toml", nil)
+	recorder := httptest.NewRecorder()
+	api.ServeHTTP(recorder, req)
+	resp := recorder.Result()
+
+	var sources map[string]store.SourceConfig
+	_, err := toml.DecodeReader(resp.Body, &sources)
+	if err != nil {
+		t.Fatalf("error decoding toml file: %v", err)
+	}
+
+	expected := map[string]store.SourceConfig{
+		"fish": {
+			Name: "fish",
+			Type: "yum-baseurl",
+			URL:  "https://download.opensuse.org/repositories/shells:/fish:/release:/3/Fedora_29/",
+		},
+	}
+
+	if diff := cmp.Diff(sources, expected); diff != "" {
+		t.Fatalf("received unexpected source: %s", diff)
+	}
+}
+
 func TestSourcesDelete(t *testing.T) {
 	var cases = []struct {
 		Method         string