JCS-pub/client/sdk/api/v1/utils.go

184 lines
4.1 KiB
Go

package api
import (
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"mime/multipart"
"net/http"
"net/url"
ul "net/url"
"strings"
"github.com/google/go-querystring/query"
"gitlink.org.cn/cloudream/common/pkgs/iterator"
"gitlink.org.cn/cloudream/common/sdks"
"gitlink.org.cn/cloudream/common/utils/http2"
"gitlink.org.cn/cloudream/common/utils/math2"
"gitlink.org.cn/cloudream/common/utils/serder"
"gitlink.org.cn/cloudream/jcs-pub/client/sdk/api"
)
func ParseJSONResponse[TBody any](resp *http.Response) (TBody, error) {
var ret TBody
contType := resp.Header.Get("Content-Type")
if strings.Contains(contType, http2.ContentTypeJSON) {
var err error
if ret, err = serder.JSONToObjectStreamEx[TBody](resp.Body); err != nil {
return ret, fmt.Errorf("parsing response: %w", err)
}
return ret, nil
}
cont, err := io.ReadAll(resp.Body)
if err != nil {
return ret, fmt.Errorf("unknow response content type: %s, status: %d", contType, resp.StatusCode)
}
strCont := string(cont)
return ret, fmt.Errorf("unknow response content type: %s, status: %d, body(prefix): %s", contType, resp.StatusCode, strCont[:math2.Min(len(strCont), 200)])
}
func JSONAPI[Resp sdks.APIResponse, Req sdks.APIRequest](cfg *api.Config, cli *http.Client, req Req, resp Resp) (Resp, error) {
param := req.MakeParam()
v1EndPoint, err := url.JoinPath(cfg.EndPoint, "v1")
if err != nil {
return resp, err
}
httpReq, err := param.MakeRequest(v1EndPoint)
if err != nil {
return resp, err
}
httpResp, err := cli.Do(httpReq)
if err != nil {
return resp, err
}
err = resp.ParseResponse(httpResp)
return resp, err
}
func JSONAPINoData[Req sdks.APIRequest](cfg *api.Config, cli *http.Client, req Req) error {
param := req.MakeParam()
v1EndPoint, err := url.JoinPath(cfg.EndPoint, "v1")
if err != nil {
return err
}
httpReq, err := param.MakeRequest(v1EndPoint)
if err != nil {
return err
}
resp, err := cli.Do(httpReq)
if err != nil {
return err
}
return sdks.ParseCodeDataJSONResponse(resp, any(nil))
}
func calcSha256(body sdks.RequestBody) string {
hasher := sha256.New()
switch body := body.(type) {
case *sdks.StringBody:
hasher.Write([]byte(body.Value))
return hex.EncodeToString(hasher.Sum(nil))
case *sdks.BytesBody:
hasher.Write(body.Value)
return hex.EncodeToString(hasher.Sum(nil))
case *sdks.StreamBody:
return ""
default:
hash := sha256.Sum256([]byte(""))
return hex.EncodeToString(hash[:])
}
}
func PostMultiPart(cfg *api.Config, url string, info any, files http2.MultiPartFileIterator) (*http.Response, error) {
req, err := http.NewRequest(http.MethodPost, url, nil)
if err != nil {
return nil, err
}
pr, pw := io.Pipe()
muWriter := multipart.NewWriter(pw)
req.Header.Set("Content-Type", fmt.Sprintf("%s;boundary=%s", http2.ContentTypeMultiPart, muWriter.Boundary()))
writeResult := make(chan error, 1)
go func() {
writeResult <- func() error {
defer pw.Close()
defer muWriter.Close()
if info != nil {
mp, err := query.Values(info)
if err != nil {
return fmt.Errorf("formValues object to map failed, err: %w", err)
}
for k, v := range mp {
err := muWriter.WriteField(k, v[0])
if err != nil {
return fmt.Errorf("write form field failed, err: %w", err)
}
}
}
for {
file, err := files.MoveNext()
if err == iterator.ErrNoMoreItem {
break
}
if err != nil {
return fmt.Errorf("opening file: %w", err)
}
err = sendFileOnePart(muWriter, file.FieldName, file.FileName, file.File)
file.File.Close()
if err != nil {
return err
}
}
return nil
}()
}()
req.Body = pr
cli := http.Client{}
resp, err := cli.Do(req)
if err != nil {
return nil, err
}
writeErr := <-writeResult
if writeErr != nil {
return nil, writeErr
}
return resp, nil
}
func sendFileOnePart(muWriter *multipart.Writer, fieldName, fileName string, file io.ReadCloser) error {
w, err := muWriter.CreateFormFile(fieldName, ul.PathEscape(fileName))
if err != nil {
return fmt.Errorf("create form file failed, err: %w", err)
}
_, err = io.Copy(w, file)
return err
}