Skip to content

Commit

Permalink
Merge pull request #1 from quanxiang-cloud/dev_go
Browse files Browse the repository at this point in the history
go polysdk v1.0.0
  • Loading branch information
vipally authored Apr 22, 2022
2 parents f9913fa + b808314 commit da839c9
Show file tree
Hide file tree
Showing 32 changed files with 2,287 additions and 0 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,8 @@

# Dependency directories (remove the comment below to include it)
# vendor/

*.json
*.jsn
*.yaml
*.yml
60 changes: 60 additions & 0 deletions go/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# polysdk-go

polysdk-go is a client for polyapi.

## usage

1. set environment value of ENV_POLY_CONFIG_PATH
It will share config file from tool and client.
```
set ENV_POLY_CONFIG_PATH=c:\data\poly_cfg.json
```
2. initial an empty config file
```
polykit i
```
3. edit the config file with AccessKeyID and Secret
```
{
"remoteUrl": "http://polyapi.qxp.com",
"key": {
"accessKeyId": "<key-id>",
"secretKey": "<secret-key>"
},
"createAt": "2021-11-12T06:35:03CST",
"description": ""
}
```
4. encrypt the config file
```
polykit c
```
5. verify the encrpted config file
```
polykit v
```
6. create a poly client from the config file
```Go
c, err := polysdk.NewPolyClient("")
if err != nil {
panic(err)
}
c.SyncServerClock() // adjust local clock with server

h := polysdk.Header{}
h.Set("Content-Type", "application/json")
body := map[string]interface{}{
"zone": "pek3d",
}
polysdk.PrettyShow(body)

uri := "/api/v1/polyapi/raw/request/system/app/jhdsk/customer/ns2/viewVM.r"
r, err := c.DoRequestAPI(uri, polysdk.MethodPost, h, body)
if err != nil {
panic(err)
}

polysdk.PrettyShow(r)
```

~~ enjoy this sdk!
249 changes: 249 additions & 0 deletions go/api_base.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,249 @@
package polysdk

import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"polysdk/internal/hash"
"polysdk/internal/polysign"
"polysdk/internal/signature"
)

// header define
const (
HeaderContentType = "Content-Type"
HeaderXRequestID = "X-Request_id"
)

// Header exports
type Header = http.Header

// http method exports
const (
MethodGet = http.MethodGet
MethodHead = http.MethodHead
MethodPost = http.MethodPost
MethodPut = http.MethodPut
MethodPatch = http.MethodPatch
MethodDelete = http.MethodDelete
MethodConnect = http.MethodConnect
MethodOptions = http.MethodOptions
MethodTrace = http.MethodTrace
)

// Content-Type MIME of the most common data formats.
const (
MIMEJSON = "application/json"
MIMEHTML = "text/html"
MIMEXML = "application/xml"
MIMEXML2 = "text/xml"
MIMEPlain = "text/plain"
MIMEPOSTForm = "application/x-www-form-urlencoded"
MIMEMultipartPOSTForm = "multipart/form-data"
MIMEPROTOBUF = "application/x-protobuf"
MIMEMSGPACK = "application/x-msgpack"
MIMEMSGPACK2 = "application/msgpack"
MIMEYAML = "application/x-yaml"
)

// BodyBase is base struct of body
type BodyBase struct {
// NOTE: path and other hide parameter
PolyHide map[string]interface{} `json:"$polyapi_hide$,omitempty"`

// NOTE: none-object customer body root
CustomerBody interface{} `json:"$body$,omitempty"`
}

// HTTPResponse is the response of http request
type HTTPResponse struct {
StatusCode int
Status string
Body json.RawMessage
Header Header
}

// DoRequestAPI is the custom api for access apis from polyapi
func (c *PolyClient) DoRequestAPI(apiPath string, method string, header Header, body interface{}) (*HTTPResponse, error) {
bodyBytes, err := c.GenHeaderSignature(header, body)
if err != nil {
return nil, err
}

resp, err := c.HTTPRequest(c.remoteURL+apiPath, method, header, bodyBytes)
if err != nil {
return nil, err
}

var respBody []byte
if resp.StatusCode == http.StatusOK {
b, err := io.ReadAll(resp.Body)
if err != nil {
return nil, err
}
resp.Body.Close()
respBody = b
} else {
respBody = []byte(fmt.Sprintf(`%q`, resp.Status))
}

r := &HTTPResponse{
Body: respBody,
Header: resp.Header,
Status: resp.Status,
StatusCode: resp.StatusCode,
}
return r, nil
}

// CustomBody is custom body
type CustomBody map[string]interface{}

// Add insert a new field to custom body
func (b CustomBody) Add(name string, data interface{}) bool {
return b.add(name, data, false)
}

// Set insert a new field to custom body forced
func (b CustomBody) Set(name string, data interface{}) bool {
return b.add(name, data, true)
}

func (b CustomBody) add(name string, data interface{}, force bool) bool {
if _, ok := b[name]; ok && !force {
return false
}
b[name] = data
return true
}

// NewCustomBody generate a custom body with signature
func (c *PolyClient) NewCustomBody() CustomBody {
return CustomBody{}
}

// MakeBodyBase create a BodyBase with signature
func (c *PolyClient) MakeBodyBase() BodyBase {
return BodyBase{}
}

// HTTPRequest do a custom http request
func (c *PolyClient) HTTPRequest(reqURL, method string, header Header, data []byte) (*http.Response, error) {
if err := validateHTTPMethod(method); err != nil {
return nil, err
}

uri, err := url.Parse(reqURL)
if err != nil {
return nil, err
}

var reader io.Reader
if method == MethodGet { // data to query if method is 'GET'
uri.RawQuery, err = signature.ToQuery(data)
if err != nil {
return nil, err
}
} else {
reader = bytes.NewReader(data)
}

req, err := http.NewRequest(method, uri.String(), reader)
if err != nil {
return nil, err
}
req.Header = header

resp, err := c.httpClient.Do(req)
if err != nil {
return nil, err
}
return resp, nil
}

//------------------------------------------------------------------------------

// GenHeaderSignature create header.Signature form body and return body bytes
func (c *PolyClient) GenHeaderSignature(header Header, body interface{}) ([]byte, error) {
var b []byte
var err error
switch d := body.(type) {
case json.RawMessage:
b = []byte(d)
case []byte:
b = d
default:
if b, err = json.Marshal(body); err != nil {
return nil, err
}
}

header.Set(HeaderXRequestID, hash.ShortID(0))

var signInfo CustomBody
if err := json.Unmarshal(b, &signInfo); err != nil {
return nil, err
}

var (
signVals = [][2]string{
{polysign.XHeaderPolySignKeyID, c.accessKeyID},
{polysign.XHeaderPolySignMethod, polysign.XHeaderPolySignMethodVal},
{polysign.XHeaderPolySignVersion, polysign.XHeaderPolySignVersionVal},
{polysign.XHeaderPolySignTimestamp, c.polyTimestamp()},
}
opSignVals = func(fn func(string, string) error) error {
for _, v := range signVals {
if err := fn(v[0], v[1]); err != nil {
return err
}
}
return nil
}
opAddBodyHeader = func(name, val string) error {
if name != polysign.XBodyPolySignSignature {
header.Set(name, val)
}
if !signInfo.Add(name, val) {
err = fmt.Errorf("duplicate field %s in body", name)
return err
}
return nil
}
opDelBody = func(name, val string) error {
delete(signInfo, name)
return nil
}
)

if err := opSignVals(opAddBodyHeader); err != nil {
return nil, err
}

signature, err := c.sign.Signature(signInfo)
if err != nil {
return nil, err
}
if err := opAddBodyHeader(polysign.XBodyPolySignSignature, signature); err != nil {
return nil, err
}

if err := opSignVals(opDelBody); err != nil {
return nil, err
}

return json.Marshal(signInfo)
}

func validateHTTPMethod(method string) error {
switch method {
case MethodGet, MethodHead, MethodPost,
MethodPut, MethodPatch, MethodDelete,
MethodConnect, MethodOptions, MethodTrace:
return nil
}
return fmt.Errorf("unsupport http method '%s'", method)
}
27 changes: 27 additions & 0 deletions go/api_poly.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package polysdk

import (
"polysdk/internal/apipath"
)

// PolyAPIRequest request poly api from polyapi
// fullNamespace is the full namespace of poly api. eg: /system/poly/sample_poly_api
func (c *PolyClient) PolyAPIRequest(fullNamespace string, method string, header Header, data interface{}) (*HTTPResponse, error) {
uri := apipath.Join(apipath.APIPolyRequest, fullNamespace)
return c.DoRequestAPI(uri, method, header, data)
}

// PolyAPIDoc request poly api from polyapi
// fullNamespace is the full namespace of poly api. eg: /system/poly/sample_poly_api
func (c *PolyClient) PolyAPIDoc(fullNamespace string, docType string, titleFirst bool) (*HTTPResponse, error) {
d := apiDocReq{
BodyBase: BodyBase{},
DocType: docType,
TitleFirst: titleFirst,
}
header := Header{
HeaderContentType: []string{MIMEJSON},
}
uri := apipath.Join(apipath.APIPolyDoc, fullNamespace)
return c.DoRequestAPI(uri, MethodPost, header, d)
}
44 changes: 44 additions & 0 deletions go/api_raw.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package polysdk

import (
"polysdk/internal/apipath"
)

// doc type
const (
DocRaw = "raw"
DocSwag = "swag"
DocCurl = "curl"
DocJavascript = "javascript"
DocPython = "python"
)

// RawAPIRequest request raw api from polyapi
// apiPath is the full namespace of raw api. eg: /system/raw/sample_raw_api
func (c *PolyClient) RawAPIRequest(fullNamespace string, method string, header Header, data interface{}) (*HTTPResponse, error) {
uri := apipath.Join(apipath.APIRequest, fullNamespace)
return c.DoRequestAPI(uri, method, header, data)
}

type apiDocReq struct {
BodyBase
DocType string `json:"docType"`
TitleFirst bool `json:"titleFirst"`
}

// RawAPIDoc request raw api from polyapi
// fullNamespace is the full namespace of raw api. eg: /system/raw/sample_raw_api
func (c *PolyClient) RawAPIDoc(fullNamespace string, docType string, titleFirst bool) (*HTTPResponse, error) {
d := apiDocReq{
BodyBase: BodyBase{
//Signature: c.bodySign.genBodySignature(),
},
DocType: docType,
TitleFirst: titleFirst,
}
header := Header{
HeaderContentType: []string{MIMEJSON},
}
uri := apipath.Join(apipath.APIDoc, fullNamespace)
return c.DoRequestAPI(uri, MethodPost, header, d)
}
Loading

0 comments on commit da839c9

Please sign in to comment.