Codebase list golang-github-nlopes-slack / 2d81bca misc.go
2d81bca

Tree @2d81bca (Download .tar.gz)

misc.go @2d81bca

1b96f65
 
 
 
 
6854e26
ece2352
6854e26
1b96f65
6854e26
1b96f65
eacfa3b
1b96f65
6854e26
 
2d81bca
ece2352
1b96f65
 
2d81bca
 
 
 
 
 
 
 
 
 
 
 
1daafac
 
065c48f
 
 
 
 
 
 
 
 
 
 
4983bc2
6854e26
1b96f65
6854e26
1b96f65
6854e26
 
 
 
 
 
 
 
1b96f65
4983bc2
6854e26
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
334c7e2
 
 
 
 
16288f9
1b96f65
20e9aa8
1b96f65
334c7e2
 
 
1b96f65
 
6854e26
334c7e2
6854e26
 
4983bc2
 
f78e7e5
 
 
2d81bca
6854e26
 
 
 
eacfa3b
 
 
 
 
 
 
6854e26
 
 
ece2352
2d81bca
 
 
 
 
 
 
 
6854e26
 
 
 
5eef13c
70580e4
 
 
 
 
 
6854e26
1b96f65
ece2352
334c7e2
ece2352
 
 
 
 
 
 
eacfa3b
 
 
 
 
 
 
 
70580e4
eacfa3b
 
 
 
2d81bca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
package slack

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"mime/multipart"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"path/filepath"
	"strings"
	"time"
)

// HTTPRequester defines the minimal interface needed for an http.Client to be implemented.
//
// Use it in conjunction with the SetHTTPClient function to allow for other capabilities
// like a tracing http.Client
type HTTPRequester interface {
	Do(*http.Request) (*http.Response, error)
}

var customHTTPClient HTTPRequester

// HTTPClient sets a custom http.Client
// deprecated: in favor of SetHTTPClient()
var HTTPClient = &http.Client{}

type WebResponse struct {
	Ok    bool      `json:"ok"`
	Error *WebError `json:"error"`
}

type WebError string

func (s WebError) Error() string {
	return string(s)
}

func fileUploadReq(path, fpath, fieldname string, values url.Values) (*http.Request, error) {
	fullpath, err := filepath.Abs(fpath)
	if err != nil {
		return nil, err
	}
	file, err := os.Open(fullpath)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	body := &bytes.Buffer{}
	wr := multipart.NewWriter(body)

	ioWriter, err := wr.CreateFormFile(fieldname, filepath.Base(fullpath))
	if err != nil {
		wr.Close()
		return nil, err
	}
	bytes, err := io.Copy(ioWriter, file)
	if err != nil {
		wr.Close()
		return nil, err
	}
	// Close the multipart writer or the footer won't be written
	wr.Close()
	stat, err := file.Stat()
	if err != nil {
		return nil, err
	}
	if bytes != stat.Size() {
		return nil, errors.New("could not read the whole file")
	}
	req, err := http.NewRequest("POST", path, body)
	if err != nil {
		return nil, err
	}
	req.Header.Add("Content-Type", wr.FormDataContentType())
	req.URL.RawQuery = (values).Encode()
	return req, nil
}

func parseResponseBody(body io.ReadCloser, intf *interface{}, debug bool) error {
	response, err := ioutil.ReadAll(body)
	if err != nil {
		return err
	}

	// FIXME: will be api.Debugf
	if debug {
		logger.Printf("parseResponseBody: %s\n", string(response))
	}

	err = json.Unmarshal(response, &intf)
	if err != nil {
		return err
	}

	return nil
}

func postWithMultipartResponse(path, filepath, fieldname string, values url.Values, intf interface{}, debug bool) error {
	req, err := fileUploadReq(SLACK_API+path, filepath, fieldname, values)
	if err != nil {
		return err
	}
	resp, err := getHTTPClient().Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	// Slack seems to send an HTML body along with 5xx error codes. Don't parse it.
	if resp.StatusCode != 200 {
		logResponse(resp, debug)
		return fmt.Errorf("Slack server error: %s.", resp.Status)
	}

	return parseResponseBody(resp.Body, &intf, debug)
}

func postForm(endpoint string, values url.Values, intf interface{}, debug bool) error {
	reqBody := strings.NewReader(values.Encode())
	req, err := http.NewRequest("POST", endpoint, reqBody)
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

	resp, err := getHTTPClient().Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	// Slack seems to send an HTML body along with 5xx error codes. Don't parse it.
	if resp.StatusCode != 200 {
		logResponse(resp, debug)
		return fmt.Errorf("Slack server error: %s.", resp.Status)
	}

	return parseResponseBody(resp.Body, &intf, debug)
}

func post(path string, values url.Values, intf interface{}, debug bool) error {
	return postForm(SLACK_API+path, values, intf, debug)
}

func parseAdminResponse(method string, teamName string, values url.Values, intf interface{}, debug bool) error {
	endpoint := fmt.Sprintf(SLACK_WEB_API_FORMAT, teamName, method, time.Now().Unix())
	return postForm(endpoint, values, intf, debug)
}

func logResponse(resp *http.Response, debug bool) error {
	if debug {
		text, err := httputil.DumpResponse(resp, true)
		if err != nil {
			return err
		}

		logger.Print(string(text))
	}

	return nil
}

func getHTTPClient() HTTPRequester {
	if customHTTPClient != nil {
		return customHTTPClient
	}

	return HTTPClient
}

// SetHTTPClient allows you to specify a custom http.Client
// Use this instead of the package level HTTPClient variable if you want to use a custom client like the
// Stackdriver Trace HTTPClient https://godoc.org/cloud.google.com/go/trace#HTTPClient
func SetHTTPClient(client HTTPRequester) {
	customHTTPClient = client
}