1
0
Fork 0
mirror of https://github.com/Luzifer/worktime.git synced 2024-10-18 16:14:21 +00:00
worktime/vendor/github.com/lancecarlson/couchgo/couch.go
2016-09-24 15:00:03 +02:00

405 lines
8.3 KiB
Go

package couch
import (
"net/http"
"net/url"
"io"
"encoding/json"
"io/ioutil"
"bytes"
"fmt"
)
type Response struct {
Ok bool
ID string
Rev string
Error string
Reason string
}
type Client struct {
URL *url.URL
}
func NewClient(u *url.URL) *Client {
return &Client{u}
}
func NewClientURL(urlString string) (*Client, error) {
u, err := url.Parse(urlString)
if err != nil {
return nil, err
}
return &Client{u}, nil
}
func (c *Client) AllDBs() ([]string, error) {
res := []string{}
_, err := c.execJSON("GET", "/_all_dbs", &res, nil, nil, nil)
if err != nil {
return nil, err
}
return res, nil
}
// TODO
//func (c *Client) AllDesignDocs() {
//}
func (c *Client) CreateDB() (resp *Response, code int, err error) {
req, err := c.NewRequest("PUT", c.UrlString(c.DBPath(), nil), nil, nil)
if err != nil {
return
}
httpResp, err := http.DefaultClient.Do(req)
if err != nil {
return
}
code, err = c.HandleResponse(httpResp, &resp)
if err != nil {
return
}
return
}
func (c *Client) DeleteDB() (resp *Response, code int, err error) {
req, err := c.NewRequest("DELETE", c.UrlString(c.DBPath(), nil), nil, nil)
if err != nil {
return
}
httpResp, err := http.DefaultClient.Do(req)
if err != nil {
return
}
code, err = c.HandleResponse(httpResp, &resp)
if err != nil {
return
}
return
}
func (c *Client) Save(doc interface{}) (res *Response, err error) {
id, _, err := ParseIdRev(doc)
if err != nil {
return
}
// If no id is provided, we assume POST
if id == "" {
_, err = c.execJSON("POST", c.URL.Path, &res, doc, nil, nil)
} else {
_, err = c.execJSON("PUT", c.DocPath(id), &res, doc, nil, nil)
}
if err != nil {
return
}
if res.Error != "" {
return res, fmt.Errorf(fmt.Sprintf("%s: %s", res.Error, res.Reason))
}
return
}
func (c *Client) Get(id string, doc interface{}) error {
_, err := c.execJSON("GET", c.DocPath(id), &doc, nil, nil, nil)
if err != nil {
return err
}
return nil
}
func (c *Client) Delete(id string, rev string) error {
headers := http.Header{}
headers.Add("If-Match", rev)
res := Response{}
_, err := c.execJSON("DELETE", c.DocPath(id), &res, nil, nil, &headers)
if err != nil {
return err
}
return nil
}
type BulkSaveRequest struct {
Docs []interface{} `json:"docs"`
}
func (c *Client) BulkSave(docs ...interface{}) (resp *[]Response, code int, err error) {
bulkSaveRequest := &BulkSaveRequest{Docs: docs}
reader, err := docReader(bulkSaveRequest)
req, err := c.NewRequest("POST", c.UrlString(c.DBPath() + "/_bulk_docs", nil), reader, nil)
if err != nil {
return
}
httpResp, err := http.DefaultClient.Do(req)
if err != nil {
return
}
code, err = c.HandleResponse(httpResp, &resp)
if err != nil {
return
}
return
}
type MultiDocResponse struct {
TotalRows uint64 `json:"total_rows"`
Offset uint64
Rows []Row
}
type Row struct {
ID *string
Key interface{}
Value interface{}
Doc interface{}
}
type KeysRequest struct {
Keys []string `json:"keys"`
}
func (c *Client) View(design string, name string, options *url.Values, keys *[]string) (multiDocResponse *MultiDocResponse, err error) {
url := c.UrlString(c.DBPath() + "/_design/" + design + "/_view/" + name, options)
method := ""
body := new(bytes.Buffer)
if keys != nil {
reqJson, _ := json.Marshal(KeysRequest{Keys: *keys})
body = bytes.NewBuffer(reqJson)
method = "POST"
} else {
method = "GET"
}
req, err := c.NewRequest(method, url, body, nil)
if err != nil {
return
}
httpResp, err := http.DefaultClient.Do(req)
if err != nil {
return
}
respBody, err := ioutil.ReadAll(httpResp.Body)
if err != nil {
return
}
if err = json.Unmarshal(respBody, &multiDocResponse); err != nil {
return
}
return
}
func (c *Client) Copy(src string, dest string, destRev *string) (resp *Response, code int, err error) {
if destRev != nil {
dest += "?rev=" + *destRev
}
req, err := c.NewRequest("COPY", c.UrlString(c.DocPath(src), nil), nil, nil)
req.Header.Add("Destination", dest)
if err != nil {
return
}
httpResp, err := http.DefaultClient.Do(req)
if err != nil {
return
}
code, err = c.HandleResponse(httpResp, &resp)
if err != nil {
return
}
return
}
type ReplicateRequest struct {
Source string `json:"source"`
Target string `json:"target"`
Cancel bool `json:"cancel,omitempty"`
Continuous bool `json:"continuous,omitempty"`
CreateTarget bool `json:"create_target,omitempty"`
DocIDs []string `json:"doc_ids,omitempty"`
Filter string `json:"filter,omitempty"`
Proxy string `json:"proxy,omitempty"`
QueryParams map[string]string `json:"query_params,omitempty"`
}
type ReplicateResponse struct {
Ok bool `json:"ok"`
LocalID bool `json:"_local_id"`
}
func (c *Client) Replicate(repReq *ReplicateRequest) (resp *ReplicateResponse, code int, err error) {
reqReader, err := docReader(repReq)
if err != nil {
return
}
req, err := c.NewRequest("POST", c.UrlString("/_replicate", nil), reqReader, nil)
if err != nil {
return
}
httpResp, err := http.DefaultClient.Do(req)
if err != nil {
return
}
code, err = c.HandleResponse(httpResp, &resp)
if err != nil {
return
}
return
}
func (c *Client) DBPath() string {
return c.URL.Path
}
func (c *Client) DocPath(id string) string {
return c.DBPath() + "/" + id
}
func (c *Client) NewRequest(method, url string, body io.Reader, headers *http.Header) (req *http.Request, err error) {
req, err = http.NewRequest(method, url, body)
if headers != nil {
req.Header = *headers
}
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Accept", "application/json")
return
}
func (c *Client) UrlString(path string, values *url.Values) string {
u := *c.URL
u.Path = path
if values != nil {
u.RawQuery = values.Encode()
}
return u.String()
}
func (c *Client) HandleResponse(resp *http.Response, result interface{}) (code int, err error) {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
return
}
code = resp.StatusCode
if err = c.HandleResponseError(code, body); err != nil {
return code, err
}
if err = json.Unmarshal(body, result); err != nil {
return 0, err
}
return
}
func (c *Client) HandleResponseError(code int, resBytes []byte) error {
if code < 200 || code >= 300 {
res := Response{}
if err := json.Unmarshal(resBytes, &res); err != nil {
return err
}
return fmt.Errorf(fmt.Sprintf("Code: %d, Error: %s, Reason: %s", code, res.Error, res.Reason))
}
return nil
}
func (c *Client) execJSON(method string, path string, result interface{}, doc interface{}, values *url.Values, headers *http.Header) (int, error) {
resBytes, code, err := c.execRead(method, path, doc, values, headers)
if err != nil {
return 0, err
}
if err = c.HandleResponseError(code, resBytes); err != nil {
return code, err
}
if err = json.Unmarshal(resBytes, result); err != nil {
return 0, err
}
return code, nil
}
func (c *Client) execRead(method string, path string, doc interface{}, values *url.Values, headers *http.Header) ([]byte, int, error) {
r, code, err := c.exec(method, path, doc, values, headers)
if err != nil {
return nil, 0, err
}
resBytes, err := ioutil.ReadAll(r)
if err != nil {
return nil, 0, err
}
return resBytes, code, nil
}
func (c *Client) exec(method string, path string, doc interface{}, values *url.Values, headers *http.Header) (io.Reader, int, error) {
reqReader, err := docReader(doc)
if err != nil {
return nil, 0, err
}
req, err := c.NewRequest(method, c.UrlString(path, values), reqReader, headers)
if err != nil {
return nil, 0, err
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, 0, err
}
return resp.Body, resp.StatusCode, nil
}
func docReader(doc interface{}) (io.Reader, error) {
if doc == nil {
return nil, nil
}
docJson, err := json.Marshal(doc)
if err != nil {
return nil, err
}
r := bytes.NewBuffer(docJson)
return r, nil
}
type IdRev struct {
ID string `json:"_id"`
Rev string `json:"_rev"`
}
func Remarshal(doc interface{}, newDoc interface{}) (err error) {
docJson, err := json.Marshal(doc)
if err != nil {
return
}
err = json.Unmarshal(docJson, newDoc)
if err != nil {
return
}
return
}
func ParseIdRev(doc interface{}) (string, string, error) {
docJson, err := json.Marshal(doc)
if err != nil {
return "", "", err
}
idRev := &IdRev{}
if err = json.Unmarshal(docJson, idRev); err != nil {
return "", "", err
}
return idRev.ID, idRev.Rev, nil
}