From c576d5059e195cdff96ce1a87c805d0e02176bee Mon Sep 17 00:00:00 2001 From: Stanislav Chzhen Date: Tue, 28 Mar 2023 18:02:32 +0300 Subject: [PATCH] Pull request 1789: AG-20200-translation-script-fix-upload Merge in DNS/adguard-home from AG-20200-translation-script-fix-upload to master Squashed commit of the following: commit 4d898926828dc438c29b0ae7cbad70d8dea0b8de Merge: 41ad204b 487675b9 Author: Stanislav Chzhen Date: Tue Mar 28 13:30:36 2023 +0300 Merge branch 'master' into AG-20200-translation-script-fix-upload commit 41ad204bc3ce52ecc61e95261b9519cfc0af9abe Author: Stanislav Chzhen Date: Tue Mar 28 13:29:26 2023 +0300 scripts: imp more commit 5ea4821ee49638ad9011809faba0316a753a078b Author: Stanislav Chzhen Date: Tue Mar 28 12:38:22 2023 +0300 scripts: imp code commit 253a72fd0e195c7603883322885b7206ed434918 Author: Stanislav Chzhen Date: Tue Mar 28 12:04:16 2023 +0300 scripts: add docs commit bfd70e338c78fd5b0292480d7f54f396593e98ca Author: Stanislav Chzhen Date: Tue Mar 28 10:56:05 2023 +0300 scripts: imp more commit 547b82785f210cc137903cb5b7e2ee41b2a20e59 Author: Stanislav Chzhen Date: Tue Mar 28 10:08:16 2023 +0300 scripts: imp code commit a788e5ef8a3aa2633cc9fb64c83534f1d4080ef7 Author: Stanislav Chzhen Date: Mon Mar 27 20:14:55 2023 +0300 scripts: add multipart commit 39d352edf85288a51931dea5b758bc74dd08e19d Author: Stanislav Chzhen Date: Mon Mar 27 18:35:08 2023 +0300 scripts: fix more commit bcbf155135c53789cc5c6c2c7c8b57dd471e4ea2 Author: Stanislav Chzhen Date: Mon Mar 27 17:47:40 2023 +0300 scripts: fix upload --- scripts/translations/main.go | 96 +++++++++++++++++++++++++++++++----- 1 file changed, 85 insertions(+), 11 deletions(-) diff --git a/scripts/translations/main.go b/scripts/translations/main.go index 1922f614..7afd7666 100644 --- a/scripts/translations/main.go +++ b/scripts/translations/main.go @@ -8,7 +8,9 @@ import ( "flag" "fmt" "io" + "mime/multipart" "net/http" + "net/textproto" "net/url" "os" "path/filepath" @@ -426,6 +428,8 @@ func printUnused(loc locales) { // upload base translation. uri is the base URL. projectID is the name of the // project. baseLang is the base language code. func upload(uri *url.URL, projectID string, baseLang langCode) (err error) { + defer func() { err = errors.Annotate(err, "upload: %w") }() + uploadURI := uri.JoinPath("upload") lang := baseLang @@ -436,20 +440,90 @@ func upload(uri *url.URL, projectID string, baseLang langCode) (err error) { } basePath := filepath.Join(localesDir, defaultBaseFile) - b, err := os.ReadFile(basePath) - if err != nil { - return fmt.Errorf("upload: %w", err) + + formData := map[string]string{ + "format": "json", + "language": string(lang), + "filename": defaultBaseFile, + "project": projectID, } - var buf bytes.Buffer - buf.Write(b) - - uri = translationURL(uploadURI, defaultBaseFile, projectID, lang) - - var client http.Client - resp, err := client.Post(uri.String(), "application/json", &buf) + buf, cType, err := prepareMultipartMsg(formData, basePath) if err != nil { - return fmt.Errorf("upload: client post: %w", err) + return fmt.Errorf("preparing multipart msg: %w", err) + } + + err = send(uploadURI.String(), cType, buf) + if err != nil { + return fmt.Errorf("sending multipart msg: %w", err) + } + + return nil +} + +// prepareMultipartMsg prepares translation data for upload. +func prepareMultipartMsg( + formData map[string]string, + basePath string, +) (buf *bytes.Buffer, cType string, err error) { + buf = &bytes.Buffer{} + w := multipart.NewWriter(buf) + var fw io.Writer + + for k, v := range formData { + err = w.WriteField(k, v) + if err != nil { + return nil, "", fmt.Errorf("writing field: %w", err) + } + } + + file, err := os.Open(basePath) + if err != nil { + return nil, "", fmt.Errorf("opening file: %w", err) + } + + defer func() { + err = errors.WithDeferred(err, file.Close()) + }() + + h := make(textproto.MIMEHeader) + h.Set("Content-Type", "application/json") + + d := fmt.Sprintf("form-data; name=%q; filename=%q", "file", defaultBaseFile) + h.Set("Content-Disposition", d) + + fw, err = w.CreatePart(h) + if err != nil { + return nil, "", fmt.Errorf("creating part: %w", err) + } + + _, err = io.Copy(fw, file) + if err != nil { + return nil, "", fmt.Errorf("copying: %w", err) + } + + err = w.Close() + if err != nil { + return nil, "", fmt.Errorf("closing writer: %w", err) + } + + return buf, w.FormDataContentType(), nil +} + +// send POST request to uriStr. +func send(uriStr, cType string, buf *bytes.Buffer) (err error) { + var client http.Client + + req, err := http.NewRequest(http.MethodPost, uriStr, buf) + if err != nil { + return fmt.Errorf("bad request: %w", err) + } + + req.Header.Set("Content-Type", cType) + + resp, err := client.Do(req) + if err != nil { + return fmt.Errorf("client post form: %w", err) } defer func() {