mattermost-community-enterp.../vendor/github.com/splitio/go-split-commons/v7/service/api/client.go
Claude ec1f89217a Merge: Complete Mattermost Server with Community Enterprise
Full Mattermost server source with integrated Community Enterprise features.
Includes vendor directory for offline/air-gapped builds.

Structure:
- enterprise-impl/: Enterprise feature implementations
- enterprise-community/: Init files that register implementations
- enterprise/: Bridge imports (community_imports.go)
- vendor/: All dependencies for offline builds

Build (online):
  go build ./cmd/mattermost

Build (offline/air-gapped):
  go build -mod=vendor ./cmd/mattermost

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-17 23:59:07 +09:00

174 lines
4.6 KiB
Go

package api
import (
"bytes"
"compress/gzip"
"fmt"
"io"
"io/ioutil"
"net/http"
"time"
"github.com/splitio/go-split-commons/v7/conf"
"github.com/splitio/go-split-commons/v7/dtos"
"github.com/splitio/go-split-commons/v7/service"
"github.com/splitio/go-toolkit/v5/logging"
)
// Cache control header constants
const (
CacheControlHeader = "Cache-Control"
CacheControlNoCache = "no-cache"
)
// Client interface for HTTPClient
type Client interface {
Get(endpoint string, fetchOptions service.RequestParams) ([]byte, error)
Post(endpoint string, body []byte, headers map[string]string) error
}
// HTTPClient structure to wrap up the net/http.Client
type HTTPClient struct {
url string
httpClient *http.Client
logger logging.LoggerInterface
apikey string
metadata dtos.Metadata
}
// NewHTTPClient instance of HttpClient
func NewHTTPClient(
apikey string,
cfg conf.AdvancedConfig,
endpoint string,
logger logging.LoggerInterface,
metadata dtos.Metadata,
) Client {
timeout := cfg.HTTPTimeout
client := &http.Client{Timeout: time.Duration(timeout) * time.Second}
return &HTTPClient{
url: endpoint,
httpClient: client,
logger: logger,
apikey: apikey,
metadata: metadata,
}
}
// Get method is a get call to an url
func (c *HTTPClient) Get(endpoint string, fetchOptions service.RequestParams) ([]byte, error) {
serviceURL := c.url + endpoint
req, _ := http.NewRequest("GET", serviceURL, nil)
authorization := c.apikey
c.logger.Debug("Authorization [ApiKey]: ", logging.ObfuscateAPIKey(authorization))
req.Header.Add("Accept-Encoding", "gzip")
req.Header.Add("Content-Type", "application/json")
parsedHeaders := AddMetadataToHeaders(c.metadata, make(map[string]string), nil)
for headerName, headerValue := range parsedHeaders {
req.Header.Add(headerName, headerValue)
}
c.logger.Debug(fmt.Sprintf("Headers: %v", req.Header))
req.Header.Add("Authorization", "Bearer "+authorization)
if fetchOptions != nil {
fetchOptions.Apply(req)
}
c.logger.Debug("[GET] ", req.URL.String())
resp, err := c.httpClient.Do(req)
if err != nil {
c.logger.Error("Error requesting data to API: ", req.URL.String(), err.Error())
return nil, err
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
message := fmt.Sprintf("GET method: [%s] Status Code: %d - %s", req.URL.String(), resp.StatusCode, resp.Status)
if resp.StatusCode == http.StatusNotModified {
c.logger.Debug(message)
} else {
c.logger.Error(message)
}
return nil, &dtos.HTTPError{
Code: resp.StatusCode,
Message: resp.Status,
}
}
defer resp.Body.Close()
// Check that the server actually sent compressed data
var reader io.ReadCloser
switch resp.Header.Get("Content-Encoding") {
case "gzip":
reader, err = gzip.NewReader(resp.Body)
if err != nil {
return nil, fmt.Errorf("error parsing gzip resopnse body: %w", err)
}
defer reader.Close()
default:
reader = resp.Body
}
body, err := ioutil.ReadAll(reader)
if err != nil {
c.logger.Error("error readig body from: ", req.URL.String(), ": ", err.Error())
return nil, err
}
c.logger.Verbose("[RESPONSE_BODY]", string(body), "[END_RESPONSE_BODY]")
return body, nil
}
// Post performs a HTTP POST request
func (c *HTTPClient) Post(endpoint string, body []byte, headers map[string]string) error {
serviceURL := c.url + endpoint
c.logger.Debug("[POST] ", serviceURL)
req, _ := http.NewRequest("POST", serviceURL, bytes.NewBuffer(body))
//****************
req.Close = true // To prevent EOF error when connection is closed
//****************
authorization := c.apikey
c.logger.Debug("Authorization [ApiKey]: ", logging.ObfuscateAPIKey(authorization))
req.Header.Add("Accept-Encoding", "gzip")
req.Header.Add("Content-Type", "application/json")
for headerName, headerValue := range headers {
req.Header.Add(headerName, headerValue)
}
c.logger.Debug(fmt.Sprintf("Headers: %v", req.Header))
req.Header.Add("Authorization", "Bearer "+authorization)
c.logger.Verbose("[REQUEST_BODY]", string(body), "[END_REQUEST_BODY]")
resp, err := c.httpClient.Do(req)
if err != nil {
c.logger.Error("Error posting data to API: ", req.URL.String(), err.Error())
return err
}
defer resp.Body.Close()
respBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
c.logger.Error(err.Error())
return err
}
c.logger.Verbose("[RESPONSE_BODY]", string(respBody), "[END_RESPONSE_BODY]")
if resp.StatusCode >= 200 && resp.StatusCode < 300 {
return nil
}
c.logger.Error(fmt.Sprintf("POST [%s] Status Code: %d - %s", req.URL.String(), resp.StatusCode, resp.Status))
return &dtos.HTTPError{
Code: resp.StatusCode,
Message: resp.Status,
}
}