Initial checkin

This commit is contained in:
Thilo Karraß 2020-10-28 11:29:12 +01:00
commit ef921ff728
7 changed files with 252 additions and 0 deletions

8
.idea/.gitignore vendored Normal file
View 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
View 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
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" addBOMForNewFiles="with NO BOM" />
</project>

View 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
View 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
View 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
View 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)),
}
}