Initial checkin
This commit is contained in:
commit
ef921ff728
8
.idea/.gitignore
vendored
Normal file
8
.idea/.gitignore
vendored
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
|
# Datasource local storage ignored files
|
||||||
|
/dataSources/
|
||||||
|
/dataSources.local.xml
|
||||||
|
# Editor-based HTTP Client requests
|
||||||
|
/httpRequests/
|
8
.idea/cure.iml
Normal file
8
.idea/cure.iml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="WEB_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager">
|
||||||
|
<content url="file://$MODULE_DIR$" />
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
4
.idea/encodings.xml
Normal file
4
.idea/encodings.xml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding" addBOMForNewFiles="with NO BOM" />
|
||||||
|
</project>
|
10
.idea/inspectionProfiles/Project_Default.xml
Normal file
10
.idea/inspectionProfiles/Project_Default.xml
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
||||||
|
<option name="processCode" value="true" />
|
||||||
|
<option name="processLiterals" value="true" />
|
||||||
|
<option name="processComments" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
6
.idea/misc.xml
Normal file
6
.idea/misc.xml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="JavaScriptSettings">
|
||||||
|
<option name="languageLevel" value="ES6" />
|
||||||
|
</component>
|
||||||
|
</project>
|
8
.idea/modules.xml
Normal file
8
.idea/modules.xml
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/cure.iml" filepath="$PROJECT_DIR$/.idea/cure.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
208
cure.go
Normal file
208
cure.go
Normal file
|
@ -0,0 +1,208 @@
|
||||||
|
// Common Uniform Rest Endpoint
|
||||||
|
//
|
||||||
|
// This is a library to access REST endpoints in a simplified way.
|
||||||
|
package cure // import "udico.de/cure"
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"github.com/golang/protobuf/jsonpb"
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Endpoint struct {
|
||||||
|
// The base url of the REST API
|
||||||
|
Base string
|
||||||
|
|
||||||
|
// use this string as user agent
|
||||||
|
UserAgent string
|
||||||
|
|
||||||
|
// Additional HTTP header attributes to set in every call.
|
||||||
|
// You can (and should) also set per Call attributes within the Call object
|
||||||
|
Head map[string]string
|
||||||
|
|
||||||
|
client *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(baseurl string) *Endpoint {
|
||||||
|
return &Endpoint{
|
||||||
|
Base: baseurl,
|
||||||
|
UserAgent: "Common Uniform Rest Endpoint, 1.0.0",
|
||||||
|
Head: make(map[string]string),
|
||||||
|
client: &http.Client{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type call struct {
|
||||||
|
url string
|
||||||
|
method string
|
||||||
|
api *Endpoint
|
||||||
|
|
||||||
|
// HTTP header attributes to set. These will override the ones set within the endpoint.
|
||||||
|
Head map[string]string
|
||||||
|
|
||||||
|
// The parameters of your call. Depending on the method, they'll be appended to the query or posted url-form-encoded
|
||||||
|
Parameters map[string]string
|
||||||
|
|
||||||
|
// If set, this data will be used as POST data (
|
||||||
|
Body *bytes.Reader
|
||||||
|
}
|
||||||
|
|
||||||
|
type callret struct {
|
||||||
|
Code int
|
||||||
|
Message string
|
||||||
|
err error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r callret) Ok() bool {
|
||||||
|
if r.err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// We don't expect 1xx codes
|
||||||
|
if (r.Code / 100) > 2 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simplify error handling at client side
|
||||||
|
func (r callret) Error() error {
|
||||||
|
if r.err != nil {
|
||||||
|
return r.err
|
||||||
|
}
|
||||||
|
if (r.Code / 100) > 2 {
|
||||||
|
return errors.New(r.Message)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Endpoint) createCall(method, endpoint string) *call {
|
||||||
|
ret := &call{
|
||||||
|
url: a.Base + endpoint, // todo: ensure we have a valid url
|
||||||
|
method: method,
|
||||||
|
api: a,
|
||||||
|
Head: make(map[string]string),
|
||||||
|
Parameters: make(map[string]string),
|
||||||
|
}
|
||||||
|
ret.Head["user-agent"] = a.UserAgent
|
||||||
|
for k, v := range a.Head {
|
||||||
|
ret.Head[k] = v
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Endpoint) Get(endpoint string) *call {
|
||||||
|
return a.createCall("GET", endpoint)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Endpoint) PostForm(endpoint string) *call {
|
||||||
|
ret := a.createCall("POST", endpoint)
|
||||||
|
ret.Head["content-type"] = "application/x-www-form-urlencoded"
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Endpoint) PostBody(endpoint string) *call {
|
||||||
|
ret := a.createCall("POST", endpoint)
|
||||||
|
ret.Head["content-type"] = "application/json"
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *call) Param(param interface{}) *call {
|
||||||
|
marshaller := new(jsonpb.Marshaler)
|
||||||
|
marshaller.EmitDefaults = false
|
||||||
|
marshaller.EnumsAsInts = false
|
||||||
|
marshaller.OrigName = true
|
||||||
|
buf := &bytes.Buffer{}
|
||||||
|
_ = marshaller.Marshal(buf, param.(proto.Message))
|
||||||
|
logrus.Debug(string(buf.Bytes()[:]))
|
||||||
|
logrus.Debug(string(buf.Bytes()[:]))
|
||||||
|
c.Body = bytes.NewReader(buf.Bytes())
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *call) Fire(ctx context.Context, result interface{}) callret {
|
||||||
|
// disable HTTP2
|
||||||
|
//client := &http.Client{Transport: &http.Transport{TLSNextProto: make(map[string]func(string, *tls.Conn) http.RoundTripper)}}
|
||||||
|
//client := &http.Client{}
|
||||||
|
|
||||||
|
// handle POST parameters
|
||||||
|
var request *http.Request
|
||||||
|
var err error
|
||||||
|
if c.method == "POST" {
|
||||||
|
if c.Body != nil {
|
||||||
|
request, err = http.NewRequest(c.method, c.url, c.Body)
|
||||||
|
//request.Header.Set("content-length", fmt.Sprintf("%v", c.Body.Len()))
|
||||||
|
} else {
|
||||||
|
fdata := url.Values{}
|
||||||
|
for k, v := range c.Parameters {
|
||||||
|
fdata.Add(k, v)
|
||||||
|
}
|
||||||
|
logrus.Debug(fdata.Encode())
|
||||||
|
sRead := strings.NewReader(fdata.Encode())
|
||||||
|
request, err = http.NewRequest(c.method, c.url, sRead)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
request, err = http.NewRequest(c.method, c.url, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
for k, v := range c.Head {
|
||||||
|
request.Header.Set(k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
request = request.WithContext(ctx)
|
||||||
|
|
||||||
|
// handle GET parameters
|
||||||
|
if c.method == "GET" {
|
||||||
|
gdata := request.URL.Query()
|
||||||
|
for k, v := range c.Parameters {
|
||||||
|
gdata.Add(k, v)
|
||||||
|
}
|
||||||
|
request.URL.RawQuery = gdata.Encode()
|
||||||
|
}
|
||||||
|
|
||||||
|
logrus.Debugf("%v %v", c.method, c.url)
|
||||||
|
for k, v := range c.Head {
|
||||||
|
logrus.Debugf(" [%v: %v]", k, v)
|
||||||
|
}
|
||||||
|
for k, v := range c.Parameters {
|
||||||
|
logrus.Debugf(" + %v = '%v'", k, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
hbuf := &bytes.Buffer{}
|
||||||
|
request.Header.Write(hbuf)
|
||||||
|
|
||||||
|
// go!
|
||||||
|
response, err := c.api.client.Do(request)
|
||||||
|
if err != nil {
|
||||||
|
return callret{0, "", err}
|
||||||
|
}
|
||||||
|
logrus.Debugf("-> %v", response.Status)
|
||||||
|
|
||||||
|
if response == nil {
|
||||||
|
return callret{0, "", errors.New("empty response")}
|
||||||
|
}
|
||||||
|
|
||||||
|
// no result requested
|
||||||
|
if result == nil {
|
||||||
|
return callret{0, "", nil}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: dump the content
|
||||||
|
//buf := &bytes.Buffer{}
|
||||||
|
buf, err := ioutil.ReadAll(response.Body)
|
||||||
|
bbuf := bytes.NewReader(buf)
|
||||||
|
logrus.Debug(string(buf[:]))
|
||||||
|
|
||||||
|
unmarshaller := new(jsonpb.Unmarshaler)
|
||||||
|
unmarshaller.AllowUnknownFields = true
|
||||||
|
return callret{
|
||||||
|
response.StatusCode,
|
||||||
|
response.Status,
|
||||||
|
unmarshaller.Unmarshal(bbuf, result.(proto.Message)),
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user