-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from quanxiang-cloud/dev_go
go polysdk v1.0.0
- Loading branch information
Showing
32 changed files
with
2,287 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,3 +13,8 @@ | |
|
||
# Dependency directories (remove the comment below to include it) | ||
# vendor/ | ||
|
||
*.json | ||
*.jsn | ||
*.yaml | ||
*.yml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
Oops, something went wrong.